summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--CHANGES33
-rw-r--r--Makefile20
-rw-r--r--UPGRADE.txt7
-rw-r--r--addons/chan_mobile.c8
-rw-r--r--addons/chan_ooh323.c12
-rw-r--r--addons/res_config_mysql.c78
-rw-r--r--apps/app_adsiprog.c10
-rw-r--r--apps/app_alarmreceiver.c8
-rw-r--r--apps/app_amd.c8
-rw-r--r--apps/app_celgenuserevent.c8
-rw-r--r--apps/app_fax.c8
-rw-r--r--apps/app_followme.c10
-rw-r--r--apps/app_getcpeid.c10
-rw-r--r--apps/app_meetme.c12
-rw-r--r--apps/app_minivm.c10
-rw-r--r--apps/app_playback.c14
-rw-r--r--apps/app_queue.c14
-rw-r--r--apps/app_speech_utils.c10
-rw-r--r--apps/app_stack.c12
-rw-r--r--apps/app_stasis.c2
-rw-r--r--apps/app_voicemail.c17
-rw-r--r--cdr/cdr_adaptive_odbc.c69
-rw-r--r--cdr/cdr_csv.c12
-rw-r--r--cdr/cdr_custom.c12
-rw-r--r--cdr/cdr_manager.c12
-rw-r--r--cdr/cdr_odbc.c12
-rw-r--r--cdr/cdr_pgsql.c114
-rw-r--r--cdr/cdr_radius.c10
-rw-r--r--cdr/cdr_tds.c12
-rw-r--r--cel/cel_custom.c2
-rw-r--r--cel/cel_odbc.c48
-rw-r--r--cel/cel_pgsql.c73
-rw-r--r--channels/chan_alsa.c10
-rw-r--r--channels/chan_bridge_media.c4
-rw-r--r--channels/chan_console.c10
-rw-r--r--channels/chan_dahdi.c16
-rw-r--r--channels/chan_iax2.c26
-rw-r--r--channels/chan_mgcp.c28
-rw-r--r--channels/chan_motif.c12
-rw-r--r--channels/chan_pjsip.c11
-rw-r--r--channels/chan_sip.c16
-rw-r--r--channels/chan_skinny.c12
-rw-r--r--channels/chan_unistim.c6
-rw-r--r--channels/sig_pri.c12
-rw-r--r--codecs/codec_adpcm.c8
-rw-r--r--codecs/codec_alaw.c8
-rw-r--r--codecs/codec_dahdi.c10
-rw-r--r--codecs/codec_g722.c8
-rw-r--r--codecs/codec_g726.c8
-rw-r--r--codecs/codec_gsm.c8
-rw-r--r--codecs/codec_lpc10.c8
-rw-r--r--codecs/codec_speex.c10
-rw-r--r--codecs/codec_ulaw.c8
-rw-r--r--codecs/lpc10/lpcini.c3
-rw-r--r--configs/basic-pbx/extensions.conf4
-rw-r--r--configs/samples/cdr_adaptive_odbc.conf.sample3
-rw-r--r--configs/samples/cel_pgsql.conf.sample2
-rw-r--r--configs/samples/pjsip.conf.sample8
-rw-r--r--configs/samples/sip.conf.sample7
-rwxr-xr-xcontrib/scripts/get_mp3_source.sh8
-rw-r--r--doc/.gitignore4
-rw-r--r--doc/asterisk-ng-doxygen.in (renamed from contrib/asterisk-ng-doxygen)0
-rw-r--r--doc/asterisk.85
-rw-r--r--funcs/func_aes.c10
-rw-r--r--funcs/func_curl.c10
-rw-r--r--funcs/func_dialplan.c2
-rw-r--r--funcs/func_odbc.c10
-rw-r--r--funcs/func_periodic_hook.c18
-rw-r--r--include/asterisk/astmm.h103
-rw-r--r--include/asterisk/astobj2.h2
-rw-r--r--include/asterisk/audiohook.h1
-rw-r--r--include/asterisk/config.h17
-rw-r--r--include/asterisk/event.h2
-rw-r--r--include/asterisk/http_websocket.h86
-rw-r--r--include/asterisk/options.h3
-rw-r--r--include/asterisk/res_pjsip.h11
-rw-r--r--include/asterisk/rtp_engine.h2
-rw-r--r--include/asterisk/sem.h4
-rw-r--r--include/asterisk/slin.h4
-rw-r--r--include/asterisk/sorcery.h128
-rw-r--r--include/asterisk/stasis.h45
-rw-r--r--include/asterisk/stasis_endpoints.h6
-rw-r--r--include/asterisk/stasis_test.h3
-rw-r--r--include/asterisk/tcptls.h1
-rw-r--r--include/asterisk/vector.h90
-rw-r--r--main/ast_expr2.c2
-rw-r--r--main/ast_expr2.y2
-rw-r--r--main/ast_expr2f.c2
-rw-r--r--main/asterisk.c122
-rw-r--r--main/astmm.c11
-rw-r--r--main/astobj2.c2
-rw-r--r--main/astobj2_hash.c11
-rw-r--r--main/audiohook.c131
-rw-r--r--main/cdr.c6
-rw-r--r--main/config.c13
-rw-r--r--main/dns_srv.c12
-rw-r--r--main/file.c4
-rw-r--r--main/hashtab.c2
-rw-r--r--main/logger.c26
-rw-r--r--main/manager.c2
-rw-r--r--main/manager_endpoints.c1
-rw-r--r--main/message.c8
-rw-r--r--main/sdp_srtp.c5
-rw-r--r--main/sorcery.c283
-rw-r--r--main/stasis.c4
-rw-r--r--main/stasis_endpoints.c71
-rw-r--r--main/tcptls.c94
-rw-r--r--pbx/pbx_ael.c10
-rw-r--r--pbx/pbx_config.c10
-rw-r--r--pbx/pbx_dundi.c12
-rw-r--r--pbx/pbx_lua.c10
-rw-r--r--pbx/pbx_spool.c35
-rw-r--r--res/ael/ael.flex2
-rw-r--r--res/ael/ael.tab.c2
-rw-r--r--res/ael/ael.y2
-rw-r--r--res/ael/ael_lex.c2
-rw-r--r--res/ael/pval.c2
-rw-r--r--res/ari/resource_events.c39
-rw-r--r--res/ari/resource_events.h15
-rw-r--r--res/res_adsi.c12
-rw-r--r--res/res_ael_share.c8
-rw-r--r--res/res_agi.c10
-rw-r--r--res/res_ari.c2
-rw-r--r--res/res_ari_applications.c2
-rw-r--r--res/res_ari_asterisk.c2
-rw-r--r--res/res_ari_bridges.c2
-rw-r--r--res/res_ari_channels.c2
-rw-r--r--res/res_ari_device_states.c2
-rw-r--r--res/res_ari_endpoints.c2
-rw-r--r--res/res_ari_events.c110
-rw-r--r--res/res_ari_mailboxes.c2
-rw-r--r--res/res_ari_model.c2
-rw-r--r--res/res_ari_playbacks.c2
-rw-r--r--res/res_ari_recordings.c2
-rw-r--r--res/res_ari_sounds.c2
-rw-r--r--res/res_calendar.c12
-rw-r--r--res/res_calendar_caldav.c10
-rw-r--r--res/res_calendar_exchange.c10
-rw-r--r--res/res_calendar_icalendar.c10
-rw-r--r--res/res_chan_stats.c2
-rw-r--r--res/res_clialiases.c10
-rw-r--r--res/res_config_curl.c12
-rw-r--r--res/res_config_odbc.c12
-rw-r--r--res/res_config_pgsql.c73
-rw-r--r--res/res_config_sqlite.c8
-rw-r--r--res/res_crypto.c12
-rw-r--r--res/res_curl.c10
-rw-r--r--res/res_fax.c12
-rw-r--r--res/res_fax_spandsp.c18
-rw-r--r--res/res_hep.c2
-rw-r--r--res/res_hep_pjsip.c10
-rw-r--r--res/res_hep_rtcp.c4
-rw-r--r--res/res_http_websocket.c119
-rw-r--r--res/res_manager_devicestate.c10
-rw-r--r--res/res_manager_presencestate.c10
-rw-r--r--res/res_monitor.c10
-rw-r--r--res/res_mwi_external_ami.c6
-rw-r--r--res/res_odbc.c12
-rw-r--r--res/res_phoneprov.c12
-rw-r--r--res/res_pjsip.c338
-rw-r--r--res/res_pjsip/location.c5
-rw-r--r--res/res_pjsip/pjsip_configuration.c116
-rw-r--r--res/res_pjsip/pjsip_options.c5
-rw-r--r--res/res_pjsip_acl.c10
-rw-r--r--res/res_pjsip_authenticator_digest.c10
-rw-r--r--res/res_pjsip_caller_id.c10
-rw-r--r--res/res_pjsip_config_wizard.c46
-rw-r--r--res/res_pjsip_dialog_info_body_generator.c8
-rw-r--r--res/res_pjsip_diversion.c10
-rw-r--r--res/res_pjsip_dtmf_info.c10
-rw-r--r--res/res_pjsip_endpoint_identifier_anonymous.c10
-rw-r--r--res/res_pjsip_endpoint_identifier_ip.c12
-rw-r--r--res/res_pjsip_endpoint_identifier_user.c10
-rw-r--r--res/res_pjsip_exten_state.c10
-rw-r--r--res/res_pjsip_header_funcs.c9
-rw-r--r--res/res_pjsip_keepalive.c12
-rw-r--r--res/res_pjsip_log_forwarder.c2
-rw-r--r--res/res_pjsip_logger.c10
-rw-r--r--res/res_pjsip_messaging.c10
-rw-r--r--res/res_pjsip_multihomed.c10
-rw-r--r--res/res_pjsip_mwi.c12
-rw-r--r--res/res_pjsip_mwi_body_generator.c8
-rw-r--r--res/res_pjsip_nat.c10
-rw-r--r--res/res_pjsip_notify.c12
-rw-r--r--res/res_pjsip_one_touch_record_info.c2
-rw-r--r--res/res_pjsip_outbound_authenticator_digest.c8
-rw-r--r--res/res_pjsip_outbound_publish.c10
-rw-r--r--res/res_pjsip_outbound_registration.c35
-rw-r--r--res/res_pjsip_path.c8
-rw-r--r--res/res_pjsip_phoneprov_provider.c10
-rw-r--r--res/res_pjsip_pidf_body_generator.c8
-rw-r--r--res/res_pjsip_pidf_digium_body_supplement.c8
-rw-r--r--res/res_pjsip_pidf_eyebeam_body_supplement.c8
-rw-r--r--res/res_pjsip_publish_asterisk.c8
-rw-r--r--res/res_pjsip_pubsub.c11
-rw-r--r--res/res_pjsip_refer.c10
-rw-r--r--res/res_pjsip_registrar.c10
-rw-r--r--res/res_pjsip_registrar_expire.c2
-rw-r--r--res/res_pjsip_rfc3326.c10
-rw-r--r--res/res_pjsip_sdp_rtp.c10
-rw-r--r--res/res_pjsip_send_to_voicemail.c2
-rw-r--r--res/res_pjsip_session.c151
-rw-r--r--res/res_pjsip_sips_contact.c10
-rw-r--r--res/res_pjsip_t38.c10
-rw-r--r--res/res_pjsip_transport_websocket.c17
-rw-r--r--res/res_pjsip_xpidf_body_generator.c8
-rw-r--r--res/res_pktccops.c10
-rw-r--r--res/res_resolver_unbound.c13
-rw-r--r--res/res_rtp_asterisk.c18
-rw-r--r--res/res_security_log.c2
-rw-r--r--res/res_smdi.c12
-rw-r--r--res/res_snmp.c8
-rw-r--r--res/res_sorcery_memory_cache.c2555
-rw-r--r--res/res_speech.c10
-rw-r--r--res/res_stasis.c2
-rw-r--r--res/res_stasis_answer.c3
-rw-r--r--res/res_stasis_device_state.c5
-rw-r--r--res/res_stasis_mailbox.c2
-rw-r--r--res/res_stasis_playback.c3
-rw-r--r--res/res_stasis_recording.c3
-rw-r--r--res/res_stasis_snoop.c3
-rw-r--r--res/res_stasis_test.c5
-rw-r--r--res/res_statsd.c2
-rw-r--r--res/res_stun_monitor.c12
-rw-r--r--res/res_timing_dahdi.c10
-rw-r--r--res/res_timing_kqueue.c10
-rw-r--r--res/res_timing_pthread.c10
-rw-r--r--res/res_timing_timerfd.c10
-rw-r--r--res/res_xmpp.c16
-rw-r--r--res/snmp/agent.c2
-rw-r--r--res/stasis/app.c5
-rw-r--r--rest-api-templates/ari_resource.h.mustache19
-rw-r--r--rest-api-templates/res_ari_resource.c.mustache72
-rw-r--r--rest-api/api-docs/applications.json2
-rw-r--r--rest-api/api-docs/asterisk.json2
-rw-r--r--rest-api/api-docs/bridges.json2
-rw-r--r--rest-api/api-docs/channels.json2
-rw-r--r--rest-api/api-docs/deviceStates.json2
-rw-r--r--rest-api/api-docs/endpoints.json2
-rw-r--r--rest-api/api-docs/events.json2
-rw-r--r--rest-api/api-docs/mailboxes.json2
-rw-r--r--rest-api/api-docs/playbacks.json2
-rw-r--r--rest-api/api-docs/recordings.json2
-rw-r--r--rest-api/api-docs/sounds.json2
-rw-r--r--sounds/Makefile5
-rw-r--r--sounds/sounds.xml27
-rw-r--r--tests/test_ari.c5
-rw-r--r--tests/test_config.c17
-rw-r--r--tests/test_endpoints.c5
-rw-r--r--tests/test_json.c8
-rw-r--r--tests/test_message.c9
-rw-r--r--tests/test_optional_api.c5
-rw-r--r--tests/test_res_stasis.c5
-rw-r--r--tests/test_sorcery.c132
-rw-r--r--tests/test_stasis.c9
-rw-r--r--tests/test_stasis_channels.c9
-rw-r--r--tests/test_stasis_endpoints.c5
-rw-r--r--tests/test_vector.c31
-rw-r--r--utils/ael_main.c62
-rw-r--r--utils/astman.c62
-rw-r--r--utils/check_expr.c40
-rw-r--r--utils/extconf.c286
-rw-r--r--utils/frame.c6
-rw-r--r--utils/frame.h2
-rw-r--r--utils/muted.c28
-rw-r--r--utils/smsq.c8
-rw-r--r--utils/streamplayer.c20
268 files changed, 5945 insertions, 1802 deletions
diff --git a/.gitignore b/.gitignore
index 4b19213cc..0281e107c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,6 @@ menuselect-tree
*.pyc
*.gcno
*.gcda
+latex
+doxygen.log
+
diff --git a/CHANGES b/CHANGES
index 1753be663..281d059e4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -104,6 +104,16 @@ Core
for DNS functionality. Modules that use this functionality will require that
a DNS resolver module is loaded and available.
+ * Modified processing of command-line options to first parse only what
+ is necessary to read asterisk.conf. Once asterisk.conf is fully loaded,
+ the remaining options are processed. The -X option now applies to
+ asterisk.conf only. To enable #exec for other config files you must
+ set execincludes=yes in asterisk.conf. Any other option set on the
+ command-line will now override the equivalent setting from asterisk.conf.
+
+ * The TLS core in Asterisk now supports X.509 certificate subject alternative
+ names. This way one X.509 certificate can be used for hosts that can be
+ reached under multiple DNS names or for multiple hosts.
Functions
------------------
@@ -153,6 +163,29 @@ cel_pgsql
* Added a new option, 'usegmtime', which causes timestamps in CEL events
to be logged in GMT.
+ * Added support to set schema where located the table cel. This settings is
+ configurable for cel_pgsql via the 'schema' in configuration file
+ cel_pgsql.conf.
+
+CDR Backends
+------------------
+
+cdr_adaptive_odbc
+------------------
+ * Added the ability to set the character to quote identifiers. This
+ allows adding the character at the start and end of table and column
+ names. This setting is configurable for cdr_adaptive_odbc via the
+ quoted_identifiers in configuration file cdr_adaptive_odbc.conf.
+
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.4.0 to Asterisk 13.5.0 ------------
+------------------------------------------------------------------------------
+
+AMI
+------------------
+ * A new ContactStatus event has been added that reflects res_pjsip contact
+ lifecycle changes: Created, Removed, Reachable, Unreachable, Unknown.
+
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 13.3.0 to Asterisk 13.4.0 ------------
------------------------------------------------------------------------------
diff --git a/Makefile b/Makefile
index 3252f7682..7157107b5 100644
--- a/Makefile
+++ b/Makefile
@@ -429,6 +429,8 @@ _clean:
rm -f doc/core-en_US.xml
rm -f doc/full-en_US.xml
rm -f doc/rest-api/*.wiki
+ rm -f doxygen.log
+ rm -rf latex
rm -f rest-api-templates/*.pyc
@$(MAKE) -C menuselect clean
cp -f .cleancount .lastclean
@@ -445,6 +447,7 @@ distclean: $(SUBDIRS_DIST_CLEAN) _clean
rm -f include/asterisk/autoconfig.h
rm -f include/asterisk/buildopts.h
rm -rf doc/api
+ rm -f doc/asterisk-ng-doxygen
rm -f build_tools/menuselect-deps
datafiles: _all doc/core-en_US.xml
@@ -763,7 +766,8 @@ webvmail:
@echo " +-------------------------------------------+"
progdocs:
- # Note, Makefile conditionals must not be tabbed out. Wasted hours with that.
+# Note, Makefile conditionals must not be tabbed out. Wasted hours with that.
+ @cp doc/asterisk-ng-doxygen.in doc/asterisk-ng-doxygen
ifeq ($(DOXYGEN),:)
@echo "Doxygen is not installed. Please install and re-run the configuration script."
else
@@ -771,20 +775,20 @@ ifeq ($(DOT),:)
@echo "DOT is not installed. Doxygen will not produce any diagrams. Please install and re-run the configuration script."
else
# Enable DOT
- @echo "HAVE_DOT = YES" >> contrib/asterisk-ng-doxygen
+ @echo "HAVE_DOT = YES" >> doc/asterisk-ng-doxygen
endif
# Set Doxygen PROJECT_NUMBER variable
ifneq ($(ASTERISKVERSION),UNKNOWN__and_probably_unsupported)
- @echo "PROJECT_NUMBER = $(ASTERISKVERSION)" >> contrib/asterisk-ng-doxygen
+ @echo "PROJECT_NUMBER = $(ASTERISKVERSION)" >> doc/asterisk-ng-doxygen
else
echo "Asterisk Version is unknown, not configuring Doxygen PROJECT_NUMBER."
endif
- # Validate Doxygen Configuration
- @doxygen -u contrib/asterisk-ng-doxygen
+ # Validate and auto-update local copy
+ @doxygen -u doc/asterisk-ng-doxygen
# Run Doxygen
- @doxygen contrib/asterisk-ng-doxygen
+ @doxygen doc/asterisk-ng-doxygen
# Remove configuration backup file
- @rm -f contrib/asterisk-ng-doxygen.bak
+ @rm -f doc/asterisk-ng-doxygen.bak
endif
install-logrotate:
@@ -937,7 +941,7 @@ nmenuselect: menuselect/nmenuselect menuselect-tree menuselect.makeopts
-@menuselect/nmenuselect menuselect.makeopts && (echo "menuselect changes saved!"; rm -f channels/h323/Makefile.ast main/asterisk) || echo "menuselect changes NOT saved!"
# options for make in menuselect/
-MAKE_MENUSELECT=CC="$(CC)" CXX="$(CXX)" LD="" AR="" RANLIB="" \
+MAKE_MENUSELECT=CC="$(BUILD_CC)" CXX="$(CXX)" LD="" AR="" RANLIB="" \
CFLAGS="$(BUILD_CFLAGS)" LDFLAGS="$(BUILD_LDFLAGS)" \
$(MAKE) -C menuselect CONFIGURE_SILENT="--silent"
diff --git a/UPGRADE.txt b/UPGRADE.txt
index d9d5cec53..91d9edc92 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -36,6 +36,13 @@ Core:
The setting can be overridden in asterisk.conf by setting refdebug in
the options category. No recompile is required to enable/disable it.
+ - Modified processing of command-line options to first parse only what
+ is necessary to read asterisk.conf. Once asterisk.conf is fully loaded,
+ the remaining options are processed. The -X option now applies to
+ asterisk.conf only. To enable #exec for other config files you must
+ set execincludes=yes in asterisk.conf. Any other option set on the
+ command-line will now override the equivalent setting from asterisk.conf.
+
AMI:
- The 'ModuleCheck' Action's Version key will no longer show the module
version. The value will always be blank.
diff --git a/addons/chan_mobile.c b/addons/chan_mobile.c
index 45357989c..26056f420 100644
--- a/addons/chan_mobile.c
+++ b/addons/chan_mobile.c
@@ -4754,8 +4754,8 @@ e_cleanup:
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Bluetooth Mobile Device Channel Driver",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DRIVER,
);
diff --git a/addons/chan_ooh323.c b/addons/chan_ooh323.c
index da63f03c5..58db56fb7 100644
--- a/addons/chan_ooh323.c
+++ b/addons/chan_ooh323.c
@@ -5188,9 +5188,9 @@ void ast_ooh323c_exit()
#endif
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Objective Systems H323 Channel",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload_module,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DRIVER
+);
diff --git a/addons/res_config_mysql.c b/addons/res_config_mysql.c
index 299fe5619..a805cb4b4 100644
--- a/addons/res_config_mysql.c
+++ b/addons/res_config_mysql.c
@@ -1202,38 +1202,44 @@ static int require_mysql(const char *database, const char *tablename, va_list ap
PICK_WHICH_ALTER_ACTION(bigint)
}
}
- } else if (strncmp(column->type, "float", 5) == 0 && !ast_rq_is_int(type) && type != RQ_FLOAT) {
- if (table->database->requirements == RQ_WARN) {
- ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
- res = -1;
- } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
- table_altered = 1;
- } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
- table_altered = 1;
- } else {
- res = -1;
+ } else if (strncmp(column->type, "float", 5) == 0) {
+ if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
+ if (table->database->requirements == RQ_WARN) {
+ ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
+ res = -1;
+ } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
+ table_altered = 1;
+ } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
+ table_altered = 1;
+ } else {
+ res = -1;
+ }
}
- } else if ((strncmp(column->type, "datetime", 8) == 0 || strncmp(column->type, "timestamp", 9) == 0) && type != RQ_DATETIME) {
- if (table->database->requirements == RQ_WARN) {
- ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
- res = -1;
- } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
- table_altered = 1;
- } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
- table_altered = 1;
- } else {
- res = -1;
+ } else if (strncmp(column->type, "datetime", 8) == 0 || strncmp(column->type, "timestamp", 9) == 0) {
+ if (type != RQ_DATETIME) {
+ if (table->database->requirements == RQ_WARN) {
+ ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
+ res = -1;
+ } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
+ table_altered = 1;
+ } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
+ table_altered = 1;
+ } else {
+ res = -1;
+ }
}
- } else if ((strncmp(column->type, "date", 4) == 0) && type != RQ_DATE) {
- if (table->database->requirements == RQ_WARN) {
- ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
- res = -1;
- } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
- table_altered = 1;
- } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
- table_altered = 1;
- } else {
- res = -1;
+ } else if (strncmp(column->type, "date", 4) == 0) {
+ if (type != RQ_DATE) {
+ if (table->database->requirements == RQ_WARN) {
+ ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
+ res = -1;
+ } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
+ table_altered = 1;
+ } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
+ table_altered = 1;
+ } else {
+ res = -1;
+ }
}
} else { /* Other, possibly unsupported types? */
if (table->database->requirements == RQ_WARN) {
@@ -1748,10 +1754,10 @@ static char *handle_cli_realtime_mysql_status(struct ast_cli_entry *e, int cmd,
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MySQL RealTime Configuration Driver",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_REALTIME_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_REALTIME_DRIVER,
+);
diff --git a/apps/app_adsiprog.c b/apps/app_adsiprog.c
index 7f5f3e6fe..3f3d11c25 100644
--- a/apps/app_adsiprog.c
+++ b/apps/app_adsiprog.c
@@ -1612,8 +1612,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk ADSI Programming Application",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .nonoptreq = "res_adsi",
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .nonoptreq = "res_adsi",
+);
diff --git a/apps/app_alarmreceiver.c b/apps/app_alarmreceiver.c
index d5a01885e..ace4df1dc 100644
--- a/apps/app_alarmreceiver.c
+++ b/apps/app_alarmreceiver.c
@@ -995,8 +995,8 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Alarm Receiver for Asterisk",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
);
diff --git a/apps/app_amd.c b/apps/app_amd.c
index ee5dd6f42..ee421b6bc 100644
--- a/apps/app_amd.c
+++ b/apps/app_amd.c
@@ -528,8 +528,8 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
);
diff --git a/apps/app_celgenuserevent.c b/apps/app_celgenuserevent.c
index fff495460..b98cd674d 100644
--- a/apps/app_celgenuserevent.c
+++ b/apps/app_celgenuserevent.c
@@ -102,7 +102,7 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Generate an User-Defined CEL event",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/apps/app_fax.c b/apps/app_fax.c
index ff323df47..d6e9b4de0 100644
--- a/apps/app_fax.c
+++ b/apps/app_fax.c
@@ -996,9 +996,9 @@ static int load_module(void)
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Simple FAX Application",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/apps/app_followme.c b/apps/app_followme.c
index 5fd5d15ba..d2000fd13 100644
--- a/apps/app_followme.c
+++ b/apps/app_followme.c
@@ -1582,8 +1582,8 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
diff --git a/apps/app_getcpeid.c b/apps/app_getcpeid.c
index 7e457fb6e..c896f9ef0 100644
--- a/apps/app_getcpeid.c
+++ b/apps/app_getcpeid.c
@@ -139,8 +139,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Get ADSI CPE ID",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .nonoptreq = "res_adsi",
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .nonoptreq = "res_adsi",
+);
diff --git a/apps/app_meetme.c b/apps/app_meetme.c
index ba83eadd6..e74ad7518 100644
--- a/apps/app_meetme.c
+++ b/apps/app_meetme.c
@@ -8271,10 +8271,10 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MeetMe conference bridge",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
+);
diff --git a/apps/app_minivm.c b/apps/app_minivm.c
index b8a1ceb77..4b1c4b575 100644
--- a/apps/app_minivm.c
+++ b/apps/app_minivm.c
@@ -3561,8 +3561,8 @@ static int unload_module(void)
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Mini VoiceMail (A minimal Voicemail e-mail System)",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
diff --git a/apps/app_playback.c b/apps/app_playback.c
index 4b9eb15e5..2875ec2b4 100644
--- a/apps/app_playback.c
+++ b/apps/app_playback.c
@@ -490,7 +490,9 @@ static int playback_exec(struct ast_channel *chan, const char *data)
ast_stopstream(chan);
}
if (res) {
- ast_log(LOG_WARNING, "Playback failed on %s for %s\n", ast_channel_name(chan), (char *)data);
+ if (!ast_check_hangup(chan)) {
+ ast_log(LOG_WARNING, "Playback failed on %s for %s\n", ast_channel_name(chan), (char *)data);
+ }
res = 0;
mres = 1;
}
@@ -570,8 +572,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Sound File Playback Application",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 069ef37ac..d04080c27 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -10912,11 +10912,11 @@ static struct member *find_member_by_queuename_and_interface(const char *queuena
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "True Call Queueing",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_DEVSTATE_CONSUMER,
- .nonoptreq = "res_monitor",
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_DEVSTATE_CONSUMER,
+ .nonoptreq = "res_monitor",
+);
diff --git a/apps/app_speech_utils.c b/apps/app_speech_utils.c
index 5b44d1468..7c34dca52 100644
--- a/apps/app_speech_utils.c
+++ b/apps/app_speech_utils.c
@@ -1002,8 +1002,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Dialplan Speech Applications",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .nonoptreq = "res_speech",
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .nonoptreq = "res_speech",
+);
diff --git a/apps/app_stack.c b/apps/app_stack.c
index 2d440abfd..be8abbf58 100644
--- a/apps/app_stack.c
+++ b/apps/app_stack.c
@@ -1318,9 +1318,9 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT | AST_MODFLAG_LOAD_ORDER, "Dialplan subroutines (Gosub, Return, etc)",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- .nonoptreq = "res_agi",
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+ .nonoptreq = "res_agi",
+);
diff --git a/apps/app_stasis.c b/apps/app_stasis.c
index 22e834292..aa77a0d3c 100644
--- a/apps/app_stasis.c
+++ b/apps/app_stasis.c
@@ -141,4 +141,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Stasis dialplan applicat
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_stasis",
- );
+);
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index 532275e3d..f2f7bad64 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -617,6 +617,7 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate);
#define VM_MESSAGEWRAP (1 << 17) /*!< Wrap around from the last message to the first, and vice-versa */
#define VM_FWDURGAUTO (1 << 18) /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
#define ERROR_LOCK_PATH -100
+#define ERROR_MAX_MSGS -101
#define OPERATOR_EXIT 300
enum vm_box {
@@ -7093,7 +7094,7 @@ static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg
} else {
if (x >= vmu->maxmsg) {
ast_unlock_path(ddir);
- return -1;
+ return ERROR_MAX_MSGS;
}
}
make_file(sfn, sizeof(sfn), dir, msg);
@@ -8918,7 +8919,7 @@ static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
} else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
/* Move to old folder before deleting */
res = save_to_folder(vmu, vms, x, 1, NULL, 0);
- if (res == ERROR_LOCK_PATH) {
+ if (res == ERROR_LOCK_PATH || res == ERROR_MAX_MSGS) {
/* If save failed do not delete the message */
ast_log(AST_LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
vms->deleted[x] = 0;
@@ -16116,9 +16117,9 @@ play2_msg_cleanup:
*/
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .nonoptreq = "res_adsi,res_smdi",
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .nonoptreq = "res_adsi,res_smdi",
+);
diff --git a/cdr/cdr_adaptive_odbc.c b/cdr/cdr_adaptive_odbc.c
index 83877cbf1..f276959a0 100644
--- a/cdr/cdr_adaptive_odbc.c
+++ b/cdr/cdr_adaptive_odbc.c
@@ -82,6 +82,7 @@ struct tables {
char *connection;
char *table;
char *schema;
+ char quoted_identifiers;
unsigned int usegmtime:1;
AST_LIST_HEAD_NOLOCK(odbc_columns, columns) columns;
AST_RWLIST_ENTRY(tables) list;
@@ -101,6 +102,7 @@ static int load_config(void)
char connection[40];
char table[40];
char schema[40];
+ char quoted_identifiers;
int lenconnection, lentable, lenschema, usegmtime = 0;
SQLLEN sqlptr;
int res = 0;
@@ -149,6 +151,16 @@ static int load_config(void)
ast_copy_string(schema, tmp, sizeof(schema));
lenschema = strlen(schema);
+ if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "quoted_identifiers"))) {
+ tmp = "";
+ }
+ quoted_identifiers = tmp[0];
+ if (strlen(tmp) > 1) {
+ ast_log(LOG_ERROR, "The quoted_identifiers setting only accepts a single character,"
+ " while a value of '%s' was provided. This option has been disabled as a result.\n", tmp);
+ quoted_identifiers = '\0';
+ }
+
res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection);
@@ -164,7 +176,7 @@ static int load_config(void)
continue;
}
- tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1 + lenschema + 1);
+ tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1 + lenschema + 1 + 1);
if (!tableptr) {
ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'%s%s%s\n", table, connection,
lenschema ? " (schema '" : "", lenschema ? schema : "", lenschema ? "')" : "");
@@ -181,6 +193,7 @@ static int load_config(void)
ast_copy_string(tableptr->connection, connection, lenconnection + 1);
ast_copy_string(tableptr->table, table, lentable + 1);
ast_copy_string(tableptr->schema, schema, lenschema + 1);
+ tableptr->quoted_identifiers = quoted_identifiers;
ast_verb(3, "Found adaptive CDR table %s@%s.\n", tableptr->table, tableptr->connection);
@@ -382,6 +395,8 @@ static int odbc_log(struct ast_cdr *cdr)
char colbuf[1024], *colptr;
SQLHSTMT stmt = NULL;
SQLLEN rows = 0;
+ char *separator;
+ int quoted = 0;
if (!sql || !sql2) {
if (sql)
@@ -399,11 +414,27 @@ static int odbc_log(struct ast_cdr *cdr)
}
AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
- int first = 1;
+ separator = "";
+
+ if (tableptr->quoted_identifiers != '\0'){
+ quoted = 1;
+ }
+
if (ast_strlen_zero(tableptr->schema)) {
- ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
+ if (quoted) {
+ ast_str_set(&sql, 0, "INSERT INTO %c%s%c (",
+ tableptr->quoted_identifiers, tableptr->table, tableptr->quoted_identifiers );
+ }else{
+ ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
+ }
} else {
- ast_str_set(&sql, 0, "INSERT INTO %s.%s (", tableptr->schema, tableptr->table);
+ if (quoted) {
+ ast_str_set(&sql, 0, "INSERT INTO %c%s%c.%c%s%c (",
+ tableptr->quoted_identifiers, tableptr->schema, tableptr->quoted_identifiers,
+ tableptr->quoted_identifiers, tableptr->table, tableptr->quoted_identifiers);
+ }else{
+ ast_str_set(&sql, 0, "INSERT INTO %s.%s (", tableptr->schema, tableptr->table);
+ }
}
ast_str_set(&sql2, 0, " VALUES (");
@@ -487,7 +518,7 @@ static int odbc_log(struct ast_cdr *cdr)
LENGTHEN_BUF2(strlen(colptr));
/* Encode value, with escaping */
- ast_str_append(&sql2, 0, "%s'", first ? "" : ",");
+ ast_str_append(&sql2, 0, "%s'", separator);
for (tmp = colptr; *tmp; tmp++) {
if (*tmp == '\'') {
ast_str_append(&sql2, 0, "''");
@@ -520,7 +551,7 @@ static int odbc_log(struct ast_cdr *cdr)
}
LENGTHEN_BUF2(17);
- ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", first ? "" : ",", year, month, day);
+ ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", separator, year, month, day);
}
break;
case SQL_TYPE_TIME:
@@ -536,7 +567,7 @@ static int odbc_log(struct ast_cdr *cdr)
}
LENGTHEN_BUF2(15);
- ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", first ? "" : ",", hour, minute, second);
+ ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", separator, hour, minute, second);
}
break;
case SQL_TYPE_TIMESTAMP:
@@ -564,7 +595,7 @@ static int odbc_log(struct ast_cdr *cdr)
}
LENGTHEN_BUF2(26);
- ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", first ? "" : ",", year, month, day, hour, minute, second);
+ ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", separator, year, month, day, hour, minute, second);
}
break;
case SQL_INTEGER:
@@ -578,7 +609,7 @@ static int odbc_log(struct ast_cdr *cdr)
}
LENGTHEN_BUF2(12);
- ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+ ast_str_append(&sql2, 0, "%s%d", separator, integer);
}
break;
case SQL_BIGINT:
@@ -592,7 +623,7 @@ static int odbc_log(struct ast_cdr *cdr)
}
LENGTHEN_BUF2(24);
- ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer);
+ ast_str_append(&sql2, 0, "%s%lld", separator, integer);
}
break;
case SQL_SMALLINT:
@@ -606,7 +637,7 @@ static int odbc_log(struct ast_cdr *cdr)
}
LENGTHEN_BUF2(6);
- ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+ ast_str_append(&sql2, 0, "%s%d", separator, integer);
}
break;
case SQL_TINYINT:
@@ -620,7 +651,7 @@ static int odbc_log(struct ast_cdr *cdr)
}
LENGTHEN_BUF2(4);
- ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+ ast_str_append(&sql2, 0, "%s%d", separator, integer);
}
break;
case SQL_BIT:
@@ -636,7 +667,7 @@ static int odbc_log(struct ast_cdr *cdr)
integer = 1;
LENGTHEN_BUF2(2);
- ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+ ast_str_append(&sql2, 0, "%s%d", separator, integer);
}
break;
case SQL_NUMERIC:
@@ -668,7 +699,7 @@ static int odbc_log(struct ast_cdr *cdr)
}
LENGTHEN_BUF2(entry->decimals);
- ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number);
+ ast_str_append(&sql2, 0, "%s%*.*lf", separator, entry->decimals, entry->radix, number);
}
break;
case SQL_FLOAT:
@@ -701,15 +732,19 @@ static int odbc_log(struct ast_cdr *cdr)
}
LENGTHEN_BUF2(entry->decimals);
- ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number);
+ ast_str_append(&sql2, 0, "%s%lf", separator, number);
}
break;
default:
ast_log(LOG_WARNING, "Column type %d (field '%s:%s:%s') is unsupported at this time.\n", entry->type, tableptr->connection, tableptr->table, entry->name);
continue;
}
- ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
- first = 0;
+ if (quoted) {
+ ast_str_append(&sql, 0, "%s%s", separator, entry->name);
+ } else {
+ ast_str_append(&sql, 0, "%s%c%s%c", separator, tableptr->quoted_identifiers, entry->name, tableptr->quoted_identifiers);
+ }
+ separator = ", ";
} else if (entry->filtervalue
&& ((!entry->negatefiltervalue && entry->filtervalue[0] != '\0')
|| (entry->negatefiltervalue && entry->filtervalue[0] == '\0'))) {
diff --git a/cdr/cdr_csv.c b/cdr/cdr_csv.c
index 31f95c00f..2877a6b54 100644
--- a/cdr/cdr_csv.c
+++ b/cdr/cdr_csv.c
@@ -357,9 +357,9 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Comma Separated Values CDR Backend",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CDR_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CDR_DRIVER,
+);
diff --git a/cdr/cdr_custom.c b/cdr/cdr_custom.c
index 2c4d8b712..24cb834f6 100644
--- a/cdr/cdr_custom.c
+++ b/cdr/cdr_custom.c
@@ -226,10 +226,10 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Customizable Comma Separated Values CDR Backend",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CDR_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CDR_DRIVER,
+);
diff --git a/cdr/cdr_manager.c b/cdr/cdr_manager.c
index 4a079aecd..ef9d63e47 100644
--- a/cdr/cdr_manager.c
+++ b/cdr/cdr_manager.c
@@ -368,9 +368,9 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Asterisk Manager Interface CDR Backend",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CDR_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CDR_DRIVER,
+);
diff --git a/cdr/cdr_odbc.c b/cdr/cdr_odbc.c
index 4d601ef9a..5fddb7b23 100644
--- a/cdr/cdr_odbc.c
+++ b/cdr/cdr_odbc.c
@@ -324,9 +324,9 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "ODBC CDR Backend",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CDR_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CDR_DRIVER,
+);
diff --git a/cdr/cdr_pgsql.c b/cdr/cdr_pgsql.c
index 310411cd3..8dc49e107 100644
--- a/cdr/cdr_pgsql.c
+++ b/cdr/cdr_pgsql.c
@@ -75,6 +75,7 @@ static char *encoding;
static char *tz;
static int connected = 0;
+/* Optimization to reduce number of memory allocations */
static int maxsize = 512, maxsize2 = 512;
static time_t connect_time = 0;
static int totalrecords = 0;
@@ -247,7 +248,7 @@ static int pgsql_log(struct ast_cdr *cdr)
struct columns *cur;
struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
char buf[257], escapebuf[513], *value;
- int first = 1;
+ char *separator = "";
if (!sql || !sql2) {
ast_free(sql);
@@ -269,86 +270,86 @@ static int pgsql_log(struct ast_cdr *cdr)
if (cur->notnull && !cur->hasdefault) {
/* Field is NOT NULL (but no default), must include it anyway */
LENGTHEN_BUF1(strlen(cur->name) + 2);
- ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
+ ast_str_append(&sql, 0, "%s\"%s\"", separator, cur->name);
LENGTHEN_BUF2(3);
- ast_str_append(&sql2, 0, "%s''", first ? "" : ",");
- first = 0;
+ ast_str_append(&sql2, 0, "%s''", separator);
+ separator = ", ";
}
continue;
}
LENGTHEN_BUF1(strlen(cur->name) + 2);
- ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
+ ast_str_append(&sql, 0, "%s\"%s\"", separator, cur->name);
if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) {
if (strncmp(cur->type, "int", 3) == 0) {
LENGTHEN_BUF2(13);
- ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->start.tv_sec);
+ ast_str_append(&sql2, 0, "%s%ld", separator, (long) cdr->start.tv_sec);
} else if (strncmp(cur->type, "float", 5) == 0) {
LENGTHEN_BUF2(31);
- ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
+ ast_str_append(&sql2, 0, "%s%f", separator, (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
} else {
/* char, hopefully */
LENGTHEN_BUF2(31);
ast_localtime(&cdr->start, &tm, tz);
ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
- ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
+ ast_str_append(&sql2, 0, "%s%s", separator, buf);
}
} else if (strcmp(cur->name, "answer") == 0) {
if (strncmp(cur->type, "int", 3) == 0) {
LENGTHEN_BUF2(13);
- ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->answer.tv_sec);
+ ast_str_append(&sql2, 0, "%s%ld", separator, (long) cdr->answer.tv_sec);
} else if (strncmp(cur->type, "float", 5) == 0) {
LENGTHEN_BUF2(31);
- ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
+ ast_str_append(&sql2, 0, "%s%f", separator, (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
} else {
/* char, hopefully */
LENGTHEN_BUF2(31);
ast_localtime(&cdr->answer, &tm, tz);
ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
- ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
+ ast_str_append(&sql2, 0, "%s%s", separator, buf);
}
} else if (strcmp(cur->name, "end") == 0) {
if (strncmp(cur->type, "int", 3) == 0) {
LENGTHEN_BUF2(13);
- ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->end.tv_sec);
+ ast_str_append(&sql2, 0, "%s%ld", separator, (long) cdr->end.tv_sec);
} else if (strncmp(cur->type, "float", 5) == 0) {
LENGTHEN_BUF2(31);
- ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
+ ast_str_append(&sql2, 0, "%s%f", separator, (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
} else {
/* char, hopefully */
LENGTHEN_BUF2(31);
ast_localtime(&cdr->end, &tm, tz);
ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
- ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
+ ast_str_append(&sql2, 0, "%s%s", separator, buf);
}
} else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) {
if (cur->type[0] == 'i') {
/* Get integer, no need to escape anything */
ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
LENGTHEN_BUF2(13);
- ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
+ ast_str_append(&sql2, 0, "%s%s", separator, value);
} else if (strncmp(cur->type, "float", 5) == 0) {
struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
LENGTHEN_BUF2(31);
- ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
+ ast_str_append(&sql2, 0, "%s%f", separator, (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
} else {
/* Char field, probably */
struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
LENGTHEN_BUF2(31);
- ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
+ ast_str_append(&sql2, 0, "%s'%f'", separator, (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
}
} else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
if (strncmp(cur->type, "int", 3) == 0) {
/* Integer, no need to escape anything */
ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 1);
LENGTHEN_BUF2(13);
- ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
+ ast_str_append(&sql2, 0, "%s%s", separator, value);
} else {
/* Although this is a char field, there are no special characters in the values for these fields */
ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
LENGTHEN_BUF2(31);
- ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value);
+ ast_str_append(&sql2, 0, "%s'%s'", separator, value);
}
} else {
/* Arbitrary field, could be anything */
@@ -357,19 +358,19 @@ static int pgsql_log(struct ast_cdr *cdr)
long long whatever;
if (value && sscanf(value, "%30lld", &whatever) == 1) {
LENGTHEN_BUF2(26);
- ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", whatever);
+ ast_str_append(&sql2, 0, "%s%lld", separator, whatever);
} else {
LENGTHEN_BUF2(2);
- ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
+ ast_str_append(&sql2, 0, "%s0", separator);
}
} else if (strncmp(cur->type, "float", 5) == 0) {
long double whatever;
if (value && sscanf(value, "%30Lf", &whatever) == 1) {
LENGTHEN_BUF2(51);
- ast_str_append(&sql2, 0, "%s%30Lf", first ? "" : ",", whatever);
+ ast_str_append(&sql2, 0, "%s%30Lf", separator, whatever);
} else {
LENGTHEN_BUF2(2);
- ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
+ ast_str_append(&sql2, 0, "%s0", separator);
}
/* XXX Might want to handle dates, times, and other misc fields here XXX */
} else {
@@ -378,10 +379,10 @@ static int pgsql_log(struct ast_cdr *cdr)
else
escapebuf[0] = '\0';
LENGTHEN_BUF2(strlen(escapebuf) + 3);
- ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", escapebuf);
+ ast_str_append(&sql2, 0, "%s'%s'", separator, escapebuf);
}
}
- first = 0;
+ separator = ", ";
}
LENGTHEN_BUF1(ast_str_strlen(sql2) + 2);
@@ -453,6 +454,15 @@ static int pgsql_log(struct ast_cdr *cdr)
records++;
}
PQclear(result);
+
+ /* Next time, just allocate buffers that are that big to start with. */
+ if (ast_str_strlen(sql) > maxsize) {
+ maxsize = ast_str_strlen(sql);
+ }
+ if (ast_str_strlen(sql2) > maxsize2) {
+ maxsize2 = ast_str_strlen(sql2);
+ }
+
ast_free(sql);
ast_free(sql2);
}
@@ -670,42 +680,20 @@ static int config_module(int reload)
version = PQserverVersion(conn);
if (version >= 70300) {
- char *schemaname, *tablename;
+ char *schemaname, *tablename, *tmp_schemaname, *tmp_tablename;
if (strchr(table, '.')) {
- schemaname = ast_strdupa(table);
- tablename = strchr(schemaname, '.');
- *tablename++ = '\0';
+ tmp_schemaname = ast_strdupa(table);
+ tmp_tablename = strchr(tmp_schemaname, '.');
+ *tmp_tablename++ = '\0';
} else {
- schemaname = "";
- tablename = table;
+ tmp_schemaname = "";
+ tmp_tablename = table;
}
+ tablename = ast_alloca(strlen(tmp_tablename) * 2 + 1);
+ PQescapeStringConn(conn, tablename, tmp_tablename, strlen(tmp_tablename), NULL);
- /* Escape special characters in schemaname */
- if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
- char *tmp = schemaname, *ptr;
-
- ptr = schemaname = ast_alloca(strlen(tmp) * 2 + 1);
- for (; *tmp; tmp++) {
- if (strchr("\\'", *tmp)) {
- *ptr++ = *tmp;
- }
- *ptr++ = *tmp;
- }
- *ptr = '\0';
- }
- /* Escape special characters in tablename */
- if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
- char *tmp = tablename, *ptr;
-
- ptr = tablename = ast_alloca(strlen(tmp) * 2 + 1);
- for (; *tmp; tmp++) {
- if (strchr("\\'", *tmp)) {
- *ptr++ = *tmp;
- }
- *ptr++ = *tmp;
- }
- *ptr = '\0';
- }
+ schemaname = ast_alloca(strlen(tmp_schemaname) * 2 + 1);
+ PQescapeStringConn(conn, schemaname, tmp_schemaname, strlen(tmp_schemaname), NULL);
snprintf(sqlcmd, sizeof(sqlcmd), "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM (((pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace AND c.relname = '%s' AND n.nspname = %s%s%s) INNER JOIN pg_catalog.pg_attribute a ON (NOT a.attisdropped) AND a.attnum > 0 AND a.attrelid = c.oid) INNER JOIN pg_catalog.pg_type t ON t.oid = a.atttypid) LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum ORDER BY n.nspname, c.relname, attnum",
tablename,
@@ -801,9 +789,9 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PostgreSQL CDR Backend",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CDR_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CDR_DRIVER,
+);
diff --git a/cdr/cdr_radius.c b/cdr/cdr_radius.c
index d9542653f..e1a639cda 100644
--- a/cdr/cdr_radius.c
+++ b/cdr/cdr_radius.c
@@ -301,8 +301,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "RADIUS CDR Backend",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CDR_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CDR_DRIVER,
+);
diff --git a/cdr/cdr_tds.c b/cdr/cdr_tds.c
index c71abd0e7..6dabeff83 100644
--- a/cdr/cdr_tds.c
+++ b/cdr/cdr_tds.c
@@ -632,9 +632,9 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "FreeTDS CDR Backend",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CDR_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CDR_DRIVER,
+);
diff --git a/cel/cel_custom.c b/cel/cel_custom.c
index 0c55e497b..f6377fcd7 100644
--- a/cel/cel_custom.c
+++ b/cel/cel_custom.c
@@ -222,5 +222,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Customizable Comma Se
.unload = unload_module,
.reload = reload,
.load_pri = AST_MODPRI_CDR_DRIVER,
- );
+);
diff --git a/cel/cel_odbc.c b/cel/cel_odbc.c
index 480344499..2d8408baa 100644
--- a/cel/cel_odbc.c
+++ b/cel/cel_odbc.c
@@ -402,7 +402,7 @@ static void odbc_log(struct ast_event *event)
}
AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
- int first = 1;
+ char *separator = "";
ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
ast_str_set(&sql2, 0, " VALUES (");
@@ -536,11 +536,11 @@ static void odbc_log(struct ast_event *event)
}
}
- ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+ ast_str_append(&sql, 0, "%s%s", separator, entry->name);
LENGTHEN_BUF2(strlen(colptr));
/* Encode value, with escaping */
- ast_str_append(&sql2, 0, "%s'", first ? "" : ",");
+ ast_str_append(&sql2, 0, "%s'", separator);
for (tmp = colptr; *tmp; tmp++) {
if (*tmp == '\'') {
ast_str_append(&sql2, 0, "''");
@@ -580,9 +580,9 @@ static void odbc_log(struct ast_event *event)
}
}
- ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+ ast_str_append(&sql, 0, "%s%s", separator, entry->name);
LENGTHEN_BUF2(17);
- ast_str_append(&sql2, 0, "%s{d '%04d-%02d-%02d'}", first ? "" : ",", year, month, day);
+ ast_str_append(&sql2, 0, "%s{d '%04d-%02d-%02d'}", separator, year, month, day);
}
break;
case SQL_TYPE_TIME:
@@ -605,9 +605,9 @@ static void odbc_log(struct ast_event *event)
}
}
- ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+ ast_str_append(&sql, 0, "%s%s", separator, entry->name);
LENGTHEN_BUF2(15);
- ast_str_append(&sql2, 0, "%s{t '%02d:%02d:%02d'}", first ? "" : ",", hour, minute, second);
+ ast_str_append(&sql2, 0, "%s{t '%02d:%02d:%02d'}", separator, hour, minute, second);
}
break;
case SQL_TYPE_TIMESTAMP:
@@ -646,9 +646,9 @@ static void odbc_log(struct ast_event *event)
}
}
- ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+ ast_str_append(&sql, 0, "%s%s", separator, entry->name);
LENGTHEN_BUF2(27);
- ast_str_append(&sql2, 0, "%s{ts '%04d-%02d-%02d %02d:%02d:%02d.%d'}", first ? "" : ",", year, month, day, hour, minute, second, fraction);
+ ast_str_append(&sql2, 0, "%s{ts '%04d-%02d-%02d %02d:%02d:%02d.%d'}", separator, year, month, day, hour, minute, second, fraction);
}
break;
case SQL_INTEGER:
@@ -659,9 +659,9 @@ static void odbc_log(struct ast_event *event)
continue;
}
- ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+ ast_str_append(&sql, 0, "%s%s", separator, entry->name);
LENGTHEN_BUF2(12);
- ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+ ast_str_append(&sql2, 0, "%s%d", separator, integer);
}
break;
case SQL_BIGINT:
@@ -673,9 +673,9 @@ static void odbc_log(struct ast_event *event)
continue;
}
- ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+ ast_str_append(&sql, 0, "%s%s", separator, entry->name);
LENGTHEN_BUF2(24);
- ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer);
+ ast_str_append(&sql2, 0, "%s%lld", separator, integer);
}
break;
case SQL_SMALLINT:
@@ -686,9 +686,9 @@ static void odbc_log(struct ast_event *event)
continue;
}
- ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+ ast_str_append(&sql, 0, "%s%s", separator, entry->name);
LENGTHEN_BUF2(7);
- ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+ ast_str_append(&sql2, 0, "%s%d", separator, integer);
}
break;
case SQL_TINYINT:
@@ -699,9 +699,9 @@ static void odbc_log(struct ast_event *event)
continue;
}
- ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+ ast_str_append(&sql, 0, "%s%s", separator, entry->name);
LENGTHEN_BUF2(4);
- ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+ ast_str_append(&sql2, 0, "%s%d", separator, integer);
}
break;
case SQL_BIT:
@@ -714,9 +714,9 @@ static void odbc_log(struct ast_event *event)
if (integer != 0)
integer = 1;
- ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+ ast_str_append(&sql, 0, "%s%s", separator, entry->name);
LENGTHEN_BUF2(2);
- ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+ ast_str_append(&sql2, 0, "%s%d", separator, integer);
}
break;
case SQL_NUMERIC:
@@ -728,9 +728,9 @@ static void odbc_log(struct ast_event *event)
continue;
}
- ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+ ast_str_append(&sql, 0, "%s%s", separator, entry->name);
LENGTHEN_BUF2(entry->decimals + 2);
- ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number);
+ ast_str_append(&sql2, 0, "%s%*.*lf", separator, entry->decimals, entry->radix, number);
}
break;
case SQL_FLOAT:
@@ -743,16 +743,16 @@ static void odbc_log(struct ast_event *event)
continue;
}
- ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+ ast_str_append(&sql, 0, "%s%s", separator, entry->name);
LENGTHEN_BUF2(entry->decimals);
- ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number);
+ ast_str_append(&sql2, 0, "%s%lf", separator, number);
}
break;
default:
ast_log(LOG_WARNING, "Column type %d (field '%s:%s:%s') is unsupported at this time.\n", entry->type, tableptr->connection, tableptr->table, entry->name);
continue;
}
- first = 0;
+ separator = ", ";
}
}
diff --git a/cel/cel_pgsql.c b/cel/cel_pgsql.c
index 2d7f0dfb0..f4959474a 100644
--- a/cel/cel_pgsql.c
+++ b/cel/cel_pgsql.c
@@ -60,6 +60,8 @@ ASTERISK_REGISTER_FILE()
#define PGSQL_BACKEND_NAME "CEL PGSQL backend"
+#define PGSQL_MIN_VERSION_SCHEMA 70300
+
static char *config = "cel_pgsql.conf";
static char *pghostname;
@@ -69,8 +71,10 @@ static char *pgpassword;
static char *pgappname;
static char *pgdbport;
static char *table;
+static char *schema;
static int connected = 0;
+/* Optimization to reduce number of memory allocations */
static int maxsize = 512, maxsize2 = 512;
static int usegmtime = 0;
@@ -201,7 +205,7 @@ static void pgsql_log(struct ast_event *event)
AST_RWLIST_RDLOCK(&psql_columns);
AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
LENGTHEN_BUF1(strlen(cur->name) + 2);
- ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
+ ast_str_append(&sql, 0, "%s\"%s\"", SEP, cur->name);
if (strcmp(cur->name, "eventtime") == 0) {
if (strncmp(cur->type, "int", 3) == 0) {
@@ -372,6 +376,14 @@ static void pgsql_log(struct ast_event *event)
}
PQclear(result);
+ /* Next time, just allocate buffers that are that big to start with. */
+ if (ast_str_strlen(sql) > maxsize) {
+ maxsize = ast_str_strlen(sql);
+ }
+ if (ast_str_strlen(sql2) > maxsize2) {
+ maxsize2 = ast_str_strlen(sql2);
+ }
+
ast_log_cleanup:
ast_free(sql);
ast_free(sql2);
@@ -418,6 +430,10 @@ static int my_unload_module(void)
ast_free(table);
table = NULL;
}
+ if (schema) {
+ ast_free(schema);
+ schema = NULL;
+ }
while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) {
ast_free(current);
}
@@ -521,6 +537,16 @@ static int process_my_load_module(struct ast_config *cfg)
} else {
usegmtime = 0;
}
+ if (!(tmp = ast_variable_retrieve(cfg, "global", "schema"))) {
+ tmp = "";
+ }
+ if (schema) {
+ ast_free(schema);
+ }
+ if (!(schema = ast_strdup(tmp))) {
+ ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying schema info\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
if (option_debug) {
if (ast_strlen_zero(pghostname)) {
ast_debug(3, "cel_pgsql: using default unix socket\n");
@@ -538,23 +564,50 @@ static int process_my_load_module(struct ast_config *cfg)
pgsql_reconnect();
if (PQstatus(conn) != CONNECTION_BAD) {
- char sqlcmd[512];
- char *fname, *ftype, *flen, *fnotnull, *fdef;
- char *tableptr;
- int i, rows;
+ char sqlcmd[768];
+ char *fname, *ftype, *flen, *fnotnull, *fdef, *tablename, *tmp_tablename;
+ int i, rows, version;
ast_debug(1, "Successfully connected to PostgreSQL database.\n");
connected = 1;
+ version = PQserverVersion(conn);
/* Remove any schema name from the table */
- if ((tableptr = strrchr(table, '.'))) {
- tableptr++;
+ if ((tmp_tablename = strrchr(table, '.'))) {
+ tmp_tablename++;
} else {
- tableptr = table;
+ tmp_tablename = table;
+ }
+ tablename = ast_alloca(strlen(tmp_tablename) * 2 + 1);
+ PQescapeStringConn(conn, tablename, tmp_tablename, strlen(tmp_tablename), NULL);
+ if (version >= PGSQL_MIN_VERSION_SCHEMA) {
+ char *schemaname;
+ int lenschema;
+ lenschema = strlen(schema);
+ schemaname = ast_alloca(lenschema * 2 + 1);
+ PQescapeStringConn(conn, schemaname, schema, lenschema, NULL);
+
+ snprintf(sqlcmd, sizeof(sqlcmd),
+ "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod "
+ "FROM (((pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace "
+ "AND c.relname = '%s' AND n.nspname = %s%s%s) "
+ "INNER JOIN pg_catalog.pg_attribute a ON ("
+ "NOT a.attisdropped) AND a.attnum > 0 AND a.attrelid = c.oid) "
+ "INNER JOIN pg_catalog.pg_type t ON t.oid = a.atttypid) "
+ "LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid "
+ "AND d.adnum = a.attnum "
+ "ORDER BY n.nspname, c.relname, attnum",
+ tablename,
+ lenschema == 0 ? "" : "'", lenschema == 0 ? "current_schema()" : schemaname, lenschema == 0 ? "" : "'");
+ } else {
+ snprintf(sqlcmd, sizeof(sqlcmd),
+ "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod "
+ "FROM pg_class c, pg_type t, pg_attribute a "
+ "LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid "
+ "AND d.adnum = a.attnum WHERE c.oid = a.attrelid AND a.atttypid = t.oid "
+ "AND (a.attnum > 0) AND c.relname = '%s' ORDER BY c.relname, attnum", tablename);
}
-
/* Query the columns */
- snprintf(sqlcmd, sizeof(sqlcmd), "select a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc from pg_class c, pg_type t, pg_attribute a left outer join pg_attrdef d on a.atthasdef and d.adrelid = a.attrelid and d.adnum = a.attnum where c.oid = a.attrelid and a.atttypid = t.oid and (a.attnum > 0) and c.relname = '%s' order by c.relname, attnum", tableptr);
result = PQexec(conn, sqlcmd);
if (PQresultStatus(result) != PGRES_TUPLES_OK) {
pgerror = PQresultErrorMessage(result);
diff --git a/channels/chan_alsa.c b/channels/chan_alsa.c
index 755936367..9441800db 100644
--- a/channels/chan_alsa.c
+++ b/channels/chan_alsa.c
@@ -1046,8 +1046,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "ALSA Console Channel Driver",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DRIVER,
+);
diff --git a/channels/chan_bridge_media.c b/channels/chan_bridge_media.c
index 0a3468a12..8e177ae0c 100644
--- a/channels/chan_bridge_media.c
+++ b/channels/chan_bridge_media.c
@@ -216,6 +216,6 @@ static int load_module(void)
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Bridge Media Channel Driver",
.support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
+ .load = load_module,
+ .unload = unload_module,
);
diff --git a/channels/chan_console.c b/channels/chan_console.c
index 4fc538b77..a8dbc9751 100644
--- a/channels/chan_console.c
+++ b/channels/chan_console.c
@@ -1578,9 +1578,9 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Console Channel Driver",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CHANNEL_DRIVER,
);
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index 0b187dc32..fe613097c 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -600,14 +600,6 @@ static int restart_monitor(void);
static int dahdi_sendtext(struct ast_channel *c, const char *text);
-static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
-{
- /* This module does not handle MWI in an event-based manner. However, it
- * subscribes to MWI for each mailbox that is configured so that the core
- * knows that we care about it. Then, chan_dahdi will get the MWI from the
- * event cache instead of checking the mailbox directly. */
-}
-
/*! \brief Avoid the silly dahdi_getevent which ignores a bunch of events */
static inline int dahdi_get_event(int fd)
{
@@ -12593,7 +12585,11 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
mailbox_specific_topic = ast_mwi_topic(tmp->mailbox);
if (mailbox_specific_topic) {
- tmp->mwi_event_sub = stasis_subscribe_pool(mailbox_specific_topic, mwi_event_cb, NULL);
+ /* This module does not handle MWI in an event-based manner. However, it
+ * subscribes to MWI for each mailbox that is configured so that the core
+ * knows that we care about it. Then, chan_dahdi will get the MWI from the
+ * event cache instead of checking the mailbox directly. */
+ tmp->mwi_event_sub = stasis_subscribe_pool(mailbox_specific_topic, stasis_subscription_cb_noop, NULL);
}
}
#ifdef HAVE_DAHDI_LINEREVERSE_VMWI
@@ -19753,4 +19749,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, tdesc,
.reload = reload,
.load_pri = AST_MODPRI_CHANNEL_DRIVER,
.nonoptreq = "res_smdi",
- );
+);
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index bb9c52bb8..dbad79dcc 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -1430,13 +1430,6 @@ static int iax2_is_control_frame_allowed(int subtype)
return is_allowed;
}
-static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
-{
- /* The MWI subscriptions exist just so the core knows we care about those
- * mailboxes. However, we just grab the events out of the cache when it
- * is time to send MWI, since it is only sent with a REGACK. */
-}
-
static void network_change_stasis_subscribe(void)
{
if (!network_change_sub) {
@@ -13010,7 +13003,10 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
mailbox_specific_topic = ast_mwi_topic(peer->mailbox);
if (mailbox_specific_topic) {
- peer->mwi_event_sub = stasis_subscribe_pool(mailbox_specific_topic, mwi_event_cb, NULL);
+ /* The MWI subscriptions exist just so the core knows we care about those
+ * mailboxes. However, we just grab the events out of the cache when it
+ * is time to send MWI, since it is only sent with a REGACK. */
+ peer->mwi_event_sub = stasis_subscribe_pool(mailbox_specific_topic, stasis_subscription_cb_noop, NULL);
}
}
@@ -15150,10 +15146,10 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Inter Asterisk eXchange (Ver 2)",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
- .nonoptreq = "res_crypto",
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CHANNEL_DRIVER,
+ .nonoptreq = "res_crypto",
+);
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index 37935be4b..16d3c6550 100644
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -489,14 +489,6 @@ static struct ast_channel_tech mgcp_tech = {
.func_channel_read = acf_channel_read,
};
-static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
-{
- /* This module does not handle MWI in an event-based manner. However, it
- * subscribes to MWI for each mailbox that is configured so that the core
- * knows that we care about it. Then, chan_mgcp will get the MWI from the
- * event cache instead of checking the mailbox directly. */
-}
-
static int has_voicemail(struct mgcp_endpoint *p)
{
int new_msgs;
@@ -4249,7 +4241,11 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
mailbox_specific_topic = ast_mwi_topic(e->mailbox);
if (mailbox_specific_topic) {
- e->mwi_event_sub = stasis_subscribe_pool(mailbox_specific_topic, mwi_event_cb, NULL);
+ /* This module does not handle MWI in an event-based manner. However, it
+ * subscribes to MWI for each mailbox that is configured so that the core
+ * knows that we care about it. Then, chan_mgcp will get the MWI from the
+ * event cache instead of checking the mailbox directly. */
+ e->mwi_event_sub = stasis_subscribe_pool(mailbox_specific_topic, stasis_subscription_cb_noop, NULL);
}
}
snprintf(e->rqnt_ident, sizeof(e->rqnt_ident), "%08lx", (unsigned long)ast_random());
@@ -5025,10 +5021,10 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Media Gateway Control Protocol (MGCP)",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
- .nonoptreq = "res_pktccops",
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CHANNEL_DRIVER,
+ .nonoptreq = "res_pktccops",
+);
diff --git a/channels/chan_motif.c b/channels/chan_motif.c
index 05c6944ef..0851bf01a 100644
--- a/channels/chan_motif.c
+++ b/channels/chan_motif.c
@@ -2819,9 +2819,9 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Motif Jingle Channel Driver",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CHANNEL_DRIVER,
+);
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 07dd1b7b4..14ba4a298 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -1864,6 +1864,7 @@ static int request(void *obj)
if (ast_strlen_zero(endpoint_name)) {
ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name\n");
req_data->cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
+ return -1;
} else if (!(endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
ast_log(LOG_ERROR, "Unable to create PJSIP channel - endpoint '%s' was not found\n", endpoint_name);
req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;
@@ -2421,8 +2422,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Channel Driver",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DRIVER,
+);
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 528e208d9..7c4c8a611 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -5008,7 +5008,7 @@ static void register_peer_exten(struct sip_peer *peer, int onoff)
static void destroy_mailbox(struct sip_mailbox *mailbox)
{
if (mailbox->event_sub) {
- mailbox->event_sub = stasis_unsubscribe(mailbox->event_sub);
+ mailbox->event_sub = stasis_unsubscribe_and_join(mailbox->event_sub);
}
ast_free(mailbox);
}
@@ -34758,10 +34758,10 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Session Initiation Protocol (SIP)",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
- .nonoptreq = "res_crypto,res_http_websocket",
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CHANNEL_DRIVER,
+ .nonoptreq = "res_crypto,res_http_websocket",
+);
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index 1d7d65af4..03da0e0d8 100644
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -8756,7 +8756,7 @@ static int unload_module(void)
skinny_unlocksub(sub);
}
if (l->mwi_event_sub) {
- l->mwi_event_sub = stasis_unsubscribe(l->mwi_event_sub);
+ l->mwi_event_sub = stasis_unsubscribe_and_join(l->mwi_event_sub);
}
ast_mutex_unlock(&l->lock);
unregister_exten(l);
@@ -8793,9 +8793,9 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Skinny Client Control Protocol (Skinny)",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CHANNEL_DRIVER,
);
diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c
index 82b3fced9..a3c0ffd5c 100644
--- a/channels/chan_unistim.c
+++ b/channels/chan_unistim.c
@@ -7178,7 +7178,7 @@ int reload(void)
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "UNISTIM Protocol (USTM)",
.support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
);
diff --git a/channels/sig_pri.c b/channels/sig_pri.c
index c58a3f358..b009c4520 100644
--- a/channels/sig_pri.c
+++ b/channels/sig_pri.c
@@ -7090,10 +7090,11 @@ static void *pri_dchannel(void *vpri)
break;
}
if (pri->pvts[chanpos]->owner) {
- int do_hangup = 0;
-
snprintf(cause_str, sizeof(cause_str), "PRI PRI_EVENT_HANGUP (%d)", e->hangup.cause);
pri_queue_pvt_cause_data(pri, chanpos, cause_str, e->hangup.cause);
+ }
+ if (pri->pvts[chanpos]->owner) {
+ int do_hangup = 0;
/* Queue a BUSY instead of a hangup if our cause is appropriate */
ast_channel_hangupcause_set(pri->pvts[chanpos]->owner, e->hangup.cause);
@@ -7234,10 +7235,11 @@ static void *pri_dchannel(void *vpri)
break;
}
if (pri->pvts[chanpos]->owner) {
- int do_hangup = 0;
-
snprintf(cause_str, sizeof(cause_str), "PRI PRI_EVENT_HANGUP_REQ (%d)", e->hangup.cause);
pri_queue_pvt_cause_data(pri, chanpos, cause_str, e->hangup.cause);
+ }
+ if (pri->pvts[chanpos]->owner) {
+ int do_hangup = 0;
ast_channel_hangupcause_set(pri->pvts[chanpos]->owner, e->hangup.cause);
switch (ast_channel_state(pri->pvts[chanpos]->owner)) {
@@ -8976,7 +8978,7 @@ void sig_pri_stop_pri(struct sig_pri_span *pri)
#if defined(HAVE_PRI_MWI)
for (idx = 0; idx < ARRAY_LEN(pri->mbox); ++idx) {
if (pri->mbox[idx].sub) {
- pri->mbox[idx].sub = stasis_unsubscribe(pri->mbox[idx].sub);
+ pri->mbox[idx].sub = stasis_unsubscribe_and_join(pri->mbox[idx].sub);
}
}
#endif /* defined(HAVE_PRI_MWI) */
diff --git a/codecs/codec_adpcm.c b/codecs/codec_adpcm.c
index d46230971..fa5d8769c 100644
--- a/codecs/codec_adpcm.c
+++ b/codecs/codec_adpcm.c
@@ -355,7 +355,7 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Adaptive Differential PCM Coder/Decoder",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/codecs/codec_alaw.c b/codecs/codec_alaw.c
index 21ca5c89d..515835dd7 100644
--- a/codecs/codec_alaw.c
+++ b/codecs/codec_alaw.c
@@ -139,7 +139,7 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "A-law Coder/Decoder",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/codecs/codec_dahdi.c b/codecs/codec_dahdi.c
index 69f6f32fe..baa73f229 100644
--- a/codecs/codec_dahdi.c
+++ b/codecs/codec_dahdi.c
@@ -873,8 +873,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Generic DAHDI Transcoder Codec Translator",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
diff --git a/codecs/codec_g722.c b/codecs/codec_g722.c
index fdae2a7d3..d368c38af 100644
--- a/codecs/codec_g722.c
+++ b/codecs/codec_g722.c
@@ -250,7 +250,7 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ITU G.722-64kbps G722 Transcoder",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/codecs/codec_g726.c b/codecs/codec_g726.c
index 0f9d33c01..8b44cbbd9 100644
--- a/codecs/codec_g726.c
+++ b/codecs/codec_g726.c
@@ -899,7 +899,7 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ITU G.726-32kbps G726 Transcoder",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/codecs/codec_gsm.c b/codecs/codec_gsm.c
index 92ccd8257..4660048c8 100644
--- a/codecs/codec_gsm.c
+++ b/codecs/codec_gsm.c
@@ -237,7 +237,7 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "GSM Coder/Decoder",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/codecs/codec_lpc10.c b/codecs/codec_lpc10.c
index b560e62d2..49df8f753 100644
--- a/codecs/codec_lpc10.c
+++ b/codecs/codec_lpc10.c
@@ -266,7 +266,7 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "LPC10 2.4kbps Coder/Decoder",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/codecs/codec_speex.c b/codecs/codec_speex.c
index f7b6046dc..c61f7c4f4 100644
--- a/codecs/codec_speex.c
+++ b/codecs/codec_speex.c
@@ -625,8 +625,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Speex Coder/Decoder",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
diff --git a/codecs/codec_ulaw.c b/codecs/codec_ulaw.c
index e75e29b10..ca2f411f9 100644
--- a/codecs/codec_ulaw.c
+++ b/codecs/codec_ulaw.c
@@ -190,7 +190,7 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "mu-Law Coder/Decoder",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/codecs/lpc10/lpcini.c b/codecs/lpc10/lpcini.c
index ebe229a5c..ea68176e3 100644
--- a/codecs/lpc10/lpcini.c
+++ b/codecs/lpc10/lpcini.c
@@ -34,7 +34,8 @@ Some OSS fixes and a few lpc changes to make it actually work
-lf2c -lm (in that order)
*/
-#include <stdlib.h>
+#define ASTMM_LIBC ASTMM_REDIRECT
+#include "asterisk.h"
#include "f2c.h"
#ifdef P_R_O_T_O_T_Y_P_E_S
diff --git a/configs/basic-pbx/extensions.conf b/configs/basic-pbx/extensions.conf
index d7fd2c3ca..e1da26185 100644
--- a/configs/basic-pbx/extensions.conf
+++ b/configs/basic-pbx/extensions.conf
@@ -145,9 +145,9 @@ exten = _25655511XX,1,Verbose(1, "External caller dialed inbound to DID ${EXTEN}
[Main-IVR]
exten = 2565551100,1,Verbose(1, "New caller, ${CALLERID(num)} dialed into the IVR.")
same = n,Answer()
- same = n(start),Background(demo-congrats)
+ same = n(start),Background(basic-pbx-ivr-main)
same = n,WaitExten(10)
- same = n,Background(demo-congrats)
+ same = n,Background(basic-pbx-ivr-main)
same = n,Hangup()
exten = 0,1,Verbose(1, "Caller ${CALLERID(num)} dialed the operator.")
diff --git a/configs/samples/cdr_adaptive_odbc.conf.sample b/configs/samples/cdr_adaptive_odbc.conf.sample
index f3c806e75..58a596649 100644
--- a/configs/samples/cdr_adaptive_odbc.conf.sample
+++ b/configs/samples/cdr_adaptive_odbc.conf.sample
@@ -57,4 +57,7 @@
; for this is to allow different sections to specify different values for
; a certain named column, presumably separated by filters.
;static "Some Special Value" => identifier_code
+;
+; Add quoted indentifiers for table and columns names.
+;quoted_identifiers=" ; Default to null
diff --git a/configs/samples/cel_pgsql.conf.sample b/configs/samples/cel_pgsql.conf.sample
index 30c789e4c..13fe06926 100644
--- a/configs/samples/cel_pgsql.conf.sample
+++ b/configs/samples/cel_pgsql.conf.sample
@@ -68,4 +68,6 @@
;password=password
;user=postgres
;table=cel ;SQL table where CEL's will be inserted
+;schema=public ;Schema where CEL's table is located. Optional parameter.
+ ;If schema support is present the default value used will be current_schema().
;appname=asterisk ; Postgres application_name support (optional). Whitespace not allowed.
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index 5e3757175..276e214e9 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -765,7 +765,13 @@
; (default: "")
;cert_file= ; Certificate file for endpoint TLS ONLY
; Will read .crt or .pem file but only uses cert,
- ; a .key file must be specified via priv_key_file
+ ; a .key file must be specified via priv_key_file.
+ ; Since PJProject version 2.5: If the file name ends in _rsa,
+ ; for example "asterisk_rsa.pem", the files "asterisk_dsa.pem"
+ ; and/or "asterisk_ecc.pem" are loaded (certificate, inter-
+ ; mediates, private key), to support multiple algorithms for
+ ; server authentication (RSA, DSA, ECDSA). If the chains are
+ ; different, at least OpenSSL 1.0.2 is required.
; (default: "")
;cipher= ; Preferred cryptography cipher names TLS ONLY (default: "")
;domain= ; Domain the transport comes from (default: "")
diff --git a/configs/samples/sip.conf.sample b/configs/samples/sip.conf.sample
index e52fa6db2..71e3fb72b 100644
--- a/configs/samples/sip.conf.sample
+++ b/configs/samples/sip.conf.sample
@@ -561,7 +561,12 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
;------------------------ TLS settings ------------------------------------------------------------
;tlscertfile=</path/to/certificate.pem> ; Certificate chain (*.pem format only) to use for TLS connections
; The certificates must be sorted starting with the subject's certificate
- ; and followed by intermediate CA certificates if applicable.
+ ; and followed by intermediate CA certificates if applicable. If the
+ ; file name ends in _rsa, for example "asterisk_rsa.pem", the files
+ ; "asterisk_dsa.pem" and/or "asterisk_ecc.pem" are loaded
+ ; (certificate, intermediates, private key), to support multiple
+ ; algorithms for server authentication (RSA, DSA, ECDSA). If the chains
+ ; are different, at least OpenSSL 1.0.2 is required.
; Default is to look for "asterisk.pem" in current directory
;tlsprivatekey=</path/to/private.pem> ; Private key file (*.pem format only) for TLS connections.
diff --git a/contrib/scripts/get_mp3_source.sh b/contrib/scripts/get_mp3_source.sh
index 860e2bc02..6d984659d 100755
--- a/contrib/scripts/get_mp3_source.sh
+++ b/contrib/scripts/get_mp3_source.sh
@@ -7,8 +7,8 @@ if [ -f addons/mp3/mpg123.h ]; then
echo "***"
# Manually patch interface.c if not done yet.
- if ! grep -q WRAP_LIBC_MALLOC addons/mp3/interface.c; then
- sed -i -e '/#include "asterisk.h"/i#define WRAP_LIBC_MALLOC' \
+ if ! grep -q ASTMM_LIBC addons/mp3/interface.c; then
+ sed -i -e '/#include "asterisk.h"/i#define ASTMM_LIBC ASTMM_REDIRECT' \
addons/mp3/interface.c
fi
@@ -18,8 +18,8 @@ fi
svn export http://svn.digium.com/svn/thirdparty/mp3/trunk addons/mp3 $@
# Manually patch interface.c if not done yet.
-if ! grep -q WRAP_LIBC_MALLOC addons/mp3/interface.c; then
- sed -i -e '/#include "asterisk.h"/i#define WRAP_LIBC_MALLOC' \
+if ! grep -q ASTMM_LIBC addons/mp3/interface.c; then
+ sed -i -e '/#include "asterisk.h"/i#define ASTMM_LIBC ASTMM_REDIRECT' \
addons/mp3/interface.c
fi
diff --git a/doc/.gitignore b/doc/.gitignore
index e098ec027..3461c58c5 100644
--- a/doc/.gitignore
+++ b/doc/.gitignore
@@ -1,2 +1,4 @@
core-en_US.xml
-rest-api \ No newline at end of file
+rest-api
+api
+asterisk-ng-doxygen
diff --git a/contrib/asterisk-ng-doxygen b/doc/asterisk-ng-doxygen.in
index 51a3f5d58..51a3f5d58 100644
--- a/contrib/asterisk-ng-doxygen
+++ b/doc/asterisk-ng-doxygen.in
diff --git a/doc/asterisk.8 b/doc/asterisk.8
index d566480f2..3f49a3401 100644
--- a/doc/asterisk.8
+++ b/doc/asterisk.8
@@ -232,9 +232,8 @@ then terminating when the command execution completes. Implies
supplied.
.TP
\-X
-Enables executing of includes via \fB#exec\fR directive.
-This can be useful if You want to do \fB#exec\fR inside
-\*(T<\fIasterisk.conf\fR\*(T>
+Enables executing of includes via \fB#exec\fR directive inside
+\*(T<\fIasterisk.conf\fR\*(T>.
.SH EXAMPLES
\fBasterisk\fR - Begin Asterisk as a daemon
.PP
diff --git a/funcs/func_aes.c b/funcs/func_aes.c
index 52fddb67e..9347b6f8e 100644
--- a/funcs/func_aes.c
+++ b/funcs/func_aes.c
@@ -179,8 +179,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "AES dialplan functions",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .nonoptreq = "res_crypto",
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .nonoptreq = "res_crypto",
+);
diff --git a/funcs/func_curl.c b/funcs/func_curl.c
index ed5f12f58..fd03fc375 100644
--- a/funcs/func_curl.c
+++ b/funcs/func_curl.c
@@ -874,9 +874,9 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Load external URL",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_REALTIME_DEPEND2,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_REALTIME_DEPEND2,
+);
diff --git a/funcs/func_dialplan.c b/funcs/func_dialplan.c
index 70bb111b1..0a5220b09 100644
--- a/funcs/func_dialplan.c
+++ b/funcs/func_dialplan.c
@@ -198,4 +198,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Dialplan Context/Exte
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_APP_DEPEND,
- );
+);
diff --git a/funcs/func_odbc.c b/funcs/func_odbc.c
index 7d09ec5a7..4744ba626 100644
--- a/funcs/func_odbc.c
+++ b/funcs/func_odbc.c
@@ -1580,9 +1580,9 @@ reload_out:
/* XXX need to revise usecount - set if query_lock is set */
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
diff --git a/funcs/func_periodic_hook.c b/funcs/func_periodic_hook.c
index e62e660bf..bb0ee0db7 100644
--- a/funcs/func_periodic_hook.c
+++ b/funcs/func_periodic_hook.c
@@ -446,13 +446,9 @@ static struct ast_custom_function hook_function = {
.write = hook_write,
};
-static struct ast_context *func_periodic_hook_context;
-
static int unload_module(void)
{
- if (func_periodic_hook_context) {
- ast_context_destroy(func_periodic_hook_context, AST_MODULE);
- }
+ ast_context_destroy(NULL, AST_MODULE);
return ast_custom_function_unregister(&hook_function);
}
@@ -461,9 +457,7 @@ static int load_module(void)
{
int res;
- func_periodic_hook_context = ast_context_find_or_create(NULL, NULL,
- context_name, AST_MODULE);
- if (!func_periodic_hook_context) {
+ if (!ast_context_find_or_create(NULL, NULL, context_name, AST_MODULE)) {
ast_log(LOG_ERROR, "Failed to create %s dialplan context.\n", context_name);
return AST_MODULE_LOAD_DECLINE;
}
@@ -521,7 +515,7 @@ int AST_OPTIONAL_API_NAME(ast_beep_stop)(struct ast_channel *chan, const char *b
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Periodic dialplan hooks.",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/include/asterisk/astmm.h b/include/asterisk/astmm.h
index 6086509ab..6c9a8aeee 100644
--- a/include/asterisk/astmm.h
+++ b/include/asterisk/astmm.h
@@ -44,16 +44,6 @@ extern "C" {
#include <stdio.h>
#include <stdarg.h>
-/* Undefine any macros */
-#undef malloc
-#undef calloc
-#undef realloc
-#undef strdup
-#undef strndup
-#undef asprintf
-#undef vasprintf
-#undef free
-
void *ast_std_malloc(size_t size);
void *ast_std_calloc(size_t nmemb, size_t size);
void *ast_std_realloc(void *ptr, size_t size);
@@ -74,9 +64,72 @@ int __ast_vasprintf(char **strp, const char *format, va_list ap, const char *fil
void __ast_mm_init_phase_1(void);
void __ast_mm_init_phase_2(void);
-/* Redefine libc malloc to our own versions */
+/*!
+ * \brief ASTMM_LIBC can be defined to control the meaning of standard allocators.
+ *
+ * \note The standard allocators effected by this compiler define are:
+ * malloc, calloc, realloc, strdup, strndup, asprintf, vasprintf and free.
+ *
+ * @{
+ */
+
+/*!
+ * \brief Produce compiler errors if standard allocators are used.
+ *
+ * \note This is the default option, and in most cases the correct option.
+ * Any use of standard allocators will cause an error, even if those uses
+ * are in unused static inline header functions.
+ */
+#define ASTMM_BLOCK 0
+
+/*!
+ * \brief Redirect standard allocators to use Asterisk functions.
+ *
+ * \note This option is used in some cases instead of changing the
+ * existing source to use Asterisk functions. New code should
+ * generally avoid this option, except where it's needed to work
+ * with situations where switching the code is unreasonable, such
+ * as output from code generators that are hard coded to use
+ * standard functions.
+ */
+#define ASTMM_REDIRECT 1
+
+/*!
+ * \brief Standard allocators are used directly.
+ *
+ * \note This option is needed when including 3rd party headers with calls
+ * to standard allocators from inline functions. Using ASTMM_REDIRECT in
+ * this situation could result in an object being allocated by malloc and
+ * freed by ast_free, or the reverse.
+ */
+#define ASTMM_IGNORE 2
+
+/*!
+ * }@
+ */
+
+#if !defined(ASTMM_LIBC)
+/* BLOCK libc allocators by default. */
+#define ASTMM_LIBC ASTMM_BLOCK
+#endif
-#ifdef WRAP_LIBC_MALLOC
+#if ASTMM_LIBC == ASTMM_IGNORE
+/* Don't touch the libc functions. */
+#else
+
+/* Undefine any macros */
+#undef malloc
+#undef calloc
+#undef realloc
+#undef strdup
+#undef strndup
+#undef asprintf
+#undef vasprintf
+#undef free
+
+#if ASTMM_LIBC == ASTMM_REDIRECT
+
+/* Redefine libc functions to our own versions */
#define calloc(a,b) \
__ast_calloc(a,b,__FILE__, __LINE__, __PRETTY_FUNCTION__)
#define malloc(a) \
@@ -93,23 +146,31 @@ void __ast_mm_init_phase_2(void);
__ast_asprintf(__FILE__, __LINE__, __PRETTY_FUNCTION__, a, b, c)
#define vasprintf(a,b,c) \
__ast_vasprintf(a,b,c,__FILE__, __LINE__, __PRETTY_FUNCTION__)
-#else
+
+#elif ASTMM_LIBC == ASTMM_BLOCK
+
+/* Redefine libc functions to cause compile errors */
#define calloc(a,b) \
- Do_not_use_calloc__use_ast_calloc(a,b)
+ Do_not_use_calloc__use_ast_calloc->fail(a,b)
#define malloc(a) \
- Do_not_use_malloc__use_ast_malloc(a)
+ Do_not_use_malloc__use_ast_malloc->fail(a)
#define free(a) \
- Do_not_use_free__use_ast_free_or_ast_std_free_for_remotely_allocated_memory(a)
+ Do_not_use_free__use_ast_free_or_ast_std_free_for_remotely_allocated_memory->fail(a)
#define realloc(a,b) \
- Do_not_use_realloc__use_ast_realloc(a,b)
+ Do_not_use_realloc__use_ast_realloc->fail(a,b)
#define strdup(a) \
- Do_not_use_strdup__use_ast_strdup(a)
+ Do_not_use_strdup__use_ast_strdup->fail(a)
#define strndup(a,b) \
- Do_not_use_strndup__use_ast_strndup(a,b)
+ Do_not_use_strndup__use_ast_strndup->fail(a,b)
#define asprintf(a, b, c...) \
- Do_not_use_asprintf__use_ast_asprintf(a,b,c)
+ Do_not_use_asprintf__use_ast_asprintf->fail(a,b,c)
#define vasprintf(a,b,c) \
- Do_not_use_vasprintf__use_ast_vasprintf(a,b,c)
+ Do_not_use_vasprintf__use_ast_vasprintf->fail(a,b,c)
+
+#else
+#error "Unacceptable value for the macro ASTMM_LIBC"
+#endif
+
#endif
/* Provide our own definitions */
diff --git a/include/asterisk/astobj2.h b/include/asterisk/astobj2.h
index e02a4cd85..4a7aeeedd 100644
--- a/include/asterisk/astobj2.h
+++ b/include/asterisk/astobj2.h
@@ -612,6 +612,8 @@ void *__ao2_weakproxy_get_object(void *weakproxy, int flags,
* \retval 0 Success
* \retval -1 Failure
*
+ * \note Callbacks are run in the reverse order of subscriptions.
+ *
* \note This procedure will allow the same cb / data pair to be added to
* the same weakproxy multiple times.
*
diff --git a/include/asterisk/audiohook.h b/include/asterisk/audiohook.h
index 375b2dd9d..cae8cc071 100644
--- a/include/asterisk/audiohook.h
+++ b/include/asterisk/audiohook.h
@@ -63,6 +63,7 @@ enum ast_audiohook_flags {
AST_AUDIOHOOK_SMALL_QUEUE = (1 << 4),
AST_AUDIOHOOK_MUTE_READ = (1 << 5), /*!< audiohook should be mute frames read */
AST_AUDIOHOOK_MUTE_WRITE = (1 << 6), /*!< audiohook should be mute frames written */
+ AST_AUDIOHOOK_COMPATIBLE = (1 << 7), /*!< is the audiohook native slin compatible */
};
enum ast_audiohook_init_flags {
diff --git a/include/asterisk/config.h b/include/asterisk/config.h
index 6489344ad..bd268a333 100644
--- a/include/asterisk/config.h
+++ b/include/asterisk/config.h
@@ -335,6 +335,23 @@ const char *ast_variable_find(const struct ast_category *category, const char *v
const char *ast_variable_find_in_list(const struct ast_variable *list, const char *variable);
/*!
+ * \brief Gets the LAST occurrence of a variable from a variable list
+ *
+ * \param list The ast_variable list to search
+ * \param variable The name of the ast_variable you wish to fetch data for
+ *
+ * \details
+ * Iterates over a given ast_variable list to search for the last occurrence of an
+ * ast_variable entry with a name attribute matching the given name (variable).
+ * This is useful if the list has duplicate entries (such as in cases where entries
+ * are created by a template)
+ *
+ * \retval The variable value on success
+ * \retval NULL if unable to find it.
+ */
+const char *ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable);
+
+/*!
* \brief Retrieve a category if it exists
*
* \param config which config to use
diff --git a/include/asterisk/event.h b/include/asterisk/event.h
index 7eea0581d..dbc080da8 100644
--- a/include/asterisk/event.h
+++ b/include/asterisk/event.h
@@ -35,7 +35,7 @@
* modules in Asterisk.
* - CEL uses the \ref ast_event representation to pass information to registered
* backends.
- * - The \file res_corosync module publishes \ref ast_event representations of
+ * - The \file res_corosync.c module publishes \ref ast_event representations of
* information to other Asterisk instances in a cluster.
* - Security event represent their event types and data using this system.
* - Theoretically, any \ref stasis message can use this system to pass
diff --git a/include/asterisk/http_websocket.h b/include/asterisk/http_websocket.h
index 3e07e608b..5adc08925 100644
--- a/include/asterisk/http_websocket.h
+++ b/include/asterisk/http_websocket.h
@@ -68,6 +68,24 @@ struct ast_websocket_server;
struct ast_websocket;
/*!
+ * \brief Callback from the HTTP request attempting to establish a websocket connection
+ *
+ * This callback occurs when an HTTP request is made to establish a websocket connection.
+ * Implementers of \ref ast_websocket_protocol can use this to deny a request, or to
+ * set up application specific data before invocation of \ref ast_websocket_callback.
+ *
+ * \param ser The TCP/TLS session
+ * \param parameters Parameters extracted from the request URI
+ * \param headers Headers included in the request
+ *
+ * \retval 0 The session should be accepted
+ * \retval -1 The session should be rejected. Note that the caller must send an error
+ * response using \ref ast_http_error.
+ * \since 13.5.0
+ */
+typedef int (*ast_websocket_pre_callback)(struct ast_tcptls_session_instance *ser, struct ast_variable *parameters, struct ast_variable *headers);
+
+/*!
* \brief Callback for when a new connection for a sub-protocol is established
*
* \param session A WebSocket session structure
@@ -81,6 +99,32 @@ struct ast_websocket;
typedef void (*ast_websocket_callback)(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers);
/*!
+ * \brief A websocket protocol implementation
+ *
+ * Users of the Websocket API can register themselves as a websocket
+ * protocol. See \ref ast_websocket_add_protocol2 and \ref ast_websocket_server_add_protocol2.
+ * Simpler implementations may use only \ref ast_websocket_add_protocol and
+ * \ref ast_websocket_server_add_protocol.
+ *
+ * \since 13.5.0
+ */
+struct ast_websocket_protocol {
+ /*! \brief Name of the protocol */
+ char *name;
+/*!
+ * \brief Protocol version. This prevents dynamically loadable modules from registering
+ * if this struct is changed.
+ */
+#define AST_WEBSOCKET_PROTOCOL_VERSION 1
+ /*! \brief Protocol version. Should be set to /ref AST_WEBSOCKET_PROTOCOL_VERSION */
+ unsigned int version;
+ /*! \brief Callback called when a new session is attempted. Optional. */
+ ast_websocket_pre_callback session_attempted;
+ /* \brief Callback called when a new session is established. Mandatory. */
+ ast_websocket_callback session_established;
+};
+
+/*!
* \brief Creates a \ref websocket_server
*
* \retval New \ref websocket_server instance
@@ -98,6 +142,15 @@ AST_OPTIONAL_API(struct ast_websocket_server *, ast_websocket_server_create, (vo
AST_OPTIONAL_API(int, ast_websocket_uri_cb, (struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers), { return -1; });
/*!
+ * \brief Allocate a websocket sub-protocol instance
+ *
+ * \retval An instance of \ref ast_websocket_protocol on success
+ * \retval NULL on error
+ * \since 13.5.0
+ */
+AST_OPTIONAL_API(struct ast_websocket_protocol *, ast_websocket_sub_protocol_alloc, (const char *name), {return NULL;});
+
+/*!
* \brief Add a sub-protocol handler to the default /ws server
*
* \param name Name of the sub-protocol to register
@@ -109,10 +162,25 @@ AST_OPTIONAL_API(int, ast_websocket_uri_cb, (struct ast_tcptls_session_instance
AST_OPTIONAL_API(int, ast_websocket_add_protocol, (const char *name, ast_websocket_callback callback), {return -1;});
/*!
+ * \brief Add a sub-protocol handler to the default /ws server
+ *
+ * \param protocol The sub-protocol to register. Note that this must
+ * be allocated using /ref ast_websocket_sub_protocol_alloc.
+ *
+ * \note This method is reference stealing. It will steal the reference to \ref protocol
+ * on success.
+ *
+ * \retval 0 success
+ * \retval -1 if sub-protocol handler could not be registered
+ * \since 13.5.0
+ */
+AST_OPTIONAL_API(int, ast_websocket_add_protocol2, (struct ast_websocket_protocol *protocol), {return -1;});
+
+/*!
* \brief Remove a sub-protocol handler from the default /ws server.
*
* \param name Name of the sub-protocol to unregister
- * \param callback Callback that was previously registered with the sub-protocol
+ * \param callback Session Established callback that was previously registered with the sub-protocol
*
* \retval 0 success
* \retval -1 if sub-protocol was not found or if callback did not match
@@ -132,6 +200,22 @@ AST_OPTIONAL_API(int, ast_websocket_remove_protocol, (const char *name, ast_webs
AST_OPTIONAL_API(int, ast_websocket_server_add_protocol, (struct ast_websocket_server *server, const char *name, ast_websocket_callback callback), {return -1;});
/*!
+ * \brief Add a sub-protocol handler to the given server.
+ *
+ * \param server The server to add the sub-protocol to.
+ * \param protocol The sub-protocol to register. Note that this must
+ * be allocated using /ref ast_websocket_sub_protocol_alloc.
+ *
+ * \note This method is reference stealing. It will steal the reference to \ref protocol
+ * on success.
+ *
+ * \retval 0 success
+ * \retval -1 if sub-protocol handler could not be registered
+ * \since 13.5.0
+ */
+AST_OPTIONAL_API(int, ast_websocket_server_add_protocol2, (struct ast_websocket_server *server, struct ast_websocket_protocol *protocol), {return -1;});
+
+/*!
* \brief Remove a sub-protocol handler from the given server.
*
* \param name Name of the sub-protocol to unregister
diff --git a/include/asterisk/options.h b/include/asterisk/options.h
index a08b8638c..e2709f918 100644
--- a/include/asterisk/options.h
+++ b/include/asterisk/options.h
@@ -66,8 +66,6 @@ enum ast_option_flags {
AST_OPT_FLAG_CACHE_RECORD_FILES = (1 << 13),
/*! Display timestamp in CLI verbose output */
AST_OPT_FLAG_TIMESTAMP = (1 << 14),
- /*! Override config */
- AST_OPT_FLAG_OVERRIDE_CONFIG = (1 << 15),
/*! Reconnect */
AST_OPT_FLAG_RECONNECT = (1 << 16),
/*! Transmit Silence during Record() and DTMF Generation */
@@ -119,7 +117,6 @@ enum ast_option_flags {
#define ast_opt_dump_core ast_test_flag(&ast_options, AST_OPT_FLAG_DUMP_CORE)
#define ast_opt_cache_record_files ast_test_flag(&ast_options, AST_OPT_FLAG_CACHE_RECORD_FILES)
#define ast_opt_timestamp ast_test_flag(&ast_options, AST_OPT_FLAG_TIMESTAMP)
-#define ast_opt_override_config ast_test_flag(&ast_options, AST_OPT_FLAG_OVERRIDE_CONFIG)
#define ast_opt_reconnect ast_test_flag(&ast_options, AST_OPT_FLAG_RECONNECT)
#define ast_opt_transmit_silence ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSMIT_SILENCE)
#define ast_opt_dont_warn ast_test_flag(&ast_options, AST_OPT_FLAG_DONT_WARN)
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 67c9c4b16..bd56c1952 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -178,7 +178,9 @@ struct ast_sip_contact {
enum ast_sip_contact_status_type {
UNAVAILABLE,
AVAILABLE,
- UNKNOWN
+ UNKNOWN,
+ CREATED,
+ REMOVED,
};
/*!
@@ -1294,6 +1296,13 @@ int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg,
*
* \retval 0 Success
* \retval -1 Failure (out-of-dialog callback will not be called.)
+ *
+ * \note Timeout processing:
+ * There are 2 timers associated with this request, PJSIP timer_b which is
+ * set globally in the "system" section of pjsip.conf, and the timeout specified
+ * on this call. The timer that expires first (before normal completion) will
+ * cause the callback to be run with e->body.tsx_state.type = PJSIP_EVENT_TIMER.
+ * The timer that expires second is simply ignored and the callback is not run again.
*/
int ast_sip_send_out_of_dialog_request(pjsip_tx_data *tdata,
struct ast_sip_endpoint *endpoint, int timeout, void *token,
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index a94cb4213..c7f6511f9 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -2316,7 +2316,7 @@ struct stasis_message_type *ast_rtp_rtcp_received_type(void);
*/
struct stasis_topic *ast_rtp_topic(void);
-/* }@ */
+/* @} */
#if defined(__cplusplus) || defined(c_plusplus)
}
diff --git a/include/asterisk/sem.h b/include/asterisk/sem.h
index 6d655d63e..fcab82a5e 100644
--- a/include/asterisk/sem.h
+++ b/include/asterisk/sem.h
@@ -20,7 +20,9 @@
#define ASTERISK_SEMAPHORE_H
/*!
- * \file Asterisk semaphore API
+ * \file
+ *
+ * \brief Asterisk semaphore API
*
* This API is a thin wrapper around the POSIX semaphore API (when available),
* so see the POSIX documentation for further details.
diff --git a/include/asterisk/slin.h b/include/asterisk/slin.h
index 148ee09ab..976637473 100644
--- a/include/asterisk/slin.h
+++ b/include/asterisk/slin.h
@@ -62,7 +62,7 @@ static inline struct ast_frame *slin8_sample(void)
{
static struct ast_frame f = {
.frametype = AST_FRAME_VOICE,
- .datalen = sizeof(ex_slin8) * 2,
+ .datalen = sizeof(ex_slin8),
.samples = ARRAY_LEN(ex_slin8),
.mallocd = 0,
.offset = 0,
@@ -79,7 +79,7 @@ static inline struct ast_frame *slin16_sample(void)
{
static struct ast_frame f = {
.frametype = AST_FRAME_VOICE,
- .datalen = sizeof(ex_slin16) * 2,
+ .datalen = sizeof(ex_slin16),
.samples = ARRAY_LEN(ex_slin16),
.mallocd = 0,
.offset = 0,
diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h
index a5061c6ab..d2dc701d8 100644
--- a/include/asterisk/sorcery.h
+++ b/include/asterisk/sorcery.h
@@ -497,6 +497,125 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
#define ast_sorcery_apply_wizard_mapping(sorcery, type, name, data, caching) \
__ast_sorcery_apply_wizard_mapping((sorcery), (type), AST_MODULE, (name), (data), (caching));
+
+/*!
+ * \brief Pre-defined locations to insert at
+ */
+enum ast_sorcery_wizard_position {
+ AST_SORCERY_WIZARD_POSITION_LAST = -1,
+ AST_SORCERY_WIZARD_POSITION_FIRST = 0,
+};
+
+/*!
+ * \brief Insert an additional object wizard mapping at a specific position
+ * in the wizard list
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to apply to
+ * \param module The name of the module, typically AST_MODULE
+ * \param name Name of the wizard to use
+ * \param data Data to be passed to wizard
+ * \param caching Wizard should cache
+ * \param position An index to insert to or one of ast_sorcery_wizard_position
+ *
+ * \return What occurred when applying the mapping
+ *
+ * \note This should be called *after* applying default mappings
+ * \note Wizards can be retrieved by using ast_sorcery_get_wizard_mapping_count
+ * and iterating over them using ast_sorcery_get_wizard_mapping.
+ *
+ * \since 13.4.0
+ */
+enum ast_sorcery_apply_result __ast_sorcery_insert_wizard_mapping(struct ast_sorcery *sorcery,
+ const char *type, const char *module, const char *name, const char *data,
+ unsigned int caching, int position);
+
+/*!
+ * \brief Insert an additional object wizard mapping at a specific position
+ * in the wizard list
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to apply to
+ * \param module The name of the module, typically AST_MODULE
+ * \param name Name of the wizard to use
+ * \param data Data to be passed to wizard
+ * \param position One of ast_sorcery_wizard_position
+ *
+ * \return What occurred when applying the mapping
+ *
+ * \note This should be called *after* applying default mappings
+ * \since 13.4.0
+ */
+#define ast_sorcery_insert_wizard_mapping(sorcery, type, name, data, caching, position) \
+ __ast_sorcery_insert_wizard_mapping((sorcery), (type), AST_MODULE, (name), (data), \
+ (caching), (position))
+
+/*!
+ * \brief Remove an object wizard mapping
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to remove from
+ * \param module The name of the module, typically AST_MODULE
+ * \param name The name of the wizard to remove
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \since 13.4.0
+ */
+int __ast_sorcery_remove_wizard_mapping(struct ast_sorcery *sorcery,
+ const char *type, const char *module, const char *name);
+
+/*!
+ * \brief Remove an object wizard mapping
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to remove from
+ * \param name The name of the wizard to remove
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \since 13.4.0
+ */
+#define ast_sorcery_remove_wizard_mapping(sorcery, type, name) \
+ __ast_sorcery_remove_wizard_mapping((sorcery), (type), AST_MODULE, (name))
+
+/*!
+ * \brief Return the number of wizards mapped to an object type
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ *
+ * \return Number of wizards or -1 for error
+ * \since 13.4.0
+ */
+int ast_sorcery_get_wizard_mapping_count(struct ast_sorcery *sorcery,
+ const char *type);
+
+/*!
+ * \brief By index, return a wizard mapped to an object type
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ * \param index Index of the wizard
+ * \param wizard A pointer to receive the wizard pointer
+ * \param data A pointer to receive the data pointer
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \warning The wizard will have its reference count bumped so you must
+ * call ao2_cleanup when you're done with it.
+ *
+ * \note The wizard and data returned are valid only for this object type
+ * and only while the wizard is applied to the object type.
+ *
+ * \since 13.4.0
+ */
+int ast_sorcery_get_wizard_mapping(struct ast_sorcery *sorcery,
+ const char *type, int index, struct ast_sorcery_wizard **wizard, void **data);
+
/*!
* \brief Register an object type
*
@@ -1160,6 +1279,15 @@ struct ast_sorcery_object_type *ast_sorcery_get_object_type(const struct ast_sor
int ast_sorcery_is_object_field_registered(const struct ast_sorcery_object_type *object_type,
const char *field_name);
+/*!
+ * \brief Get the module that has opened the provided sorcery instance.
+ *
+ * \param sorcery The sorcery instance
+ *
+ * \return The module
+ */
+const char *ast_sorcery_get_module(const struct ast_sorcery *sorcery);
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/asterisk/stasis.h b/include/asterisk/stasis.h
index 0b1b1e83f..69b2d0f22 100644
--- a/include/asterisk/stasis.h
+++ b/include/asterisk/stasis.h
@@ -173,8 +173,6 @@
#include "asterisk/utils.h"
#include "asterisk/event.h"
-/*! @{ */
-
/*!
* \brief Metadata about a \ref stasis_message.
* \since 12
@@ -451,10 +449,6 @@ struct ast_manager_event_blob *stasis_message_to_ami(
struct ast_event *stasis_message_to_event(
struct stasis_message *message);
-/*! @} */
-
-/*! @{ */
-
/*!
* \brief A topic to which messages may be posted, and subscribers, well, subscribe
* \since 12
@@ -508,10 +502,6 @@ void stasis_publish(struct stasis_topic *topic, struct stasis_message *message);
*/
void stasis_publish_sync(struct stasis_subscription *sub, struct stasis_message *message);
-/*! @} */
-
-/*! @{ */
-
/*!
* \brief Callback function type for Stasis subscriptions.
* \param data Data field provided with subscription.
@@ -521,6 +511,17 @@ void stasis_publish_sync(struct stasis_subscription *sub, struct stasis_message
typedef void (*stasis_subscription_cb)(void *data, struct stasis_subscription *sub, struct stasis_message *message);
/*!
+ * \brief Stasis subscription callback function that does nothing.
+ *
+ * \note This callback should be used for events are not directly processed, but need
+ * to be generated so data can be retrieved from cache later. Subscriptions with this
+ * callback can be released with \ref stasis_unsubscribe, even during module unload.
+ *
+ * \since 13.5
+ */
+void stasis_subscription_cb_noop(void *data, struct stasis_subscription *sub, struct stasis_message *message);
+
+/*!
* \brief Create a subscription.
*
* In addition to being AO2 managed memory (requiring an ao2_cleanup() to free
@@ -699,8 +700,6 @@ struct stasis_message_type *stasis_subscription_change_type(void);
/*! @} */
-/*! @{ */
-
/*!
* \brief Pool for topic aggregation
*/
@@ -723,8 +722,6 @@ struct stasis_topic_pool *stasis_topic_pool_create(struct stasis_topic *pooled_t
*/
struct stasis_topic *stasis_topic_pool_get_topic(struct stasis_topic_pool *pool, const char *topic_name);
-/*! @} */
-
/*! \addtogroup StasisTopicsAndMessages
* @{
*/
@@ -757,8 +754,6 @@ struct stasis_message_type *stasis_cache_clear_type(void);
/*! @} */
-/*! @{ */
-
/*!
* \brief A message cache, for use with \ref stasis_caching_topic.
* \since 12
@@ -1090,6 +1085,10 @@ struct ao2_container *stasis_cache_dump_by_eid(struct stasis_cache *cache, struc
*/
struct ao2_container *stasis_cache_dump_all(struct stasis_cache *cache, struct stasis_message_type *type);
+/*! \addtogroup StasisTopicsAndMessages
+ * @{
+ */
+
/*!
* \brief Object type code for multi user object snapshots
*/
@@ -1163,8 +1162,6 @@ void ast_multi_object_blob_single_channel_publish(struct ast_channel *chan, stru
/*! @} */
-/*! @{ */
-
/*!
* \internal
* \brief Log a message about invalid attempt to access a type.
@@ -1267,10 +1264,6 @@ void stasis_log_bad_type_access(const char *name);
_priv_ ## name = NULL; \
})
-/*! @} */
-
-/*! @{ */
-
/*!
* \brief Initialize the Stasis subsystem.
* \return 0 on success.
@@ -1279,10 +1272,6 @@ void stasis_log_bad_type_access(const char *name);
*/
int stasis_init(void);
-/*! @} */
-
-/*! @{ */
-
/*!
* \internal
* \brief called by stasis_init() for cache initialization.
@@ -1301,12 +1290,10 @@ int stasis_cache_init(void);
*/
int stasis_config_init(void);
-/*! @} */
-
/*!
* \defgroup StasisTopicsAndMessages Stasis topics, and their messages.
*
- * This group contains the topics, messages and corresponding message types
+ * \brief This group contains the topics, messages and corresponding message types
* found within Asterisk.
*/
diff --git a/include/asterisk/stasis_endpoints.h b/include/asterisk/stasis_endpoints.h
index 1d56a8fea..539f270cf 100644
--- a/include/asterisk/stasis_endpoints.h
+++ b/include/asterisk/stasis_endpoints.h
@@ -119,6 +119,12 @@ void ast_endpoint_blob_publish(struct ast_endpoint *endpoint, struct stasis_mess
struct stasis_message_type *ast_endpoint_state_type(void);
/*!
+ * \brief Message type for endpoint contact state changes.
+ * \since 13.5
+ */
+struct stasis_message_type *ast_endpoint_contact_state_type(void);
+
+/*!
* \brief Message type for \ref ast_endpoint_snapshot.
* \since 12
*/
diff --git a/include/asterisk/stasis_test.h b/include/asterisk/stasis_test.h
index ad4020a08..d9df1c97f 100644
--- a/include/asterisk/stasis_test.h
+++ b/include/asterisk/stasis_test.h
@@ -20,7 +20,8 @@
#define _ASTERISK_STASIS_TEST_H
/*!
- * \file \brief Test infrastructure for dealing with Stasis.
+ * \file
+ * \brief Test infrastructure for dealing with Stasis.
*
* \author David M. Lee, II <dlee@digium.com>
*
diff --git a/include/asterisk/tcptls.h b/include/asterisk/tcptls.h
index 0e8d9d042..a3f3f2884 100644
--- a/include/asterisk/tcptls.h
+++ b/include/asterisk/tcptls.h
@@ -65,6 +65,7 @@
#ifdef DO_SSL
#include <openssl/ssl.h>
#include <openssl/err.h>
+#include <openssl/x509v3.h>
#else
/* declare dummy types so we can define a pointer to them */
typedef struct {} SSL;
diff --git a/include/asterisk/vector.h b/include/asterisk/vector.h
index 255c30b43..0a13c560b 100644
--- a/include/asterisk/vector.h
+++ b/include/asterisk/vector.h
@@ -273,6 +273,36 @@
})
/*!
+ * \brief Add an element into a sorted vector
+ *
+ * \param vec Sorted vector to add to.
+ * \param elem Element to insert.
+ * \param cmp A strcmp compatible compare function.
+ *
+ * \return 0 on success.
+ * \return Non-zero on failure.
+ *
+ * \warning Use of this macro on an unsorted vector will produce unpredictable results
+ */
+#define AST_VECTOR_ADD_SORTED(vec, elem, cmp) ({ \
+ int res = 0; \
+ size_t __idx = (vec)->current; \
+ do { \
+ if (__make_room((vec)->current, vec) != 0) { \
+ res = -1; \
+ break; \
+ } \
+ while (__idx > 0 && (cmp((vec)->elems[__idx - 1], elem) > 0)) { \
+ (vec)->elems[__idx] = (vec)->elems[__idx - 1]; \
+ __idx--; \
+ } \
+ (vec)->elems[__idx] = elem; \
+ (vec)->current++; \
+ } while (0); \
+ res; \
+})
+
+/*!
* \brief Remove an element from a vector by index.
*
* Note that elements in the vector may be reordered, so that the remove can
@@ -280,35 +310,48 @@
*
* \param vec Vector to remove from.
* \param idx Index of the element to remove.
+ * \param preserve_order Preserve the vector order.
+ *
* \return The element that was removed.
*/
-#define AST_VECTOR_REMOVE_UNORDERED(vec, idx) ({ \
- typeof((vec)->elems[0]) res; \
- size_t __idx = (idx); \
- ast_assert(__idx < (vec)->current); \
- res = (vec)->elems[__idx]; \
- (vec)->elems[__idx] = (vec)->elems[--(vec)->current]; \
+#define AST_VECTOR_REMOVE(vec, idx, preserve_ordered) ({ \
+ typeof((vec)->elems[0]) res; \
+ size_t __idx = (idx); \
+ ast_assert(__idx < (vec)->current); \
+ res = (vec)->elems[__idx]; \
+ if ((preserve_ordered)) { \
+ size_t __move; \
+ __move = ((vec)->current - (__idx) - 1) * sizeof(typeof((vec)->elems[0])); \
+ memmove(&(vec)->elems[__idx], &(vec)->elems[__idx + 1], __move); \
+ (vec)->current--; \
+ } else { \
+ (vec)->elems[__idx] = (vec)->elems[--(vec)->current]; \
+ }; \
res; \
})
/*!
+ * \brief Remove an element from an unordered vector by index.
+ *
+ * Note that elements in the vector may be reordered, so that the remove can
+ * happen in constant time.
+ *
+ * \param vec Vector to remove from.
+ * \param idx Index of the element to remove.
+ * \return The element that was removed.
+ */
+#define AST_VECTOR_REMOVE_UNORDERED(vec, idx) \
+ AST_VECTOR_REMOVE(vec, idx, 0)
+
+/*!
* \brief Remove an element from a vector by index while maintaining order.
*
* \param vec Vector to remove from.
* \param idx Index of the element to remove.
* \return The element that was removed.
*/
-#define AST_VECTOR_REMOVE_ORDERED(vec, idx) ({ \
- typeof((vec)->elems[0]) res; \
- size_t __idx = (idx); \
- size_t __move; \
- ast_assert(__idx < (vec)->current); \
- res = (vec)->elems[__idx]; \
- __move = ((vec)->current - (__idx) - 1) * sizeof(typeof((vec)->elems[0])); \
- memmove(&(vec)->elems[__idx], &(vec)->elems[__idx + 1], __move); \
- (vec)->current--; \
- res; \
-})
+#define AST_VECTOR_REMOVE_ORDERED(vec, idx) \
+ AST_VECTOR_REMOVE(vec, idx, 1)
/*!
* \brief Remove an element from a vector that matches the given comparison
@@ -421,6 +464,17 @@
#define AST_VECTOR_SIZE(vec) (vec)->current
/*!
+ * \brief Reset vector.
+ *
+ * \param vec Vector to reset.
+ * \param callback A cleanup callback or AST_VECTOR_ELEM_CLEANUP_NOOP.
+ */
+#define AST_VECTOR_RESET(vec, cleanup) ({ \
+ AST_VECTOR_CALLBACK_VOID(vec, cleanup); \
+ (vec)->current = 0; \
+})
+
+/*!
* \brief Get an address of element in a vector.
*
* \param vec Vector to query.
@@ -508,6 +562,8 @@
* \brief Execute a callback on every element in a vector returning the matching
* elements in a new vector
*
+ * This macro basically provides a filtered clone.
+ *
* \param vec Vector to operate on.
* \param callback A callback that takes at least 1 argument (the element)
* plus number of optional arguments
diff --git a/main/ast_expr2.c b/main/ast_expr2.c
index 84a3d7b97..d41072d6e 100644
--- a/main/ast_expr2.c
+++ b/main/ast_expr2.c
@@ -91,7 +91,7 @@
* $FreeBSD: src/bin/expr/expr.y,v 1.16 2000/07/22 10:59:36 se Exp $
*/
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
#include "asterisk.h"
#include <sys/types.h>
diff --git a/main/ast_expr2.y b/main/ast_expr2.y
index 8a151679d..762e83d84 100644
--- a/main/ast_expr2.y
+++ b/main/ast_expr2.y
@@ -12,7 +12,7 @@
* $FreeBSD: src/bin/expr/expr.y,v 1.16 2000/07/22 10:59:36 se Exp $
*/
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
#include "asterisk.h"
#include <sys/types.h>
diff --git a/main/ast_expr2f.c b/main/ast_expr2f.c
index c2d4e00a4..c6a1b9735 100644
--- a/main/ast_expr2f.c
+++ b/main/ast_expr2f.c
@@ -1,4 +1,4 @@
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
#include "asterisk.h"
#line 2 "ast_expr2f.c"
diff --git a/main/asterisk.c b/main/asterisk.c
index 277604bf7..53bceadfd 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -1,7 +1,7 @@
/*
* Asterisk -- An open source telephony toolkit.
*
- * Copyright (C) 1999 - 2014, Digium, Inc.
+ * Copyright (C) 1999 - 2015, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
@@ -53,7 +53,7 @@
*
* \section copyright Copyright and Author
*
- * Copyright (C) 1999 - 2014, Digium, Inc.
+ * Copyright (C) 1999 - 2015, Digium, Inc.
* Asterisk is a <a href="http://www.digium.com/en/company/view-policy.php?id=Trademark-Policy">registered trademark</a>
* of <a rel="nofollow" href="http://www.digium.com">Digium, Inc</a>.
*
@@ -301,7 +301,7 @@ int daemon(int, int); /* defined in libresolv of all places */
/*! \brief Welcome message when starting a CLI interface */
#define WELCOME_MESSAGE \
- ast_verbose("Asterisk %s, Copyright (C) 1999 - 2014, Digium, Inc. and others.\n" \
+ ast_verbose("Asterisk %s, Copyright (C) 1999 - 2015, Digium, Inc. and others.\n" \
"Created by Mark Spencer <markster@digium.com>\n" \
"Asterisk comes with ABSOLUTELY NO WARRANTY; type 'core show warranty' for details.\n" \
"This is free software, with components licensed under the GNU General Public\n" \
@@ -3344,7 +3344,7 @@ static int show_version(void)
static int show_cli_help(void)
{
- printf("Asterisk %s, Copyright (C) 1999 - 2014, Digium, Inc. and others.\n", ast_get_version());
+ printf("Asterisk %s, Copyright (C) 1999 - 2015, Digium, Inc. and others.\n", ast_get_version());
printf("Usage: asterisk [OPTIONS]\n");
printf("Valid Options:\n");
printf(" -V Display version number and exit\n");
@@ -3352,7 +3352,7 @@ static int show_cli_help(void)
printf(" -G <group> Run as a group other than the caller\n");
printf(" -U <user> Run as a user other than the caller\n");
printf(" -c Provide console CLI\n");
- printf(" -d Enable extra debugging\n");
+ printf(" -d Increase debugging (multiple d's = more debugging)\n");
#if HAVE_WORKING_FORK
printf(" -f Do not fork\n");
printf(" -F Always fork\n");
@@ -3375,7 +3375,7 @@ static int show_cli_help(void)
printf(" of output to the CLI\n");
printf(" -v Increase verbosity (multiple v's = more verbose)\n");
printf(" -x <cmd> Execute command <cmd> (implies -r)\n");
- printf(" -X Execute includes by default (allows #exec in asterisk.conf)\n");
+ printf(" -X Enable use of #exec in asterisk.conf\n");
printf(" -W Adjust terminal colors to compensate for a light background\n");
printf("\n");
return 0;
@@ -3385,7 +3385,6 @@ static void ast_readconfig(void)
{
struct ast_config *cfg;
struct ast_variable *v;
- char *config = DEFAULT_CONFIG_FILE;
char hostname[MAXHOSTNAMELEN] = "";
struct ast_flags config_flags = { CONFIG_FLAG_NOREALTIME };
struct {
@@ -3394,19 +3393,12 @@ static void ast_readconfig(void)
} found = { 0, 0 };
/* Default to false for security */
int live_dangerously = 0;
+ int option_debug_new = 0;
+ int option_verbose_new = 0;
/* Set default value */
option_dtmfminduration = AST_MIN_DTMF_DURATION;
- if (ast_opt_override_config) {
- cfg = ast_config_load2(ast_config_AST_CONFIG_FILE, "" /* core, can't reload */, config_flags);
- if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
- fprintf(stderr, "Unable to open specified master config file '%s', using built-in defaults\n", ast_config_AST_CONFIG_FILE);
- }
- } else {
- cfg = ast_config_load2(config, "" /* core, can't reload */, config_flags);
- }
-
/* init with buildtime config */
ast_copy_string(cfg_paths.config_dir, DEFAULT_CONFIG_DIR, sizeof(cfg_paths.config_dir));
ast_copy_string(cfg_paths.spool_dir, DEFAULT_SPOOL_DIR, sizeof(cfg_paths.spool_dir));
@@ -3432,8 +3424,15 @@ static void ast_readconfig(void)
ast_set_default_eid(&ast_eid_default);
+ cfg = ast_config_load2(ast_config_AST_CONFIG_FILE, "" /* core, can't reload */, config_flags);
+
+ /* If AST_OPT_FLAG_EXEC_INCLUDES was previously enabled with -X turn it off now.
+ * Using #exec from other configs requires that it be enabled from asterisk.conf. */
+ ast_clear_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES);
+
/* no asterisk.conf? no problem, use buildtime config! */
if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
+ fprintf(stderr, "Unable to open specified master config file '%s', using built-in defaults\n", ast_config_AST_CONFIG_FILE);
return;
}
@@ -3487,7 +3486,7 @@ static void ast_readconfig(void)
for (v = ast_variable_browse(cfg, "options"); v; v = v->next) {
/* verbose level (-v at startup) */
if (!strcasecmp(v->name, "verbose")) {
- option_verbose = atoi(v->value);
+ option_verbose_new = atoi(v->value);
/* whether or not to force timestamping in CLI verbose output. (-T at startup) */
} else if (!strcasecmp(v->name, "timestamp")) {
ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TIMESTAMP);
@@ -3496,9 +3495,9 @@ static void ast_readconfig(void)
ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_EXEC_INCLUDES);
/* debug level (-d at startup) */
} else if (!strcasecmp(v->name, "debug")) {
- option_debug = 0;
- if (sscanf(v->value, "%30d", &option_debug) != 1) {
- option_debug = ast_true(v->value);
+ option_debug_new = 0;
+ if (sscanf(v->value, "%30d", &option_debug_new) != 1) {
+ option_debug_new = ast_true(v->value) ? 1 : 0;
}
} else if (!strcasecmp(v->name, "refdebug")) {
ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_REF_DEBUG);
@@ -3647,6 +3646,9 @@ static void ast_readconfig(void)
pbx_live_dangerously(live_dangerously);
}
+ option_debug += option_debug_new;
+ option_verbose += option_verbose_new;
+
ast_config_destroy(cfg);
}
@@ -3787,9 +3789,9 @@ int main(int argc, char *argv[])
int isroot = 1, rundir_exists = 0;
char *buf;
const char *runuser = NULL, *rungroup = NULL;
- char *remotesock = NULL;
int moduleresult; /*!< Result from the module load subsystem */
struct rlimit l;
+ static const char *getopt_settings = "BC:cde:FfG:ghIiL:M:mnpqRrs:TtU:VvWXx:";
/* Remember original args for restart */
if (argc > ARRAY_LEN(_argv) - 1) {
@@ -3813,11 +3815,57 @@ int main(int argc, char *argv[])
if (getenv("HOME"))
snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
+
+ /* Set config file to default before checking arguments for override. */
+ ast_copy_string(cfg_paths.config_file, DEFAULT_CONFIG_FILE, sizeof(cfg_paths.config_file));
+
+ /* Process command-line options that effect asterisk.conf load. */
+ while ((c = getopt(argc, argv, getopt_settings)) != -1) {
+ switch (c) {
+ case 'X':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES);
+ break;
+ case 'C':
+ ast_copy_string(cfg_paths.config_file, optarg, sizeof(cfg_paths.config_file));
+ break;
+ case 'd':
+ option_debug++;
+ break;
+ case 'h':
+ show_cli_help();
+ exit(0);
+ case 'R':
+ case 'r':
+ case 'x':
+ /* ast_opt_remote is checked during config load. This is only part of what
+ * these options do, see the second loop for the rest of the actions. */
+ ast_set_flag(&ast_options, AST_OPT_FLAG_REMOTE);
+ break;
+ case 'V':
+ show_version();
+ exit(0);
+ case 'v':
+ option_verbose++;
+ break;
+ case '?':
+ exit(1);
+ }
+ }
+
+ /* Initialize env so it is available if #exec is used in asterisk.conf. */
+ env_init();
+
+ ast_readconfig();
+
+ /* Update env to include any systemname that was set. */
+ env_init();
+
/*! \brief Check for options
*
* \todo Document these options
*/
- while ((c = getopt(argc, argv, "BC:cde:FfG:ghIiL:M:mnpqRrs:TtU:VvWXx:")) != -1) {
+ optind = 0;
+ while ((c = getopt(argc, argv, getopt_settings)) != -1) {
/*!\note Please keep the ordering here to alphabetical, capital letters
* first. This will make it easier in the future to select unused
* option flags for new features. */
@@ -3827,18 +3875,16 @@ int main(int argc, char *argv[])
ast_clear_flag(&ast_options, AST_OPT_FLAG_LIGHT_BACKGROUND);
break;
case 'X':
- ast_set_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES);
+ /* The command-line -X option enables #exec for asterisk.conf only. */
break;
case 'C':
- ast_copy_string(cfg_paths.config_file, optarg, sizeof(cfg_paths.config_file));
- ast_set_flag(&ast_options, AST_OPT_FLAG_OVERRIDE_CONFIG);
+ /* already processed. */
break;
case 'c':
ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_CONSOLE);
break;
case 'd':
- option_debug++;
- ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK);
+ /* already processed. */
break;
#if defined(HAVE_SYSINFO)
case 'e':
@@ -3862,8 +3908,8 @@ int main(int argc, char *argv[])
ast_set_flag(&ast_options, AST_OPT_FLAG_DUMP_CORE);
break;
case 'h':
- show_cli_help();
- exit(0);
+ /* already processed. */
+ break;
case 'I':
fprintf(stderr,
"NOTICE: The -I option is no longer needed.\n"
@@ -3901,7 +3947,9 @@ int main(int argc, char *argv[])
ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE);
break;
case 's':
- remotesock = ast_strdupa(optarg);
+ if (ast_opt_remote) {
+ ast_copy_string((char *) cfg_paths.socket_path, optarg, sizeof(cfg_paths.socket_path));
+ }
break;
case 'T':
ast_set_flag(&ast_options, AST_OPT_FLAG_TIMESTAMP);
@@ -3913,11 +3961,8 @@ int main(int argc, char *argv[])
runuser = ast_strdupa(optarg);
break;
case 'V':
- show_version();
- exit(0);
case 'v':
- option_verbose++;
- ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK);
+ /* already processed. */
break;
case 'W': /* White background */
ast_set_flag(&ast_options, AST_OPT_FLAG_LIGHT_BACKGROUND);
@@ -3931,7 +3976,8 @@ int main(int argc, char *argv[])
xarg = ast_strdupa(optarg);
break;
case '?':
- exit(1);
+ /* already processed. */
+ break;
}
}
@@ -3945,12 +3991,6 @@ int main(int argc, char *argv[])
}
}
- ast_readconfig();
- env_init();
-
- if (ast_opt_remote && remotesock != NULL)
- ast_copy_string((char *) cfg_paths.socket_path, remotesock, sizeof(cfg_paths.socket_path));
-
if (!ast_language_is_prefix && !ast_opt_remote) {
fprintf(stderr, "The 'languageprefix' option in asterisk.conf is deprecated; in a future release it will be removed, and your sound files will need to be organized in the 'new style' language layout.\n");
}
diff --git a/main/astmm.c b/main/astmm.c
index a4d5602ce..8260460ab 100644
--- a/main/astmm.c
+++ b/main/astmm.c
@@ -28,6 +28,7 @@
<support_level>core</support_level>
***/
+#define ASTMM_LIBC ASTMM_IGNORE
#include "asterisk.h"
#if defined(__AST_DEBUG_MALLOC)
@@ -61,16 +62,6 @@ enum func_type {
FUNC_ASPRINTF
};
-/* Undefine all our macros */
-#undef malloc
-#undef calloc
-#undef realloc
-#undef strdup
-#undef strndup
-#undef free
-#undef vasprintf
-#undef asprintf
-
#define FENCE_MAGIC 0xfeedbabe /*!< Allocated memory high/low fence overwrite check. */
#define FREED_MAGIC 0xdeaddead /*!< Freed memory wipe filler. */
#define MALLOC_FILLER 0x55 /*!< Malloced memory filler. Must not be zero. */
diff --git a/main/astobj2.c b/main/astobj2.c
index 1db2dd42d..ed91577be 100644
--- a/main/astobj2.c
+++ b/main/astobj2.c
@@ -928,7 +928,7 @@ int ao2_weakproxy_subscribe(void *weakproxy, ao2_weakproxy_notification_cb cb, v
if (sub) {
sub->cb = cb;
sub->data = data;
- AST_LIST_INSERT_TAIL(&weak->destroyed_cb, sub, list);
+ AST_LIST_INSERT_HEAD(&weak->destroyed_cb, sub, list);
ret = 0;
}
} else {
diff --git a/main/astobj2_hash.c b/main/astobj2_hash.c
index 91ad2d2d0..b036911b4 100644
--- a/main/astobj2_hash.c
+++ b/main/astobj2_hash.c
@@ -215,8 +215,7 @@ static struct hash_bucket_node *hash_ao2_new_node(struct ao2_container_hash *sel
return NULL;
}
- i = abs(self->hash_fn(obj_new, OBJ_SEARCH_OBJECT));
- i %= self->n_buckets;
+ i = abs(self->hash_fn(obj_new, OBJ_SEARCH_OBJECT) % self->n_buckets);
__ao2_ref(obj_new, +1, tag ?: "Container node creation", file, line, func);
node->common.obj = obj_new;
@@ -362,8 +361,8 @@ static struct hash_bucket_node *hash_ao2_find_first(struct ao2_container_hash *s
case OBJ_SEARCH_OBJECT:
case OBJ_SEARCH_KEY:
/* we know hash can handle this case */
- bucket_cur = abs(self->hash_fn(arg, flags & OBJ_SEARCH_MASK));
- bucket_cur %= self->n_buckets;
+ bucket_cur = abs(self->hash_fn(arg, flags & OBJ_SEARCH_MASK)
+ % self->n_buckets);
state->sort_fn = self->common.sort_fn;
break;
case OBJ_SEARCH_PARTIAL_KEY:
@@ -960,8 +959,8 @@ static int hash_ao2_integrity(struct ao2_container_hash *self)
++count_obj;
/* Check container hash key for expected bucket. */
- bucket_exp = abs(self->hash_fn(node->common.obj, OBJ_SEARCH_OBJECT));
- bucket_exp %= self->n_buckets;
+ bucket_exp = abs(self->hash_fn(node->common.obj, OBJ_SEARCH_OBJECT)
+ % self->n_buckets);
if (bucket != bucket_exp) {
ast_log(LOG_ERROR, "Bucket %d node hashes to bucket %d!\n",
bucket, bucket_exp);
diff --git a/main/audiohook.c b/main/audiohook.c
index b754f2329..3e233fa2a 100644
--- a/main/audiohook.c
+++ b/main/audiohook.c
@@ -46,6 +46,8 @@ ASTERISK_REGISTER_FILE()
#define AST_AUDIOHOOK_SYNC_TOLERANCE 100 /*!< Tolerance in milliseconds for audiohooks synchronization */
#define AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE 100 /*!< When small queue is enabled, this is the maximum amount of audio that can remain queued at a time. */
+#define DEFAULT_INTERNAL_SAMPLE_RATE 8000
+
struct ast_audiohook_translate {
struct ast_trans_pvt *trans_pvt;
struct ast_format *format;
@@ -117,7 +119,7 @@ int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type
audiohook->init_flags = init_flags;
/* initialize internal rate at 8khz, this will adjust if necessary */
- audiohook_set_internal_rate(audiohook, 8000, 0);
+ audiohook_set_internal_rate(audiohook, DEFAULT_INTERNAL_SAMPLE_RATE, 0);
/* Since we are just starting out... this audiohook is new */
ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_NEW);
@@ -361,7 +363,19 @@ static struct ast_frame *audiohook_read_frame_helper(struct ast_audiohook *audio
struct ast_frame *read_frame = NULL, *final_frame = NULL;
struct ast_format *slin;
- audiohook_set_internal_rate(audiohook, ast_format_get_sample_rate(format), 1);
+ /*
+ * Update the rate if compatibility mode is turned off or if it is
+ * turned on and the format rate is higher than the current rate.
+ *
+ * This makes it so any unnecessary rate switching/resetting does
+ * not take place and also any associated audiohook_list's internal
+ * sample rate maintains the highest sample rate between hooks.
+ */
+ if (!ast_test_flag(audiohook, AST_AUDIOHOOK_COMPATIBLE) ||
+ (ast_test_flag(audiohook, AST_AUDIOHOOK_COMPATIBLE) &&
+ ast_format_get_sample_rate(format) > audiohook->hook_internal_samp_rate)) {
+ audiohook_set_internal_rate(audiohook, ast_format_get_sample_rate(format), 1);
+ }
if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ?
audiohook_read_frame_both(audiohook, samples, read_reference, write_reference) :
@@ -425,6 +439,22 @@ struct ast_frame *ast_audiohook_read_frame_all(struct ast_audiohook *audiohook,
static void audiohook_list_set_samplerate_compatibility(struct ast_audiohook_list *audiohook_list)
{
struct ast_audiohook *ah = NULL;
+
+ /*
+ * Anytime the samplerate compatibility is set (attach/remove an audiohook) the
+ * list's internal sample rate needs to be reset so that the next time processing
+ * through write_list, if needed, it will get updated to the correct rate.
+ *
+ * A list's internal rate always chooses the higher between its own rate and a
+ * given rate. If the current rate is being driven by an audiohook that wanted a
+ * higher rate then when this audiohook is removed the list's rate would remain
+ * at that level when it should be lower, and with no way to lower it since any
+ * rate compared against it would be lower.
+ *
+ * By setting it back to the lowest rate it can recalulate the new highest rate.
+ */
+ audiohook_list->list_internal_samp_rate = DEFAULT_INTERNAL_SAMPLE_RATE;
+
audiohook_list->native_slin_compatible = 1;
AST_LIST_TRAVERSE(&audiohook_list->manipulate_list, ah, list) {
if (!(ah->init_flags & AST_AUDIOHOOK_MANIPULATE_ALL_RATES)) {
@@ -455,7 +485,7 @@ int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audioho
AST_LIST_HEAD_INIT_NOLOCK(&ast_channel_audiohooks(chan)->whisper_list);
AST_LIST_HEAD_INIT_NOLOCK(&ast_channel_audiohooks(chan)->manipulate_list);
/* This sample rate will adjust as necessary when writing to the list. */
- ast_channel_audiohooks(chan)->list_internal_samp_rate = 8000;
+ ast_channel_audiohooks(chan)->list_internal_samp_rate = DEFAULT_INTERNAL_SAMPLE_RATE;
}
/* Drop into respective list */
@@ -467,8 +497,11 @@ int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audioho
AST_LIST_INSERT_TAIL(&ast_channel_audiohooks(chan)->manipulate_list, audiohook, list);
}
-
- audiohook_set_internal_rate(audiohook, ast_channel_audiohooks(chan)->list_internal_samp_rate, 1);
+ /*
+ * Initialize the audiohook's rate to the default. If it needs to be,
+ * it will get updated later.
+ */
+ audiohook_set_internal_rate(audiohook, DEFAULT_INTERNAL_SAMPLE_RATE, 1);
audiohook_list_set_samplerate_compatibility(ast_channel_audiohooks(chan));
/* Change status over to running since it is now attached */
@@ -766,14 +799,14 @@ static struct ast_frame *audiohook_list_translate_to_slin(struct ast_audiohook_l
struct ast_frame *new_frame = frame;
struct ast_format *slin;
- /* If we are capable of maintaining doing samplerates other that 8khz, update
- * the internal audiohook_list's rate and higher samplerate audio arrives. By
- * updating the list's rate, all the audiohooks in the list will be updated as well
- * as the are written and read from. */
- if (audiohook_list->native_slin_compatible) {
- audiohook_list->list_internal_samp_rate =
- MAX(ast_format_get_sample_rate(frame->subclass.format), audiohook_list->list_internal_samp_rate);
- }
+ /*
+ * If we are capable of sample rates other that 8khz, update the internal
+ * audiohook_list's rate and higher sample rate audio arrives. If native
+ * slin compatibility is turned on all audiohooks in the list will be
+ * updated as well during read/write processing.
+ */
+ audiohook_list->list_internal_samp_rate =
+ MAX(ast_format_get_sample_rate(frame->subclass.format), audiohook_list->list_internal_samp_rate);
slin = ast_format_cache_get_slin_by_rate(audiohook_list->list_internal_samp_rate);
if (ast_format_cmp(frame->subclass.format, slin) == AST_FORMAT_CMP_EQUAL) {
@@ -822,6 +855,36 @@ static struct ast_frame *audiohook_list_translate_to_native(struct ast_audiohook
}
/*!
+ *\brief Set the audiohook's internal sample rate to the audiohook_list's rate,
+ * but only when native slin compatibility is turned on.
+ *
+ * \param audiohook_list audiohook_list data object
+ * \param audiohook the audiohook to update
+ * \param rate the current max internal sample rate
+ */
+static void audiohook_list_set_hook_rate(struct ast_audiohook_list *audiohook_list,
+ struct ast_audiohook *audiohook, int *rate)
+{
+ /* The rate should always be the max between itself and the hook */
+ if (audiohook->hook_internal_samp_rate > *rate) {
+ *rate = audiohook->hook_internal_samp_rate;
+ }
+
+ /*
+ * If native slin compatibility is turned on then update the audiohook
+ * with the audiohook_list's current rate. Note, the audiohook's rate is
+ * set to the audiohook_list's rate and not the given rate. If there is
+ * a change in rate the hook's rate is changed on its next check.
+ */
+ if (audiohook_list->native_slin_compatible) {
+ ast_set_flag(audiohook, AST_AUDIOHOOK_COMPATIBLE);
+ audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
+ } else {
+ ast_clear_flag(audiohook, AST_AUDIOHOOK_COMPATIBLE);
+ }
+}
+
+/*!
* \brief Pass an AUDIO frame off to be handled by the audiohook core
*
* \details
@@ -851,6 +914,7 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
int samples;
int middle_frame_manipulated = 0;
int removed = 0;
+ int internal_sample_rate;
/* ---Part_1. translate start_frame to SLINEAR if necessary. */
if (!(middle_frame = audiohook_list_translate_to_slin(audiohook_list, direction, start_frame))) {
@@ -858,6 +922,19 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
}
samples = middle_frame->samples;
+ /*
+ * While processing each audiohook check to see if the internal sample rate needs
+ * to be adjusted (it should be the highest rate specified between formats and
+ * hooks). The given audiohook_list's internal sample rate is then set to the
+ * updated value before returning.
+ *
+ * If slin compatibility mode is turned on then an audiohook's internal sample
+ * rate is set to its audiohook_list's rate. If an audiohook_list's rate is
+ * adjusted during this pass then the change is picked up by the audiohooks
+ * on the next pass.
+ */
+ internal_sample_rate = audiohook_list->list_internal_samp_rate;
+
/* ---Part_2: Send middle_frame to spy and manipulator lists. middle_frame is guaranteed to be SLINEAR here.*/
/* Queue up signed linear frame to each spy */
AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->spy_list, audiohook, list) {
@@ -872,7 +949,7 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
}
continue;
}
- audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
+ audiohook_list_set_hook_rate(audiohook_list, audiohook, &internal_sample_rate);
ast_audiohook_write_frame(audiohook, direction, middle_frame);
ast_audiohook_unlock(audiohook);
}
@@ -896,7 +973,7 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
}
continue;
}
- audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
+ audiohook_list_set_hook_rate(audiohook_list, audiohook, &internal_sample_rate);
if (ast_slinfactory_available(factory) >= samples && ast_slinfactory_read(factory, read_buf, samples)) {
/* Take audio from this whisper source and combine it into our main buffer */
for (i = 0, data1 = combine_buf, data2 = read_buf; i < samples; i++, data1++, data2++) {
@@ -929,14 +1006,16 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
}
continue;
}
- audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
- /* Feed in frame to manipulation. */
- if (!audiohook->manipulate_callback(audiohook, chan, middle_frame, direction)) {
- /* If the manipulation fails then the frame will be returned in its original state.
- * Since there are potentially more manipulator callbacks in the list, no action should
- * be taken here to exit early. */
- middle_frame_manipulated = 1;
- }
+ audiohook_list_set_hook_rate(audiohook_list, audiohook, &internal_sample_rate);
+ /*
+ * Feed in frame to manipulation.
+ *
+ * XXX FAILURES ARE IGNORED XXX
+ * If the manipulation fails then the frame will be returned in its original state.
+ * Since there are potentially more manipulator callbacks in the list, no action should
+ * be taken here to exit early.
+ */
+ audiohook->manipulate_callback(audiohook, chan, middle_frame, direction);
ast_audiohook_unlock(audiohook);
}
AST_LIST_TRAVERSE_SAFE_END;
@@ -960,6 +1039,12 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
/* Before returning, if an audiohook got removed, reset samplerate compatibility */
if (removed) {
audiohook_list_set_samplerate_compatibility(audiohook_list);
+ } else {
+ /*
+ * Set the audiohook_list's rate to the updated rate. Note that if a hook
+ * was removed then the list's internal rate is reset to the default.
+ */
+ audiohook_list->list_internal_samp_rate = internal_sample_rate;
}
return end_frame;
diff --git a/main/cdr.c b/main/cdr.c
index 9b32f9d9f..c6f49f16f 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -3096,13 +3096,9 @@ int ast_cdr_serialize_variables(const char *channel_name, struct ast_str **buf,
struct cdr_object *it_cdr;
struct ast_var_t *variable;
const char *var;
- RAII_VAR(char *, workspace, ast_malloc(256), ast_free);
+ char workspace[256];
int total = 0, x = 0, i;
- if (!workspace) {
- return 0;
- }
-
if (!cdr) {
RAII_VAR(struct module_config *, mod_cfg,
ao2_global_obj_ref(module_configs), ao2_cleanup);
diff --git a/main/config.c b/main/config.c
index bc622ccab..d6a077b2d 100644
--- a/main/config.c
+++ b/main/config.c
@@ -735,6 +735,19 @@ const char *ast_variable_find_in_list(const struct ast_variable *list, const cha
return NULL;
}
+const char *ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable)
+{
+ const struct ast_variable *v;
+ const char *found = NULL;
+
+ for (v = list; v; v = v->next) {
+ if (!strcasecmp(variable, v->name)) {
+ found = v->value;
+ }
+ }
+ return found;
+}
+
static struct ast_variable *variable_clone(const struct ast_variable *old)
{
struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
diff --git a/main/dns_srv.c b/main/dns_srv.c
index f5d038ae7..e4a3d8bbd 100644
--- a/main/dns_srv.c
+++ b/main/dns_srv.c
@@ -112,13 +112,15 @@ void dns_srv_sort(struct ast_dns_result *result)
struct dns_records newlist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
while (AST_LIST_FIRST(&result->records)) {
- unsigned short cur_priority = 0;
+ unsigned short cur_priority = ((struct ast_dns_srv_record *)(AST_LIST_FIRST(&result->records)))->priority;
struct dns_records temp_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
- /* Find the lowest current priority to work on */
- AST_LIST_TRAVERSE(&result->records, current, list) {
- if (!cur_priority || ((struct ast_dns_srv_record *)current)->priority < cur_priority) {
- cur_priority = ((struct ast_dns_srv_record *)current)->priority;
+ /* Find the lowest current priority to work on, but if the priority is already zero there is no lower priority */
+ if (cur_priority) {
+ AST_LIST_TRAVERSE(&result->records, current, list) {
+ if (((struct ast_dns_srv_record *)current)->priority < cur_priority) {
+ cur_priority = ((struct ast_dns_srv_record *)current)->priority;
+ }
}
}
diff --git a/main/file.c b/main/file.c
index acd2cc6bc..bfad6e025 100644
--- a/main/file.c
+++ b/main/file.c
@@ -897,7 +897,7 @@ static enum fsread_res ast_readaudio_callback(struct ast_filestream *s)
if (!fr /* stream complete */ || ast_write(s->owner, fr) /* error writing */) {
if (fr) {
- ast_log(LOG_WARNING, "Failed to write frame\n");
+ ast_debug(2, "Failed to write frame\n");
ast_frfree(fr);
}
goto return_failure;
@@ -954,7 +954,7 @@ static enum fsread_res ast_readvideo_callback(struct ast_filestream *s)
if (!fr /* stream complete */ || ast_write(s->owner, fr) /* error writing */) {
if (fr) {
- ast_log(LOG_WARNING, "Failed to write frame\n");
+ ast_debug(2, "Failed to write frame\n");
ast_frfree(fr);
}
ast_channel_vstreamid_set(s->owner, -1);
diff --git a/main/hashtab.c b/main/hashtab.c
index 27a700a9d..c08880c69 100644
--- a/main/hashtab.c
+++ b/main/hashtab.c
@@ -26,7 +26,7 @@
<support_level>core</support_level>
***/
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
diff --git a/main/logger.c b/main/logger.c
index 41f26e823..bdcd6c4f5 100644
--- a/main/logger.c
+++ b/main/logger.c
@@ -375,16 +375,25 @@ static int init_logger_chain(int locked, const char *altconf)
const char *s;
struct ast_flags config_flags = { 0 };
- display_callids = 1;
-
if (!(cfg = ast_config_load2(S_OR(altconf, "logger.conf"), "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
cfg = NULL;
}
- /* delete our list of log channels */
if (!locked) {
AST_RWLIST_WRLOCK(&logchannels);
}
+
+ /* Set defaults */
+ hostname[0] = '\0';
+ display_callids = 1;
+ memset(&logfiles, 0, sizeof(logfiles));
+ logfiles.queue_log = 1;
+ ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
+ ast_copy_string(queue_log_name, QUEUELOG, sizeof(queue_log_name));
+ exec_after_rotate[0] = '\0';
+ rotatestrategy = SEQUENTIAL;
+
+ /* delete our list of log channels */
while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list))) {
ast_free(chan);
}
@@ -424,17 +433,14 @@ static int init_logger_chain(int locked, const char *altconf)
ast_copy_string(hostname, "unknown", sizeof(hostname));
fprintf(stderr, "What box has no hostname???\n");
}
- } else
- hostname[0] = '\0';
- } else
- hostname[0] = '\0';
+ }
+ }
if ((s = ast_variable_retrieve(cfg, "general", "display_callids"))) {
display_callids = ast_true(s);
}
- if ((s = ast_variable_retrieve(cfg, "general", "dateformat")))
+ if ((s = ast_variable_retrieve(cfg, "general", "dateformat"))) {
ast_copy_string(dateformat, s, sizeof(dateformat));
- else
- ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
+ }
if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) {
logfiles.queue_log = ast_true(s);
}
diff --git a/main/manager.c b/main/manager.c
index 17a27384f..60e185b92 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -6781,9 +6781,9 @@ static int ast_manager_register_struct(struct manager_action *act)
return -1;
}
if (ret > 0) { /* Insert these alphabetically */
- prev = cur;
break;
}
+ prev = cur;
}
ao2_t_ref(act, +1, "action object added to list");
diff --git a/main/manager_endpoints.c b/main/manager_endpoints.c
index 424e32108..ffcdef06f 100644
--- a/main/manager_endpoints.c
+++ b/main/manager_endpoints.c
@@ -75,6 +75,7 @@ int manager_endpoints_init(void)
}
ret |= stasis_message_router_add(endpoint_router, ast_endpoint_state_type(), endpoint_state_cb, NULL);
+ ret |= stasis_message_router_add(endpoint_router, ast_endpoint_contact_state_type(), endpoint_state_cb, NULL);
/* If somehow we failed to add any routes, just shut down the whole
* thing and fail it.
diff --git a/main/message.c b/main/message.c
index 64cefcb1b..7098f698d 100644
--- a/main/message.c
+++ b/main/message.c
@@ -743,6 +743,7 @@ static void chan_cleanup(struct ast_channel *chan)
struct ast_datastore *msg_ds, *ds;
struct varshead *headp;
struct ast_var_t *vardata;
+ struct ast_frame *cur;
ast_channel_lock(chan);
@@ -772,6 +773,13 @@ static void chan_cleanup(struct ast_channel *chan)
}
/*
+ * Remove frames from read queue
+ */
+ while ((cur = AST_LIST_REMOVE_HEAD(ast_channel_readq(chan), frame_list))) {
+ ast_frfree(cur);
+ }
+
+ /*
* Restore msg datastore.
*/
if (msg_ds) {
diff --git a/main/sdp_srtp.c b/main/sdp_srtp.c
index 8c86f1407..e576258c3 100644
--- a/main/sdp_srtp.c
+++ b/main/sdp_srtp.c
@@ -16,7 +16,7 @@
* at the top of the source tree.
*/
-/*! \file ast_sdp_crypto.c
+/*! \file
*
* \brief SRTP and SDP Security descriptions
*
@@ -238,7 +238,8 @@ int ast_sdp_crypto_process(struct ast_rtp_instance *rtp, struct ast_sdp_srtp *sr
return -1;
}
- if (sscanf(tag, "%30d", &crypto->tag) != 1 || crypto->tag <= 0 || crypto->tag > 9) {
+ /* RFC4568 9.1 - tag is 1-9 digits, greater than zero */
+ if (sscanf(tag, "%30d", &crypto->tag) != 1 || crypto->tag <= 0 || crypto->tag > 999999999) {
ast_log(LOG_WARNING, "Unacceptable a=crypto tag: %s\n", tag);
return -1;
}
diff --git a/main/sorcery.c b/main/sorcery.c
index 1a4b3a072..f84855eff 100644
--- a/main/sorcery.c
+++ b/main/sorcery.c
@@ -43,6 +43,7 @@ ASTERISK_REGISTER_FILE()
#include "asterisk/taskprocessor.h"
#include "asterisk/threadpool.h"
#include "asterisk/json.h"
+#include "asterisk/vector.h"
/* To prevent DEBUG_FD_LEAKS from interfering with things we undef open and close */
#undef open
@@ -86,6 +87,35 @@ ASTERISK_REGISTER_FILE()
/*! \brief Thread pool for observers */
static struct ast_threadpool *threadpool;
+/*! \brief Structure for an internal wizard instance */
+struct ast_sorcery_internal_wizard {
+ /*!
+ * \brief Wizard interface itself
+ * \warning Callbacks must always be declared first in this structure
+ * so an ao2_ref on &callbacks will adjust the ref count on
+ * internal_wizard.
+ */
+ struct ast_sorcery_wizard callbacks;
+
+ /*! \brief Observers */
+ struct ao2_container *observers;
+};
+
+/*! \brief Structure for a wizard instance which operates on objects */
+struct ast_sorcery_object_wizard {
+ /*! \brief Wizard interface itself */
+ struct ast_sorcery_internal_wizard *wizard;
+
+ /*! \brief Unique data for the wizard */
+ void *data;
+
+ /*! \brief Wizard is acting as an object cache */
+ unsigned int caching:1;
+};
+
+/*! \brief Interface for a sorcery object type wizards */
+AST_VECTOR_RW(ast_sorcery_object_wizards, struct ast_sorcery_object_wizard *);
+
/*! \brief Structure for internal sorcery object information */
struct ast_sorcery_object {
/*! \brief Unique identifier of this object */
@@ -119,7 +149,7 @@ struct ast_sorcery_object_type {
sorcery_diff_handler diff;
/*! \brief Wizard instances */
- struct ao2_container *wizards;
+ struct ast_sorcery_object_wizards wizards;
/*! \brief Object fields */
struct ao2_container *fields;
@@ -176,27 +206,6 @@ struct ast_sorcery_object_field {
intptr_t args[];
};
-/*! \brief Structure for an internal wizard instance */
-struct ast_sorcery_internal_wizard {
- /*! \brief Wizard interface itself */
- struct ast_sorcery_wizard callbacks;
-
- /*! \brief Observers */
- struct ao2_container *observers;
-};
-
-/*! \brief Structure for a wizard instance which operates on objects */
-struct ast_sorcery_object_wizard {
- /*! \brief Wizard interface itself */
- struct ast_sorcery_internal_wizard *wizard;
-
- /*! \brief Unique data for the wizard */
- void *data;
-
- /*! \brief Wizard is acting as an object cache */
- unsigned int caching:1;
-};
-
/*! \brief Full structure for sorcery */
struct ast_sorcery {
/*! \brief Container for known object types */
@@ -789,7 +798,10 @@ static void sorcery_object_type_destructor(void *obj)
{
struct ast_sorcery_object_type *object_type = obj;
- ao2_cleanup(object_type->wizards);
+ AST_VECTOR_RW_WRLOCK(&object_type->wizards);
+ AST_VECTOR_CALLBACK_VOID(&object_type->wizards, ao2_cleanup);
+ AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+ AST_VECTOR_RW_FREE(&object_type->wizards);
ao2_cleanup(object_type->fields);
ao2_cleanup(object_type->observers);
@@ -806,6 +818,7 @@ static void sorcery_object_type_destructor(void *obj)
/*! \brief Internal function which allocates an object type structure */
static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *type, const char *module)
{
+#define INITIAL_WIZARD_VECTOR_SIZE 5
struct ast_sorcery_object_type *object_type;
char uuid[AST_UUID_STR_LEN];
@@ -814,7 +827,7 @@ static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *typ
}
/* Order matters for object wizards */
- if (!(object_type->wizards = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, sorcery_wizard_cmp))) {
+ if (AST_VECTOR_RW_INIT(&object_type->wizards, INITIAL_WIZARD_VECTOR_SIZE) != 0) {
ao2_ref(object_type, -1);
return NULL;
}
@@ -864,7 +877,7 @@ static void sorcery_object_wizard_destructor(void *obj)
{
struct ast_sorcery_object_wizard *object_wizard = obj;
- if (object_wizard->data) {
+ if (object_wizard->data && object_wizard->wizard->callbacks.close) {
object_wizard->wizard->callbacks.close(object_wizard->data);
}
@@ -875,9 +888,73 @@ static void sorcery_object_wizard_destructor(void *obj)
ao2_cleanup(object_wizard->wizard);
}
-/*! \brief Internal function which creates an object type and adds a wizard mapping */
-enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery,
- const char *type, const char *module, const char *name, const char *data, unsigned int caching)
+/*! \brief Return the number of wizards mapped to an object type */
+int ast_sorcery_get_wizard_mapping_count(struct ast_sorcery *sorcery,
+ const char *type)
+{
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+
+ if (!object_type) {
+ return -1;
+ }
+
+ return AST_VECTOR_SIZE(&object_type->wizards);
+}
+
+int ast_sorcery_get_wizard_mapping(struct ast_sorcery *sorcery,
+ const char *type, int index, struct ast_sorcery_wizard **wizard, void **data)
+{
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+ struct ast_sorcery_object_wizard *owizard;
+
+ if (!object_type) {
+ return -1;
+ }
+
+ if (index < 0 || index >= AST_VECTOR_SIZE(&object_type->wizards)) {
+ return -1;
+ }
+
+ owizard = AST_VECTOR_GET(&object_type->wizards, index);
+
+ if (wizard != NULL) {
+ *wizard = &(owizard->wizard->callbacks);
+ ao2_bump(owizard->wizard);
+ } else {
+ return -1;
+ }
+
+ if (data != NULL) {
+ *data = owizard->data;
+ }
+
+ return 0;
+}
+
+/*! \brief Internal function removes a wizard mapping */
+int __ast_sorcery_remove_wizard_mapping(struct ast_sorcery *sorcery,
+ const char *type, const char *module, const char *name)
+{
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+ int res;
+
+ if (!object_type) {
+ return -1;
+ }
+
+ AST_VECTOR_RW_WRLOCK(&object_type->wizards);
+#define WIZARD_NAME_COMPARE(a, b) (strcmp((a)->wizard->callbacks.name, (b)) == 0)
+ res = AST_VECTOR_REMOVE_CMP_ORDERED(&object_type->wizards, name, WIZARD_NAME_COMPARE, ao2_cleanup);
+#undef WIZARD_NAME_COMPARE
+ AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+
+ return res;
+}
+
+/*! \brief Internal function which creates an object type and inserts a wizard mapping */
+enum ast_sorcery_apply_result __ast_sorcery_insert_wizard_mapping(struct ast_sorcery *sorcery,
+ const char *type, const char *module, const char *name, const char *data,
+ unsigned int caching, int position)
{
RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
RAII_VAR(struct ast_sorcery_internal_wizard *, wizard, ao2_find(wizards, name, OBJ_KEY), ao2_cleanup);
@@ -899,19 +976,23 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
created = 1;
}
+ AST_VECTOR_RW_WRLOCK(&object_type->wizards);
if (!created) {
- struct ast_sorcery_wizard *found;
+ struct ast_sorcery_object_wizard *found;
- found = ao2_find(object_type->wizards, wizard, OBJ_SEARCH_OBJECT);
+#define WIZARD_COMPARE(a, b) ((a)->wizard == (b))
+ found = AST_VECTOR_GET_CMP(&object_type->wizards, wizard, WIZARD_COMPARE);
+#undef WIZARD_COMPARE
if (found) {
ast_debug(1, "Wizard %s already applied to object type %s\n",
wizard->callbacks.name, object_type->name);
- ao2_cleanup(found);
+ AST_VECTOR_RW_UNLOCK(&object_type->wizards);
return AST_SORCERY_APPLY_DUPLICATE;
}
}
if (wizard->callbacks.open && !(object_wizard->data = wizard->callbacks.open(data))) {
+ AST_VECTOR_RW_UNLOCK(&object_type->wizards);
return AST_SORCERY_APPLY_FAIL;
}
@@ -920,7 +1001,16 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
object_wizard->wizard = ao2_bump(wizard);
object_wizard->caching = caching;
- ao2_link(object_type->wizards, object_wizard);
+ if (position == AST_SORCERY_WIZARD_POSITION_LAST) {
+ position = AST_VECTOR_SIZE(&object_type->wizards);
+ }
+
+ if (AST_VECTOR_INSERT_AT(&object_type->wizards, position, object_wizard) != 0) {
+ AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+ return AST_SORCERY_APPLY_FAIL;
+ }
+ AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+ ao2_bump(object_wizard);
if (created) {
ao2_link(sorcery->types, object_type);
@@ -932,6 +1022,14 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
return AST_SORCERY_APPLY_SUCCESS;
}
+/*! \brief Internal function which creates an object type and adds a wizard mapping */
+enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery,
+ const char *type, const char *module, const char *name, const char *data, unsigned int caching)
+{
+ return __ast_sorcery_insert_wizard_mapping(sorcery, type, module, name, data,
+ caching, AST_SORCERY_WIZARD_POSITION_LAST);
+}
+
enum ast_sorcery_apply_result __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, const char *module)
{
struct ast_flags flags = { 0 };
@@ -1260,7 +1358,9 @@ static int sorcery_object_load(void *obj, void *arg, int flags)
NOTIFY_INSTANCE_OBSERVERS(details->sorcery->observers, object_type_loading,
details->sorcery->module_name, details->sorcery, type->name, details->reload);
- ao2_callback(type->wizards, OBJ_NODATA, sorcery_wizard_load, details);
+ AST_VECTOR_RW_RDLOCK(&type->wizards);
+ AST_VECTOR_CALLBACK(&type->wizards, sorcery_wizard_load, NULL, details, 0);
+ AST_VECTOR_RW_UNLOCK(&type->wizards);
NOTIFY_INSTANCE_OBSERVERS(details->sorcery->observers, object_type_loaded,
details->sorcery->module_name, details->sorcery, type->name, details->reload);
@@ -1700,31 +1800,36 @@ void *ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *
{
RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
void *object = NULL;
- struct ao2_iterator i;
- struct ast_sorcery_object_wizard *wizard;
+ int i;
unsigned int cached = 0;
if (!object_type || ast_strlen_zero(id)) {
return NULL;
}
- i = ao2_iterator_init(object_type->wizards, 0);
- for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
+ AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+ for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+ struct ast_sorcery_object_wizard *wizard =
+ AST_VECTOR_GET(&object_type->wizards, i);
+
if (wizard->wizard->callbacks.retrieve_id &&
!(object = wizard->wizard->callbacks.retrieve_id(sorcery, wizard->data, object_type->name, id))) {
continue;
}
cached = wizard->caching;
-
- ao2_ref(wizard, -1);
break;
}
- ao2_iterator_destroy(&i);
if (!cached && object) {
- ao2_callback(object_type->wizards, 0, sorcery_cache_create, object);
+ struct sorcery_details sdetails = {
+ .sorcery = sorcery,
+ .obj = object,
+ };
+
+ AST_VECTOR_CALLBACK(&object_type->wizards, sorcery_cache_create, NULL, &sdetails, 0);
}
+ AST_VECTOR_RW_UNLOCK(&object_type->wizards);
return object;
}
@@ -1733,8 +1838,7 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
{
RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
void *object = NULL;
- struct ao2_iterator i;
- struct ast_sorcery_object_wizard *wizard;
+ int i;
unsigned int cached = 0;
if (!object_type) {
@@ -1748,9 +1852,11 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
}
}
- /* Inquire with the available wizards for retrieval */
- i = ao2_iterator_init(object_type->wizards, 0);
- for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
+ AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+ for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+ struct ast_sorcery_object_wizard *wizard =
+ AST_VECTOR_GET(&object_type->wizards, i);
+
if ((flags & AST_RETRIEVE_FLAG_MULTIPLE)) {
if (wizard->wizard->callbacks.retrieve_multiple) {
wizard->wizard->callbacks.retrieve_multiple(sorcery, wizard->data, object_type->name, object, fields);
@@ -1767,15 +1873,14 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
cached = wizard->caching;
- ao2_ref(wizard, -1);
break;
}
- ao2_iterator_destroy(&i);
/* If we are returning a single object and it came from a non-cache source create it in any caches */
if (!(flags & AST_RETRIEVE_FLAG_MULTIPLE) && !cached && object) {
- ao2_callback(object_type->wizards, 0, sorcery_cache_create, object);
+ AST_VECTOR_CALLBACK(&object_type->wizards, sorcery_cache_create, NULL, object, 0);
}
+ AST_VECTOR_RW_UNLOCK(&object_type->wizards);
return object;
}
@@ -1784,22 +1889,24 @@ struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *so
{
RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
struct ao2_container *objects;
- struct ao2_iterator i;
- struct ast_sorcery_object_wizard *wizard;
+ int i;
if (!object_type || !(objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
return NULL;
}
- i = ao2_iterator_init(object_type->wizards, 0);
- for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
+ AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+ for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+ struct ast_sorcery_object_wizard *wizard =
+ AST_VECTOR_GET(&object_type->wizards, i);
+
if (!wizard->wizard->callbacks.retrieve_regex) {
continue;
}
wizard->wizard->callbacks.retrieve_regex(sorcery, wizard->data, object_type->name, objects, regex);
}
- ao2_iterator_destroy(&i);
+ AST_VECTOR_RW_UNLOCK(&object_type->wizards);
return objects;
}
@@ -1846,7 +1953,9 @@ int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
{
const struct ast_sorcery_object_details *details = object;
RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
- RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+ struct ast_sorcery_object_wizard *object_wizard = NULL;
+ struct ast_sorcery_object_wizard *found_wizard;
+ int i;
struct sorcery_details sdetails = {
.sorcery = sorcery,
.obj = object,
@@ -1856,14 +1965,21 @@ int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
return -1;
}
- if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_create, &sdetails)) &&
- ao2_container_count(object_type->observers)) {
- struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+ AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+ for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+ found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+ if (sorcery_wizard_create(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+ object_wizard = found_wizard;
+ if(ao2_container_count(object_type->observers)) {
+ struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
- if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_create, invocation)) {
- ao2_cleanup(invocation);
+ if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_create, invocation)) {
+ ao2_cleanup(invocation);
+ }
+ }
}
}
+ AST_VECTOR_RW_UNLOCK(&object_type->wizards);
return object_wizard ? 0 : -1;
}
@@ -1905,7 +2021,9 @@ int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
{
const struct ast_sorcery_object_details *details = object;
RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
- RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+ struct ast_sorcery_object_wizard *object_wizard = NULL;
+ struct ast_sorcery_object_wizard *found_wizard;
+ int i;
struct sorcery_details sdetails = {
.sorcery = sorcery,
.obj = object,
@@ -1915,14 +2033,21 @@ int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
return -1;
}
- if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_update, &sdetails)) &&
- ao2_container_count(object_type->observers)) {
- struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+ AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+ for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+ found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+ if (sorcery_wizard_update(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+ object_wizard = found_wizard;
+ if (ao2_container_count(object_type->observers)) {
+ struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
- if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_update, invocation)) {
- ao2_cleanup(invocation);
+ if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_update, invocation)) {
+ ao2_cleanup(invocation);
+ }
+ }
}
}
+ AST_VECTOR_RW_UNLOCK(&object_type->wizards);
return object_wizard ? 0 : -1;
}
@@ -1964,7 +2089,9 @@ int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
{
const struct ast_sorcery_object_details *details = object;
RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
- RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+ struct ast_sorcery_object_wizard *object_wizard = NULL;
+ struct ast_sorcery_object_wizard *found_wizard;
+ int i;
struct sorcery_details sdetails = {
.sorcery = sorcery,
.obj = object,
@@ -1974,14 +2101,21 @@ int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
return -1;
}
- if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_delete, &sdetails)) &&
- ao2_container_count(object_type->observers)) {
- struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+ AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+ for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+ found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+ if (sorcery_wizard_delete(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+ object_wizard = found_wizard;
+ if (ao2_container_count(object_type->observers)) {
+ struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
- if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_delete, invocation)) {
- ao2_cleanup(invocation);
+ if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_delete, invocation)) {
+ ao2_cleanup(invocation);
+ }
+ }
}
}
+ AST_VECTOR_RW_UNLOCK(&object_type->wizards);
return object_wizard ? 0 : -1;
}
@@ -2199,3 +2333,8 @@ int ast_sorcery_is_object_field_registered(const struct ast_sorcery_object_type
ao2_cleanup(object_field);
return res;
}
+
+const char *ast_sorcery_get_module(const struct ast_sorcery *sorcery)
+{
+ return sorcery->module_name;
+}
diff --git a/main/stasis.c b/main/stasis.c
index 6a5926546..e168ce93b 100644
--- a/main/stasis.c
+++ b/main/stasis.c
@@ -444,6 +444,10 @@ static void subscription_invoke(struct stasis_subscription *sub,
static void send_subscription_subscribe(struct stasis_topic *topic, struct stasis_subscription *sub);
static void send_subscription_unsubscribe(struct stasis_topic *topic, struct stasis_subscription *sub);
+void stasis_subscription_cb_noop(void *data, struct stasis_subscription *sub, struct stasis_message *message)
+{
+}
+
struct stasis_subscription *internal_stasis_subscribe(
struct stasis_topic *topic,
stasis_subscription_cb callback,
diff --git a/main/stasis_endpoints.c b/main/stasis_endpoints.c
index f19bb91fe..da6505355 100644
--- a/main/stasis_endpoints.c
+++ b/main/stasis_endpoints.c
@@ -71,6 +71,35 @@ ASTERISK_REGISTER_FILE()
</syntax>
</managerEventInstance>
</managerEvent>
+ <managerEvent language="en_US" name="ContactStatus">
+ <managerEventInstance class="EVENT_FLAG_SYSTEM">
+ <synopsis>Raised when the state of a contact changes.</synopsis>
+ <syntax>
+ <parameter name="URI">
+ <para>This contact's URI.</para>
+ </parameter>
+ <parameter name="ContactStatus">
+ <para>New status of the contact.</para>
+ <enumlist>
+ <enum name="Unknown"/>
+ <enum name="Unreachable"/>
+ <enum name="Reachable"/>
+ <enum name="Created"/>
+ <enum name="Removed"/>
+ </enumlist>
+ </parameter>
+ <parameter name="AOR">
+ <para>The name of the associated aor.</para>
+ </parameter>
+ <parameter name="EndpointName">
+ <para>The name of the associated endpoint.</para>
+ </parameter>
+ <parameter name="RoundtripUsec">
+ <para>The RTT measured during the last qualify.</para>
+ </parameter>
+ </syntax>
+ </managerEventInstance>
+ </managerEvent>
***/
static struct stasis_cp_all *endpoint_cache_all;
@@ -137,6 +166,46 @@ static struct ast_manager_event_blob *peerstatus_to_ami(struct stasis_message *m
ast_str_buffer(peerstatus_event_string));
}
+static struct ast_manager_event_blob *contactstatus_to_ami(struct stasis_message *msg);
+
+STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_contact_state_type,
+ .to_ami = contactstatus_to_ami,
+);
+
+static struct ast_manager_event_blob *contactstatus_to_ami(struct stasis_message *msg)
+{
+ struct ast_endpoint_blob *obj = stasis_message_data(msg);
+ RAII_VAR(struct ast_str *, contactstatus_event_string, ast_str_create(64), ast_free);
+ const char *value;
+
+ if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "uri")))) {
+ return NULL;
+ }
+ ast_str_append(&contactstatus_event_string, 0, "URI: %s\r\n", value);
+
+ if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "contact_status")))) {
+ return NULL;
+ }
+ ast_str_append(&contactstatus_event_string, 0, "ContactStatus: %s\r\n", value);
+
+ if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "aor")))) {
+ return NULL;
+ }
+ ast_str_append(&contactstatus_event_string, 0, "AOR: %s\r\n", value);
+
+ if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "endpoint_name")))) {
+ return NULL;
+ }
+ ast_str_append(&contactstatus_event_string, 0, "EndpointName: %s\r\n", value);
+
+ if ((value = ast_json_string_get(ast_json_object_get(obj->blob, "roundtrip_usec")))) {
+ ast_str_append(&contactstatus_event_string, 0, "RoundtripUsec: %s\r\n", value);
+ }
+
+ return ast_manager_event_blob_create(EVENT_FLAG_SYSTEM, "ContactStatus",
+ "%s", ast_str_buffer(contactstatus_event_string));
+}
+
static void endpoint_blob_dtor(void *obj)
{
struct ast_endpoint_blob *event = obj;
@@ -294,6 +363,7 @@ static void endpoints_stasis_cleanup(void)
{
STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_snapshot_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_state_type);
+ STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_contact_state_type);
ao2_cleanup(endpoint_cache_all);
endpoint_cache_all = NULL;
@@ -312,6 +382,7 @@ int ast_endpoint_stasis_init(void)
res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_snapshot_type);
res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_state_type);
+ res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_contact_state_type);
return res;
}
diff --git a/main/tcptls.c b/main/tcptls.c
index 0b06d22ac..338b8bb36 100644
--- a/main/tcptls.c
+++ b/main/tcptls.c
@@ -555,6 +555,34 @@ static void session_instance_destructor(void *obj)
ao2_cleanup(i->private_data);
}
+#ifdef DO_SSL
+static int check_tcptls_cert_name(ASN1_STRING *cert_str, const char *hostname, const char *desc)
+{
+ unsigned char *str;
+ int ret;
+
+ ret = ASN1_STRING_to_UTF8(&str, cert_str);
+ if (ret < 0 || !str) {
+ return -1;
+ }
+
+ if (strlen((char *) str) != ret) {
+ ast_log(LOG_WARNING, "Invalid certificate %s length (contains NULL bytes?)\n", desc);
+
+ ret = -1;
+ } else if (!strcasecmp(hostname, (char *) str)) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+
+ ast_debug(3, "SSL %s compare s1='%s' s2='%s'\n", desc, hostname, str);
+ OPENSSL_free(str);
+
+ return ret;
+}
+#endif
+
/*! \brief
* creates a FILE * from the fd passed by the accept thread.
* This operation is potentially expensive (certificate verification),
@@ -631,8 +659,8 @@ static void *handle_tcptls_connection(void *data)
}
if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) {
ASN1_STRING *str;
- unsigned char *str2;
X509_NAME *name = X509_get_subject_name(peer);
+ STACK_OF(GENERAL_NAME) *alt_names;
int pos = -1;
int found = 0;
@@ -643,25 +671,36 @@ static void *handle_tcptls_connection(void *data)
if (pos < 0) {
break;
}
+
str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos));
- ret = ASN1_STRING_to_UTF8(&str2, str);
- if (ret < 0) {
- continue;
+ if (!check_tcptls_cert_name(str, tcptls_session->parent->hostname, "common name")) {
+ found = 1;
+ break;
}
+ }
+
+ if (!found) {
+ alt_names = X509_get_ext_d2i(peer, NID_subject_alt_name, NULL, NULL);
+ if (alt_names != NULL) {
+ int alt_names_count = sk_GENERAL_NAME_num(alt_names);
+
+ for (pos = 0; pos < alt_names_count; pos++) {
+ const GENERAL_NAME *alt_name = sk_GENERAL_NAME_value(alt_names, pos);
+
+ if (alt_name->type != GEN_DNS) {
+ continue;
+ }
- if (str2) {
- if (strlen((char *) str2) != ret) {
- ast_log(LOG_WARNING, "Invalid certificate common name length (contains NULL bytes?)\n");
- } else if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2)) {
- found = 1;
+ if (!check_tcptls_cert_name(alt_name->d.dNSName, tcptls_session->parent->hostname, "alt name")) {
+ found = 1;
+ break;
+ }
}
- ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2);
- OPENSSL_free(str2);
- }
- if (found) {
- break;
+
+ sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
}
}
+
if (!found) {
ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname);
X509_free(peer);
@@ -752,6 +791,22 @@ void *ast_tcptls_server_root(void *data)
return NULL;
}
+static void __ssl_setup_certs(struct ast_tls_config *cfg, const size_t cert_file_len, const char *key_type_extension, const char *key_type)
+{
+ char *cert_file = ast_strdupa(cfg->certfile);
+
+ memcpy(cert_file + cert_file_len - 8, key_type_extension, 5);
+ if (access(cert_file, F_OK) == 0) {
+ if (SSL_CTX_use_certificate_chain_file(cfg->ssl_ctx, cert_file) == 0) {
+ ast_log(LOG_WARNING, "TLS/SSL error loading public %s key (certificate) from <%s>.\n", key_type, cert_file);
+ } else if (SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, cert_file, SSL_FILETYPE_PEM) == 0) {
+ ast_log(LOG_WARNING, "TLS/SSL error loading private %s key from <%s>.\n", key_type, cert_file);
+ } else if (SSL_CTX_check_private_key(cfg->ssl_ctx) == 0) {
+ ast_log(LOG_WARNING, "TLS/SSL error matching private %s key and certificate in <%s>.\n", key_type, cert_file);
+ }
+ }
+}
+
static int __ssl_setup(struct ast_tls_config *cfg, int client)
{
#ifndef DO_SSL
@@ -839,6 +894,17 @@ static int __ssl_setup(struct ast_tls_config *cfg, int client)
return 0;
}
}
+ if (!client) {
+ size_t certfile_len = strlen(cfg->certfile);
+
+ /* expects a file name which contains _rsa. like asterisk_rsa.pem
+ * ignores any 3-character file-extension like .pem, .cer, .crt
+ */
+ if (certfile_len >= 8 && !strncmp(cfg->certfile + certfile_len - 8, "_rsa.", 5)) {
+ __ssl_setup_certs(cfg, certfile_len, "_ecc.", "ECC");
+ __ssl_setup_certs(cfg, certfile_len, "_dsa.", "DSA");
+ }
+ }
}
if (!ast_strlen_zero(cfg->cipher)) {
if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) {
diff --git a/pbx/pbx_ael.c b/pbx/pbx_ael.c
index 7e28b8946..1985d27a0 100644
--- a/pbx/pbx_ael.c
+++ b/pbx/pbx_ael.c
@@ -297,11 +297,11 @@ int ael_external_load_module(void)
#endif
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk Extension Language Compiler",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
#ifdef AAL_ARGCHECK
static const char * const ael_funclist[] =
diff --git a/pbx/pbx_config.c b/pbx/pbx_config.c
index 09941a6d5..d85b901f9 100644
--- a/pbx/pbx_config.c
+++ b/pbx/pbx_config.c
@@ -2104,8 +2104,8 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Text Extension Configuration",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
diff --git a/pbx/pbx_dundi.c b/pbx/pbx_dundi.c
index 8ea8dd369..660968f14 100644
--- a/pbx/pbx_dundi.c
+++ b/pbx/pbx_dundi.c
@@ -5052,10 +5052,10 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .nonoptreq = "res_crypto",
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .nonoptreq = "res_crypto",
+);
diff --git a/pbx/pbx_lua.c b/pbx/pbx_lua.c
index 002f05fa6..84b1f7126 100644
--- a/pbx/pbx_lua.c
+++ b/pbx/pbx_lua.c
@@ -1673,9 +1673,9 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Lua PBX Switch",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
diff --git a/pbx/pbx_spool.c b/pbx/pbx_spool.c
index 0dad6060d..c858ed22f 100644
--- a/pbx/pbx_spool.c
+++ b/pbx/pbx_spool.c
@@ -102,6 +102,14 @@ struct outgoing {
};
#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
+struct direntry {
+ AST_LIST_ENTRY(direntry) list;
+ time_t mtime;
+ char name[0];
+};
+
+static AST_LIST_HEAD_STATIC(dirlist, direntry);
+
static void queue_file(const char *filename, time_t when);
#endif
@@ -323,6 +331,10 @@ static int remove_from_queue(struct outgoing *o, const char *status)
char newfn[256];
const char *bname;
+#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
+ struct direntry *cur;
+#endif
+
if (!ast_test_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE)) {
struct stat current_file_status;
@@ -333,6 +345,19 @@ static int remove_from_queue(struct outgoing *o, const char *status)
}
}
+#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
+ AST_LIST_LOCK(&dirlist);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&dirlist, cur, list) {
+ if (!strcmp(cur->name, o->fn)) {
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_free(cur);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&dirlist);
+#endif
+
if (!ast_test_flag(&o->options, SPOOL_FLAG_ARCHIVE)) {
unlink(o->fn);
return 0;
@@ -486,14 +511,6 @@ static int scan_service(const char *fn, time_t now)
return 0;
}
-#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
-struct direntry {
- AST_LIST_ENTRY(direntry) list;
- time_t mtime;
- char name[0];
-};
-
-static AST_LIST_HEAD_STATIC(dirlist, direntry);
#if defined(HAVE_INOTIFY)
/* Only one thread is accessing this list, so no lock is necessary */
@@ -501,6 +518,8 @@ static AST_LIST_HEAD_NOLOCK_STATIC(createlist, direntry);
static AST_LIST_HEAD_NOLOCK_STATIC(openlist, direntry);
#endif
+#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
+
static void queue_file(const char *filename, time_t when)
{
struct stat st;
diff --git a/res/ael/ael.flex b/res/ael/ael.flex
index a412eaf34..b1b2bd76d 100644
--- a/res/ael/ael.flex
+++ b/res/ael/ael.flex
@@ -68,7 +68,7 @@
%option bison-locations
%{
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
diff --git a/res/ael/ael.tab.c b/res/ael/ael.tab.c
index f146ffb09..9f1f19bb6 100644
--- a/res/ael/ael.tab.c
+++ b/res/ael/ael.tab.c
@@ -99,7 +99,7 @@
*
*/
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
diff --git a/res/ael/ael.y b/res/ael/ael.y
index 5ab9c969f..e5c1655c5 100644
--- a/res/ael/ael.y
+++ b/res/ael/ael.y
@@ -22,7 +22,7 @@
*
*/
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
diff --git a/res/ael/ael_lex.c b/res/ael/ael_lex.c
index c5eba92b7..a7a20aa60 100644
--- a/res/ael/ael_lex.c
+++ b/res/ael/ael_lex.c
@@ -1,4 +1,4 @@
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
#include "asterisk.h"
#line 2 "ael_lex.c"
diff --git a/res/ael/pval.c b/res/ael/pval.c
index d29bbcfbd..d5ea5accf 100644
--- a/res/ael/pval.c
+++ b/res/ael/pval.c
@@ -27,7 +27,7 @@
<support_level>extended</support_level>
***/
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
diff --git a/res/ari/resource_events.c b/res/ari/resource_events.c
index eddcb662c..e666f2e05 100644
--- a/res/ari/resource_events.c
+++ b/res/ari/resource_events.c
@@ -119,6 +119,10 @@ static void app_handler(void *data, const char *app_name,
const char *msg_application = S_OR(
ast_json_string_get(ast_json_object_get(message, "application")),
"");
+
+ if (!session) {
+ return;
+ }
/* Determine if we've been replaced */
if (strcmp(msg_type, "ApplicationReplaced") == 0 &&
@@ -168,7 +172,40 @@ static int session_register_app(struct event_session *session,
return 0;
}
-void ast_ari_websocket_events_event_websocket(struct ast_ari_websocket_session *ws_session,
+int ast_ari_websocket_events_event_websocket_attempted(struct ast_tcptls_session_instance *ser,
+ struct ast_variable *headers,
+ struct ast_ari_events_event_websocket_args *args)
+{
+ int res = 0;
+ size_t i, j;
+
+ ast_debug(3, "/events WebSocket attempted\n");
+
+ if (args->app_count == 0) {
+ ast_http_error(ser, 400, "Bad Request", "Missing param 'app'");
+ return -1;
+ }
+
+ for (i = 0; i < args->app_count; ++i) {
+ if (ast_strlen_zero(args->app[i])) {
+ res = -1;
+ break;
+ }
+
+ res |= stasis_app_register(args->app[i], app_handler, NULL);
+ }
+
+ if (res) {
+ for (j = 0; j < i; ++j) {
+ stasis_app_unregister(args->app[j]);
+ }
+ ast_http_error(ser, 400, "Bad Request", "Invalid application provided in param 'app'.");
+ }
+
+ return res;
+}
+
+void ast_ari_websocket_events_event_websocket_established(struct ast_ari_websocket_session *ws_session,
struct ast_variable *headers,
struct ast_ari_events_event_websocket_args *args)
{
diff --git a/res/ari/resource_events.h b/res/ari/resource_events.h
index 646cf9bfc..2b631819b 100644
--- a/res/ari/resource_events.h
+++ b/res/ari/resource_events.h
@@ -48,6 +48,19 @@ struct ast_ari_events_event_websocket_args {
/*! Parsing context for app. */
char *app_parse;
};
+
+/*!
+ * \brief WebSocket connection for events.
+ *
+ * \param ser HTTP TCP/TLS Server Session
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ *
+ * \retval 0 success
+ * \retval non-zero error
+ */
+int ast_ari_websocket_events_event_websocket_attempted(struct ast_tcptls_session_instance *ser, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args);
+
/*!
* \brief WebSocket connection for events.
*
@@ -55,7 +68,7 @@ struct ast_ari_events_event_websocket_args {
* \param headers HTTP headers.
* \param args Swagger parameters.
*/
-void ast_ari_websocket_events_event_websocket(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args);
+void ast_ari_websocket_events_event_websocket_established(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args);
/*! Argument struct for ast_ari_events_user_event() */
struct ast_ari_events_user_event_args {
/*! Event name */
diff --git a/res/res_adsi.c b/res/res_adsi.c
index 80086f8fd..314a4eabd 100644
--- a/res/res_adsi.c
+++ b/res/res_adsi.c
@@ -1210,9 +1210,9 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "ADSI Resource",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_ael_share.c b/res/res_ael_share.c
index 936d3b42e..c6306abb8 100644
--- a/res/res_ael_share.c
+++ b/res/res_ael_share.c
@@ -53,7 +53,7 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "share-able code for AEL",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module
+);
diff --git a/res/res_agi.c b/res/res_agi.c
index fb62cc8c3..1dc6d4040 100644
--- a/res/res_agi.c
+++ b/res/res_agi.c
@@ -4314,8 +4314,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Asterisk Gateway Interface (AGI)",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_ari.c b/res/res_ari.c
index d676b5ee1..bdc4379b2 100644
--- a/res/res_ari.c
+++ b/res/res_ari.c
@@ -1106,4 +1106,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_
.reload = reload_module,
.nonoptreq = "res_http_websocket",
.load_pri = AST_MODPRI_APP_DEPEND,
- );
+);
diff --git a/res/res_ari_applications.c b/res/res_ari_applications.c
index 5d38616f8..f25fd070f 100644
--- a/res/res_ari_applications.c
+++ b/res/res_ari_applications.c
@@ -546,4 +546,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - Sta
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_ari,res_stasis",
- );
+);
diff --git a/res/res_ari_asterisk.c b/res/res_ari_asterisk.c
index 92f013301..cb6219b42 100644
--- a/res/res_ari_asterisk.c
+++ b/res/res_ari_asterisk.c
@@ -447,4 +447,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - Ast
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_ari,res_stasis",
- );
+);
diff --git a/res/res_ari_bridges.c b/res/res_ari_bridges.c
index 72a34648b..633dc94eb 100644
--- a/res/res_ari_bridges.c
+++ b/res/res_ari_bridges.c
@@ -1417,4 +1417,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - Bri
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_ari,res_stasis",
- );
+);
diff --git a/res/res_ari_channels.c b/res/res_ari_channels.c
index a2305a514..05d8d0714 100644
--- a/res/res_ari_channels.c
+++ b/res/res_ari_channels.c
@@ -2739,4 +2739,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - Cha
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_ari,res_stasis",
- );
+);
diff --git a/res/res_ari_device_states.c b/res/res_ari_device_states.c
index c4eff8ccd..f6d578194 100644
--- a/res/res_ari_device_states.c
+++ b/res/res_ari_device_states.c
@@ -362,4 +362,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - Dev
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_ari,res_stasis",
- );
+);
diff --git a/res/res_ari_endpoints.c b/res/res_ari_endpoints.c
index c23fbc658..af50a0949 100644
--- a/res/res_ari_endpoints.c
+++ b/res/res_ari_endpoints.c
@@ -502,4 +502,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - End
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_ari,res_stasis",
- );
+);
diff --git a/res/res_ari_events.c b/res/res_ari_events.c
index aea318d68..454233945 100644
--- a/res/res_ari_events.c
+++ b/res/res_ari_events.c
@@ -53,7 +53,92 @@ ASTERISK_REGISTER_FILE()
#define MAX_VALS 128
-static void ast_ari_events_event_websocket_ws_cb(struct ast_websocket *ws_session,
+static int ast_ari_events_event_websocket_ws_attempted_cb(struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *headers)
+{
+ struct ast_ari_events_event_websocket_args args = {};
+ int res = 0;
+ RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
+ struct ast_variable *i;
+
+ response = ast_calloc(1, sizeof(*response));
+ if (!response) {
+ ast_log(LOG_ERROR, "Failed to create response.\n");
+ goto fin;
+ }
+
+ for (i = get_params; i; i = i->next) {
+ if (strcmp(i->name, "app") == 0) {
+ /* Parse comma separated list */
+ char *vals[MAX_VALS];
+ size_t j;
+
+ args.app_parse = ast_strdup(i->value);
+ if (!args.app_parse) {
+ ast_ari_response_alloc_failed(response);
+ goto fin;
+ }
+
+ if (strlen(args.app_parse) == 0) {
+ /* ast_app_separate_args can't handle "" */
+ args.app_count = 1;
+ vals[0] = args.app_parse;
+ } else {
+ args.app_count = ast_app_separate_args(
+ args.app_parse, ',', vals,
+ ARRAY_LEN(vals));
+ }
+
+ if (args.app_count == 0) {
+ ast_ari_response_alloc_failed(response);
+ goto fin;
+ }
+
+ if (args.app_count >= MAX_VALS) {
+ ast_ari_response_error(response, 400,
+ "Bad Request",
+ "Too many values for app");
+ goto fin;
+ }
+
+ args.app = ast_malloc(sizeof(*args.app) * args.app_count);
+ if (!args.app) {
+ ast_ari_response_alloc_failed(response);
+ goto fin;
+ }
+
+ for (j = 0; j < args.app_count; ++j) {
+ args.app[j] = (vals[j]);
+ }
+ } else
+ {}
+ }
+
+ res = ast_ari_websocket_events_event_websocket_attempted(ser, headers, &args);
+
+fin: __attribute__((unused))
+ if (!response) {
+ ast_http_error(ser, 500, "Server Error", "Memory allocation error");
+ res = -1;
+ } else if (response->response_code != 0) {
+ /* Param parsing failure */
+ RAII_VAR(char *, msg, NULL, ast_json_free);
+ if (response->message) {
+ msg = ast_json_dump_string(response->message);
+ } else {
+ ast_log(LOG_ERROR, "Missing response message\n");
+ }
+
+ if (msg) {
+ ast_http_error(ser, response->response_code, response->response_text, msg);
+ }
+ res = -1;
+ }
+ ast_free(args.app_parse);
+ ast_free(args.app);
+ return res;
+}
+
+static void ast_ari_events_event_websocket_ws_established_cb(struct ast_websocket *ws_session,
struct ast_variable *get_params, struct ast_variable *headers)
{
struct ast_ari_events_event_websocket_args args = {};
@@ -126,16 +211,11 @@ static void ast_ari_events_event_websocket_ws_cb(struct ast_websocket *ws_sessio
{}
}
- ast_ari_websocket_events_event_websocket(session, headers, &args);
+ ast_ari_websocket_events_event_websocket_established(session, headers, &args);
fin: __attribute__((unused))
if (response && response->response_code != 0) {
/* Param parsing failure */
- /* TODO - ideally, this would return the error code to the
- * HTTP client; but we've already done the WebSocket
- * negotiation. Param parsing should happen earlier, but we
- * need a way to pass it through the WebSocket code to the
- * callback */
RAII_VAR(char *, msg, NULL, ast_json_free);
if (response->message) {
msg = ast_json_dump_string(response->message);
@@ -351,12 +431,22 @@ static struct stasis_rest_handlers events = {
static int load_module(void)
{
int res = 0;
+ struct ast_websocket_protocol *protocol;
+
events.ws_server = ast_websocket_server_create();
if (!events.ws_server) {
return AST_MODULE_LOAD_FAILURE;
}
- res |= ast_websocket_server_add_protocol(events.ws_server,
- "ari", ast_ari_events_event_websocket_ws_cb);
+
+ protocol = ast_websocket_sub_protocol_alloc("ari");
+ if (!protocol) {
+ ao2_ref(events.ws_server, -1);
+ events.ws_server = NULL;
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ protocol->session_attempted = ast_ari_events_event_websocket_ws_attempted_cb;
+ protocol->session_established = ast_ari_events_event_websocket_ws_established_cb;
+ res |= ast_websocket_server_add_protocol2(events.ws_server, protocol);
stasis_app_ref();
res |= ast_ari_add_handler(&events);
return res;
@@ -376,4 +466,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - Web
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_ari,res_stasis",
- );
+);
diff --git a/res/res_ari_mailboxes.c b/res/res_ari_mailboxes.c
index 0fe83c224..4f3c73120 100644
--- a/res/res_ari_mailboxes.c
+++ b/res/res_ari_mailboxes.c
@@ -368,4 +368,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - Mai
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_ari,res_stasis",
- );
+);
diff --git a/res/res_ari_model.c b/res/res_ari_model.c
index 64e2c5d9b..14265cdce 100644
--- a/res/res_ari_model.c
+++ b/res/res_ari_model.c
@@ -208,4 +208,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER | AST_MODFLAG_GLOBAL_SY
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_APP_DEPEND,
- );
+);
diff --git a/res/res_ari_playbacks.c b/res/res_ari_playbacks.c
index a0fc29a5b..d01802145 100644
--- a/res/res_ari_playbacks.c
+++ b/res/res_ari_playbacks.c
@@ -319,4 +319,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - Pla
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_ari,res_stasis",
- );
+);
diff --git a/res/res_ari_recordings.c b/res/res_ari_recordings.c
index 61d23aa32..df4a124be 100644
--- a/res/res_ari_recordings.c
+++ b/res/res_ari_recordings.c
@@ -843,4 +843,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - Rec
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_ari,res_stasis",
- );
+);
diff --git a/res/res_ari_sounds.c b/res/res_ari_sounds.c
index ea7f60a0b..8bb217153 100644
--- a/res/res_ari_sounds.c
+++ b/res/res_ari_sounds.c
@@ -248,4 +248,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - Sou
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_ari,res_stasis",
- );
+);
diff --git a/res/res_calendar.c b/res/res_calendar.c
index 78bc24b21..1a9878b32 100644
--- a/res/res_calendar.c
+++ b/res/res_calendar.c
@@ -1913,9 +1913,9 @@ static int load_module(void)
return AST_MODULE_LOAD_SUCCESS;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Asterisk Calendar integration",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
+);
diff --git a/res/res_calendar_caldav.c b/res/res_calendar_caldav.c
index 2e3eb934b..590408571 100644
--- a/res/res_calendar_caldav.c
+++ b/res/res_calendar_caldav.c
@@ -725,8 +725,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Asterisk CalDAV Calendar Integration",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_DEVSTATE_PLUGIN,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_DEVSTATE_PLUGIN,
+);
diff --git a/res/res_calendar_exchange.c b/res/res_calendar_exchange.c
index b11edcf22..577d65ba6 100644
--- a/res/res_calendar_exchange.c
+++ b/res/res_calendar_exchange.c
@@ -741,8 +741,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Asterisk MS Exchange Calendar Integration",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_DEVSTATE_PLUGIN,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_DEVSTATE_PLUGIN,
+);
diff --git a/res/res_calendar_icalendar.c b/res/res_calendar_icalendar.c
index 4c7c8a903..507f19cbf 100644
--- a/res/res_calendar_icalendar.c
+++ b/res/res_calendar_icalendar.c
@@ -504,8 +504,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Asterisk iCalendar .ics file integration",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_DEVSTATE_PLUGIN,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_DEVSTATE_PLUGIN,
+);
diff --git a/res/res_chan_stats.c b/res/res_chan_stats.c
index c9167d93a..8b188dc13 100644
--- a/res/res_chan_stats.c
+++ b/res/res_chan_stats.c
@@ -184,4 +184,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Example of how to use St
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_statsd"
- );
+);
diff --git a/res/res_clialiases.c b/res/res_clialiases.c
index ba55551b9..74d763ac7 100644
--- a/res/res_clialiases.c
+++ b/res/res_clialiases.c
@@ -300,8 +300,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "CLI Aliases",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload_module,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload_module,
+);
diff --git a/res/res_config_curl.c b/res/res_config_curl.c
index 985f51eed..7b571d549 100644
--- a/res/res_config_curl.c
+++ b/res/res_config_curl.c
@@ -658,9 +658,9 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Realtime Curl configuration",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload_module,
- .load_pri = AST_MODPRI_REALTIME_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload_module,
+ .load_pri = AST_MODPRI_REALTIME_DRIVER,
+);
diff --git a/res/res_config_odbc.c b/res/res_config_odbc.c
index b36bcb68a..37c6d3ff9 100644
--- a/res/res_config_odbc.c
+++ b/res/res_config_odbc.c
@@ -1221,9 +1221,9 @@ static int reload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Realtime ODBC configuration",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload_module,
- .load_pri = AST_MODPRI_REALTIME_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload_module,
+ .load_pri = AST_MODPRI_REALTIME_DRIVER,
+);
diff --git a/res/res_config_pgsql.c b/res/res_config_pgsql.c
index 00cde50ed..c8cd461f3 100644
--- a/res/res_config_pgsql.c
+++ b/res/res_config_pgsql.c
@@ -277,63 +277,30 @@ static struct tables *find_table(const char *database, const char *orig_tablenam
/* Not found, scan the table */
if (has_schema_support) {
- char *schemaname, *tablename;
+ char *schemaname, *tablename, *tmp_schemaname, *tmp_tablename;
if (strchr(orig_tablename, '.')) {
- schemaname = ast_strdupa(orig_tablename);
- tablename = strchr(schemaname, '.');
- *tablename++ = '\0';
+ tmp_schemaname = ast_strdupa(orig_tablename);
+ tmp_tablename = strchr(tmp_schemaname, '.');
+ *tmp_tablename++ = '\0';
} else {
- schemaname = "";
- tablename = ast_strdupa(orig_tablename);
+ tmp_schemaname = "";
+ tmp_tablename = ast_strdupa(orig_tablename);
}
- /* Escape special characters in schemaname */
- if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
- char *tmp = schemaname, *ptr;
-
- ptr = schemaname = ast_alloca(strlen(tmp) * 2 + 1);
- for (; *tmp; tmp++) {
- if (strchr("\\'", *tmp)) {
- *ptr++ = *tmp;
- }
- *ptr++ = *tmp;
- }
- *ptr = '\0';
- }
- /* Escape special characters in tablename */
- if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
- char *tmp = tablename, *ptr;
-
- ptr = tablename = ast_alloca(strlen(tmp) * 2 + 1);
- for (; *tmp; tmp++) {
- if (strchr("\\'", *tmp)) {
- *ptr++ = *tmp;
- }
- *ptr++ = *tmp;
- }
- *ptr = '\0';
- }
+ tablename = ast_alloca(strlen(tmp_tablename) * 2 + 1);
+ PQescapeStringConn(pgsqlConn, tablename, tmp_tablename, strlen(tmp_tablename), NULL);
+ schemaname = ast_alloca(strlen(tmp_schemaname) * 2 + 1);
+ PQescapeStringConn(pgsqlConn, schemaname, tmp_schemaname, strlen(tmp_schemaname), NULL);
ast_str_set(&sql, 0, "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM (((pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace AND c.relname = '%s' AND n.nspname = %s%s%s) INNER JOIN pg_catalog.pg_attribute a ON (NOT a.attisdropped) AND a.attnum > 0 AND a.attrelid = c.oid) INNER JOIN pg_catalog.pg_type t ON t.oid = a.atttypid) LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum ORDER BY n.nspname, c.relname, attnum",
tablename,
ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
} else {
- /* Escape special characters in tablename */
- if (strchr(orig_tablename, '\\') || strchr(orig_tablename, '\'')) {
- const char *tmp = orig_tablename;
- char *ptr;
-
- orig_tablename = ptr = ast_alloca(strlen(tmp) * 2 + 1);
- for (; *tmp; tmp++) {
- if (strchr("\\'", *tmp)) {
- *ptr++ = *tmp;
- }
- *ptr++ = *tmp;
- }
- *ptr = '\0';
- }
+ char *tablename;
+ tablename = ast_alloca(strlen(orig_tablename) * 2 + 1);
+ PQescapeStringConn(pgsqlConn, tablename, orig_tablename, strlen(orig_tablename), NULL);
- ast_str_set(&sql, 0, "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM pg_class c, pg_type t, pg_attribute a LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum WHERE c.oid = a.attrelid AND a.atttypid = t.oid AND (a.attnum > 0) AND c.relname = '%s' ORDER BY c.relname, attnum", orig_tablename);
+ ast_str_set(&sql, 0, "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM pg_class c, pg_type t, pg_attribute a LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum WHERE c.oid = a.attrelid AND a.atttypid = t.oid AND (a.attnum > 0) AND c.relname = '%s' ORDER BY c.relname, attnum", tablename);
}
exec_result = pgsql_exec(database, orig_tablename, ast_str_buffer(sql), &result);
@@ -1655,9 +1622,9 @@ static char *handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd,
/* needs usecount semantics defined */
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PostgreSQL RealTime Configuration Driver",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_REALTIME_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_REALTIME_DRIVER,
+);
diff --git a/res/res_config_sqlite.c b/res/res_config_sqlite.c
index 41fbb0e95..5659b4e41 100644
--- a/res/res_config_sqlite.c
+++ b/res/res_config_sqlite.c
@@ -1768,8 +1768,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Realtime SQLite configuration",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_REALTIME_DRIVER,
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_REALTIME_DRIVER,
);
diff --git a/res/res_crypto.c b/res/res_crypto.c
index a1d711c81..d8f328f08 100644
--- a/res/res_crypto.c
+++ b/res/res_crypto.c
@@ -663,9 +663,9 @@ static int unload_module(void)
/* needs usecount semantics defined */
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Cryptographic Digital Signatures",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND, /*!< Since we don't have a config file, we could move up to REALTIME_DEPEND, if necessary */
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND, /*!< Since we don't have a config file, we could move up to REALTIME_DEPEND, if necessary */
+);
diff --git a/res/res_curl.c b/res/res_curl.c
index c3f22c785..eeacbd298 100644
--- a/res/res_curl.c
+++ b/res/res_curl.c
@@ -97,8 +97,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "cURL Resource Module",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_REALTIME_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_REALTIME_DEPEND,
+);
diff --git a/res/res_fax.c b/res/res_fax.c
index 29ec315f7..62fe726de 100644
--- a/res/res_fax.c
+++ b/res/res_fax.c
@@ -4678,9 +4678,9 @@ static int reload_module(void)
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Generic FAX Applications",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_fax_spandsp.c b/res/res_fax_spandsp.c
index 288f91dac..46db86460 100644
--- a/res/res_fax_spandsp.c
+++ b/res/res_fax_spandsp.c
@@ -48,14 +48,12 @@
<support_level>extended</support_level>
***/
+/* Needed for spandsp headers */
+#define ASTMM_LIBC ASTMM_IGNORE
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
-#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
-#include <spandsp.h>
-#include <spandsp/version.h>
-
#include "asterisk/logger.h"
#include "asterisk/module.h"
#include "asterisk/strings.h"
@@ -67,6 +65,10 @@ ASTERISK_REGISTER_FILE()
#include "asterisk/channel.h"
#include "asterisk/format_cache.h"
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+#include <spandsp.h>
+#include <spandsp/version.h>
+
#define SPANDSP_FAX_SAMPLES 160
#define SPANDSP_FAX_TIMER_RATE 8000 / SPANDSP_FAX_SAMPLES /* 50 ticks per second, 20ms, 160 samples per second */
#define SPANDSP_ENGAGE_UDPTL_NAT_RETRY 3
@@ -1263,7 +1265,7 @@ static int load_module(void)
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Spandsp G.711 and T.38 FAX Technologies",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/res/res_hep.c b/res/res_hep.c
index 72b806945..e831fff02 100644
--- a/res/res_hep.c
+++ b/res/res_hep.c
@@ -625,4 +625,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_
.unload = unload_module,
.reload = reload_module,
.load_pri = AST_MODPRI_APP_DEPEND,
- );
+);
diff --git a/res/res_hep_pjsip.c b/res/res_hep_pjsip.c
index d0c95e348..87d68e36d 100644
--- a/res/res_hep_pjsip.c
+++ b/res/res_hep_pjsip.c
@@ -172,8 +172,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP HEPv3 Logger",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_DEFAULT,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_DEFAULT,
+);
diff --git a/res/res_hep_rtcp.c b/res/res_hep_rtcp.c
index a6b7351af..25aed1520 100644
--- a/res/res_hep_rtcp.c
+++ b/res/res_hep_rtcp.c
@@ -131,7 +131,7 @@ static int load_module(void)
static int unload_module(void)
{
if (stasis_rtp_subscription) {
- stasis_rtp_subscription = stasis_unsubscribe(stasis_rtp_subscription);
+ stasis_rtp_subscription = stasis_unsubscribe_and_join(stasis_rtp_subscription);
}
return 0;
@@ -141,4 +141,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "RTCP HEPv3 Logger",
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_DEFAULT,
- );
+);
diff --git a/res/res_http_websocket.c b/res/res_http_websocket.c
index 0481e7ecd..40aedff72 100644
--- a/res/res_http_websocket.c
+++ b/res/res_http_websocket.c
@@ -88,16 +88,10 @@ struct ast_websocket {
struct websocket_client *client; /*!< Client object when connected as a client websocket */
};
-/*! \brief Structure definition for protocols */
-struct websocket_protocol {
- char *name; /*!< Name of the protocol */
- ast_websocket_callback callback; /*!< Callback called when a new session is established */
-};
-
/*! \brief Hashing function for protocols */
static int protocol_hash_fn(const void *obj, const int flags)
{
- const struct websocket_protocol *protocol = obj;
+ const struct ast_websocket_protocol *protocol = obj;
const char *name = obj;
return ast_str_case_hash(flags & OBJ_KEY ? name : protocol->name);
@@ -106,7 +100,7 @@ static int protocol_hash_fn(const void *obj, const int flags)
/*! \brief Comparison function for protocols */
static int protocol_cmp_fn(void *obj, void *arg, int flags)
{
- const struct websocket_protocol *protocol1 = obj, *protocol2 = arg;
+ const struct ast_websocket_protocol *protocol1 = obj, *protocol2 = arg;
const char *protocol = arg;
return !strcasecmp(protocol1->name, flags & OBJ_KEY ? protocol : protocol2->name) ? CMP_MATCH | CMP_STOP : 0;
@@ -115,7 +109,7 @@ static int protocol_cmp_fn(void *obj, void *arg, int flags)
/*! \brief Destructor function for protocols */
static void protocol_destroy_fn(void *obj)
{
- struct websocket_protocol *protocol = obj;
+ struct ast_websocket_protocol *protocol = obj;
ast_free(protocol->name);
}
@@ -182,54 +176,91 @@ static void session_destroy_fn(void *obj)
ast_free(session->payload);
}
+struct ast_websocket_protocol *AST_OPTIONAL_API_NAME(ast_websocket_sub_protocol_alloc)(const char *name)
+{
+ struct ast_websocket_protocol *protocol;
+
+ protocol = ao2_alloc(sizeof(*protocol), protocol_destroy_fn);
+ if (!protocol) {
+ return NULL;
+ }
+
+ protocol->name = ast_strdup(name);
+ if (!protocol->name) {
+ ao2_ref(protocol, -1);
+ return NULL;
+ }
+ protocol->version = AST_WEBSOCKET_PROTOCOL_VERSION;
+
+ return protocol;
+}
+
int AST_OPTIONAL_API_NAME(ast_websocket_server_add_protocol)(struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
{
- struct websocket_protocol *protocol;
+ struct ast_websocket_protocol *protocol;
if (!server->protocols) {
return -1;
}
- ao2_lock(server->protocols);
+ protocol = ast_websocket_sub_protocol_alloc(name);
+ if (!protocol) {
+ ao2_unlock(server->protocols);
+ return -1;
+ }
+ protocol->session_established = callback;
- /* Ensure a second protocol handler is not registered for the same protocol */
- if ((protocol = ao2_find(server->protocols, name, OBJ_KEY | OBJ_NOLOCK))) {
+ if (ast_websocket_server_add_protocol2(server, protocol)) {
ao2_ref(protocol, -1);
- ao2_unlock(server->protocols);
return -1;
}
- if (!(protocol = ao2_alloc(sizeof(*protocol), protocol_destroy_fn))) {
- ao2_unlock(server->protocols);
+ return 0;
+}
+
+int AST_OPTIONAL_API_NAME(ast_websocket_server_add_protocol2)(struct ast_websocket_server *server, struct ast_websocket_protocol *protocol)
+{
+ struct ast_websocket_protocol *existing;
+
+ if (!server->protocols) {
return -1;
}
- if (!(protocol->name = ast_strdup(name))) {
- ao2_ref(protocol, -1);
- ao2_unlock(server->protocols);
+ if (protocol->version != AST_WEBSOCKET_PROTOCOL_VERSION) {
+ ast_log(LOG_WARNING, "WebSocket could not register sub-protocol '%s': "
+ "expected version '%u', got version '%u'\n",
+ protocol->name, AST_WEBSOCKET_PROTOCOL_VERSION, protocol->version);
return -1;
}
- protocol->callback = callback;
+ ao2_lock(server->protocols);
+
+ /* Ensure a second protocol handler is not registered for the same protocol */
+ existing = ao2_find(server->protocols, protocol->name, OBJ_KEY | OBJ_NOLOCK);
+ if (existing) {
+ ao2_ref(existing, -1);
+ ao2_unlock(server->protocols);
+ return -1;
+ }
ao2_link_flags(server->protocols, protocol, OBJ_NOLOCK);
ao2_unlock(server->protocols);
- ao2_ref(protocol, -1);
- ast_verb(2, "WebSocket registered sub-protocol '%s'\n", name);
+ ast_verb(2, "WebSocket registered sub-protocol '%s'\n", protocol->name);
+ ao2_ref(protocol, -1);
return 0;
}
int AST_OPTIONAL_API_NAME(ast_websocket_server_remove_protocol)(struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
{
- struct websocket_protocol *protocol;
+ struct ast_websocket_protocol *protocol;
if (!(protocol = ao2_find(server->protocols, name, OBJ_KEY))) {
return -1;
}
- if (protocol->callback != callback) {
+ if (protocol->session_established != callback) {
ao2_ref(protocol, -1);
return -1;
}
@@ -600,7 +631,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, cha
/*!
* \brief If the server has exactly one configured protocol, return it.
*/
-static struct websocket_protocol *one_protocol(
+static struct ast_websocket_protocol *one_protocol(
struct ast_websocket_server *server)
{
SCOPED_AO2LOCK(lock, server->protocols);
@@ -643,7 +674,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
struct ast_variable *v;
char *upgrade = NULL, *key = NULL, *key1 = NULL, *key2 = NULL, *protos = NULL, *requested_protocols = NULL, *protocol = NULL;
int version = 0, flags = 1;
- struct websocket_protocol *protocol_handler = NULL;
+ struct ast_websocket_protocol *protocol_handler = NULL;
struct ast_websocket *session;
struct ast_websocket_server *server;
@@ -742,6 +773,14 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
}
session->timeout = AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT;
+ if (protocol_handler->session_attempted
+ && protocol_handler->session_attempted(ser, get_vars, headers)) {
+ ast_debug(3, "WebSocket connection from '%s' rejected by protocol handler '%s'\n",
+ ast_sockaddr_stringify(&ser->remote_address), protocol_handler->name);
+ ao2_ref(protocol_handler, -1);
+ return 0;
+ }
+
fprintf(ser->f, "HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: %s\r\n"
"Connection: Upgrade\r\n"
@@ -797,7 +836,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
/* Give up ownership of the socket and pass it to the protocol handler */
ast_tcptls_stream_set_exclusive_input(ser->stream_cookie, 0);
- protocol_handler->callback(session, get_vars, headers);
+ protocol_handler->session_established(session, get_vars, headers);
ao2_ref(protocol_handler, -1);
/*
@@ -881,6 +920,22 @@ int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol)(const char *name, ast_webs
return res;
}
+int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol2)(struct ast_websocket_protocol *protocol)
+{
+ struct ast_websocket_server *ws_server = websocketuri.data;
+
+ if (!ws_server) {
+ return -1;
+ }
+
+ if (ast_websocket_server_add_protocol2(ws_server, protocol)) {
+ return -1;
+ }
+
+ ast_module_ref(ast_module_info->self);
+ return 0;
+}
+
static int websocket_remove_protocol_internal(const char *name, ast_websocket_callback callback)
{
struct ast_websocket_server *ws_server = websocketuri.data;
@@ -1321,8 +1376,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "HTTP WebSocket Support",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
diff --git a/res/res_manager_devicestate.c b/res/res_manager_devicestate.c
index 3d1f1ab10..a506e9783 100644
--- a/res/res_manager_devicestate.c
+++ b/res/res_manager_devicestate.c
@@ -143,8 +143,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Manager Device State Topic Forwarder",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_DEVSTATE_CONSUMER,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_DEVSTATE_CONSUMER,
+);
diff --git a/res/res_manager_presencestate.c b/res/res_manager_presencestate.c
index bb9e63ab9..fb5719214 100644
--- a/res/res_manager_presencestate.c
+++ b/res/res_manager_presencestate.c
@@ -142,8 +142,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Manager Presence State Topic Forwarder",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_DEVSTATE_CONSUMER,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_DEVSTATE_CONSUMER,
+);
diff --git a/res/res_monitor.c b/res/res_monitor.c
index 047b37008..6fee0c2a0 100644
--- a/res/res_monitor.c
+++ b/res/res_monitor.c
@@ -1001,8 +1001,8 @@ static int unload_module(void)
/* usecount semantics need to be defined */
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Call Monitoring Resource",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
diff --git a/res/res_mwi_external_ami.c b/res/res_mwi_external_ami.c
index 24562e193..7777214cd 100644
--- a/res/res_mwi_external_ami.c
+++ b/res/res_mwi_external_ami.c
@@ -357,9 +357,9 @@ static int load_module(void)
ast_mwi_external_ref();
res = 0;
- res |= ast_manager_register_xml_core("MWIGet", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, mwi_mailbox_get);
- res |= ast_manager_register_xml_core("MWIDelete", EVENT_FLAG_CALL, mwi_mailbox_delete);
- res |= ast_manager_register_xml_core("MWIUpdate", EVENT_FLAG_CALL, mwi_mailbox_update);
+ res |= ast_manager_register_xml("MWIGet", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, mwi_mailbox_get);
+ res |= ast_manager_register_xml("MWIDelete", EVENT_FLAG_CALL, mwi_mailbox_delete);
+ res |= ast_manager_register_xml("MWIUpdate", EVENT_FLAG_CALL, mwi_mailbox_update);
if (res) {
unload_module();
return AST_MODULE_LOAD_DECLINE;
diff --git a/res/res_odbc.c b/res/res_odbc.c
index 2530a4879..06009a2a5 100644
--- a/res/res_odbc.c
+++ b/res/res_odbc.c
@@ -1909,9 +1909,9 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "ODBC resource",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_REALTIME_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_REALTIME_DEPEND,
+);
diff --git a/res/res_phoneprov.c b/res/res_phoneprov.c
index b9eeccee6..cf566b3d0 100644
--- a/res/res_phoneprov.c
+++ b/res/res_phoneprov.c
@@ -1489,12 +1489,12 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
/**** Public API for register/unregister, set defaults, and add extension. ****/
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index a2638099b..7bf489772 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -2842,126 +2842,6 @@ static pj_bool_t does_method_match(const pj_str_t *message_method, const char *s
#define TIMER_INACTIVE 0
#define TIMEOUT_TIMER2 5
-struct tsx_data {
- void *token;
- void (*cb)(void*, pjsip_event*);
- pjsip_transaction *tsx;
- pj_timer_entry *timeout_timer;
-};
-
-static void send_tsx_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event);
-
-pjsip_module send_tsx_module = {
- .name = { "send_tsx_module", 23 },
- .id = -1,
- .priority = PJSIP_MOD_PRIORITY_APPLICATION,
- .on_tsx_state = &send_tsx_on_tsx_state,
-};
-
-/*! \brief This is the pjsip_tsx_send_msg callback */
-static void send_tsx_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
-{
- struct tsx_data *tsx_data;
-
- if (event->type != PJSIP_EVENT_TSX_STATE) {
- return;
- }
-
- tsx_data = (struct tsx_data*) tsx->mod_data[send_tsx_module.id];
- if (tsx_data == NULL) {
- return;
- }
-
- if (tsx->status_code < 200) {
- return;
- }
-
- if (event->body.tsx_state.type == PJSIP_EVENT_TIMER) {
- ast_debug(1, "PJSIP tsx timer expired\n");
- }
-
- if (tsx_data->timeout_timer && tsx_data->timeout_timer->id != TIMER_INACTIVE) {
- pj_mutex_lock(tsx->mutex_b);
- pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(tsx->endpt),
- tsx_data->timeout_timer, TIMER_INACTIVE);
- pj_mutex_unlock(tsx->mutex_b);
- }
-
- /* Call the callback, if any, and prevent the callback from being called again
- * by clearing the transaction's module_data.
- */
- tsx->mod_data[send_tsx_module.id] = NULL;
-
- if (tsx_data->cb) {
- (*tsx_data->cb)(tsx_data->token, event);
- }
-}
-
-static void tsx_timer_callback(pj_timer_heap_t *theap, pj_timer_entry *entry)
-{
- struct tsx_data *tsx_data = entry->user_data;
-
- entry->id = TIMER_INACTIVE;
- ast_debug(1, "Internal tsx timer expired\n");
- pjsip_tsx_terminate(tsx_data->tsx, PJSIP_SC_TSX_TIMEOUT);
-}
-
-static pj_status_t endpt_send_transaction(pjsip_endpoint *endpt,
- pjsip_tx_data *tdata, int timeout, void *token,
- pjsip_endpt_send_callback cb)
-{
- pjsip_transaction *tsx;
- struct tsx_data *tsx_data;
- pj_status_t status;
- pjsip_event event;
-
- ast_assert(endpt && tdata);
-
- status = pjsip_tsx_create_uac(&send_tsx_module, tdata, &tsx);
- if (status != PJ_SUCCESS) {
- pjsip_tx_data_dec_ref(tdata);
- ast_log(LOG_ERROR, "Unable to create pjsip uac\n");
- return status;
- }
-
- tsx_data = PJ_POOL_ALLOC_T(tsx->pool, struct tsx_data);
- tsx_data->token = token;
- tsx_data->cb = cb;
- tsx_data->tsx = tsx;
- if (timeout > 0) {
- tsx_data->timeout_timer = PJ_POOL_ALLOC_T(tsx->pool, pj_timer_entry);
- } else {
- tsx_data->timeout_timer = NULL;
- }
- tsx->mod_data[send_tsx_module.id] = tsx_data;
-
- PJSIP_EVENT_INIT_TX_MSG(event, tdata);
- pjsip_tx_data_set_transport(tdata, &tsx->tp_sel);
-
- if (timeout > 0) {
- pj_time_val timeout_timer_val = { timeout / 1000, timeout % 1000 };
-
- pj_timer_entry_init(tsx_data->timeout_timer, TIMEOUT_TIMER2,
- tsx_data, &tsx_timer_callback);
- pj_mutex_lock(tsx->mutex_b);
- pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(tsx->endpt),
- tsx_data->timeout_timer, TIMER_INACTIVE);
- pj_timer_heap_schedule(pjsip_endpt_get_timer_heap(tsx->endpt),
- tsx_data->timeout_timer, &timeout_timer_val);
- tsx_data->timeout_timer->id = TIMEOUT_TIMER2;
- pj_mutex_unlock(tsx->mutex_b);
- }
-
- status = (*tsx->state_handler)(tsx, &event);
- pjsip_tx_data_dec_ref(tdata);
- if (status != PJ_SUCCESS) {
- ast_log(LOG_ERROR, "Unable to send message\n");
- return status;
- }
-
- return status;
-}
-
/*! \brief Structure to hold information about an outbound request */
struct send_request_data {
/*! The endpoint associated with this request */
@@ -3006,41 +2886,212 @@ struct send_request_wrapper {
void (*callback)(void *token, pjsip_event *e);
/*! Non-zero when the callback is called. */
unsigned int cb_called;
+ /*! Timeout timer. */
+ pj_timer_entry *timeout_timer;
+ /*! Original timeout. */
+ pj_int32_t timeout;
+ /*! Timeout/cleanup lock. */
+ pj_mutex_t *lock;
+ /*! The transmit data. */
+ pjsip_tx_data *tdata;
};
-static void endpt_send_request_wrapper(void *token, pjsip_event *e)
+/*! \internal This function gets called by pjsip when the transaction ends,
+ * even if it timed out. The lock prevents a race condition if both the pjsip
+ * transaction timer and our own timer expire simultaneously.
+ */
+static void endpt_send_request_cb(void *token, pjsip_event *e)
{
struct send_request_wrapper *req_wrapper = token;
- req_wrapper->cb_called = 1;
- if (req_wrapper->callback) {
+ if (e->body.tsx_state.type == PJSIP_EVENT_TIMER) {
+ ast_debug(2, "%p: PJSIP tsx timer expired\n", req_wrapper);
+
+ if (req_wrapper->timeout_timer
+ && req_wrapper->timeout_timer->id != TIMEOUT_TIMER2) {
+ ast_debug(3, "%p: Timeout already handled\n", req_wrapper);
+ ao2_ref(req_wrapper, -1);
+ return;
+ }
+ } else {
+ ast_debug(2, "%p: PJSIP tsx response received\n", req_wrapper);
+ }
+
+ pj_mutex_lock(req_wrapper->lock);
+
+ /* It's possible that our own timer was already processing while
+ * we were waiting on the lock so check the timer id. If it's
+ * still TIMER2 then we still need to process.
+ */
+ if (req_wrapper->timeout_timer
+ && req_wrapper->timeout_timer->id == TIMEOUT_TIMER2) {
+ int timers_cancelled = 0;
+
+ ast_debug(3, "%p: Cancelling timer\n", req_wrapper);
+
+ timers_cancelled = pj_timer_heap_cancel_if_active(
+ pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
+ req_wrapper->timeout_timer, TIMER_INACTIVE);
+
+ if (timers_cancelled > 0) {
+ /* If the timer was cancelled the callback will never run so
+ * clean up its reference to the wrapper.
+ */
+ ast_debug(3, "%p: Timer cancelled\n", req_wrapper);
+ ao2_ref(req_wrapper, -1);
+ } else {
+ /* If it wasn't cancelled, it MAY be in the callback already
+ * waiting on the lock so set the id to INACTIVE so
+ * when the callback comes out of the lock, it knows to not
+ * proceed.
+ */
+ ast_debug(3, "%p: Timer already expired\n", req_wrapper);
+ req_wrapper->timeout_timer->id = TIMER_INACTIVE;
+ }
+ }
+
+ /* It's possible that our own timer expired and called the callbacks
+ * so no need to call them again.
+ */
+ if (!req_wrapper->cb_called && req_wrapper->callback) {
req_wrapper->callback(req_wrapper->token, e);
+ req_wrapper->cb_called = 1;
+ ast_debug(2, "%p: Callbacks executed\n", req_wrapper);
}
+ pj_mutex_unlock(req_wrapper->lock);
ao2_ref(req_wrapper, -1);
}
+/*! \internal This function gets called by our own timer when it expires.
+ * If the timer is cancelled however, the function does NOT get called.
+ * The lock prevents a race condition if both the pjsip transaction timer
+ * and our own timer expire simultaneously.
+ */
+static void send_request_timer_callback(pj_timer_heap_t *theap, pj_timer_entry *entry)
+{
+ pjsip_event event;
+ struct send_request_wrapper *req_wrapper = entry->user_data;
+
+ ast_debug(2, "%p: Internal tsx timer expired after %d msec\n",
+ req_wrapper, req_wrapper->timeout);
+
+ pj_mutex_lock(req_wrapper->lock);
+ /* If the id is not TIMEOUT_TIMER2 then the timer was cancelled above
+ * while the lock was being held so just clean up.
+ */
+ if (entry->id != TIMEOUT_TIMER2) {
+ pj_mutex_unlock(req_wrapper->lock);
+ ast_debug(3, "%p: Timeout already handled\n", req_wrapper);
+ ao2_ref(req_wrapper, -1);
+ return;
+ }
+
+ ast_debug(3, "%p: Timer handled here\n", req_wrapper);
+
+ PJSIP_EVENT_INIT_TX_MSG(event, req_wrapper->tdata);
+ event.body.tsx_state.type = PJSIP_EVENT_TIMER;
+ entry->id = TIMER_INACTIVE;
+
+ if (!req_wrapper->cb_called && req_wrapper->callback) {
+ req_wrapper->callback(req_wrapper->token, &event);
+ req_wrapper->cb_called = 1;
+ ast_debug(2, "%p: Callbacks executed\n", req_wrapper);
+ }
+
+ pj_mutex_unlock(req_wrapper->lock);
+ ao2_ref(req_wrapper, -1);
+}
+
+static void send_request_wrapper_destructor(void *obj)
+{
+ struct send_request_wrapper *req_wrapper = obj;
+
+ pj_mutex_destroy(req_wrapper->lock);
+ pjsip_tx_data_dec_ref(req_wrapper->tdata);
+ ast_debug(2, "%p: wrapper destroyed\n", req_wrapper);
+}
+
static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
- pjsip_tx_data *tdata, int timeout, void *token, pjsip_endpt_send_callback cb)
+ pjsip_tx_data *tdata, pj_int32_t timeout, void *token, pjsip_endpt_send_callback cb)
{
struct send_request_wrapper *req_wrapper;
pj_status_t ret_val;
+ pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
/* Create wrapper to detect if the callback was actually called on an error. */
- req_wrapper = ao2_alloc_options(sizeof(*req_wrapper), NULL,
+ req_wrapper = ao2_alloc_options(sizeof(*req_wrapper), send_request_wrapper_destructor,
AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!req_wrapper) {
pjsip_tx_data_dec_ref(tdata);
return PJ_ENOMEM;
}
+
+ ast_debug(2, "%p: Wrapper created\n", req_wrapper);
+
req_wrapper->token = token;
req_wrapper->callback = cb;
+ req_wrapper->timeout = timeout;
+ req_wrapper->timeout_timer = NULL;
+ req_wrapper->lock = NULL;
+ req_wrapper->tdata = tdata;
+ /* Add a reference to tdata. The wrapper destructor cleans it up. */
+ pjsip_tx_data_add_ref(tdata);
+
+ ret_val = pj_mutex_create_simple(tdata->pool, "tsx_timeout", &req_wrapper->lock);
+ if (ret_val != PJ_SUCCESS) {
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(ret_val, errmsg, sizeof(errmsg));
+ ast_log(LOG_ERROR, "Error %d '%s' sending %.*s request to endpoint %s\n",
+ (int) ret_val, errmsg, (int) pj_strlen(&tdata->msg->line.req.method.name),
+ pj_strbuf(&tdata->msg->line.req.method.name),
+ endpoint ? ast_sorcery_object_get_id(endpoint) : "<unknown>");
+ pjsip_tx_data_dec_ref(tdata);
+ ao2_ref(req_wrapper, -1);
+ return PJ_ENOMEM;
+ }
+
+ pj_mutex_lock(req_wrapper->lock);
+
+ if (timeout > 0) {
+ pj_time_val timeout_timer_val = { timeout / 1000, timeout % 1000 };
+
+ req_wrapper->timeout_timer = PJ_POOL_ALLOC_T(tdata->pool, pj_timer_entry);
+
+ ast_debug(2, "%p: Set timer to %d msec\n", req_wrapper, timeout);
+ pj_timer_entry_init(req_wrapper->timeout_timer, TIMEOUT_TIMER2,
+ req_wrapper, &send_request_timer_callback);
+
+ pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(endpt),
+ req_wrapper->timeout_timer, TIMER_INACTIVE);
+
+ /* We need to insure that the wrapper and tdata are available if/when the
+ * timer callback is executed.
+ */
+ ao2_ref(req_wrapper, +1);
+ pj_timer_heap_schedule(pjsip_endpt_get_timer_heap(endpt),
+ req_wrapper->timeout_timer, &timeout_timer_val);
+
+ req_wrapper->timeout_timer->id = TIMEOUT_TIMER2;
+ } else {
+ req_wrapper->timeout_timer = NULL;
+ }
+
+ /* We need to insure that the wrapper and tdata are available when the
+ * transaction callback is executed.
+ */
ao2_ref(req_wrapper, +1);
- ret_val = endpt_send_transaction(ast_sip_get_pjsip_endpoint(), tdata, timeout,
- req_wrapper, endpt_send_request_wrapper);
+
+ ret_val = pjsip_endpt_send_request(endpt, tdata, -1, req_wrapper, endpt_send_request_cb);
if (ret_val != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
+ if (timeout > 0) {
+ pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(endpt),
+ req_wrapper->timeout_timer, TIMER_INACTIVE);
+ ao2_ref(req_wrapper, -1);
+ }
+
/* Complain of failure to send the request. */
pj_strerror(ret_val, errmsg, sizeof(errmsg));
ast_log(LOG_ERROR, "Error %d '%s' sending %.*s request to endpoint %s\n",
@@ -3061,6 +3112,7 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
ao2_ref(req_wrapper, -1);
}
}
+ pj_mutex_unlock(req_wrapper->lock);
ao2_ref(req_wrapper, -1);
return ret_val;
}
@@ -3076,10 +3128,6 @@ static void send_request_cb(void *token, pjsip_event *e)
int res;
switch(e->body.tsx_state.type) {
- case PJSIP_EVENT_USER:
- /* Map USER (transaction cancelled by timeout) to TIMER */
- e->body.tsx_state.type = PJSIP_EVENT_TIMER;
- break;
case PJSIP_EVENT_TRANSPORT_ERROR:
case PJSIP_EVENT_TIMER:
break;
@@ -3695,25 +3743,8 @@ static int load_module(void)
return AST_MODULE_LOAD_DECLINE;
}
- if (internal_sip_register_service(&send_tsx_module)) {
- ast_log(LOG_ERROR, "Failed to initialize send request module. Aborting load\n");
- internal_sip_unregister_service(&supplement_module);
- ast_sip_destroy_distributor();
- ast_res_pjsip_destroy_configuration();
- ast_sip_destroy_global_headers();
- stop_monitor_thread();
- ast_sip_destroy_system();
- pj_pool_release(memory_pool);
- memory_pool = NULL;
- pjsip_endpt_destroy(ast_pjsip_endpoint);
- ast_pjsip_endpoint = NULL;
- pj_caching_pool_destroy(&caching_pool);
- return AST_MODULE_LOAD_DECLINE;
- }
-
if (internal_sip_initialize_outbound_authentication()) {
ast_log(LOG_ERROR, "Failed to initialize outbound authentication. Aborting load\n");
- internal_sip_unregister_service(&send_tsx_module);
internal_sip_unregister_service(&supplement_module);
ast_sip_destroy_distributor();
ast_res_pjsip_destroy_configuration();
@@ -3757,7 +3788,6 @@ static int unload_pjsip(void *data)
ast_res_pjsip_destroy_configuration();
ast_sip_destroy_system();
ast_sip_destroy_global_headers();
- internal_sip_unregister_service(&send_tsx_module);
internal_sip_unregister_service(&supplement_module);
if (monitor_thread) {
stop_monitor_thread();
@@ -3786,9 +3816,9 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Basic SIP resource",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND - 5,
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND - 5,
);
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index 887053b07..9e75929d7 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -354,6 +354,7 @@ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variab
contacts = ast_strdupa(var->value);
while ((contact_uri = strsep(&contacts, ","))) {
struct ast_sip_contact *contact;
+ struct ast_sip_contact_status *status;
char contact_id[strlen(aor_id) + strlen(contact_uri) + 2 + 1];
if (ast_sip_push_task_synchronous(NULL, permanent_contact_validate, contact_uri)) {
@@ -376,10 +377,12 @@ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variab
return -1;
}
- if (!ast_res_pjsip_find_or_create_contact_status(contact)) {
+ status = ast_res_pjsip_find_or_create_contact_status(contact);
+ if (!status) {
ao2_ref(contact, -1);
return -1;
}
+ ao2_ref(status, -1);
ast_string_field_set(contact, uri, contact_uri);
ao2_link(aor->permanent_contacts, contact);
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index f147b34d3..4ce773563 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -56,30 +56,54 @@ static int persistent_endpoint_cmp(void *obj, void *arg, int flags)
return !strcmp(ast_endpoint_get_resource(persistent1->endpoint), id) ? CMP_MATCH | CMP_STOP : 0;
}
+/*! \brief Structure for communicating contact status to
+ * persistent_endpoint_update_state from the contact/contact_status
+ * observers.
+ */
+struct sip_contact_status {
+ char *uri;
+ enum ast_sip_contact_status_type status;
+ int64_t rtt;
+};
+
/*! \brief Callback function for changing the state of an endpoint */
-static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
+static int persistent_endpoint_update_state(void *obj, void *arg, void *data, int flags)
{
struct sip_persistent_endpoint *persistent = obj;
struct ast_endpoint *endpoint = persistent->endpoint;
char *aor = arg;
+ struct sip_contact_status *status = data;
struct ao2_container *contacts;
struct ast_json *blob;
struct ao2_iterator i;
struct ast_sip_contact *contact;
enum ast_endpoint_state state = AST_ENDPOINT_OFFLINE;
- if (!ast_strlen_zero(aor) && !strstr(persistent->aors, aor)) {
- return 0;
- }
+ if (!ast_strlen_zero(aor)) {
+ if (!strstr(persistent->aors, aor)) {
+ return 0;
+ }
+ if (status) {
+ char rtt[32];
+ snprintf(rtt, 31, "%" PRId64, status->rtt);
+ blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
+ "contact_status", ast_sip_get_contact_status_label(status->status),
+ "aor", aor,
+ "uri", status->uri,
+ "roundtrip_usec", rtt,
+ "endpoint_name", ast_endpoint_get_resource(endpoint));
+ ast_endpoint_blob_publish(endpoint, ast_endpoint_contact_state_type(), blob);
+ ast_json_unref(blob);
+ }
+ }
/* Find all the contacts for this endpoint. If ANY are available,
* mark the endpoint as ONLINE.
*/
contacts = ast_sip_location_retrieve_contacts_from_aor_list(persistent->aors);
if (contacts) {
i = ao2_iterator_init(contacts, 0);
- while ((contact = ao2_iterator_next(&i))
- && state == AST_ENDPOINT_OFFLINE) {
+ while (state == AST_ENDPOINT_OFFLINE && (contact = ao2_iterator_next(&i))) {
struct ast_sip_contact_status *contact_status;
const char *contact_id = ast_sorcery_object_get_id(contact);
@@ -121,11 +145,28 @@ static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
/*! \brief Function called when stuff relating to a contact happens (created/deleted) */
static void persistent_endpoint_contact_created_observer(const void *object)
{
- char *id = ast_strdupa(ast_sorcery_object_get_id(object)), *aor = NULL;
+ const struct ast_sip_contact *contact = object;
+ char *id = ast_strdupa(ast_sorcery_object_get_id(contact));
+ char *aor = NULL;
+ char *contact_uri = NULL;
+ struct sip_contact_status status;
+
+ aor = id;
+ /* Dynamic contacts are delimited with ";@" and static ones with "@@" */
+ if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) {
+ *contact_uri = '\0';
+ contact_uri += 2;
+ } else {
+ contact_uri = id;
+ }
- aor = strsep(&id, ";@");
+ status.uri = contact_uri;
+ status.status = CREATED;
+ status.rtt = 0;
- ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor);
+ ast_verb(1, "Contact %s/%s has been created\n", aor, contact_uri);
+
+ ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status);
}
/*! \brief Function called when stuff relating to a contact happens (created/deleted) */
@@ -133,20 +174,25 @@ static void persistent_endpoint_contact_deleted_observer(const void *object)
{
char *id = ast_strdupa(ast_sorcery_object_get_id(object));
char *aor = NULL;
- char *contact = NULL;
+ char *contact_uri = NULL;
+ struct sip_contact_status status;
aor = id;
/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
- if ((contact = strstr(id, ";@")) || (contact = strstr(id, "@@"))) {
- *contact = '\0';
- contact += 2;
+ if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) {
+ *contact_uri = '\0';
+ contact_uri += 2;
} else {
- contact = id;
+ contact_uri = id;
}
- ast_verb(1, "Contact %s/%s is now Unavailable\n", aor, contact);
+ ast_verb(1, "Contact %s/%s has been deleted\n", aor, contact_uri);
+
+ status.uri = contact_uri;
+ status.status = REMOVED;
+ status.rtt = 0;
- ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor);
+ ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status);
}
/*! \brief Observer for contacts so state can be updated on respective endpoints */
@@ -161,23 +207,32 @@ static void persistent_endpoint_contact_status_observer(const void *object)
const struct ast_sip_contact_status *contact_status = object;
char *id = ast_strdupa(ast_sorcery_object_get_id(object));
char *aor = NULL;
- char *contact = NULL;
+ char *contact_uri = NULL;
+ struct sip_contact_status status;
- /* If rtt_start is set (this is the outgoing OPTIONS) or
- * there's no status change, ignore.
- */
- if (contact_status->rtt_start.tv_sec > 0
- || contact_status->status == contact_status->last_status) {
+ /* If rtt_start is set (this is the outgoing OPTIONS), ignore. */
+ if (contact_status->rtt_start.tv_sec > 0) {
return;
}
aor = id;
/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
- if ((contact = strstr(id, ";@")) || (contact = strstr(id, "@@"))) {
- *contact = '\0';
- contact += 2;
+ if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) {
+ *contact_uri = '\0';
+ contact_uri += 2;
} else {
- contact = id;
+ contact_uri = id;
+ }
+
+ if (contact_status->status == contact_status->last_status) {
+ ast_debug(3, "Contact %s status didn't change: %s, RTT: %.3f msec\n",
+ contact_uri, ast_sip_get_contact_status_label(contact_status->status),
+ contact_status->rtt / 1000.0);
+ return;
+ } else {
+ ast_verb(1, "Contact %s/%s is now %s. RTT: %.3f msec\n", aor, contact_uri,
+ ast_sip_get_contact_status_label(contact_status->status),
+ contact_status->rtt / 1000.0);
}
ast_test_suite_event_notify("AOR_CONTACT_UPDATE",
@@ -186,10 +241,11 @@ static void persistent_endpoint_contact_status_observer(const void *object)
ast_sorcery_object_get_id(contact_status),
ast_sip_get_contact_status_label(contact_status->status));
- ast_verb(1, "Contact %s/%s is now %s\n", aor, contact,
- ast_sip_get_contact_status_label(contact_status->status));
+ status.uri = contact_uri;
+ status.status = contact_status->status;
+ status.rtt = contact_status->rtt;
- ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor);
+ ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status);
}
/*! \brief Observer for contacts so state can be updated on respective endpoints */
@@ -1014,7 +1070,7 @@ static struct ast_endpoint *persistent_endpoint_find_or_create(const struct ast_
if (ast_strlen_zero(persistent->aors)) {
ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_UNKNOWN);
} else {
- persistent_endpoint_update_state(persistent, NULL, 0);
+ persistent_endpoint_update_state(persistent, NULL, NULL, 0);
}
ao2_link_flags(persistent_endpoints, persistent, OBJ_NOLOCK);
diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c
index 87c67fae3..e3e8f1808 100644
--- a/res/res_pjsip/pjsip_options.c
+++ b/res/res_pjsip/pjsip_options.c
@@ -39,12 +39,17 @@ static const char *status_map [] = {
[UNAVAILABLE] = "Unreachable",
[AVAILABLE] = "Reachable",
[UNKNOWN] = "Unknown",
+ [CREATED] = "Created",
+ [REMOVED] = "Removed",
+
};
static const char *short_status_map [] = {
[UNAVAILABLE] = "Unavail",
[AVAILABLE] = "Avail",
[UNKNOWN] = "Unknown",
+ [CREATED] = "Created",
+ [REMOVED] = "Removed",
};
const char *ast_sip_get_contact_status_label(const enum ast_sip_contact_status_type status)
diff --git a/res/res_pjsip_acl.c b/res/res_pjsip_acl.c
index 151ebeded..5c10e5779 100644
--- a/res/res_pjsip_acl.c
+++ b/res/res_pjsip_acl.c
@@ -317,8 +317,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP ACL Resource",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_authenticator_digest.c b/res/res_pjsip_authenticator_digest.c
index 8a781254c..e610bb804 100644
--- a/res/res_pjsip_authenticator_digest.c
+++ b/res/res_pjsip_authenticator_digest.c
@@ -486,9 +486,9 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP authentication resource",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
);
diff --git a/res/res_pjsip_caller_id.c b/res/res_pjsip_caller_id.c
index 63ef1f4d4..c084a8181 100644
--- a/res/res_pjsip_caller_id.c
+++ b/res/res_pjsip_caller_id.c
@@ -742,8 +742,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Caller ID Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_config_wizard.c b/res/res_pjsip_config_wizard.c
index 6bde704f2..9d85a4615 100644
--- a/res/res_pjsip_config_wizard.c
+++ b/res/res_pjsip_config_wizard.c
@@ -334,10 +334,10 @@ static void *create_object(const struct ast_sorcery *sorcery,
return obj;
}
-/*! \brief Finds a variable in a list and tests it */
+/*! \brief Finds the last variable in a list and tests it */
static int is_variable_true(struct ast_variable *vars, const char *name)
{
- return ast_true(ast_variable_find_in_list(vars, name));
+ return ast_true(ast_variable_find_last_in_list(vars, name));
}
/*! \brief Appends a variable to the end of an existing list */
@@ -539,7 +539,7 @@ static int handle_auth(const struct ast_sorcery *sorcery, struct object_type_wiz
}
if (is_variable_true(wizvars, test_variable)) {
- if (!ast_variable_find_in_list(vars, "username")) {
+ if (!ast_variable_find_last_in_list(vars, "username")) {
ast_log(LOG_ERROR,
"Wizard '%s' must have '%s_auth/username' if it %s.\n", id, direction, test_variable);
return -1;
@@ -557,7 +557,7 @@ static int handle_auth(const struct ast_sorcery *sorcery, struct object_type_wiz
variable_list_append_return(&vars, "@pjsip_wizard", id);
/* If the user set auth_type, don't override it. */
- if (!ast_variable_find_in_list(vars, "auth_type")) {
+ if (!ast_variable_find_last_in_list(vars, "auth_type")) {
variable_list_append_return(&vars, "auth_type", "userpass");
}
@@ -599,8 +599,8 @@ static int handle_aor(const struct ast_sorcery *sorcery, struct object_type_wiza
variable_list_append(&vars, "@pjsip_wizard", id);
/* If the user explicitly specified an aor/contact, don't use remote hosts. */
- if (!ast_variable_find_in_list(vars, "contact")) {
- if (!(contact_pattern = ast_variable_find_in_list(wizvars, "contact_pattern"))) {
+ if (!ast_variable_find_last_in_list(vars, "contact")) {
+ if (!(contact_pattern = ast_variable_find_last_in_list(wizvars, "contact_pattern"))) {
contact_pattern = "sip:${REMOTE_HOST}";
}
@@ -645,10 +645,10 @@ static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type
struct ast_variable *wizvars = ast_category_first(wiz);
struct ast_sorcery_object *obj = NULL;
const char *id = ast_category_get_name(wiz);
- const char *transport = ast_variable_find_in_list(wizvars, "transport");
- const char *hint_context = hint_context = ast_variable_find_in_list(wizvars, "hint_context");
- const char *hint_exten = ast_variable_find_in_list(wizvars, "hint_exten");
- const char *hint_application= ast_variable_find_in_list(wizvars, "hint_application");
+ const char *transport = ast_variable_find_last_in_list(wizvars, "transport");
+ const char *hint_context = hint_context = ast_variable_find_last_in_list(wizvars, "hint_context");
+ const char *hint_exten = ast_variable_find_last_in_list(wizvars, "hint_exten");
+ const char *hint_application= ast_variable_find_last_in_list(wizvars, "hint_application");
char new_id[strlen(id) + MAX_ID_SUFFIX];
RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "endpoint/"), ast_variables_destroy);
@@ -656,7 +656,7 @@ static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type
variable_list_append_return(&vars, "aors", id);
if (ast_strlen_zero(hint_context)) {
- hint_context = ast_variable_find_in_list(vars, "context");
+ hint_context = ast_variable_find_last_in_list(vars, "context");
}
if (ast_strlen_zero(hint_context)) {
@@ -737,7 +737,7 @@ static int handle_identify(const struct ast_sorcery *sorcery, struct object_type
variable_list_append_return(&vars, "endpoint", id);
variable_list_append_return(&vars, "@pjsip_wizard", id);
- if (!ast_variable_find_in_list(vars, "match")) {
+ if (!ast_variable_find_last_in_list(vars, "match")) {
for (host_counter = 0; host_counter < host_count; host_counter++) {
char *rhost = AST_VECTOR_GET(remote_hosts_vector, host_counter);
char host[strlen(rhost) + 1];
@@ -787,7 +787,7 @@ static int handle_phoneprov(const struct ast_sorcery *sorcery, struct object_typ
return 0;
}
- if (!ast_variable_find_in_list(wizvars, "phoneprov/MAC")) {
+ if (!ast_variable_find_last_in_list(wizvars, "phoneprov/MAC")) {
ast_log(LOG_ERROR,
"Wizard '%s' must have 'phoneprov/MAC' if it has_phoneprov.\n", id);
return -1;
@@ -834,7 +834,7 @@ static int handle_registrations(const struct ast_sorcery *sorcery, struct object
const char *id = ast_category_get_name(wiz);
const char *server_uri_pattern;
const char *client_uri_pattern;
- const char *transport = ast_variable_find_in_list(wizvars, "transport");
+ const char *transport = ast_variable_find_last_in_list(wizvars, "transport");
const char *username;
char new_id[strlen(id) + MAX_ID_SUFFIX];
int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
@@ -871,16 +871,16 @@ static int handle_registrations(const struct ast_sorcery *sorcery, struct object
variable_list_append_return(&vars, "@pjsip_wizard", id);
- if (!(server_uri_pattern = ast_variable_find_in_list(wizvars, "server_uri_pattern"))) {
+ if (!(server_uri_pattern = ast_variable_find_last_in_list(wizvars, "server_uri_pattern"))) {
server_uri_pattern = "sip:${REMOTE_HOST}";
}
- if (!(client_uri_pattern = ast_variable_find_in_list(wizvars, "client_uri_pattern"))) {
+ if (!(client_uri_pattern = ast_variable_find_last_in_list(wizvars, "client_uri_pattern"))) {
client_uri_pattern = "sip:${USERNAME}@${REMOTE_HOST}";
}
if(is_variable_true(wizvars, "sends_auth")) {
- username = ast_variable_find_in_list(wizvars, "outbound_auth/username");
+ username = ast_variable_find_last_in_list(wizvars, "outbound_auth/username");
} else {
username = id;
}
@@ -958,7 +958,7 @@ static int wizard_apply_handler(const struct ast_sorcery *sorcery, struct object
int rc = -1;
AST_VECTOR_INIT(&remote_hosts_vector, 16);
- remote_hosts = ast_variable_find_in_list(wizvars, "remote_hosts");
+ remote_hosts = ast_variable_find_last_in_list(wizvars, "remote_hosts");
if (!ast_strlen_zero(remote_hosts)) {
char *host;
@@ -1195,8 +1195,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Config Wizard",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_REALTIME_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_REALTIME_DRIVER,
+);
diff --git a/res/res_pjsip_dialog_info_body_generator.c b/res/res_pjsip_dialog_info_body_generator.c
index d9725f4c5..c8dc4014e 100644
--- a/res/res_pjsip_dialog_info_body_generator.c
+++ b/res/res_pjsip_dialog_info_body_generator.c
@@ -207,8 +207,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State Dialog Info+XML Provider",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
);
diff --git a/res/res_pjsip_diversion.c b/res/res_pjsip_diversion.c
index 49f789212..3dfae455b 100644
--- a/res/res_pjsip_diversion.c
+++ b/res/res_pjsip_diversion.c
@@ -348,8 +348,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Add Diversion Header Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_dtmf_info.c b/res/res_pjsip_dtmf_info.c
index b0a6649a0..b6a0b4a1e 100644
--- a/res/res_pjsip_dtmf_info.c
+++ b/res/res_pjsip_dtmf_info.c
@@ -163,8 +163,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP DTMF INFO Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_endpoint_identifier_anonymous.c b/res/res_pjsip_endpoint_identifier_anonymous.c
index 274c05586..06933a9ad 100644
--- a/res/res_pjsip_endpoint_identifier_anonymous.c
+++ b/res/res_pjsip_endpoint_identifier_anonymous.c
@@ -121,8 +121,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Anonymous endpoint identifier",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_DEFAULT,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_DEFAULT,
+);
diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c
index 5c6e2cc3e..bbf340761 100644
--- a/res/res_pjsip_endpoint_identifier_ip.c
+++ b/res/res_pjsip_endpoint_identifier_ip.c
@@ -531,9 +531,9 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP IP endpoint identifier",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .reload = reload_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .reload = reload_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_endpoint_identifier_user.c b/res/res_pjsip_endpoint_identifier_user.c
index 5abf87999..aede2f7d3 100644
--- a/res/res_pjsip_endpoint_identifier_user.c
+++ b/res/res_pjsip_endpoint_identifier_user.c
@@ -127,8 +127,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP username endpoint identifier",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_exten_state.c b/res/res_pjsip_exten_state.c
index a05e1915d..a8a11bc7f 100644
--- a/res/res_pjsip_exten_state.c
+++ b/res/res_pjsip_exten_state.c
@@ -354,7 +354,7 @@ static int new_subscribe(struct ast_sip_endpoint *endpoint,
{
if (!ast_exists_extension(NULL, endpoint->context, resource, PRIORITY_HINT, NULL)) {
ast_log(LOG_NOTICE, "Extension state subscription failed: Extension %s does not exist in context '%s' or has no associated hint\n",
- endpoint->context, resource);
+ resource, endpoint->context);
return 404;
}
@@ -517,8 +517,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State Notifications",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
);
diff --git a/res/res_pjsip_header_funcs.c b/res/res_pjsip_header_funcs.c
index 4ab3fb1d8..7d164b12a 100644
--- a/res/res_pjsip_header_funcs.c
+++ b/res/res_pjsip_header_funcs.c
@@ -620,7 +620,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Header Functions",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,);
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_keepalive.c b/res/res_pjsip_keepalive.c
index b854fc957..d850973f8 100644
--- a/res/res_pjsip_keepalive.c
+++ b/res/res_pjsip_keepalive.c
@@ -261,9 +261,9 @@ static int reload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Stateful Connection Keepalive Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .reload = reload_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .reload = reload_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
+);
diff --git a/res/res_pjsip_log_forwarder.c b/res/res_pjsip_log_forwarder.c
index 5324063bf..85ca9358c 100644
--- a/res/res_pjsip_log_forwarder.c
+++ b/res/res_pjsip_log_forwarder.c
@@ -122,4 +122,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_CHANNEL_DEPEND - 6,
- );
+);
diff --git a/res/res_pjsip_logger.c b/res/res_pjsip_logger.c
index db54aa949..427cb6fb7 100644
--- a/res/res_pjsip_logger.c
+++ b/res/res_pjsip_logger.c
@@ -260,8 +260,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Packet Logger",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_messaging.c b/res/res_pjsip_messaging.c
index dab70ca96..f3985059c 100644
--- a/res/res_pjsip_messaging.c
+++ b/res/res_pjsip_messaging.c
@@ -779,8 +779,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Messaging Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_multihomed.c b/res/res_pjsip_multihomed.c
index e1abff34f..68a431153 100644
--- a/res/res_pjsip_multihomed.c
+++ b/res/res_pjsip_multihomed.c
@@ -232,8 +232,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Multihomed Routing Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_mwi.c b/res/res_pjsip_mwi.c
index 2ab7dfee0..371f3abf8 100644
--- a/res/res_pjsip_mwi.c
+++ b/res/res_pjsip_mwi.c
@@ -471,7 +471,7 @@ static int unsubscribe_stasis(void *obj, void *arg, int flags)
struct mwi_stasis_subscription *mwi_stasis = obj;
if (mwi_stasis->stasis_sub) {
ast_debug(3, "Removing stasis subscription to mailbox %s\n", mwi_stasis->mailbox);
- mwi_stasis->stasis_sub = stasis_unsubscribe(mwi_stasis->stasis_sub);
+ mwi_stasis->stasis_sub = stasis_unsubscribe_and_join(mwi_stasis->stasis_sub);
}
return CMP_MATCH;
}
@@ -1025,9 +1025,9 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP MWI resource",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
);
diff --git a/res/res_pjsip_mwi_body_generator.c b/res/res_pjsip_mwi_body_generator.c
index cc558ac82..e4b39d534 100644
--- a/res/res_pjsip_mwi_body_generator.c
+++ b/res/res_pjsip_mwi_body_generator.c
@@ -109,8 +109,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP MWI resource",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
);
diff --git a/res/res_pjsip_nat.c b/res/res_pjsip_nat.c
index 6e093abcd..c717ba21c 100644
--- a/res/res_pjsip_nat.c
+++ b/res/res_pjsip_nat.c
@@ -306,8 +306,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP NAT Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_notify.c b/res/res_pjsip_notify.c
index e494daf26..2396337ef 100644
--- a/res/res_pjsip_notify.c
+++ b/res/res_pjsip_notify.c
@@ -1027,9 +1027,9 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "CLI/AMI PJSIP NOTIFY Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .reload = reload_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .reload = reload_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..30dfcaabf 100644
--- a/res/res_pjsip_one_touch_record_info.c
+++ b/res/res_pjsip_one_touch_record_info.c
@@ -128,4 +128,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP INFO One Touch
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_APP_DEPEND,
- );
+);
diff --git a/res/res_pjsip_outbound_authenticator_digest.c b/res/res_pjsip_outbound_authenticator_digest.c
index de77616fd..86a15c7b7 100644
--- a/res/res_pjsip_outbound_authenticator_digest.c
+++ b/res/res_pjsip_outbound_authenticator_digest.c
@@ -171,8 +171,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP authentication resource",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
);
diff --git a/res/res_pjsip_outbound_publish.c b/res/res_pjsip_outbound_publish.c
index f7669932e..a58bcbb91 100644
--- a/res/res_pjsip_outbound_publish.c
+++ b/res/res_pjsip_outbound_publish.c
@@ -1189,8 +1189,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Outbound Publish Support",
- .load = load_module,
- .reload = reload_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
- );
+ .load = load_module,
+ .reload = reload_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 1dd5f583b..7f60acdb3 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -383,22 +383,27 @@ static int line_identify_relationship(void *obj, void *arg, int flags)
return !pj_strcmp2(&line->value, state->client_state->line) ? CMP_MATCH | CMP_STOP : 0;
}
+static struct pjsip_param *get_uri_option_line(const void *uri)
+{
+ pjsip_sip_uri *pjuri;
+ static const pj_str_t LINE_STR = { "line", 4 };
+
+ if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) {
+ return NULL;
+ }
+ pjuri = pjsip_uri_get_uri(uri);
+ return pjsip_param_find(&pjuri->other_param, &LINE_STR);
+}
+
/*! \brief Endpoint identifier which uses the 'line' parameter to establish a relationship to an outgoing registration */
static struct ast_sip_endpoint *line_identify(pjsip_rx_data *rdata)
{
- pjsip_sip_uri *uri;
- static const pj_str_t LINE_STR = { "line", 4 };
pjsip_param *line;
RAII_VAR(struct ao2_container *, states, NULL, ao2_cleanup);
RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
- if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) {
- return NULL;
- }
- uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);
-
- line = pjsip_param_find(&uri->other_param, &LINE_STR);
- if (!line) {
+ if (!(line = get_uri_option_line(rdata->msg_info.to->uri))
+ && !(line = get_uri_option_line(rdata->msg_info.msg->line.req.uri))) {
return NULL;
}
@@ -1822,9 +1827,9 @@ static int reload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Outbound Registration Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .reload = reload_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .reload = reload_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_path.c b/res/res_pjsip_path.c
index d0ee5a49a..03cbe5076 100644
--- a/res/res_pjsip_path.c
+++ b/res/res_pjsip_path.c
@@ -246,8 +246,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Path Header Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
);
diff --git a/res/res_pjsip_phoneprov_provider.c b/res/res_pjsip_phoneprov_provider.c
index 8dafabc53..eef3a082f 100644
--- a/res/res_pjsip_phoneprov_provider.c
+++ b/res/res_pjsip_phoneprov_provider.c
@@ -413,8 +413,8 @@ static int reload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Phoneprov Provider",
- .load = load_module,
- .reload = reload_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .load = load_module,
+ .reload = reload_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_pidf_body_generator.c b/res/res_pjsip_pidf_body_generator.c
index ef0cce599..0863eafb3 100644
--- a/res/res_pjsip_pidf_body_generator.c
+++ b/res/res_pjsip_pidf_body_generator.c
@@ -132,8 +132,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State PIDF Provider",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
);
diff --git a/res/res_pjsip_pidf_digium_body_supplement.c b/res/res_pjsip_pidf_digium_body_supplement.c
index 86e673afa..b6212ea5e 100644
--- a/res/res_pjsip_pidf_digium_body_supplement.c
+++ b/res/res_pjsip_pidf_digium_body_supplement.c
@@ -110,8 +110,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP PIDF Digium presence supplement",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
);
diff --git a/res/res_pjsip_pidf_eyebeam_body_supplement.c b/res/res_pjsip_pidf_eyebeam_body_supplement.c
index cd590c3d3..cc6dfd125 100644
--- a/res/res_pjsip_pidf_eyebeam_body_supplement.c
+++ b/res/res_pjsip_pidf_eyebeam_body_supplement.c
@@ -109,8 +109,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP PIDF Eyebeam supplement",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
);
diff --git a/res/res_pjsip_publish_asterisk.c b/res/res_pjsip_publish_asterisk.c
index c0d3b90cf..50d4cc387 100644
--- a/res/res_pjsip_publish_asterisk.c
+++ b/res/res_pjsip_publish_asterisk.c
@@ -923,8 +923,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Asterisk Event PUBLISH Support",
- .load = load_module,
- .reload = reload_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .load = load_module,
+ .reload = reload_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
);
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index 1ecb17d99..c01c7c473 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -148,6 +148,9 @@
<enum name="presence"><para>
Device state and presence reporting.
</para></enum>
+ <enum name="dialog"><para>
+ This is identical to <replaceable>presence</replaceable>.
+ </para></enum>
<enum name="message-summary"><para>
Message-waiting indication (MWI) reporting.
</para></enum>
@@ -4311,8 +4314,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP event resource",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
);
diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c
index 5195177d5..7420e7e6c 100644
--- a/res/res_pjsip_refer.c
+++ b/res/res_pjsip_refer.c
@@ -1141,8 +1141,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Blind and Attended Transfer Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index 944a6055e..cc18ffa7c 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -823,8 +823,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Registrar Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_registrar_expire.c b/res/res_pjsip_registrar_expire.c
index 5b2b350ea..399e7bd0e 100644
--- a/res/res_pjsip_registrar_expire.c
+++ b/res/res_pjsip_registrar_expire.c
@@ -293,4 +293,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Contact Auto-Ex
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_APP_DEPEND,
- );
+);
diff --git a/res/res_pjsip_rfc3326.c b/res/res_pjsip_rfc3326.c
index 3ed8de659..6eb27ffe2 100644
--- a/res/res_pjsip_rfc3326.c
+++ b/res/res_pjsip_rfc3326.c
@@ -143,8 +143,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP RFC3326 Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 55fbfd462..3f4868351 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -1367,8 +1367,8 @@ end:
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP SDP RTP/AVP stream handler",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DRIVER,
+);
diff --git a/res/res_pjsip_send_to_voicemail.c b/res/res_pjsip_send_to_voicemail.c
index 3a57aea7a..9d2b5b14b 100644
--- a/res/res_pjsip_send_to_voicemail.c
+++ b/res/res_pjsip_send_to_voicemail.c
@@ -232,4 +232,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP REFER Send to V
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_APP_DEPEND,
- );
+);
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 87ce2b0c1..ca89d76a9 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -1418,16 +1418,95 @@ void ast_sip_session_unsuspend(struct ast_sip_session *session)
ao2_ref(suspender, -1);
}
-static int session_outbound_auth(pjsip_dialog *dlg, pjsip_tx_data *tdata, void *user_data)
+/*!
+ * \internal
+ * \brief Handle initial INVITE challenge response message.
+ * \since 13.5.0
+ *
+ * \param rdata PJSIP receive response message data.
+ *
+ * \retval PJ_FALSE Did not handle message.
+ * \retval PJ_TRUE Handled message.
+ */
+static pj_bool_t outbound_invite_auth(pjsip_rx_data *rdata)
{
- pjsip_inv_session *inv = pjsip_dlg_get_inv_session(dlg);
- struct ast_sip_session *session = inv->mod_data[session_module.id];
+ pjsip_transaction *tsx;
+ pjsip_dialog *dlg;
+ pjsip_inv_session *inv;
+ pjsip_tx_data *tdata;
+ struct ast_sip_session *session;
+
+ if (rdata->msg_info.msg->line.status.code != 401
+ && rdata->msg_info.msg->line.status.code != 407) {
+ /* Doesn't pertain to us. Move on */
+ return PJ_FALSE;
+ }
+
+ tsx = pjsip_rdata_get_tsx(rdata);
+ dlg = pjsip_rdata_get_dlg(rdata);
+ if (!dlg || !tsx) {
+ return PJ_FALSE;
+ }
- if (inv->state < PJSIP_INV_STATE_CONFIRMED && tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) {
- pjsip_inv_uac_restart(inv, PJ_FALSE);
+ if (tsx->method.id != PJSIP_INVITE_METHOD) {
+ /* Not an INVITE that needs authentication */
+ return PJ_FALSE;
+ }
+
+ inv = pjsip_dlg_get_inv_session(dlg);
+ if (PJSIP_INV_STATE_CONFIRMED <= inv->state) {
+ /*
+ * We cannot handle reINVITE authentication at this
+ * time because the reINVITE transaction is still in
+ * progress.
+ */
+ ast_debug(1, "A reINVITE is being challenged.\n");
+ return PJ_FALSE;
}
+ ast_debug(1, "Initial INVITE is being challenged.\n");
+
+ session = inv->mod_data[session_module.id];
+
+ if (ast_sip_create_request_with_auth(&session->endpoint->outbound_auths, rdata, tsx,
+ &tdata)) {
+ return PJ_FALSE;
+ }
+
+ /*
+ * Restart the outgoing initial INVITE transaction to deal
+ * with authentication.
+ */
+ pjsip_inv_uac_restart(inv, PJ_FALSE);
+
ast_sip_session_send_request(session, tdata);
- return 0;
+ return PJ_TRUE;
+}
+
+static pjsip_module outbound_invite_auth_module = {
+ .name = {"Outbound INVITE Auth", 20},
+ .priority = PJSIP_MOD_PRIORITY_DIALOG_USAGE,
+ .on_rx_response = outbound_invite_auth,
+};
+
+/*!
+ * \internal
+ * \brief Setup outbound initial INVITE authentication.
+ * \since 13.5.0
+ *
+ * \param dlg PJSIP dialog to attach outbound authentication.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int setup_outbound_invite_auth(pjsip_dialog *dlg)
+{
+ pj_status_t status;
+
+ ++dlg->sess_count;
+ status = pjsip_dlg_add_usage(dlg, &outbound_invite_auth_module, NULL);
+ --dlg->sess_count;
+
+ return status != PJ_SUCCESS ? -1 : 0;
}
struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint *endpoint,
@@ -1465,7 +1544,7 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
return NULL;
}
- if (ast_sip_dialog_setup_outbound_authentication(dlg, endpoint, session_outbound_auth, NULL)) {
+ if (setup_outbound_invite_auth(dlg)) {
pjsip_dlg_terminate(dlg);
return NULL;
}
@@ -1505,7 +1584,7 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
ao2_cleanup(joint_caps);
}
- if ((pjsip_dlg_add_usage(dlg, &session_module, NULL) != PJ_SUCCESS)) {
+ if (pjsip_dlg_add_usage(dlg, &session_module, NULL) != PJ_SUCCESS) {
pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
/* Since we are not notifying ourselves that the INVITE session is being terminated
* we need to manually drop its reference to session
@@ -2254,6 +2333,7 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
{
ast_sip_session_response_cb cb;
struct ast_sip_session *session = inv->mod_data[session_module.id];
+ pjsip_tx_data *tdata;
print_debug_details(inv, tsx, e);
if (!session) {
@@ -2283,12 +2363,23 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
if (tsx->status_code == PJSIP_SC_REQUEST_PENDING) {
reschedule_reinvite(session, cb);
return;
- } else if (inv->state == PJSIP_INV_STATE_CONFIRMED &&
- tsx->status_code != 488) {
- /* Other reinvite failures (except 488) result in destroying the session. */
- pjsip_tx_data *tdata;
- if (pjsip_inv_end_session(inv, 500, NULL, &tdata) == PJ_SUCCESS) {
- ast_sip_session_send_request(session, tdata);
+ }
+ if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
+ ast_debug(1, "reINVITE received final response code %d\n",
+ tsx->status_code);
+ if ((tsx->status_code == 401 || tsx->status_code == 407)
+ && !ast_sip_create_request_with_auth(
+ &session->endpoint->outbound_auths,
+ e->body.tsx_state.src.rdata, tsx, &tdata)) {
+ /* Send authed reINVITE */
+ ast_sip_session_send_request_with_cb(session, tdata, cb);
+ return;
+ }
+ if (tsx->status_code != 488) {
+ /* Other reinvite failures (except 488) result in destroying the session. */
+ if (pjsip_inv_end_session(inv, 500, NULL, &tdata) == PJ_SUCCESS) {
+ ast_sip_session_send_request(session, tdata);
+ }
}
}
} else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
@@ -2299,14 +2390,30 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
* a cancelled call. Our role is to immediately send a BYE to end the
* dialog.
*/
- pjsip_tx_data *tdata;
-
if (pjsip_inv_end_session(inv, 500, NULL, &tdata) == PJ_SUCCESS) {
ast_sip_session_send_request(session, tdata);
}
}
}
}
+ } else {
+ /* All other methods */
+ if (tsx->role == PJSIP_ROLE_UAC) {
+ if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
+ /* This means we got a final response to our outgoing method */
+ ast_debug(1, "%.*s received final response code %d\n",
+ (int) pj_strlen(&tsx->method.name), pj_strbuf(&tsx->method.name),
+ tsx->status_code);
+ if ((tsx->status_code == 401 || tsx->status_code == 407)
+ && !ast_sip_create_request_with_auth(
+ &session->endpoint->outbound_auths,
+ e->body.tsx_state.src.rdata, tsx, &tdata)) {
+ /* Send authed version of the method */
+ ast_sip_session_send_request_with_cb(session, tdata, cb);
+ return;
+ }
+ }
+ }
}
if (cb) {
cb(session, e->body.tsx_state.src.rdata);
@@ -2640,6 +2747,7 @@ static int load_module(void)
return AST_MODULE_LOAD_DECLINE;
}
ast_sip_register_service(&session_reinvite_module);
+ ast_sip_register_service(&outbound_invite_auth_module);
ast_module_shutdown_ref(ast_module_info->self);
@@ -2648,6 +2756,7 @@ static int load_module(void)
static int unload_module(void)
{
+ ast_sip_unregister_service(&outbound_invite_auth_module);
ast_sip_unregister_service(&session_reinvite_module);
ast_sip_unregister_service(&session_module);
ast_sorcery_delete(ast_sip_get_sorcery(), nat_hook);
@@ -2657,8 +2766,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Session resource",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_sips_contact.c b/res/res_pjsip_sips_contact.c
index f8e554cf4..7579be6f3 100644
--- a/res/res_pjsip_sips_contact.c
+++ b/res/res_pjsip_sips_contact.c
@@ -100,8 +100,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "UAC SIPS Contact support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c
index 08e7cf935..addfd5861 100644
--- a/res/res_pjsip_t38.c
+++ b/res/res_pjsip_t38.c
@@ -907,8 +907,8 @@ end:
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP T.38 UDPTL Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DRIVER,
+);
diff --git a/res/res_pjsip_transport_websocket.c b/res/res_pjsip_transport_websocket.c
index 94902d65b..914c8b8ff 100644
--- a/res/res_pjsip_transport_websocket.c
+++ b/res/res_pjsip_transport_websocket.c
@@ -197,12 +197,13 @@ static int transport_read(void *data)
pjsip_rx_data *rdata = &newtransport->rdata;
int recvd;
pj_str_t buf;
+ int pjsip_pkt_len;
pj_gettimeofday(&rdata->pkt_info.timestamp);
- pj_memcpy(rdata->pkt_info.packet, read_data->payload,
- PJSIP_MAX_PKT_LEN < read_data->payload_len ? PJSIP_MAX_PKT_LEN : read_data->payload_len);
- rdata->pkt_info.len = read_data->payload_len;
+ pjsip_pkt_len = PJSIP_MAX_PKT_LEN < read_data->payload_len ? PJSIP_MAX_PKT_LEN : read_data->payload_len;
+ pj_memcpy(rdata->pkt_info.packet, read_data->payload, pjsip_pkt_len);
+ rdata->pkt_info.len = pjsip_pkt_len;
rdata->pkt_info.zero = 0;
pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(session))), &rdata->pkt_info.src_addr);
@@ -397,8 +398,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP WebSocket Transport Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_xpidf_body_generator.c b/res/res_pjsip_xpidf_body_generator.c
index 43cb1e78b..37676ff96 100644
--- a/res/res_pjsip_xpidf_body_generator.c
+++ b/res/res_pjsip_xpidf_body_generator.c
@@ -174,8 +174,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State PIDF Provider",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
);
diff --git a/res/res_pktccops.c b/res/res_pktccops.c
index 06282c572..fedd069df 100644
--- a/res/res_pktccops.c
+++ b/res/res_pktccops.c
@@ -1511,9 +1511,9 @@ static int reload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "PktcCOPS manager for MGCP",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .reload = reload_module,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload_module,
+);
diff --git a/res/res_resolver_unbound.c b/res/res_resolver_unbound.c
index 4e79359a7..a4e86a682 100644
--- a/res/res_resolver_unbound.c
+++ b/res/res_resolver_unbound.c
@@ -25,6 +25,7 @@
ASTERISK_REGISTER_FILE()
+#include <signal.h>
#include <unbound.h>
#include <arpa/nameser.h>
@@ -1457,9 +1458,9 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Unbound DNS Resolver Support",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
+);
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 62601dcad..2e9d7e35e 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -1270,6 +1270,7 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
}
if (!ast_rtp_engine_srtp_is_registered()) {
+ ast_log(LOG_ERROR, "SRTP support module is not loaded or available. Try loading res_srtp.so.\n");
return -1;
}
@@ -2015,8 +2016,9 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
#ifdef HAVE_OPENSSL_SRTP
dtls_srtp_check_pending(instance, rtp, rtcp);
- /* If this is an SSL packet pass it to OpenSSL for processing */
- if ((*in >= 20) && (*in <= 64)) {
+ /* If this is an SSL packet pass it to OpenSSL for processing. RFC section for first byte value:
+ * https://tools.ietf.org/html/rfc5764#section-5.1.2 */
+ if ((*in >= 20) && (*in <= 63)) {
struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
int res = 0;
@@ -5366,9 +5368,9 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Asterisk RTP Stack",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
diff --git a/res/res_security_log.c b/res/res_security_log.c
index d32b32c9e..94a78d803 100644
--- a/res/res_security_log.c
+++ b/res/res_security_log.c
@@ -152,7 +152,7 @@ static int load_module(void)
static int unload_module(void)
{
if (security_stasis_sub) {
- security_stasis_sub = stasis_unsubscribe(security_stasis_sub);
+ security_stasis_sub = stasis_unsubscribe_and_join(security_stasis_sub);
}
ast_logger_unregister_level(LOG_SECURITY_NAME);
diff --git a/res/res_smdi.c b/res/res_smdi.c
index 6d9a678aa..770ac62d2 100644
--- a/res/res_smdi.c
+++ b/res/res_smdi.c
@@ -1433,9 +1433,9 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Simplified Message Desk Interface (SMDI) Resource",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
diff --git a/res/res_snmp.c b/res/res_snmp.c
index 1a19d7699..7eef9f85b 100644
--- a/res/res_snmp.c
+++ b/res/res_snmp.c
@@ -136,7 +136,7 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "SNMP [Sub]Agent for Asterisk",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/res/res_sorcery_memory_cache.c b/res/res_sorcery_memory_cache.c
new file mode 100644
index 000000000..d2c648cff
--- /dev/null
+++ b/res/res_sorcery_memory_cache.c
@@ -0,0 +1,2555 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Sorcery Memory Cache Object Wizard
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_REGISTER_FILE()
+
+#include "asterisk/module.h"
+#include "asterisk/sorcery.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/sched.h"
+#include "asterisk/test.h"
+#include "asterisk/heap.h"
+#include "asterisk/cli.h"
+#include "asterisk/manager.h"
+
+/*** DOCUMENTATION
+ <manager name="SorceryMemoryCacheExpireObject" language="en_US">
+ <synopsis>
+ Expire (remove) an object from a sorcery memory cache.
+ </synopsis>
+ <syntax>
+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+ <parameter name="Cache" required="true">
+ <para>The name of the cache to expire the object from.</para>
+ </parameter>
+ <parameter name="Object" required="true">
+ <para>The name of the object to expire.</para>
+ </parameter>
+ </syntax>
+ <description>
+ <para>Expires (removes) an object from a sorcery memory cache.</para>
+ </description>
+ </manager>
+ <manager name="SorceryMemoryCacheExpire" language="en_US">
+ <synopsis>
+ Expire (remove) ALL objects from a sorcery memory cache.
+ </synopsis>
+ <syntax>
+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+ <parameter name="Cache" required="true">
+ <para>The name of the cache to expire all objects from.</para>
+ </parameter>
+ </syntax>
+ <description>
+ <para>Expires (removes) ALL objects from a sorcery memory cache.</para>
+ </description>
+ </manager>
+ <manager name="SorceryMemoryCacheStaleObject" language="en_US">
+ <synopsis>
+ Mark an object in a sorcery memory cache as stale.
+ </synopsis>
+ <syntax>
+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+ <parameter name="Cache" required="true">
+ <para>The name of the cache to mark the object as stale in.</para>
+ </parameter>
+ <parameter name="Object" required="true">
+ <para>The name of the object to mark as stale.</para>
+ </parameter>
+ </syntax>
+ <description>
+ <para>Marks an object as stale within a sorcery memory cache.</para>
+ </description>
+ </manager>
+ <manager name="SorceryMemoryCacheStale" language="en_US">
+ <synopsis>
+ Marks ALL objects in a sorcery memory cache as stale.
+ </synopsis>
+ <syntax>
+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+ <parameter name="Cache" required="true">
+ <para>The name of the cache to mark all object as stale in.</para>
+ </parameter>
+ </syntax>
+ <description>
+ <para>Marks ALL objects in a sorcery memory cache as stale.</para>
+ </description>
+ </manager>
+ ***/
+
+/*! \brief Structure for storing a memory cache */
+struct sorcery_memory_cache {
+ /*! \brief The name of the memory cache */
+ char *name;
+ /*! \brief Objects in the cache */
+ struct ao2_container *objects;
+ /*! \brief The maximum number of objects permitted in the cache, 0 if no limit */
+ unsigned int maximum_objects;
+ /*! \brief The maximum time (in seconds) an object will stay in the cache, 0 if no limit */
+ unsigned int object_lifetime_maximum;
+ /*! \brief The amount of time (in seconds) before an object is marked as stale, 0 if disabled */
+ unsigned int object_lifetime_stale;
+ /*! \brief Whether objects are prefetched from normal storage at load time, 0 if disabled */
+ unsigned int prefetch;
+ /** \brief Whether all objects are expired when the object type is reloaded, 0 if disabled */
+ unsigned int expire_on_reload;
+ /*! \brief Heap of cached objects. Oldest object is at the top. */
+ struct ast_heap *object_heap;
+ /*! \brief Scheduler item for expiring oldest object. */
+ int expire_id;
+#ifdef TEST_FRAMEWORK
+ /*! \brief Variable used to indicate we should notify a test when we reach empty */
+ unsigned int cache_notify;
+ /*! \brief Mutex lock used for signaling when the cache has reached empty */
+ ast_mutex_t lock;
+ /*! \brief Condition used for signaling when the cache has reached empty */
+ ast_cond_t cond;
+ /*! \brief Variable that is set when the cache has reached empty */
+ unsigned int cache_completed;
+#endif
+};
+
+/*! \brief Structure for stored a cached object */
+struct sorcery_memory_cached_object {
+ /*! \brief The cached object */
+ void *object;
+ /*! \brief The time at which the object was created */
+ struct timeval created;
+ /*! \brief index required by heap */
+ ssize_t __heap_index;
+ /*! \brief scheduler id of stale update task */
+ int stale_update_sched_id;
+};
+
+static void *sorcery_memory_cache_open(const char *data);
+static int sorcery_memory_cache_create(const struct ast_sorcery *sorcery, void *data, void *object);
+static void sorcery_memory_cache_load(void *data, const struct ast_sorcery *sorcery, const char *type);
+static void sorcery_memory_cache_reload(void *data, const struct ast_sorcery *sorcery, const char *type);
+static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type,
+ const char *id);
+static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object);
+static void sorcery_memory_cache_close(void *data);
+
+static struct ast_sorcery_wizard memory_cache_object_wizard = {
+ .name = "memory_cache",
+ .open = sorcery_memory_cache_open,
+ .create = sorcery_memory_cache_create,
+ .update = sorcery_memory_cache_create,
+ .delete = sorcery_memory_cache_delete,
+ .load = sorcery_memory_cache_load,
+ .reload = sorcery_memory_cache_reload,
+ .retrieve_id = sorcery_memory_cache_retrieve_id,
+ .close = sorcery_memory_cache_close,
+};
+
+/*! \brief The bucket size for the container of caches */
+#define CACHES_CONTAINER_BUCKET_SIZE 53
+
+/*! \brief The default bucket size for the container of objects in the cache */
+#define CACHE_CONTAINER_BUCKET_SIZE 53
+
+/*! \brief Height of heap for cache object heap. Allows 31 initial objects */
+#define CACHE_HEAP_INIT_HEIGHT 5
+
+/*! \brief Container of created caches */
+static struct ao2_container *caches;
+
+/*! \brief Scheduler for cache management */
+static struct ast_sched_context *sched;
+
+#define STALE_UPDATE_THREAD_ID 0x5EED1E55
+AST_THREADSTORAGE(stale_update_id_storage);
+
+static int is_stale_update(void)
+{
+ uint32_t *stale_update_thread_id;
+
+ stale_update_thread_id = ast_threadstorage_get(&stale_update_id_storage,
+ sizeof(*stale_update_thread_id));
+ if (!stale_update_thread_id) {
+ return 0;
+ }
+
+ return *stale_update_thread_id == STALE_UPDATE_THREAD_ID;
+}
+
+static void start_stale_update(void)
+{
+ uint32_t *stale_update_thread_id;
+
+ stale_update_thread_id = ast_threadstorage_get(&stale_update_id_storage,
+ sizeof(*stale_update_thread_id));
+ if (!stale_update_thread_id) {
+ ast_log(LOG_ERROR, "Could not set stale update ID for sorcery memory cache thread\n");
+ return;
+ }
+
+ *stale_update_thread_id = STALE_UPDATE_THREAD_ID;
+}
+
+static void end_stale_update(void)
+{
+ uint32_t *stale_update_thread_id;
+
+ stale_update_thread_id = ast_threadstorage_get(&stale_update_id_storage,
+ sizeof(*stale_update_thread_id));
+ if (!stale_update_thread_id) {
+ ast_log(LOG_ERROR, "Could not set stale update ID for sorcery memory cache thread\n");
+ return;
+ }
+
+ *stale_update_thread_id = 0;
+}
+
+/*!
+ * \internal
+ * \brief Hashing function for the container holding caches
+ *
+ * \param obj A sorcery memory cache or name of one
+ * \param flags Hashing flags
+ *
+ * \return The hash of the memory cache name
+ */
+static int sorcery_memory_cache_hash(const void *obj, int flags)
+{
+ const struct sorcery_memory_cache *cache = obj;
+ const char *name = obj;
+ int hash;
+
+ switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
+ default:
+ case OBJ_SEARCH_OBJECT:
+ name = cache->name;
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ hash = ast_str_hash(name);
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ /* Should never happen in hash callback. */
+ ast_assert(0);
+ hash = 0;
+ break;
+ }
+ return hash;
+}
+
+/*!
+ * \internal
+ * \brief Comparison function for the container holding caches
+ *
+ * \param obj A sorcery memory cache
+ * \param arg A sorcery memory cache, or name of one
+ * \param flags Comparison flags
+ *
+ * \retval CMP_MATCH if the name is the same
+ * \retval 0 if the name does not match
+ */
+static int sorcery_memory_cache_cmp(void *obj, void *arg, int flags)
+{
+ const struct sorcery_memory_cache *left = obj;
+ const struct sorcery_memory_cache *right = arg;
+ const char *right_name = arg;
+ int cmp;
+
+ switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
+ default:
+ case OBJ_SEARCH_OBJECT:
+ right_name = right->name;
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ cmp = strcmp(left->name, right_name);
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ cmp = strncmp(left->name, right_name, strlen(right_name));
+ break;
+ }
+ return cmp ? 0 : CMP_MATCH;
+}
+
+/*!
+ * \internal
+ * \brief Hashing function for the container holding cached objects
+ *
+ * \param obj A cached object or id of one
+ * \param flags Hashing flags
+ *
+ * \return The hash of the cached object id
+ */
+static int sorcery_memory_cached_object_hash(const void *obj, int flags)
+{
+ const struct sorcery_memory_cached_object *cached = obj;
+ const char *name = obj;
+ int hash;
+
+ switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
+ default:
+ case OBJ_SEARCH_OBJECT:
+ name = ast_sorcery_object_get_id(cached->object);
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ hash = ast_str_hash(name);
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ /* Should never happen in hash callback. */
+ ast_assert(0);
+ hash = 0;
+ break;
+ }
+ return hash;
+}
+
+/*!
+ * \internal
+ * \brief Comparison function for the container holding cached objects
+ *
+ * \param obj A cached object
+ * \param arg A cached object, or id of one
+ * \param flags Comparison flags
+ *
+ * \retval CMP_MATCH if the id is the same
+ * \retval 0 if the id does not match
+ */
+static int sorcery_memory_cached_object_cmp(void *obj, void *arg, int flags)
+{
+ struct sorcery_memory_cached_object *left = obj;
+ struct sorcery_memory_cached_object *right = arg;
+ const char *right_name = arg;
+ int cmp;
+
+ switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
+ default:
+ case OBJ_SEARCH_OBJECT:
+ right_name = ast_sorcery_object_get_id(right->object);
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ cmp = strcmp(ast_sorcery_object_get_id(left->object), right_name);
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ cmp = strncmp(ast_sorcery_object_get_id(left->object), right_name, strlen(right_name));
+ break;
+ }
+ return cmp ? 0 : CMP_MATCH;
+}
+
+/*!
+ * \internal
+ * \brief Destructor function for a sorcery memory cache
+ *
+ * \param obj A sorcery memory cache
+ */
+static void sorcery_memory_cache_destructor(void *obj)
+{
+ struct sorcery_memory_cache *cache = obj;
+
+ ast_free(cache->name);
+ ao2_cleanup(cache->objects);
+ if (cache->object_heap) {
+ ast_heap_destroy(cache->object_heap);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Destructor function for sorcery memory cached objects
+ *
+ * \param obj A sorcery memory cached object
+ */
+static void sorcery_memory_cached_object_destructor(void *obj)
+{
+ struct sorcery_memory_cached_object *cached = obj;
+
+ ao2_cleanup(cached->object);
+}
+
+static int schedule_cache_expiration(struct sorcery_memory_cache *cache);
+
+/*!
+ * \internal
+ * \brief Remove an object from the cache.
+ *
+ * This removes the item from both the hashtable and the heap.
+ *
+ * \pre cache->objects is write-locked
+ *
+ * \param cache The cache from which the object is being removed.
+ * \param id The sorcery object id of the object to remove.
+ * \param reschedule Reschedule cache expiration if this was the oldest object.
+ *
+ * \retval 0 Success
+ * \retval non-zero Failure
+ */
+static int remove_from_cache(struct sorcery_memory_cache *cache, const char *id, int reschedule)
+{
+ struct sorcery_memory_cached_object *hash_object;
+ struct sorcery_memory_cached_object *oldest_object;
+ struct sorcery_memory_cached_object *heap_object;
+
+ hash_object = ao2_find(cache->objects, id,
+ OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NOLOCK);
+ if (!hash_object) {
+ return -1;
+ }
+ oldest_object = ast_heap_peek(cache->object_heap, 1);
+ heap_object = ast_heap_remove(cache->object_heap, hash_object);
+
+ ast_assert(heap_object == hash_object);
+
+ ao2_ref(hash_object, -1);
+
+ if (reschedule && (oldest_object == heap_object)) {
+ schedule_cache_expiration(cache);
+ }
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Scheduler callback invoked to expire old objects
+ *
+ * \param data The opaque callback data (in our case, the memory cache)
+ */
+static int expire_objects_from_cache(const void *data)
+{
+ struct sorcery_memory_cache *cache = (struct sorcery_memory_cache *)data;
+ struct sorcery_memory_cached_object *cached;
+
+ ao2_wrlock(cache->objects);
+
+ cache->expire_id = -1;
+
+ /* This is an optimization for objects which have been cached close to eachother */
+ while ((cached = ast_heap_peek(cache->object_heap, 1))) {
+ int expiration;
+
+ expiration = ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(cache->object_lifetime_maximum, 1)), ast_tvnow());
+
+ /* If the current oldest object has not yet expired stop and reschedule for it */
+ if (expiration > 0) {
+ break;
+ }
+
+ remove_from_cache(cache, ast_sorcery_object_get_id(cached->object), 0);
+ }
+
+ schedule_cache_expiration(cache);
+
+ ao2_unlock(cache->objects);
+
+ ao2_ref(cache, -1);
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Remove all objects from the cache.
+ *
+ * This removes ALL objects from both the hash table and heap.
+ *
+ * \pre cache->objects is write-locked
+ *
+ * \param cache The cache to empty.
+ */
+static void remove_all_from_cache(struct sorcery_memory_cache *cache)
+{
+ while (ast_heap_pop(cache->object_heap));
+
+ ao2_callback(cache->objects, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
+ NULL, NULL);
+
+ AST_SCHED_DEL_UNREF(sched, cache->expire_id, ao2_ref(cache, -1));
+}
+
+/*!
+ * \internal
+ * \brief AO2 callback function for making an object stale immediately
+ *
+ * This changes the creation time of an object so it appears as though it is stale immediately.
+ *
+ * \param obj The cached object
+ * \param arg The cache itself
+ * \param flags Unused flags
+ */
+static int object_stale_callback(void *obj, void *arg, int flags)
+{
+ struct sorcery_memory_cached_object *cached = obj;
+ struct sorcery_memory_cache *cache = arg;
+
+ /* Since our granularity is seconds it's possible for something to retrieve us within a window
+ * where we wouldn't be treated as stale. To ensure that doesn't happen we use the configured stale
+ * time plus a second.
+ */
+ cached->created = ast_tvsub(cached->created, ast_samp2tv(cache->object_lifetime_stale + 1, 1));
+
+ return CMP_MATCH;
+}
+
+/*!
+ * \internal
+ * \brief Mark an object as stale explicitly.
+ *
+ * This changes the creation time of an object so it appears as though it is stale immediately.
+ *
+ * \pre cache->objects is read-locked
+ *
+ * \param cache The cache the object is in
+ * \param id The unique identifier of the object
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+static int mark_object_as_stale_in_cache(struct sorcery_memory_cache *cache, const char *id)
+{
+ struct sorcery_memory_cached_object *cached;
+
+ cached = ao2_find(cache->objects, id, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ if (!cached) {
+ return -1;
+ }
+
+ object_stale_callback(cached, cache, 0);
+ ao2_ref(cached, -1);
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Mark all objects as stale within a cache.
+ *
+ * This changes the creation time of ALL objects so they appear as though they are stale.
+ *
+ * \pre cache->objects is read-locked
+ *
+ * \param cache
+ */
+static void mark_all_as_stale_in_cache(struct sorcery_memory_cache *cache)
+{
+ ao2_callback(cache->objects, OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, object_stale_callback, cache);
+}
+
+/*!
+ * \internal
+ * \brief Schedule a callback for cached object expiration.
+ *
+ * \pre cache->objects is write-locked
+ *
+ * \param cache The cache that is having its callback scheduled.
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+static int schedule_cache_expiration(struct sorcery_memory_cache *cache)
+{
+ struct sorcery_memory_cached_object *cached;
+ int expiration = 0;
+
+ if (!cache->object_lifetime_maximum) {
+ return 0;
+ }
+
+ if (cache->expire_id != -1) {
+ /* If we can't unschedule this expiration then it is currently attempting to run,
+ * so let it run - it just means that it'll be the one scheduling instead of us.
+ */
+ if (ast_sched_del(sched, cache->expire_id)) {
+ return 0;
+ }
+
+ /* Since it successfully cancelled we need to drop the ref to the cache it had */
+ ao2_ref(cache, -1);
+ cache->expire_id = -1;
+ }
+
+ cached = ast_heap_peek(cache->object_heap, 1);
+ if (!cached) {
+#ifdef TEST_FRAMEWORK
+ ast_mutex_lock(&cache->lock);
+ cache->cache_completed = 1;
+ ast_cond_signal(&cache->cond);
+ ast_mutex_unlock(&cache->lock);
+#endif
+ return 0;
+ }
+
+ expiration = MAX(ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(cache->object_lifetime_maximum, 1)), ast_tvnow()),
+ 1);
+
+ cache->expire_id = ast_sched_add(sched, expiration, expire_objects_from_cache, ao2_bump(cache));
+ if (cache->expire_id < 0) {
+ ao2_ref(cache, -1);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Remove the oldest item from the cache.
+ *
+ * \pre cache->objects is write-locked
+ *
+ * \param cache The cache from which to remove the oldest object
+ *
+ * \retval 0 Success
+ * \retval non-zero Failure
+ */
+static int remove_oldest_from_cache(struct sorcery_memory_cache *cache)
+{
+ struct sorcery_memory_cached_object *heap_old_object;
+ struct sorcery_memory_cached_object *hash_old_object;
+
+ heap_old_object = ast_heap_pop(cache->object_heap);
+ if (!heap_old_object) {
+ return -1;
+ }
+ hash_old_object = ao2_find(cache->objects, heap_old_object,
+ OBJ_SEARCH_OBJECT | OBJ_UNLINK | OBJ_NOLOCK);
+
+ ast_assert(heap_old_object == hash_old_object);
+
+ ao2_ref(hash_old_object, -1);
+
+ schedule_cache_expiration(cache);
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Add a new object to the cache.
+ *
+ * \pre cache->objects is write-locked
+ *
+ * \param cache The cache in which to add the new object
+ * \param cached_object The object to add to the cache
+ *
+ * \retval 0 Success
+ * \retval non-zero Failure
+ */
+static int add_to_cache(struct sorcery_memory_cache *cache,
+ struct sorcery_memory_cached_object *cached_object)
+{
+ if (!ao2_link_flags(cache->objects, cached_object, OBJ_NOLOCK)) {
+ return -1;
+ }
+
+ if (ast_heap_push(cache->object_heap, cached_object)) {
+ ao2_find(cache->objects, cached_object,
+ OBJ_SEARCH_OBJECT | OBJ_UNLINK | OBJ_NODATA | OBJ_NOLOCK);
+ return -1;
+ }
+
+ if (cache->expire_id == -1) {
+ schedule_cache_expiration(cache);
+ }
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Callback function to cache an object in a memory cache
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param object The object to cache
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+static int sorcery_memory_cache_create(const struct ast_sorcery *sorcery, void *data, void *object)
+{
+ struct sorcery_memory_cache *cache = data;
+ struct sorcery_memory_cached_object *cached;
+
+ cached = ao2_alloc(sizeof(*cached), sorcery_memory_cached_object_destructor);
+ if (!cached) {
+ return -1;
+ }
+ cached->object = ao2_bump(object);
+ cached->created = ast_tvnow();
+ cached->stale_update_sched_id = -1;
+
+ /* As there is no guarantee that this won't be called by multiple threads wanting to cache
+ * the same object we remove any old ones, which turns this into a create/update function
+ * in reality. As well since there's no guarantee that the object in the cache is the same
+ * one here we remove any old objects using the object identifier.
+ */
+
+ ao2_wrlock(cache->objects);
+ remove_from_cache(cache, ast_sorcery_object_get_id(object), 1);
+ if (cache->maximum_objects && ao2_container_count(cache->objects) >= cache->maximum_objects) {
+ if (remove_oldest_from_cache(cache)) {
+ ast_log(LOG_ERROR, "Unable to make room in cache for sorcery object '%s'.\n",
+ ast_sorcery_object_get_id(object));
+ ao2_ref(cached, -1);
+ ao2_unlock(cache->objects);
+ return -1;
+ }
+ }
+ if (add_to_cache(cache, cached)) {
+ ast_log(LOG_ERROR, "Unable to add object '%s' to the cache\n",
+ ast_sorcery_object_get_id(object));
+ ao2_ref(cached, -1);
+ ao2_unlock(cache->objects);
+ return -1;
+ }
+ ao2_unlock(cache->objects);
+
+ ao2_ref(cached, -1);
+ return 0;
+}
+
+struct stale_update_task_data {
+ struct ast_sorcery *sorcery;
+ struct sorcery_memory_cache *cache;
+ void *object;
+};
+
+static void stale_update_task_data_destructor(void *obj)
+{
+ struct stale_update_task_data *task_data = obj;
+
+ ao2_cleanup(task_data->cache);
+ ao2_cleanup(task_data->object);
+ ast_sorcery_unref(task_data->sorcery);
+}
+
+static struct stale_update_task_data *stale_update_task_data_alloc(struct ast_sorcery *sorcery,
+ struct sorcery_memory_cache *cache, const char *type, void *object)
+{
+ struct stale_update_task_data *task_data;
+
+ task_data = ao2_alloc_options(sizeof(*task_data), stale_update_task_data_destructor,
+ AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!task_data) {
+ return NULL;
+ }
+
+ task_data->sorcery = ao2_bump(sorcery);
+ task_data->cache = ao2_bump(cache);
+ task_data->object = ao2_bump(object);
+
+ return task_data;
+}
+
+static int stale_item_update(const void *data)
+{
+ struct stale_update_task_data *task_data = (struct stale_update_task_data *) data;
+ void *object;
+
+ start_stale_update();
+
+ object = ast_sorcery_retrieve_by_id(task_data->sorcery,
+ ast_sorcery_object_get_type(task_data->object),
+ ast_sorcery_object_get_id(task_data->object));
+ if (!object) {
+ ast_debug(1, "Backend no longer has object type '%s' ID '%s'. Removing from cache\n",
+ ast_sorcery_object_get_type(task_data->object),
+ ast_sorcery_object_get_id(task_data->object));
+ sorcery_memory_cache_delete(task_data->sorcery, task_data->cache,
+ task_data->object);
+ } else {
+ ast_debug(1, "Refreshing stale cache object type '%s' ID '%s'\n",
+ ast_sorcery_object_get_type(task_data->object),
+ ast_sorcery_object_get_id(task_data->object));
+ sorcery_memory_cache_create(task_data->sorcery, task_data->cache,
+ object);
+ }
+
+ ao2_ref(task_data, -1);
+ end_stale_update();
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Callback function to retrieve an object from a memory cache
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param type The type of the object to retrieve
+ * \param id The id of the object to retrieve
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id)
+{
+ struct sorcery_memory_cache *cache = data;
+ struct sorcery_memory_cached_object *cached;
+ void *object;
+
+ if (is_stale_update()) {
+ return NULL;
+ }
+
+ cached = ao2_find(cache->objects, id, OBJ_SEARCH_KEY);
+ if (!cached) {
+ return NULL;
+ }
+
+ if (cache->object_lifetime_stale) {
+ struct timeval elapsed;
+
+ elapsed = ast_tvsub(ast_tvnow(), cached->created);
+ if (elapsed.tv_sec > cache->object_lifetime_stale) {
+ ao2_lock(cached);
+ if (cached->stale_update_sched_id == -1) {
+ struct stale_update_task_data *task_data;
+
+ task_data = stale_update_task_data_alloc((struct ast_sorcery *)sorcery, cache,
+ type, cached->object);
+ if (task_data) {
+ ast_debug(1, "Cached sorcery object type '%s' ID '%s' is stale. Refreshing\n",
+ type, id);
+ cached->stale_update_sched_id = ast_sched_add(sched, 1, stale_item_update, task_data);
+ } else {
+ ast_log(LOG_ERROR, "Unable to update stale cached object type '%s', ID '%s'.\n",
+ type, id);
+ }
+ }
+ ao2_unlock(cached);
+ }
+ }
+
+ object = ao2_bump(cached->object);
+ ao2_ref(cached, -1);
+
+ return object;
+}
+
+/*!
+ * \internal
+ * \brief Callback function to finish configuring the memory cache and to prefetch objects
+ *
+ * \param data The sorcery memory cache
+ * \param sorcery The sorcery instance
+ * \param type The type of object being loaded
+ */
+static void sorcery_memory_cache_load(void *data, const struct ast_sorcery *sorcery, const char *type)
+{
+ struct sorcery_memory_cache *cache = data;
+
+ /* If no name was explicitly specified generate one given the sorcery instance and object type */
+ if (ast_strlen_zero(cache->name)) {
+ ast_asprintf(&cache->name, "%s/%s", ast_sorcery_get_module(sorcery), type);
+ }
+
+ ao2_link(caches, cache);
+ ast_debug(1, "Memory cache '%s' associated with sorcery instance '%p' of module '%s' with object type '%s'\n",
+ cache->name, sorcery, ast_sorcery_get_module(sorcery), type);
+}
+
+/*!
+ * \internal
+ * \brief Callback function to expire objects from the memory cache on reload (if configured)
+ *
+ * \param data The sorcery memory cache
+ * \param sorcery The sorcery instance
+ * \param type The type of object being reloaded
+ */
+static void sorcery_memory_cache_reload(void *data, const struct ast_sorcery *sorcery, const char *type)
+{
+}
+
+/*!
+ * \internal
+ * \brief Function used to take an unsigned integer based configuration option and parse it
+ *
+ * \param value The string value of the configuration option
+ * \param result The unsigned integer to place the result in
+ *
+ * \retval 0 failure
+ * \retval 1 success
+ */
+static int configuration_parse_unsigned_integer(const char *value, unsigned int *result)
+{
+ if (ast_strlen_zero(value) || !strncmp(value, "-", 1)) {
+ return 0;
+ }
+
+ return sscanf(value, "%30u", result);
+}
+
+static int age_cmp(void *a, void *b)
+{
+ return ast_tvcmp(((struct sorcery_memory_cached_object *) b)->created,
+ ((struct sorcery_memory_cached_object *) a)->created);
+}
+
+/*!
+ * \internal
+ * \brief Callback function to create a new sorcery memory cache using provided configuration
+ *
+ * \param data A stringified configuration for the memory cache
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static void *sorcery_memory_cache_open(const char *data)
+{
+ char *options = ast_strdup(data), *option;
+ RAII_VAR(struct sorcery_memory_cache *, cache, NULL, ao2_cleanup);
+
+ cache = ao2_alloc_options(sizeof(*cache), sorcery_memory_cache_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!cache) {
+ return NULL;
+ }
+
+ cache->expire_id = -1;
+
+ /* If no configuration options have been provided this memory cache will operate in a default
+ * configuration.
+ */
+ while (!ast_strlen_zero(options) && (option = strsep(&options, ","))) {
+ char *name = strsep(&option, "="), *value = option;
+
+ if (!strcasecmp(name, "name")) {
+ if (ast_strlen_zero(value)) {
+ ast_log(LOG_ERROR, "A name must be specified for the memory cache\n");
+ return NULL;
+ }
+ ast_free(cache->name);
+ cache->name = ast_strdup(value);
+ } else if (!strcasecmp(name, "maximum_objects")) {
+ if (configuration_parse_unsigned_integer(value, &cache->maximum_objects) != 1) {
+ ast_log(LOG_ERROR, "Unsupported maximum objects value of '%s' used for memory cache\n",
+ value);
+ return NULL;
+ }
+ } else if (!strcasecmp(name, "object_lifetime_maximum")) {
+ if (configuration_parse_unsigned_integer(value, &cache->object_lifetime_maximum) != 1) {
+ ast_log(LOG_ERROR, "Unsupported object maximum lifetime value of '%s' used for memory cache\n",
+ value);
+ return NULL;
+ }
+ } else if (!strcasecmp(name, "object_lifetime_stale")) {
+ if (configuration_parse_unsigned_integer(value, &cache->object_lifetime_stale) != 1) {
+ ast_log(LOG_ERROR, "Unsupported object stale lifetime value of '%s' used for memory cache\n",
+ value);
+ return NULL;
+ }
+ } else if (!strcasecmp(name, "prefetch")) {
+ cache->prefetch = ast_true(value);
+ } else if (!strcasecmp(name, "expire_on_reload")) {
+ cache->expire_on_reload = ast_true(value);
+ } else {
+ ast_log(LOG_ERROR, "Unsupported option '%s' used for memory cache\n", name);
+ return NULL;
+ }
+ }
+
+ cache->objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK,
+ cache->maximum_objects ? cache->maximum_objects : CACHE_CONTAINER_BUCKET_SIZE,
+ sorcery_memory_cached_object_hash, sorcery_memory_cached_object_cmp);
+ if (!cache->objects) {
+ ast_log(LOG_ERROR, "Could not create a container to hold cached objects for memory cache\n");
+ return NULL;
+ }
+
+ cache->object_heap = ast_heap_create(CACHE_HEAP_INIT_HEIGHT, age_cmp,
+ offsetof(struct sorcery_memory_cached_object, __heap_index));
+ if (!cache->object_heap) {
+ ast_log(LOG_ERROR, "Could not create heap to hold cached objects\n");
+ return NULL;
+ }
+
+ /* The memory cache is not linked to the caches container until the load callback is invoked.
+ * Linking occurs there so an intelligent cache name can be constructed using the module of
+ * the sorcery instance and the specific object type if no cache name was specified as part
+ * of the configuration.
+ */
+
+ /* This is done as RAII_VAR will drop the reference */
+ return ao2_bump(cache);
+}
+
+/*!
+ * \internal
+ * \brief Callback function to delete an object from a memory cache
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param object The object to cache
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object)
+{
+ struct sorcery_memory_cache *cache = data;
+ int res;
+
+ ao2_wrlock(cache->objects);
+ res = remove_from_cache(cache, ast_sorcery_object_get_id(object), 1);
+ ao2_unlock(cache->objects);
+
+ if (res) {
+ ast_log(LOG_ERROR, "Unable to delete object '%s' from sorcery cache\n", ast_sorcery_object_get_id(object));
+ }
+
+ return res;
+}
+
+/*!
+ * \internal
+ * \brief Callback function to terminate a memory cache
+ *
+ * \param data The sorcery memory cache
+ */
+static void sorcery_memory_cache_close(void *data)
+{
+ struct sorcery_memory_cache *cache = data;
+
+ /* This can occur if a cache is created but never loaded */
+ if (!ast_strlen_zero(cache->name)) {
+ ao2_unlink(caches, cache);
+ }
+
+ if (cache->object_lifetime_maximum) {
+ /* If object lifetime support is enabled we need to explicitly drop all cached objects here
+ * and stop the scheduled task. Failure to do so could potentially keep the cache around for
+ * a prolonged period of time.
+ */
+ ao2_wrlock(cache->objects);
+ ao2_callback(cache->objects, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
+ NULL, NULL);
+ AST_SCHED_DEL_UNREF(sched, cache->expire_id, ao2_ref(cache, -1));
+ ao2_unlock(cache->objects);
+ }
+
+ ao2_ref(cache, -1);
+}
+
+/*!
+ * \internal
+ * \brief CLI tab completion for cache names
+ */
+static char *sorcery_memory_cache_complete_name(const char *word, int state)
+{
+ struct sorcery_memory_cache *cache;
+ struct ao2_iterator it_caches;
+ int wordlen = strlen(word);
+ int which = 0;
+ char *result = NULL;
+
+ it_caches = ao2_iterator_init(caches, 0);
+ while ((cache = ao2_iterator_next(&it_caches))) {
+ if (!strncasecmp(word, cache->name, wordlen)
+ && ++which > state) {
+ result = ast_strdup(cache->name);
+ }
+ ao2_ref(cache, -1);
+ if (result) {
+ break;
+ }
+ }
+ ao2_iterator_destroy(&it_caches);
+ return result;
+}
+
+/*!
+ * \internal
+ * \brief CLI command implementation for 'sorcery memory cache show'
+ */
+static char *sorcery_memory_cache_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct sorcery_memory_cache *cache;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sorcery memory cache show";
+ e->usage =
+ "Usage: sorcery memory cache show <name>\n"
+ " Show sorcery memory cache configuration and statistics.\n";
+ return NULL;
+ case CLI_GENERATE:
+ if (a->pos == 4) {
+ return sorcery_memory_cache_complete_name(a->word, a->n);
+ } else {
+ return NULL;
+ }
+ }
+
+ if (a->argc != 5) {
+ return CLI_SHOWUSAGE;
+ }
+
+ cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
+ if (!cache) {
+ ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd, "Sorcery memory cache: %s\n", cache->name);
+ ast_cli(a->fd, "Number of objects within cache: %d\n", ao2_container_count(cache->objects));
+ if (cache->maximum_objects) {
+ ast_cli(a->fd, "Maximum allowed objects: %d\n", cache->maximum_objects);
+ } else {
+ ast_cli(a->fd, "There is no limit on the maximum number of objects in the cache\n");
+ }
+ if (cache->object_lifetime_maximum) {
+ ast_cli(a->fd, "Number of seconds before object expires: %d\n", cache->object_lifetime_maximum);
+ } else {
+ ast_cli(a->fd, "Object expiration is not enabled - cached objects will not expire\n");
+ }
+ if (cache->object_lifetime_stale) {
+ ast_cli(a->fd, "Number of seconds before object becomes stale: %d\n", cache->object_lifetime_stale);
+ } else {
+ ast_cli(a->fd, "Object staleness is not enabled - cached objects will not go stale\n");
+ }
+ ast_cli(a->fd, "Prefetch: %s\n", AST_CLI_ONOFF(cache->prefetch));
+ ast_cli(a->fd, "Expire all objects on reload: %s\n", AST_CLI_ONOFF(cache->expire_on_reload));
+
+ ao2_ref(cache, -1);
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief Structure used to pass data for printing cached object information */
+struct print_object_details {
+ /*! \brief The sorcery memory cache */
+ struct sorcery_memory_cache *cache;
+ /*! \brief The CLI arguments */
+ struct ast_cli_args *a;
+};
+
+/*!
+ * \internal
+ * \brief Callback function for displaying object within the cache
+ */
+static int sorcery_memory_cache_print_object(void *obj, void *arg, int flags)
+{
+#define FORMAT "%-25.25s %-15u %-15u \n"
+ struct sorcery_memory_cached_object *cached = obj;
+ struct print_object_details *details = arg;
+ int seconds_until_expire = 0, seconds_until_stale = 0;
+
+ if (details->cache->object_lifetime_maximum) {
+ seconds_until_expire = ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(details->cache->object_lifetime_maximum, 1)), ast_tvnow()) / 1000;
+ }
+ if (details->cache->object_lifetime_stale) {
+ seconds_until_stale = ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(details->cache->object_lifetime_stale, 1)), ast_tvnow()) / 1000;
+ }
+
+ ast_cli(details->a->fd, FORMAT, ast_sorcery_object_get_id(cached->object), MAX(seconds_until_stale, 0), MAX(seconds_until_expire, 0));
+
+ return CMP_MATCH;
+#undef FORMAT
+}
+
+/*!
+ * \internal
+ * \brief CLI command implementation for 'sorcery memory cache dump'
+ */
+static char *sorcery_memory_cache_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%-25.25s %-15.15s %-15.15s \n"
+ struct sorcery_memory_cache *cache;
+ struct print_object_details details;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sorcery memory cache dump";
+ e->usage =
+ "Usage: sorcery memory cache dump <name>\n"
+ " Dump a list of the objects within the cache, listed by object identifier.\n";
+ return NULL;
+ case CLI_GENERATE:
+ if (a->pos == 4) {
+ return sorcery_memory_cache_complete_name(a->word, a->n);
+ } else {
+ return NULL;
+ }
+ }
+
+ if (a->argc != 5) {
+ return CLI_SHOWUSAGE;
+ }
+
+ cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
+ if (!cache) {
+ ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
+ return CLI_FAILURE;
+ }
+
+ details.cache = cache;
+ details.a = a;
+
+ ast_cli(a->fd, "Dumping sorcery memory cache '%s':\n", cache->name);
+ if (!cache->object_lifetime_stale) {
+ ast_cli(a->fd, " * Staleness is not enabled - objects will not go stale\n");
+ }
+ if (!cache->object_lifetime_maximum) {
+ ast_cli(a->fd, " * Object lifetime is not enabled - objects will not expire\n");
+ }
+ ast_cli(a->fd, FORMAT, "Object Name", "Stale In", "Expires In");
+ ast_cli(a->fd, FORMAT, "-------------------------", "---------------", "---------------");
+ ao2_callback(cache->objects, OBJ_NODATA | OBJ_MULTIPLE, sorcery_memory_cache_print_object, &details);
+ ast_cli(a->fd, FORMAT, "-------------------------", "---------------", "---------------");
+ ast_cli(a->fd, "Total number of objects cached: %d\n", ao2_container_count(cache->objects));
+
+ ao2_ref(cache, -1);
+
+ return CLI_SUCCESS;
+#undef FORMAT
+}
+
+/*!
+ * \internal
+ * \brief CLI tab completion for cached object names
+ */
+static char *sorcery_memory_cache_complete_object_name(const char *cache_name, const char *word, int state)
+{
+ struct sorcery_memory_cache *cache;
+ struct sorcery_memory_cached_object *cached;
+ struct ao2_iterator it_cached;
+ int wordlen = strlen(word);
+ int which = 0;
+ char *result = NULL;
+
+ cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
+ if (!cache) {
+ return NULL;
+ }
+
+ it_cached = ao2_iterator_init(cache->objects, 0);
+ while ((cached = ao2_iterator_next(&it_cached))) {
+ if (!strncasecmp(word, ast_sorcery_object_get_id(cached->object), wordlen)
+ && ++which > state) {
+ result = ast_strdup(ast_sorcery_object_get_id(cached->object));
+ }
+ ao2_ref(cached, -1);
+ if (result) {
+ break;
+ }
+ }
+ ao2_iterator_destroy(&it_cached);
+
+ ao2_ref(cache, -1);
+
+ return result;
+}
+
+/*!
+ * \internal
+ * \brief CLI command implementation for 'sorcery memory cache expire'
+ */
+static char *sorcery_memory_cache_expire(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct sorcery_memory_cache *cache;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sorcery memory cache expire";
+ e->usage =
+ "Usage: sorcery memory cache expire <cache name> [object name]\n"
+ " Expire a specific object or ALL objects within a sorcery memory cache.\n";
+ return NULL;
+ case CLI_GENERATE:
+ if (a->pos == 4) {
+ return sorcery_memory_cache_complete_name(a->word, a->n);
+ } else if (a->pos == 5) {
+ return sorcery_memory_cache_complete_object_name(a->argv[4], a->word, a->n);
+ } else {
+ return NULL;
+ }
+ }
+
+ if (a->argc > 6) {
+ return CLI_SHOWUSAGE;
+ }
+
+ cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
+ if (!cache) {
+ ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
+ return CLI_FAILURE;
+ }
+
+ ao2_wrlock(cache->objects);
+ if (a->argc == 5) {
+ remove_all_from_cache(cache);
+ ast_cli(a->fd, "All objects have been removed from cache '%s'\n", a->argv[4]);
+ } else {
+ if (!remove_from_cache(cache, a->argv[5], 1)) {
+ ast_cli(a->fd, "Successfully expired object '%s' from cache '%s'\n", a->argv[5], a->argv[4]);
+ } else {
+ ast_cli(a->fd, "Object '%s' was not expired from cache '%s' as it was not found\n", a->argv[5],
+ a->argv[4]);
+ }
+ }
+ ao2_unlock(cache->objects);
+
+ ao2_ref(cache, -1);
+
+ return CLI_SUCCESS;
+}
+
+/*!
+ * \internal
+ * \brief CLI command implementation for 'sorcery memory cache stale'
+ */
+static char *sorcery_memory_cache_stale(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct sorcery_memory_cache *cache;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sorcery memory cache stale";
+ e->usage =
+ "Usage: sorcery memory cache stale <cache name> [object name]\n"
+ " Mark a specific object or ALL objects as stale in a sorcery memory cache.\n";
+ return NULL;
+ case CLI_GENERATE:
+ if (a->pos == 4) {
+ return sorcery_memory_cache_complete_name(a->word, a->n);
+ } else if (a->pos == 5) {
+ return sorcery_memory_cache_complete_object_name(a->argv[4], a->word, a->n);
+ } else {
+ return NULL;
+ }
+ }
+
+ if (a->argc > 6) {
+ return CLI_SHOWUSAGE;
+ }
+
+ cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
+ if (!cache) {
+ ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
+ return CLI_FAILURE;
+ }
+
+ if (!cache->object_lifetime_stale) {
+ ast_cli(a->fd, "Specified sorcery memory cache '%s' does not have staleness enabled\n", a->argv[4]);
+ ao2_ref(cache, -1);
+ return CLI_FAILURE;
+ }
+
+ ao2_rdlock(cache->objects);
+ if (a->argc == 5) {
+ mark_all_as_stale_in_cache(cache);
+ ast_cli(a->fd, "Marked all objects in sorcery memory cache '%s' as stale\n", a->argv[4]);
+ } else {
+ if (!mark_object_as_stale_in_cache(cache, a->argv[5])) {
+ ast_cli(a->fd, "Successfully marked object '%s' in memory cache '%s' as stale\n",
+ a->argv[5], a->argv[4]);
+ } else {
+ ast_cli(a->fd, "Object '%s' in sorcery memory cache '%s' could not be marked as stale as it was not found\n",
+ a->argv[5], a->argv[4]);
+ }
+ }
+ ao2_unlock(cache->objects);
+
+ ao2_ref(cache, -1);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_memory_cache[] = {
+ AST_CLI_DEFINE(sorcery_memory_cache_show, "Show sorcery memory cache information"),
+ AST_CLI_DEFINE(sorcery_memory_cache_dump, "Dump all objects within a sorcery memory cache"),
+ AST_CLI_DEFINE(sorcery_memory_cache_expire, "Expire a specific object or ALL objects within a sorcery memory cache"),
+ AST_CLI_DEFINE(sorcery_memory_cache_stale, "Mark a specific object or ALL objects as stale within a sorcery memory cache"),
+};
+
+/*!
+ * \internal
+ * \brief AMI command implementation for 'SorceryMemoryCacheExpireObject'
+ */
+static int sorcery_memory_cache_ami_expire_object(struct mansession *s, const struct message *m)
+{
+ const char *cache_name = astman_get_header(m, "Cache");
+ const char *object_name = astman_get_header(m, "Object");
+ struct sorcery_memory_cache *cache;
+ int res;
+
+ if (ast_strlen_zero(cache_name)) {
+ astman_send_error(s, m, "SorceryMemoryCacheExpireObject requires that a cache name be provided.\n");
+ return 0;
+ } else if (ast_strlen_zero(object_name)) {
+ astman_send_error(s, m, "SorceryMemoryCacheExpireObject requires that an object name be provided\n");
+ return 0;
+ }
+
+ cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
+ if (!cache) {
+ astman_send_error(s, m, "The provided cache does not exist\n");
+ return 0;
+ }
+
+ ao2_wrlock(cache->objects);
+ res = remove_from_cache(cache, object_name, 1);
+ ao2_unlock(cache->objects);
+
+ ao2_ref(cache, -1);
+
+ if (!res) {
+ astman_send_ack(s, m, "The provided object was expired from the cache\n");
+ } else {
+ astman_send_error(s, m, "The provided object could not be expired from the cache\n");
+ }
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief AMI command implementation for 'SorceryMemoryCacheExpire'
+ */
+static int sorcery_memory_cache_ami_expire(struct mansession *s, const struct message *m)
+{
+ const char *cache_name = astman_get_header(m, "Cache");
+ struct sorcery_memory_cache *cache;
+
+ if (ast_strlen_zero(cache_name)) {
+ astman_send_error(s, m, "SorceryMemoryCacheExpire requires that a cache name be provided.\n");
+ return 0;
+ }
+
+ cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
+ if (!cache) {
+ astman_send_error(s, m, "The provided cache does not exist\n");
+ return 0;
+ }
+
+ ao2_wrlock(cache->objects);
+ remove_all_from_cache(cache);
+ ao2_unlock(cache->objects);
+
+ ao2_ref(cache, -1);
+
+ astman_send_ack(s, m, "All objects were expired from the cache\n");
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief AMI command implementation for 'SorceryMemoryCacheStaleObject'
+ */
+static int sorcery_memory_cache_ami_stale_object(struct mansession *s, const struct message *m)
+{
+ const char *cache_name = astman_get_header(m, "Cache");
+ const char *object_name = astman_get_header(m, "Object");
+ struct sorcery_memory_cache *cache;
+ int res;
+
+ if (ast_strlen_zero(cache_name)) {
+ astman_send_error(s, m, "SorceryMemoryCacheStaleObject requires that a cache name be provided.\n");
+ return 0;
+ } else if (ast_strlen_zero(object_name)) {
+ astman_send_error(s, m, "SorceryMemoryCacheStaleObject requires that an object name be provided\n");
+ return 0;
+ }
+
+ cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
+ if (!cache) {
+ astman_send_error(s, m, "The provided cache does not exist\n");
+ return 0;
+ }
+
+ ao2_rdlock(cache->objects);
+ res = mark_object_as_stale_in_cache(cache, object_name);
+ ao2_unlock(cache->objects);
+
+ ao2_ref(cache, -1);
+
+ if (!res) {
+ astman_send_ack(s, m, "The provided object was marked as stale in the cache\n");
+ } else {
+ astman_send_error(s, m, "The provided object could not be marked as stale in the cache\n");
+ }
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief AMI command implementation for 'SorceryMemoryCacheStale'
+ */
+static int sorcery_memory_cache_ami_stale(struct mansession *s, const struct message *m)
+{
+ const char *cache_name = astman_get_header(m, "Cache");
+ struct sorcery_memory_cache *cache;
+
+ if (ast_strlen_zero(cache_name)) {
+ astman_send_error(s, m, "SorceryMemoryCacheStale requires that a cache name be provided.\n");
+ return 0;
+ }
+
+ cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
+ if (!cache) {
+ astman_send_error(s, m, "The provided cache does not exist\n");
+ return 0;
+ }
+
+ ao2_rdlock(cache->objects);
+ mark_all_as_stale_in_cache(cache);
+ ao2_unlock(cache->objects);
+
+ ao2_ref(cache, -1);
+
+ astman_send_ack(s, m, "All objects were marked as stale in the cache\n");
+
+ return 0;
+}
+
+#ifdef TEST_FRAMEWORK
+
+/*! \brief Dummy sorcery object */
+struct test_sorcery_object {
+ SORCERY_OBJECT(details);
+};
+
+/*!
+ * \internal
+ * \brief Allocator for test object
+ *
+ * \param id The identifier for the object
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static void *test_sorcery_object_alloc(const char *id)
+{
+ return ast_sorcery_generic_alloc(sizeof(struct test_sorcery_object), NULL);
+}
+
+/*!
+ * \internal
+ * \brief Allocator for test sorcery instance
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static struct ast_sorcery *alloc_and_initialize_sorcery(void)
+{
+ struct ast_sorcery *sorcery;
+
+ if (!(sorcery = ast_sorcery_open())) {
+ return NULL;
+ }
+
+ if ((ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
+ ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
+ ast_sorcery_unref(sorcery);
+ return NULL;
+ }
+
+ return sorcery;
+}
+
+AST_TEST_DEFINE(open_with_valid_options)
+{
+ int res = AST_TEST_PASS;
+ struct sorcery_memory_cache *cache;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "open_with_valid_options";
+ info->category = "/res/res_sorcery_memory_cache/";
+ info->summary = "Attempt to create sorcery memory caches using valid options";
+ info->description = "This test performs the following:\n"
+ "\t* Creates a memory cache with default configuration\n"
+ "\t* Creates a memory cache with a maximum object count of 10 and verifies it\n"
+ "\t* Creates a memory cache with a maximum object lifetime of 60 and verifies it\n"
+ "\t* Creates a memory cache with a stale object lifetime of 90 and verifies it\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ cache = sorcery_memory_cache_open("");
+ if (!cache) {
+ ast_test_status_update(test, "Failed to create a sorcery memory cache using default configuration\n");
+ res = AST_TEST_FAIL;
+ } else {
+ sorcery_memory_cache_close(cache);
+ }
+
+ cache = sorcery_memory_cache_open("maximum_objects=10");
+ if (!cache) {
+ ast_test_status_update(test, "Failed to create a sorcery memory cache with a maximum object count of 10\n");
+ res = AST_TEST_FAIL;
+ } else {
+ if (cache->maximum_objects != 10) {
+ ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of 10 but it has '%u'\n",
+ cache->maximum_objects);
+ }
+ sorcery_memory_cache_close(cache);
+ }
+
+ cache = sorcery_memory_cache_open("object_lifetime_maximum=60");
+ if (!cache) {
+ ast_test_status_update(test, "Failed to create a sorcery memory cache with a maximum object lifetime of 60\n");
+ res = AST_TEST_FAIL;
+ } else {
+ if (cache->object_lifetime_maximum != 60) {
+ ast_test_status_update(test, "Created a sorcery memory cache with a maximum object lifetime of 60 but it has '%u'\n",
+ cache->object_lifetime_maximum);
+ }
+ sorcery_memory_cache_close(cache);
+ }
+
+ cache = sorcery_memory_cache_open("object_lifetime_stale=90");
+ if (!cache) {
+ ast_test_status_update(test, "Failed to create a sorcery memory cache with a stale object lifetime of 90\n");
+ res = AST_TEST_FAIL;
+ } else {
+ if (cache->object_lifetime_stale != 90) {
+ ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of 90 but it has '%u'\n",
+ cache->object_lifetime_stale);
+ }
+ sorcery_memory_cache_close(cache);
+ }
+
+
+ return res;
+}
+
+AST_TEST_DEFINE(open_with_invalid_options)
+{
+ int res = AST_TEST_PASS;
+ struct sorcery_memory_cache *cache;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "open_with_invalid_options";
+ info->category = "/res/res_sorcery_memory_cache/";
+ info->summary = "Attempt to create sorcery memory caches using invalid options";
+ info->description = "This test attempts to perform the following:\n"
+ "\t* Create a memory cache with an empty name\n"
+ "\t* Create a memory cache with a maximum object count of -1\n"
+ "\t* Create a memory cache with a maximum object count of toast\n"
+ "\t* Create a memory cache with a maximum object lifetime of -1\n"
+ "\t* Create a memory cache with a maximum object lifetime of toast\n"
+ "\t* Create a memory cache with a stale object lifetime of -1\n"
+ "\t* Create a memory cache with a stale object lifetime of toast\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ cache = sorcery_memory_cache_open("name=");
+ if (cache) {
+ ast_test_status_update(test, "Created a sorcery memory cache with an empty name\n");
+ sorcery_memory_cache_close(cache);
+ res = AST_TEST_FAIL;
+ }
+
+ cache = sorcery_memory_cache_open("maximum_objects=-1");
+ if (cache) {
+ ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of -1\n");
+ sorcery_memory_cache_close(cache);
+ res = AST_TEST_FAIL;
+ }
+
+ cache = sorcery_memory_cache_open("maximum_objects=toast");
+ if (cache) {
+ ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of toast\n");
+ sorcery_memory_cache_close(cache);
+ res = AST_TEST_FAIL;
+ }
+
+ cache = sorcery_memory_cache_open("object_lifetime_maximum=-1");
+ if (cache) {
+ ast_test_status_update(test, "Created a sorcery memory cache with an object lifetime maximum of -1\n");
+ sorcery_memory_cache_close(cache);
+ res = AST_TEST_FAIL;
+ }
+
+ cache = sorcery_memory_cache_open("object_lifetime_maximum=toast");
+ if (cache) {
+ ast_test_status_update(test, "Created a sorcery memory cache with an object lifetime maximum of toast\n");
+ sorcery_memory_cache_close(cache);
+ res = AST_TEST_FAIL;
+ }
+
+ cache = sorcery_memory_cache_open("object_lifetime_stale=-1");
+ if (cache) {
+ ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of -1\n");
+ sorcery_memory_cache_close(cache);
+ res = AST_TEST_FAIL;
+ }
+
+ cache = sorcery_memory_cache_open("object_lifetime_stale=toast");
+ if (cache) {
+ ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of toast\n");
+ sorcery_memory_cache_close(cache);
+ res = AST_TEST_FAIL;
+ }
+
+ cache = sorcery_memory_cache_open("tacos");
+ if (cache) {
+ ast_test_status_update(test, "Created a sorcery memory cache with an invalid configuration option 'tacos'\n");
+ sorcery_memory_cache_close(cache);
+ res = AST_TEST_FAIL;
+ }
+
+ return res;
+}
+
+AST_TEST_DEFINE(create_and_retrieve)
+{
+ int res = AST_TEST_FAIL;
+ struct ast_sorcery *sorcery = NULL;
+ struct sorcery_memory_cache *cache = NULL;
+ RAII_VAR(void *, object, NULL, ao2_cleanup);
+ RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "create";
+ info->category = "/res/res_sorcery_memory_cache/";
+ info->summary = "Attempt to create an object in the cache";
+ info->description = "This test performs the following:\n"
+ "\t* Creates a memory cache with default options\n"
+ "\t* Creates a sorcery instance with a test object\n"
+ "\t* Creates a test object with an id of test\n"
+ "\t* Pushes the test object into the memory cache\n"
+ "\t* Confirms that the test object is in the cache\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ cache = sorcery_memory_cache_open("");
+ if (!cache) {
+ ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
+ goto cleanup;
+ }
+
+ if (ao2_container_count(cache->objects)) {
+ ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
+ goto cleanup;
+ }
+
+ sorcery = alloc_and_initialize_sorcery();
+ if (!sorcery) {
+ ast_test_status_update(test, "Failed to create a test sorcery instance\n");
+ goto cleanup;
+ }
+
+ object = ast_sorcery_alloc(sorcery, "test", "test");
+ if (!object) {
+ ast_test_status_update(test, "Failed to allocate a test object\n");
+ goto cleanup;
+ }
+
+ sorcery_memory_cache_create(sorcery, cache, object);
+
+ if (!ao2_container_count(cache->objects)) {
+ ast_test_status_update(test, "Added test object to memory cache but cache remains empty\n");
+ goto cleanup;
+ }
+
+ cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
+ if (!cached_object) {
+ ast_test_status_update(test, "Object placed into memory cache could not be retrieved\n");
+ goto cleanup;
+ }
+
+ if (cached_object != object) {
+ ast_test_status_update(test, "Object retrieved from memory cached is not the one we cached\n");
+ goto cleanup;
+ }
+
+ res = AST_TEST_PASS;
+
+cleanup:
+ if (cache) {
+ sorcery_memory_cache_close(cache);
+ }
+ if (sorcery) {
+ ast_sorcery_unref(sorcery);
+ }
+
+ return res;
+}
+
+AST_TEST_DEFINE(update)
+{
+ int res = AST_TEST_FAIL;
+ struct ast_sorcery *sorcery = NULL;
+ struct sorcery_memory_cache *cache = NULL;
+ RAII_VAR(void *, original_object, NULL, ao2_cleanup);
+ RAII_VAR(void *, updated_object, NULL, ao2_cleanup);
+ RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "create";
+ info->category = "/res/res_sorcery_memory_cache/";
+ info->summary = "Attempt to create and then update an object in the cache";
+ info->description = "This test performs the following:\n"
+ "\t* Creates a memory cache with default options\n"
+ "\t* Creates a sorcery instance with a test object\n"
+ "\t* Creates a test object with an id of test\n"
+ "\t* Pushes the test object into the memory cache\n"
+ "\t* Confirms that the test object is in the cache\n"
+ "\t* Creates a new test object with the same id of test\n"
+ "\t* Pushes the new test object into the memory cache\n"
+ "\t* Confirms that the new test object has replaced the old one\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ cache = sorcery_memory_cache_open("");
+ if (!cache) {
+ ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
+ goto cleanup;
+ }
+
+ if (ao2_container_count(cache->objects)) {
+ ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
+ goto cleanup;
+ }
+
+ sorcery = alloc_and_initialize_sorcery();
+ if (!sorcery) {
+ ast_test_status_update(test, "Failed to create a test sorcery instance\n");
+ goto cleanup;
+ }
+
+ original_object = ast_sorcery_alloc(sorcery, "test", "test");
+ if (!original_object) {
+ ast_test_status_update(test, "Failed to allocate a test object\n");
+ goto cleanup;
+ }
+
+ sorcery_memory_cache_create(sorcery, cache, original_object);
+
+ updated_object = ast_sorcery_alloc(sorcery, "test", "test");
+ if (!updated_object) {
+ ast_test_status_update(test, "Failed to allocate an updated test object\n");
+ goto cleanup;
+ }
+
+ sorcery_memory_cache_create(sorcery, cache, updated_object);
+
+ if (ao2_container_count(cache->objects) != 1) {
+ ast_test_status_update(test, "Added updated test object to memory cache but cache now contains %d objects instead of 1\n",
+ ao2_container_count(cache->objects));
+ goto cleanup;
+ }
+
+ cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
+ if (!cached_object) {
+ ast_test_status_update(test, "Updated object placed into memory cache could not be retrieved\n");
+ goto cleanup;
+ }
+
+ if (cached_object == original_object) {
+ ast_test_status_update(test, "Updated object placed into memory cache but old one is being retrieved\n");
+ goto cleanup;
+ } else if (cached_object != updated_object) {
+ ast_test_status_update(test, "Updated object placed into memory cache but different one is being retrieved\n");
+ goto cleanup;
+ }
+
+ res = AST_TEST_PASS;
+
+cleanup:
+ if (cache) {
+ sorcery_memory_cache_close(cache);
+ }
+ if (sorcery) {
+ ast_sorcery_unref(sorcery);
+ }
+
+ return res;
+}
+
+AST_TEST_DEFINE(delete)
+{
+ int res = AST_TEST_FAIL;
+ struct ast_sorcery *sorcery = NULL;
+ struct sorcery_memory_cache *cache = NULL;
+ RAII_VAR(void *, object, NULL, ao2_cleanup);
+ RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "delete";
+ info->category = "/res/res_sorcery_memory_cache/";
+ info->summary = "Attempt to create and then delete an object in the cache";
+ info->description = "This test performs the following:\n"
+ "\t* Creates a memory cache with default options\n"
+ "\t* Creates a sorcery instance with a test object\n"
+ "\t* Creates a test object with an id of test\n"
+ "\t* Pushes the test object into the memory cache\n"
+ "\t* Confirms that the test object is in the cache\n"
+ "\t* Deletes the test object from the cache\n"
+ "\t* Confirms that the test object is no longer in the cache\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ cache = sorcery_memory_cache_open("");
+ if (!cache) {
+ ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
+ goto cleanup;
+ }
+
+ if (ao2_container_count(cache->objects)) {
+ ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
+ goto cleanup;
+ }
+
+ sorcery = alloc_and_initialize_sorcery();
+ if (!sorcery) {
+ ast_test_status_update(test, "Failed to create a test sorcery instance\n");
+ goto cleanup;
+ }
+
+ object = ast_sorcery_alloc(sorcery, "test", "test");
+ if (!object) {
+ ast_test_status_update(test, "Failed to allocate a test object\n");
+ goto cleanup;
+ }
+
+ sorcery_memory_cache_create(sorcery, cache, object);
+
+ if (!ao2_container_count(cache->objects)) {
+ ast_test_status_update(test, "Added test object to memory cache but cache contains no objects\n");
+ goto cleanup;
+ }
+
+ cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
+ if (!cached_object) {
+ ast_test_status_update(test, "Test object placed into memory cache could not be retrieved\n");
+ goto cleanup;
+ }
+
+ ao2_ref(cached_object, -1);
+ cached_object = NULL;
+
+ sorcery_memory_cache_delete(sorcery, cache, object);
+
+ cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
+ if (cached_object) {
+ ast_test_status_update(test, "Test object deleted from memory cache can still be retrieved\n");
+ goto cleanup;
+ }
+
+ res = AST_TEST_PASS;
+
+cleanup:
+ if (cache) {
+ sorcery_memory_cache_close(cache);
+ }
+ if (sorcery) {
+ ast_sorcery_unref(sorcery);
+ }
+
+ return res;
+}
+
+static int check_cache_content(struct ast_test *test, struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache,
+ const char **in_cache, size_t num_in_cache, const char **not_in_cache, size_t num_not_in_cache)
+{
+ int i;
+ int res = 0;
+ RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
+
+ for (i = 0; i < num_in_cache; ++i) {
+ cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", in_cache[i]);
+ if (!cached_object) {
+ ast_test_status_update(test, "Failed to retrieve '%s' object from the cache\n",
+ in_cache[i]);
+ res = -1;
+ }
+ ao2_ref(cached_object, -1);
+ }
+
+ for (i = 0; i < num_not_in_cache; ++i) {
+ cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", not_in_cache[i]);
+ if (cached_object) {
+ ast_test_status_update(test, "Retrieved '%s' object from the cache unexpectedly\n",
+ not_in_cache[i]);
+ ao2_ref(cached_object, -1);
+ res = -1;
+ }
+ }
+
+ return res;
+}
+
+AST_TEST_DEFINE(maximum_objects)
+{
+ int res = AST_TEST_FAIL;
+ struct ast_sorcery *sorcery = NULL;
+ struct sorcery_memory_cache *cache = NULL;
+ RAII_VAR(void *, alice, NULL, ao2_cleanup);
+ RAII_VAR(void *, bob, NULL, ao2_cleanup);
+ RAII_VAR(void *, charlie, NULL, ao2_cleanup);
+ RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
+ const char *in_cache[2];
+ const char *not_in_cache[2];
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "maximum_objects";
+ info->category = "/res/res_sorcery_memory_cache/";
+ info->summary = "Ensure that the 'maximum_objects' option works as expected";
+ info->description = "This test performs the following:\n"
+ "\t* Creates a memory cache with maximum_objects=2\n"
+ "\t* Creates a sorcery instance\n"
+ "\t* Creates a three test objects: alice, bob, charlie, and david\n"
+ "\t* Pushes alice and bob into the memory cache\n"
+ "\t* Confirms that alice and bob are in the memory cache\n"
+ "\t* Pushes charlie into the memory cache\n"
+ "\t* Confirms that bob and charlie are in the memory cache\n"
+ "\t* Deletes charlie from the memory cache\n"
+ "\t* Confirms that only bob is in the memory cache\n"
+ "\t* Pushes alice into the memory cache\n"
+ "\t* Confirms that bob and alice are in the memory cache\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ cache = sorcery_memory_cache_open("maximum_objects=2");
+ if (!cache) {
+ ast_test_status_update(test, "Failed to create a sorcery memory cache with maximum_objects=2\n");
+ goto cleanup;
+ }
+
+ if (ao2_container_count(cache->objects)) {
+ ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
+ goto cleanup;
+ }
+
+ sorcery = alloc_and_initialize_sorcery();
+ if (!sorcery) {
+ ast_test_status_update(test, "Failed to create a test sorcery instance\n");
+ goto cleanup;
+ }
+
+ alice = ast_sorcery_alloc(sorcery, "test", "alice");
+ bob = ast_sorcery_alloc(sorcery, "test", "bob");
+ charlie = ast_sorcery_alloc(sorcery, "test", "charlie");
+
+ if (!alice || !bob || !charlie) {
+ ast_test_status_update(test, "Failed to allocate sorcery object(s)\n");
+ goto cleanup;
+ }
+
+ sorcery_memory_cache_create(sorcery, cache, alice);
+ in_cache[0] = "alice";
+ in_cache[1] = NULL;
+ not_in_cache[0] = "bob";
+ not_in_cache[1] = "charlie";
+ if (check_cache_content(test, sorcery, cache, in_cache, 1, not_in_cache, 2)) {
+ goto cleanup;
+ }
+
+ /* Delays are added to ensure that we are not adding cache entries within the
+ * same microsecond
+ */
+ usleep(1000);
+
+ sorcery_memory_cache_create(sorcery, cache, bob);
+ in_cache[0] = "alice";
+ in_cache[1] = "bob";
+ not_in_cache[0] = "charlie";
+ not_in_cache[1] = NULL;
+ if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
+ goto cleanup;
+ }
+
+ usleep(1000);
+
+ sorcery_memory_cache_create(sorcery, cache, charlie);
+ in_cache[0] = "bob";
+ in_cache[1] = "charlie";
+ not_in_cache[0] = "alice";
+ not_in_cache[1] = NULL;
+ if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
+ goto cleanup;
+ }
+ usleep(1000);
+
+ sorcery_memory_cache_delete(sorcery, cache, charlie);
+ in_cache[0] = "bob";
+ in_cache[1] = NULL;
+ not_in_cache[0] = "alice";
+ not_in_cache[1] = "charlie";
+ if (check_cache_content(test, sorcery, cache, in_cache, 1, not_in_cache, 2)) {
+ goto cleanup;
+ }
+ usleep(1000);
+
+ sorcery_memory_cache_create(sorcery, cache, alice);
+ in_cache[0] = "bob";
+ in_cache[1] = "alice";
+ not_in_cache[0] = "charlie";
+ not_in_cache[1] = NULL;
+ if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
+ goto cleanup;
+ }
+
+ res = AST_TEST_PASS;
+
+cleanup:
+ if (cache) {
+ sorcery_memory_cache_close(cache);
+ }
+ if (sorcery) {
+ ast_sorcery_unref(sorcery);
+ }
+
+ return res;
+}
+
+AST_TEST_DEFINE(expiration)
+{
+ int res = AST_TEST_FAIL;
+ struct ast_sorcery *sorcery = NULL;
+ struct sorcery_memory_cache *cache = NULL;
+ int i;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "expiration";
+ info->category = "/res/res_sorcery_memory_cache/";
+ info->summary = "Add objects to a cache configured with maximum lifetime, confirm they are removed";
+ info->description = "This test performs the following:\n"
+ "\t* Creates a memory cache with a maximum object lifetime of 5 seconds\n"
+ "\t* Pushes 10 objects into the memory cache\n"
+ "\t* Waits (up to) 10 seconds for expiration to occur\n"
+ "\t* Confirms that the objects have been removed from the cache\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ cache = sorcery_memory_cache_open("object_lifetime_maximum=5");
+ if (!cache) {
+ ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
+ goto cleanup;
+ }
+
+ sorcery = alloc_and_initialize_sorcery();
+ if (!sorcery) {
+ ast_test_status_update(test, "Failed to create a test sorcery instance\n");
+ goto cleanup;
+ }
+
+ cache->cache_notify = 1;
+ ast_mutex_init(&cache->lock);
+ ast_cond_init(&cache->cond, NULL);
+
+ for (i = 0; i < 5; ++i) {
+ char uuid[AST_UUID_STR_LEN];
+ void *object;
+
+ object = ast_sorcery_alloc(sorcery, "test", ast_uuid_generate_str(uuid, sizeof(uuid)));
+ if (!object) {
+ ast_test_status_update(test, "Failed to allocate test object for expiration\n");
+ goto cleanup;
+ }
+
+ sorcery_memory_cache_create(sorcery, cache, object);
+
+ ao2_ref(object, -1);
+ }
+
+ ast_mutex_lock(&cache->lock);
+ while (!cache->cache_completed) {
+ struct timeval start = ast_tvnow();
+ struct timespec end = {
+ .tv_sec = start.tv_sec + 10,
+ .tv_nsec = start.tv_usec * 1000,
+ };
+
+ if (ast_cond_timedwait(&cache->cond, &cache->lock, &end) == ETIMEDOUT) {
+ break;
+ }
+ }
+ ast_mutex_unlock(&cache->lock);
+
+ if (ao2_container_count(cache->objects)) {
+ ast_test_status_update(test, "Objects placed into the memory cache did not expire and get removed\n");
+ goto cleanup;
+ }
+
+ res = AST_TEST_PASS;
+
+cleanup:
+ if (cache) {
+ if (cache->cache_notify) {
+ ast_cond_destroy(&cache->cond);
+ ast_mutex_destroy(&cache->lock);
+ }
+ sorcery_memory_cache_close(cache);
+ }
+ if (sorcery) {
+ ast_sorcery_unref(sorcery);
+ }
+
+ return res;
+}
+
+/*!
+ * \brief Backend data that the mock sorcery wizard uses to create objects
+ */
+static struct backend_data {
+ /*! An arbitrary data field */
+ int salt;
+ /*! Another arbitrary data field */
+ int pepper;
+ /*! Indicates whether the backend has data */
+ int exists;
+} *real_backend_data;
+
+/*!
+ * \brief Sorcery object created based on backend data
+ */
+struct test_data {
+ SORCERY_OBJECT(details);
+ /*! Mirrors the backend data's salt field */
+ int salt;
+ /*! Mirrors the backend data's pepper field */
+ int pepper;
+};
+
+/*!
+ * \brief Allocation callback for test_data sorcery object
+ */
+static void *test_data_alloc(const char *id) {
+ return ast_sorcery_generic_alloc(sizeof(struct test_data), NULL);
+}
+
+/*!
+ * \brief Callback for retrieving sorcery object by ID
+ *
+ * The mock wizard uses the \ref real_backend_data in order to construct
+ * objects. If the backend data is "nonexisent" then no object is returned.
+ * Otherwise, an object is created that has the backend data's salt and
+ * pepper values copied.
+ *
+ * \param sorcery The sorcery instance
+ * \param data Unused
+ * \param type The object type. Will always be "test".
+ * \param id The object id. Will always be "test".
+ *
+ * \retval NULL Backend data does not exist
+ * \retval non-NULL An object representing the backend data
+ */
+static void *mock_retrieve_id(const struct ast_sorcery *sorcery, void *data,
+ const char *type, const char *id)
+{
+ struct test_data *b_data;
+
+ if (!real_backend_data->exists) {
+ return NULL;
+ }
+
+ b_data = ast_sorcery_alloc(sorcery, type, id);
+ if (!b_data) {
+ return NULL;
+ }
+
+ b_data->salt = real_backend_data->salt;
+ b_data->pepper = real_backend_data->pepper;
+ return b_data;
+}
+
+/*!
+ * \brief A mock sorcery wizard used for the stale test
+ */
+static struct ast_sorcery_wizard mock_wizard = {
+ .name = "mock",
+ .retrieve_id = mock_retrieve_id,
+};
+
+/*!
+ * \brief Wait for the cache to be updated after a stale object is retrieved.
+ *
+ * Since the cache does not know what type of objects it is dealing with, and
+ * since we do not have the internals of the cache, the only way to make this
+ * determination is to continuously retrieve an object from the cache until
+ * we retrieve a different object than we had previously retrieved.
+ *
+ * \param sorcery The sorcery instance
+ * \param previous_object The object we had previously retrieved from the cache
+ * \param[out] new_object The new object we retrieve from the cache
+ *
+ * \retval 0 Successfully retrieved a new object from the cache
+ * \retval non-zero Failed to retrieve a new object from the cache
+ */
+static int wait_for_cache_update(const struct ast_sorcery *sorcery,
+ void *previous_object, struct test_data **new_object)
+{
+ struct timeval start = ast_tvnow();
+
+ while (ast_remaining_ms(start, 5000) > 0) {
+ void *object;
+
+ object = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
+ if (object != previous_object) {
+ *new_object = object;
+ return 0;
+ }
+ ao2_cleanup(object);
+ }
+
+ return -1;
+}
+
+AST_TEST_DEFINE(stale)
+{
+ int res = AST_TEST_FAIL;
+ struct ast_sorcery *sorcery = NULL;
+ struct test_data *backend_object;
+ struct backend_data iterations[] = {
+ { .salt = 1, .pepper = 2, .exists = 1 },
+ { .salt = 568729, .pepper = -234123, .exists = 1 },
+ { .salt = 0, .pepper = 0, .exists = 0 },
+ };
+ struct backend_data initial = {
+ .salt = 0,
+ .pepper = 0,
+ .exists = 1,
+ };
+ int i;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "stale";
+ info->category = "/res/res_sorcery_memory_cache/";
+ info->summary = "Ensure that stale objects are replaced with updated objects";
+ info->description = "This test performs the following:\n"
+ "\t* Create a sorcery instance with two wizards"
+ "\t\t* The first is a memory cache that marks items stale after 3 seconds\n"
+ "\t\t* The second is a mock of a back-end\n"
+ "\t* Pre-populates the cache by retrieving some initial data from the backend.\n"
+ "\t* Performs iterations of the following:\n"
+ "\t\t* Update backend data with new values\n"
+ "\t\t* Retrieve item from the cache\n"
+ "\t\t* Ensure the retrieved item does not have the new backend values\n"
+ "\t\t* Wait for cached object to become stale\n"
+ "\t\t* Retrieve the stale cached object\n"
+ "\t\t* Ensure that the stale object retrieved is the same as the fresh one from earlier\n"
+ "\t\t* Wait for the cache to update with new data\n"
+ "\t\t* Ensure that new data in the cache matches backend data\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ ast_sorcery_wizard_register(&mock_wizard);
+
+ sorcery = ast_sorcery_open();
+ if (!sorcery) {
+ ast_test_status_update(test, "Failed to create sorcery instance\n");
+ goto cleanup;
+ }
+
+ ast_sorcery_apply_wizard_mapping(sorcery, "test", "memory_cache",
+ "object_lifetime_stale=3", 1);
+ ast_sorcery_apply_wizard_mapping(sorcery, "test", "mock", NULL, 0);
+ ast_sorcery_internal_object_register(sorcery, "test", test_data_alloc, NULL, NULL);
+
+ /* Prepopulate the cache */
+ real_backend_data = &initial;
+
+ backend_object = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
+ if (!backend_object) {
+ ast_test_status_update(test, "Unable to retrieve backend data and populate the cache\n");
+ goto cleanup;
+ }
+ ao2_ref(backend_object, -1);
+
+ for (i = 0; i < ARRAY_LEN(iterations); ++i) {
+ RAII_VAR(struct test_data *, cache_fresh, NULL, ao2_cleanup);
+ RAII_VAR(struct test_data *, cache_stale, NULL, ao2_cleanup);
+ RAII_VAR(struct test_data *, cache_new, NULL, ao2_cleanup);
+
+ real_backend_data = &iterations[i];
+
+ ast_test_status_update(test, "Begininning iteration %d\n", i);
+
+ cache_fresh = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
+ if (!cache_fresh) {
+ ast_test_status_update(test, "Unable to retrieve fresh cached object\n");
+ goto cleanup;
+ }
+
+ if (cache_fresh->salt == iterations[i].salt || cache_fresh->pepper == iterations[i].pepper) {
+ ast_test_status_update(test, "Fresh cached object has unexpected values. Did we hit the backend?\n");
+ goto cleanup;
+ }
+
+ sleep(5);
+
+ cache_stale = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
+ if (!cache_stale) {
+ ast_test_status_update(test, "Unable to retrieve stale cached object\n");
+ goto cleanup;
+ }
+
+ if (cache_stale != cache_fresh) {
+ ast_test_status_update(test, "Stale cache hit retrieved different object than fresh cache hit\n");
+ goto cleanup;
+ }
+
+ if (wait_for_cache_update(sorcery, cache_stale, &cache_new)) {
+ ast_test_status_update(test, "Cache was not updated\n");
+ goto cleanup;
+ }
+
+ if (iterations[i].exists) {
+ if (!cache_new) {
+ ast_test_status_update(test, "Failed to retrieve item from cache when there should be one present\n");
+ goto cleanup;
+ } else if (cache_new->salt != iterations[i].salt ||
+ cache_new->pepper != iterations[i].pepper) {
+ ast_test_status_update(test, "New cached item has unexpected values\n");
+ goto cleanup;
+ }
+ } else if (cache_new) {
+ ast_test_status_update(test, "Retrieved a cached item when there should not have been one present\n");
+ goto cleanup;
+ }
+ }
+
+ res = AST_TEST_PASS;
+
+cleanup:
+ if (sorcery) {
+ ast_sorcery_unref(sorcery);
+ }
+ ast_sorcery_wizard_unregister(&mock_wizard);
+ return res;
+}
+
+#endif
+
+static int unload_module(void)
+{
+ if (sched) {
+ ast_sched_context_destroy(sched);
+ sched = NULL;
+ }
+
+ ao2_cleanup(caches);
+
+ ast_sorcery_wizard_unregister(&memory_cache_object_wizard);
+
+ ast_cli_unregister_multiple(cli_memory_cache, ARRAY_LEN(cli_memory_cache));
+
+ ast_manager_unregister("SorceryMemoryCacheExpireObject");
+ ast_manager_unregister("SorceryMemoryCacheExpire");
+ ast_manager_unregister("SorceryMemoryCacheStaleObject");
+ ast_manager_unregister("SorceryMemoryCacheStale");
+
+ AST_TEST_UNREGISTER(open_with_valid_options);
+ AST_TEST_UNREGISTER(open_with_invalid_options);
+ AST_TEST_UNREGISTER(create_and_retrieve);
+ AST_TEST_UNREGISTER(update);
+ AST_TEST_UNREGISTER(delete);
+ AST_TEST_UNREGISTER(maximum_objects);
+ AST_TEST_UNREGISTER(expiration);
+ AST_TEST_UNREGISTER(stale);
+
+ return 0;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ sched = ast_sched_context_create();
+ if (!sched) {
+ ast_log(LOG_ERROR, "Failed to create scheduler for cache management\n");
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (ast_sched_start_thread(sched)) {
+ ast_log(LOG_ERROR, "Failed to create scheduler thread for cache management\n");
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ caches = ao2_container_alloc(CACHES_CONTAINER_BUCKET_SIZE, sorcery_memory_cache_hash,
+ sorcery_memory_cache_cmp);
+ if (!caches) {
+ ast_log(LOG_ERROR, "Failed to create container for configured caches\n");
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (ast_sorcery_wizard_register(&memory_cache_object_wizard)) {
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ res = ast_cli_register_multiple(cli_memory_cache, ARRAY_LEN(cli_memory_cache));
+ res |= ast_manager_register_xml("SorceryMemoryCacheExpireObject", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_expire_object);
+ res |= ast_manager_register_xml("SorceryMemoryCacheExpire", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_expire);
+ res |= ast_manager_register_xml("SorceryMemoryCacheStaleObject", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_stale_object);
+ res |= ast_manager_register_xml("SorceryMemoryCacheStale", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_stale);
+
+ if (res) {
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ AST_TEST_REGISTER(open_with_valid_options);
+ AST_TEST_REGISTER(open_with_invalid_options);
+ AST_TEST_REGISTER(create_and_retrieve);
+ AST_TEST_REGISTER(update);
+ AST_TEST_REGISTER(delete);
+ AST_TEST_REGISTER(maximum_objects);
+ AST_TEST_REGISTER(expiration);
+ AST_TEST_REGISTER(stale);
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Sorcery Memory Cache Object Wizard",
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_REALTIME_DRIVER,
+);
diff --git a/res/res_speech.c b/res/res_speech.c
index 39c7d5a9a..1505ca428 100644
--- a/res/res_speech.c
+++ b/res/res_speech.c
@@ -362,8 +362,8 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Generic Speech Recognition API",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_APP_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_stasis.c b/res/res_stasis.c
index 0fa51d8cd..631af1e2e 100644
--- a/res/res_stasis.c
+++ b/res/res_stasis.c
@@ -2061,4 +2061,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis applicatio
.support_level = AST_MODULE_SUPPORT_CORE,
.load = load_module,
.unload = unload_module,
- );
+);
diff --git a/res/res_stasis_answer.c b/res/res_stasis_answer.c
index 8ced30b87..830de8015 100644
--- a/res/res_stasis_answer.c
+++ b/res/res_stasis_answer.c
@@ -75,4 +75,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis applicatio
.support_level = AST_MODULE_SUPPORT_CORE,
.load = load_module,
.unload = unload_module,
- .nonoptreq = "res_stasis");
+ .nonoptreq = "res_stasis"
+);
diff --git a/res/res_stasis_device_state.c b/res/res_stasis_device_state.c
index cbebce8b2..7f7c51364 100644
--- a/res/res_stasis_device_state.c
+++ b/res/res_stasis_device_state.c
@@ -105,7 +105,7 @@ static int device_state_subscriptions_cmp(void *obj, void *arg, int flags)
static void device_state_subscription_destroy(void *obj)
{
struct device_state_subscription *sub = obj;
- sub->sub = stasis_unsubscribe(sub->sub);
+ sub->sub = stasis_unsubscribe_and_join(sub->sub);
ast_string_field_free_memory(sub);
}
@@ -414,4 +414,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis applicatio
.support_level = AST_MODULE_SUPPORT_CORE,
.load = load_module,
.unload = unload_module,
- .nonoptreq = "res_stasis");
+ .nonoptreq = "res_stasis"
+);
diff --git a/res/res_stasis_mailbox.c b/res/res_stasis_mailbox.c
index 30765329d..e34cb2058 100644
--- a/res/res_stasis_mailbox.c
+++ b/res/res_stasis_mailbox.c
@@ -163,4 +163,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis applicatio
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_stasis,res_mwi_external"
- );
+);
diff --git a/res/res_stasis_playback.c b/res/res_stasis_playback.c
index 7aac06d66..9abfe3d17 100644
--- a/res/res_stasis_playback.c
+++ b/res/res_stasis_playback.c
@@ -685,4 +685,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis applicatio
.support_level = AST_MODULE_SUPPORT_CORE,
.load = load_module,
.unload = unload_module,
- .nonoptreq = "res_stasis,res_stasis_recording");
+ .nonoptreq = "res_stasis,res_stasis_recording"
+);
diff --git a/res/res_stasis_recording.c b/res/res_stasis_recording.c
index 3a10d41e0..02ad875a8 100644
--- a/res/res_stasis_recording.c
+++ b/res/res_stasis_recording.c
@@ -657,4 +657,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_stasis",
- .load_pri = AST_MODPRI_APP_DEPEND);
+ .load_pri = AST_MODPRI_APP_DEPEND
+);
diff --git a/res/res_stasis_snoop.c b/res/res_stasis_snoop.c
index 68d76a420..e5b5bf604 100644
--- a/res/res_stasis_snoop.c
+++ b/res/res_stasis_snoop.c
@@ -427,4 +427,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis applicatio
.support_level = AST_MODULE_SUPPORT_CORE,
.load = load_module,
.unload = unload_module,
- .nonoptreq = "res_stasis");
+ .nonoptreq = "res_stasis"
+);
diff --git a/res/res_stasis_test.c b/res/res_stasis_test.c
index 12b9cc5ce..efdbc4b52 100644
--- a/res/res_stasis_test.c
+++ b/res/res_stasis_test.c
@@ -17,7 +17,8 @@
*/
/*!
- * \file \brief Test infrastructure for dealing with Stasis.
+ * \file
+ * \brief Test infrastructure for dealing with Stasis.
*
* \author David M. Lee, II <dlee@digium.com>
*/
@@ -284,4 +285,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_APP_DEPEND,
- );
+);
diff --git a/res/res_statsd.c b/res/res_statsd.c
index 012333524..fefe395e8 100644
--- a/res/res_statsd.c
+++ b/res/res_statsd.c
@@ -322,4 +322,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_
.unload = unload_module,
.reload = reload_module,
.load_pri = 0,
- );
+);
diff --git a/res/res_stun_monitor.c b/res/res_stun_monitor.c
index 34558c755..e1bd85b4f 100644
--- a/res/res_stun_monitor.c
+++ b/res/res_stun_monitor.c
@@ -476,9 +476,9 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "STUN Network Monitor",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND
+);
diff --git a/res/res_timing_dahdi.c b/res/res_timing_dahdi.c
index 4f96959c1..23fb8e358 100644
--- a/res/res_timing_dahdi.c
+++ b/res/res_timing_dahdi.c
@@ -235,8 +235,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "DAHDI Timing Interface",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_TIMING,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_TIMING,
+);
diff --git a/res/res_timing_kqueue.c b/res/res_timing_kqueue.c
index 17f98360e..dd2a8397c 100644
--- a/res/res_timing_kqueue.c
+++ b/res/res_timing_kqueue.c
@@ -499,8 +499,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "KQueue Timing Interface",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
diff --git a/res/res_timing_pthread.c b/res/res_timing_pthread.c
index dc7d0918b..6476e74fb 100644
--- a/res/res_timing_pthread.c
+++ b/res/res_timing_pthread.c
@@ -477,8 +477,8 @@ static int unload_module(void)
return res;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "pthread Timing Interface",
- .support_level = AST_MODULE_SUPPORT_EXTENDED,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_TIMING,
- );
+ .support_level = AST_MODULE_SUPPORT_EXTENDED,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_TIMING,
+);
diff --git a/res/res_timing_timerfd.c b/res/res_timing_timerfd.c
index 6d5400bc3..5ee21fcc0 100644
--- a/res/res_timing_timerfd.c
+++ b/res/res_timing_timerfd.c
@@ -272,8 +272,8 @@ static int unload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Timerfd Timing Interface",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_TIMING,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_TIMING,
+);
diff --git a/res/res_xmpp.c b/res/res_xmpp.c
index 59b59e69d..2a087b055 100644
--- a/res/res_xmpp.c
+++ b/res/res_xmpp.c
@@ -3568,12 +3568,12 @@ int ast_xmpp_client_disconnect(struct ast_xmpp_client *client)
}
if (client->mwi_sub) {
- client->mwi_sub = stasis_unsubscribe(client->mwi_sub);
+ client->mwi_sub = stasis_unsubscribe_and_join(client->mwi_sub);
xmpp_pubsub_unsubscribe(client, "message_waiting");
}
if (client->device_state_sub) {
- client->device_state_sub = stasis_unsubscribe(client->device_state_sub);
+ client->device_state_sub = stasis_unsubscribe_and_join(client->device_state_sub);
xmpp_pubsub_unsubscribe(client, "device_state");
}
@@ -4661,9 +4661,9 @@ static int reload(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Asterisk XMPP Interface",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
diff --git a/res/snmp/agent.c b/res/snmp/agent.c
index bebcee11e..f0e089fb6 100644
--- a/res/snmp/agent.c
+++ b/res/snmp/agent.c
@@ -18,6 +18,8 @@
<support_level>extended</support_level>
***/
+/* Needed for net-snmp headers */
+#define ASTMM_LIBC ASTMM_IGNORE
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
diff --git a/res/stasis/app.c b/res/stasis/app.c
index 5a1c5f8e2..b99e23205 100644
--- a/res/stasis/app.c
+++ b/res/stasis/app.c
@@ -871,8 +871,7 @@ struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *dat
strncpy(app->name, name, size - sizeof(*app));
app->handler = handler;
- ao2_ref(data, +1);
- app->data = data;
+ app->data = ao2_bump(data);
ao2_ref(app, +1);
return app;
@@ -950,7 +949,7 @@ void app_update(struct stasis_app *app, stasis_app_cb handler, void *data)
{
SCOPED_AO2LOCK(lock, app);
- if (app->handler) {
+ if (app->handler && app->data) {
RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
ast_verb(1, "Replacing Stasis app '%s'\n", app->name);
diff --git a/rest-api-templates/ari_resource.h.mustache b/rest-api-templates/ari_resource.h.mustache
index 3a20776a7..d3f40b6bd 100644
--- a/rest-api-templates/ari_resource.h.mustache
+++ b/rest-api-templates/ari_resource.h.mustache
@@ -89,6 +89,23 @@ int ast_ari_{{c_name}}_{{c_nickname}}_parse_body(
void ast_ari_{{c_name}}_{{c_nickname}}(struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args, struct ast_ari_response *response);
{{/is_req}}
{{#is_websocket}}
+
+/*!
+ * \brief {{summary}}
+{{#notes}}
+ *
+ * {{{notes}}}
+{{/notes}}
+ *
+ * \param ser HTTP TCP/TLS Server Session
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ *
+ * \retval 0 success
+ * \retval non-zero error
+ */
+int ast_ari_websocket_{{c_name}}_{{c_nickname}}_attempted(struct ast_tcptls_session_instance *ser, struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args);
+
/*!
* \brief {{summary}}
{{#notes}}
@@ -100,7 +117,7 @@ void ast_ari_{{c_name}}_{{c_nickname}}(struct ast_variable *headers, struct ast_
* \param headers HTTP headers.
* \param args Swagger parameters.
*/
-void ast_ari_websocket_{{c_name}}_{{c_nickname}}(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args);
+void ast_ari_websocket_{{c_name}}_{{c_nickname}}_established(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args);
{{/is_websocket}}
{{/operations}}
{{/apis}}
diff --git a/rest-api-templates/res_ari_resource.c.mustache b/rest-api-templates/res_ari_resource.c.mustache
index d9ca39d6a..7fe360e89 100644
--- a/rest-api-templates/res_ari_resource.c.mustache
+++ b/rest-api-templates/res_ari_resource.c.mustache
@@ -137,7 +137,52 @@ fin: __attribute__((unused))
}
{{/is_req}}
{{#is_websocket}}
-static void ast_ari_{{c_name}}_{{c_nickname}}_ws_cb(struct ast_websocket *ws_session,
+static int ast_ari_{{c_name}}_{{c_nickname}}_ws_attempted_cb(struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *headers)
+{
+ struct ast_ari_{{c_name}}_{{c_nickname}}_args args = {};
+{{#has_parameters}}
+ int res = 0;
+ RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
+ struct ast_variable *i;
+{{/has_parameters}}
+
+{{#has_parameters}}
+ response = ast_calloc(1, sizeof(*response));
+ if (!response) {
+ ast_log(LOG_ERROR, "Failed to create response.\n");
+ goto fin;
+ }
+{{/has_parameters}}
+
+{{> param_parsing}}
+
+ res = ast_ari_websocket_{{c_name}}_{{c_nickname}}_attempted(ser, headers, &args);
+
+fin: __attribute__((unused))
+ if (!response) {
+ ast_http_error(ser, 500, "Server Error", "Memory allocation error");
+ res = -1;
+ } else if (response->response_code != 0) {
+ /* Param parsing failure */
+ RAII_VAR(char *, msg, NULL, ast_json_free);
+ if (response->message) {
+ msg = ast_json_dump_string(response->message);
+ } else {
+ ast_log(LOG_ERROR, "Missing response message\n");
+ }
+
+ if (msg) {
+ ast_http_error(ser, response->response_code, response->response_text, msg);
+ }
+ res = -1;
+ }
+{{> param_cleanup}}
+{{#has_parameters}}
+ return res;
+{{/has_parameters}}
+}
+
+static void ast_ari_{{c_name}}_{{c_nickname}}_ws_established_cb(struct ast_websocket *ws_session,
struct ast_variable *get_params, struct ast_variable *headers)
{
struct ast_ari_{{c_name}}_{{c_nickname}}_args args = {};
@@ -175,16 +220,11 @@ static void ast_ari_{{c_name}}_{{c_nickname}}_ws_cb(struct ast_websocket *ws_ses
{{> param_parsing}}
- ast_ari_websocket_{{c_name}}_{{c_nickname}}(session, headers, &args);
+ ast_ari_websocket_{{c_name}}_{{c_nickname}}_established(session, headers, &args);
fin: __attribute__((unused))
if (response && response->response_code != 0) {
/* Param parsing failure */
- /* TODO - ideally, this would return the error code to the
- * HTTP client; but we've already done the WebSocket
- * negotiation. Param parsing should happen earlier, but we
- * need a way to pass it through the WebSocket code to the
- * callback */
RAII_VAR(char *, msg, NULL, ast_json_free);
if (response->message) {
msg = ast_json_dump_string(response->message);
@@ -211,16 +251,26 @@ static int load_module(void)
{
int res = 0;
{{#apis}}
+{{#operations}}
{{#has_websocket}}
+ struct ast_websocket_protocol *protocol;
+
{{full_name}}.ws_server = ast_websocket_server_create();
if (!{{full_name}}.ws_server) {
return AST_MODULE_LOAD_FAILURE;
}
+
+ protocol = ast_websocket_sub_protocol_alloc("{{websocket_protocol}}");
+ if (!protocol) {
+ ao2_ref({{full_name}}.ws_server, -1);
+ {{full_name}}.ws_server = NULL;
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ protocol->session_attempted = ast_ari_{{c_name}}_{{c_nickname}}_ws_attempted_cb;
+ protocol->session_established = ast_ari_{{c_name}}_{{c_nickname}}_ws_established_cb;
{{/has_websocket}}
-{{#operations}}
{{#is_websocket}}
- res |= ast_websocket_server_add_protocol({{full_name}}.ws_server,
- "{{websocket_protocol}}", ast_ari_{{c_name}}_{{c_nickname}}_ws_cb);
+ res |= ast_websocket_server_add_protocol2({{full_name}}.ws_server, protocol);
{{/is_websocket}}
{{/operations}}
{{/apis}}
@@ -247,5 +297,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - {{{
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_ari,res_stasis",
- );
+);
{{/api_declaration}}
diff --git a/rest-api/api-docs/applications.json b/rest-api/api-docs/applications.json
index f6fe72ac5..6dcdb78b1 100644
--- a/rest-api/api-docs/applications.json
+++ b/rest-api/api-docs/applications.json
@@ -2,7 +2,7 @@
"_copyright": "Copyright (C) 2013, Digium, Inc.",
"_author": "David M. Lee, II <dlee@digium.com>",
"_svn_revision": "$Revision$",
- "apiVersion": "1.6.0",
+ "apiVersion": "1.7.0",
"swaggerVersion": "1.1",
"basePath": "http://localhost:8088/ari",
"resourcePath": "/api-docs/applications.{format}",
diff --git a/rest-api/api-docs/asterisk.json b/rest-api/api-docs/asterisk.json
index f13b0493e..263bfd614 100644
--- a/rest-api/api-docs/asterisk.json
+++ b/rest-api/api-docs/asterisk.json
@@ -2,7 +2,7 @@
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
"_author": "David M. Lee, II <dlee@digium.com>",
"_svn_revision": "$Revision$",
- "apiVersion": "1.6.0",
+ "apiVersion": "1.7.0",
"swaggerVersion": "1.1",
"basePath": "http://localhost:8088/ari",
"resourcePath": "/api-docs/asterisk.{format}",
diff --git a/rest-api/api-docs/bridges.json b/rest-api/api-docs/bridges.json
index 4f7f72400..b608be6d6 100644
--- a/rest-api/api-docs/bridges.json
+++ b/rest-api/api-docs/bridges.json
@@ -2,7 +2,7 @@
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
"_author": "David M. Lee, II <dlee@digium.com>",
"_svn_revision": "$Revision$",
- "apiVersion": "1.6.0",
+ "apiVersion": "1.7.0",
"swaggerVersion": "1.1",
"basePath": "http://localhost:8088/ari",
"resourcePath": "/api-docs/bridges.{format}",
diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json
index 9bcfda978..bc0879b57 100644
--- a/rest-api/api-docs/channels.json
+++ b/rest-api/api-docs/channels.json
@@ -2,7 +2,7 @@
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
"_author": "David M. Lee, II <dlee@digium.com>",
"_svn_revision": "$Revision$",
- "apiVersion": "1.6.0",
+ "apiVersion": "1.7.0",
"swaggerVersion": "1.1",
"basePath": "http://localhost:8088/ari",
"resourcePath": "/api-docs/channels.{format}",
diff --git a/rest-api/api-docs/deviceStates.json b/rest-api/api-docs/deviceStates.json
index 02d5d208d..e23227396 100644
--- a/rest-api/api-docs/deviceStates.json
+++ b/rest-api/api-docs/deviceStates.json
@@ -2,7 +2,7 @@
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
"_author": "Kevin Harwell <kharwell@digium.com>",
"_svn_revision": "$Revision$",
- "apiVersion": "1.6.0",
+ "apiVersion": "1.7.0",
"swaggerVersion": "1.1",
"basePath": "http://localhost:8088/ari",
"resourcePath": "/api-docs/deviceStates.{format}",
diff --git a/rest-api/api-docs/endpoints.json b/rest-api/api-docs/endpoints.json
index 17b884784..4fd077c14 100644
--- a/rest-api/api-docs/endpoints.json
+++ b/rest-api/api-docs/endpoints.json
@@ -2,7 +2,7 @@
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
"_author": "David M. Lee, II <dlee@digium.com>",
"_svn_revision": "$Revision$",
- "apiVersion": "1.6.0",
+ "apiVersion": "1.7.0",
"swaggerVersion": "1.1",
"basePath": "http://localhost:8088/ari",
"resourcePath": "/api-docs/endpoints.{format}",
diff --git a/rest-api/api-docs/events.json b/rest-api/api-docs/events.json
index 35c9377e5..392b0ac70 100644
--- a/rest-api/api-docs/events.json
+++ b/rest-api/api-docs/events.json
@@ -2,7 +2,7 @@
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
"_author": "David M. Lee, II <dlee@digium.com>",
"_svn_revision": "$Revision$",
- "apiVersion": "1.6.0",
+ "apiVersion": "1.7.0",
"swaggerVersion": "1.2",
"basePath": "http://localhost:8088/ari",
"resourcePath": "/api-docs/events.{format}",
diff --git a/rest-api/api-docs/mailboxes.json b/rest-api/api-docs/mailboxes.json
index 87c6f0347..324e37885 100644
--- a/rest-api/api-docs/mailboxes.json
+++ b/rest-api/api-docs/mailboxes.json
@@ -2,7 +2,7 @@
"_copyright": "Copyright (C) 2013, Digium, Inc.",
"_author": "Jonathan Rose <jrose@digium.com>",
"_svn_revision": "$Revision$",
- "apiVersion": "1.6.0",
+ "apiVersion": "1.7.0",
"swaggerVersion": "1.1",
"basePath": "http://localhost:8088/ari",
"resourcePath": "/api-docs/mailboxes.{format}",
diff --git a/rest-api/api-docs/playbacks.json b/rest-api/api-docs/playbacks.json
index 65ef52457..63df3f24b 100644
--- a/rest-api/api-docs/playbacks.json
+++ b/rest-api/api-docs/playbacks.json
@@ -2,7 +2,7 @@
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
"_author": "David M. Lee, II <dlee@digium.com>",
"_svn_revision": "$Revision$",
- "apiVersion": "1.6.0",
+ "apiVersion": "1.7.0",
"swaggerVersion": "1.1",
"basePath": "http://localhost:8088/ari",
"resourcePath": "/api-docs/playbacks.{format}",
diff --git a/rest-api/api-docs/recordings.json b/rest-api/api-docs/recordings.json
index d96184bd0..51f0a21f4 100644
--- a/rest-api/api-docs/recordings.json
+++ b/rest-api/api-docs/recordings.json
@@ -2,7 +2,7 @@
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
"_author": "David M. Lee, II <dlee@digium.com>",
"_svn_revision": "$Revision$",
- "apiVersion": "1.6.0",
+ "apiVersion": "1.7.0",
"swaggerVersion": "1.1",
"basePath": "http://localhost:8088/ari",
"resourcePath": "/api-docs/recordings.{format}",
diff --git a/rest-api/api-docs/sounds.json b/rest-api/api-docs/sounds.json
index 906eb6bf1..628aa1948 100644
--- a/rest-api/api-docs/sounds.json
+++ b/rest-api/api-docs/sounds.json
@@ -2,7 +2,7 @@
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
"_author": "David M. Lee, II <dlee@digium.com>",
"_svn_revision": "$Revision$",
- "apiVersion": "1.6.0",
+ "apiVersion": "1.7.0",
"swaggerVersion": "1.1",
"basePath": "http://localhost:8088/ari",
"resourcePath": "/api-docs/sounds.{format}",
diff --git a/sounds/Makefile b/sounds/Makefile
index 13a7184ac..a726ece15 100644
--- a/sounds/Makefile
+++ b/sounds/Makefile
@@ -19,7 +19,7 @@ CMD_PREFIX?=@
SOUNDS_DIR:=$(DESTDIR)$(ASTDATADIR)/sounds
SOUNDS_CACHE_DIR?=
MOH_DIR:=$(DESTDIR)$(ASTDATADIR)/moh
-CORE_SOUNDS_VERSION:=1.4.26
+CORE_SOUNDS_VERSION:=1.4.27
EXTRA_SOUNDS_VERSION:=1.4.15
MOH_VERSION:=2.03
SOUNDS_URL:=http://downloads.asterisk.org/pub/telephony/sounds/releases
@@ -31,6 +31,7 @@ MCS:=$(subst -ES-,-es-,$(MCS))
MCS:=$(subst -RU-,-ru-,$(MCS))
MCS:=$(subst -IT-,-it-,$(MCS))
MCS:=$(subst -JA-,-ja-,$(MCS))
+MCS:=$(subst -SV-,-sv-,$(MCS))
MCS:=$(subst -WAV,-wav,$(MCS))
MCS:=$(subst -ULAW,-ulaw,$(MCS))
MCS:=$(subst -ALAW,-alaw,$(MCS))
@@ -153,6 +154,8 @@ $(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),core-sounds,ru,$(CORE_SOUNDS_
$(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),core-sounds,ja,$(CORE_SOUNDS_VERSION)))
+$(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),core-sounds,sv,$(CORE_SOUNDS_VERSION)))
+
$(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),extra-sounds,en,$(EXTRA_SOUNDS_VERSION)))
$(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),extra-sounds,en_GB,$(EXTRA_SOUNDS_VERSION)))
diff --git a/sounds/sounds.xml b/sounds/sounds.xml
index 5934bb34e..547be4b4a 100644
--- a/sounds/sounds.xml
+++ b/sounds/sounds.xml
@@ -216,6 +216,33 @@
<member name="CORE-SOUNDS-JA-SIREN14" displayname="Japanese, G.722.1C (Siren14) format">
<support_level>core</support_level>
</member>
+ <member name="CORE-SOUNDS-SV-WAV" displayname="Swedish, WAV format">
+ <support_level>core</support_level>
+ </member>
+ <member name="CORE-SOUNDS-SV-ULAW" displayname="Swedish, mu-Law format">
+ <support_level>core</support_level>
+ </member>
+ <member name="CORE-SOUNDS-SV-ALAW" displayname="Swedish, a-Law format">
+ <support_level>core</support_level>
+ </member>
+ <member name="CORE-SOUNDS-SV-GSM" displayname="Swedish, GSM format">
+ <support_level>core</support_level>
+ </member>
+ <member name="CORE-SOUNDS-SV-G729" displayname="Swedish, G.729 format">
+ <support_level>core</support_level>
+ </member>
+ <member name="CORE-SOUNDS-SV-G722" displayname="Swedish, G.722 format">
+ <support_level>core</support_level>
+ </member>
+ <member name="CORE-SOUNDS-SV-SLN16" displayname="Swedish, Signed-linear 16kHz format">
+ <support_level>core</support_level>
+ </member>
+ <member name="CORE-SOUNDS-SV-SIREN7" displayname="Swedish, G.722.1 (Siren7) format">
+ <support_level>core</support_level>
+ </member>
+ <member name="CORE-SOUNDS-SV-SIREN14" displayname="Swedish, G.722.1C (Siren14) format">
+ <support_level>core</support_level>
+ </member>
</category>
<category name="MENUSELECT_MOH" displayname="Music On Hold File Packages" positive_output="yes">
<member name="MOH-OPSOUND-WAV" displayname="opsound.org Music On Hold Files, WAV format" >
diff --git a/tests/test_ari.c b/tests/test_ari.c
index 7da98f2cf..efec810e1 100644
--- a/tests/test_ari.c
+++ b/tests/test_ari.c
@@ -17,7 +17,8 @@
*/
/*!
- * \file \brief Test ARI API.
+ * \file
+ * \brief Test ARI API.
* \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim
*
* \ingroup tests
@@ -567,4 +568,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ARI testing",
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_ari",
- );
+);
diff --git a/tests/test_config.c b/tests/test_config.c
index ad7ddcae5..12c75bfc6 100644
--- a/tests/test_config.c
+++ b/tests/test_config.c
@@ -234,6 +234,7 @@ AST_TEST_DEFINE(config_basic_ops)
struct ast_config *cfg = NULL;
struct ast_category *cat = NULL;
struct ast_variable *var;
+ struct ast_variable *varlist;
char temp[32];
const char *cat_name;
const char *var_value;
@@ -537,6 +538,22 @@ AST_TEST_DEFINE(config_basic_ops)
goto out;
}
+ varlist = ast_variable_new("name1", "value1", "");
+ ast_variable_list_append_hint(&varlist, NULL, ast_variable_new("name1", "value2", ""));
+ ast_variable_list_append_hint(&varlist, NULL, ast_variable_new("name1", "value3", ""));
+
+ var_value = ast_variable_find_in_list(varlist, "name1");
+ if (strcmp(var_value, "value1") != 0) {
+ ast_test_status_update(test, "Wrong variable retrieved %s.\n", var_value);
+ goto out;
+ }
+
+ var_value = ast_variable_find_last_in_list(varlist, "name1");
+ if (strcmp(var_value, "value3") != 0) {
+ ast_test_status_update(test, "Wrong variable retrieved %s.\n", var_value);
+ goto out;
+ }
+
res = AST_TEST_PASS;
out:
diff --git a/tests/test_endpoints.c b/tests/test_endpoints.c
index b7a2efd57..d1239037b 100644
--- a/tests/test_endpoints.c
+++ b/tests/test_endpoints.c
@@ -17,7 +17,8 @@
*/
/*!
- * \file \brief Test endpoints.
+ * \file
+ * \brief Test endpoints.
*
* \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim
*
@@ -154,4 +155,4 @@ static int load_module(void)
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Endpoint testing",
.load = load_module,
.unload = unload_module,
- );
+);
diff --git a/tests/test_json.c b/tests/test_json.c
index b84899475..915578128 100644
--- a/tests/test_json.c
+++ b/tests/test_json.c
@@ -17,7 +17,8 @@
*/
/*!
- * \file \brief Test JSON API.
+ * \file
+ * \brief Test JSON API.
*
* While some of these tests are actually testing our JSON library wrapper, the bulk of
* them are exploratory tests to determine what the behavior of the underlying JSON
@@ -1806,5 +1807,6 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, 0, "JSON testing",
- .load = load_module,
- .unload = unload_module);
+ .load = load_module,
+ .unload = unload_module
+);
diff --git a/tests/test_message.c b/tests/test_message.c
index 26cd90a4d..2c9334ad9 100644
--- a/tests/test_message.c
+++ b/tests/test_message.c
@@ -51,8 +51,6 @@ ASTERISK_REGISTER_FILE()
/*! \brief The number of user events we should get in a dialplan test */
#define DEFAULT_EXPECTED_EVENTS 4
-static struct ast_context *test_message_context;
-
/*! \brief The current number of received user events */
static int received_user_events;
@@ -822,9 +820,7 @@ static int unload_module(void)
AST_TEST_UNREGISTER(test_message_has_destination_handler);
AST_TEST_UNREGISTER(test_message_msg_send);
- if (test_message_context) {
- ast_context_destroy(test_message_context, AST_MODULE);
- }
+ ast_context_destroy(NULL, AST_MODULE);
ast_manager_unregister_hook(&user_event_hook);
@@ -835,8 +831,7 @@ static int create_test_dialplan(void)
{
int res = 0;
- test_message_context = ast_context_find_or_create(NULL, NULL, TEST_CONTEXT, AST_MODULE);
- if (!test_message_context) {
+ if (!ast_context_find_or_create(NULL, NULL, TEST_CONTEXT, AST_MODULE)) {
return -1;
}
diff --git a/tests/test_optional_api.c b/tests/test_optional_api.c
index 3867a5579..f7809d32b 100644
--- a/tests/test_optional_api.c
+++ b/tests/test_optional_api.c
@@ -17,7 +17,8 @@
*/
/*!
- * \file \brief Test optional API.
+ * \file
+ * \brief Test optional API.
*
* This tests exercise the underlying implementation functions. Acutal usage
* won't look anything like this; it would use the wrapper macros.
@@ -184,4 +185,4 @@ static int load_module(void)
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ARI testing",
.load = load_module,
.unload = unload_module,
- );
+);
diff --git a/tests/test_res_stasis.c b/tests/test_res_stasis.c
index 45e8f5ddd..4e28d44c6 100644
--- a/tests/test_res_stasis.c
+++ b/tests/test_res_stasis.c
@@ -17,7 +17,8 @@
*/
/*!
- * \file \brief Test Stasis Application API.
+ * \file
+ * \brief Test Stasis Application API.
* \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim
*
* \ingroup tests
@@ -195,4 +196,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Stasis Core testing",
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_stasis",
- );
+);
diff --git a/tests/test_sorcery.c b/tests/test_sorcery.c
index ce04c62cb..b6b3d09c9 100644
--- a/tests/test_sorcery.c
+++ b/tests/test_sorcery.c
@@ -179,6 +179,19 @@ static struct sorcery_test_caching cache = { 0, };
/*! \brief Global scope observer structure for testing */
static struct sorcery_test_observer observer;
+static void *wizard2_data;
+
+static void *sorcery_test_open(const char *data)
+{
+ wizard2_data = (void *)data;
+ return wizard2_data;
+}
+
+static void sorcery_test_close(void *data)
+{
+
+}
+
static int sorcery_test_create(const struct ast_sorcery *sorcery, void *data, void *object)
{
cache.created = 1;
@@ -204,7 +217,7 @@ static int sorcery_test_delete(const struct ast_sorcery *sorcery, void *data, vo
return 0;
}
-/*! \brief Dummy sorcery wizard, not actually used so we only populate the name and nothing else */
+/*! \brief Dummy sorcery wizards, not actually used so we only populate the name and nothing else */
static struct ast_sorcery_wizard test_wizard = {
.name = "test",
.create = sorcery_test_create,
@@ -213,6 +226,16 @@ static struct ast_sorcery_wizard test_wizard = {
.delete = sorcery_test_delete,
};
+static struct ast_sorcery_wizard test_wizard2 = {
+ .name = "test2",
+ .open = sorcery_test_open,
+ .close = sorcery_test_close,
+ .create = sorcery_test_create,
+ .retrieve_id = sorcery_test_retrieve_id,
+ .update = sorcery_test_update,
+ .delete = sorcery_test_delete,
+};
+
static void sorcery_observer_created(const void *object)
{
SCOPED_MUTEX(lock, &observer.lock);
@@ -3340,6 +3363,111 @@ AST_TEST_DEFINE(wizard_observation)
return AST_TEST_PASS;
}
+AST_TEST_DEFINE(wizard_apply_and_insert)
+{
+ RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+ RAII_VAR(struct ast_sorcery_wizard *, wizard1, &test_wizard, ast_sorcery_wizard_unregister);
+ RAII_VAR(struct ast_sorcery_wizard *, wizard2, &test_wizard2, ast_sorcery_wizard_unregister);
+ RAII_VAR(struct ast_sorcery_wizard *, wizard, NULL, ao2_cleanup);
+ void *data;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "wizard_apply_and_insert";
+ info->category = "/main/sorcery/";
+ info->summary = "sorcery wizard apply and insert test";
+ info->description =
+ "sorcery wizard apply and insert test";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ wizard1->load = sorcery_test_load;
+ wizard1->reload = sorcery_test_load;
+
+ wizard2->load = sorcery_test_load;
+ wizard2->reload = sorcery_test_load;
+
+ if (!(sorcery = ast_sorcery_open())) {
+ ast_test_status_update(test, "Failed to open a sorcery instance\n");
+ return AST_TEST_FAIL;
+ }
+
+ ast_sorcery_wizard_register(wizard1);
+ ast_sorcery_wizard_register(wizard2);
+
+ /* test_object_type isn't registered yet so count should return error */
+ ast_test_validate(test,
+ ast_sorcery_get_wizard_mapping_count(sorcery, "test_object_type") == -1);
+
+ ast_sorcery_apply_default(sorcery, "test_object_type", "test", NULL);
+
+ ast_test_validate(test,
+ ast_sorcery_get_wizard_mapping_count(sorcery, "test_object_type") == 1);
+
+ ast_test_validate(test,
+ ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 0, &wizard, NULL) == 0);
+ ast_test_validate(test, strcmp("test", wizard->name) == 0);
+ ao2_ref(wizard, -1);
+ wizard = NULL;
+
+ ast_test_validate(test,
+ ast_sorcery_insert_wizard_mapping(sorcery, "test_object_type", "test2", "test2data", 0, 0) == 0);
+
+ ast_test_validate(test,
+ ast_sorcery_insert_wizard_mapping(sorcery, "test_object_type", "test2", "test2data", 0, 0) != 0);
+
+ ast_test_validate(test,
+ ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 0, &wizard, &data) == 0);
+ ast_test_validate(test, strcmp("test2", wizard->name) == 0);
+ ast_test_validate(test, strcmp("test2data", data) == 0);
+ ao2_ref(wizard, -1);
+ wizard = NULL;
+
+ ast_test_validate(test,
+ ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 1, &wizard, NULL) == 0);
+ ast_test_validate(test, strcmp("test", wizard->name) == 0);
+ ao2_ref(wizard, -1);
+ wizard = NULL;
+
+ /* Test failures */
+ ast_test_validate(test,
+ ast_sorcery_get_wizard_mapping(sorcery, "non-existent-type", 0, &wizard, NULL) != 0);
+
+ ast_test_validate(test,
+ ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", -1, &wizard, &data) != 0);
+
+ ast_test_validate(test,
+ ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 2, &wizard, NULL) != 0);
+
+ ast_test_validate(test,
+ ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 2, NULL, NULL) != 0);
+
+ /* Test remove */
+ /* Should fail */
+ ast_test_validate(test,
+ ast_sorcery_remove_wizard_mapping(sorcery, "non-existent-type", "somewizard") != 0);
+ ast_test_validate(test,
+ ast_sorcery_remove_wizard_mapping(sorcery, "test_object_type", "somewizard") != 0);
+
+ /* should work */
+ ast_test_validate(test,
+ ast_sorcery_remove_wizard_mapping(sorcery, "test_object_type", "test") == 0);
+
+ ast_test_validate(test,
+ ast_sorcery_get_wizard_mapping_count(sorcery, "test_object_type") == 1);
+
+ ast_test_validate(test,
+ ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 0, &wizard, &data) == 0);
+ ast_test_validate(test, strcmp("test2", wizard->name) == 0);
+ ast_test_validate(test, strcmp("test2data", data) == 0);
+ ao2_ref(wizard, -1);
+ wizard = NULL;
+
+ return AST_TEST_PASS;
+}
+
static int unload_module(void)
{
AST_TEST_UNREGISTER(wizard_registration);
@@ -3390,12 +3518,14 @@ static int unload_module(void)
AST_TEST_UNREGISTER(global_observation);
AST_TEST_UNREGISTER(instance_observation);
AST_TEST_UNREGISTER(wizard_observation);
+ AST_TEST_UNREGISTER(wizard_apply_and_insert);
return 0;
}
static int load_module(void)
{
+ AST_TEST_REGISTER(wizard_apply_and_insert);
AST_TEST_REGISTER(wizard_registration);
AST_TEST_REGISTER(sorcery_open);
AST_TEST_REGISTER(apply_default);
diff --git a/tests/test_stasis.c b/tests/test_stasis.c
index f5d74a806..b5755c843 100644
--- a/tests/test_stasis.c
+++ b/tests/test_stasis.c
@@ -17,7 +17,8 @@
*/
/*!
- * \file \brief Test Stasis message bus.
+ * \file
+ * \brief Test Stasis message bus.
*
* \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim
*
@@ -2104,6 +2105,6 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, 0, "Stasis testing",
- .load = load_module,
- .unload = unload_module
- );
+ .load = load_module,
+ .unload = unload_module
+);
diff --git a/tests/test_stasis_channels.c b/tests/test_stasis_channels.c
index 91dfe09c6..fe320f29b 100644
--- a/tests/test_stasis_channels.c
+++ b/tests/test_stasis_channels.c
@@ -17,7 +17,8 @@
*/
/*!
- * \file \brief Test Stasis Channel messages and objects
+ * \file
+ * \brief Test Stasis Channel messages and objects
*
* \author\verbatim Matt Jordan <mjordan@digium.com> \endverbatim
*
@@ -328,6 +329,6 @@ static int load_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, 0, "Stasis Channel Testing",
- .load = load_module,
- .unload = unload_module
- );
+ .load = load_module,
+ .unload = unload_module
+);
diff --git a/tests/test_stasis_endpoints.c b/tests/test_stasis_endpoints.c
index 2059c60b1..848d86b1c 100644
--- a/tests/test_stasis_endpoints.c
+++ b/tests/test_stasis_endpoints.c
@@ -17,7 +17,8 @@
*/
/*!
- * \file \brief Test endpoints.
+ * \file
+ * \brief Test endpoints.
*
* \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim
*
@@ -307,4 +308,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Endpoint stasis-related
.load = load_module,
.unload = unload_module,
.nonoptreq = "res_stasis_test",
- );
+);
diff --git a/tests/test_vector.c b/tests/test_vector.c
index bd45b0c2b..ff305b5d7 100644
--- a/tests/test_vector.c
+++ b/tests/test_vector.c
@@ -63,7 +63,9 @@ AST_TEST_DEFINE(basic_ops)
char *CCC = "CCC";
char *YYY = "YYY";
char *ZZZ = "ZZZ";
+ char CCC2[4];
+ strcpy(CCC2, "CCC");
switch (cmd) {
case TEST_INIT:
info->name = "basic";
@@ -202,6 +204,29 @@ AST_TEST_DEFINE(basic_ops)
ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 0) == CCC, rc, cleanup);
ast_test_validate_cleanup(test, cleanup_count == 1, rc, cleanup);
+ /* Test INSERT_SORTED */
+ AST_VECTOR_FREE(&sv1);
+ ast_test_validate(test, AST_VECTOR_INIT(&sv1, 0) == 0);
+
+ ast_test_validate_cleanup(test, AST_VECTOR_ADD_SORTED(&sv1, BBB, strcmp) == 0, rc, cleanup);
+ ast_test_validate_cleanup(test, AST_VECTOR_ADD_SORTED(&sv1, ZZZ, strcmp) == 0, rc, cleanup);
+ ast_test_validate_cleanup(test, AST_VECTOR_ADD_SORTED(&sv1, CCC, strcmp) == 0, rc, cleanup);
+ ast_test_validate_cleanup(test, AST_VECTOR_ADD_SORTED(&sv1, AAA, strcmp) == 0, rc, cleanup);
+ ast_test_validate_cleanup(test, AST_VECTOR_ADD_SORTED(&sv1, CCC2, strcmp) == 0, rc, cleanup);
+
+ ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 0) == AAA, rc, cleanup);
+ ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 1) == BBB, rc, cleanup);
+ ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 2) == CCC, rc, cleanup);
+ ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 3) == CCC2, rc, cleanup);
+ ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 4) == ZZZ, rc, cleanup);
+
+ cleanup_count = 0;
+ AST_VECTOR_RESET(&sv1, cleanup);
+ ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 0, rc, cleanup);
+ ast_test_validate_cleanup(test, sv1.max >= 5, rc, cleanup);
+ ast_test_validate_cleanup(test, sv1.elems != NULL, rc, cleanup);
+ ast_test_validate_cleanup(test, cleanup_count == 5, rc, cleanup);
+
cleanup:
AST_VECTOR_FREE(&sv1);
return rc;
@@ -218,13 +243,13 @@ AST_TEST_DEFINE(basic_ops_integer)
int rc = AST_TEST_PASS;
int AAA = 1;
- int BBB = 2;
- int CCC = 3;
+ int BBB = 3;
+ int CCC = 5;
int ZZZ = 26;
switch (cmd) {
case TEST_INIT:
- info->name = "basic integer";
+ info->name = "basic_integer";
info->category = "/main/vector/";
info->summary = "Test integer vector basic ops";
info->description = "Test integer vector basic ops";
diff --git a/utils/ael_main.c b/utils/ael_main.c
index 06ba8e66e..d2c815b3b 100644
--- a/utils/ael_main.c
+++ b/utils/ael_main.c
@@ -48,7 +48,7 @@ struct namelist
struct namelist *next;
};
-struct ast_context
+struct ast_context
{
int extension_count;
char name[100];
@@ -136,7 +136,7 @@ void ast_log(int level, const char *file, int line, const char *function, const
{
va_list vars;
va_start(vars,fmt);
-
+
printf("LOG: lev:%d file:%s line:%d func: %s ",
level, file, line, function);
vprintf(fmt, vars);
@@ -147,21 +147,21 @@ void ast_log(int level, const char *file, int line, const char *function, const
struct ast_exten *pbx_find_extension(struct ast_channel *chan,
struct ast_context *bypass,
struct pbx_find_info *q,
- const char *context,
- const char *exten,
+ const char *context,
+ const char *exten,
int priority,
- const char *label,
- const char *callerid,
+ const char *label,
+ const char *callerid,
enum ext_match_t action);
struct ast_exten *pbx_find_extension(struct ast_channel *chan,
struct ast_context *bypass,
struct pbx_find_info *q,
- const char *context,
- const char *exten,
+ const char *context,
+ const char *exten,
int priority,
- const char *label,
- const char *callerid,
+ const char *label,
+ const char *callerid,
enum ext_match_t action)
{
return localized_find_extension(bypass, q, context, exten, priority, label, callerid, action);
@@ -218,7 +218,7 @@ void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char
void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count)
{
if (cp1 && *cp1)
- strncpy(cp2,cp1,AST_MAX_EXTENSION); /* Right now, this routine is ONLY being called for
+ strncpy(cp2,cp1,AST_MAX_EXTENSION); /* Right now, this routine is ONLY being called for
a possible var substitution on extension names,
so....! */
else
@@ -255,15 +255,15 @@ int ast_add_extension2(struct ast_context *con,
if( FIRST_TIME ) {
FIRST_TIME = 0;
-
+
if( globalvars )
fprintf(dumpfile,"[globals]\n");
-
+
for(n=globalvars;n;n=n->next) {
fprintf(dumpfile, "%s\n", n->name);
}
}
-
+
/* print out each extension , possibly the context header also */
if( con != last_context ) {
fprintf(dumpfile,"\n\n[%s]\n", con->name);
@@ -280,7 +280,7 @@ int ast_add_extension2(struct ast_context *con,
for(n=con->eswitches;n;n=n->next) {
fprintf(dumpfile, "eswitch => %s/%s\n", n->name, n->name2);
}
-
+
}
if( data ) {
filter_newlines((char*)data);
@@ -303,7 +303,7 @@ int ast_add_extension2(struct ast_context *con,
fprintf(dumpfile,"exten => %s,%d,%s\n", extension, priority, application);
}
}
-
+
/* since add_extension2 is responsible for the malloc'd data stuff */
free(data);
return 0;
@@ -320,7 +320,7 @@ void pbx_builtin_setvar(void *chan, void *data)
ADD_LAST(globalvars,x);
}
}
-
+
struct ast_context * ast_context_create(void **extcontexts, const char *name, const char *registrar)
{
@@ -458,7 +458,7 @@ void filter_leading_space_from_exprs(char *str)
{
/* Mainly for aesthetics */
char *t, *v, *u = str;
-
+
while ( u && *u ) {
if( *u == '$' && *(u+1) == '[' ) {
@@ -471,7 +471,7 @@ void filter_leading_space_from_exprs(char *str)
}
}
}
-
+
u++;
}
}
@@ -500,7 +500,7 @@ int main(int argc, char **argv)
int i;
struct namelist *n;
struct ast_context *lp,*lp2;
-
+
for(i=1;i<argc;i++) {
if( argv[i][0] == '-' && argv[i][1] == 'n' )
no_comp =1;
@@ -513,7 +513,7 @@ int main(int argc, char **argv)
if( argv[i][0] == '-' && argv[i][1] == 'w' )
dump_extensions =1;
}
-
+
if( !quiet ) {
printf("\n(If you find progress and other non-error messages irritating, you can use -q to suppress them)\n");
if( !no_comp )
@@ -533,30 +533,30 @@ int main(int argc, char **argv)
localized_use_conf_dir();
}
strcpy(var_dir, "/var/lib/asterisk");
-
+
if( dump_extensions ) {
dumpfile = fopen("extensions.conf.aeldump","w");
if( !dumpfile ) {
printf("\n\nSorry, cannot open extensions.conf.aeldump for writing! Correct the situation and try again!\n\n");
exit(10);
}
-
+
}
FIRST_TIME = 1;
-
+
ael_external_load_module();
-
+
ast_log(4, "ael2_parse", __LINE__, "main", "%d contexts, %d extensions, %d priorities\n", conts, extens, priors);
if( dump_extensions && dumpfile ) {
-
+
for( lp = context_list; lp; lp = lp->next ) { /* print out any contexts that didn't have any
extensions in them */
if( lp->extension_count == 0 ) {
-
+
fprintf(dumpfile,"\n\n[%s]\n", lp->name);
-
+
for(n=lp->ignorepats;n;n=n->next) {
fprintf(dumpfile, "ignorepat => %s\n", n->name);
}
@@ -572,10 +572,10 @@ int main(int argc, char **argv)
}
}
}
-
+
if( dump_extensions && dumpfile )
fclose(dumpfile);
-
+
for( lp = context_list; lp; lp = lp2 ) { /* free the ast_context structs */
lp2 = lp->next;
lp->next = 0;
@@ -587,7 +587,7 @@ int main(int argc, char **argv)
free(lp);
}
-
+
return 0;
}
diff --git a/utils/astman.c b/utils/astman.c
index 0099a13f6..50e6a8901 100644
--- a/utils/astman.c
+++ b/utils/astman.c
@@ -55,8 +55,8 @@ ASTERISK_REGISTER_FILE()
/*
* 2005.05.27 - different versions of newt define the type of the buffer
- * for the 5th argument to newtEntry() as char ** or const char ** . To
- * let the code compile cleanly with -Werror, we cast it to void * through
+ * for the 5th argument to newtEntry() as char ** or const char ** . To
+ * let the code compile cleanly with -Werror, we cast it to void * through
* _NEWT_CAST.
*/
#define _NEWT_CAST (void *)
@@ -280,7 +280,7 @@ static int process_message(struct ast_mansession *s, struct message *m)
for (x=0;x<m->hdrcount;x++) {
printf("Header: %s\n", m->headers[x]);
}
-#endif
+#endif
return 0;
}
@@ -295,8 +295,8 @@ static void rebuild_channels(newtComponent c)
newtListboxClear(c);
AST_LIST_TRAVERSE(&chans, chan, list) {
snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
- if (strlen(chan->exten))
- snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
+ if (strlen(chan->exten))
+ snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
tmpn, chan->state,
chan->exten, chan->context, chan->priority);
else
@@ -313,8 +313,8 @@ static void rebuild_channels(newtComponent c)
static int has_input(struct ast_mansession *s)
{
int x;
- for (x=1;x<s->inlen;x++)
- if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
+ for (x=1;x<s->inlen;x++)
+ if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
return 1;
return 0;
}
@@ -337,7 +337,7 @@ static int get_input(struct ast_mansession *s, char *output)
s->inlen -= (x + 1);
return 1;
}
- }
+ }
if (s->inlen >= sizeof(s->inbuf) - 1) {
fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
s->inlen = 0;
@@ -489,7 +489,7 @@ static void try_status(void)
show_message("Status Failed Failed", get_header(m, "Message"));
}
}
-
+
static void try_hangup(newtComponent c)
{
@@ -506,7 +506,7 @@ static void try_hangup(newtComponent c)
show_message("Hangup Failed", get_header(m, "Message"));
}
}
-
+
}
static int get_user_input(char *msg, char *buf, int buflen)
@@ -528,7 +528,7 @@ static int get_user_input(char *msg, char *buf, int buflen)
newtFormAddComponents(form, inpfield, ok, cancel, NULL);
newtFormRun(form, &es);
strncpy(buf, input, buflen - 1);
- if (es.u.co == ok)
+ if (es.u.co == ok)
res = 0;
else
res = -1;
@@ -550,7 +550,7 @@ static void try_redirect(newtComponent c)
if (chan) {
strncpy(channame, chan->name, sizeof(channame) - 1);
snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame);
- if (get_user_input(tmp, dest, sizeof(dest)))
+ if (get_user_input(tmp, dest, sizeof(dest)))
return;
if ((context = strchr(dest, '@'))) {
*context = '\0';
@@ -566,7 +566,7 @@ static void try_redirect(newtComponent c)
show_message("Hangup Failed", get_header(m, "Message"));
}
}
-
+
}
static int manage_calls(char *host)
@@ -581,23 +581,23 @@ static int manage_calls(char *host)
/* Mark: If there's one thing you learn from this code, it is this...
Never, ever fly Air France. Their customer service is absolutely
- the worst. I've never heard the words "That's not my problem" as
+ the worst. I've never heard the words "That's not my problem" as
many times as I have from their staff -- It should, without doubt
- be their corporate motto if it isn't already. Don't bother giving
+ be their corporate motto if it isn't already. Don't bother giving
them business because you're just a pain in their side and they
will be sure to let you know the first time you speak to them.
-
+
If you ever want to make me happy just tell me that you, too, will
never fly Air France again either (in spite of their excellent
- cuisine).
-
+ cuisine).
+
Update by oej: The merger with KLM has transferred this
- behaviour to KLM as well.
+ behaviour to KLM as well.
Don't bother giving them business either...
Only if you want to travel randomly without luggage, you
might pick either of them.
-
+
*/
snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
newtCenteredWindow(74, 20, tmp);
@@ -610,7 +610,7 @@ static int manage_calls(char *host)
channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
newtListboxSetWidth(channels, 72);
-
+
show_doing("Getting Status", "Retrieving system status...");
try_status();
hide_doing();
@@ -654,18 +654,18 @@ static int manager_login(char *hostname)
char tmp[55];
struct hostent *hp;
int res = -1;
-
+
session.fd = socket(AF_INET, SOCK_STREAM, 0);
if (session.fd < 0) {
snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
show_message("Socket failed", tmp);
return -1;
}
-
+
snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
show_doing("Connecting....", tmp);
-
-
+
+
hp = gethostbyname(hostname);
if (!hp) {
snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
@@ -685,21 +685,21 @@ static int manager_login(char *hostname)
show_message("Connect Failed", tmp);
return -1;
}
-
+
hide_doing();
-
+
login = newtButton(5, 6, "Login");
cancel = newtButton(25, 6, "Cancel");
newtCenteredWindow(40, 10, "Asterisk Manager Login");
snprintf(tmp, sizeof(tmp), "Host: %s", hostname);
label = newtLabel(4,1, tmp);
-
+
ulabel = newtLabel(4,2,"Username:");
plabel = newtLabel(4,3,"Password:");
-
+
username = newtEntry(14, 2, "", 20, _NEWT_CAST &user, 0);
password = newtEntry(14, 3, "", 20, _NEWT_CAST &pass, NEWT_FLAG_HIDDEN);
-
+
form = newtForm(NULL, NULL, 0);
newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
newtFormRun(form, &es);
@@ -736,7 +736,7 @@ static int manager_login(char *hostname)
show_message("Login Failed", get_header(m, "Message"));
}
} else {
- manager_action("Login",
+ manager_action("Login",
"Username: %s\r\n"
"Secret: %s\r\n",
user, pass);
diff --git a/utils/check_expr.c b/utils/check_expr.c
index de668f691..f29363c91 100644
--- a/utils/check_expr.c
+++ b/utils/check_expr.c
@@ -145,7 +145,7 @@ void ast_log(int level, const char *file, int line, const char *function, const
{
va_list vars;
va_start(vars,fmt);
-
+
printf("LOG: lev:%d file:%s line:%d func: %s ",
level, file, line, function);
vprintf(fmt, vars);
@@ -162,7 +162,7 @@ void parse_file(const char *fname);
void __ast_register_file(const char *file);
void __ast_register_file(const char *file) { }
#if !defined(LOW_MEMORY)
-int ast_add_profile(const char *x, uint64_t scale) { return 0;}
+int ast_add_profile(const char *x, uint64_t scale) { return 0;}
#endif
int ast_atomic_fetchadd_int_slow(volatile int *p, int v)
{
@@ -207,7 +207,7 @@ unsigned int check_expr(char* buffer, char* error_report)
unsigned int warn_found = 0;
error_report[0] = 0;
-
+
for (cp = buffer; *cp; ++cp)
{
switch (*cp)
@@ -223,7 +223,7 @@ unsigned int check_expr(char* buffer, char* error_report)
global_lineno);
}
break;
-
+
case '>':
case '<':
case '!':
@@ -240,7 +240,7 @@ unsigned int check_expr(char* buffer, char* error_report)
++warn_found;
}
break;
-
+
case '|':
case '&':
case '=':
@@ -292,13 +292,13 @@ int check_eval(char *buffer, char *error_report)
if (*cp == '$' && *(cp+1) == '{') {
int brack_lev = 1;
char *xp= cp+2;
-
+
while (*xp) {
if (*xp == '{')
brack_lev++;
else if (*xp == '}')
brack_lev--;
-
+
if (brack_lev == 0)
break;
xp++;
@@ -306,7 +306,7 @@ int check_eval(char *buffer, char *error_report)
if (*xp == '}') {
char varname[200];
char *val;
-
+
strncpy(varname,cp+2, xp-cp-2);
varname[xp-cp-2] = 0;
cp = xp;
@@ -358,7 +358,7 @@ void parse_file(const char *fname)
int c1;
char last_char= 0;
char buffer[30000]; /* I sure hope no expr gets this big! */
-
+
if (!f) {
fprintf(stderr,"Couldn't open %s for reading... need an extensions.conf file to parse!\n",fname);
exit(20);
@@ -367,9 +367,9 @@ void parse_file(const char *fname)
fprintf(stderr,"Couldn't open 'expr2_log' file for writing... please fix and re-run!\n");
exit(21);
}
-
+
global_lineno = 1;
-
+
while ((c1 = fgetc(f)) != EOF) {
if (c1 == '\n')
global_lineno++;
@@ -380,7 +380,7 @@ void parse_file(const char *fname)
int bufcount = 0;
int retval;
char error_report[30000];
-
+
while ((c1 = fgetc(f)) != EOF) {
if (c1 == '[')
bracklev++;
@@ -392,7 +392,7 @@ void parse_file(const char *fname)
fclose(l);
printf("--- ERROR --- A newline in the middle of an expression at line %d!\n", global_lineno);
}
-
+
if (bracklev == 0)
break;
buffer[bufcount++] = c1;
@@ -404,18 +404,18 @@ void parse_file(const char *fname)
printf("--- ERROR --- EOF reached in middle of an expression at line %d!\n", global_lineno);
exit(22);
}
-
+
buffer[bufcount] = 0;
/* update stats */
global_expr_tot_size += bufcount;
global_expr_count++;
if (bufcount > global_expr_max_size)
global_expr_max_size = bufcount;
-
+
retval = check_expr(buffer, error_report); /* check_expr should bump the warning counter */
if (retval != 0) {
/* print error report */
- printf("Warning(s) at line %d, expression: $[%s]; see expr2_log file for details\n",
+ printf("Warning(s) at line %d, expression: $[%s]; see expr2_log file for details\n",
global_lineno, buffer);
fprintf(l, "%s", error_report);
}
@@ -436,7 +436,7 @@ void parse_file(const char *fname)
global_warn_count,
global_expr_max_size,
(global_expr_count) ? global_expr_tot_size/global_expr_count : 0);
-
+
fclose(f);
fclose(l);
}
@@ -446,7 +446,7 @@ int main(int argc,char **argv)
{
int argc1;
char *eq;
-
+
if (argc < 2) {
printf("check_expr -- a program to look thru extensions.conf files for $[...] expressions,\n");
printf(" and run them thru the parser, looking for problems\n");
@@ -458,7 +458,7 @@ int main(int argc,char **argv)
printf(" Note that messages about operators not being surrounded by spaces is merely to alert\n");
printf(" you to possible problems where you might be expecting those operators as part of a string.\n");
printf(" (to include operators in a string, wrap with double quotes!)\n");
-
+
exit(19);
}
global_varlist = 0;
@@ -470,7 +470,7 @@ int main(int argc,char **argv)
}
/* parse command args for x=y and set varz */
-
+
parse_file(argv[1]);
return 0;
}
diff --git a/utils/extconf.c b/utils/extconf.c
index 53347b41a..4eaea3c2d 100644
--- a/utils/extconf.c
+++ b/utils/extconf.c
@@ -1,4 +1,4 @@
-/*
+/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2006, Digium, Inc.
@@ -18,7 +18,7 @@
/*!
- * \file extconf
+ * \file
* A condensation of the pbx_config stuff, to read into exensions.conf, and provide an interface to the data there,
* for operations outside of asterisk. A huge, awful hack.
*
@@ -43,7 +43,7 @@
<support_level>extended</support_level>
***/
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
#include "asterisk.h"
#undef DEBUG_THREADS
@@ -86,7 +86,7 @@ void ast_verbose(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
#endif
#define AST_API_MODULE 1 /* gimme the inline defs! */
-struct ast_channel
+struct ast_channel
{
char x; /* basically empty! */
};
@@ -245,7 +245,7 @@ static void __attribute__((constructor)) init_empty_mutex(void)
static inline int __ast_pthread_mutex_init_attr(const char *filename, int lineno, const char *func,
const char *mutex_name, ast_mutex_t *t,
- pthread_mutexattr_t *attr)
+ pthread_mutexattr_t *attr)
{
#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
int canlog = strcmp(filename, "logger.c");
@@ -464,7 +464,7 @@ static inline int __ast_pthread_mutex_unlock(const char *filename, int lineno, c
}
if ((res = pthread_mutex_unlock(&t->mutex))) {
- __ast_mutex_logger("%s line %d (%s): Error releasing mutex: %s\n",
+ __ast_mutex_logger("%s line %d (%s): Error releasing mutex: %s\n",
filename, lineno, func, strerror(res));
DO_THREAD_CRASH;
}
@@ -505,7 +505,7 @@ static void __attribute__((constructor)) init_##mutex(void) \
ast_mutex_init(&mutex); \
}
#else /* !AST_MUTEX_INIT_W_CONSTRUCTORS */
-/* By default, use static initialization of mutexes. */
+/* By default, use static initialization of mutexes. */
#define __AST_MUTEX_DEFINE(scope, mutex) \
scope ast_mutex_t mutex = AST_MUTEX_INIT_VALUE
#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
@@ -618,7 +618,7 @@ AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
{
__asm __volatile (
" lock xaddl %0, %1 ; "
- : "+r" (v), /* 0 (result) */
+ : "+r" (v), /* 0 (result) */
"=m" (*p) /* 1 */
: "m" (*p)); /* 2 */
return (v);
@@ -864,7 +864,7 @@ char * attribute_malloc _ast_strdup(const char *str, const char *file, int linen
* message in the case that the allocation fails.
*
* ast_strndup(), unlike strndup(), can safely accept a NULL argument for the
- * string to duplicate. If a NULL argument is provided, ast_strdup will return
+ * string to duplicate. If a NULL argument is provided, ast_strdup will return
* NULL without generating any kind of error log message.
*
* The arguments and return value are the same as strndup()
@@ -1033,19 +1033,19 @@ static void LLB_ADD(char *str)
int siz = strlen(str);
if (rem < siz+1) {
lline_buffer = ast_realloc(lline_buffer, lline_buffer_size + CB_INCR + siz + 1);
- if (!lline_buffer)
+ if (!lline_buffer)
return;
lline_buffer_size += CB_INCR + siz + 1;
}
strcat(lline_buffer,str);
}
-static void CB_RESET(void )
-{
- comment_buffer[0] = 0;
+static void CB_RESET(void )
+{
+ comment_buffer[0] = 0;
lline_buffer[0] = 0;
}
-
+
/*! \brief Keep track of how many threads are currently trying to wait*() on
* a child process */
static unsigned int safe_system_level = 0;
@@ -1108,7 +1108,7 @@ int ast_safe_system(const char *s)
pid = fork();
#else
pid = vfork();
-#endif
+#endif
if (pid == 0) {
#ifdef HAVE_WORKING_FORK
@@ -1124,7 +1124,7 @@ int ast_safe_system(const char *s)
if (res > -1) {
res = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
break;
- } else if (errno != EINTR)
+ } else if (errno != EINTR)
break;
}
} else {
@@ -1141,7 +1141,7 @@ int ast_safe_system(const char *s)
}
static struct ast_comment *ALLOC_COMMENT(const char *buffer)
-{
+{
struct ast_comment *x = ast_calloc(1,sizeof(struct ast_comment)+strlen(buffer)+1);
strcpy(x->cmt, buffer);
return x;
@@ -1164,7 +1164,7 @@ static struct ast_config_engine *config_engine_list;
struct ast_category {
char name[80];
int ignored; /*!< do not let user of the config see this category */
- int include_level;
+ int include_level;
char *file; /*!< the file name from whence this declaration was read */
int lineno;
struct ast_comment *precomments;
@@ -1257,7 +1257,7 @@ char *ast_trim_blanks(char *str),
if (work) {
work += strlen(work) - 1;
- /* It's tempting to only want to erase after we exit this loop,
+ /* It's tempting to only want to erase after we exit this loop,
but since ast_trim_blanks *could* receive a constant string
(which we presumably wouldn't have to touch), we shouldn't
actually set anything unless we must, and it's easier just
@@ -1286,7 +1286,7 @@ char *ast_strip(char *s),
if (s)
ast_trim_blanks(s);
return s;
-}
+}
)
@@ -1319,15 +1319,15 @@ void localized_ast_include_rename(struct ast_config *conf, const char *from_file
static struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename);
-static struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
+static struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
{
struct ast_variable *variable;
- int name_len = strlen(name) + 1;
+ int name_len = strlen(name) + 1;
if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + strlen(filename) + 1 + sizeof(*variable)))) {
variable->name = variable->stuff;
- variable->value = variable->stuff + name_len;
- variable->file = variable->value + strlen(value) + 1;
+ variable->value = variable->stuff + name_len;
+ variable->file = variable->value + strlen(value) + 1;
strcpy(variable->name,name);
strcpy(variable->value,value);
strcpy(variable->file,filename);
@@ -1339,11 +1339,11 @@ static struct ast_variable *ast_variable_new(const char *name, const char *value
static struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size)
{
/* a file should be included ONCE. Otherwise, if one of the instances is changed,
- then all be changed. -- how do we know to include it? -- Handling modified
+ then all be changed. -- how do we know to include it? -- Handling modified
instances is possible, I'd have
to create a new master for each instance. */
struct ast_config_include *inc;
-
+
inc = ast_include_find(conf, included_file);
if (inc)
{
@@ -1352,7 +1352,7 @@ static struct ast_config_include *ast_include_new(struct ast_config *conf, const
ast_log(LOG_WARNING,"'%s', line %d: Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name);
} else
*real_included_file_name = 0;
-
+
inc = ast_calloc(1,sizeof(struct ast_config_include));
inc->include_location_file = ast_strdup(from_file);
inc->include_location_lineno = from_lineno;
@@ -1360,15 +1360,15 @@ static struct ast_config_include *ast_include_new(struct ast_config *conf, const
inc->included_file = ast_strdup(real_included_file_name);
else
inc->included_file = ast_strdup(included_file);
-
+
inc->exec = is_exec;
if (is_exec)
inc->exec_file = ast_strdup(exec_file);
-
+
/* attach this new struct to the conf struct */
inc->next = conf->includes;
conf->includes = inc;
-
+
return inc;
}
@@ -1377,13 +1377,13 @@ void localized_ast_include_rename(struct ast_config *conf, const char *from_file
struct ast_config_include *incl;
struct ast_category *cat;
struct ast_variable *v;
-
+
int from_len = strlen(from_file);
int to_len = strlen(to_file);
-
+
if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
return;
-
+
/* the manager code allows you to read in one config file, then
write it back out under a different name. But, the new arrangement
ties output lines to the file name. So, before you try to write
@@ -1392,7 +1392,7 @@ void localized_ast_include_rename(struct ast_config *conf, const char *from_file
*/
/* file names are on categories, includes (of course), and on variables. So,
traverse all this and swap names */
-
+
for (incl = conf->includes; incl; incl=incl->next) {
if (strcmp(incl->include_location_file,from_file) == 0) {
if (from_len >= to_len)
@@ -1523,7 +1523,7 @@ static struct ast_variable *variable_clone(const struct ast_variable *old)
return new;
}
-
+
static void ast_variables_destroy(struct ast_variable *v)
{
struct ast_variable *vn;
@@ -1538,7 +1538,7 @@ static void ast_variables_destroy(struct ast_variable *v)
static void ast_includes_destroy(struct ast_config_include *incls)
{
struct ast_config_include *incl,*inclnext;
-
+
for (incl=incls; incl; incl = inclnext) {
inclnext = incl->next;
if (incl->include_location_file)
@@ -1559,7 +1559,7 @@ static void ast_config_destroy(struct ast_config *cfg)
return;
ast_includes_destroy(cfg->includes);
-
+
cat = cfg->root;
while (cat) {
ast_variables_destroy(cat->root);
@@ -1695,7 +1695,7 @@ extern int ast_language_is_prefix;
*/
#define AST_RWLIST_RDLOCK(head) \
ast_rwlock_rdlock(&(head)->lock)
-
+
/*!
\brief Attempts to unlock a read/write based list.
\param head This is a pointer to the list head structure
@@ -1969,7 +1969,7 @@ struct { \
}
#define AST_RWLIST_ENTRY AST_LIST_ENTRY
-
+
/*!
\brief Returns the first entry contained in a list.
\param head This is a pointer to the list head structure
@@ -2381,7 +2381,7 @@ struct ast_switch {
AST_LIST_ENTRY(ast_switch) list;
const char *name; /*!< Name of the switch */
const char *description; /*!< Description of the switch */
-
+
ast_switch_f *exists;
ast_switch_f *canmatch;
ast_switch_f *exec;
@@ -2455,7 +2455,7 @@ static const char *ast_var_name(const struct ast_var_t *var)
}
/* experiment 1: see if it's easier just to use existing config code
- * to read in the extensions.conf file. In this scenario,
+ * to read in the extensions.conf file. In this scenario,
I have to rip/copy code from other modules, because they
are staticly declared as-is. A solution would be to move
the ripped code to another location and make them available
@@ -2467,7 +2467,7 @@ static void ast_log(int level, const char *file, int line, const char *function,
{
va_list vars;
va_start(vars,fmt);
-
+
printf("LOG: lev:%d file:%s line:%d func: %s ",
level, file, line, function);
vprintf(fmt, vars);
@@ -2479,7 +2479,7 @@ void __attribute__((format(printf, 1, 2))) ast_verbose(const char *fmt, ...)
{
va_list vars;
va_start(vars,fmt);
-
+
printf("VERBOSE: ");
vprintf(fmt, vars);
fflush(stdout);
@@ -2697,7 +2697,7 @@ struct ast_state_cb {
/*! \brief Structure for dial plan hints
\note Hints are pointers from an extension in the dialplan to one or
- more devices (tech/name)
+ more devices (tech/name)
- See \ref AstExtState
*/
struct ast_hint {
@@ -2725,7 +2725,7 @@ AST_LIST_HEAD(store_hints, store_hint);
#define STATUS_SUCCESS 5
static struct ast_var_t *ast_var_assign(const char *name, const char *value)
-{
+{
struct ast_var_t *var;
int name_len = strlen(name) + 1;
int value_len = strlen(value) + 1;
@@ -2737,10 +2737,10 @@ static struct ast_var_t *ast_var_assign(const char *name, const char *value)
ast_copy_string(var->name, name, name_len);
var->value = var->name + name_len;
ast_copy_string(var->value, value, value_len);
-
+
return var;
-}
-
+}
+
static void ast_var_delete(struct ast_var_t *var)
{
free(var);
@@ -3017,7 +3017,7 @@ static void null_datad(void *foo)
}
/*! \brief Find realtime engine for realtime family */
-static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz)
+static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz)
{
struct ast_config_engine *eng, *ret = NULL;
struct ast_config_map *map;
@@ -3040,12 +3040,12 @@ static struct ast_config_engine *find_engine(const char *family, char *database,
ret = eng;
}
}
-
-
+
+
/* if we found a mapping, but the engine is not available, then issue a warning */
if (map && !ret)
ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
-
+
return ret;
}
@@ -3120,7 +3120,7 @@ static void ast_category_destroy(struct ast_category *cat)
ast_variables_destroy(cat->root);
if (cat->file)
free(cat->file);
-
+
free(cat);
}
@@ -3137,7 +3137,7 @@ static struct ast_config *ast_config_internal_load(const char *filename, struct
char db[256];
char table[256];
struct ast_config_engine *loader = &text_file_engine;
- struct ast_config *result;
+ struct ast_config *result;
if (cfg->include_level == cfg->max_include_level) {
ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
@@ -3165,7 +3165,7 @@ static struct ast_config *ast_config_internal_load(const char *filename, struct
}
result = loader->load_func(db, table, filename, cfg, withcomments, suggested_incl_file);
- /* silence is golden
+ /* silence is golden
ast_log(LOG_WARNING, "finished internal loading file %s level=%d\n", filename, cfg->include_level);
*/
@@ -3204,7 +3204,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
return -1;
}
(*cat)->lineno = lineno;
-
+
/* add comments */
if (withcomments && comment_buffer && comment_buffer[0] ) {
newcat->precomments = ALLOC_COMMENT(comment_buffer);
@@ -3214,7 +3214,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
}
if( withcomments )
CB_RESET();
-
+
/* If there are options or categories to inherit from, process them now */
if (c) {
if (!(cur = strchr(c, ')'))) {
@@ -3241,7 +3241,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
}
} else {
struct ast_category *base;
-
+
base = category_get(cfg, cur, 1);
if (!base) {
ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
@@ -3264,7 +3264,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
c = ast_skip_blanks(c + 1);
if (!*c)
c = NULL;
- } else
+ } else
c = NULL;
do_include = !strcasecmp(cur, "include");
if(!do_include)
@@ -3279,7 +3279,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
if (c) {
char *cur2;
char real_inclusion_name[256];
-
+
/* Strip off leading and trailing "'s and <>'s */
while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
/* Get rid of leading mess */
@@ -3294,7 +3294,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
}
/* #exec </path/to/executable>
We create a tmp file, then we #include it, then we delete it. */
- if (do_exec) {
+ if (do_exec) {
snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d.%ld", (int)time(NULL), (long)pthread_self());
snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
ast_safe_system(cmd);
@@ -3303,26 +3303,26 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
exec_file[0] = '\0';
/* A #include */
/* ast_log(LOG_WARNING, "Reading in included file %s withcomments=%d\n", cur, withcomments); */
-
+
/* record this inclusion */
ast_include_new(cfg, configfile, cur, do_exec, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
-
+
do_include = ast_config_internal_load(cur, cfg, withcomments, real_inclusion_name) ? 1 : 0;
if(!ast_strlen_zero(exec_file))
unlink(exec_file);
if(!do_include)
return 0;
/* ast_log(LOG_WARNING, "Done reading in included file %s withcomments=%d\n", cur, withcomments); */
-
+
} else {
- ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
+ ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
do_exec ? "exec" : "include",
do_exec ? "/path/to/executable" : "filename",
lineno,
configfile);
}
}
- else
+ else
ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
} else {
/* Just a line (variable = value) */
@@ -3356,7 +3356,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
}
if( withcomments )
CB_RESET();
-
+
} else {
return -1;
}
@@ -3394,7 +3394,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
struct ast_category *cat = NULL;
int count = 0;
struct stat statbuf;
-
+
cat = ast_config_get_current_category(cfg);
if (filename[0] == '/') {
@@ -3409,7 +3409,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
if (withcomments && cfg && cfg->include_level < 2 ) {
CB_INIT();
}
-
+
#ifdef AST_INCLUDE_GLOB
{
int glob_ret;
@@ -3460,17 +3460,17 @@ static struct ast_config *config_text_file_load(const char *database, const char
while(!feof(f)) {
lineno++;
if (fgets(buf, sizeof(buf), f)) {
- if ( withcomments ) {
+ if ( withcomments ) {
CB_ADD(lline_buffer); /* add the current lline buffer to the comment buffer */
lline_buffer[0] = 0; /* erase the lline buffer */
}
-
+
new_buf = buf;
- if (comment)
+ if (comment)
process_buf = NULL;
else
process_buf = buf;
-
+
while ((comment_p = strchr(new_buf, COMMENT_META))) {
if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
/* Yuck, gotta memmove */
@@ -3502,7 +3502,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
CB_ADD(";");
CB_ADD_LEN(oldptr+1,new_buf-oldptr-1);
}
-
+
memmove(oldptr, new_buf, strlen(new_buf) + 1);
new_buf = oldptr;
} else
@@ -3510,12 +3510,12 @@ static struct ast_config *config_text_file_load(const char *database, const char
}
} else {
if (!comment) {
- /* If ; is found, and we are not nested in a comment,
+ /* If ; is found, and we are not nested in a comment,
we immediately stop all comment processing */
if ( withcomments ) {
LLB_ADD(comment_p);
}
- *comment_p = '\0';
+ *comment_p = '\0';
new_buf = comment_p;
} else
new_buf = comment_p + 1;
@@ -3525,7 +3525,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
{
CB_ADD(buf); /* the whole line is a comment, store it */
}
-
+
if (process_buf) {
char *stripped_process_buf = ast_strip(process_buf);
if (!ast_strlen_zero(stripped_process_buf)) {
@@ -3537,7 +3537,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
}
}
}
- fclose(f);
+ fclose(f);
} while(0);
if (comment) {
ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment]);
@@ -3551,12 +3551,12 @@ static struct ast_config *config_text_file_load(const char *database, const char
}
#endif
if (cfg && cfg->include_level == 1 && withcomments && comment_buffer) {
- if (comment_buffer) {
+ if (comment_buffer) {
free(comment_buffer);
free(lline_buffer);
- comment_buffer=0;
- lline_buffer=0;
- comment_buffer_size=0;
+ comment_buffer=0;
+ lline_buffer=0;
+ comment_buffer_size=0;
lline_buffer_size=0;
}
}
@@ -3569,7 +3569,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
static struct ast_config *ast_config_new(void) ;
-static struct ast_config *ast_config_new(void)
+static struct ast_config *ast_config_new(void)
{
struct ast_config *config;
@@ -3622,7 +3622,7 @@ static struct ast_category *next_available_category(struct ast_category *cat)
}
static char *ast_category_browse(struct ast_config *config, const char *prev)
-{
+{
struct ast_category *cat = NULL;
if (prev && config->last_browse && (config->last_browse->name == prev))
@@ -3645,7 +3645,7 @@ static char *ast_category_browse(struct ast_config *config, const char *prev)
}
}
}
-
+
if (cat)
cat = next_available_category(cat);
@@ -3666,21 +3666,21 @@ void ast_config_set_current_category(struct ast_config *cfg, const struct ast_ca
/* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
- be shocked and mystified as to why things are not showing up in the files!
-
+ be shocked and mystified as to why things are not showing up in the files!
+
Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
and line number are stored for each include, plus the name of the file included, so that these statements may be
- included in the output files on a file_save operation.
-
+ included in the output files on a file_save operation.
+
The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
and a header gets added.
-
+
vars and category heads are output in the order they are stored in the config file. So, if the software
shuffles these at all, then the placement of #include directives might get a little mixed up, because the
file/lineno data probably won't get changed.
-
+
*/
static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
@@ -3689,7 +3689,7 @@ static void gen_header(FILE *f1, const char *configfile, const char *fn, const c
time_t t;
time(&t);
ast_copy_string(date, ctime(&t), sizeof(date));
-
+
fprintf(f1, ";!\n");
fprintf(f1, ";! Automatically generated configuration file\n");
if (strcmp(configfile, fn))
@@ -3708,7 +3708,7 @@ static void set_fn(char *fn, int fn_size, const char *file, const char *configfi
ast_copy_string(fn, configfile, fn_size);
else
snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
- } else if (file[0] == '/')
+ } else if (file[0] == '/')
ast_copy_string(fn, file, fn_size);
else
snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
@@ -3725,20 +3725,20 @@ int localized_config_text_file_save(const char *configfile, const struct ast_con
struct ast_comment *cmt;
struct ast_config_include *incl;
int blanklines = 0;
-
+
/* reset all the output flags, in case this isn't our first time saving this data */
-
+
for (incl=cfg->includes; incl; incl = incl->next)
incl->output = 0;
-
+
/* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
are all truncated to zero bytes and have that nice header*/
-
+
for (incl=cfg->includes; incl; incl = incl->next)
{
if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/
FILE *f1;
-
+
set_fn(fn, sizeof(fn), incl->included_file, configfile); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
f1 = fopen(fn,"w");
if (f1) {
@@ -3749,24 +3749,24 @@ int localized_config_text_file_save(const char *configfile, const struct ast_con
}
}
}
-
+
set_fn(fn, sizeof(fn), 0, configfile); /* just set fn to absolute ver of configfile */
-#ifdef __CYGWIN__
+#ifdef __CYGWIN__
if ((f = fopen(fn, "w+"))) {
#else
if ((f = fopen(fn, "w"))) {
-#endif
+#endif
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Saving '%s': ", fn);
gen_header(f, configfile, fn, generator);
cat = cfg->root;
fclose(f);
-
+
/* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
- /* since each var, cat, and associated comments can come from any file, we have to be
+ /* since each var, cat, and associated comments can come from any file, we have to be
mobile, and open each file, print, and close it on an entry-by-entry basis */
-
+
while(cat) {
set_fn(fn, sizeof(fn), cat->file, configfile);
f = fopen(fn, "a");
@@ -3775,7 +3775,7 @@ int localized_config_text_file_save(const char *configfile, const struct ast_con
ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
return -1;
}
-
+
/* dump any includes that happen before this category header */
for (incl=cfg->includes; incl; incl = incl->next) {
if (strcmp(incl->include_location_file, cat->file) == 0){
@@ -3788,7 +3788,7 @@ int localized_config_text_file_save(const char *configfile, const struct ast_con
}
}
}
-
+
/* Dump section with any appropriate comment */
for (cmt = cat->precomments; cmt; cmt=cmt->next) {
if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
@@ -3803,7 +3803,7 @@ int localized_config_text_file_save(const char *configfile, const struct ast_con
if (!cat->sameline)
fprintf(f,"\n");
fclose(f);
-
+
var = cat->root;
while(var) {
set_fn(fn, sizeof(fn), var->file, configfile);
@@ -3813,7 +3813,7 @@ int localized_config_text_file_save(const char *configfile, const struct ast_con
ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
return -1;
}
-
+
/* dump any includes that happen before this category header */
for (incl=cfg->includes; incl; incl = incl->next) {
if (strcmp(incl->include_location_file, var->file) == 0){
@@ -3826,24 +3826,24 @@ int localized_config_text_file_save(const char *configfile, const struct ast_con
}
}
}
-
+
for (cmt = var->precomments; cmt; cmt=cmt->next) {
if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
fprintf(f,"%s", cmt->cmt);
}
- if (var->sameline)
+ if (var->sameline)
fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
- else
+ else
fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
if (var->blanklines) {
blanklines = var->blanklines;
while (blanklines--)
fprintf(f, "\n");
}
-
+
fclose(f);
-
-
+
+
var = var->next;
}
cat = cat->next;
@@ -3860,7 +3860,7 @@ int localized_config_text_file_save(const char *configfile, const struct ast_con
/* Now, for files with trailing #include/#exec statements,
we have to make sure every entry is output */
-
+
for (incl=cfg->includes; incl; incl = incl->next) {
if (!incl->output) {
/* open the respective file */
@@ -3871,7 +3871,7 @@ int localized_config_text_file_save(const char *configfile, const struct ast_con
ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
return -1;
}
-
+
/* output the respective include */
if (incl->exec)
fprintf(f,"#exec \"%s\"\n", incl->exec_file);
@@ -3881,7 +3881,7 @@ int localized_config_text_file_save(const char *configfile, const struct ast_con
incl->output = 1;
}
}
-
+
return 0;
}
@@ -4207,7 +4207,7 @@ static int ext_cmp1(const char **p)
break;
}
/* locate end of set */
- end = strchr(*p, ']');
+ end = strchr(*p, ']');
if (end == NULL) {
ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
@@ -4566,24 +4566,24 @@ static inline int include_valid(struct ast_include *i)
static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
- struct ast_context *bypass,
+ struct ast_context *bypass,
struct pbx_find_info *q,
- const char *context,
- const char *exten,
+ const char *context,
+ const char *exten,
int priority,
- const char *label,
- const char *callerid,
+ const char *label,
+ const char *callerid,
enum ext_match_t action);
static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
- struct ast_context *bypass,
+ struct ast_context *bypass,
struct pbx_find_info *q,
- const char *context,
- const char *exten,
+ const char *context,
+ const char *exten,
int priority,
- const char *label,
- const char *callerid,
+ const char *label,
+ const char *callerid,
enum ext_match_t action)
{
int x;
@@ -4667,7 +4667,7 @@ static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
continue;
}
/* No need to Substitute variables now; we shouldn't be here if there's any */
-
+
/* equivalent of extension_match_core() at the switch level */
if (action == E_CANMATCH)
aswf = asw->canmatch;
@@ -4701,20 +4701,20 @@ static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
struct ast_exten *localized_find_extension(struct ast_context *bypass,
struct pbx_find_info *q,
- const char *context,
- const char *exten,
+ const char *context,
+ const char *exten,
int priority,
- const char *label,
- const char *callerid,
+ const char *label,
+ const char *callerid,
enum ext_match_t action);
struct ast_exten *localized_find_extension(struct ast_context *bypass,
struct pbx_find_info *q,
- const char *context,
- const char *exten,
+ const char *context,
+ const char *exten,
int priority,
- const char *label,
- const char *callerid,
+ const char *label,
+ const char *callerid,
enum ext_match_t action)
{
return pbx_find_extension(NULL, bypass, q, context, exten, priority, label, callerid, action);
@@ -5328,7 +5328,7 @@ static void pbx_retrieve_variable(struct ast_channel *c, const char *var, char *
int offset, length;
int i, need_substring;
struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
-
+
/*
* Make a copy of var because parse_variable_name() modifies the string.
* Then if called directly, we might need to run substring() on the result;
@@ -5336,7 +5336,7 @@ static void pbx_retrieve_variable(struct ast_channel *c, const char *var, char *
*/
tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
-
+
/*
* Look first into predefined variables, then into variable lists.
* Variable 's' points to the result, according to the following rules:
@@ -5357,7 +5357,7 @@ static void pbx_retrieve_variable(struct ast_channel *c, const char *var, char *
if (!strcmp(var, "EPOCH")) {
snprintf(workspace, workspacelen, "%u",(int)time(NULL));
}
-
+
s = workspace;
}
/* if not found, look into chanvars or global vars */
@@ -5600,11 +5600,11 @@ static int pbx_load_config(const char *config_file)
autofallthrough_config = ast_true(aft);
clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars"));
- if ((cxt = ast_variable_retrieve(cfg, "general", "userscontext")))
+ if ((cxt = ast_variable_retrieve(cfg, "general", "userscontext")))
ast_copy_string(userscontext, cxt, sizeof(userscontext));
else
ast_copy_string(userscontext, "default", sizeof(userscontext));
-
+
for (v = ast_variable_browse(cfg, "globals"); v; v = v->next) {
memset(realvalue, 0, sizeof(realvalue));
pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
@@ -5905,7 +5905,7 @@ int localized_pbx_load_module(void)
printf("Context: %s\n", con->name);
}
printf("=========\n");
-
+
return 0;
}
diff --git a/utils/frame.c b/utils/frame.c
index 62dc5f9f0..6752f4a7d 100644
--- a/utils/frame.c
+++ b/utils/frame.c
@@ -7,8 +7,8 @@
* Name: frame.c
* Version: see static char *standardversion, below.
* Author: Mark Roberts <mark@manumark.de>
- * Michael Labuschke <michael@labuschke.de> sys_errlist fixes
- *
+ * Michael Labuschke <michael@labuschke.de> sys_errlist fixes
+ *
****************************************************************************/
/****************************************************************************
* These are useful functions that all DSP programs might find handy
@@ -850,7 +850,7 @@ void parseargs( int argcount, char *args[], int fileswitch)
}
/*-------------------------------------------------*
- * Set samplefrequency, width, wavout,
+ * Set samplefrequency, width, wavout,
*-------------------------------------------------*/
parseintarg( argcount, args, "f", &samplefrequency);
wavout = parseswitcharg( argcount, args, "h");
diff --git a/utils/frame.h b/utils/frame.h
index a07c605ec..4bbc2199b 100644
--- a/utils/frame.h
+++ b/utils/frame.h
@@ -102,7 +102,7 @@ extern int getremainingfilelength( FILE *anyin, long *result);
void readpkheader( FILE *anyin);
/* -----------------------------------------------------------------------
- Read a .WAV header from 'anyin'.
+ Read a .WAV header from 'anyin'.
If it is recognised, the data is used.
Otherwise, we assume it's PCM-data and ignore the header.
The global variable 'iswav' is set on success, otherwise cleared.
diff --git a/utils/muted.c b/utils/muted.c
index a78312e28..ee1de7f54 100644
--- a/utils/muted.c
+++ b/utils/muted.c
@@ -5,7 +5,7 @@
*
* Mark Spencer <markster@digium.com>
*
- * Updated for Mac OSX CoreAudio
+ * Updated for Mac OSX CoreAudio
* by Josh Roberson <josh@asteriasgi.com>
*
* See http://www.asterisk.org for more information about
@@ -25,7 +25,7 @@
*
* \author Mark Spencer <markster@digium.com>
*
- * Updated for Mac OSX CoreAudio
+ * Updated for Mac OSX CoreAudio
* \arg Josh Roberson <josh@asteriasgi.com>
*
* \note Specially written for Malcolm Davenport, but I think I'll use it too
@@ -51,7 +51,7 @@
#include "asterisk/autoconfig.h"
#ifdef __Darwin__
-#include <CoreAudio/AudioHardware.h>
+#include <CoreAudio/AudioHardware.h>
#include <sys/types.h>
#include <pwd.h>
#include <sys/stat.h>
@@ -117,7 +117,7 @@ static void add_channel(char *tech, char *location)
chan->next = channels;
channels = chan;
}
-
+
}
static int load_config(void)
@@ -176,7 +176,7 @@ static int load_config(void)
} else if (!strcasecmp(buf, "mutelevel")) {
if (val && (sscanf(val, "%3d", &x) == 1) && (x > -1) && (x < 101)) {
mutelevel = x;
- } else
+ } else
fprintf(stderr, "mutelevel must be a number from 0 (most muted) to 100 (no mute) at line %d\n", lineno);
} else if (!strcasecmp(buf, "channel")) {
if (val && strlen(val)) {
@@ -199,7 +199,7 @@ static int load_config(void)
fprintf(stderr, "no 'host' specification in config file\n");
else if (!strlen(user))
fprintf(stderr, "no 'user' specification in config file\n");
- else if (!channels)
+ else if (!channels)
fprintf(stderr, "no 'channel' specifications in config file\n");
else
return 0;
@@ -286,7 +286,7 @@ static int login_asterisk(void)
fprintf(stderr, "disconnected (1)\n");
return -1;
}
- fprintf(astf,
+ fprintf(astf,
"Action: Login\r\n"
"Username: %s\r\n"
"Secret: %s\r\n\r\n", user, pass);
@@ -304,7 +304,7 @@ static int login_asterisk(void)
fprintf(stderr, "disconnected (3)\n");
return -1;
}
- fprintf(astf,
+ fprintf(astf,
"Action: Status\r\n\r\n");
if (!(welcome = get_line())) {
fprintf(stderr, "disconnected (4)\n");
@@ -404,7 +404,7 @@ static int setvol(float vol)
{
#ifndef __Darwin__
if (ioctl(mixfd, MIXER_WRITE(mixchan), &vol)) {
-#else
+#else
float volumeL = vol;
float volumeR = vol;
OSStatus err;
@@ -462,7 +462,7 @@ static float mutedlevel(float orig, float level)
master = level * master / 100.0;
return master;
#endif
-
+
}
static void mute(void)
@@ -479,7 +479,7 @@ static void mute(void)
vol = getvol();
oldvol = vol;
if (smoothfade)
-#ifdef __Darwin__
+#ifdef __Darwin__
start = mutelevel;
#else
start = 100;
@@ -587,7 +587,7 @@ static void append_sub(struct channel *chan, char *name)
struct subchannel *sub;
sub = chan->subs;
while(sub) {
- if (!strcasecmp(sub->name, name))
+ if (!strcasecmp(sub->name, name))
return;
sub = sub->next;
}
@@ -650,7 +650,7 @@ static int wait_event(void)
strncpy(oldname, resp + strlen("Oldname: "), sizeof(oldname) - 1);
}
if (strlen(channel)) {
- if (!strcasecmp(event, "Hangup"))
+ if (!strcasecmp(event, "Hangup"))
hangup_chan(channel);
else
offhook_chan(channel);
@@ -711,7 +711,7 @@ int main(int argc, char *argv[])
exit(1);
}
if (login_asterisk()) {
-#ifndef __Darwin__
+#ifndef __Darwin__
close(mixfd);
#endif
fclose(astf);
diff --git a/utils/smsq.c b/utils/smsq.c
index 21ab80d04..051609797 100644
--- a/utils/smsq.c
+++ b/utils/smsq.c
@@ -41,7 +41,7 @@
/*!
* \brief reads next USC character from null terminated UTF-8 string and advanced pointer
* for non valid UTF-8 sequences.
- * \return character as is Does \b NOT advance pointer for null termination
+ * \return character as is Does \b NOT advance pointer for null termination
*/
static int utf8decode (unsigned char **pp)
{
@@ -92,12 +92,12 @@ static int utf8decode (unsigned char **pp)
return *p; /* not sensible */
}
-/*!
+/*!
* \brief check for any queued messages in specific queue (queue="" means any queue)
* \param dir,queue,subaddress,channel,callerid,wait,delay,retries,concurrent
* \retval 0 if nothing queued
* \retval 1 if queued and outgoing set up OK
- * \retval 2 of outgoing exists
+ * \retval 2 of outgoing exists
*/
static char txqcheck (char *dir, char *queue, char subaddress, char *channel, char *callerid, int wait, int delay, int retries, int concurrent)
{
@@ -193,7 +193,7 @@ static char txqcheck (char *dir, char *queue, char subaddress, char *channel, ch
return 2;
}
-/*!
+/*!
* \brief Process received queue entries
* Run through a process, setting environment variables
*/
diff --git a/utils/streamplayer.c b/utils/streamplayer.c
index 6c70b0840..809bd64b8 100644
--- a/utils/streamplayer.c
+++ b/utils/streamplayer.c
@@ -19,7 +19,7 @@
/*!
* \file
* \author Russell Bryant <russell@digium.com>
- *
+ *
* \brief A utility for reading from a raw TCP stream
*
* This application is intended for use when a raw TCP stream is desired to be
@@ -84,33 +84,33 @@ int main(int argc, char *argv[])
}
memset(&sin, 0, sizeof(sin));
-
+
sin.sin_family = AF_INET;
sin.sin_port = htons(atoi(argv[2]));
memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
-
+
s = socket(AF_INET, SOCK_STREAM, 0);
-
+
if (s < 0) {
fprintf(stderr, "Unable to allocate socket!\n");
exit(1);
- }
+ }
res = connect(s, (struct sockaddr *)&sin, sizeof(sin));
-
+
if (res) {
fprintf(stderr, "Unable to connect to host!\n");
close(s);
- exit(1);
+ exit(1);
}
while (1) {
res = read(s, buf, sizeof(buf));
if (res < 1)
- break;
-
- memset(&tv, 0, sizeof(tv));
+ break;
+
+ memset(&tv, 0, sizeof(tv));
FD_ZERO(&wfds);
FD_SET(1, &wfds);