Compare commits
172 Commits
Author | SHA1 | Date |
---|---|---|
Asterisk Development Team | 1d64cb99d2 | |
Mike Bradeen | f8dfbaf225 | |
Sean Bright | 6e50550d28 | |
Asterisk Development Team | e7f0440032 | |
Asterisk Development Team | 460d13e916 | |
Asterisk Development Team | 93813c9dca | |
George Joseph | ceda5a9859 | |
Sean Bright | e5c5cd6e25 | |
Naveen Albert | ede67a99be | |
Sean Bright | 827222d607 | |
Nick French | 200dc7d0e8 | |
Mike Bradeen | 5c11d7adea | |
cmaj | 5b0e3444c3 | |
Mike Bradeen | 2308afed8e | |
Mike Bradeen | 98742388b6 | |
Mike Bradeen | 37e558f6ef | |
Sean Bright | aeb16aa7d8 | |
Sean Bright | aef0c0ce0e | |
Mike Bradeen | 58636a6ea6 | |
Sean Bright | 96d9ad51ac | |
Naveen Albert | 88b2c741ca | |
Mike Bradeen | 70856e865f | |
Naveen Albert | 8a45cd7af4 | |
sungtae kim | f99849f8d5 | |
Sean Bright | 56051d1ac5 | |
Naveen Albert | a1da8042d1 | |
Sean Bright | ef16eaee36 | |
George Joseph | 2f5aece0c9 | |
Alexei Gradinari | e86d5d7fda | |
Igor Goncharovsky | 3526441e41 | |
George Joseph | 4710f37ef6 | |
George Joseph | 62ca063fca | |
Naveen Albert | d33bd6d67a | |
Naveen Albert | e06fe8e344 | |
Naveen Albert | 68e345286b | |
Naveen Albert | 3b3fef2347 | |
Naveen Albert | 7b8f7428da | |
George Joseph | 24102ba236 | |
Boris P. Korzun | edc90c96ac | |
Holger Hans Peter Freyther | 3d9b9a2b16 | |
George Joseph | d454801c2d | |
Naveen Albert | cc8d9b947b | |
Naveen Albert | c7598ee947 | |
Ben Ford | 881faf544f | |
Naveen Albert | 20d4775d0a | |
Naveen Albert | cbb1fd2cb9 | |
Igor Goncharovsky | 115a1b4f0a | |
Peter Fern | 58404b5c22 | |
Naveen Albert | 36bea9ad33 | |
Asterisk Development Team | fefc236e7c | |
Alexandre Fournier | 01b3962201 | |
Joshua C. Colp | b6855755ce | |
Naveen Albert | 2f9cdfbc50 | |
Michael Kuron | 5c114dcb4a | |
Michael Kuron | fee9012fe1 | |
Joshua C. Colp | 564349ff5d | |
Naveen Albert | b9c031c1f8 | |
Marcel Wagner | 58534b309f | |
Naveen Albert | 531eacd6c9 | |
Naveen Albert | b365ea8601 | |
Naveen Albert | 0d6003fa9a | |
Marcel Wagner | b83af13f65 | |
Naveen Albert | 80e6205bb0 | |
Naveen Albert | 406143ae61 | |
Naveen Albert | 83eb113e0f | |
Naveen Albert | b90e57758b | |
Naveen Albert | 52c7d3ed07 | |
Naveen Albert | a4bcdce1db | |
Naveen Albert | 691178c48e | |
Ben Ford | d476994768 | |
George Joseph | 7684c9e907 | |
Mike Bradeen | 81f10e847e | |
Mike Bradeen | eb1d7ab53c | |
Naveen Albert | c7df5ee7c1 | |
Naveen Albert | 5ede4e217a | |
Maximilian Fridrich | 60b81eabe0 | |
Naveen Albert | 2efa290d3c | |
Jaco Kroon | ce2153fc5a | |
Naveen Albert | 002afc3f2a | |
Naveen Albert | 1e77b8c473 | |
Joshua C. Colp | 61922d2934 | |
Naveen Albert | 6e59b01e1a | |
Naveen Albert | 49cfdbbdff | |
Naveen Albert | 8142b313c3 | |
George Joseph | 0c1c623dee | |
Naveen Albert | dfe2f38642 | |
George Joseph | f723b465e5 | |
Mike Bradeen | 50e2921a48 | |
Naveen Albert | afd86b47c1 | |
Igor Goncharovsky | 096529d33f | |
Naveen Albert | ca8900b0f6 | |
Henning Westerholt | 12445040d3 | |
Naveen Albert | 40b52322e5 | |
Naveen Albert | c32b39d123 | |
Frederic LE FOLL | 50a4495799 | |
Naveen Albert | 180ca32565 | |
Naveen Albert | 9258d8212a | |
Naveen Albert | 407216a0a5 | |
Philip Prindeville | d0bea5a725 | |
Mike Bradeen | 907d7e7d7d | |
Naveen Albert | b331caca30 | |
Naveen Albert | e0e7f35730 | |
Naveen Albert | 98fc05f13b | |
Philip Prindeville | ef74ecacc7 | |
Philip Prindeville | 5e2485b5c0 | |
George Joseph | 2a500b325a | |
Maximilian Fridrich | 0d2e140123 | |
Asterisk Development Team | 7f80830ced | |
Holger Hans Peter Freyther | 62881c668b | |
Naveen Albert | 8afb313a43 | |
Naveen Albert | 7335b0cffe | |
Naveen Albert | 407167cc28 | |
Naveen Albert | a5ec60e6c6 | |
Naveen Albert | 1e29607b5c | |
Naveen Albert | 8c791f9a65 | |
Philip Prindeville | 3e7ce90f9c | |
Naveen Albert | 1ed4518328 | |
Maximilian Fridrich | 5bbad0d27c | |
Naveen Albert | 8aae0b9f08 | |
Jaco Kroon | 278c5726ca | |
Naveen Albert | ab1dbfef75 | |
George Joseph | e25b690d10 | |
George Joseph | e33f2dcc0f | |
Philip Prindeville | 026dc08529 | |
Asterisk Development Team | f01ed3eea4 | |
Mike Bradeen | 7a44296ca9 | |
George Joseph | 8cbea1c7ef | |
sungtae kim | 80bc844fd6 | |
Ben Ford | 881a3f2306 | |
Philip Prindeville | 3e054c9ebc | |
Philip Prindeville | 736cdf84f4 | |
Philip Prindeville | 2d7656cb50 | |
Philip Prindeville | 5809d879b0 | |
Philip Prindeville | 2c4c44ca64 | |
Philip Prindeville | b9df2c481b | |
Philip Prindeville | d13afaf302 | |
Naveen Albert | 2dac2bf8dc | |
Naveen Albert | c487425620 | |
Naveen Albert | 205c7c8d21 | |
Naveen Albert | 2de016b181 | |
George Joseph | 05f42806cc | |
George Joseph | c799db6a21 | |
George Joseph | 4ffc5561c4 | |
George Joseph | 2d5a6498dd | |
Joshua C. Colp | f3de933b16 | |
Naveen Albert | c7612521be | |
Joshua C. Colp | a0713a9f70 | |
Naveen Albert | 754346a4a9 | |
Mike Bradeen | 46776c77c4 | |
Sean Bright | 583e017f34 | |
Alexei Gradinari | 12c4c1bf5f | |
Sean Bright | 155c796203 | |
Naveen Albert | 3fa66c92b5 | |
Mike Bradeen | adffb975dc | |
Mike Bradeen | 4fc9e06db1 | |
Naveen Albert | e2e049e473 | |
George Joseph | 8a8416e365 | |
Naveen Albert | ff044c222b | |
Naveen Albert | dc7ec11c26 | |
George Joseph | 30d7a212b0 | |
Naveen Albert | f4a020a45b | |
Naveen Albert | c654486547 | |
Naveen Albert | 5feebc0857 | |
Naveen Albert | 165368bf0b | |
Naveen Albert | 2d8f2696b2 | |
Naveen Albert | 4af881506e | |
Naveen Albert | 83912496ab | |
Naveen Albert | c771e2dd7a | |
Sergey V. Lobanov | f645157a4b | |
Naveen Albert | a9223f210e | |
Naveen Albert | ce18196280 | |
George Joseph | f8000daff5 |
|
@ -1,6 +1,5 @@
|
|||
[gerrit]
|
||||
defaultbranch=master
|
||||
basebranch=master
|
||||
defaultbranch=20
|
||||
#
|
||||
# Intentional padding to ensure it is possible to point a commit
|
||||
# to an alternative gerrit server/repository without breaking
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
40
|
306
CHANGES
306
CHANGES
|
@ -12,10 +12,316 @@
|
|||
===
|
||||
==============================================================================
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 20.1.0 to Asterisk 20.2.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
app_broadcast
|
||||
------------------
|
||||
* A Broadcast application is now available which allows
|
||||
for asynchronous one-to-many and many-to-one channel audio.
|
||||
|
||||
app_directory
|
||||
------------------
|
||||
* A new option 's' has been added to the Directory() application that
|
||||
will skip calling the extension and instead set the extension as
|
||||
DIRECTORY_EXTEN channel variable.
|
||||
|
||||
app_read
|
||||
------------------
|
||||
* A new option 'e' has been added to allow Read() to return the
|
||||
terminator as the dialed digits in the case where only the terminator
|
||||
is entered.
|
||||
|
||||
app_senddtmf
|
||||
------------------
|
||||
* A new option has been added to SendDTMF() which will answer the
|
||||
specified channel if it is not already up. If no channel is specified,
|
||||
the current channel will be answered instead.
|
||||
|
||||
app_signal
|
||||
------------------
|
||||
* Adds Signal and WaitForSignal applications
|
||||
which can be used for signaling or as a
|
||||
simple message queue in the dialplan.
|
||||
|
||||
func_json
|
||||
------------------
|
||||
* Additional parsing capabilities have been added to the
|
||||
JSON_DECODE function, including support for arrays
|
||||
and recursive indexing.
|
||||
|
||||
res_phoneprov
|
||||
------------------
|
||||
* On multihomed Asterisk servers with dynamic SERVER template variables,
|
||||
reloading this module is no longer required when re-provisioning your
|
||||
phone to another interface address (e.g. when moving between VLANs.)
|
||||
|
||||
res_pjsip_rfc3326
|
||||
------------------
|
||||
* Add ability to set HANGUPCAUSE when SIP causecode received in BYE Reason header (in
|
||||
addition to currently supported Q.850). The first header found will be used to set
|
||||
the HANGUPCAUSE variable.
|
||||
|
||||
res_pjsip_session
|
||||
------------------
|
||||
* The overlap_context option now allows explicitly
|
||||
specifying a context to use for overlap dialing matches.
|
||||
|
||||
res_rtp_asterisk
|
||||
------------------
|
||||
* This module has been updated to provide additional
|
||||
quality statistics in the form of an Asterisk
|
||||
Media Experience Score. The score is available using
|
||||
the same mechanisms you'd use to retrieve jitter, loss,
|
||||
and rtt statistics. For more information about the
|
||||
score and how to retrieve it, see
|
||||
https://wiki.asterisk.org/wiki/display/AST/Media+Experience+Score
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 20.0.0 to Asterisk 20.1.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
AMI
|
||||
------------------
|
||||
* The AOCMessage action can now be used to generate AOC-S messages.
|
||||
|
||||
Add support for named capture agent.
|
||||
------------------
|
||||
* A name for the capture agent can now be specified
|
||||
using the capture_name option which, if specified,
|
||||
will be sent to the HEP server.
|
||||
|
||||
app_if
|
||||
------------------
|
||||
* Adds the If, ElseIf, Else, EndIf, and ExitIf applications
|
||||
for conditional execution of a block of code.
|
||||
|
||||
app_mixmonitor
|
||||
------------------
|
||||
* The d option for MixMonitor now allows deleting
|
||||
the original recording when MixMonitor exits,
|
||||
which can be useful when MixMonitor copies it
|
||||
somewhere else before exiting.
|
||||
|
||||
* Adds the c option to use the real Caller ID on
|
||||
the channel in voicemail recordings as opposed
|
||||
to the Connected Line.
|
||||
|
||||
app_voicemail
|
||||
------------------
|
||||
* The voicemail user option attachextrecs can
|
||||
now be set to control whether external recordings
|
||||
trigger voicemail email notifications.
|
||||
|
||||
cdr
|
||||
------------------
|
||||
* Two new options have been added which allow
|
||||
bridging and dial state changes to be ignored
|
||||
in CDRs, which can be useful if a single CDR
|
||||
is desired for a channel.
|
||||
|
||||
chan_dahdi
|
||||
------------------
|
||||
* FXO channels (FXS signaled) that don't use callerid or
|
||||
distinctive ring detection can now be configured
|
||||
to enter the dialplan immediately using immediate=yes,
|
||||
instead of waiting for at least one ring.
|
||||
|
||||
pbx_builtins
|
||||
------------------
|
||||
* It is now possible to not wait for media on
|
||||
a channel when answering it using Answer,
|
||||
by specifying the i option.
|
||||
|
||||
res_pjsip
|
||||
------------------
|
||||
* Added options "security_negotiation" and "security_mechanisms" to pjsip
|
||||
endpoints and registrations. "security_negotiation" can be set to "no" (default)
|
||||
or "mediasec", and "security_mechanisms" can be a list of comma-separated
|
||||
security_mechanisms in the form defined by RFC 3329 section 2.2.
|
||||
|
||||
* A new option named "all_codecs_on_empty_reinvite" has been added to the
|
||||
global section. When this option is enabled, on reception of a re-INVITE
|
||||
without SDP, Asterisk will send an SDP offer in the 200 OK response containing
|
||||
all configured codecs on the endpoint, instead of simply those that have
|
||||
already been negotiated. RFC 3261 specifies this as a SHOULD requirement.
|
||||
The default value is "off".
|
||||
|
||||
res_pjsip_aoc
|
||||
------------------
|
||||
* Added res_pjsip_aoc which gives chan_pjsip the ability to send Advice-of-Charge messages.
|
||||
A new endpoint option, send_aoc, controls this.
|
||||
|
||||
res_pjsip_header_funcs
|
||||
------------------
|
||||
* The new PJSIP_HEADER_PARAM function now fully supports both
|
||||
URI and header parameters. Both reading and writing
|
||||
parameters are supported.
|
||||
|
||||
res_pjsip_logger
|
||||
------------------
|
||||
* SIP messages can now be filtered by SIP request method
|
||||
(INVITE, CANCEL, ACK, BYE, REGISTER, OPTION,
|
||||
SUBSCRIBE, NOTIFY, PUBLISH, INFO, and MESSAGE),
|
||||
allowing for more granular debugging to be done
|
||||
in the CLI. This applies to requests but not responses.
|
||||
|
||||
res_pjsip_notify
|
||||
------------------
|
||||
* Allows using the config options in pjsip_notify.conf
|
||||
from AMI actions as with the existing CLI commands.
|
||||
|
||||
res_tonedetect
|
||||
------------------
|
||||
* The TONE_DETECT function now supports
|
||||
detection of audible ringback tone
|
||||
using the p option.
|
||||
|
||||
xmldocs
|
||||
------------------
|
||||
* The XML documentation can now be reloaded without restarting
|
||||
Asterisk, which makes it possible to load new modules that
|
||||
enforce documentation without restarting Asterisk.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 19.0.0 to Asterisk 20.0.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
New EXPORT function
|
||||
------------------
|
||||
* A new function, EXPORT, allows writing variables
|
||||
and functions on other channels, the complement
|
||||
of the IMPORT function.
|
||||
|
||||
app_amd
|
||||
------------------
|
||||
* An audio file to play during AMD processing can
|
||||
now be specified to the AMD application or configured
|
||||
in the amd.conf configuration file.
|
||||
|
||||
app_bridgewait
|
||||
------------------
|
||||
* Adds the n option to not answer the channel when
|
||||
the BridgeWait application is called.
|
||||
|
||||
features
|
||||
------------------
|
||||
* The Bridge application now has the n "no answer" option
|
||||
that can be used to prevent the channel from being
|
||||
automatically answered prior to bridging.
|
||||
|
||||
func_strings
|
||||
------------------
|
||||
* Three new functions, TRIM, LTRIM, and RTRIM, are
|
||||
now available for trimming leading and trailing
|
||||
whitespace.
|
||||
|
||||
res_pjsip
|
||||
------------------
|
||||
* A new option named "peer_supported" has been added to the endpoint option
|
||||
100rel. When set to this option, Asterisk sends provisional responses
|
||||
reliably if the peer supports it. If the peer does not support reliable
|
||||
provisional responses, Asterisk sends them normally.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 19.0.0 to Asterisk 20.0.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Transfer feature
|
||||
------------------
|
||||
* The following capabilities have been added to the
|
||||
transfer feature:
|
||||
|
||||
- The transfer initiation announcement prompt can
|
||||
now be customized in features.conf.
|
||||
|
||||
- The TRANSFER_EXTEN variable now can be set on the
|
||||
transferer's channel in order to allow the transfer
|
||||
function to automatically attempt to go to the extension
|
||||
contained in this variable, if it exists. The transfer
|
||||
context behavior is not changed (TRANSFER_CONTEXT is used
|
||||
if it exists; otherwise the default context is used).
|
||||
|
||||
app_confbridge
|
||||
------------------
|
||||
* Adds the end_marked_any option which can be used
|
||||
to kick users from a conference after any
|
||||
marked user leaves (including marked users).
|
||||
|
||||
db
|
||||
------------------
|
||||
* The DBPrefixGet AMI action now allows retrieving
|
||||
all of the DB keys beginning with a particular
|
||||
prefix.
|
||||
|
||||
locks
|
||||
------------------
|
||||
* A new AMI event, DeadlockStart, is now available
|
||||
when Asterisk is compiled with DETECT_DEADLOCKS,
|
||||
and can indicate that a deadlock has occured.
|
||||
|
||||
res_geolocation
|
||||
------------------
|
||||
* * Added processing for the 'confidence' element.
|
||||
* Added documentation to some APIs.
|
||||
* removed a lot of complex code related to the very-off-nominal
|
||||
case of needing to process multiple location info sources.
|
||||
* Create a new 'ast_geoloc_eprofile_to_pidf' API that just takes
|
||||
one eprofile instead of a datastore of multiples.
|
||||
* Plugged a huge leak in XML processing that arose from
|
||||
insufficient documentation by the libxml/libxslt authors.
|
||||
* Refactored stylesheets to be more efficient.
|
||||
* Renamed 'profile_action' to 'profile_precedence' to better
|
||||
reflect it's purpose.
|
||||
* Added the config option for 'allow_routing_use' which
|
||||
sets the value of the 'Geolocation-Routing' header.
|
||||
* Removed the GeolocProfileCreate and GeolocProfileDelete
|
||||
dialplan apps.
|
||||
* Changed the GEOLOC_PROFILE dialplan function as follows:
|
||||
* Removed the 'profile' argument.
|
||||
* Automatically create a profile if it doesn't exist.
|
||||
* Delete a profile if 'inheritable' is set to no.
|
||||
* Fixed various bugs and leaks
|
||||
* Updated Asterisk WiKi documentation.
|
||||
|
||||
Added 4 built-in profiles:
|
||||
"<prefer_config>"
|
||||
"<discard_config>"
|
||||
"<prefer_incoming>"
|
||||
"<discard_incoming>"
|
||||
The profiles are empty except for having their precedence
|
||||
set.
|
||||
|
||||
Added profile parameter "suppress_empty_ca_elements" that
|
||||
will cause Civic Address elements that are empty to be
|
||||
suppressed from the outgoing PIDF-LO document.
|
||||
|
||||
You can now specify the location object's format, location_info,
|
||||
method, location_source and confidence parameters directly on
|
||||
a profile object for simple scenarios where the location
|
||||
information isn't common with any other profiles. This is
|
||||
mutually exclusive with setting location_reference on the
|
||||
profile.
|
||||
|
||||
Added an 'a' option to the GEOLOC_PROFILE function to allow
|
||||
variable lists like location_info_refinement to be appended
|
||||
to instead of replacing the entire list.
|
||||
|
||||
Added an 'r' option to the GEOLOC_PROFILE function to resolve all
|
||||
variables before a read operation and after a Set operation.
|
||||
|
||||
res_musiconhold_answeredonly
|
||||
------------------
|
||||
* This change adds an option, answeredonly, that will prevent music
|
||||
on hold on channels that are not answered.
|
||||
|
||||
res_pjsip
|
||||
------------------
|
||||
* TLS transports in res_pjsip can now reload their TLS certificate
|
||||
and private key files, provided the filename of them has not
|
||||
changed.
|
||||
|
||||
Applications
|
||||
------------------
|
||||
* added support for Danish syntax, playing the correct plural sound file
|
||||
|
|
6
Makefile
6
Makefile
|
@ -561,9 +561,9 @@ bininstall: _all installdirs $(SUBDIRS_INSTALL) main-bininstall
|
|||
$(INSTALL) -m 755 contrib/scripts/astversion "$(DESTDIR)$(ASTSBINDIR)/"
|
||||
$(INSTALL) -m 755 contrib/scripts/astgenkey "$(DESTDIR)$(ASTSBINDIR)/"
|
||||
$(INSTALL) -m 755 contrib/scripts/autosupport "$(DESTDIR)$(ASTSBINDIR)/"
|
||||
if [ ! -f /sbin/launchd ]; then \
|
||||
./build_tools/install_subst contrib/scripts/safe_asterisk "$(DESTDIR)$(ASTSBINDIR)/safe_asterisk"; \
|
||||
fi
|
||||
ifneq ($(HAVE_SBIN_LAUNCHD),1)
|
||||
./build_tools/install_subst contrib/scripts/safe_asterisk "$(DESTDIR)$(ASTSBINDIR)/safe_asterisk";
|
||||
endif
|
||||
|
||||
ifneq ($(DISABLE_XMLDOC),yes)
|
||||
$(INSTALL) -m 644 doc/core-*.xml "$(DESTDIR)$(ASTDATADIR)/documentation"
|
||||
|
|
|
@ -213,10 +213,10 @@ endif
|
|||
# extern const size_t _binary_abc_def_xml_size;
|
||||
%.o: %.xml
|
||||
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
||||
$(CMD_PREFIX) $(CC) -g -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||
$(CMD_PREFIX) $(CC) -g -Wl,-znoexecstack -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||
|
||||
%.o: %.xslt
|
||||
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
||||
$(CMD_PREFIX) $(CC) -g -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||
$(CMD_PREFIX) $(CC) -g -Wl,-znoexecstack -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||
|
||||
dist-clean:: clean
|
||||
|
|
36
UPGRADE.txt
36
UPGRADE.txt
|
@ -18,6 +18,42 @@
|
|||
===
|
||||
===========================================================
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 20.1.0 to Asterisk 20.2.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
app_playback
|
||||
------------------
|
||||
* In Asterisk 11, if a channel was redirected away during Playback(),
|
||||
the PLAYBACKSTATUS variable would be set to SUCCESS. In Asterisk 12
|
||||
(specifically commit 7d9871b3940fa50e85039aef6a8fb9870a7615b9) that
|
||||
behavior was inadvertently changed and the same operation would result
|
||||
in the PLAYBACKSTATUS variable being set to FAILED. The Asterisk 11
|
||||
behavior has been restored.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 20.0.0 to Asterisk 20.1.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
AMI (Asterisk Manager Interface)
|
||||
------------------
|
||||
* Previously, GetConfig and UpdateConfig were able to access files outside of
|
||||
the Asterisk configuration directory. Now this access is put behind the
|
||||
live_dangerously configuration option in asterisk.conf, which is disabled by
|
||||
default. If access to configuration files outside of the Asterisk configuation
|
||||
directory is required via AMI, then the live_dangerously configuration option
|
||||
must be set to yes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 19.0.0 to Asterisk 20.0.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
res_crypto
|
||||
------------------
|
||||
* In addition to only paying attention to files ending with .key or .pub
|
||||
in the keys directory, we now also ignore any files which aren't regular
|
||||
files.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- New functionality introduced in Asterisk 20.0.0 --------------------------
|
||||
------------------------------------------------------------------------------
|
||||
|
|
|
@ -192,11 +192,13 @@ EXTERN int ooQ931Decode
|
|||
screening indicators ;-) */
|
||||
if(ie->discriminator == Q931CallingPartyNumberIE)
|
||||
{
|
||||
int numoffset=1;
|
||||
OOTRACEDBGB1(" CallingPartyNumber IE = {\n");
|
||||
if(ie->length < OO_MAX_NUMBER_LENGTH)
|
||||
if(!(0x80 & ie->data[0])) numoffset = 2;
|
||||
|
||||
if( (ie->length >= numoffset) &&
|
||||
(ie->length < OO_MAX_NUMBER_LENGTH) )
|
||||
{
|
||||
int numoffset=1;
|
||||
if(!(0x80 & ie->data[0])) numoffset = 2;
|
||||
memcpy(number, ie->data+numoffset,ie->length-numoffset);
|
||||
number[ie->length-numoffset]='\0';
|
||||
OOTRACEDBGB2(" %s\n", number);
|
||||
|
@ -204,7 +206,7 @@ EXTERN int ooQ931Decode
|
|||
ooCallSetCallingPartyNumber(call, number);
|
||||
}
|
||||
else{
|
||||
OOTRACEERR3("Error:Calling party number too long. (%s, %s)\n",
|
||||
OOTRACEERR3("Error:Calling party number outside range. (%s, %s)\n",
|
||||
call->callType, call->callToken);
|
||||
}
|
||||
OOTRACEDBGB1(" }\n");
|
||||
|
@ -214,7 +216,8 @@ EXTERN int ooQ931Decode
|
|||
if(ie->discriminator == Q931CalledPartyNumberIE)
|
||||
{
|
||||
OOTRACEDBGB1(" CalledPartyNumber IE = {\n");
|
||||
if(ie->length < OO_MAX_NUMBER_LENGTH)
|
||||
if( (ie->length >= 1) &&
|
||||
(ie->length < OO_MAX_NUMBER_LENGTH) )
|
||||
{
|
||||
memcpy(number, ie->data+1,ie->length-1);
|
||||
number[ie->length-1]='\0';
|
||||
|
@ -223,7 +226,7 @@ EXTERN int ooQ931Decode
|
|||
ooCallSetCalledPartyNumber(call, number);
|
||||
}
|
||||
else{
|
||||
OOTRACEERR3("Error:Calling party number too long. (%s, %s)\n",
|
||||
OOTRACEERR3("Error:Calling party number outside range. (%s, %s)\n",
|
||||
call->callType, call->callToken);
|
||||
}
|
||||
OOTRACEDBGB1(" }\n");
|
||||
|
|
|
@ -92,6 +92,12 @@
|
|||
<para>Is the maximum duration of a word to accept.</para>
|
||||
<para>If exceeded, then the result is detection as a MACHINE</para>
|
||||
</parameter>
|
||||
<parameter name="audioFile" required="false">
|
||||
<para>Is an audio file to play to the caller while AMD is in progress.</para>
|
||||
<para>By default, no audio file is played.</para>
|
||||
<para>If an audio file is configured in amd.conf, then that file will be used
|
||||
if one is not specified here. That file may be overridden by this argument.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This application attempts to detect answering machines at the beginning
|
||||
|
@ -155,6 +161,9 @@ static int dfltBetweenWordsSilence = 50;
|
|||
static int dfltMaximumNumberOfWords = 2;
|
||||
static int dfltSilenceThreshold = 256;
|
||||
static int dfltMaximumWordLength = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
|
||||
static char *dfltAudioFile = NULL;
|
||||
|
||||
static ast_mutex_t config_lock;
|
||||
|
||||
/* Set to the lowest ms value provided in amd.conf or application parameters */
|
||||
static int dfltMaxWaitTimeForFrame = 50;
|
||||
|
@ -179,7 +188,7 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
char amdCause[256] = "", amdStatus[256] = "";
|
||||
char *parse = ast_strdupa(data);
|
||||
|
||||
/* Lets set the initial values of the variables that will control the algorithm.
|
||||
/* Let's set the initial values of the variables that will control the algorithm.
|
||||
The initial values are the default ones. If they are passed as arguments
|
||||
when invoking the application, then the default values will be overwritten
|
||||
by the ones passed as parameters. */
|
||||
|
@ -193,6 +202,7 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
int silenceThreshold = dfltSilenceThreshold;
|
||||
int maximumWordLength = dfltMaximumWordLength;
|
||||
int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
|
||||
const char *audioFile = NULL;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(argInitialSilence);
|
||||
|
@ -204,8 +214,15 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
AST_APP_ARG(argMaximumNumberOfWords);
|
||||
AST_APP_ARG(argSilenceThreshold);
|
||||
AST_APP_ARG(argMaximumWordLength);
|
||||
AST_APP_ARG(audioFile);
|
||||
);
|
||||
|
||||
ast_mutex_lock(&config_lock);
|
||||
if (!ast_strlen_zero(dfltAudioFile)) {
|
||||
audioFile = ast_strdupa(dfltAudioFile);
|
||||
}
|
||||
ast_mutex_unlock(&config_lock);
|
||||
|
||||
ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
|
||||
S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"),
|
||||
S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"),
|
||||
|
@ -233,6 +250,9 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
silenceThreshold = atoi(args.argSilenceThreshold);
|
||||
if (!ast_strlen_zero(args.argMaximumWordLength))
|
||||
maximumWordLength = atoi(args.argMaximumWordLength);
|
||||
if (!ast_strlen_zero(args.audioFile)) {
|
||||
audioFile = args.audioFile;
|
||||
}
|
||||
} else {
|
||||
ast_debug(1, "AMD using the default parameters.\n");
|
||||
}
|
||||
|
@ -280,6 +300,11 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
/* Set our start time so we can tie the loop to real world time and not RTP updates */
|
||||
amd_tvstart = ast_tvnow();
|
||||
|
||||
/* Optional audio file to play to caller while AMD is doing its thing. */
|
||||
if (!ast_strlen_zero(audioFile)) {
|
||||
ast_streamfile(chan, audioFile, ast_channel_language(chan));
|
||||
}
|
||||
|
||||
/* Now we go into a loop waiting for frames from the channel */
|
||||
while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
|
||||
int ms = 0;
|
||||
|
@ -462,10 +487,14 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
/* Free the DSP used to detect silence */
|
||||
ast_dsp_free(silenceDetector);
|
||||
|
||||
/* If we were playing something to pass the time, stop it now. */
|
||||
if (!ast_strlen_zero(audioFile)) {
|
||||
ast_stopstream(chan);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static int amd_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
isAnsweringMachine(chan, data);
|
||||
|
@ -516,7 +545,16 @@ static int load_config(int reload)
|
|||
dfltMaximumNumberOfWords = atoi(var->value);
|
||||
} else if (!strcasecmp(var->name, "maximum_word_length")) {
|
||||
dfltMaximumWordLength = atoi(var->value);
|
||||
|
||||
} else if (!strcasecmp(var->name, "playback_file")) {
|
||||
ast_mutex_lock(&config_lock);
|
||||
if (dfltAudioFile) {
|
||||
ast_free(dfltAudioFile);
|
||||
dfltAudioFile = NULL;
|
||||
}
|
||||
if (!ast_strlen_zero(var->value)) {
|
||||
dfltAudioFile = ast_strdup(var->value);
|
||||
}
|
||||
ast_mutex_unlock(&config_lock);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
|
||||
app, cat, var->name, var->lineno);
|
||||
|
@ -539,6 +577,12 @@ static int load_config(int reload)
|
|||
|
||||
static int unload_module(void)
|
||||
{
|
||||
ast_mutex_lock(&config_lock);
|
||||
if (dfltAudioFile) {
|
||||
ast_free(dfltAudioFile);
|
||||
}
|
||||
ast_mutex_unlock(&config_lock);
|
||||
ast_mutex_destroy(&config_lock);
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
|
@ -554,6 +598,7 @@ static int unload_module(void)
|
|||
*/
|
||||
static int load_module(void)
|
||||
{
|
||||
ast_mutex_init(&config_lock);
|
||||
if (load_config(0) || ast_register_application_xml(app, amd_exec)) {
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
|
|
@ -100,6 +100,9 @@
|
|||
<para>Automatically exit the bridge and return to the PBX after
|
||||
<emphasis>duration</emphasis> seconds.</para>
|
||||
</option>
|
||||
<option name="n">
|
||||
<para>Do not automatically answer the channel.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
|
@ -108,7 +111,7 @@
|
|||
The channel will then wait in the holding bridge until some event occurs
|
||||
which removes it from the holding bridge.</para>
|
||||
<note><para>This application will answer calls which haven't already
|
||||
been answered.</para></note>
|
||||
been answered, unless the n option is provided.</para></note>
|
||||
</description>
|
||||
</application>
|
||||
***/
|
||||
|
@ -186,6 +189,7 @@ enum bridgewait_flags {
|
|||
MUXFLAG_MOHCLASS = (1 << 0),
|
||||
MUXFLAG_ENTERTAINMENT = (1 << 1),
|
||||
MUXFLAG_TIMEOUT = (1 << 2),
|
||||
MUXFLAG_NOANSWER = (1 << 3),
|
||||
};
|
||||
|
||||
enum bridgewait_args {
|
||||
|
@ -199,6 +203,7 @@ AST_APP_OPTIONS(bridgewait_opts, {
|
|||
AST_APP_OPTION_ARG('e', MUXFLAG_ENTERTAINMENT, OPT_ARG_ENTERTAINMENT),
|
||||
AST_APP_OPTION_ARG('m', MUXFLAG_MOHCLASS, OPT_ARG_MOHCLASS),
|
||||
AST_APP_OPTION_ARG('S', MUXFLAG_TIMEOUT, OPT_ARG_TIMEOUT),
|
||||
AST_APP_OPTION('n', MUXFLAG_NOANSWER),
|
||||
});
|
||||
|
||||
static int bridgewait_timeout_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
|
||||
|
@ -458,7 +463,7 @@ static int bridgewait_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
|
||||
/* Answer the channel if needed */
|
||||
if (ast_channel_state(chan) != AST_STATE_UP) {
|
||||
if (ast_channel_state(chan) != AST_STATE_UP && !ast_test_flag(&flags, MUXFLAG_NOANSWER)) {
|
||||
ast_answer(chan);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,619 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2022, Naveen Albert
|
||||
*
|
||||
* 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 Channel audio broadcasting
|
||||
*
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>extended</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/audiohook.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/autochan.h"
|
||||
#include "asterisk/format_cache.h"
|
||||
#include "asterisk/cli.h" /* use ESS macro */
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="Broadcast" language="en_US">
|
||||
<synopsis>
|
||||
Transmit or receive audio to or from multiple channels simultaneously
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="options">
|
||||
<optionlist>
|
||||
<option name="b">
|
||||
<para>In addition to broadcasting to target channels, also
|
||||
broadcast to any channels to which target channels are bridged.</para>
|
||||
</option>
|
||||
<option name="l">
|
||||
<para>Allow usage of a long queue to store audio frames.</para>
|
||||
<note><para>This may introduce some delay in the received audio feed, but will improve the audio quality.</para></note>
|
||||
</option>
|
||||
<option name="o">
|
||||
<para>Do not mix streams when combining audio from target channels (only applies with s option).</para>
|
||||
</option>
|
||||
<option name="r">
|
||||
<para>Feed frames to barge channels in "reverse" by injecting them into the primary channel's read queue instead.</para>
|
||||
<para>This option is required for barge to work in a n-party bridge (but not for 2-party bridges). Alternately, you
|
||||
can add an intermediate channel by using a non-optimized Local channel, so that the target channel is bridged with
|
||||
a single channel that is connected to the bridge, but it is recommended this option be used instead.</para>
|
||||
<para>Note that this option will always feed injected audio to the other party, regardless of whether the target
|
||||
channel is bridged or not.</para>
|
||||
</option>
|
||||
<option name="s">
|
||||
<para>Rather than broadcast audio to a bunch of channels, receive the combined audio from the target channels.</para>
|
||||
</option>
|
||||
<option name="w">
|
||||
<para>Broadcast audio received on this channel to other channels.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
<parameter name="channels" required="true" argsep=",">
|
||||
<para>List of channels for broadcast targets.</para>
|
||||
<para>Channel names must be the full channel names, not merely device names.</para>
|
||||
<para>Broadcasting will continue until the broadcasting channel hangs up or all target channels have hung up.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This application can be used to broadcast audio to multiple channels at once.
|
||||
Any audio received on this channel will be transmitted to all of the specified channels and, optionally, their bridged peers.</para>
|
||||
<para>It can also be used to aggregate audio from multiple channels at once.
|
||||
Any audio on any of the specified channels, and optionally their bridged peers, will be transmitted to this channel.</para>
|
||||
<para>Execution of the application continues until either the broadcasting channel hangs up
|
||||
or all specified channels have hung up.</para>
|
||||
<para>This application is used for one-to-many and many-to-one audio applications where
|
||||
bridge mixing cannot be done synchronously on all the involved channels.
|
||||
This is primarily useful for injecting the same audio stream into multiple channels at once,
|
||||
or doing the reverse, combining the audio from multiple channels into a single stream.
|
||||
This contrasts with using a separate injection channel for each target channel and/or
|
||||
using a conference bridge.</para>
|
||||
<para>The channel running the Broadcast application must do so synchronously. The specified channels,
|
||||
however, may be doing other things.</para>
|
||||
<example title="Broadcast received audio to three channels and their bridged peers">
|
||||
same => n,Broadcast(wb,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<example title="Broadcast received audio to three channels, only">
|
||||
same => n,Broadcast(w,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<example title="Combine audio from three channels and their bridged peers to us">
|
||||
same => n,Broadcast(s,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<example title="Combine audio from three channels to us">
|
||||
same => n,Broadcast(so,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<example title="Two-way audio with a bunch of channels">
|
||||
same => n,Broadcast(wbso,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<para>Note that in the last example above, this is NOT the same as a conference bridge.
|
||||
The specified channels are not audible to each other, only to the channel running the
|
||||
Broadcast application. The two-way audio is only between the broadcasting channel and
|
||||
each of the specified channels, individually.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">ChanSpy</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
static const char app_broadcast[] = "Broadcast";
|
||||
|
||||
enum {
|
||||
OPTION_READONLY = (1 << 0), /* Don't mix the two channels */
|
||||
OPTION_BARGE = (1 << 1), /* Barge mode (whisper to both channels) */
|
||||
OPTION_LONG_QUEUE = (1 << 2), /* Allow usage of a long queue to store audio frames. */
|
||||
OPTION_WHISPER = (1 << 3),
|
||||
OPTION_SPY = (1 << 4),
|
||||
OPTION_REVERSE_FEED = (1 << 5),
|
||||
OPTION_ANSWER_WARN = (1 << 6), /* Internal flag, not set by user */
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(spy_opts, {
|
||||
AST_APP_OPTION('b', OPTION_BARGE),
|
||||
AST_APP_OPTION('l', OPTION_LONG_QUEUE),
|
||||
AST_APP_OPTION('o', OPTION_READONLY),
|
||||
AST_APP_OPTION('r', OPTION_REVERSE_FEED),
|
||||
AST_APP_OPTION('s', OPTION_SPY),
|
||||
AST_APP_OPTION('w', OPTION_WHISPER),
|
||||
});
|
||||
|
||||
struct multi_autochan {
|
||||
char *name;
|
||||
struct ast_autochan *autochan;
|
||||
struct ast_autochan *bridge_autochan;
|
||||
struct ast_audiohook whisper_audiohook;
|
||||
struct ast_audiohook bridge_whisper_audiohook;
|
||||
struct ast_audiohook spy_audiohook;
|
||||
unsigned int connected:1;
|
||||
unsigned int bridge_connected:1;
|
||||
unsigned int spying:1;
|
||||
AST_LIST_ENTRY(multi_autochan) entry; /*!< Next record */
|
||||
};
|
||||
|
||||
AST_RWLIST_HEAD(multi_autochan_list, multi_autochan);
|
||||
|
||||
struct multi_spy {
|
||||
struct multi_autochan_list *chanlist;
|
||||
unsigned int readonly:1;
|
||||
};
|
||||
|
||||
static void *spy_alloc(struct ast_channel *chan, void *data)
|
||||
{
|
||||
return data; /* just store the data pointer in the channel structure */
|
||||
}
|
||||
|
||||
static void spy_release(struct ast_channel *chan, void *data)
|
||||
{
|
||||
return; /* nothing to do */
|
||||
}
|
||||
|
||||
static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
|
||||
{
|
||||
struct multi_spy *multispy = data;
|
||||
struct multi_autochan_list *chanlist = multispy->chanlist;
|
||||
struct multi_autochan *mac;
|
||||
struct ast_frame *f;
|
||||
short *data1, *data2;
|
||||
int res, i;
|
||||
|
||||
/* All the frames we get are slin, so they will all have the same number of samples. */
|
||||
static const int num_samples = 160;
|
||||
short combine_buf[num_samples];
|
||||
struct ast_frame wf = {
|
||||
.frametype = AST_FRAME_VOICE,
|
||||
.offset = 0,
|
||||
.subclass.format = ast_format_slin,
|
||||
.datalen = num_samples * 2,
|
||||
.samples = num_samples,
|
||||
.src = __FUNCTION__,
|
||||
};
|
||||
|
||||
memset(&combine_buf, 0, sizeof(combine_buf));
|
||||
wf.data.ptr = combine_buf;
|
||||
|
||||
AST_RWLIST_WRLOCK(chanlist);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(chanlist, mac, entry) {
|
||||
ast_audiohook_lock(&mac->spy_audiohook);
|
||||
if (mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
ast_audiohook_unlock(&mac->spy_audiohook); /* Channel is already gone more than likely, the broadcasting channel will clean this up. */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (multispy->readonly) { /* Option 'o' was set, so don't mix channel audio */
|
||||
f = ast_audiohook_read_frame(&mac->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, ast_format_slin);
|
||||
} else {
|
||||
f = ast_audiohook_read_frame(&mac->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, ast_format_slin);
|
||||
}
|
||||
ast_audiohook_unlock(&mac->spy_audiohook);
|
||||
|
||||
if (!f) {
|
||||
continue; /* No frame? No problem. */
|
||||
}
|
||||
|
||||
/* Mix the samples. */
|
||||
for (i = 0, data1 = combine_buf, data2 = f->data.ptr; i < num_samples; i++, data1++, data2++) {
|
||||
ast_slinear_saturated_add(data1, data2);
|
||||
}
|
||||
ast_frfree(f);
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(chanlist);
|
||||
|
||||
res = ast_write(chan, &wf);
|
||||
ast_frfree(&wf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct ast_generator spygen = {
|
||||
.alloc = spy_alloc,
|
||||
.release = spy_release,
|
||||
.generate = spy_generate,
|
||||
};
|
||||
|
||||
static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook, struct ast_flags *flags)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_autochan_channel_lock(autochan);
|
||||
ast_debug(1, "Attaching spy channel %s to %s\n", spychan_name, ast_channel_name(autochan->chan));
|
||||
|
||||
if (ast_test_flag(flags, OPTION_READONLY)) {
|
||||
ast_set_flag(audiohook, AST_AUDIOHOOK_MUTE_WRITE);
|
||||
} else {
|
||||
ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
|
||||
}
|
||||
if (ast_test_flag(flags, OPTION_LONG_QUEUE)) {
|
||||
ast_debug(2, "Using a long queue to store audio frames in spy audiohook\n");
|
||||
} else {
|
||||
ast_set_flag(audiohook, AST_AUDIOHOOK_SMALL_QUEUE);
|
||||
}
|
||||
res = ast_audiohook_attach(autochan->chan, audiohook);
|
||||
ast_autochan_channel_unlock(autochan);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int attach_barge(struct ast_autochan *spyee_autochan, struct ast_autochan **spyee_bridge_autochan,
|
||||
struct ast_audiohook *bridge_whisper_audiohook, const char *spyer_name, const char *name, struct ast_flags *flags)
|
||||
{
|
||||
int retval = 0;
|
||||
struct ast_autochan *internal_bridge_autochan;
|
||||
struct ast_channel *spyee_chan;
|
||||
RAII_VAR(struct ast_channel *, bridged, NULL, ast_channel_cleanup);
|
||||
|
||||
ast_autochan_channel_lock(spyee_autochan);
|
||||
spyee_chan = ast_channel_ref(spyee_autochan->chan);
|
||||
ast_autochan_channel_unlock(spyee_autochan);
|
||||
|
||||
/* Note that ast_channel_bridge_peer only returns non-NULL for 2-party bridges, not n-party bridges (e.g. ConfBridge) */
|
||||
bridged = ast_channel_bridge_peer(spyee_chan);
|
||||
ast_channel_unref(spyee_chan);
|
||||
if (!bridged) {
|
||||
ast_debug(9, "Channel %s is not yet bridged, unable to setup barge\n", ast_channel_name(spyee_chan));
|
||||
/* If we're bridged, but it's not a 2-party bridge, then we probably should have used OPTION_REVERSE_FEED. */
|
||||
if (ast_test_flag(flags, OPTION_ANSWER_WARN) && ast_channel_is_bridged(spyee_chan)) {
|
||||
ast_clear_flag(flags, OPTION_ANSWER_WARN); /* Don't warn more than once. */
|
||||
ast_log(LOG_WARNING, "Barge failed: channel is bridged, but not to a 2-party bridge. Use the 'r' option.\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_audiohook_init(bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Broadcast", 0);
|
||||
internal_bridge_autochan = ast_autochan_setup(bridged);
|
||||
if (!internal_bridge_autochan) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (start_spying(internal_bridge_autochan, spyer_name, bridge_whisper_audiohook, flags)) {
|
||||
ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee '%s'. Barge mode disabled.\n", name);
|
||||
retval = -1;
|
||||
}
|
||||
|
||||
*spyee_bridge_autochan = internal_bridge_autochan;
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void multi_autochan_free(struct multi_autochan *mac)
|
||||
{
|
||||
if (mac->connected) {
|
||||
if (mac->whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
ast_debug(2, "Whisper audiohook no longer running\n");
|
||||
}
|
||||
ast_audiohook_lock(&mac->whisper_audiohook);
|
||||
ast_audiohook_detach(&mac->whisper_audiohook);
|
||||
ast_audiohook_unlock(&mac->whisper_audiohook);
|
||||
ast_audiohook_destroy(&mac->whisper_audiohook);
|
||||
}
|
||||
if (mac->bridge_connected) {
|
||||
if (mac->bridge_whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
ast_debug(2, "Whisper (bridged) audiohook no longer running\n");
|
||||
}
|
||||
ast_audiohook_lock(&mac->bridge_whisper_audiohook);
|
||||
ast_audiohook_detach(&mac->bridge_whisper_audiohook);
|
||||
ast_audiohook_unlock(&mac->bridge_whisper_audiohook);
|
||||
ast_audiohook_destroy(&mac->bridge_whisper_audiohook);
|
||||
}
|
||||
if (mac->spying) {
|
||||
if (mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
ast_debug(2, "Spy audiohook no longer running\n");
|
||||
}
|
||||
ast_audiohook_lock(&mac->spy_audiohook);
|
||||
ast_audiohook_detach(&mac->spy_audiohook);
|
||||
ast_audiohook_unlock(&mac->spy_audiohook);
|
||||
ast_audiohook_destroy(&mac->spy_audiohook);
|
||||
}
|
||||
if (mac->name) {
|
||||
int total = mac->connected + mac->bridge_connected + mac->spying;
|
||||
ast_debug(1, "Removing channel %s from target list (%d hook%s)\n", mac->name, total, ESS(total));
|
||||
ast_free(mac->name);
|
||||
}
|
||||
if (mac->autochan) {
|
||||
ast_autochan_destroy(mac->autochan);
|
||||
}
|
||||
if (mac->bridge_autochan) {
|
||||
ast_autochan_destroy(mac->bridge_autochan);
|
||||
}
|
||||
ast_free(mac);
|
||||
}
|
||||
|
||||
static int do_broadcast(struct ast_channel *chan, struct ast_flags *flags, const char *channels)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_frame *f;
|
||||
struct ast_silence_generator *silgen = NULL;
|
||||
struct multi_spy multispy;
|
||||
struct multi_autochan_list chanlist;
|
||||
struct multi_autochan *mac;
|
||||
int numchans = 0;
|
||||
int readonly = ast_test_flag(flags, OPTION_READONLY) ? 1 : 0;
|
||||
char *next, *chansdup = ast_strdupa(channels);
|
||||
|
||||
AST_RWLIST_HEAD_INIT(&chanlist);
|
||||
ast_channel_set_flag(chan, AST_FLAG_SPYING);
|
||||
|
||||
ast_set_flag(flags, OPTION_ANSWER_WARN); /* Initialize answer warn to 1 */
|
||||
|
||||
/* Hey, look ma, no list lock needed! Sometimes, it's nice to not have to share... */
|
||||
|
||||
/* Build a list of targets */
|
||||
while ((next = strsep(&chansdup, ","))) {
|
||||
struct ast_channel *ochan;
|
||||
if (ast_strlen_zero(next)) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(next, ast_channel_name(chan))) {
|
||||
ast_log(LOG_WARNING, "Refusing to broadcast to ourself: %s\n", next);
|
||||
continue;
|
||||
}
|
||||
ochan = ast_channel_get_by_name(next);
|
||||
if (!ochan) {
|
||||
ast_log(LOG_WARNING, "No such channel: %s\n", next);
|
||||
continue;
|
||||
}
|
||||
/* Append to end of list. */
|
||||
if (!(mac = ast_calloc(1, sizeof(*mac)))) {
|
||||
ast_log(LOG_WARNING, "Multi autochan allocation failure\n");
|
||||
continue;
|
||||
}
|
||||
mac->name = ast_strdup(next);
|
||||
mac->autochan = ast_autochan_setup(ochan);
|
||||
if (!mac->name || !mac->autochan) {
|
||||
multi_autochan_free(mac);
|
||||
continue;
|
||||
}
|
||||
if (ast_test_flag(flags, OPTION_WHISPER)) {
|
||||
mac->connected = 1;
|
||||
ast_audiohook_init(&mac->whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Broadcast", 0);
|
||||
/* Inject audio from our channel to this target. */
|
||||
if (start_spying(mac->autochan, next, &mac->whisper_audiohook, flags)) {
|
||||
ast_log(LOG_WARNING, "Unable to attach whisper audiohook to %s\n", next);
|
||||
multi_autochan_free(mac);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (ast_test_flag(flags, OPTION_SPY)) {
|
||||
mac->spying = 1;
|
||||
ast_audiohook_init(&mac->spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "Broadcast", 0);
|
||||
if (start_spying(mac->autochan, next, &mac->spy_audiohook, flags)) {
|
||||
ast_log(LOG_WARNING, "Unable to attach spy audiohook to %s\n", next);
|
||||
multi_autochan_free(mac);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
AST_RWLIST_INSERT_TAIL(&chanlist, mac, entry);
|
||||
numchans++;
|
||||
ochan = ast_channel_unref(ochan);
|
||||
}
|
||||
|
||||
ast_verb(4, "Broadcasting to %d channel%s on %s\n", numchans, ESS(numchans), ast_channel_name(chan));
|
||||
ast_debug(1, "Broadcasting: (TX->1) whisper=%d, (TX->2) barge=%d, (RX<-%d) spy=%d (%s)\n",
|
||||
ast_test_flag(flags, OPTION_WHISPER) ? 1 : 0,
|
||||
ast_test_flag(flags, OPTION_BARGE) ? 1 : 0,
|
||||
readonly ? 1 : 2,
|
||||
ast_test_flag(flags, OPTION_SPY) ? 1 : 0,
|
||||
readonly ? "single" : "both");
|
||||
|
||||
if (ast_test_flag(flags, OPTION_SPY)) {
|
||||
multispy.chanlist = &chanlist;
|
||||
multispy.readonly = readonly;
|
||||
ast_activate_generator(chan, &spygen, &multispy);
|
||||
} else {
|
||||
/* We're not expecting to read any audio, just broadcast audio to a bunch of other channels. */
|
||||
silgen = ast_channel_start_silence_generator(chan);
|
||||
}
|
||||
|
||||
while (numchans && ast_waitfor(chan, -1) > 0) {
|
||||
int fres = 0;
|
||||
f = ast_read(chan);
|
||||
if (!f) {
|
||||
ast_debug(1, "Channel %s must have hung up\n", ast_channel_name(chan));
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
if (f->frametype != AST_FRAME_VOICE) { /* Ignore any non-voice frames */
|
||||
ast_frfree(f);
|
||||
continue;
|
||||
}
|
||||
/* Write the frame to all our targets. */
|
||||
AST_RWLIST_WRLOCK(&chanlist);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&chanlist, mac, entry) {
|
||||
/* Note that if no media is received, execution is suspended, but assuming continuous or
|
||||
* or frequent audio on the broadcasting channel, we'll quickly enough detect hung up targets.
|
||||
* This isn't really an issue, just something that might be confusing at first, but this is
|
||||
* due to the limitation with audiohooks of using the channel for timing. */
|
||||
if ((ast_test_flag(flags, OPTION_WHISPER) && mac->whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
|
||||
|| (ast_test_flag(flags, OPTION_SPY) && mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
|
||||
|| (mac->bridge_connected && ast_test_flag(flags, OPTION_BARGE) && mac->bridge_whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)) {
|
||||
/* Even if we're spying only and not actually broadcasting audio, we need to detect channel hangup. */
|
||||
AST_RWLIST_REMOVE_CURRENT(entry);
|
||||
ast_debug(2, "Looks like %s has hung up\n", mac->name);
|
||||
multi_autochan_free(mac);
|
||||
numchans--;
|
||||
ast_debug(2, "%d channel%s remaining in broadcast on %s\n", numchans, ESS(numchans), ast_channel_name(chan));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ast_test_flag(flags, OPTION_WHISPER)) {
|
||||
ast_audiohook_lock(&mac->whisper_audiohook);
|
||||
fres |= ast_audiohook_write_frame(&mac->whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
|
||||
ast_audiohook_unlock(&mac->whisper_audiohook);
|
||||
}
|
||||
|
||||
if (ast_test_flag(flags, OPTION_BARGE)) {
|
||||
/* This hook lets us inject audio into the channel that the spyee is currently
|
||||
* bridged with. If the spyee isn't bridged with anything yet, nothing will
|
||||
* be attached and we'll need to continue attempting to attach the barge
|
||||
* audio hook.
|
||||
* The exception to this is if we are emulating barge by doing it "directly",
|
||||
* that is injecting the frames onto this channel's read queue, rather than
|
||||
* its bridged peer's write queue, then skip this. We only do one or the other. */
|
||||
if (!ast_test_flag(flags, OPTION_REVERSE_FEED) && !mac->bridge_connected && !attach_barge(mac->autochan, &mac->bridge_autochan,
|
||||
&mac->bridge_whisper_audiohook, ast_channel_name(chan), mac->name, flags)) {
|
||||
ast_debug(2, "Attached barge channel for %s\n", mac->name);
|
||||
mac->bridge_connected = 1;
|
||||
}
|
||||
|
||||
if (mac->bridge_connected) {
|
||||
ast_audiohook_lock(&mac->bridge_whisper_audiohook);
|
||||
fres |= ast_audiohook_write_frame(&mac->bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
|
||||
ast_audiohook_unlock(&mac->bridge_whisper_audiohook);
|
||||
} else if (ast_test_flag(flags, OPTION_REVERSE_FEED)) {
|
||||
/* So, this is really clever...
|
||||
* If we're connected to an n-party bridge instead of a 2-party bridge,
|
||||
* attach_barge will ALWAYS fail because we're connected to a bridge, not
|
||||
* a single peer channel.
|
||||
* Recall that the objective is for injected audio to be audible to both
|
||||
* sides of the channel. So really, the typical way of doing this by
|
||||
* directly injecting frames separately onto both channels is kind of
|
||||
* bizarre to begin with, when you think about it.
|
||||
*
|
||||
* In other words, this is how ChanSpy and this module by default work:
|
||||
* We have audio F to inject onto channels A and B, which are <= bridged =>:
|
||||
* READ <- A -> WRITE <==> READ <- B -> WRITE
|
||||
* F --^ F --^
|
||||
*
|
||||
* So that makes the same audio audible to both channels A and B, but
|
||||
* in kind of a roundabout way. What if the bridged peer changes at
|
||||
* some point, for example?
|
||||
*
|
||||
* While that method works for 2-party bridges, it doesn't work at all
|
||||
* for an n-party bridge, so we do the thing that seems obvious to begin with:
|
||||
* dump the frames onto THIS channel's read queue, and the channels will
|
||||
* make their way into the bridge like any other audio from this channel,
|
||||
* and everything just works perfectly, no matter what kind of bridging
|
||||
* scenario is being used. At that point, we don't even care if we're
|
||||
* bridged or not, and really, why should we?
|
||||
*
|
||||
* In other words, we do this:
|
||||
* READ <- A -> WRITE <==> READ <- B -> WRITE
|
||||
* F --^ F --^
|
||||
*/
|
||||
ast_audiohook_lock(&mac->whisper_audiohook);
|
||||
fres |= ast_audiohook_write_frame(&mac->whisper_audiohook, AST_AUDIOHOOK_DIRECTION_READ, f);
|
||||
ast_audiohook_unlock(&mac->whisper_audiohook);
|
||||
}
|
||||
}
|
||||
if (fres) {
|
||||
ast_log(LOG_WARNING, "Failed to write to audiohook for %s\n", mac->name);
|
||||
fres = 0;
|
||||
}
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(&chanlist);
|
||||
ast_frfree(f);
|
||||
}
|
||||
|
||||
if (!numchans) {
|
||||
ast_debug(1, "Exiting due to all target channels having left the broadcast\n");
|
||||
}
|
||||
|
||||
if (ast_test_flag(flags, OPTION_SPY)) {
|
||||
ast_deactivate_generator(chan);
|
||||
} else {
|
||||
ast_channel_stop_silence_generator(chan, silgen);
|
||||
}
|
||||
|
||||
/* Cleanup any remaining targets */
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&chanlist, mac, entry) {
|
||||
AST_RWLIST_REMOVE_CURRENT(entry);
|
||||
multi_autochan_free(mac);
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
|
||||
ast_channel_clear_flag(chan, AST_FLAG_SPYING);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int broadcast_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
struct ast_flags flags;
|
||||
struct ast_format *write_format;
|
||||
int res = -1;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(options);
|
||||
AST_APP_ARG(channels); /* Channel list last, so we can have multiple */
|
||||
);
|
||||
char *parse = NULL;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Broadcast requires at least one channel\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
parse = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (ast_strlen_zero(args.channels)) {
|
||||
ast_log(LOG_WARNING, "Must specify at least one channel for broadcast\n");
|
||||
return -1;
|
||||
}
|
||||
if (args.options) {
|
||||
ast_app_parse_options(spy_opts, &flags, NULL, args.options);
|
||||
} else {
|
||||
ast_clear_flag(&flags, AST_FLAGS_ALL);
|
||||
}
|
||||
|
||||
if (!ast_test_flag(&flags, OPTION_BARGE) && !ast_test_flag(&flags, OPTION_SPY) && !ast_test_flag(&flags, OPTION_WHISPER)) {
|
||||
ast_log(LOG_WARNING, "At least one of the b, s, or w option must be specified (provided options have no effect)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_format = ao2_bump(ast_channel_writeformat(chan));
|
||||
if (ast_set_write_format(chan, ast_format_slin) < 0) {
|
||||
ast_log(LOG_ERROR, "Failed to set write format to slin.\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
res = do_broadcast(chan, &flags, args.channels);
|
||||
|
||||
/* Restore previous write format */
|
||||
if (ast_set_write_format(chan, write_format)) {
|
||||
ast_log(LOG_ERROR, "Failed to restore write format for channel %s\n", ast_channel_name(chan));
|
||||
}
|
||||
|
||||
cleanup:
|
||||
ao2_ref(write_format, -1);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
return ast_unregister_application(app_broadcast);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application_xml(app_broadcast, broadcast_exec);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Channel Audio Broadcasting");
|
|
@ -393,6 +393,37 @@
|
|||
ConfbridgeListRoomsComplete.</para>
|
||||
</description>
|
||||
</manager>
|
||||
<managerEvent language="en_US" name="ConfbridgeListRooms">
|
||||
<managerEventInstance class="EVENT_FLAG_REPORTING">
|
||||
<synopsis>Raised as part of the ConfbridgeListRooms action response list.</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Conference">
|
||||
<para>The name of the Confbridge conference.</para>
|
||||
</parameter>
|
||||
<parameter name="Parties">
|
||||
<para>Number of users in the conference.</para>
|
||||
<para>This includes both active and waiting users.</para>
|
||||
</parameter>
|
||||
<parameter name="Marked">
|
||||
<para>Number of marked users in the conference.</para>
|
||||
</parameter>
|
||||
<parameter name="Locked">
|
||||
<para>Is the conference locked?</para>
|
||||
<enumlist>
|
||||
<enum name="Yes"/>
|
||||
<enum name="No"/>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="Muted">
|
||||
<para>Is the conference muted?</para>
|
||||
<enumlist>
|
||||
<enum name="Yes"/>
|
||||
<enum name="No"/>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
<manager name="ConfbridgeMute" language="en_US">
|
||||
<synopsis>
|
||||
Mute a Confbridge user.
|
||||
|
@ -3991,6 +4022,7 @@ static int action_confbridgelist_item(struct mansession *s, const char *id_text,
|
|||
"MarkedUser: %s\r\n"
|
||||
"WaitMarked: %s\r\n"
|
||||
"EndMarked: %s\r\n"
|
||||
"EndMarkedAny: %s\r\n"
|
||||
"Waiting: %s\r\n"
|
||||
"Muted: %s\r\n"
|
||||
"Talking: %s\r\n"
|
||||
|
@ -4003,6 +4035,7 @@ static int action_confbridgelist_item(struct mansession *s, const char *id_text,
|
|||
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)),
|
||||
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)),
|
||||
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED)),
|
||||
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_ENDMARKEDANY)),
|
||||
AST_YESNO(waiting),
|
||||
AST_YESNO(user->muted),
|
||||
AST_YESNO(user->talking),
|
||||
|
|
|
@ -103,6 +103,14 @@
|
|||
receiver to their ear while entering DTMF.</para>
|
||||
<argument name="n" required="true" />
|
||||
</option>
|
||||
<option name="c">
|
||||
<para>Load the specified config file instead of voicemail.conf</para>
|
||||
<argument name="filename" required="true" />
|
||||
</option>
|
||||
<option name="s">
|
||||
<para>Skip calling the extension, instead set it in the <variable>DIRECTORY_EXTEN</variable>
|
||||
channel variable.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
<note><para>Only one of the <replaceable>f</replaceable>, <replaceable>l</replaceable>, or <replaceable>b</replaceable>
|
||||
options may be specified. <emphasis>If more than one is specified</emphasis>, then Directory will act as
|
||||
|
@ -114,12 +122,12 @@
|
|||
<description>
|
||||
<para>This application will present the calling channel with a directory of extensions from which they can search
|
||||
by name. The list of names and corresponding extensions is retrieved from the
|
||||
voicemail configuration file, <filename>voicemail.conf</filename>.</para>
|
||||
voicemail configuration file, <filename>voicemail.conf</filename>, or from the specified filename.</para>
|
||||
<para>This application will immediately exit if one of the following DTMF digits are
|
||||
received and the extension to jump to exists:</para>
|
||||
<para><literal>0</literal> - Jump to the 'o' extension, if it exists.</para>
|
||||
<para><literal>*</literal> - Jump to the 'a' extension, if it exists.</para>
|
||||
<para>This application will set the following channel variable before completion:</para>
|
||||
<para>This application will set the following channel variables before completion:</para>
|
||||
<variablelist>
|
||||
<variable name="DIRECTORY_RESULT">
|
||||
<para>Reason Directory application exited.</para>
|
||||
|
@ -131,6 +139,10 @@
|
|||
<value name="USEREXIT">User exited with '#' during selection</value>
|
||||
<value name="FAILED">The application failed</value>
|
||||
</variable>
|
||||
<variable name="DIRECTORY_EXTEN">
|
||||
<para>If the skip calling option is set this will be set to the selected extension
|
||||
provided one is selected.</para>
|
||||
</variable>
|
||||
</variablelist>
|
||||
</description>
|
||||
</application>
|
||||
|
@ -153,6 +165,8 @@ enum {
|
|||
OPT_PAUSE = (1 << 5),
|
||||
OPT_NOANSWER = (1 << 6),
|
||||
OPT_ALIAS = (1 << 7),
|
||||
OPT_CONFIG_FILE = (1 << 8),
|
||||
OPT_SKIP = (1 << 9),
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -160,8 +174,9 @@ enum {
|
|||
OPT_ARG_LASTNAME = 1,
|
||||
OPT_ARG_EITHER = 2,
|
||||
OPT_ARG_PAUSE = 3,
|
||||
OPT_ARG_FILENAME = 4,
|
||||
/* This *must* be the last value in this enum! */
|
||||
OPT_ARG_ARRAY_SIZE = 4,
|
||||
OPT_ARG_ARRAY_SIZE = 5,
|
||||
};
|
||||
|
||||
struct directory_item {
|
||||
|
@ -183,6 +198,8 @@ AST_APP_OPTIONS(directory_app_options, {
|
|||
AST_APP_OPTION('m', OPT_SELECTFROMMENU),
|
||||
AST_APP_OPTION('n', OPT_NOANSWER),
|
||||
AST_APP_OPTION('a', OPT_ALIAS),
|
||||
AST_APP_OPTION_ARG('c', OPT_CONFIG_FILE, OPT_ARG_FILENAME),
|
||||
AST_APP_OPTION('s', OPT_SKIP),
|
||||
});
|
||||
|
||||
static int compare(const char *text, const char *template)
|
||||
|
@ -318,6 +335,9 @@ static int select_entry(struct ast_channel *chan, const char *dialcontext, const
|
|||
if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
|
||||
/* We still want to set the exten though */
|
||||
ast_channel_exten_set(chan, item->exten);
|
||||
} else if (ast_test_flag(flags, OPT_SKIP)) {
|
||||
/* Skip calling the extension, only set it in the channel variable. */
|
||||
pbx_builtin_setvar_helper(chan, "DIRECTORY_EXTEN", item->exten);
|
||||
} else if (ast_goto_if_exists(chan, S_OR(dialcontext, item->context), item->exten, 1)) {
|
||||
ast_log(LOG_WARNING,
|
||||
"Can't find extension '%s' in context '%s'. "
|
||||
|
@ -458,7 +478,7 @@ static int select_item_menu(struct ast_channel *chan, struct directory_item **it
|
|||
|
||||
AST_THREADSTORAGE(commonbuf);
|
||||
|
||||
static struct ast_config *realtime_directory(char *context)
|
||||
static struct ast_config *realtime_directory(char *context, const char *filename)
|
||||
{
|
||||
struct ast_config *cfg;
|
||||
struct ast_config *rtdata = NULL;
|
||||
|
@ -475,14 +495,14 @@ static struct ast_config *realtime_directory(char *context)
|
|||
}
|
||||
|
||||
/* Load flat file config. */
|
||||
cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
|
||||
cfg = ast_config_load(filename, config_flags);
|
||||
|
||||
if (!cfg) {
|
||||
/* Loading config failed. */
|
||||
ast_log(LOG_WARNING, "Loading config failed.\n");
|
||||
return NULL;
|
||||
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
|
||||
ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", VOICEMAIL_CONFIG);
|
||||
ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -867,7 +887,9 @@ static int directory_exec(struct ast_channel *chan, const char *data)
|
|||
if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options))
|
||||
return -1;
|
||||
|
||||
if (!(cfg = realtime_directory(args.vmcontext))) {
|
||||
cfg = realtime_directory(args.vmcontext, S_OR(opts[OPT_ARG_FILENAME], VOICEMAIL_CONFIG));
|
||||
|
||||
if (!cfg) {
|
||||
ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,383 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright 2022, Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* 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 If Branch Implementation
|
||||
*
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>extended</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/channel.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="If" language="en_US">
|
||||
<synopsis>
|
||||
Start an if branch.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="expr" required="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Start an If branch. Execution will continue inside the branch
|
||||
if expr is true.</para>
|
||||
<note><para>This application (and related applications) set variables
|
||||
internally during execution.</para></note>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">ElseIf</ref>
|
||||
<ref type="application">Else</ref>
|
||||
<ref type="application">EndIf</ref>
|
||||
<ref type="application">ExitIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="ElseIf" language="en_US">
|
||||
<synopsis>
|
||||
Start an else if branch.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="expr" required="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Start an optional ElseIf branch. Execution will continue inside the branch
|
||||
if expr is true and if previous If and ElseIf branches evaluated to false.</para>
|
||||
<para>Please note that execution inside a true If branch will fallthrough into
|
||||
ElseIf unless the If segment is terminated with an ExitIf call. This is only
|
||||
necessary with ElseIf but not with Else.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">If</ref>
|
||||
<ref type="application">Else</ref>
|
||||
<ref type="application">EndIf</ref>
|
||||
<ref type="application">ExitIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="Else" language="en_US">
|
||||
<synopsis>
|
||||
Define an optional else branch.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="expr" required="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Start an Else branch. Execution will jump here if all previous
|
||||
If and ElseIf branches evaluated to false.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">If</ref>
|
||||
<ref type="application">ElseIf</ref>
|
||||
<ref type="application">EndIf</ref>
|
||||
<ref type="application">ExitIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="EndIf" language="en_US">
|
||||
<synopsis>
|
||||
End an if branch.
|
||||
</synopsis>
|
||||
<syntax />
|
||||
<description>
|
||||
<para>Ends the branch begun by the preceding <literal>If()</literal> application.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">If</ref>
|
||||
<ref type="application">ElseIf</ref>
|
||||
<ref type="application">Else</ref>
|
||||
<ref type="application">ExitIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="ExitIf" language="en_US">
|
||||
<synopsis>
|
||||
End an If branch.
|
||||
</synopsis>
|
||||
<syntax />
|
||||
<description>
|
||||
<para>Exits an <literal>If()</literal> branch, whether or not it has completed.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">If</ref>
|
||||
<ref type="application">ElseIf</ref>
|
||||
<ref type="application">Else</ref>
|
||||
<ref type="application">EndIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
static char *if_app = "If";
|
||||
static char *elseif_app = "ElseIf";
|
||||
static char *else_app = "Else";
|
||||
static char *stop_app = "EndIf";
|
||||
static char *exit_app = "ExitIf";
|
||||
|
||||
#define VAR_SIZE 64
|
||||
|
||||
static const char *get_index(struct ast_channel *chan, const char *prefix, int idx)
|
||||
{
|
||||
char varname[VAR_SIZE];
|
||||
|
||||
snprintf(varname, VAR_SIZE, "%s_%d", prefix, idx);
|
||||
return pbx_builtin_getvar_helper(chan, varname);
|
||||
}
|
||||
|
||||
static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
|
||||
{
|
||||
struct ast_exten *e;
|
||||
struct ast_context *c2;
|
||||
int idx;
|
||||
|
||||
for (e = ast_walk_context_extensions(c, NULL); e; e = ast_walk_context_extensions(c, e)) {
|
||||
if (ast_extension_match(ast_get_extension_name(e), exten)) {
|
||||
int needmatch = ast_get_extension_matchcid(e);
|
||||
if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
|
||||
(!needmatch)) {
|
||||
/* This is the matching extension we want */
|
||||
struct ast_exten *p;
|
||||
for (p = ast_walk_extension_priorities(e, NULL); p; p = ast_walk_extension_priorities(e, p)) {
|
||||
if (priority != ast_get_extension_priority(p))
|
||||
continue;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* No match; run through includes */
|
||||
for (idx = 0; idx < ast_context_includes_count(c); idx++) {
|
||||
const struct ast_include *i = ast_context_includes_get(c, idx);
|
||||
|
||||
for (c2 = ast_walk_contexts(NULL); c2; c2 = ast_walk_contexts(c2)) {
|
||||
if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
|
||||
e = find_matching_priority(c2, exten, priority, callerid);
|
||||
if (e)
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int find_matching_endif(struct ast_channel *chan, const char *otherapp)
|
||||
{
|
||||
struct ast_context *c;
|
||||
int res = -1;
|
||||
|
||||
if (ast_rdlock_contexts()) {
|
||||
ast_log(LOG_ERROR, "Failed to lock contexts list\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (c = ast_walk_contexts(NULL); c; c = ast_walk_contexts(c)) {
|
||||
struct ast_exten *e;
|
||||
|
||||
if (!ast_rdlock_context(c)) {
|
||||
if (!strcmp(ast_get_context_name(c), ast_channel_context(chan))) {
|
||||
/* This is the matching context we want */
|
||||
int cur_priority = ast_channel_priority(chan) + 1, level = 1;
|
||||
|
||||
for (e = find_matching_priority(c, ast_channel_exten(chan), cur_priority,
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
|
||||
e;
|
||||
e = find_matching_priority(c, ast_channel_exten(chan), ++cur_priority,
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
|
||||
if (!strcasecmp(ast_get_extension_app(e), "IF")) {
|
||||
level++;
|
||||
} else if (!strcasecmp(ast_get_extension_app(e), "ENDIF")) {
|
||||
level--;
|
||||
}
|
||||
|
||||
if (!otherapp && level == 0) {
|
||||
res = cur_priority;
|
||||
break;
|
||||
} else if (otherapp && level == 1 && !strcasecmp(ast_get_extension_app(e), otherapp)) {
|
||||
res = cur_priority;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_unlock_context(c);
|
||||
if (res > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_unlock_contexts();
|
||||
return res;
|
||||
}
|
||||
|
||||
static int if_helper(struct ast_channel *chan, const char *data, int end)
|
||||
{
|
||||
int res = 0;
|
||||
const char *if_pri = NULL;
|
||||
char *my_name = NULL;
|
||||
const char *label = NULL;
|
||||
char varname[VAR_SIZE + 3]; /* + IF_ */
|
||||
char end_varname[sizeof(varname) + 4]; /* + END_ + sizeof(varname) */
|
||||
const char *prefix = "IF";
|
||||
size_t size = 0;
|
||||
int used_index_i = -1, x = 0;
|
||||
char used_index[VAR_SIZE] = "0", new_index[VAR_SIZE] = "0";
|
||||
|
||||
if (!chan) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (x = 0 ;; x++) {
|
||||
if (get_index(chan, prefix, x)) {
|
||||
used_index_i = x;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(used_index, sizeof(used_index), "%d", used_index_i);
|
||||
snprintf(new_index, sizeof(new_index), "%d", used_index_i + 1);
|
||||
|
||||
size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
|
||||
my_name = ast_alloca(size);
|
||||
memset(my_name, 0, size);
|
||||
snprintf(my_name, size, "%s_%s_%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
|
||||
|
||||
ast_channel_lock(chan);
|
||||
if (end > 1) {
|
||||
label = used_index;
|
||||
} else if (!(label = pbx_builtin_getvar_helper(chan, my_name))) {
|
||||
label = new_index;
|
||||
pbx_builtin_setvar_helper(chan, my_name, label);
|
||||
}
|
||||
snprintf(varname, sizeof(varname), "%s_%s", prefix, label);
|
||||
if ((if_pri = pbx_builtin_getvar_helper(chan, varname)) && !end) {
|
||||
if_pri = ast_strdupa(if_pri);
|
||||
snprintf(end_varname,sizeof(end_varname),"END_%s",varname);
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
if ((end <= 1 && !pbx_checkcondition(ast_strdupa(data))) || (end > 1)) {
|
||||
/* Condition Met (clean up helper vars) */
|
||||
const char *goto_str;
|
||||
int pri, endifpri;
|
||||
pbx_builtin_setvar_helper(chan, varname, NULL);
|
||||
pbx_builtin_setvar_helper(chan, my_name, NULL);
|
||||
snprintf(end_varname,sizeof(end_varname),"END_%s",varname);
|
||||
ast_channel_lock(chan);
|
||||
endifpri = find_matching_endif(chan, NULL);
|
||||
if ((goto_str = pbx_builtin_getvar_helper(chan, end_varname))) {
|
||||
ast_parseable_goto(chan, goto_str);
|
||||
pbx_builtin_setvar_helper(chan, end_varname, NULL);
|
||||
} else if (end <= 1 && (pri = find_matching_endif(chan, "ElseIf")) > 0 && pri < endifpri) {
|
||||
pri--; /* back up a priority, since it returned the priority after the ElseIf */
|
||||
/* If is false, and ElseIf exists, so jump to ElseIf */
|
||||
ast_verb(3, "Taking conditional false branch, jumping to priority %d\n", pri);
|
||||
ast_channel_priority_set(chan, pri);
|
||||
} else if (end <= 1 && (pri = find_matching_endif(chan, "Else")) > 0 && pri < endifpri) {
|
||||
/* don't need to back up a priority, because we don't actually need to execute Else, just jump to the priority after. Directly executing Else will exit the conditional. */
|
||||
/* If is false, and Else exists, so jump to Else */
|
||||
ast_verb(3, "Taking absolute false branch, jumping to priority %d\n", pri);
|
||||
ast_channel_priority_set(chan, pri);
|
||||
} else {
|
||||
pri = endifpri;
|
||||
if (pri > 0) {
|
||||
ast_verb(3, "Exiting conditional, jumping to priority %d\n", pri);
|
||||
ast_channel_priority_set(chan, pri);
|
||||
} else if (end == 4) { /* Condition added because of end > 0 instead of end == 4 */
|
||||
ast_log(LOG_WARNING, "Couldn't find matching EndIf? (If at %s@%s priority %d)\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
|
||||
}
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (end <= 1 && !if_pri) {
|
||||
char *goto_str;
|
||||
size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
|
||||
goto_str = ast_alloca(size);
|
||||
memset(goto_str, 0, size);
|
||||
snprintf(goto_str, size, "%s,%s,%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
|
||||
pbx_builtin_setvar_helper(chan, varname, goto_str);
|
||||
} else if (end > 1 && if_pri) {
|
||||
/* END of branch */
|
||||
snprintf(end_varname, sizeof(end_varname), "END_%s", varname);
|
||||
if (!pbx_builtin_getvar_helper(chan, end_varname)) {
|
||||
char *goto_str;
|
||||
size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
|
||||
goto_str = ast_alloca(size);
|
||||
memset(goto_str, 0, size);
|
||||
snprintf(goto_str, size, "%s,%s,%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan)+1);
|
||||
pbx_builtin_setvar_helper(chan, end_varname, goto_str);
|
||||
}
|
||||
ast_parseable_goto(chan, if_pri);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int if_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 0);
|
||||
}
|
||||
|
||||
static int elseif_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 1);
|
||||
}
|
||||
|
||||
static int end_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 2);
|
||||
}
|
||||
|
||||
static int else_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 3);
|
||||
}
|
||||
|
||||
static int exit_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 4);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(if_app);
|
||||
res |= ast_unregister_application(elseif_app);
|
||||
res |= ast_unregister_application(stop_app);
|
||||
res |= ast_unregister_application(else_app);
|
||||
res |= ast_unregister_application(exit_app);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_register_application_xml(if_app, if_exec);
|
||||
res |= ast_register_application_xml(elseif_app, elseif_exec);
|
||||
res |= ast_register_application_xml(stop_app, end_exec);
|
||||
res |= ast_register_application_xml(else_app, else_exec);
|
||||
res |= ast_register_application_xml(exit_app, exit_exec);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "If Branch and Conditional Execution");
|
|
@ -639,6 +639,82 @@
|
|||
</syntax>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
<managerEvent language="en_US" name="MeetmeList">
|
||||
<managerEventInstance class="EVENT_FLAG_CALL">
|
||||
<synopsis>Raised in response to a MeetmeList command.</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Conference">
|
||||
<para>Conference ID.</para>
|
||||
</parameter>
|
||||
<parameter name="UserNumber">
|
||||
<para>User ID.</para>
|
||||
</parameter>
|
||||
<parameter name="CallerIDNum">
|
||||
<para>Caller ID number.</para>
|
||||
</parameter>
|
||||
<parameter name="CallerIDName">
|
||||
<para>Caller ID name.</para>
|
||||
</parameter>
|
||||
<parameter name="ConnectedLineNum">
|
||||
<para>Connected Line number.</para>
|
||||
</parameter>
|
||||
<parameter name="ConnectedLineName">
|
||||
<para>Connected Line name.</para>
|
||||
</parameter>
|
||||
<parameter name="Channel">
|
||||
<para>Channel name</para>
|
||||
</parameter>
|
||||
<parameter name="Admin">
|
||||
<para>Whether or not the user is an admin.</para>
|
||||
</parameter>
|
||||
<parameter name="Role">
|
||||
<para>User role. Can be "Listen only", "Talk only", or "Talk and listen".</para>
|
||||
</parameter>
|
||||
<parameter name="MarkedUser">
|
||||
<para>Whether or not the user is a marked user.</para>
|
||||
</parameter>
|
||||
<parameter name="Muted">
|
||||
<para>Whether or not the user is currently muted.</para>
|
||||
</parameter>
|
||||
<parameter name="Talking">
|
||||
<para>Whether or not the user is currently talking.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<see-also>
|
||||
<ref type="manager">MeetmeList</ref>
|
||||
<ref type="application">MeetMe</ref>
|
||||
</see-also>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
<managerEvent language="en_US" name="MeetmeListRooms">
|
||||
<managerEventInstance class="EVENT_FLAG_CALL">
|
||||
<synopsis>Raised in response to a MeetmeListRooms command.</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Conference">
|
||||
<para>Conference ID.</para>
|
||||
</parameter>
|
||||
<parameter name="Parties">
|
||||
<para>Number of parties in the conference.</para>
|
||||
</parameter>
|
||||
<parameter name="Marked">
|
||||
<para>Number of marked users in the conference.</para>
|
||||
</parameter>
|
||||
<parameter name="Activity">
|
||||
<para>Total duration of conference in HH:MM:SS format.</para>
|
||||
</parameter>
|
||||
<parameter name="Creation">
|
||||
<para>How the conference was created: "Dyanmic" or "Static".</para>
|
||||
</parameter>
|
||||
<parameter name="Locked">
|
||||
<para>Whether or not the conference is locked.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<see-also>
|
||||
<ref type="manager">MeetmeListRooms</ref>
|
||||
<ref type="application">MeetMe</ref>
|
||||
</see-also>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
***/
|
||||
|
||||
#define CONFIG_FILE_NAME "meetme.conf"
|
||||
|
@ -6073,7 +6149,7 @@ struct run_station_args {
|
|||
|
||||
static void answer_trunk_chan(struct ast_channel *chan)
|
||||
{
|
||||
ast_answer(chan);
|
||||
ast_raw_answer(chan);
|
||||
ast_indicate(chan, -1);
|
||||
}
|
||||
|
||||
|
@ -6918,8 +6994,18 @@ static void *dial_trunk(void *data)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
/* Wait for dial to end, while servicing the channel */
|
||||
while (ast_waitfor(trunk_ref->chan, 100)) {
|
||||
unsigned int done = 0;
|
||||
struct ast_frame *fr = ast_read(trunk_ref->chan);
|
||||
|
||||
if (!fr) {
|
||||
ast_debug(1, "Channel %s did not return a frame, must have hung up\n", ast_channel_name(trunk_ref->chan));
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
ast_frfree(fr); /* Ignore while dialing */
|
||||
|
||||
switch ((dial_res = ast_dial_state(dial))) {
|
||||
case AST_DIAL_RESULT_ANSWERED:
|
||||
trunk_ref->trunk->chan = ast_dial_answered(dial);
|
||||
|
@ -6956,8 +7042,6 @@ static void *dial_trunk(void *data)
|
|||
last_state = current_state;
|
||||
}
|
||||
|
||||
/* avoid tight loop... sleep for 1/10th second */
|
||||
ast_safe_sleep(trunk_ref->chan, 100);
|
||||
}
|
||||
|
||||
if (!trunk_ref->trunk->chan) {
|
||||
|
@ -7116,8 +7200,10 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
|
|||
sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
|
||||
/* Create a thread to dial the trunk and dump it into the conference.
|
||||
* However, we want to wait until the trunk has been dialed and the
|
||||
* conference is created before continuing on here. */
|
||||
ast_autoservice_start(chan);
|
||||
* conference is created before continuing on here.
|
||||
* Don't autoservice the channel or we'll have multiple threads
|
||||
* handling it. dial_trunk services the channel.
|
||||
*/
|
||||
ast_mutex_init(&cond_lock);
|
||||
ast_cond_init(&cond, NULL);
|
||||
ast_mutex_lock(&cond_lock);
|
||||
|
@ -7126,7 +7212,7 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
|
|||
ast_mutex_unlock(&cond_lock);
|
||||
ast_mutex_destroy(&cond_lock);
|
||||
ast_cond_destroy(&cond);
|
||||
ast_autoservice_stop(chan);
|
||||
|
||||
if (!trunk_ref->trunk->chan) {
|
||||
ast_debug(1, "Trunk didn't get created. chan: %lx\n", (unsigned long) trunk_ref->trunk->chan);
|
||||
pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
|
||||
|
|
|
@ -235,6 +235,7 @@ static const char sendmf_name[] = "SendMF";
|
|||
* \param buflen Size of buffer
|
||||
* \param timeout ms to wait for all digits before giving up
|
||||
* \param features Any additional DSP features to use
|
||||
* \param laxkp Receive digits even if KP not received
|
||||
* \param override Start over if we receive additional KPs
|
||||
* \param no_kp Don't include KP in the output
|
||||
* \param no_st Don't include start digits in the output
|
||||
|
|
|
@ -90,6 +90,16 @@
|
|||
<para>Play a periodic beep while this call is being recorded.</para>
|
||||
<argument name="interval"><para>Interval, in seconds. Default is 15.</para></argument>
|
||||
</option>
|
||||
<option name="c">
|
||||
<para>Use the real Caller ID from the channel for the voicemail Caller ID.</para>
|
||||
<para>By default, the Connected Line is used. If you want the channel caller's
|
||||
real number, you may need to specify this option.</para>
|
||||
</option>
|
||||
<option name="d">
|
||||
<para>Delete the recording file as soon as MixMonitor is done with it.</para>
|
||||
<para>For example, if you use the m option to dispatch the recording to a voicemail box,
|
||||
you can specify this option to delete the original copy of it afterwards.</para>
|
||||
</option>
|
||||
<option name="v">
|
||||
<para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
|
||||
(range <literal>-4</literal> to <literal>4</literal>)</para>
|
||||
|
@ -407,6 +417,8 @@ enum mixmonitor_flags {
|
|||
MUXFLAG_BEEP_STOP = (1 << 13),
|
||||
MUXFLAG_DEPRECATED_RWSYNC = (1 << 14),
|
||||
MUXFLAG_NO_RWSYNC = (1 << 15),
|
||||
MUXFLAG_AUTO_DELETE = (1 << 16),
|
||||
MUXFLAG_REAL_CALLERID = (1 << 17),
|
||||
};
|
||||
|
||||
enum mixmonitor_args {
|
||||
|
@ -427,6 +439,8 @@ AST_APP_OPTIONS(mixmonitor_opts, {
|
|||
AST_APP_OPTION('a', MUXFLAG_APPEND),
|
||||
AST_APP_OPTION('b', MUXFLAG_BRIDGED),
|
||||
AST_APP_OPTION_ARG('B', MUXFLAG_BEEP, OPT_ARG_BEEP_INTERVAL),
|
||||
AST_APP_OPTION('c', MUXFLAG_REAL_CALLERID),
|
||||
AST_APP_OPTION('d', MUXFLAG_AUTO_DELETE),
|
||||
AST_APP_OPTION('p', MUXFLAG_BEEP_START),
|
||||
AST_APP_OPTION('P', MUXFLAG_BEEP_STOP),
|
||||
AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
|
||||
|
@ -860,6 +874,19 @@ static void *mixmonitor_thread(void *obj)
|
|||
ast_debug(3, "No recipients to forward monitor to, moving on.\n");
|
||||
}
|
||||
|
||||
if (ast_test_flag(mixmonitor, MUXFLAG_AUTO_DELETE)) {
|
||||
ast_debug(3, "Deleting our copies of recording files\n");
|
||||
if (!ast_strlen_zero(fs_ext)) {
|
||||
ast_filedelete(mixmonitor->filename, fs_ext);
|
||||
}
|
||||
if (!ast_strlen_zero(fs_read_ext)) {
|
||||
ast_filedelete(mixmonitor->filename_read, fs_ext);
|
||||
}
|
||||
if (!ast_strlen_zero(fs_write_ext)) {
|
||||
ast_filedelete(mixmonitor->filename_write, fs_ext);
|
||||
}
|
||||
}
|
||||
|
||||
mixmonitor_free(mixmonitor);
|
||||
|
||||
ast_module_unref(ast_module_info->self);
|
||||
|
@ -1015,20 +1042,37 @@ static int launch_monitor_thread(struct ast_channel *chan, const char *filename,
|
|||
|
||||
if (!ast_strlen_zero(recipients)) {
|
||||
char callerid[256];
|
||||
struct ast_party_connected_line *connected;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
|
||||
/* We use the connected line of the invoking channel for caller ID. */
|
||||
/* We use the connected line of the invoking channel for caller ID,
|
||||
* unless we've been told to use the Caller ID.
|
||||
* The initial use for this relied on Connected Line to get the
|
||||
* actual number for recording with Digium phones,
|
||||
* but in generic use the Caller ID is likely what people want.
|
||||
*/
|
||||
|
||||
connected = ast_channel_connected(chan);
|
||||
ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
|
||||
connected->id.name.str, connected->id.number.valid,
|
||||
connected->id.number.str);
|
||||
ast_callerid_merge(callerid, sizeof(callerid),
|
||||
S_COR(connected->id.name.valid, connected->id.name.str, NULL),
|
||||
S_COR(connected->id.number.valid, connected->id.number.str, NULL),
|
||||
"Unknown");
|
||||
if (ast_test_flag(mixmonitor, MUXFLAG_REAL_CALLERID)) {
|
||||
struct ast_party_caller *caller;
|
||||
caller = ast_channel_caller(chan);
|
||||
ast_debug(3, "Caller ID = %d - %s : %d - %s\n", caller->id.name.valid,
|
||||
caller->id.name.str, caller->id.number.valid,
|
||||
caller->id.number.str);
|
||||
ast_callerid_merge(callerid, sizeof(callerid),
|
||||
S_COR(caller->id.name.valid, caller->id.name.str, NULL),
|
||||
S_COR(caller->id.number.valid, caller->id.number.str, NULL),
|
||||
"Unknown");
|
||||
} else {
|
||||
struct ast_party_connected_line *connected;
|
||||
connected = ast_channel_connected(chan);
|
||||
ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
|
||||
connected->id.name.str, connected->id.number.valid,
|
||||
connected->id.number.str);
|
||||
ast_callerid_merge(callerid, sizeof(callerid),
|
||||
S_COR(connected->id.name.valid, connected->id.name.str, NULL),
|
||||
S_COR(connected->id.number.valid, connected->id.number.str, NULL),
|
||||
"Unknown");
|
||||
}
|
||||
|
||||
ast_string_field_set(mixmonitor, call_context, ast_channel_context(chan));
|
||||
ast_string_field_set(mixmonitor, call_macrocontext, ast_channel_macrocontext(chan));
|
||||
|
|
|
@ -101,7 +101,7 @@ static int mp3play(const char *filename, unsigned int sampling_rate, int fd)
|
|||
/* Execute mpg123, but buffer if it's a net connection */
|
||||
if (!strncasecmp(filename, "http://", 7) && strstr(filename, ".m3u")) {
|
||||
char buffer_size_str[8];
|
||||
snprintf(buffer_size_str, 8, "%u", (int) 0.5*2*sampling_rate/1000); // 0.5 seconds for a live stream
|
||||
snprintf(buffer_size_str, 8, "%u", (int) 0.5*2*sampling_rate/1000); /* 0.5 seconds for a live stream */
|
||||
/* Most commonly installed in /usr/local/bin */
|
||||
execl(LOCAL_MPG_123, "mpg123", "-e", "s16", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
||||
/* But many places has it in /usr/bin */
|
||||
|
@ -111,7 +111,7 @@ static int mp3play(const char *filename, unsigned int sampling_rate, int fd)
|
|||
}
|
||||
else if (!strncasecmp(filename, "http://", 7)) {
|
||||
char buffer_size_str[8];
|
||||
snprintf(buffer_size_str, 8, "%u", 6*2*sampling_rate/1000); // 6 seconds for a remote MP3 file
|
||||
snprintf(buffer_size_str, 8, "%u", 6*2*sampling_rate/1000); /* 6 seconds for a remote MP3 file */
|
||||
/* Most commonly installed in /usr/local/bin */
|
||||
execl(LOCAL_MPG_123, "mpg123", "-e", "s16", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
||||
/* But many places has it in /usr/bin */
|
||||
|
|
|
@ -507,8 +507,7 @@ static int playback_exec(struct ast_channel *chan, const char *data)
|
|||
if (!res) {
|
||||
res = ast_waitstream(chan, "");
|
||||
ast_stopstream(chan);
|
||||
}
|
||||
if (res) {
|
||||
} else {
|
||||
if (!ast_check_hangup(chan)) {
|
||||
ast_log(LOG_WARNING, "Playback failed on %s for %s\n", ast_channel_name(chan), (char *)data);
|
||||
}
|
||||
|
|
|
@ -1277,7 +1277,7 @@
|
|||
</syntax>
|
||||
<see-also>
|
||||
<ref type="application">PauseQueueMember</ref>
|
||||
<ref type="application">UnPauseQueueMember</ref>
|
||||
<ref type="application">UnpauseQueueMember</ref>
|
||||
</see-also>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
|
@ -2956,7 +2956,11 @@ static void init_queue(struct call_queue *q)
|
|||
q->timeout = DEFAULT_TIMEOUT;
|
||||
q->maxlen = 0;
|
||||
|
||||
ast_string_field_set(q, announce, "");
|
||||
ast_string_field_set(q, context, "");
|
||||
ast_string_field_set(q, membermacro, "");
|
||||
ast_string_field_set(q, membergosub, "");
|
||||
ast_string_field_set(q, defaultrule, "");
|
||||
|
||||
q->announcefrequency = 0;
|
||||
q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
|
||||
|
@ -2985,7 +2989,10 @@ static void init_queue(struct call_queue *q)
|
|||
q->periodicannouncefrequency = 0;
|
||||
q->randomperiodicannounce = 0;
|
||||
q->numperiodicannounce = 0;
|
||||
q->relativeperiodicannounce = 0;
|
||||
q->autopause = QUEUE_AUTOPAUSE_OFF;
|
||||
q->autopausebusy = 0;
|
||||
q->autopauseunavail = 0;
|
||||
q->timeoutpriority = TIMEOUT_PRIORITY_APP;
|
||||
q->autopausedelay = 0;
|
||||
if (!q->members) {
|
||||
|
@ -3010,6 +3017,7 @@ static void init_queue(struct call_queue *q)
|
|||
ast_string_field_set(q, sound_minute, "queue-minute");
|
||||
ast_string_field_set(q, sound_seconds, "queue-seconds");
|
||||
ast_string_field_set(q, sound_thanks, "queue-thankyou");
|
||||
ast_string_field_set(q, sound_callerannounce, "");
|
||||
ast_string_field_set(q, sound_reporthold, "queue-reporthold");
|
||||
|
||||
if (!q->sound_periodicannounce[0]) {
|
||||
|
@ -8172,7 +8180,7 @@ static int pqm_exec(struct ast_channel *chan, const char *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief UnPauseQueueMember application */
|
||||
/*! \brief UnpauseQueueMember application */
|
||||
static int upqm_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
char *parse;
|
||||
|
@ -8193,7 +8201,7 @@ static int upqm_exec(struct ast_channel *chan, const char *data)
|
|||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (ast_strlen_zero(args.interface)) {
|
||||
ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
|
||||
ast_log(LOG_WARNING, "Missing interface argument to UnpauseQueueMember ([queuename],interface[,options[,reason]])\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,13 @@
|
|||
and you will need to rely on duration and max digits
|
||||
for ending input.</para>
|
||||
</option>
|
||||
<option name="e">
|
||||
<para>to read the terminator as the digit string if the
|
||||
only digit read is the terminator. This is for cases
|
||||
where the terminator is a valid digit, but only by itself.
|
||||
ie; <literal>1234</literal> and <literal>#</literal> are
|
||||
valid, but <literal>1234#</literal> is not.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
<parameter name="attempts">
|
||||
|
@ -125,6 +132,7 @@ enum read_option_flags {
|
|||
OPT_INDICATION = (1 << 1),
|
||||
OPT_NOANSWER = (1 << 2),
|
||||
OPT_TERMINATOR = (1 << 3),
|
||||
OPT_KEEP_TERMINATOR = (1 << 4),
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -138,6 +146,7 @@ AST_APP_OPTIONS(read_app_options, {
|
|||
AST_APP_OPTION('i', OPT_INDICATION),
|
||||
AST_APP_OPTION('n', OPT_NOANSWER),
|
||||
AST_APP_OPTION_ARG('t', OPT_TERMINATOR, OPT_ARG_TERMINATOR),
|
||||
AST_APP_OPTION('e', OPT_KEEP_TERMINATOR),
|
||||
});
|
||||
|
||||
static char *app = "Read";
|
||||
|
@ -261,12 +270,20 @@ static int read_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
} else {
|
||||
res = ast_app_getdata_terminator(chan, arglist.filename, tmp, maxdigits, to, terminator);
|
||||
if (res == AST_GETDATA_COMPLETE || res == AST_GETDATA_EMPTY_END_TERMINATED)
|
||||
if (res == AST_GETDATA_COMPLETE) {
|
||||
status = "OK";
|
||||
else if (res == AST_GETDATA_TIMEOUT)
|
||||
} else if (res == AST_GETDATA_EMPTY_END_TERMINATED) {
|
||||
if (ast_test_flag(&flags, OPT_KEEP_TERMINATOR)) {
|
||||
/* if the option is set to do so, read the
|
||||
returned string as the terminator string */
|
||||
ast_copy_string(tmp, terminator, sizeof(tmp));
|
||||
}
|
||||
status = "OK";
|
||||
} else if (res == AST_GETDATA_TIMEOUT) {
|
||||
status = "TIMEOUT";
|
||||
else if (res == AST_GETDATA_INTERRUPTED)
|
||||
} else if (res == AST_GETDATA_INTERRUPTED) {
|
||||
status = "INTERRUPTED";
|
||||
}
|
||||
}
|
||||
if (res > -1) {
|
||||
pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
|
||||
|
|
|
@ -57,6 +57,15 @@
|
|||
<parameter name="channel" required="false">
|
||||
<para>Channel where digits will be played</para>
|
||||
</parameter>
|
||||
<parameter name="options">
|
||||
<optionlist>
|
||||
<option name="a">
|
||||
<para>Answer the channel specified by the <literal>channel</literal>
|
||||
parameter if it is not already up. If no <literal>channel</literal>
|
||||
parameter is provided, the current channel will be answered.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>It will send all digits or terminate if it encounters an error.</para>
|
||||
|
@ -90,6 +99,19 @@
|
|||
</manager>
|
||||
***/
|
||||
|
||||
enum read_option_flags {
|
||||
OPT_ANSWER = (1 << 0),
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(senddtmf_app_options, {
|
||||
AST_APP_OPTION('a', OPT_ANSWER),
|
||||
});
|
||||
|
||||
enum {
|
||||
/* note: this entry _MUST_ be the last one in the enum */
|
||||
OPT_ARG_ARRAY_SIZE,
|
||||
};
|
||||
|
||||
static const char senddtmf_name[] = "SendDTMF";
|
||||
|
||||
static int senddtmf_exec(struct ast_channel *chan, const char *vdata)
|
||||
|
@ -100,11 +122,14 @@ static int senddtmf_exec(struct ast_channel *chan, const char *vdata)
|
|||
struct ast_channel *chan_found = NULL;
|
||||
struct ast_channel *chan_dest = chan;
|
||||
struct ast_channel *chan_autoservice = NULL;
|
||||
char *opt_args[OPT_ARG_ARRAY_SIZE];
|
||||
struct ast_flags flags = {0};
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(digits);
|
||||
AST_APP_ARG(dinterval);
|
||||
AST_APP_ARG(duration);
|
||||
AST_APP_ARG(channel);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(vdata)) {
|
||||
|
@ -136,6 +161,12 @@ static int senddtmf_exec(struct ast_channel *chan, const char *vdata)
|
|||
chan_autoservice = chan;
|
||||
}
|
||||
}
|
||||
if (!ast_strlen_zero(args.options)) {
|
||||
ast_app_parse_options(senddtmf_app_options, &flags, opt_args, args.options);
|
||||
}
|
||||
if (ast_test_flag(&flags, OPT_ANSWER)) {
|
||||
ast_auto_answer(chan_dest);
|
||||
}
|
||||
res = ast_dtmf_stream(chan_dest, chan_autoservice, args.digits,
|
||||
dinterval <= 0 ? 250 : dinterval, duration);
|
||||
if (chan_found) {
|
||||
|
|
|
@ -139,8 +139,6 @@
|
|||
</example>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">SendImage</ref>
|
||||
<ref type="application">SendURL</ref>
|
||||
<ref type="application">ReceiveText</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
|
@ -182,8 +180,6 @@
|
|||
</description>
|
||||
<see-also>
|
||||
<ref type="application">SendText</ref>
|
||||
<ref type="application">SendImage</ref>
|
||||
<ref type="application">SendURL</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
|
|
@ -0,0 +1,471 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2022, Naveen Albert
|
||||
*
|
||||
* Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* 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 Channel signaling applications
|
||||
*
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>extended</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/module.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="Signal" language="en_US">
|
||||
<synopsis>
|
||||
Sends a signal to any waiting channels.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="signalname" required="true">
|
||||
<para>Name of signal to send.</para>
|
||||
</parameter>
|
||||
<parameter name="payload" required="false">
|
||||
<para>Payload data to deliver.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Sends a named signal to any channels that may be
|
||||
waiting for one. Acts as a producer in a simple
|
||||
message queue.</para>
|
||||
<variablelist>
|
||||
<variable name="SIGNALSTATUS">
|
||||
<value name="SUCCESS">
|
||||
Signal was successfully sent to at least
|
||||
one listener for processing.
|
||||
</value>
|
||||
<value name="FAILURE">
|
||||
Signal could not be sent or nobody
|
||||
was listening for this signal.
|
||||
</value>
|
||||
</variable>
|
||||
</variablelist>
|
||||
<example title="Send a signal named workdone">
|
||||
same => n,Signal(workdone,Work has completed)
|
||||
</example>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">WaitForSignal</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="WaitForSignal" language="en_US">
|
||||
<synopsis>
|
||||
Waits for a named signal on a channel.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="signalname" required="true">
|
||||
<para>Name of signal to send.</para>
|
||||
</parameter>
|
||||
<parameter name="signaltimeout" required="false">
|
||||
<para>Maximum time, in seconds, to wait for signal.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Waits for <replaceable>signaltimeout</replaceable> seconds on the current
|
||||
channel to receive a signal with name <replaceable>signalname</replaceable>.
|
||||
Acts as a consumer in a simple message queue.</para>
|
||||
<para>Result of signal wait will be stored in the following variables:</para>
|
||||
<variablelist>
|
||||
<variable name="WAITFORSIGNALSTATUS">
|
||||
<value name="SIGNALED">
|
||||
Signal was received.
|
||||
</value>
|
||||
<value name="TIMEOUT">
|
||||
Timed out waiting for signal.
|
||||
</value>
|
||||
<value name="HANGUP">
|
||||
Channel hung up before signal was received.
|
||||
</value>
|
||||
</variable>
|
||||
<variable name="WAITFORSIGNALPAYLOAD">
|
||||
<para>Data payload attached to signal, if it exists</para>
|
||||
</variable>
|
||||
</variablelist>
|
||||
<example title="Wait for the workdone signal, indefinitely, and print out payload">
|
||||
same => n,WaitForSignal(workdone)
|
||||
same => n,NoOp(Received: ${WAITFORSIGNALPAYLOAD})
|
||||
</example>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">Signal</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
static const char * const app = "Signal";
|
||||
static const char * const app2 = "WaitForSignal";
|
||||
|
||||
struct signalitem {
|
||||
ast_mutex_t lock;
|
||||
char name[AST_MAX_CONTEXT];
|
||||
int sig_alert_pipe[2];
|
||||
int watchers;
|
||||
unsigned int signaled:1;
|
||||
char *payload;
|
||||
AST_LIST_ENTRY(signalitem) entry; /*!< Next Signal item */
|
||||
};
|
||||
|
||||
static AST_RWLIST_HEAD_STATIC(signals, signalitem);
|
||||
|
||||
static struct signalitem *alloc_signal(const char *sname)
|
||||
{
|
||||
struct signalitem *s;
|
||||
|
||||
if (!(s = ast_calloc(1, sizeof(*s)))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_mutex_init(&s->lock);
|
||||
ast_copy_string(s->name, sname, sizeof(s->name));
|
||||
|
||||
s->sig_alert_pipe[0] = -1;
|
||||
s->sig_alert_pipe[1] = -1;
|
||||
s->watchers = 0;
|
||||
s->payload = NULL;
|
||||
ast_alertpipe_init(s->sig_alert_pipe);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static int dealloc_signal(struct signalitem *s)
|
||||
{
|
||||
if (s->watchers) { /* somebody is still using us... refuse to go away */
|
||||
ast_debug(1, "Signal '%s' is still being used by %d listener(s)\n", s->name, s->watchers);
|
||||
return -1;
|
||||
}
|
||||
ast_alertpipe_close(s->sig_alert_pipe);
|
||||
ast_mutex_destroy(&s->lock);
|
||||
if (s->payload) {
|
||||
ast_free(s->payload);
|
||||
s->payload = NULL;
|
||||
}
|
||||
ast_free(s);
|
||||
s = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remove_signal(char *sname)
|
||||
{
|
||||
int res = -1;
|
||||
struct signalitem *s;
|
||||
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&signals, s, entry) {
|
||||
if (!strcmp(s->name, sname)) {
|
||||
AST_LIST_REMOVE_CURRENT(entry);
|
||||
res = dealloc_signal(s);
|
||||
ast_debug(1, "Removed signal '%s'\n", sname);
|
||||
}
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct signalitem *get_signal(char *sname, int addnew)
|
||||
{
|
||||
struct signalitem *s = NULL;
|
||||
AST_RWLIST_WRLOCK(&signals);
|
||||
AST_LIST_TRAVERSE(&signals, s, entry) {
|
||||
if (!strcasecmp(s->name, sname)) {
|
||||
ast_debug(1, "Using existing signal item '%s'\n", sname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!s) {
|
||||
if (addnew) { /* signal doesn't exist, so create it */
|
||||
s = alloc_signal(sname);
|
||||
/* Totally fail if we fail to find/create an entry */
|
||||
if (s) {
|
||||
ast_debug(1, "Created new signal item '%s'\n", sname);
|
||||
AST_RWLIST_INSERT_HEAD(&signals, s, entry);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Failed to create signal item for '%s'\n", sname);
|
||||
}
|
||||
} else {
|
||||
ast_debug(1, "Signal '%s' doesn't exist, and not creating it\n", sname);
|
||||
}
|
||||
}
|
||||
AST_RWLIST_UNLOCK(&signals);
|
||||
return s;
|
||||
}
|
||||
|
||||
static int wait_for_signal_or_hangup(struct ast_channel *chan, char *signame, int timeout)
|
||||
{
|
||||
struct signalitem *s = NULL;
|
||||
int ms, remaining_time, res = 1, goaway = 0;
|
||||
struct timeval start;
|
||||
struct ast_frame *frame = NULL;
|
||||
|
||||
remaining_time = timeout;
|
||||
start = ast_tvnow();
|
||||
|
||||
s = get_signal(signame, 1);
|
||||
|
||||
ast_mutex_lock(&s->lock);
|
||||
s->watchers = s->watchers + 1; /* we unlock, because a) other people need to use this and */
|
||||
ast_mutex_unlock(&s->lock); /* b) the signal will be available to us as long as watchers > 0 */
|
||||
|
||||
while (timeout == 0 || remaining_time > 0) {
|
||||
int ofd, exception;
|
||||
|
||||
ms = 1000;
|
||||
errno = 0;
|
||||
if (ast_waitfor_nandfds(&chan, 1, &s->sig_alert_pipe[0], 1, &exception, &ofd, &ms)) { /* channel won */
|
||||
if (!(frame = ast_read(chan))) { /* channel hung up */
|
||||
ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));
|
||||
res = -1;
|
||||
break;
|
||||
} else {
|
||||
ast_frfree(frame); /* handle frames */
|
||||
}
|
||||
} else if (ofd == s->sig_alert_pipe[0]) { /* fd won */
|
||||
if (ast_alertpipe_read(s->sig_alert_pipe) == AST_ALERT_READ_SUCCESS) {
|
||||
ast_debug(1, "Alert pipe has data for us\n");
|
||||
res = 0;
|
||||
break;
|
||||
} else {
|
||||
ast_debug(1, "Alert pipe does not have data for us\n");
|
||||
}
|
||||
} else { /* nobody won */
|
||||
if (ms && (ofd < 0)) {
|
||||
if (!((errno == 0) || (errno == EINTR))) {
|
||||
ast_log(LOG_WARNING, "Something bad happened while channel '%s' was polling.\n", ast_channel_name(chan));
|
||||
break;
|
||||
}
|
||||
} /* else, nothing happened */
|
||||
}
|
||||
if (timeout) {
|
||||
remaining_time = ast_remaining_ms(start, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/* WRLOCK the list so that if we're going to destroy the signal now, nobody else can grab it before that happens. */
|
||||
AST_RWLIST_WRLOCK(&signals);
|
||||
ast_mutex_lock(&s->lock);
|
||||
if (s->payload) {
|
||||
pbx_builtin_setvar_helper(chan, "WAITFORSIGNALPAYLOAD", s->payload);
|
||||
}
|
||||
s->watchers = s->watchers - 1;
|
||||
if (s->watchers) { /* folks are still waiting for this, pass it on... */
|
||||
int save_errno = errno;
|
||||
if (ast_alertpipe_write(s->sig_alert_pipe)) {
|
||||
ast_log(LOG_WARNING, "%s: write() failed: %s\n", __FUNCTION__, strerror(errno));
|
||||
}
|
||||
errno = save_errno;
|
||||
} else { /* nobody else is waiting for this */
|
||||
goaway = 1; /* we were the last guy using this, so mark signal item for destruction */
|
||||
}
|
||||
ast_mutex_unlock(&s->lock);
|
||||
|
||||
if (goaway) {
|
||||
/* remove_signal calls ast_mutex_destroy, so don't call it with the mutex itself locked. */
|
||||
remove_signal(signame);
|
||||
}
|
||||
AST_RWLIST_UNLOCK(&signals);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int send_signal(char *signame, char *payload)
|
||||
{
|
||||
struct signalitem *s;
|
||||
int save_errno = errno;
|
||||
int res = 0;
|
||||
|
||||
s = get_signal(signame, 0); /* if signal doesn't exist already, no point in creating it, because nobody could be waiting for it! */
|
||||
|
||||
if (!s) {
|
||||
return -1; /* this signal didn't exist, so we can't send a signal for it */
|
||||
}
|
||||
|
||||
/* at this point, we know someone is listening, since signals are destroyed when watchers gets down to 0 */
|
||||
ast_mutex_lock(&s->lock);
|
||||
s->signaled = 1;
|
||||
if (payload && *payload) {
|
||||
int len = strlen(payload);
|
||||
if (s->payload) {
|
||||
ast_free(s->payload); /* if there was already a payload, replace it */
|
||||
s->payload = NULL;
|
||||
}
|
||||
s->payload = ast_malloc(len + 1);
|
||||
if (!s->payload) {
|
||||
ast_log(LOG_WARNING, "Failed to allocate signal payload '%s'\n", payload);
|
||||
} else {
|
||||
ast_copy_string(s->payload, payload, len + 1);
|
||||
}
|
||||
}
|
||||
if (ast_alertpipe_write(s->sig_alert_pipe)) {
|
||||
ast_log(LOG_WARNING, "%s: write() failed: %s\n", __FUNCTION__, strerror(errno));
|
||||
s->signaled = 0; /* okay, so we didn't send a signal after all... */
|
||||
res = -1;
|
||||
}
|
||||
errno = save_errno;
|
||||
ast_debug(1, "Sent '%s' signal to %d listeners\n", signame, s->watchers);
|
||||
ast_mutex_unlock(&s->lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int waitsignal_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
char *argcopy;
|
||||
int r = 0, timeoutms = 0;
|
||||
double timeout = 0;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(signame);
|
||||
AST_APP_ARG(sigtimeout);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Signal() requires arguments\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
argcopy = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, argcopy);
|
||||
|
||||
if (ast_strlen_zero(args.signame)) {
|
||||
ast_log(LOG_WARNING, "Missing signal name\n");
|
||||
return -1;
|
||||
}
|
||||
if (strlen(args.signame) >= AST_MAX_CONTEXT) {
|
||||
ast_log(LOG_WARNING, "Signal name '%s' is too long\n", args.signame);
|
||||
return -1;
|
||||
}
|
||||
if (!ast_strlen_zero(args.sigtimeout)) {
|
||||
if (sscanf(args.sigtimeout, "%30lg", &timeout) != 1 || timeout < 0) {
|
||||
ast_log(LOG_WARNING, "Invalid timeout provided: %s. Defaulting to no timeout.\n", args.sigtimeout);
|
||||
} else {
|
||||
timeoutms = timeout * 1000; /* sec to msec */
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout > 0) {
|
||||
ast_debug(1, "Waiting for signal '%s' for %d ms\n", args.signame, timeoutms);
|
||||
} else {
|
||||
ast_debug(1, "Waiting for signal '%s', indefinitely\n", args.signame);
|
||||
}
|
||||
|
||||
r = wait_for_signal_or_hangup(chan, args.signame, timeoutms);
|
||||
|
||||
if (r == 1) {
|
||||
ast_verb(3, "Channel '%s' timed out, waiting for signal '%s'\n", ast_channel_name(chan), args.signame);
|
||||
pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "TIMEOUT");
|
||||
} else if (!r) {
|
||||
ast_verb(3, "Received signal '%s' on channel '%s'\n", args.signame, ast_channel_name(chan));
|
||||
pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "SIGNALED");
|
||||
} else {
|
||||
pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "HANGUP");
|
||||
ast_verb(3, "Channel '%s' hung up\n", ast_channel_name(chan));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int signal_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
char *argcopy;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(signame);
|
||||
AST_APP_ARG(payload);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Signal() requires arguments\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
argcopy = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, argcopy);
|
||||
|
||||
if (ast_strlen_zero(args.signame)) {
|
||||
ast_log(LOG_WARNING, "Missing signal name\n");
|
||||
return -1;
|
||||
}
|
||||
if (strlen(args.signame) >= AST_MAX_CONTEXT) {
|
||||
ast_log(LOG_WARNING, "Signal name '%s' is too long\n", args.signame);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (send_signal(args.signame, args.payload)) {
|
||||
pbx_builtin_setvar_helper(chan, "SIGNALSTATUS", "FAILURE");
|
||||
} else {
|
||||
pbx_builtin_setvar_helper(chan, "SIGNALSTATUS", "SUCCESS");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
struct signalitem *s;
|
||||
int res = 0;
|
||||
|
||||
/* To avoid a locking nightmare, and for logistical reasons, this module
|
||||
* will refuse to unload if watchers > 0. That way we know a signal's
|
||||
* pipe won't disappear while it's being used. */
|
||||
|
||||
AST_RWLIST_WRLOCK(&signals);
|
||||
/* Don't just use AST_RWLIST_REMOVE_HEAD, because if dealloc_signal fails, it should stay in the list. */
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&signals, s, entry) {
|
||||
int mres = dealloc_signal(s);
|
||||
res |= mres;
|
||||
if (!mres) {
|
||||
AST_LIST_REMOVE_CURRENT(entry);
|
||||
}
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(&signals);
|
||||
|
||||
/* One or more signals still has watchers. */
|
||||
if (res) {
|
||||
ast_log(LOG_WARNING, "One or more signals is currently in use. Unload failed.\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
res |= ast_unregister_application(app);
|
||||
res |= ast_unregister_application(app2);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_register_application_xml(app, signal_exec);
|
||||
res |= ast_register_application_xml(app2, waitsignal_exec);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Channel Signaling Applications");
|
|
@ -377,6 +377,26 @@ static int pop_exec(struct ast_channel *chan, const char *data)
|
|||
return res;
|
||||
}
|
||||
|
||||
static int frames_left(struct ast_channel *chan)
|
||||
{
|
||||
struct ast_datastore *stack_store;
|
||||
struct gosub_stack_list *oldlist;
|
||||
int exists;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
|
||||
if (!stack_store) {
|
||||
ast_channel_unlock(chan);
|
||||
return -1;
|
||||
}
|
||||
oldlist = stack_store->data;
|
||||
AST_LIST_LOCK(oldlist);
|
||||
exists = oldlist->first ? 1 : 0;
|
||||
AST_LIST_UNLOCK(oldlist);
|
||||
ast_channel_unlock(chan);
|
||||
return exists;
|
||||
}
|
||||
|
||||
static int return_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
struct ast_datastore *stack_store;
|
||||
|
@ -384,6 +404,7 @@ static int return_exec(struct ast_channel *chan, const char *data)
|
|||
struct gosub_stack_list *oldlist;
|
||||
const char *retval = data;
|
||||
int res = 0;
|
||||
int lastframe;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
|
||||
|
@ -395,6 +416,7 @@ static int return_exec(struct ast_channel *chan, const char *data)
|
|||
oldlist = stack_store->data;
|
||||
AST_LIST_LOCK(oldlist);
|
||||
oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
|
||||
lastframe = oldlist->first ? 0 : 1;
|
||||
AST_LIST_UNLOCK(oldlist);
|
||||
|
||||
if (!oldframe) {
|
||||
|
@ -412,12 +434,19 @@ static int return_exec(struct ast_channel *chan, const char *data)
|
|||
* what was there before. Channels that do not have a PBX may
|
||||
* not have the context or exten set.
|
||||
*/
|
||||
ast_channel_context_set(chan, oldframe->context);
|
||||
ast_channel_exten_set(chan, oldframe->extension);
|
||||
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) {
|
||||
--oldframe->priority;
|
||||
if (ast_channel_pbx(chan) || !lastframe) {
|
||||
/* If there's no PBX, the "old location" is simply
|
||||
* the configured context for the device, such as
|
||||
* for pre-dial handlers, and restoring this location
|
||||
* is nonsensical. So if no PBX and there are no further
|
||||
* frames, leave the location as it is. */
|
||||
ast_channel_context_set(chan, oldframe->context);
|
||||
ast_channel_exten_set(chan, oldframe->extension);
|
||||
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) {
|
||||
--oldframe->priority;
|
||||
}
|
||||
ast_channel_priority_set(chan, oldframe->priority);
|
||||
}
|
||||
ast_channel_priority_set(chan, oldframe->priority);
|
||||
ast_set2_flag(ast_channel_flags(chan), oldframe->in_subroutine, AST_FLAG_SUBROUTINE_EXEC);
|
||||
|
||||
gosub_release_frame(chan, oldframe);
|
||||
|
@ -1068,15 +1097,18 @@ static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_
|
|||
ast_channel_priority(chan), ast_channel_name(chan));
|
||||
}
|
||||
|
||||
/* Did the routine return? */
|
||||
if (ast_channel_priority(chan) == saved_priority
|
||||
/* Did the routine return?
|
||||
* For things like predial where there's no PBX on the channel yet,
|
||||
* the last return leaves the location alone so we can print it out correctly here.
|
||||
* So to ensure we finished properly, make sure there are no frames left in that case. */
|
||||
if ((!ast_channel_pbx(chan) && !frames_left(chan)) || (ast_channel_priority(chan) == saved_priority
|
||||
&& !strcmp(ast_channel_context(chan), saved_context)
|
||||
&& !strcmp(ast_channel_exten(chan), saved_exten)) {
|
||||
&& !strcmp(ast_channel_exten(chan), saved_exten))) {
|
||||
ast_verb(3, "%s Internal %s(%s) complete GOSUB_RETVAL=%s\n",
|
||||
ast_channel_name(chan), app_gosub, sub_args,
|
||||
S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "%s Abnormal '%s(%s)' exit. Popping routine return locations.\n",
|
||||
ast_log(LOG_WARNING, "%s Abnormal '%s(%s)' exit. Popping routine return locations.\n",
|
||||
ast_channel_name(chan), app_gosub, sub_args);
|
||||
balance_stack(chan);
|
||||
pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
|
||||
|
|
|
@ -570,6 +570,7 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate);
|
|||
#define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
|
||||
#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 VM_EMAIL_EXT_RECS (1 << 19) /*!< Send voicemail emails when an external recording is added to a mailbox */
|
||||
#define ERROR_LOCK_PATH -100
|
||||
#define ERROR_MAX_MSGS -101
|
||||
#define OPERATOR_EXIT 300
|
||||
|
@ -1258,6 +1259,8 @@ static void apply_option(struct ast_vm_user *vmu, const char *var, const char *v
|
|||
ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
|
||||
} else if (!strcasecmp(var, "attachfmt")) {
|
||||
ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
|
||||
} else if (!strcasecmp(var, "attachextrecs")) {
|
||||
ast_set2_flag(vmu, ast_true(value), VM_EMAIL_EXT_RECS);
|
||||
} else if (!strcasecmp(var, "serveremail")) {
|
||||
ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
|
||||
} else if (!strcasecmp(var, "fromstring")) {
|
||||
|
@ -4481,15 +4484,16 @@ static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxco
|
|||
*/
|
||||
static int remove_file(char *dir, int msgnum)
|
||||
{
|
||||
char fn[PATH_MAX];
|
||||
char full_fn[PATH_MAX];
|
||||
char fn[PATH_MAX] = "";
|
||||
char full_fn[PATH_MAX + 4]; /* Plus .txt */
|
||||
char msgnums[80];
|
||||
|
||||
if (msgnum > -1) {
|
||||
snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
|
||||
make_file(fn, sizeof(fn), dir, msgnum);
|
||||
} else
|
||||
} else {
|
||||
ast_copy_string(fn, dir, sizeof(fn));
|
||||
}
|
||||
ast_filedelete(fn, NULL);
|
||||
snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
|
||||
unlink(full_fn);
|
||||
|
@ -6418,6 +6422,12 @@ static int msg_create_from_file(struct ast_vm_recording_data *recdata)
|
|||
* to do both with one line and is also safe to use with file storage mode. Also, if we are using ODBC, now is a good
|
||||
* time to create the voicemail database entry. */
|
||||
if (ast_fileexists(destination, NULL, NULL) > 0) {
|
||||
struct ast_channel *chan = NULL;
|
||||
char fmt[80];
|
||||
char clid[80];
|
||||
char cidnum[80], cidname[80];
|
||||
int send_email;
|
||||
|
||||
if (ast_check_realtime("voicemail_data")) {
|
||||
get_date(date, sizeof(date));
|
||||
ast_store_realtime("voicemail_data",
|
||||
|
@ -6437,7 +6447,27 @@ static int msg_create_from_file(struct ast_vm_recording_data *recdata)
|
|||
}
|
||||
|
||||
STORE(dir, recipient->mailbox, recipient->context, msgnum, NULL, recipient, fmt, 0, vms, "", msg_id);
|
||||
notify_new_state(recipient);
|
||||
|
||||
send_email = ast_test_flag(recipient, VM_EMAIL_EXT_RECS);
|
||||
|
||||
if (send_email) {
|
||||
/* Send an email if possible, fall back to just notifications if not. */
|
||||
ast_copy_string(fmt, recdata->recording_ext, sizeof(fmt));
|
||||
ast_copy_string(clid, recdata->call_callerid, sizeof(clid));
|
||||
ast_callerid_split(clid, cidname, sizeof(cidname), cidnum, sizeof(cidnum));
|
||||
|
||||
/* recdata->call_callerchan itself no longer exists, so we can't use the real channel. Use a dummy one. */
|
||||
chan = ast_dummy_channel_alloc();
|
||||
}
|
||||
if (chan) {
|
||||
notify_new_message(chan, recipient, NULL, msgnum, duration, fmt, cidnum, cidname, "");
|
||||
ast_channel_unref(chan);
|
||||
} else {
|
||||
if (send_email) { /* We tried and failed. */
|
||||
ast_log(LOG_WARNING, "Failed to allocate dummy channel, email will not be sent\n");
|
||||
}
|
||||
notify_new_state(recipient);
|
||||
}
|
||||
}
|
||||
|
||||
free_user(recipient);
|
||||
|
@ -11197,12 +11227,14 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
|
|||
password[0] = '\0';
|
||||
} else {
|
||||
if (ast_streamfile(chan, vm_password, ast_channel_language(chan))) {
|
||||
ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
|
||||
if (!ast_check_hangup(chan)) {
|
||||
ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
|
||||
}
|
||||
free_user(vmu);
|
||||
return -1;
|
||||
}
|
||||
if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
|
||||
ast_log(AST_LOG_WARNING, "Unable to read password\n");
|
||||
ast_log(AST_LOG_NOTICE, "Unable to read password\n");
|
||||
free_user(vmu);
|
||||
return -1;
|
||||
} else if (password[0] == '*') {
|
||||
|
|
|
@ -120,6 +120,9 @@
|
|||
<configOption name="end_marked">
|
||||
<synopsis>Kick the user from the conference when the last marked user leaves</synopsis>
|
||||
</configOption>
|
||||
<configOption name="end_marked_any">
|
||||
<synopsis>Kick the user from the conference when any marked user leaves</synopsis>
|
||||
</configOption>
|
||||
<configOption name="talk_detection_events">
|
||||
<synopsis>Set whether or not notifications of when a user begins and ends talking should be sent out as events over AMI</synopsis>
|
||||
</configOption>
|
||||
|
@ -1440,10 +1443,7 @@ static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *
|
|||
|
||||
/* if adding any of the actions failed, bail */
|
||||
if (res) {
|
||||
struct conf_menu_action *menu_action;
|
||||
while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
|
||||
ast_free(menu_action);
|
||||
}
|
||||
conf_menu_entry_destroy(menu_entry);
|
||||
ast_free(menu_entry);
|
||||
return -1;
|
||||
}
|
||||
|
@ -1452,6 +1452,7 @@ static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *
|
|||
AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) {
|
||||
if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
|
||||
AST_LIST_REMOVE_CURRENT(entry);
|
||||
conf_menu_entry_destroy(cur);
|
||||
ast_free(cur);
|
||||
break;
|
||||
}
|
||||
|
@ -1583,9 +1584,12 @@ static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, in
|
|||
ast_cli(a->fd,"Wait Marked: %s\n",
|
||||
u_profile.flags & USER_OPT_WAITMARKED ?
|
||||
"enabled" : "disabled");
|
||||
ast_cli(a->fd,"END Marked: %s\n",
|
||||
ast_cli(a->fd,"END Marked (All): %s\n",
|
||||
u_profile.flags & USER_OPT_ENDMARKED ?
|
||||
"enabled" : "disabled");
|
||||
ast_cli(a->fd,"END Marked (Any): %s\n",
|
||||
u_profile.flags & USER_OPT_ENDMARKEDANY ?
|
||||
"enabled" : "disabled");
|
||||
ast_cli(a->fd,"Drop_silence: %s\n",
|
||||
u_profile.flags & USER_OPT_DROP_SILENCE ?
|
||||
"enabled" : "disabled");
|
||||
|
@ -2409,6 +2413,7 @@ int conf_load_config(void)
|
|||
aco_option_register(&cfg_info, "announce_only_user", ACO_EXACT, user_types, "yes", OPT_BOOLFLAG_T, 0, FLDSET(struct user_profile, flags), USER_OPT_NOONLYPERSON);
|
||||
aco_option_register(&cfg_info, "wait_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_WAITMARKED);
|
||||
aco_option_register(&cfg_info, "end_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKED);
|
||||
aco_option_register(&cfg_info, "end_marked_any", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKEDANY);
|
||||
aco_option_register(&cfg_info, "talk_detection_events", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_TALKER_DETECT);
|
||||
aco_option_register(&cfg_info, "dtmf_passthrough", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DTMF_PASS);
|
||||
aco_option_register(&cfg_info, "announce_join_leave", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ANNOUNCE_JOIN_LEAVE);
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* 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.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* 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.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* 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.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* 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.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* 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.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
@ -85,37 +82,39 @@ static void leave_marked(struct confbridge_user *user)
|
|||
|
||||
conf_remove_user_marked(user->conference, user);
|
||||
|
||||
if (user->conference->markedusers == 0) {
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&user->conference->active_list, user_iter, list) {
|
||||
/* Kick ENDMARKED cbu_iters */
|
||||
if (ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKED) && !user_iter->kicked) {
|
||||
if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
||||
&& !ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER)) {
|
||||
AST_LIST_REMOVE_CURRENT(list);
|
||||
user_iter->conference->activeusers--;
|
||||
AST_LIST_INSERT_TAIL(&user_iter->conference->waiting_list, user_iter, list);
|
||||
user_iter->conference->waitingusers++;
|
||||
}
|
||||
user_iter->kicked = 1;
|
||||
pbx_builtin_setvar_helper(user_iter->chan, "CONFBRIDGE_RESULT", "ENDMARKED");
|
||||
ast_bridge_remove(user_iter->conference->bridge, user_iter->chan);
|
||||
} else if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
||||
&& !ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER)) {
|
||||
need_prompt = 1;
|
||||
|
||||
/* If all marked users have left, or we're set to kick if any marked user leaves, then boot everyone */
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&user->conference->active_list, user_iter, list) {
|
||||
if (user->conference->markedusers > 0 && !ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKEDANY)) {
|
||||
continue;
|
||||
}
|
||||
/* Kick ENDMARKED cbu_iters */
|
||||
if ((ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKED) || ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKEDANY)) && !user_iter->kicked) {
|
||||
if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
||||
&& (!ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER) || ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKEDANY))) {
|
||||
AST_LIST_REMOVE_CURRENT(list);
|
||||
user_iter->conference->activeusers--;
|
||||
AST_LIST_INSERT_TAIL(&user_iter->conference->waiting_list, user_iter, list);
|
||||
user_iter->conference->waitingusers++;
|
||||
} else {
|
||||
/* User is neither wait_marked nor end_marked; however, they
|
||||
* should still hear the prompt.
|
||||
*/
|
||||
need_prompt = 1;
|
||||
}
|
||||
user_iter->kicked = 1;
|
||||
pbx_builtin_setvar_helper(user_iter->chan, "CONFBRIDGE_RESULT", "ENDMARKED");
|
||||
ast_bridge_remove(user_iter->conference->bridge, user_iter->chan);
|
||||
} else if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
||||
&& !ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER)) {
|
||||
need_prompt = 1;
|
||||
|
||||
AST_LIST_REMOVE_CURRENT(list);
|
||||
user_iter->conference->activeusers--;
|
||||
AST_LIST_INSERT_TAIL(&user_iter->conference->waiting_list, user_iter, list);
|
||||
user_iter->conference->waitingusers++;
|
||||
} else {
|
||||
/* User is neither wait_marked nor end_marked nor end_marked_any; however, they
|
||||
* should still hear the prompt.
|
||||
*/
|
||||
need_prompt = 1;
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
|
||||
switch (user->conference->activeusers) {
|
||||
case 0:
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* 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.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* 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.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* 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.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -71,6 +71,7 @@ enum user_profile_flags {
|
|||
USER_OPT_TEXT_MESSAGING = (1 << 19), /*!< Send text messages to the user */
|
||||
USER_OPT_ANSWER_CHANNEL = (1 << 20), /*!< Sets if the channel should be answered if currently unanswered */
|
||||
USER_OPT_HEAR_OWN_JOIN_SOUND = (1 << 21), /*!< Set if the caller should hear the join sound */
|
||||
USER_OPT_ENDMARKEDANY = (1 << 22), /*!< Set if the user should be kicked after any marked user exits */
|
||||
};
|
||||
|
||||
enum bridge_profile_flags {
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><title>Release Summary - asterisk-20.2.1</title><h1 align="center"><a name="top">Release Summary</a></h1><h3 align="center">asterisk-20.2.1</h3><h3 align="center">Date: 2023-04-03</h3><h3 align="center"><asteriskteam@digium.com></h3><hr><h2 align="center">Table of Contents</h2><ol>
|
||||
<li><a href="#summary">Summary</a></li>
|
||||
<li><a href="#contributors">Contributors</a></li>
|
||||
<li><a href="#closed_issues">Closed Issues</a></li>
|
||||
<li><a href="#diffstat">Diffstat</a></li>
|
||||
</ol><hr><a name="summary"><h2 align="center">Summary</h2></a><center><a href="#top">[Back to Top]</a></center><p>This release is a point release of an existing major version. The changes included were made to address problems that have been identified in this release series, or are minor, backwards compatible new features or improvements. Users should be able to safely upgrade to this version if this release series is already in use. Users considering upgrading from a previous version are strongly encouraged to review the UPGRADE.txt document as well as the CHANGES document for information about upgrading to this release series.</p><p>The data in this summary reflects changes that have been made since the previous release, asterisk-20.2.0.</p><hr><a name="contributors"><h2 align="center">Contributors</h2></a><center><a href="#top">[Back to Top]</a></center><p>This table lists the people who have submitted code, those that have tested patches, as well as those that reported issues on the issue tracker that were resolved in this release. For coders, the number is how many of their patches (of any size) were committed into this release. For testers, the number is the number of times their name was listed as assisting with testing a patch. Finally, for reporters, the number is the number of issues that they reported that were affected by commits that went into this release.</p><table width="100%" border="0">
|
||||
<tr><th width="33%">Coders</th><th width="33%">Testers</th><th width="33%">Reporters</th></tr>
|
||||
<tr valign="top"><td width="33%">1 Sean Bright <sean@seanbright.com><br/>1 Mike Bradeen <mbradeen@sangoma.com><br/></td><td width="33%"><td width="33%">1 N A <asterisk@phreaknet.org><br/>1 isrl <isrlgb@gmail.com><br/></td></tr>
|
||||
</table><hr><a name="closed_issues"><h2 align="center">Closed Issues</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a list of all issues from the issue tracker that were closed by changes that went into this release.</p><h3>Bug</h3><h4>Category: PBX/pbx_ael</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-30472">ASTERISK-30472</a>: pbx_ael: Literal usage for variables broken<br/>Reported by: isrl<ul>
|
||||
<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6e50550d28f90676a8510045e45f7750b88964b5">[6e50550d28]</a> Sean Bright -- Revert "pbx_ael: Global variables are not expanded."</li>
|
||||
</ul><br><h4>Category: Resources/res_pjsip_pubsub</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-30469">ASTERISK-30469</a>: res_pjsip_pubsub: Regression for subscription shutdowns<br/>Reported by: N A<ul>
|
||||
<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=f8dfbaf22594c110e84d35e2a7dff5a998e4547f">[f8dfbaf225]</a> Mike Bradeen -- res_pjsip_pubsub: subscription cleanup changes</li>
|
||||
</ul><br><hr><a name="diffstat"><h2 align="center">Diffstat Results</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a summary of the changes to the source code that went into this release that was generated using the diffstat utility.</p><pre>ael/pval.c | 14 +--------
|
||||
res_pjsip_pubsub.c | 81 +++++++++++++++++++++++++++++++----------------------
|
||||
2 files changed, 51 insertions(+), 44 deletions(-)</pre><br></html>
|
|
@ -0,0 +1,91 @@
|
|||
Release Summary
|
||||
|
||||
asterisk-20.2.1
|
||||
|
||||
Date: 2023-04-03
|
||||
|
||||
<asteriskteam@digium.com>
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Summary
|
||||
2. Contributors
|
||||
3. Closed Issues
|
||||
4. Diffstat
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Summary
|
||||
|
||||
[Back to Top]
|
||||
|
||||
This release is a point release of an existing major version. The changes
|
||||
included were made to address problems that have been identified in this
|
||||
release series, or are minor, backwards compatible new features or
|
||||
improvements. Users should be able to safely upgrade to this version if
|
||||
this release series is already in use. Users considering upgrading from a
|
||||
previous version are strongly encouraged to review the UPGRADE.txt
|
||||
document as well as the CHANGES document for information about upgrading
|
||||
to this release series.
|
||||
|
||||
The data in this summary reflects changes that have been made since the
|
||||
previous release, asterisk-20.2.0.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Contributors
|
||||
|
||||
[Back to Top]
|
||||
|
||||
This table lists the people who have submitted code, those that have
|
||||
tested patches, as well as those that reported issues on the issue tracker
|
||||
that were resolved in this release. For coders, the number is how many of
|
||||
their patches (of any size) were committed into this release. For testers,
|
||||
the number is the number of times their name was listed as assisting with
|
||||
testing a patch. Finally, for reporters, the number is the number of
|
||||
issues that they reported that were affected by commits that went into
|
||||
this release.
|
||||
|
||||
Coders Testers Reporters
|
||||
1 Sean Bright 1 N A
|
||||
1 Mike Bradeen 1 isrl
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Closed Issues
|
||||
|
||||
[Back to Top]
|
||||
|
||||
This is a list of all issues from the issue tracker that were closed by
|
||||
changes that went into this release.
|
||||
|
||||
Bug
|
||||
|
||||
Category: PBX/pbx_ael
|
||||
|
||||
ASTERISK-30472: pbx_ael: Literal usage for variables broken
|
||||
Reported by: isrl
|
||||
* [6e50550d28] Sean Bright -- Revert "pbx_ael: Global variables are not
|
||||
expanded."
|
||||
|
||||
Category: Resources/res_pjsip_pubsub
|
||||
|
||||
ASTERISK-30469: res_pjsip_pubsub: Regression for subscription shutdowns
|
||||
Reported by: N A
|
||||
* [f8dfbaf225] Mike Bradeen -- res_pjsip_pubsub: subscription cleanup
|
||||
changes
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Diffstat Results
|
||||
|
||||
[Back to Top]
|
||||
|
||||
This is a summary of the changes to the source code that went into this
|
||||
release that was generated using the diffstat utility.
|
||||
|
||||
ael/pval.c | 14 +--------
|
||||
res_pjsip_pubsub.c | 81 +++++++++++++++++++++++++++++++----------------------
|
||||
2 files changed, 51 insertions(+), 44 deletions(-)
|
|
@ -3447,7 +3447,7 @@ struct sig_ss7_callback sig_ss7_callbacks =
|
|||
*/
|
||||
static void notify_message(char *mailbox, int thereornot)
|
||||
{
|
||||
char s[sizeof(mwimonitornotify) + 80];
|
||||
char s[sizeof(mwimonitornotify) + 164];
|
||||
|
||||
if (ast_strlen_zero(mailbox)) {
|
||||
return;
|
||||
|
@ -4115,7 +4115,7 @@ static void dahdi_r2_on_context_log(openr2_context_t *r2context, openr2_log_leve
|
|||
{
|
||||
#define CONTEXT_TAG "Context - "
|
||||
char logmsg[256];
|
||||
char completemsg[sizeof(logmsg) + sizeof(CONTEXT_TAG) - 1];
|
||||
char completemsg[sizeof(logmsg) * 2];
|
||||
vsnprintf(logmsg, sizeof(logmsg), fmt, ap);
|
||||
snprintf(completemsg, sizeof(completemsg), CONTEXT_TAG "%s", logmsg);
|
||||
dahdi_r2_write_log(level, completemsg);
|
||||
|
@ -4128,10 +4128,11 @@ static void dahdi_r2_on_chan_log(openr2_chan_t *r2chan, openr2_log_level_t level
|
|||
{
|
||||
#define CHAN_TAG "Chan "
|
||||
char logmsg[256];
|
||||
char completemsg[sizeof(logmsg) + sizeof(CHAN_TAG) - 1];
|
||||
char completemsg[sizeof(logmsg) * 2];
|
||||
vsnprintf(logmsg, sizeof(logmsg), fmt, ap);
|
||||
snprintf(completemsg, sizeof(completemsg), CHAN_TAG "%d - %s", openr2_chan_get_number(r2chan), logmsg);
|
||||
dahdi_r2_write_log(level, completemsg);
|
||||
#undef CHAN_TAG
|
||||
}
|
||||
|
||||
static int dahdi_r2_on_dnis_digit_received(openr2_chan_t *r2chan, char digit)
|
||||
|
@ -13651,6 +13652,7 @@ static struct ast_channel *dahdi_request(const char *type, struct ast_format_cap
|
|||
struct ast_channel *tmp = NULL;
|
||||
struct dahdi_pvt *exitpvt;
|
||||
int channelmatched = 0;
|
||||
int foundowner = 0;
|
||||
int groupmatched = 0;
|
||||
#if defined(HAVE_PRI) || defined(HAVE_SS7)
|
||||
int transcapdigital = 0;
|
||||
|
@ -13674,6 +13676,10 @@ static struct ast_channel *dahdi_request(const char *type, struct ast_format_cap
|
|||
if (start.roundrobin)
|
||||
round_robin[start.rr_starting_point] = p;
|
||||
|
||||
if (p->owner) {
|
||||
foundowner++;
|
||||
}
|
||||
|
||||
if (is_group_or_channel_match(p, start.span, start.groupmatch, &groupmatched, start.channelmatch, &channelmatched)
|
||||
&& available(&p, channelmatched)) {
|
||||
ast_debug(1, "Using channel %d\n", p->channel);
|
||||
|
@ -13792,7 +13798,7 @@ next:
|
|||
ast_mutex_unlock(&iflock);
|
||||
restart_monitor();
|
||||
if (cause && !tmp) {
|
||||
if (callwait || channelmatched) {
|
||||
if (callwait || (channelmatched && foundowner)) {
|
||||
*cause = AST_CAUSE_BUSY;
|
||||
} else if (groupmatched) {
|
||||
*cause = AST_CAUSE_CONGESTION;
|
||||
|
@ -19411,7 +19417,7 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
|
|||
report_alarms = REPORT_SPAN_ALARMS;
|
||||
}
|
||||
} else if (!(options & PROC_DAHDI_OPT_NOWARN) )
|
||||
ast_log(LOG_WARNING, "Ignoring any changes to '%s' (on reload) at line %d.\n", v->name, v->lineno);
|
||||
ast_log(LOG_NOTICE, "Ignoring any changes to '%s' (on reload) at line %d.\n", v->name, v->lineno);
|
||||
}
|
||||
|
||||
if (dahdichan) {
|
||||
|
|
|
@ -226,6 +226,16 @@
|
|||
</enum>
|
||||
</enumlist>
|
||||
</info>
|
||||
<info name="Dial_Resource" language="en_US" tech="IAX2">
|
||||
<para>The general syntax is:</para>
|
||||
<para><literal>Dial(IAX2/[username[:password]@]peer[:port][/exten[@context]][/options]</literal></para>
|
||||
<para>IAX2 optionally allows modifiers to be specified after the extension.</para>
|
||||
<enumlist>
|
||||
<enum name="a">
|
||||
<para>Request auto answer (supporting equipment/configuration required)</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</info>
|
||||
<manager name="IAXpeers" language="en_US">
|
||||
<synopsis>
|
||||
List IAX peers.
|
||||
|
@ -3407,7 +3417,7 @@ static int send_packet(struct iax_frame *f)
|
|||
|
||||
/* Called with iaxsl held */
|
||||
if (iaxdebug) {
|
||||
ast_debug(3, "Sending %u on %d/%d to %s\n", f->ts, callno, iaxs[callno]->peercallno, ast_sockaddr_stringify(&iaxs[callno]->addr));
|
||||
ast_debug(8, "Sending %u on %d/%d to %s\n", f->ts, callno, iaxs[callno]->peercallno, ast_sockaddr_stringify(&iaxs[callno]->addr));
|
||||
}
|
||||
if (f->transfer) {
|
||||
iax_outputframe(f, NULL, 0, &iaxs[callno]->transfer, f->datalen - sizeof(struct ast_iax2_full_hdr));
|
||||
|
@ -4148,9 +4158,19 @@ static void __get_from_jb(const void *p)
|
|||
now.tv_usec += 1000;
|
||||
|
||||
ms = ast_tvdiff_ms(now, pvt->rxcore);
|
||||
|
||||
voicefmt = ast_format_compatibility_bitfield2format(pvt->voiceformat);
|
||||
if (voicefmt && ms >= (next = jb_next(pvt->jb))) {
|
||||
if (ms >= (next = jb_next(pvt->jb))) {
|
||||
voicefmt = ast_format_compatibility_bitfield2format(pvt->voiceformat);
|
||||
if (!voicefmt) {
|
||||
/* pvt->voiceformat won't be set if we haven't received any voice frames yet.
|
||||
* In this case, fall back to using the format negotiated during call setup,
|
||||
* so we don't stall the jitterbuffer completely. */
|
||||
voicefmt = ast_format_compatibility_bitfield2format(pvt->peerformat);
|
||||
}
|
||||
if (!voicefmt) {
|
||||
/* Really shouldn't happen, but if it does, should be looked into */
|
||||
ast_log(LOG_WARNING, "No voice format and no peer format available on %s, backlogging frame\n", ast_channel_name(pvt->owner));
|
||||
goto cleanup; /* Don't crash if there's no voice format */
|
||||
}
|
||||
ret = jb_get(pvt->jb, &frame, ms, ast_format_get_default_ms(voicefmt));
|
||||
switch(ret) {
|
||||
case JB_OK:
|
||||
|
@ -4192,6 +4212,7 @@ static void __get_from_jb(const void *p)
|
|||
break;
|
||||
}
|
||||
}
|
||||
cleanup:
|
||||
if (pvt)
|
||||
update_jbsched(pvt);
|
||||
ast_mutex_unlock(&iaxsl[callno]);
|
||||
|
@ -6384,7 +6405,7 @@ static int invalid_key(ast_aes_decrypt_key *ecx)
|
|||
#ifdef HAVE_OPENSSL
|
||||
int i;
|
||||
for (i = 0; i < 60; i++) {
|
||||
if (ecx->rd_key[i]) {
|
||||
if (ecx->raw[i]) {
|
||||
return 0; /* stop if we encounter anything non-zero */
|
||||
}
|
||||
}
|
||||
|
@ -10363,7 +10384,7 @@ static int socket_process_helper(struct iax2_thread *thread)
|
|||
}
|
||||
if (ast_test_flag64(iaxs[fr->callno], IAX_ENCRYPTED) && !decrypted) {
|
||||
if (decrypt_frame(fr->callno, fh, &f, &res)) {
|
||||
ast_log(LOG_NOTICE, "Packet Decrypt Failed!\n");
|
||||
ast_log(LOG_WARNING, "Packet Decrypt Failed!\n");
|
||||
ast_variables_destroy(ies.vars);
|
||||
ast_mutex_unlock(&iaxsl[fr->callno]);
|
||||
return 1;
|
||||
|
@ -12033,7 +12054,7 @@ immediatedial:
|
|||
iaxs[fr->callno]->last = fr->ts;
|
||||
#if 1
|
||||
if (iaxdebug)
|
||||
ast_debug(3, "For call=%d, set last=%u\n", fr->callno, fr->ts);
|
||||
ast_debug(8, "For call=%d, set last=%u\n", fr->callno, fr->ts);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -2513,6 +2513,15 @@ static int hangup(void *data)
|
|||
if (session) {
|
||||
int cause = h_data->cause;
|
||||
|
||||
if (channel->session->active_media_state &&
|
||||
channel->session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO]) {
|
||||
struct ast_sip_session_media *media =
|
||||
channel->session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO];
|
||||
if (media->rtp) {
|
||||
ast_rtp_instance_set_stats_vars(ast, media->rtp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* It's possible that session_terminate might cause the session to be destroyed
|
||||
* immediately so we need to keep a reference to it so we can NULL session->channel
|
||||
|
@ -2872,97 +2881,6 @@ static int chan_pjsip_sendtext(struct ast_channel *ast, const char *text)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*! \brief Convert SIP hangup causes to Asterisk hangup causes */
|
||||
static int hangup_sip2cause(int cause)
|
||||
{
|
||||
/* Possible values taken from causes.h */
|
||||
|
||||
switch(cause) {
|
||||
case 401: /* Unauthorized */
|
||||
return AST_CAUSE_CALL_REJECTED;
|
||||
case 403: /* Not found */
|
||||
return AST_CAUSE_CALL_REJECTED;
|
||||
case 404: /* Not found */
|
||||
return AST_CAUSE_UNALLOCATED;
|
||||
case 405: /* Method not allowed */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 407: /* Proxy authentication required */
|
||||
return AST_CAUSE_CALL_REJECTED;
|
||||
case 408: /* No reaction */
|
||||
return AST_CAUSE_NO_USER_RESPONSE;
|
||||
case 409: /* Conflict */
|
||||
return AST_CAUSE_NORMAL_TEMPORARY_FAILURE;
|
||||
case 410: /* Gone */
|
||||
return AST_CAUSE_NUMBER_CHANGED;
|
||||
case 411: /* Length required */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 413: /* Request entity too large */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 414: /* Request URI too large */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 415: /* Unsupported media type */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 420: /* Bad extension */
|
||||
return AST_CAUSE_NO_ROUTE_DESTINATION;
|
||||
case 480: /* No answer */
|
||||
return AST_CAUSE_NO_ANSWER;
|
||||
case 481: /* No answer */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 482: /* Loop detected */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 483: /* Too many hops */
|
||||
return AST_CAUSE_NO_ANSWER;
|
||||
case 484: /* Address incomplete */
|
||||
return AST_CAUSE_INVALID_NUMBER_FORMAT;
|
||||
case 485: /* Ambiguous */
|
||||
return AST_CAUSE_UNALLOCATED;
|
||||
case 486: /* Busy everywhere */
|
||||
return AST_CAUSE_BUSY;
|
||||
case 487: /* Request terminated */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 488: /* No codecs approved */
|
||||
return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
|
||||
case 491: /* Request pending */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 493: /* Undecipherable */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 500: /* Server internal failure */
|
||||
return AST_CAUSE_FAILURE;
|
||||
case 501: /* Call rejected */
|
||||
return AST_CAUSE_FACILITY_REJECTED;
|
||||
case 502:
|
||||
return AST_CAUSE_DESTINATION_OUT_OF_ORDER;
|
||||
case 503: /* Service unavailable */
|
||||
return AST_CAUSE_CONGESTION;
|
||||
case 504: /* Gateway timeout */
|
||||
return AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE;
|
||||
case 505: /* SIP version not supported */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 600: /* Busy everywhere */
|
||||
return AST_CAUSE_USER_BUSY;
|
||||
case 603: /* Decline */
|
||||
return AST_CAUSE_CALL_REJECTED;
|
||||
case 604: /* Does not exist anywhere */
|
||||
return AST_CAUSE_UNALLOCATED;
|
||||
case 606: /* Not acceptable */
|
||||
return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
|
||||
default:
|
||||
if (cause < 500 && cause >= 400) {
|
||||
/* 4xx class error that is unknown - someting wrong with our request */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
} else if (cause < 600 && cause >= 500) {
|
||||
/* 5xx class error - problem in the remote end */
|
||||
return AST_CAUSE_CONGESTION;
|
||||
} else if (cause < 700 && cause >= 600) {
|
||||
/* 6xx - global errors in the 4xx class */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
}
|
||||
return AST_CAUSE_NORMAL;
|
||||
}
|
||||
/* Never reached */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chan_pjsip_session_begin(struct ast_sip_session *session)
|
||||
{
|
||||
RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
|
||||
|
@ -2993,11 +2911,21 @@ static void chan_pjsip_session_end(struct ast_sip_session *session)
|
|||
SCOPE_EXIT_RTN("No channel\n");
|
||||
}
|
||||
|
||||
|
||||
if (session->active_media_state &&
|
||||
session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO]) {
|
||||
struct ast_sip_session_media *media =
|
||||
session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO];
|
||||
if (media->rtp) {
|
||||
ast_rtp_instance_set_stats_vars(session->channel, media->rtp);
|
||||
}
|
||||
}
|
||||
|
||||
chan_pjsip_remove_hold(ast_channel_uniqueid(session->channel));
|
||||
|
||||
ast_set_hangupsource(session->channel, ast_channel_name(session->channel), 0);
|
||||
if (!ast_channel_hangupcause(session->channel) && session->inv_session) {
|
||||
int cause = hangup_sip2cause(session->inv_session->cause);
|
||||
int cause = ast_sip_hangup_sip2cause(session->inv_session->cause);
|
||||
|
||||
ast_queue_hangup_with_cause(session->channel, cause);
|
||||
} else {
|
||||
|
@ -3009,11 +2937,11 @@ static void chan_pjsip_session_end(struct ast_sip_session *session)
|
|||
|
||||
static void set_sipdomain_variable(struct ast_sip_session *session)
|
||||
{
|
||||
pjsip_sip_uri *sip_ruri = pjsip_uri_get_uri(session->request_uri);
|
||||
size_t size = pj_strlen(&sip_ruri->host) + 1;
|
||||
const pj_str_t *host = ast_sip_pjsip_uri_get_hostname(session->request_uri);
|
||||
size_t size = pj_strlen(host) + 1;
|
||||
char *domain = ast_alloca(size);
|
||||
|
||||
ast_copy_pj_str(domain, &sip_ruri->host, size);
|
||||
ast_copy_pj_str(domain, host, size);
|
||||
|
||||
pbx_builtin_setvar_helper(session->channel, "SIPDOMAIN", domain);
|
||||
return;
|
||||
|
@ -3191,7 +3119,7 @@ static void chan_pjsip_incoming_response_update_cause(struct ast_sip_session *se
|
|||
snprintf(cause_code->code, data_size - sizeof(*cause_code) + 1, "SIP %d %.*s", status.code,
|
||||
(int) pj_strlen(&status.reason), pj_strbuf(&status.reason));
|
||||
|
||||
cause_code->ast_cause = hangup_sip2cause(status.code);
|
||||
cause_code->ast_cause = ast_sip_hangup_sip2cause(status.code);
|
||||
ast_queue_control_data(session->channel, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size);
|
||||
ast_channel_hangupcause_hash_set(session->channel, cause_code, data_size);
|
||||
|
||||
|
|
|
@ -381,9 +381,9 @@ static struct ast_channel *unicast_rtp_request(const char *type, struct ast_form
|
|||
|
||||
ast_channel_tech_pvt_set(chan, instance);
|
||||
|
||||
ast_rtp_instance_get_local_address(instance, &local_address);
|
||||
pbx_builtin_setvar_helper(chan, "UNICASTRTP_LOCAL_ADDRESS",
|
||||
ast_sockaddr_stringify_addr(&local_address));
|
||||
ast_rtp_instance_get_local_address(instance, &local_address);
|
||||
pbx_builtin_setvar_helper(chan, "UNICASTRTP_LOCAL_PORT",
|
||||
ast_sockaddr_stringify_port(&local_address));
|
||||
|
||||
|
|
|
@ -304,6 +304,12 @@
|
|||
<enum name="rtt">
|
||||
<para>Round trip time</para>
|
||||
</enum>
|
||||
<enum name="txmes">
|
||||
<para>Transmitted Media Experience Score</para>
|
||||
</enum>
|
||||
<enum name="rxmes">
|
||||
<para>Received Media Experience Score</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_jitter">
|
||||
|
@ -387,6 +393,37 @@
|
|||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_mes">
|
||||
<para>Retrieve a summary of all RTCP Media Experience Score information.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minmes">
|
||||
<para>Minimum MES based on us analysing received packets.</para>
|
||||
</enum>
|
||||
<enum name="maxmes">
|
||||
<para>Maximum MES based on us analysing received packets.</para>
|
||||
</enum>
|
||||
<enum name="avgmes">
|
||||
<para>Average MES based on us analysing received packets.</para>
|
||||
</enum>
|
||||
<enum name="stdevmes">
|
||||
<para>Standard deviation MES based on us analysing received packets.</para>
|
||||
</enum>
|
||||
<enum name="reported_minmes">
|
||||
<para>Minimum MES based on data we get in Sender and Receiver Reports sent by the remote end</para>
|
||||
</enum>
|
||||
<enum name="reported_maxmes">
|
||||
<para>Maximum MES based on data we get in Sender and Receiver Reports sent by the remote end</para>
|
||||
</enum>
|
||||
<enum name="reported_avgmes">
|
||||
<para>Average MES based on data we get in Sender and Receiver Reports sent by the remote end</para>
|
||||
</enum>
|
||||
<enum name="reported_stdevmes">
|
||||
<para>Standard deviation MES based on data we get in Sender and Receiver Reports sent by the remote end</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="txcount"><para>Transmitted packet count</para></enum>
|
||||
<enum name="rxcount"><para>Received packet count</para></enum>
|
||||
<enum name="txjitter"><para>Transmitted packet jitter</para></enum>
|
||||
|
@ -416,6 +453,24 @@
|
|||
<enum name="stdevrtt"><para>Standard deviation round trip time</para></enum>
|
||||
<enum name="local_ssrc"><para>Our Synchronization Source identifier</para></enum>
|
||||
<enum name="remote_ssrc"><para>Their Synchronization Source identifier</para></enum>
|
||||
<enum name="txmes"><para>
|
||||
Current MES based on us analyzing rtt, jitter and loss
|
||||
in the actual received RTP stream received from the remote end.
|
||||
I.E. This is the MES for the incoming audio stream.
|
||||
</para></enum>
|
||||
<enum name="rxmes"><para>
|
||||
Current MES based on rtt and the jitter and loss values in
|
||||
RTCP sender and receiver reports we receive from the
|
||||
remote end. I.E. This is the MES for the outgoing audio stream.
|
||||
</para></enum>
|
||||
<enum name="remote_maxmes"><para>Max MES based on data we get in Sender and Receiver Reports sent by the remote end</para></enum>
|
||||
<enum name="remote_minmes"><para>Min MES based on data we get in Sender and Receiver Reports sent by the remote end</para></enum>
|
||||
<enum name="remote_normdevmes"><para>Average MES based on data we get in Sender and Receiver Reports sent by the remote end</para></enum>
|
||||
<enum name="remote_stdevmes"><para>Standard deviation MES based on data we get in Sender and Receiver Reports sent by the remote end</para></enum>
|
||||
<enum name="local_maxmes"><para>Max MES based on us analyzing the received RTP stream</para></enum>
|
||||
<enum name="local_minmes"><para>Min MES based on us analyzing the received RTP stream</para></enum>
|
||||
<enum name="local_normdevmes"><para>Average MES based on us analyzing the received RTP stream</para></enum>
|
||||
<enum name="local_stdevmes"><para>Standard deviation MES based on us analyzing the received RTP stream</para></enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="media_type" required="false">
|
||||
|
@ -678,6 +733,8 @@ static int channel_read_rtcp(struct ast_channel *chan, const char *type, const c
|
|||
stat_field = AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT;
|
||||
} else if (!strcasecmp(type, "all_loss")) {
|
||||
stat_field = AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS;
|
||||
} else if (!strcasecmp(type, "all_mes")) {
|
||||
stat_field = AST_RTP_INSTANCE_STAT_FIELD_QUALITY_MES;
|
||||
}
|
||||
|
||||
if (!ast_rtp_instance_get_quality(media->rtp, stat_field, buf, buflen)) {
|
||||
|
@ -724,6 +781,16 @@ static int channel_read_rtcp(struct ast_channel *chan, const char *type, const c
|
|||
{ "stdevrtt", DBL, { .d8 = &stats.stdevrtt, }, },
|
||||
{ "local_ssrc", INT, { .i4 = &stats.local_ssrc, }, },
|
||||
{ "remote_ssrc", INT, { .i4 = &stats.remote_ssrc, }, },
|
||||
{ "txmes", DBL, { .d8 = &stats.txmes, }, },
|
||||
{ "rxmes", DBL, { .d8 = &stats.rxmes, }, },
|
||||
{ "remote_maxmes", DBL, { .d8 = &stats.remote_maxmes, }, },
|
||||
{ "remote_minmes", DBL, { .d8 = &stats.remote_minmes, }, },
|
||||
{ "remote_normdevmes", DBL, { .d8 = &stats.remote_normdevmes, }, },
|
||||
{ "remote_stdevmes", DBL, { .d8 = &stats.remote_stdevmes, }, },
|
||||
{ "local_maxmes", DBL, { .d8 = &stats.local_maxmes, }, },
|
||||
{ "local_minmes", DBL, { .d8 = &stats.local_minmes, }, },
|
||||
{ "local_normdevmes", DBL, { .d8 = &stats.local_normdevmes, }, },
|
||||
{ "local_stdevmes", DBL, { .d8 = &stats.local_stdevmes, }, },
|
||||
{ NULL, },
|
||||
};
|
||||
|
||||
|
|
|
@ -2138,7 +2138,7 @@ static void *__analog_ss_thread(void *data)
|
|||
/* If starting a threeway call, never timeout on the first digit so someone
|
||||
can use flash-hook as a "hold" feature */
|
||||
if (p->subs[ANALOG_SUB_THREEWAY].owner) {
|
||||
timeout = 999999;
|
||||
timeout = INT_MAX;
|
||||
}
|
||||
while (len < AST_MAX_EXTENSION-1) {
|
||||
int is_exten_parking = 0;
|
||||
|
@ -3731,6 +3731,32 @@ void *analog_handle_init_event(struct analog_pvt *i, int event)
|
|||
/* Handle an event on a given channel for the monitor thread. */
|
||||
switch (event) {
|
||||
case ANALOG_EVENT_WINKFLASH:
|
||||
case ANALOG_EVENT_RINGBEGIN:
|
||||
switch (i->sig) {
|
||||
case ANALOG_SIG_FXSLS:
|
||||
case ANALOG_SIG_FXSGS:
|
||||
case ANALOG_SIG_FXSKS:
|
||||
if (i->immediate) {
|
||||
if (i->use_callerid || i->usedistinctiveringdetection) {
|
||||
ast_log(LOG_WARNING, "Can't start PBX immediately, must wait for Caller ID / distinctive ring\n");
|
||||
} else {
|
||||
/* If we don't care about Caller ID or Distinctive Ring, then there's
|
||||
* no need to wait for anything before accepting the call, as
|
||||
* waiting will buy us nothing.
|
||||
* So if the channel is configured for immediate, actually start immediately
|
||||
* and get the show on the road as soon as possible. */
|
||||
ast_debug(1, "Disabling ring timeout (previously %d) to begin handling immediately\n", i->ringt_base);
|
||||
analog_set_ringtimeout(i, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* Fall through */
|
||||
if (!(ISTRUNK(i) && i->immediate && !i->use_callerid && !i->usedistinctiveringdetection)) {
|
||||
break;
|
||||
}
|
||||
case ANALOG_EVENT_RINGOFFHOOK:
|
||||
if (i->inalarm) {
|
||||
break;
|
||||
|
|
|
@ -8,6 +8,11 @@ total_analysis_time = 5000 ; Maximum time allowed for the algorithm to decide
|
|||
silence_threshold = 256 ; If the average level of noise in a sample does not reach
|
||||
; this value, from a scale of 0 to 32767, then we will consider
|
||||
; it to be silence.
|
||||
;playback_file = ; Audio file to play while AMD is running, so the caller
|
||||
; does not just hear silence. Note that specifying this here
|
||||
; will apply to ALL AMD runs, so you may wish to set it
|
||||
; in the dialplan as an argument to AMD() instead.
|
||||
; Default is no audio file (not to play anything).
|
||||
|
||||
; Greeting ;
|
||||
initial_silence = 2500 ; Maximum silence duration before the greeting.
|
||||
|
@ -19,7 +24,7 @@ greeting = 1500 ; Maximum length of a greeting. If exceeded, then the
|
|||
|
||||
; Word detection ;
|
||||
min_word_length = 100 ; Minimum duration of Voice to considered as a word
|
||||
maximum_word_length = 5000 ; Maximum duration of a single Voice utterance allowed.
|
||||
maximum_word_length = 5000 ; Maximum duration of a single Voice utterance allowed.
|
||||
between_words_silence = 50 ; Minimum duration of silence after a word to consider
|
||||
; the audio what follows as a new word
|
||||
|
||||
|
|
|
@ -95,10 +95,13 @@ documentation_language = en_US ; Set the language you want documentation
|
|||
; documented in extensions.conf.sample.
|
||||
; Default gosub.
|
||||
;live_dangerously = no ; Enable the execution of 'dangerous' dialplan
|
||||
; functions from external sources (AMI,
|
||||
; etc.) These functions (such as SHELL) are
|
||||
; considered dangerous because they can allow
|
||||
; privilege escalation.
|
||||
; functions and configuration file access from
|
||||
; external sources (AMI, etc.) These functions
|
||||
; (such as SHELL) are considered dangerous
|
||||
; because they can allow privilege escalation.
|
||||
; Configuration files are considered dangerous
|
||||
; if they exist outside of the Asterisk
|
||||
; configuration directory.
|
||||
; Default no
|
||||
;entityid=00:11:22:33:44:55 ; Entity ID.
|
||||
; This is in the form of a MAC address.
|
||||
|
|
|
@ -32,6 +32,17 @@
|
|||
; is "no".
|
||||
;congestion = no
|
||||
|
||||
; Define whether or not to ignore bridging changes in CDRs. This prevents
|
||||
; bridging changes from resulting in multiple CDRs for different parts of
|
||||
; a call. Default is "no". This setting cannot be changed on a reload.
|
||||
;ignorestatechanges = no
|
||||
|
||||
; Define whether or not to ignore dial updates in CDRs. This prevents
|
||||
; dial updates from resulting in multiple CDRs for different parts of
|
||||
; a call. The last disposition on the channel will be used for the CDR.
|
||||
; Use with caution. Default is "no".
|
||||
;ignoredialchanges = no
|
||||
|
||||
; Normally, CDR's are not closed out until after all extensions are finished
|
||||
; executing. By enabling this option, the CDR will be ended before executing
|
||||
; the "h" extension and hangup handlers so that CDR values such as "end" and
|
||||
|
@ -102,8 +113,6 @@
|
|||
; Here are all the possible back ends:
|
||||
;
|
||||
; csv, custom, manager, odbc, pgsql, radius, sqlite, tds
|
||||
; (also, mysql is available via the asterisk-addons, due to licensing
|
||||
; requirements)
|
||||
; (please note, also, that other backends can be created, by creating
|
||||
; a new backend module in the source cdr/ directory!)
|
||||
;
|
||||
|
|
|
@ -936,8 +936,13 @@ pickupgroup=1
|
|||
; target of the transfer.
|
||||
|
||||
;
|
||||
; Specify whether the channel should be answered immediately or if the simple
|
||||
; switch should provide dialtone, read digits, etc.
|
||||
; On FXS channels (FXO signaled), specifies whether the channel should enter the dialplan
|
||||
; immediately or if the simple switch should provide dialtone, read digits, etc.
|
||||
; On FXO channels (FXS signaled), specifies whether the call should enter the dialplan
|
||||
; immediately or if we should wait for at least one ring. This is required if
|
||||
; Caller ID or distinctive ringing is enabled. If you do not need either, you can
|
||||
; skip waiting for the first ring to begin call processing sooner.
|
||||
;
|
||||
; Note: If immediate=yes the dialplan execution will always start at extension
|
||||
; 's' priority 1 regardless of the dialed number!
|
||||
;
|
||||
|
|
|
@ -58,8 +58,11 @@ type=user
|
|||
; when a channel enters a empty conference. On by default.
|
||||
;wait_marked=yes ; Sets if the user must wait for a marked user to enter before
|
||||
; joining the conference. Off by default.
|
||||
;end_marked=yes ; This option will kick every user with this option set in their
|
||||
; user profile after the last Marked user exists the conference.
|
||||
;end_marked=yes ; This option will kick every non-marked user with this option set in their
|
||||
; user profile after the last marked user exits the conference.
|
||||
;end_marked_any=no ; This option will kick every user with this option set in
|
||||
; their user profile after any marked user exits the conference.
|
||||
; Additionally, note that unlike end_marked, this includes marked users.
|
||||
|
||||
;dsp_drop_silence=yes ; This option drops what Asterisk detects as silence from
|
||||
; entering into the bridge. Enabling this option will drastically
|
||||
|
|
|
@ -6,7 +6,11 @@
|
|||
|
||||
[general]
|
||||
;transferdigittimeout => 3 ; Number of seconds to wait between digits when transferring a call
|
||||
; (default is 3 seconds)
|
||||
; (default is 3 seconds). If the TRANSFER_EXTEN dialplan variable has been set
|
||||
; on the channel of the user that is invoking the transfer feature, then
|
||||
; this option is not used as the user is transferred directly to the extension
|
||||
; specified by TRANSFER_EXTEN (the transfer context remains the context specified
|
||||
; by TRANSFER_CONTEXT, if set, and otherwise the default context).
|
||||
;xfersound = beep ; to indicate an attended transfer is complete
|
||||
;xferfailsound = beeperr ; to indicate a failed transfer
|
||||
;pickupexten = *8 ; Configure the pickup extension. (default is *8)
|
||||
|
@ -26,12 +30,13 @@
|
|||
; By default, this is 2.
|
||||
;transferdialattempts = 3 ; Number of times that a transferer may attempt to dial an extension before
|
||||
; being kicked back to the original call.
|
||||
;transferannouncesound = beep ; Sound to play to a transferer to indicate transfer process has begun. If empty, no sound will be played.
|
||||
;transferretrysound = beep ; Sound to play when a transferer fails to dial a valid extension.
|
||||
;transferinvalidsound = beeperr ; Sound to play when a transferer fails to dial a valid extension and is out of retries.
|
||||
;atxferabort = *1 ; cancel the attended transfer
|
||||
;atxfercomplete = *2 ; complete the attended transfer, dropping out of the call
|
||||
;atxferthreeway = *3 ; complete the attended transfer, but stay in the call. This will turn the call into a multi-party bridge
|
||||
;atxferswap = *4 ; swap to the other party. Once an attended transfer has begun, this options may be used multiple times
|
||||
;atxferswap = *4 ; swap to the other party. Once an attended transfer has begun, this option may be used multiple times
|
||||
|
||||
; Note that the DTMF features listed below only work when two channels have answered and are bridged together.
|
||||
; They can not be used while the remote party is ringing or in progress. If you require this feature you can use
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
;--
|
||||
Geolocation Profile Sample Configuration
|
||||
|
||||
Please see https://wiki.asterisk.org/wiki/display/AST/Geolocation
|
||||
for the most current information.
|
||||
--;
|
||||
|
||||
;--
|
||||
|
@ -134,14 +136,24 @@ allowed. See RFC8787.
|
|||
Example:
|
||||
location_source = sip1.myserver.net
|
||||
|
||||
-- confidence (optional) -----------------------------------------
|
||||
The confidence in the location specified.
|
||||
confidence = pdf=[ unknown | normal | rectangular ], value=<percent_confident>
|
||||
|
||||
Please see RFC7459 for the exact description of this parameter.
|
||||
|
||||
Example:
|
||||
confidence = pdf=normal, value=75
|
||||
|
||||
|
||||
-- Location Example ---------------------------------------------------
|
||||
|
||||
[mylocation]
|
||||
type = location
|
||||
format = civicAddress
|
||||
location_info = country=US
|
||||
location_info = A1="New York", A3="New York", A4=Manhattan
|
||||
location_info = HNO=1633, PRD=W, RD=46th, STS=Street
|
||||
location_info = A1="New York", A3="New York", A4=Manhattan
|
||||
location_info = HNO=1633, PRD=W, RD=46th, STS=Street
|
||||
location_info = PC=10222
|
||||
method = Manual
|
||||
location_source = sip1.myserver.net
|
||||
|
@ -160,9 +172,10 @@ location_source = sip1.myserver.net
|
|||
Defines the object type.
|
||||
type = profile
|
||||
|
||||
-- profile_action (optional) ------------------------------------------
|
||||
-- profile_precedence (optional) --------------------------------------
|
||||
Sets how to reconcile incoming and configured profiles.
|
||||
profile_action = < prefer_incoming | prefer_config | discard_incoming
|
||||
|
||||
profile_precedence = < prefer_incoming | prefer_config | discard_incoming
|
||||
| discard_config >
|
||||
|
||||
On an incoming call leg, "incoming" is the location description
|
||||
|
@ -188,11 +201,13 @@ discard_config: Discard any configured location description. If
|
|||
discard_incoming is the default.
|
||||
|
||||
Example:
|
||||
profile_action = prefer_config
|
||||
profile_precedence = prefer_config
|
||||
|
||||
-- pidf_element (optional) --------------------------------------------
|
||||
PIDF-LO element in which to place the location description.
|
||||
|
||||
pidf_element = < tuple | device | person >
|
||||
Default: device
|
||||
|
||||
If the format is civicAddress or GML, this sets the PIDF element into
|
||||
which the location information will be placed.
|
||||
|
@ -207,10 +222,12 @@ Per [RFC5491], "device" is preferred and therefore the default.
|
|||
Example:
|
||||
pidf_element = tuple
|
||||
|
||||
-- geolocation_routing (optional) -------------------------------------
|
||||
-- allow_routing_use (optional) ---------------------------------------
|
||||
Sets whether the "Geolocation-Routing" header is added to outgoing
|
||||
requests.
|
||||
geolocation_routing = < yes | no >
|
||||
|
||||
allow_routing_use = < yes | no >
|
||||
Default: no
|
||||
|
||||
Set to "yes" to indicate that servers later in the path
|
||||
can use the location information for routing purposes. Set to "no"
|
||||
|
@ -218,7 +235,7 @@ if they should not. If this value isn't specified, no
|
|||
"Geolocation-Routing" header will be added.
|
||||
|
||||
Example:
|
||||
geolocation_routing = yes
|
||||
allow_routing_use = yes
|
||||
|
||||
-- location_reference (optional) --------------------------------------
|
||||
The name of an existing Location object.
|
||||
|
@ -243,7 +260,7 @@ floor and room just for this profile
|
|||
Example:
|
||||
location_info_refinement = floor=20, room=20a2
|
||||
|
||||
-- location_variables -------------------------------------------------
|
||||
-- location_variables (optional) --------------------------------------
|
||||
|
||||
If the referenced Location object uses any replacement variables, they
|
||||
can be assigned here. There is no need to define variables that come
|
||||
|
@ -251,6 +268,33 @@ from the channel using this profile. They get assigned automatically.
|
|||
|
||||
location_variables = myfloor=20, myroom=222
|
||||
|
||||
-- suppress_empty_ca_elements (optional) ------------------------------
|
||||
Sets whether empty values for Civic Address elements should be
|
||||
suppressed from the outgoing PIDF-LO document.
|
||||
|
||||
suppress_empty_ca_elements = < yes | no >
|
||||
Default: no
|
||||
|
||||
Setting to "yes" allows you to define a location info template
|
||||
with channel variables that may or may not exist.
|
||||
|
||||
For example, with:
|
||||
location_info_refinement = FLR=${MyFlr}
|
||||
suppress_empty_ca_elements = no ; the default
|
||||
|
||||
If the MyFlr channel variable weren't set, the outgoing PIDF-LO document
|
||||
would have an empty <FLR/> element in it. If suppress_empty_ca_elements
|
||||
were set to "yes", the FLR element would be dropped from the PIDF-LO
|
||||
document altogether.
|
||||
|
||||
-- format, location_info, location_source, method, confidence ---------
|
||||
You can specify the location object's format, location_info,
|
||||
method, location_source and confidence parameters directly on
|
||||
a profile object for simple scenarios where the location
|
||||
information isn't common with any other profiles. This is
|
||||
mutually exclusive with setting location_reference on the
|
||||
profile.
|
||||
|
||||
-- Profile Example ----------------------------------------------------
|
||||
|
||||
[myprofile]
|
||||
|
@ -262,3 +306,13 @@ profile_action = discard_incoming
|
|||
|
||||
=======================================================================
|
||||
--;
|
||||
|
||||
-- NOTE ---------------------------------------------------------------
|
||||
There are 4 built-in profiles that can be assigned to endpoints:
|
||||
"<prefer_config>"
|
||||
"<discard_config>"
|
||||
"<prefer_incoming>"
|
||||
"<discard_incoming>"
|
||||
The profiles are empty except for having their precedence
|
||||
set.
|
||||
|
||||
|
|
|
@ -22,6 +22,9 @@ capture_password = foo ; If specified, the authorization password
|
|||
capture_id = 1234 ; A unique integer identifier for this
|
||||
; server. This ID will be embedded sent
|
||||
; with each packet from this server.
|
||||
;capture_name = asterisk ; A unique string identifier for this
|
||||
; server. This ID will be embedded sent
|
||||
; with each packet from this server.
|
||||
uuid_type = call-id ; Specify the preferred source for the Homer
|
||||
; correlation UUID. Valid options are:
|
||||
; - 'call-id' for the PJSIP or chan_sip SIP
|
||||
|
|
|
@ -82,6 +82,9 @@ directory=moh
|
|||
; ; in alphabetical order. If 'randstart', the files are sorted
|
||||
; ; in alphabetical order as well, but the first file is chosen
|
||||
; ; at random. If unspecified, the sort order is undefined.
|
||||
;answeredonly=yes ; Only allow answered channels to have music on hold.
|
||||
; Enabling this will prevent MOH on unanswered channels.
|
||||
; (default: "no")
|
||||
|
||||
;[native-alphabetical]
|
||||
;mode=files
|
||||
|
|
|
@ -616,6 +616,8 @@
|
|||
;aggregate_mwi=yes ; (default: "yes")
|
||||
;allow= ; Media Codec s to allow (default: "")
|
||||
;allow_overlap=yes ; Enable RFC3578 overlap dialing support. (default: "yes")
|
||||
;overlap_context=default ; Context to used for overlap dialing matches
|
||||
; (default: same as context option)
|
||||
;aors= ; AoR s to be used with the endpoint (default: "")
|
||||
;auth= ; Authentication Object s associated with the endpoint (default: "")
|
||||
;callerid= ; CallerID information for the endpoint (default: "")
|
||||
|
@ -964,6 +966,11 @@
|
|||
; by the channel driver from the dialplan before they're forwarded
|
||||
; the remote endpoint.
|
||||
;
|
||||
; send_aoc =
|
||||
; This options turns on and off support for sending AOC to endpoints.
|
||||
; AOC updates can be sent using the AOCMessage AMI action or come
|
||||
; from PRI channels.
|
||||
; (default: no)
|
||||
|
||||
|
||||
;==========================AUTH SECTION OPTIONS=========================
|
||||
|
@ -1054,11 +1061,16 @@
|
|||
; 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.
|
||||
; different, at least OpenSSL 1.0.2 is required. This option
|
||||
; can be reloaded resulting in an updated certificate if the
|
||||
; filename remains unchanged.
|
||||
; (default: "")
|
||||
;cipher= ; Preferred cryptography cipher names TLS ONLY (default: "")
|
||||
;method= ; Method of SSL transport TLS ONLY (default: "")
|
||||
;priv_key_file= ; Private key file TLS ONLY (default: "")
|
||||
;priv_key_file= ; Private key file TLS ONLY. This option can be reloaded
|
||||
; resulting in an updated private key if the filename remains
|
||||
; unchanged.
|
||||
; (default: "")
|
||||
;verify_client= ; Require verification of client certificate TLS ONLY (default:
|
||||
; "")
|
||||
;verify_server= ; Require verification of server certificate TLS ONLY (default:
|
||||
|
@ -1331,6 +1343,13 @@
|
|||
; creating an implicit subscription (see RFC 4488).
|
||||
; (default: "yes")
|
||||
|
||||
;all_codecs_on_empty_reinvite=yes
|
||||
; On reception of a re-INVITE without SDP Asterisk will send an SDP
|
||||
; offer in the 200 OK response containing all configured codecs on the
|
||||
; endpoint, instead of simply those that have already been negotiated.
|
||||
; RFC 3261 specifies this as a SHOULD requirement.
|
||||
; (default: "no")
|
||||
|
||||
;allow_sending_180_after_183=yes ; Allow Asterisk to send 180 Ringing to an endpoint
|
||||
; after 183 Session Progress has been send.
|
||||
; If disabled Asterisk will instead send only a
|
||||
|
@ -1582,3 +1601,16 @@
|
|||
|
||||
;mailbox_state_filter= ; Optional regular expression used to filter what
|
||||
; mailboxes we accept events for.
|
||||
|
||||
|
||||
;================================TEL URIs=====================================
|
||||
;
|
||||
; Asterisk has TEL URI support, but with limited scope. Support is only for
|
||||
; TEL URIs present in traffic from a remote party. Asterisk does not generate
|
||||
; any TEL URIs of its own.
|
||||
;
|
||||
; Currently, the allowed request types are INVITE, ACK, BYE, and CANCEL. Any
|
||||
; other request type that contains a TEL URI will behave as it did before.
|
||||
; TEL URIs are allowed in the request, From, and To headers.
|
||||
;
|
||||
; You can match a TEL URI From header by IP, header, or auth_username.
|
||||
|
|
|
@ -78,6 +78,12 @@ monitor-type = MixMonitor
|
|||
;
|
||||
;announce = queue-markq
|
||||
;
|
||||
; An announcement may be specified which is played to the caller just
|
||||
; before they are bridged with an agent. The default is to not play an
|
||||
; announcement to the caller.
|
||||
;
|
||||
;queue-callerannounce = you-are-being-connected
|
||||
;
|
||||
; A strategy may be specified. Valid strategies include:
|
||||
;
|
||||
; ringall - ring all available channels until one answers (default)
|
||||
|
@ -354,6 +360,10 @@ monitor-type = MixMonitor
|
|||
;queue-thereare = queue-thereare
|
||||
; ("calls waiting.")
|
||||
;queue-callswaiting = queue-callswaiting
|
||||
; ("Currently there are more than")
|
||||
;queue-quantity1 = queue-quantity1
|
||||
; ("callers waiting to speak with a representative")
|
||||
;queue-quantity2 = queue-quantity2
|
||||
; ("The current est. holdtime is")
|
||||
;queue-holdtime = queue-holdtime
|
||||
; ("minute.")
|
||||
|
|
|
@ -262,6 +262,8 @@ pagerdateformat=%A, %B %d, %Y at %r
|
|||
; option lets you customize the format sent to particular mailboxes.
|
||||
; Useful if Windows users want wav49, but Linux users want gsm.
|
||||
; [per-mailbox only]
|
||||
; attachextrecs=no ; Whether to attach recordings that are externally added to mailboxes,
|
||||
; such as through MixMonitor. Default is no.
|
||||
; saycid=yes ; Say the caller id information before the message. If not described,
|
||||
; or set to no, it will be in the envelope. When enabled, if a recorded file
|
||||
; with the same name as the caller id exists in
|
||||
|
|
25
configure.ac
25
configure.ac
|
@ -589,6 +589,8 @@ AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS], [PJSIP INVIT
|
|||
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_ENDPOINT_COMPACT_FORM], [PJSIP Compact Form Support on Endpoint], [PJPROJECT], [pjsip])
|
||||
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE], [PJSIP Transport Connection Reuse Disabling], [PJPROJECT], [pjsip])
|
||||
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_OAUTH_AUTHENTICATION], [PJSIP OAuth Authentication Support], [PJPROJECT], [pjsip])
|
||||
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_TLS_TRANSPORT_RESTART], [PJSIP TLS Transport Restart Support], [PJPROJECT], [pjsip])
|
||||
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_EVSUB_PENDING_NOTIFY], [PJSIP NOTIFY Required on SUBSCRIBE], [PJPROJECT], [pjsip])
|
||||
fi
|
||||
|
||||
AST_EXT_LIB_SETUP([POPT], [popt], [popt])
|
||||
|
@ -1397,6 +1399,16 @@ else
|
|||
fi
|
||||
AC_SUBST(AST_NO_FORMAT_TRUNCATION)
|
||||
|
||||
AC_MSG_CHECKING(for -Wno-format-y2k)
|
||||
if $(${CC} -Wno-format-y2k -Werror -S -o /dev/null -xc /dev/null > /dev/null 2>&1); then
|
||||
AC_MSG_RESULT(yes)
|
||||
AST_NO_FORMAT_Y2K=-Wno-format-y2k
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
AST_NO_FORMAT_Y2K=
|
||||
fi
|
||||
AC_SUBST(AST_NO_FORMAT_Y2K)
|
||||
|
||||
AC_MSG_CHECKING(for -Wno-stringop-truncation)
|
||||
if $(${CC} -Wno-stringop-truncation -Werror -S -o /dev/null -xc /dev/null > /dev/null 2>&1); then
|
||||
AC_MSG_RESULT(yes)
|
||||
|
@ -2495,6 +2507,19 @@ if test "$USE_PJPROJECT" != "no" ; then
|
|||
AST_EXT_LIB_CHECK([PJSIP_INV_SESSION_REF], [pjsip], [pjsip_inv_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
|
||||
AST_EXT_LIB_CHECK([PJSIP_AUTH_CLT_DEINIT], [pjsip], [pjsip_auth_clt_deinit], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
|
||||
AST_EXT_LIB_CHECK([PJSIP_TSX_LAYER_FIND_TSX2], [pjsip], [pjsip_tsx_layer_find_tsx2], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
|
||||
AST_EXT_LIB_CHECK([PJSIP_TLS_TRANSPORT_RESTART], [pjsip], [pjsip_tls_transport_restart], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pending_notify in evsub.c" >&5
|
||||
$as_echo_n "checking for pending_notify in evsub... " >&6; }
|
||||
pending_notify=$(${SED} -n -r -e '/^struct\s+pjsip_evsub/,/^\s+void\s+*mod_data/!d ; /pending_notify/p' $(find $PJSIP_EVSUB_PENDING_NOTIFY_DIR -name evsub.c))
|
||||
if test -n "$pending_notify" ; then
|
||||
AC_DEFINE(HAVE_PJSIP_EVSUB_PENDING_NOTIFY, 1, [Define to 1 if evsub requires a NOTIFY on SUBSCRIBE.])
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
"""add security_negotiation and security_mechanisms to endpoint
|
||||
|
||||
Revision ID: 417c0247fd7e
|
||||
Revises: 539f68bede2c
|
||||
Create Date: 2022-08-08 15:35:31.416964
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '417c0247fd7e'
|
||||
down_revision = '539f68bede2c'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects.postgresql import ENUM
|
||||
|
||||
SECURITY_NEGOTIATION_NAME = 'security_negotiation_values'
|
||||
SECURITY_NEGOTIATION_VALUES = ['no', 'mediasec']
|
||||
|
||||
def upgrade():
|
||||
context = op.get_context()
|
||||
|
||||
if context.bind.dialect.name == 'postgresql':
|
||||
security_negotiation_values = ENUM(*SECURITY_NEGOTIATION_VALUES, name=SECURITY_NEGOTIATION_NAME)
|
||||
security_negotiation_values.create(op.get_bind(), checkfirst=False)
|
||||
|
||||
op.add_column('ps_endpoints', sa.Column('security_negotiation',
|
||||
ENUM(*SECURITY_NEGOTIATION_VALUES, name=SECURITY_NEGOTIATION_NAME, create_type=False)))
|
||||
op.add_column('ps_endpoints', sa.Column('security_mechanisms', sa.String(512)))
|
||||
|
||||
op.add_column('ps_registrations', sa.Column('security_negotiation',
|
||||
ENUM(*SECURITY_NEGOTIATION_VALUES, name=SECURITY_NEGOTIATION_NAME, create_type=False)))
|
||||
op.add_column('ps_registrations', sa.Column('security_mechanisms', sa.String(512)))
|
||||
|
||||
def downgrade():
|
||||
context = op.get_context()
|
||||
|
||||
if context.bind.dialect.name == 'mssql':
|
||||
op.drop_constraint('ck_ps_endpoints_security_negotiation_security_negotiation_values', 'ps_endpoints')
|
||||
op.drop_constraint('ck_ps_registrations_security_negotiation_security_negotiation_values', 'ps_registrations')
|
||||
|
||||
op.drop_column('ps_endpoints', 'security_negotiation')
|
||||
op.drop_column('ps_endpoints', 'security_mechanisms')
|
||||
op.drop_column('ps_registrations', 'security_negotiation')
|
||||
op.drop_column('ps_registrations', 'security_mechanisms')
|
||||
|
||||
if context.bind.dialect.name == 'postgresql':
|
||||
enum = ENUM(*SECURITY_NEGOTIATION_VALUES, name=SECURITY_NEGOTIATION_NAME)
|
||||
enum.drop(op.get_bind(), checkfirst=False)
|
|
@ -0,0 +1,57 @@
|
|||
"""Add peer_supported to 100rel
|
||||
|
||||
Revision ID: 539f68bede2c
|
||||
Revises: 9f3692b1654b
|
||||
Create Date: 2022-08-10 09:36:16.576049
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '539f68bede2c'
|
||||
down_revision = '9f3692b1654b'
|
||||
|
||||
from alembic import op
|
||||
from sqlalchemy.dialects.postgresql import ENUM
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
OLD_ENUM = ['no', 'required', 'yes']
|
||||
NEW_ENUM = ['no', 'required', 'peer_supported', 'yes']
|
||||
|
||||
old_type = sa.Enum(*OLD_ENUM, name='pjsip_100rel_values')
|
||||
new_type = sa.Enum(*NEW_ENUM, name='pjsip_100rel_values_v2')
|
||||
|
||||
def upgrade():
|
||||
context = op.get_context()
|
||||
|
||||
# Upgrading to this revision WILL clear your directmedia values.
|
||||
if context.bind.dialect.name != 'postgresql':
|
||||
op.alter_column('ps_endpoints', '100rel',
|
||||
type_=new_type,
|
||||
existing_type=old_type)
|
||||
else:
|
||||
enum = ENUM(*NEW_ENUM, name='pjsip_100rel_values_v2')
|
||||
enum.create(op.get_bind(), checkfirst=False)
|
||||
|
||||
op.execute('ALTER TABLE ps_endpoints ALTER COLUMN 100rel TYPE'
|
||||
' pjsip_100rel_values_v2 USING'
|
||||
' 100rel::text::pjsip_100rel_values_v2')
|
||||
|
||||
ENUM(name="pjsip_100rel_values").drop(op.get_bind(), checkfirst=False)
|
||||
|
||||
def downgrade():
|
||||
context = op.get_context()
|
||||
|
||||
if context.bind.dialect.name != 'postgresql':
|
||||
op.alter_column('ps_endpoints', '100rel',
|
||||
type_=old_type,
|
||||
existing_type=new_type)
|
||||
else:
|
||||
enum = ENUM(*OLD_ENUM, name='pjsip_100rel_values')
|
||||
enum.create(op.get_bind(), checkfirst=False)
|
||||
|
||||
op.execute('ALTER TABLE ps_endpoints ALTER COLUMN 100rel TYPE'
|
||||
' pjsip_100rel_values USING'
|
||||
' 100rel::text::pjsip_100rel_values')
|
||||
|
||||
ENUM(name="pjsip_100rel_values_v2").drop(op.get_bind(), checkfirst=False)
|
|
@ -0,0 +1,38 @@
|
|||
"""add aoc option
|
||||
|
||||
Revision ID: 5a2247c957d2
|
||||
Revises: ccf795ee535f
|
||||
Create Date: 2022-10-30 12:47:56.173511
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '5a2247c957d2'
|
||||
down_revision = 'ccf795ee535f'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects.postgresql import ENUM
|
||||
|
||||
AST_BOOL_NAME = 'ast_bool_values'
|
||||
# We'll just ignore the n/y and f/t abbreviations as Asterisk does not write
|
||||
# those aliases.
|
||||
AST_BOOL_VALUES = [ '0', '1',
|
||||
'off', 'on',
|
||||
'false', 'true',
|
||||
'no', 'yes' ]
|
||||
|
||||
def upgrade():
|
||||
############################# Enums ##############################
|
||||
|
||||
# ast_bool_values has already been created, so use postgres enum object
|
||||
# type to get around "already created" issue - works okay with mysql
|
||||
ast_bool_values = ENUM(*AST_BOOL_VALUES, name=AST_BOOL_NAME, create_type=False)
|
||||
|
||||
op.add_column('ps_endpoints', sa.Column('send_aoc', ast_bool_values))
|
||||
|
||||
def downgrade():
|
||||
if op.get_context().bind.dialect.name == 'mssql':
|
||||
op.drop_constraint('ck_ps_endpoints_send_aoc_ast_bool_values', 'ps_endpoints')
|
||||
op.drop_column('ps_endpoints', 'send_aoc')
|
||||
pass
|
|
@ -0,0 +1,58 @@
|
|||
"""Add Stir Shaken Profile and Codec Preference to ps endpoint
|
||||
|
||||
Revision ID: 9f3692b1654b
|
||||
Revises: 7197536bb68d
|
||||
Create Date: 2022-08-17 11:20:56.433088
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '9f3692b1654b'
|
||||
down_revision = '7197536bb68d'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects.postgresql import ENUM
|
||||
|
||||
PJSIP_INCOMING_CALL_OFFER_PREF_NAME ='pjsip_incoming_call_offer_pref_values'
|
||||
PJSIP_INCOMING_CALL_OFFER_PREF_VALUES = ['local', 'local_first',
|
||||
'remote', 'remote_first']
|
||||
|
||||
PJSIP_OUTGOING_CALL_OFFER_PREF_NAME = 'pjsip_outgoing_call_offer_pref_values'
|
||||
PJSIP_OUTGOING_CALL_OFFER_PREF_VALUES = ['local', 'local_merge', 'local_first',
|
||||
'remote', 'remote_merge', 'remote_first']
|
||||
|
||||
def upgrade():
|
||||
context = op.get_context()
|
||||
|
||||
if context.bind.dialect.name == 'postgresql':
|
||||
enum_in = ENUM(*PJSIP_INCOMING_CALL_OFFER_PREF_VALUES, name=PJSIP_INCOMING_CALL_OFFER_PREF_NAME)
|
||||
enum_out = ENUM(*PJSIP_OUTGOING_CALL_OFFER_PREF_VALUES, name=PJSIP_OUTGOING_CALL_OFFER_PREF_NAME)
|
||||
|
||||
enum_in.create(op.get_bind(), checkfirst=False)
|
||||
enum_out.create(op.get_bind(), checkfirst=False)
|
||||
|
||||
op.add_column('ps_endpoints', sa.Column('incoming_call_offer_pref',
|
||||
sa.Enum(*PJSIP_INCOMING_CALL_OFFER_PREF_VALUES, name=PJSIP_INCOMING_CALL_OFFER_PREF_NAME)))
|
||||
|
||||
op.add_column('ps_endpoints', sa.Column('outgoing_call_offer_pref',
|
||||
sa.Enum(*PJSIP_OUTGOING_CALL_OFFER_PREF_VALUES, name=PJSIP_OUTGOING_CALL_OFFER_PREF_NAME)))
|
||||
|
||||
op.add_column('ps_endpoints', sa.Column('stir_shaken_profile', sa.String(80)))
|
||||
|
||||
def downgrade():
|
||||
context = op.get_context()
|
||||
|
||||
if context.bind.dialect.name == 'mssql':
|
||||
op.drop_constraint('ck_ps_endpoints_incoming_call_offer_pref_pjsip_incoming_call_offer_pref_values', 'ps_endpoints')
|
||||
op.drop_constraint('ck_ps_endpoints_outgoing_call_offer_pref_pjsip_outgoing_call_offer_pref_values', 'ps_endpoints')
|
||||
|
||||
op.drop_column('ps_endpoints', 'outgoing_call_offer_pref')
|
||||
op.drop_column('ps_endpoints', 'incoming_call_offer_pref')
|
||||
op.drop_column('ps_endpoints', 'stir_shaken_profile')
|
||||
|
||||
enums = [PJSIP_INCOMING_CALL_OFFER_PREF_NAME, PJSIP_OUTGOING_CALL_OFFER_PREF_NAME]
|
||||
|
||||
if context.bind.dialect.name == 'postgresql':
|
||||
for e in enums:
|
||||
ENUM(name=e).drop(op.get_bind(), checkfirst=False)
|
|
@ -0,0 +1,37 @@
|
|||
"""all_codecs_on_empty_reinvite
|
||||
|
||||
Revision ID: ccf795ee535f
|
||||
Revises: 539f68bede2c
|
||||
Create Date: 2022-09-28 09:14:36.709781
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'ccf795ee535f'
|
||||
down_revision = '417c0247fd7e'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects.postgresql import ENUM
|
||||
|
||||
AST_BOOL_NAME = 'ast_bool_values'
|
||||
# We'll just ignore the n/y and f/t abbreviations as Asterisk does not write
|
||||
# those aliases.
|
||||
AST_BOOL_VALUES = [ '0', '1',
|
||||
'off', 'on',
|
||||
'false', 'true',
|
||||
'no', 'yes' ]
|
||||
|
||||
def upgrade():
|
||||
############################# Enums ##############################
|
||||
|
||||
# ast_bool_values has already been created, so use postgres enum object
|
||||
# type to get around "already created" issue - works okay with mysql
|
||||
ast_bool_values = ENUM(*AST_BOOL_VALUES, name=AST_BOOL_NAME, create_type=False)
|
||||
|
||||
op.add_column('ps_globals', sa.Column('all_codecs_on_empty_reinvite', ast_bool_values))
|
||||
|
||||
def downgrade():
|
||||
if op.get_context().bind.dialect.name == 'mssql':
|
||||
op.drop_constraint('ck_ps_globals_all_codecs_on_empty_reinvite_ast_bool_values', 'ps_globals')
|
||||
op.drop_column('ps_globals', 'all_codecs_on_empty_reinvite')
|
|
@ -0,0 +1,21 @@
|
|||
"""add overlap_context
|
||||
|
||||
Revision ID: f261363a857f
|
||||
Revises: 5a2247c957d2
|
||||
Create Date: 2022-12-09 13:58:48.622000
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'f261363a857f'
|
||||
down_revision = '5a2247c957d2'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('ps_endpoints', sa.Column('overlap_context', sa.String(80)))
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('ps_endpoints', 'overlap_context')
|
|
@ -0,0 +1,41 @@
|
|||
CREATE TABLE alembic_version (
|
||||
version_num VARCHAR(32) NOT NULL,
|
||||
CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num)
|
||||
);
|
||||
|
||||
-- Running upgrade -> 210693f3123d
|
||||
|
||||
CREATE TABLE cdr (
|
||||
accountcode VARCHAR(20),
|
||||
src VARCHAR(80),
|
||||
dst VARCHAR(80),
|
||||
dcontext VARCHAR(80),
|
||||
clid VARCHAR(80),
|
||||
channel VARCHAR(80),
|
||||
dstchannel VARCHAR(80),
|
||||
lastapp VARCHAR(80),
|
||||
lastdata VARCHAR(80),
|
||||
start DATETIME,
|
||||
answer DATETIME,
|
||||
end DATETIME,
|
||||
duration INTEGER,
|
||||
billsec INTEGER,
|
||||
disposition VARCHAR(45),
|
||||
amaflags VARCHAR(45),
|
||||
userfield VARCHAR(256),
|
||||
uniqueid VARCHAR(150),
|
||||
linkedid VARCHAR(150),
|
||||
peeraccount VARCHAR(20),
|
||||
sequence INTEGER
|
||||
);
|
||||
|
||||
INSERT INTO alembic_version (version_num) VALUES ('210693f3123d');
|
||||
|
||||
-- Running upgrade 210693f3123d -> 54cde9847798
|
||||
|
||||
ALTER TABLE cdr MODIFY accountcode VARCHAR(80) NULL;
|
||||
|
||||
ALTER TABLE cdr MODIFY peeraccount VARCHAR(80) NULL;
|
||||
|
||||
UPDATE alembic_version SET version_num='54cde9847798' WHERE alembic_version.version_num = '210693f3123d';
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,35 @@
|
|||
CREATE TABLE alembic_version (
|
||||
version_num VARCHAR(32) NOT NULL,
|
||||
CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num)
|
||||
);
|
||||
|
||||
-- Running upgrade -> a2e9769475e
|
||||
|
||||
CREATE TABLE voicemail_messages (
|
||||
dir VARCHAR(255) NOT NULL,
|
||||
msgnum INTEGER NOT NULL,
|
||||
context VARCHAR(80),
|
||||
macrocontext VARCHAR(80),
|
||||
callerid VARCHAR(80),
|
||||
origtime INTEGER,
|
||||
duration INTEGER,
|
||||
recording BLOB,
|
||||
flag VARCHAR(30),
|
||||
category VARCHAR(30),
|
||||
mailboxuser VARCHAR(30),
|
||||
mailboxcontext VARCHAR(30),
|
||||
msg_id VARCHAR(40)
|
||||
);
|
||||
|
||||
ALTER TABLE voicemail_messages ADD CONSTRAINT voicemail_messages_dir_msgnum PRIMARY KEY (dir, msgnum);
|
||||
|
||||
CREATE INDEX voicemail_messages_dir ON voicemail_messages (dir);
|
||||
|
||||
INSERT INTO alembic_version (version_num) VALUES ('a2e9769475e');
|
||||
|
||||
-- Running upgrade a2e9769475e -> 39428242f7f5
|
||||
|
||||
ALTER TABLE voicemail_messages MODIFY recording BLOB(4294967295) NULL;
|
||||
|
||||
UPDATE alembic_version SET version_num='39428242f7f5' WHERE alembic_version.version_num = 'a2e9769475e';
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
BEGIN;
|
||||
|
||||
CREATE TABLE alembic_version (
|
||||
version_num VARCHAR(32) NOT NULL,
|
||||
CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num)
|
||||
);
|
||||
|
||||
-- Running upgrade -> 210693f3123d
|
||||
|
||||
CREATE TABLE cdr (
|
||||
accountcode VARCHAR(20),
|
||||
src VARCHAR(80),
|
||||
dst VARCHAR(80),
|
||||
dcontext VARCHAR(80),
|
||||
clid VARCHAR(80),
|
||||
channel VARCHAR(80),
|
||||
dstchannel VARCHAR(80),
|
||||
lastapp VARCHAR(80),
|
||||
lastdata VARCHAR(80),
|
||||
start TIMESTAMP WITHOUT TIME ZONE,
|
||||
answer TIMESTAMP WITHOUT TIME ZONE,
|
||||
"end" TIMESTAMP WITHOUT TIME ZONE,
|
||||
duration INTEGER,
|
||||
billsec INTEGER,
|
||||
disposition VARCHAR(45),
|
||||
amaflags VARCHAR(45),
|
||||
userfield VARCHAR(256),
|
||||
uniqueid VARCHAR(150),
|
||||
linkedid VARCHAR(150),
|
||||
peeraccount VARCHAR(20),
|
||||
sequence INTEGER
|
||||
);
|
||||
|
||||
INSERT INTO alembic_version (version_num) VALUES ('210693f3123d');
|
||||
|
||||
-- Running upgrade 210693f3123d -> 54cde9847798
|
||||
|
||||
ALTER TABLE cdr ALTER COLUMN accountcode TYPE VARCHAR(80);
|
||||
|
||||
ALTER TABLE cdr ALTER COLUMN peeraccount TYPE VARCHAR(80);
|
||||
|
||||
UPDATE alembic_version SET version_num='54cde9847798' WHERE alembic_version.version_num = '210693f3123d';
|
||||
|
||||
COMMIT;
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,39 @@
|
|||
BEGIN;
|
||||
|
||||
CREATE TABLE alembic_version (
|
||||
version_num VARCHAR(32) NOT NULL,
|
||||
CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num)
|
||||
);
|
||||
|
||||
-- Running upgrade -> a2e9769475e
|
||||
|
||||
CREATE TABLE voicemail_messages (
|
||||
dir VARCHAR(255) NOT NULL,
|
||||
msgnum INTEGER NOT NULL,
|
||||
context VARCHAR(80),
|
||||
macrocontext VARCHAR(80),
|
||||
callerid VARCHAR(80),
|
||||
origtime INTEGER,
|
||||
duration INTEGER,
|
||||
recording BYTEA,
|
||||
flag VARCHAR(30),
|
||||
category VARCHAR(30),
|
||||
mailboxuser VARCHAR(30),
|
||||
mailboxcontext VARCHAR(30),
|
||||
msg_id VARCHAR(40)
|
||||
);
|
||||
|
||||
ALTER TABLE voicemail_messages ADD CONSTRAINT voicemail_messages_dir_msgnum PRIMARY KEY (dir, msgnum);
|
||||
|
||||
CREATE INDEX voicemail_messages_dir ON voicemail_messages (dir);
|
||||
|
||||
INSERT INTO alembic_version (version_num) VALUES ('a2e9769475e');
|
||||
|
||||
-- Running upgrade a2e9769475e -> 39428242f7f5
|
||||
|
||||
ALTER TABLE voicemail_messages ALTER COLUMN recording TYPE BYTEA;
|
||||
|
||||
UPDATE alembic_version SET version_num='39428242f7f5' WHERE alembic_version.version_num = 'a2e9769475e';
|
||||
|
||||
COMMIT;
|
||||
|
|
@ -69,10 +69,10 @@
|
|||
<!ATTLIST configInfo name CDATA #REQUIRED>
|
||||
<!ATTLIST configInfo language CDATA #REQUIRED>
|
||||
|
||||
<!ELEMENT configFile (configObject+)>
|
||||
<!ELEMENT configFile (configObject|xi:include)+>
|
||||
<!ATTLIST configFile name CDATA #REQUIRED>
|
||||
|
||||
<!ELEMENT configObject (synopsis?|description?|syntax?|see-also?|configOption)*>
|
||||
<!ELEMENT configObject (synopsis?|description?|syntax?|see-also?|(configOption|xi:include))*>
|
||||
<!ATTLIST configObject name CDATA #REQUIRED>
|
||||
|
||||
<!ELEMENT configOption (synopsis,description?,syntax?,see-also?)*>
|
||||
|
|
|
@ -189,7 +189,7 @@ static int check_header(FILE *f, int hz)
|
|||
}
|
||||
if(memcmp(buf, "data", 4) == 0 )
|
||||
break;
|
||||
ast_log(LOG_DEBUG, "Skipping unknown block '%.4s'\n", buf);
|
||||
ast_debug(1, "Skipping unknown block '%.4s'\n", buf);
|
||||
if (fseek(f,data,SEEK_CUR) == -1 ) {
|
||||
ast_log(LOG_WARNING, "Failed to skip '%.4s' block: %d\n", buf, data);
|
||||
return -1;
|
||||
|
|
|
@ -1611,6 +1611,7 @@ static int redirecting_write(struct ast_channel *chan, const char *cmd, char *da
|
|||
* reason, so we can just set the reason string to what was given and set the
|
||||
* code to be unknown
|
||||
*/
|
||||
ast_log(LOG_WARNING, "Unknown redirecting reason '%s', defaulting to unknown\n", val);
|
||||
redirecting.orig_reason.code = AST_REDIRECTING_REASON_UNKNOWN;
|
||||
redirecting.orig_reason.str = val;
|
||||
set_it(chan, &redirecting, NULL);
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2021-2022, Naveen Albert
|
||||
*
|
||||
* 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 Set variables and functions on other channels
|
||||
*
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \ingroup functions
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>extended</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/stringfields.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<function name="EXPORT" language="en_US">
|
||||
<synopsis>
|
||||
Set variables or dialplan functions on any arbitrary channel that exists.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="channel" required="true">
|
||||
<para>The complete channel name: <literal>SIP/12-abcd1234</literal>.</para>
|
||||
</parameter>
|
||||
<parameter name="var" required="true">
|
||||
<para>Variable name</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Allows setting variables or functions on any existing channel if it exists.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">IMPORT</ref>
|
||||
<ref type="function">MASTER_CHANNEL</ref>
|
||||
<ref type="function">SHARED</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
***/
|
||||
|
||||
static int func_export_write(struct ast_channel *chan, const char *function, char *data, const char *value)
|
||||
{
|
||||
struct ast_channel *ochan;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(channel);
|
||||
AST_APP_ARG(var);
|
||||
);
|
||||
AST_STANDARD_APP_ARGS(args, data);
|
||||
|
||||
if (!args.channel) {
|
||||
ast_log(LOG_WARNING, "No channel was provided to %s function.\n", function);
|
||||
return -1;
|
||||
}
|
||||
if (!args.var) {
|
||||
ast_log(LOG_WARNING, "No variable name was provided to %s function.\n", function);
|
||||
return -1;
|
||||
}
|
||||
ochan = ast_channel_get_by_name(args.channel);
|
||||
if (!ochan) {
|
||||
ast_log(LOG_WARNING, "Channel '%s' not found! '%s' not set.\n", args.channel, args.var);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pbx_builtin_setvar_helper(ochan, data, value);
|
||||
ast_channel_unref(ochan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ast_custom_function export_function = {
|
||||
.name = "EXPORT",
|
||||
.write = func_export_write,
|
||||
};
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
return ast_custom_function_unregister(&export_function);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_custom_function_register(&export_function);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Set variables and functions on other channels");
|
|
@ -341,8 +341,7 @@ static void print_frame(struct ast_frame *frame)
|
|||
ast_verbose("SubClass: PVT_CAUSE_CODE\n");
|
||||
break;
|
||||
case AST_CONTROL_MASQUERADE_NOTIFY:
|
||||
/* Should never happen. */
|
||||
ast_assert(0);
|
||||
ast_verbose("SubClass: MASQUERADE_NOTIFY\n");
|
||||
break;
|
||||
case AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE:
|
||||
ast_verbose("SubClass: STREAM_TOPOLOGY_REQUEST_CHANGE\n");
|
||||
|
@ -398,6 +397,7 @@ static void print_frame(struct ast_frame *frame)
|
|||
break;
|
||||
case AST_FRAME_TEXT:
|
||||
ast_verbose("FrameType: TXT\n");
|
||||
ast_verbose("Text: %.*s\n", frame->datalen, (char*) frame->data.ptr);
|
||||
break;
|
||||
case AST_FRAME_TEXT_DATA:
|
||||
ast_verbose("FrameType: TXT_DATA\n");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2021, Naveen Albert
|
||||
* Copyright (C) 2021-2022, Naveen Albert
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
|
@ -54,6 +54,21 @@
|
|||
</parameter>
|
||||
<parameter name="item" required="true">
|
||||
<para>The name of the key whose value to return.</para>
|
||||
<para>Multiple keys can be listed separated by a hierarchy delimeter, which will recursively index into a nested JSON string to retrieve a specific subkey's value.</para>
|
||||
</parameter>
|
||||
<parameter name="separator" required="false">
|
||||
<para>A single character that delimits a key hierarchy for nested indexing. Default is a period (.)</para>
|
||||
<para>This value should not appear in the key or hierarchy of keys itself, except to delimit the hierarchy of keys.</para>
|
||||
</parameter>
|
||||
<parameter name="options" required="no">
|
||||
<optionlist>
|
||||
<option name="c">
|
||||
<para>For keys that reference a JSON array, return
|
||||
the number of items in the array.</para>
|
||||
<para>This option has no effect on any other type
|
||||
of value.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
|
@ -69,19 +84,131 @@
|
|||
|
||||
AST_THREADSTORAGE(result_buf);
|
||||
|
||||
enum json_option_flags {
|
||||
OPT_COUNT = (1 << 0),
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(json_options, {
|
||||
AST_APP_OPTION('c', OPT_COUNT),
|
||||
});
|
||||
|
||||
#define MAX_JSON_STACK 32
|
||||
|
||||
static int parse_node(char **key, char *currentkey, char *nestchar, int count, struct ast_json *json, char *buf, size_t len, int *depth)
|
||||
{
|
||||
const char *result = NULL;
|
||||
char *previouskey;
|
||||
struct ast_json *jsonval = json;
|
||||
|
||||
/* Prevent a huge JSON string from blowing the stack. */
|
||||
if (*depth > MAX_JSON_STACK) {
|
||||
ast_log(LOG_WARNING, "Max JSON stack (%d) exceeded\n", MAX_JSON_STACK);
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(buf, len, "%s", ""); /* clear the buffer from previous round if necessary */
|
||||
if (!json) { /* no error or warning should be thrown */
|
||||
ast_debug(1, "Could not find key '%s' in parsed JSON\n", currentkey);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch(ast_json_typeof(jsonval)) {
|
||||
unsigned long int size;
|
||||
int r;
|
||||
|
||||
case AST_JSON_STRING:
|
||||
result = ast_json_string_get(jsonval);
|
||||
ast_debug(1, "Got JSON string: %s\n", result);
|
||||
ast_copy_string(buf, result, len);
|
||||
break;
|
||||
case AST_JSON_INTEGER:
|
||||
r = ast_json_integer_get(jsonval);
|
||||
ast_debug(1, "Got JSON integer: %d\n", r);
|
||||
snprintf(buf, len, "%d", r); /* the snprintf below is mutually exclusive with this one */
|
||||
break;
|
||||
case AST_JSON_ARRAY:
|
||||
ast_debug(1, "Got JSON array\n");
|
||||
previouskey = currentkey;
|
||||
currentkey = strsep(key, nestchar); /* retrieve the desired index */
|
||||
size = ast_json_array_size(jsonval);
|
||||
ast_debug(1, "Parsed JSON array of size %lu, key: %s\n", size, currentkey);
|
||||
if (!currentkey) { /* this is the end, so just dump the array */
|
||||
if (count) {
|
||||
ast_debug(1, "No key on which to index in the array, so returning count: %lu\n", size);
|
||||
snprintf(buf, len, "%lu", size);
|
||||
return 0;
|
||||
} else {
|
||||
char *result2 = ast_json_dump_string(jsonval);
|
||||
ast_debug(1, "No key on which to index in the array, so dumping '%s' array\n", previouskey);
|
||||
ast_copy_string(buf, result2, len);
|
||||
ast_json_free(result2);
|
||||
}
|
||||
} else if (ast_str_to_int(currentkey, &r) || r < 0) {
|
||||
ast_debug(1, "Requested index '%s' is not numeric or is invalid\n", currentkey);
|
||||
} else if (r >= size) {
|
||||
ast_debug(1, "Requested index '%d' does not exist in parsed array\n", r);
|
||||
} else {
|
||||
struct ast_json *json2 = ast_json_array_get(jsonval, r);
|
||||
if (!json2) {
|
||||
ast_debug(1, "Array index %d contains empty item\n", r);
|
||||
return -1;
|
||||
}
|
||||
previouskey = currentkey;
|
||||
currentkey = strsep(key, nestchar); /* get the next subkey */
|
||||
ast_debug(1, "Recursing on index %d in array (key was '%s' and is now '%s')\n", r, previouskey, currentkey);
|
||||
/* json2 is a borrowed ref. That's fine, since json won't get freed until recursing is over */
|
||||
/* If there are keys remaining, then parse the next object we can get. Otherwise, just dump the child */
|
||||
if (parse_node(key, currentkey, nestchar, count, currentkey ? ast_json_object_get(json2, currentkey) : json2, buf, len, depth)) { /* recurse on this node */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AST_JSON_OBJECT:
|
||||
default:
|
||||
ast_debug(1, "Got generic JSON object for key %s\n", currentkey);
|
||||
if (!currentkey) { /* this is the end, so just dump the object */
|
||||
char *result2 = ast_json_dump_string(jsonval);
|
||||
ast_copy_string(buf, result2, len);
|
||||
ast_json_free(result2);
|
||||
} else {
|
||||
previouskey = currentkey;
|
||||
currentkey = strsep(key, nestchar); /* retrieve the desired index */
|
||||
ast_debug(1, "Recursing on object (key was '%s' and is now '%s')\n", previouskey, currentkey);
|
||||
if (parse_node(key, currentkey, nestchar, count, ast_json_object_get(jsonval, currentkey), buf, len, depth)) { /* recurse on this node */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_decode_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
{
|
||||
struct ast_json *json, *jsonval;
|
||||
int count = 0;
|
||||
struct ast_flags flags = {0};
|
||||
struct ast_json *json = NULL;
|
||||
char *nestchar = "."; /* default delimeter for nesting key indexing is . */
|
||||
int res, depth = 0;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(varname);
|
||||
AST_APP_ARG(key);
|
||||
AST_APP_ARG(nestchar);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
char *varsubst, *result2;
|
||||
const char *result = NULL;
|
||||
char *varsubst, *key, *currentkey, *nextkey, *firstkey, *tmp;
|
||||
struct ast_str *str = ast_str_thread_get(&result_buf, 16);
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, data);
|
||||
|
||||
if (!ast_strlen_zero(args.options)) {
|
||||
ast_app_parse_options(json_options, &flags, NULL, args.options);
|
||||
if (ast_test_flag(&flags, OPT_COUNT)) {
|
||||
count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(args.varname)) {
|
||||
ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
|
||||
return -1;
|
||||
|
@ -90,6 +217,15 @@ static int json_decode_read(struct ast_channel *chan, const char *cmd, char *dat
|
|||
ast_log(LOG_WARNING, "%s requires a key\n", cmd);
|
||||
return -1;
|
||||
}
|
||||
key = ast_strdupa(args.key);
|
||||
if (!ast_strlen_zero(args.nestchar)) {
|
||||
int seplen = strlen(args.nestchar);
|
||||
if (seplen != 1) {
|
||||
ast_log(LOG_WARNING, "Nesting separator '%s' has length %d and is invalid (must be a single character)\n", args.nestchar, seplen);
|
||||
} else {
|
||||
nestchar = args.nestchar;
|
||||
}
|
||||
}
|
||||
|
||||
varsubst = ast_alloca(strlen(args.varname) + 4); /* +4 for ${} and null terminator */
|
||||
if (!varsubst) {
|
||||
|
@ -98,45 +234,43 @@ static int json_decode_read(struct ast_channel *chan, const char *cmd, char *dat
|
|||
}
|
||||
sprintf(varsubst, "${%s}", args.varname); /* safe, because of the above allocation */
|
||||
ast_str_substitute_variables(&str, 0, chan, varsubst);
|
||||
|
||||
ast_debug(1, "Parsing JSON using nesting delimeter '%s'\n", nestchar);
|
||||
|
||||
if (ast_str_strlen(str) == 0) {
|
||||
ast_debug(1, "Variable '%s' contains no data, nothing to search!\n", args.varname);
|
||||
return -1; /* empty json string */
|
||||
}
|
||||
|
||||
ast_debug(1, "Parsing JSON: %s\n", ast_str_buffer(str));
|
||||
/* allow for multiple key nesting */
|
||||
currentkey = key;
|
||||
firstkey = ast_strdupa(currentkey);
|
||||
tmp = strstr(firstkey, nestchar);
|
||||
if (tmp) {
|
||||
*tmp = '\0';
|
||||
}
|
||||
|
||||
/* parse a string as JSON */
|
||||
ast_debug(1, "Parsing JSON: %s (key: '%s')\n", ast_str_buffer(str), currentkey);
|
||||
if (ast_strlen_zero(currentkey)) {
|
||||
ast_debug(1, "Empty JSON key\n");
|
||||
return -1;
|
||||
}
|
||||
if (ast_str_strlen(str) == 0) {
|
||||
ast_debug(1, "JSON node '%s', contains no data, nothing to search!\n", currentkey);
|
||||
return -1; /* empty json string */
|
||||
}
|
||||
json = ast_json_load_str(str, NULL);
|
||||
|
||||
if (!json) {
|
||||
ast_log(LOG_WARNING, "Failed to parse as JSON: %s\n", ast_str_buffer(str));
|
||||
return -1;
|
||||
}
|
||||
|
||||
jsonval = ast_json_object_get(json, args.key);
|
||||
if (!jsonval) { /* no error or warning should be thrown */
|
||||
ast_debug(1, "Could not find key '%s' in parsed JSON\n", args.key);
|
||||
ast_json_free(json);
|
||||
return -1;
|
||||
}
|
||||
switch(ast_json_typeof(jsonval)) {
|
||||
int r;
|
||||
case AST_JSON_STRING:
|
||||
result = ast_json_string_get(jsonval);
|
||||
snprintf(buf, len, "%s", result);
|
||||
break;
|
||||
case AST_JSON_INTEGER:
|
||||
r = ast_json_integer_get(jsonval);
|
||||
snprintf(buf, len, "%d", r); /* the snprintf below is mutually exclusive with this one */
|
||||
break;
|
||||
default:
|
||||
result2 = ast_json_dump_string(jsonval);
|
||||
snprintf(buf, len, "%s", result2);
|
||||
ast_json_free(result2);
|
||||
break;
|
||||
}
|
||||
ast_json_free(json);
|
||||
|
||||
return 0;
|
||||
/* parse the JSON object, potentially recursively */
|
||||
nextkey = strsep(&key, nestchar);
|
||||
res = parse_node(&key, nextkey, nestchar, count, ast_json_object_get(json, firstkey), buf, len, &depth);
|
||||
ast_json_unref(json);
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct ast_custom_function json_decode_function = {
|
||||
|
@ -151,12 +285,26 @@ AST_TEST_DEFINE(test_JSON_DECODE)
|
|||
struct ast_channel *chan; /* dummy channel */
|
||||
struct ast_str *str; /* fancy string for holding comparing value */
|
||||
|
||||
const char *test_strings[][5] = {
|
||||
{"{\"city\": \"Anytown\", \"state\": \"USA\"}", "city", "Anytown"},
|
||||
{"{\"city\": \"Anytown\", \"state\": \"USA\"}", "state", "USA"},
|
||||
{"{\"city\": \"Anytown\", \"state\": \"USA\"}", "blah", ""},
|
||||
{"{\"key1\": \"123\", \"key2\": \"456\"}", "key1", "123"},
|
||||
{"{\"key1\": 123, \"key2\": 456}", "key1", "123"},
|
||||
const char *test_strings[][6] = {
|
||||
{"{\"city\": \"Anytown\", \"state\": \"USA\"}", "", "city", "Anytown"},
|
||||
{"{\"city\": \"Anytown\", \"state\": \"USA\"}", "", "state", "USA"},
|
||||
{"{\"city\": \"Anytown\", \"state\": \"USA\"}", "", "blah", ""},
|
||||
{"{\"key1\": \"123\", \"key2\": \"456\"}", "", "key1", "123"},
|
||||
{"{\"key1\": 123, \"key2\": 456}", "", "key1", "123"},
|
||||
{"{ \"path\": { \"to\": { \"elem\": \"someVar\" } } }", "/", "path/to/elem", "someVar"},
|
||||
{"{ \"path\": { \"to\": { \"elem\": \"someVar\" } } }", "", "path.to.elem2", ""},
|
||||
{"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }", "/", "path/to/arr/2", ""}, /* nonexistent index */
|
||||
{"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }", "/", "path/to/arr/-1", ""}, /* bogus index */
|
||||
{"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }", "/", "path/to/arr/test", ""}, /* bogus index */
|
||||
{"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }", "", "path.to.arr.test.test2.subkey", ""}, /* bogus index */
|
||||
{"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }", ",c", "path.to.arr", "2"}, /* test count */
|
||||
{"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }", "", "path.to.arr", "[\"item0\",\"item1\"]"},
|
||||
{"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }", ".", "path.to.arr.1", "item1"},
|
||||
{"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }", "/", "path/to/arr", "[\"item0\",\"item1\"]"},
|
||||
{"{ \"path\": { \"to\": { \"arr\": [ \"item0\", \"item1\" ] } } }", "/", "path/to/arr/1", "item1"},
|
||||
{"{ \"path\": { \"to\": { \"arr\": [ {\"name\": \"John Smith\", \"phone\": \"123\"}, {\"name\": \"Jane Doe\", \"phone\": \"234\"} ] } } }", ",c", "path.to.arr.0.name", "John Smith"},
|
||||
{"{ \"path\": { \"to\": { \"arr\": [ {\"name\": 1, \"phone\": 123}, {\"name\": 2, \"phone\": 234} ] } } }", ",c", "path.to.arr.0.name", "1"},
|
||||
{"{ \"path\": { \"to\": { \"arr\": [ {\"name\": [ \"item11\", \"item12\" ], \"phone\": [ \"item13\", \"item14\" ]}, {\"name\": [ \"item15\", \"item16\" ], \"phone\": [ \"item17\", \"item18\" ]} ] } } }", ",c", "path.to.arr.0.name.1", "item12"},
|
||||
};
|
||||
|
||||
switch (cmd) {
|
||||
|
@ -182,7 +330,7 @@ AST_TEST_DEFINE(test_JSON_DECODE)
|
|||
}
|
||||
|
||||
for (i = 0; i < ARRAY_LEN(test_strings); i++) {
|
||||
char tmp[512], tmp2[512] = "";
|
||||
char tmp[512];
|
||||
|
||||
struct ast_var_t *var = ast_var_assign("test_string", test_strings[i][0]);
|
||||
if (!var) {
|
||||
|
@ -194,11 +342,11 @@ AST_TEST_DEFINE(test_JSON_DECODE)
|
|||
|
||||
AST_LIST_INSERT_HEAD(ast_channel_varshead(chan), var, entries);
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "${JSON_DECODE(%s,%s)}", "test_string", test_strings[i][1]);
|
||||
snprintf(tmp, sizeof(tmp), "${JSON_DECODE(%s,%s,%s)}", "test_string", test_strings[i][2], test_strings[i][1]);
|
||||
|
||||
ast_str_substitute_variables(&str, 0, chan, tmp);
|
||||
if (strcmp(test_strings[i][2], ast_str_buffer(str))) {
|
||||
ast_test_status_update(test, "Format string '%s' substituted to '%s'. Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][2]);
|
||||
if (strcmp(test_strings[i][3], ast_str_buffer(str))) {
|
||||
ast_test_status_update(test, "Format string '%s' substituted to '%s' (key: %s). Expected '%s'.\n", test_strings[i][0], ast_str_buffer(str), test_strings[i][2], test_strings[i][3]);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
}
|
||||
|
@ -214,7 +362,9 @@ static int unload_module(void)
|
|||
{
|
||||
int res;
|
||||
|
||||
#ifdef TEST_FRAMEWORK
|
||||
AST_TEST_UNREGISTER(test_JSON_DECODE);
|
||||
#endif
|
||||
res = ast_custom_function_unregister(&json_decode_function);
|
||||
|
||||
return res;
|
||||
|
@ -224,7 +374,9 @@ static int load_module(void)
|
|||
{
|
||||
int res;
|
||||
|
||||
#ifdef TEST_FRAMEWORK
|
||||
AST_TEST_REGISTER(test_JSON_DECODE);
|
||||
#endif
|
||||
res = ast_custom_function_register(&json_decode_function);
|
||||
|
||||
return res;
|
||||
|
|
|
@ -187,8 +187,7 @@ static int acf_if(struct ast_channel *chan, const char *cmd, char *data, char *b
|
|||
AST_NONSTANDARD_APP_ARGS(args2, args1.remainder, ':');
|
||||
|
||||
if (ast_strlen_zero(args1.expr) || !(args2.iftrue || args2.iffalse)) {
|
||||
ast_log(LOG_WARNING, "Syntax IF(<expr>?[<true>][:<false>]) (expr must be non-null, and either <true> or <false> must be non-null)\n");
|
||||
ast_log(LOG_WARNING, " In this case, <expr>='%s', <true>='%s', and <false>='%s'\n", args1.expr, args2.iftrue, args2.iffalse);
|
||||
ast_debug(1, "<expr>='%s', <true>='%s', and <false>='%s'\n", args1.expr, args2.iftrue, args2.iffalse);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -850,8 +850,8 @@ static int load_module(void)
|
|||
for (; db_entry; db_entry = db_entry->next) {
|
||||
const char *dev_name = strrchr(db_entry->key, '/') + 1;
|
||||
enum ast_presence_state state;
|
||||
char *message;
|
||||
char *subtype;
|
||||
char *message = NULL;
|
||||
char *subtype = NULL;
|
||||
if (dev_name <= (const char *) 1) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -211,6 +211,15 @@ AST_TEST_DEFINE(test_SAYFILES_function)
|
|||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
/* + should be ignored and there should not be a leading & */
|
||||
ast_str_set(&expr, 0, "${SAYFILES(+18005551212,digits)}");
|
||||
ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
|
||||
if (strcmp(ast_str_buffer(result), "digits/1&digits/8&digits/0&digits/0&digits/5&digits/5&digits/5&digits/1&digits/2&digits/1&digits/2") != 0) {
|
||||
ast_test_status_update(test, "SAYFILES(+18005551212,digits) test failed ('%s')\n",
|
||||
ast_str_buffer(result));
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
ast_str_set(&expr, 0, "${SAYFILES(35,number)}");
|
||||
ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
|
||||
if (strcmp(ast_str_buffer(result), "digits/30&digits/5") != 0) {
|
||||
|
|
|
@ -125,6 +125,7 @@ static int scramble_callback(struct ast_audiohook *audiohook, struct ast_channel
|
|||
}
|
||||
|
||||
if (frame->frametype == AST_FRAME_VOICE) { /* only invert voice frequencies */
|
||||
ni = datastore->data;
|
||||
/* Based on direction of frame, and confirm it is applicable */
|
||||
if (!(direction == AST_AUDIOHOOK_DIRECTION_READ ? ni->rx : ni->tx)) {
|
||||
return 0;
|
||||
|
|
|
@ -65,6 +65,16 @@
|
|||
as <literal>getnum</literal>, then it will return the total number of results
|
||||
that are available.</para>
|
||||
</parameter>
|
||||
<parameter name="field" required="false">
|
||||
<para>The field of the result to retrieve.</para>
|
||||
<para>The fields that can be retrieved are:</para>
|
||||
<enumlist>
|
||||
<enum name="host"/>
|
||||
<enum name="port"/>
|
||||
<enum name="priority"/>
|
||||
<enum name="weight"/>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This function will retrieve results from a previous use
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright (C) 2005-2006, Digium, Inc.
|
||||
* Portions Copyright (C) 2005, Tilghman Lesher. All rights reserved.
|
||||
* Portions Copyright (C) 2005, Anthony Minessale II
|
||||
* Portions Copyright (C) 2021, Naveen Albert
|
||||
* Portions Copyright (C) 2021, 2022, Naveen Albert
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
|
@ -183,6 +183,51 @@ AST_THREADSTORAGE(tmp_buf);
|
|||
</example>
|
||||
</description>
|
||||
</function>
|
||||
<function name="TRIM" language="en_US">
|
||||
<synopsis>
|
||||
Trim leading and trailing whitespace in a string
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="string" required="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Replaces all leading and trailing whitespace in the provided string.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">LTRIM</ref>
|
||||
<ref type="function">RTRIM</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
<function name="LTRIM" language="en_US">
|
||||
<synopsis>
|
||||
Trim leading whitespace in a string
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="string" required="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Replaces all leading whitespace in the provided string.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">TRIM</ref>
|
||||
<ref type="function">RTRIM</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
<function name="RTRIM" language="en_US">
|
||||
<synopsis>
|
||||
Trim trailing whitespace in a string
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="string" required="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Replaces all trailing whitespace in the provided string.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">TRIM</ref>
|
||||
<ref type="function">LTRIM</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
<function name="PASSTHRU" language="en_US">
|
||||
<synopsis>
|
||||
Pass the given argument back as a value.
|
||||
|
@ -1045,6 +1090,84 @@ static struct ast_custom_function strbetween_function = {
|
|||
.read2 = strbetween,
|
||||
};
|
||||
|
||||
#define ltrim(s) while (isspace(*s)) s++;
|
||||
#define rtrim(s) { \
|
||||
if (s) { \
|
||||
char *back = s + strlen(s); \
|
||||
while (back != s && isspace(*--back)); \
|
||||
if (*s) { \
|
||||
*(back + 1) = '\0'; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
static int function_trim(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
{
|
||||
char *c;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
c = ast_strdupa(data);
|
||||
ltrim(c);
|
||||
rtrim(c);
|
||||
|
||||
ast_copy_string(buf, c, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int function_ltrim(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
{
|
||||
char *c;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
c = data;
|
||||
ltrim(c);
|
||||
|
||||
ast_copy_string(buf, c, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int function_rtrim(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
{
|
||||
char *c;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
c = ast_strdupa(data);
|
||||
rtrim(c);
|
||||
|
||||
ast_copy_string(buf, c, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef ltrim
|
||||
#undef rtrim
|
||||
|
||||
static struct ast_custom_function trim_function = {
|
||||
.name = "TRIM",
|
||||
.read = function_trim,
|
||||
};
|
||||
|
||||
static struct ast_custom_function ltrim_function = {
|
||||
.name = "LTRIM",
|
||||
.read = function_ltrim,
|
||||
};
|
||||
|
||||
static struct ast_custom_function rtrim_function = {
|
||||
.name = "RTRIM",
|
||||
.read = function_rtrim,
|
||||
};
|
||||
|
||||
static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
|
||||
size_t len)
|
||||
{
|
||||
|
@ -2126,6 +2249,59 @@ AST_TEST_DEFINE(test_STRBETWEEN)
|
|||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(test_TRIM)
|
||||
{
|
||||
int i, res = AST_TEST_PASS;
|
||||
struct ast_channel *chan; /* dummy channel */
|
||||
struct ast_str *str; /* fancy string for holding comparing value */
|
||||
|
||||
const char *test_strings[][5] = {
|
||||
{"TRIM", " abcd ", "abcd"},
|
||||
{"LTRIM", " abcd ", "abcd "},
|
||||
{"RTRIM", " abcd ", " abcd"},
|
||||
{"TRIM", "abcd", "abcd"},
|
||||
{"TRIM", " a b c d ", "a b c d"},
|
||||
};
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "func_TRIM";
|
||||
info->category = "/funcs/func_strings/";
|
||||
info->summary = "Test TRIM functions";
|
||||
info->description = "Verify TRIM behavior";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(chan = ast_dummy_channel_alloc())) {
|
||||
ast_test_status_update(test, "Unable to allocate dummy channel\n");
|
||||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
if (!(str = ast_str_create(64))) {
|
||||
ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
|
||||
ast_channel_release(chan);
|
||||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_LEN(test_strings); i++) {
|
||||
char tmp[512], tmp2[512] = "";
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "${%s(%s)}", test_strings[i][0], test_strings[i][1]);
|
||||
ast_str_substitute_variables(&str, 0, chan, tmp);
|
||||
if (strcmp(test_strings[i][2], ast_str_buffer(str))) {
|
||||
ast_test_status_update(test, "Format string '%s' substituted to '%s'. Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][2]);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
ast_free(str);
|
||||
ast_channel_release(chan);
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int unload_module(void)
|
||||
|
@ -2137,6 +2313,7 @@ static int unload_module(void)
|
|||
AST_TEST_UNREGISTER(test_FILTER);
|
||||
AST_TEST_UNREGISTER(test_STRREPLACE);
|
||||
AST_TEST_UNREGISTER(test_STRBETWEEN);
|
||||
AST_TEST_UNREGISTER(test_TRIM);
|
||||
res |= ast_custom_function_unregister(&fieldqty_function);
|
||||
res |= ast_custom_function_unregister(&fieldnum_function);
|
||||
res |= ast_custom_function_unregister(&filter_function);
|
||||
|
@ -2163,6 +2340,9 @@ static int unload_module(void)
|
|||
res |= ast_custom_function_unregister(&push_function);
|
||||
res |= ast_custom_function_unregister(&unshift_function);
|
||||
res |= ast_custom_function_unregister(&passthru_function);
|
||||
res |= ast_custom_function_unregister(&trim_function);
|
||||
res |= ast_custom_function_unregister(<rim_function);
|
||||
res |= ast_custom_function_unregister(&rtrim_function);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -2176,6 +2356,7 @@ static int load_module(void)
|
|||
AST_TEST_REGISTER(test_FILTER);
|
||||
AST_TEST_REGISTER(test_STRREPLACE);
|
||||
AST_TEST_REGISTER(test_STRBETWEEN);
|
||||
AST_TEST_REGISTER(test_TRIM);
|
||||
res |= ast_custom_function_register(&fieldqty_function);
|
||||
res |= ast_custom_function_register(&fieldnum_function);
|
||||
res |= ast_custom_function_register(&filter_function);
|
||||
|
@ -2202,6 +2383,9 @@ static int load_module(void)
|
|||
res |= ast_custom_function_register(&push_function);
|
||||
res |= ast_custom_function_register(&unshift_function);
|
||||
res |= ast_custom_function_register(&passthru_function);
|
||||
res |= ast_custom_function_register(&trim_function);
|
||||
res |= ast_custom_function_register(<rim_function);
|
||||
res |= ast_custom_function_register(&rtrim_function);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ int ast_term_init(void); /*!< Provided by term.c */
|
|||
int astdb_init(void); /*!< Provided by db.c */
|
||||
int ast_channels_init(void); /*!< Provided by channel.c */
|
||||
void ast_builtins_init(void); /*!< Provided by cli.c */
|
||||
void ast_cli_channels_init(void); /*!< Provided by cli.c */
|
||||
int ast_cli_perms_init(int reload); /*!< Provided by cli.c */
|
||||
void dnsmgr_start_refresh(void); /*!< Provided by dnsmgr.c */
|
||||
int ast_dns_system_resolver_init(void); /*!< Provided by dns_system_resolver.c */
|
||||
|
|
|
@ -118,6 +118,7 @@ struct ast_audiohook {
|
|||
ast_audiohook_manipulate_callback manipulate_callback; /*!< Manipulation callback */
|
||||
struct ast_audiohook_options options; /*!< Applicable options */
|
||||
unsigned int hook_internal_samp_rate; /*!< internal read/write sample rate on the audiohook.*/
|
||||
enum ast_audiohook_direction direction; /*!< Intended audiohook direction, BOTH by default on init */
|
||||
AST_LIST_ENTRY(ast_audiohook) list; /*!< Linked list information */
|
||||
};
|
||||
|
||||
|
@ -140,6 +141,14 @@ int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type
|
|||
*/
|
||||
int ast_audiohook_destroy(struct ast_audiohook *audiohook);
|
||||
|
||||
/*! \brief Sets direction on audiohook
|
||||
* \param audiohook
|
||||
* \param direction In which direction should the audiohook feed frames, ie if we are snooping 'in', set direction to READ so that only the 'in' frames are fed to the slin factory
|
||||
* \retval 0 on success
|
||||
* \retval -1 on failure due to audiohook already in use or in shutdown. Can only set direction on NEW audiohooks
|
||||
*/
|
||||
int ast_audiohook_set_frame_feed_direction(struct ast_audiohook *audiohook, enum ast_audiohook_direction direction);
|
||||
|
||||
/*! \brief Writes a frame into the audiohook structure
|
||||
* \param audiohook
|
||||
* \param direction Direction the audio frame came from
|
||||
|
|
|
@ -632,6 +632,9 @@
|
|||
/* Define to 1 if PJPROJECT has the PJSIP EVSUB Group Lock support feature. */
|
||||
#undef HAVE_PJSIP_EVSUB_GRP_LOCK
|
||||
|
||||
/* Define to 1 if evsub requires a NOTIFY on SUBSCRIBE. */
|
||||
#undef HAVE_PJSIP_EVSUB_PENDING_NOTIFY
|
||||
|
||||
/* Define to 1 if PJPROJECT has the PJSIP External Resolver Support feature.
|
||||
*/
|
||||
#undef HAVE_PJSIP_EXTERNAL_RESOLVER
|
||||
|
@ -656,6 +659,10 @@
|
|||
/* Define if your system has the PJSIP_TLS_TRANSPORT_PROTO headers. */
|
||||
#undef HAVE_PJSIP_TLS_TRANSPORT_PROTO
|
||||
|
||||
/* Define to 1 if PJPROJECT has the PJSIP TLS Transport Restart Support
|
||||
feature. */
|
||||
#undef HAVE_PJSIP_TLS_TRANSPORT_RESTART
|
||||
|
||||
/* Define if your system has the PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE
|
||||
headers. */
|
||||
#undef HAVE_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE
|
||||
|
|
|
@ -225,6 +225,8 @@ enum ast_cdr_settings {
|
|||
CDR_INITIATED_SECONDS = 1 << 5, /*!< Include microseconds into the billing time */
|
||||
CDR_DEBUG = 1 << 6, /*!< Enables extra debug statements */
|
||||
CDR_CHANNEL_DEFAULT_ENABLED = 1 << 7, /*!< Whether CDR is enabled for each channel by default */
|
||||
CDR_IGNORE_STATE_CHANGES = 1 << 8, /*!< Whether to ignore bridge and other call state change events */
|
||||
CDR_IGNORE_DIAL_CHANGES = 1 << 9, /*!< Whether to ignore dial state changes */
|
||||
};
|
||||
|
||||
/*! \brief CDR Batch Mode settings */
|
||||
|
|
|
@ -2255,8 +2255,8 @@ int ast_senddigit_mf_end(struct ast_channel *chan);
|
|||
* \param chan channel to act upon
|
||||
* \param digit the MF digit to send, encoded in ASCII
|
||||
* \param duration the duration of a numeric digit ending in ms
|
||||
* \param duration the duration of a KP digit ending in ms
|
||||
* \param duration the duration of a ST, STP, ST2P, or ST3P digit ending in ms
|
||||
* \param durationkp the duration of a KP digit ending in ms
|
||||
* \param durationst the duration of a ST, STP, ST2P, or ST3P digit ending in ms
|
||||
* \param is_external 1 if called by a thread that is not the channel's media
|
||||
* handler thread, 0 if called by the channel's media handler
|
||||
* thread.
|
||||
|
@ -4112,8 +4112,6 @@ struct ast_channel_monitor {
|
|||
};
|
||||
|
||||
/* ACCESSOR FUNCTIONS */
|
||||
/*! \brief Set the channel name */
|
||||
void ast_channel_name_set(struct ast_channel *chan, const char *name);
|
||||
|
||||
#define DECLARE_STRINGFIELD_SETTERS_FOR(field) \
|
||||
void ast_channel_##field##_set(struct ast_channel *chan, const char *field); \
|
||||
|
|
|
@ -30,20 +30,22 @@ extern "C" {
|
|||
#include "asterisk/optional_api.h"
|
||||
#include "asterisk/logger.h"
|
||||
|
||||
#ifdef HAVE_CRYPTO
|
||||
#include "openssl/aes.h"
|
||||
typedef AES_KEY ast_aes_encrypt_key;
|
||||
typedef AES_KEY ast_aes_decrypt_key;
|
||||
#else /* !HAVE_CRYPTO */
|
||||
typedef char ast_aes_encrypt_key;
|
||||
typedef char ast_aes_decrypt_key;
|
||||
#endif /* HAVE_CRYPTO */
|
||||
/* We previously used the key length explicitly; replace with constant.
|
||||
* For now, Asterisk is limited to 1024 bit (128 byte) RSA keys.
|
||||
*/
|
||||
#define AST_CRYPTO_RSA_KEY_BITS 1024
|
||||
#define AST_CRYPTO_AES_BLOCKSIZE 128
|
||||
|
||||
struct aes_key {
|
||||
unsigned char raw[AST_CRYPTO_AES_BLOCKSIZE / 8];
|
||||
};
|
||||
|
||||
typedef struct aes_key ast_aes_encrypt_key;
|
||||
typedef struct aes_key ast_aes_decrypt_key;
|
||||
|
||||
#define AST_KEY_PUBLIC (1 << 0)
|
||||
#define AST_KEY_PRIVATE (1 << 1)
|
||||
|
||||
struct ast_key;
|
||||
|
||||
/*!
|
||||
* \brief Retrieve a key
|
||||
* \param kname Name of the key we are retrieving
|
||||
|
@ -108,10 +110,10 @@ AST_OPTIONAL_API(int, ast_sign_bin, (struct ast_key *key, const char *msg, int m
|
|||
|
||||
/*!
|
||||
* \brief Encrypt a message using a given private key
|
||||
* \param key a private key to use to encrypt
|
||||
* \param dst a pointer to a buffer of at least srclen * 1.5 bytes in which the encrypted
|
||||
* \param src the message to encrypt
|
||||
* \param srclen the length of the message to encrypt
|
||||
* \param dst a pointer to a buffer of at least srclen * 1.5 bytes in which the encrypted
|
||||
* \param key a private key to use to encrypt
|
||||
* answer will be stored
|
||||
*
|
||||
* \retval length of encrypted data on success.
|
||||
|
@ -122,10 +124,10 @@ AST_OPTIONAL_API(int, ast_encrypt_bin, (unsigned char *dst, const unsigned char
|
|||
|
||||
/*!
|
||||
* \brief Decrypt a message using a given private key
|
||||
* \param key a private key to use to decrypt
|
||||
* \param dst a pointer to a buffer of at least srclen bytes in which the decrypted
|
||||
* \param src the message to decrypt
|
||||
* \param srclen the length of the message to decrypt
|
||||
* \param dst a pointer to a buffer of at least srclen bytes in which the decrypted
|
||||
* \param key a private key to use to decrypt
|
||||
* answer will be stored
|
||||
*
|
||||
* \retval length of decrypted data on success.
|
||||
|
@ -162,24 +164,30 @@ AST_OPTIONAL_API(int, ast_aes_set_decrypt_key,
|
|||
* \brief AES encrypt data
|
||||
* \param in data to be encrypted
|
||||
* \param out pointer to a buffer to hold the encrypted output
|
||||
* \param ctx address of an aes encryption context filled in with ast_aes_set_encrypt_key
|
||||
* \param key pointer to the ast_aes_encrypt_key to use for encryption
|
||||
* \retval <= 0 failure
|
||||
* \retval otherwise number of bytes in output buffer
|
||||
*/
|
||||
AST_OPTIONAL_API(void, ast_aes_encrypt,
|
||||
(const unsigned char *in, unsigned char *out, const ast_aes_encrypt_key *ctx),
|
||||
{ ast_log(LOG_WARNING, "AES encryption disabled. Install OpenSSL.\n");return; });
|
||||
AST_OPTIONAL_API(int, ast_aes_encrypt,
|
||||
(const unsigned char *in, unsigned char *out, const ast_aes_encrypt_key *key),
|
||||
{ ast_log(LOG_WARNING, "AES encryption disabled. Install OpenSSL.\n");return -1; });
|
||||
|
||||
/*!
|
||||
* \brief AES decrypt data
|
||||
* \param in encrypted data
|
||||
* \param out pointer to a buffer to hold the decrypted output
|
||||
* \param ctx address of an aes encryption context filled in with ast_aes_set_decrypt_key
|
||||
* \param key pointer to the ast_aes_decrypt_key to use for decryption
|
||||
* \retval <= 0 failure
|
||||
* \retval otherwise number of bytes in output buffer
|
||||
*/
|
||||
AST_OPTIONAL_API(void, ast_aes_decrypt,
|
||||
(const unsigned char *in, unsigned char *out, const ast_aes_decrypt_key *ctx),
|
||||
{ ast_log(LOG_WARNING, "AES encryption disabled. Install OpenSSL.\n");return; });
|
||||
AST_OPTIONAL_API(int, ast_aes_decrypt,
|
||||
(const unsigned char *in, unsigned char *out, const ast_aes_decrypt_key *key),
|
||||
{ ast_log(LOG_WARNING, "AES encryption disabled. Install OpenSSL.\n");return -1; });
|
||||
|
||||
AST_OPTIONAL_API(int, ast_crypto_loaded, (void), { return 0; });
|
||||
|
||||
AST_OPTIONAL_API(int, ast_crypto_reload, (void), { return 0; });
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -72,6 +72,8 @@ struct ast_features_xfer_config {
|
|||
AST_STRING_FIELD(transferretrysound);
|
||||
/*! Sound played when an invalid extension is dialed, and the transferer is being returned to the call. */
|
||||
AST_STRING_FIELD(transferinvalidsound);
|
||||
/*! Sound to play to announce the transfer process has started. */
|
||||
AST_STRING_FIELD_EXTENDED(transferannouncesound);
|
||||
);
|
||||
/*! Seconds allowed between digit presses when dialing transfer destination */
|
||||
unsigned int transferdigittimeout;
|
||||
|
|
|
@ -80,6 +80,7 @@ int ast_streamfile(struct ast_channel *c, const char *filename, const char *pref
|
|||
* \brief stream file until digit
|
||||
* If the file name is non-empty, try to play it.
|
||||
* \note If digits == "" then we can simply check for non-zero.
|
||||
* \note If a failure is encountered, the stream will be closed before returning.
|
||||
* \retval 0 if success.
|
||||
* \retval -1 if error.
|
||||
* \retval digit if interrupted by a digit.
|
||||
|
|
|
@ -592,6 +592,15 @@ struct ast_json *ast_json_object_get(struct ast_json *object, const char *key);
|
|||
*/
|
||||
#define ast_json_object_integer_get(object, key) ast_json_integer_get(ast_json_object_get(object, key))
|
||||
|
||||
/*!
|
||||
* \brief Get a double field from a JSON object.
|
||||
* \param object JSON object.
|
||||
* \param key Key of double field to look up.
|
||||
* \return Value of a JSON double.
|
||||
* \retval 0 if \a real is not a JSON real number.
|
||||
*/
|
||||
#define ast_json_object_real_get(object, key) ast_json_real_get(ast_json_object_get(object, key))
|
||||
|
||||
/*!
|
||||
* \brief Set a field in a JSON object.
|
||||
* \since 12.0.0
|
||||
|
|
|
@ -350,6 +350,18 @@ void astman_send_list_complete_start(struct mansession *s, const struct message
|
|||
*/
|
||||
void astman_send_list_complete_end(struct mansession *s);
|
||||
|
||||
/*!
|
||||
* \brief Enable/disable the inclusion of 'dangerous' configurations outside
|
||||
* of the ast_config_AST_CONFIG_DIR
|
||||
*
|
||||
* This function can globally enable/disable the loading of configuration files
|
||||
* outside of ast_config_AST_CONFIG_DIR.
|
||||
*
|
||||
* \param new_live_dangerously If true, enable the access of files outside
|
||||
* ast_config_AST_CONFIG_DIR from astman.
|
||||
*/
|
||||
void astman_live_dangerously(int new_live_dangerously);
|
||||
|
||||
void __attribute__((format(printf, 2, 3))) astman_append(struct mansession *s, const char *fmt, ...);
|
||||
|
||||
/*! \brief Determine if a manager session ident is authenticated */
|
||||
|
|
|
@ -271,9 +271,9 @@ int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data);
|
|||
/*!
|
||||
* \brief Execute an application
|
||||
*
|
||||
* \param c channel to execute on
|
||||
* \param app name of app to execute
|
||||
* \param data the data passed into the app
|
||||
* \param chan channel to execute on
|
||||
* \param app_name name of app to execute
|
||||
* \param app_args the data passed into the app
|
||||
*
|
||||
* This application executes an application by name on a given channel.
|
||||
* It is a wrapper around pbx_exec that will perform variable substitution
|
||||
|
@ -1432,7 +1432,7 @@ void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead
|
|||
/*!
|
||||
* \brief Substitutes variables, similar to pbx_substitute_variables_helper_full, but allows passing the context, extension, and priority in.
|
||||
*/
|
||||
void pbx_substitute_variables_helper_full_location(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int cp2_size, size_t *used, char *context, char *exten, int pri);
|
||||
void pbx_substitute_variables_helper_full_location(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int cp2_size, size_t *used, const char *context, const char *exten, int pri);
|
||||
/*! @} */
|
||||
|
||||
/*! @name Substitution routines, using dynamic string buffers
|
||||
|
|
|
@ -55,7 +55,7 @@ struct ao2_container *ast_aeap_client_configs_get(const char *protocol);
|
|||
/*!
|
||||
* \brief Retrieve codec capabilities from the configuration
|
||||
*
|
||||
* \param config A configuration object
|
||||
* \param cfg A configuration object
|
||||
*
|
||||
* \returns The configuration's codec capabilities
|
||||
*/
|
||||
|
@ -64,7 +64,7 @@ const struct ast_format_cap *ast_aeap_client_config_codecs(const struct ast_aeap
|
|||
/*!
|
||||
* \brief Check a given protocol against that in an Asterisk external application configuration
|
||||
*
|
||||
* \param config A configuration object
|
||||
* \param cfg A configuration object
|
||||
* \param protocol The protocol to check
|
||||
*
|
||||
* \returns True if the configuration's protocol matches, false otherwise
|
||||
|
@ -294,7 +294,8 @@ void ast_aeap_user_data_unregister(struct ast_aeap *aeap, const char *id);
|
|||
* handler that [potentially] frees it the caller of this function must ensure
|
||||
* it's done using the returned object before it's unregistered.
|
||||
*
|
||||
* \param data A user data object
|
||||
* \param aeap An Asterisk external application object
|
||||
* \param id The look up id for the object
|
||||
*
|
||||
* \returns A user data object
|
||||
*/
|
||||
|
@ -359,7 +360,6 @@ struct ast_aeap_tsx_params {
|
|||
* is specified in "params".
|
||||
*
|
||||
* \param aeap An Asterisk external application object
|
||||
* \param msg The message to send
|
||||
* \param params (optional) Additional parameters to consider when sending. Heap allocation
|
||||
* not required.
|
||||
*
|
||||
|
|
|
@ -241,7 +241,6 @@ struct ast_aeap_message *ast_aeap_message_create_response(const struct ast_aeap_
|
|||
* \param name The name of the message
|
||||
* \param id Optional id
|
||||
* \param error_msg Error message to set
|
||||
* \param params Other optional parameter(s) to possibly use
|
||||
*
|
||||
* \returns An ao2 reference counted AEAP response object, or NULL on error
|
||||
*/
|
||||
|
@ -315,7 +314,7 @@ const char *ast_aeap_message_name(const struct ast_aeap_message *message);
|
|||
* \note Case insensitive
|
||||
*
|
||||
* \param message A message object
|
||||
* \param message name The name to check against
|
||||
* \param name The name to check against
|
||||
*
|
||||
* \returns True if matched, false otherwise
|
||||
*/
|
||||
|
|
|
@ -29,8 +29,8 @@
|
|||
|
||||
enum ast_geoloc_pidf_element {
|
||||
AST_PIDF_ELEMENT_NONE = 0,
|
||||
AST_PIDF_ELEMENT_TUPLE,
|
||||
AST_PIDF_ELEMENT_DEVICE,
|
||||
AST_PIDF_ELEMENT_TUPLE,
|
||||
AST_PIDF_ELEMENT_PERSON,
|
||||
AST_PIDF_ELEMENT_LAST,
|
||||
};
|
||||
|
@ -43,13 +43,22 @@ enum ast_geoloc_format {
|
|||
AST_GEOLOC_FORMAT_LAST,
|
||||
};
|
||||
|
||||
enum ast_geoloc_action {
|
||||
AST_GEOLOC_ACT_PREFER_INCOMING = 0,
|
||||
AST_GEOLOC_ACT_PREFER_CONFIG,
|
||||
AST_GEOLOC_ACT_DISCARD_INCOMING,
|
||||
AST_GEOLOC_ACT_DISCARD_CONFIG,
|
||||
enum ast_geoloc_precedence {
|
||||
AST_GEOLOC_PRECED_PREFER_INCOMING = 0,
|
||||
AST_GEOLOC_PRECED_PREFER_CONFIG,
|
||||
AST_GEOLOC_PRECED_DISCARD_INCOMING,
|
||||
AST_GEOLOC_PRECED_DISCARD_CONFIG,
|
||||
};
|
||||
|
||||
#define CONFIG_STR_TO_ENUM_DECL(_stem) int ast_geoloc_ ## _stem ## _str_to_enum(const char *str);
|
||||
CONFIG_STR_TO_ENUM_DECL(pidf_element)
|
||||
CONFIG_STR_TO_ENUM_DECL(format);
|
||||
CONFIG_STR_TO_ENUM_DECL(precedence);
|
||||
#define GEOLOC_ENUM_TO_NAME_DECL(_stem) const char * ast_geoloc_ ## _stem ## _to_name(int ix);
|
||||
GEOLOC_ENUM_TO_NAME_DECL(pidf_element)
|
||||
GEOLOC_ENUM_TO_NAME_DECL(format);
|
||||
GEOLOC_ENUM_TO_NAME_DECL(precedence);
|
||||
|
||||
struct ast_geoloc_location {
|
||||
SORCERY_OBJECT(details);
|
||||
AST_DECLARE_STRING_FIELDS(
|
||||
|
@ -58,6 +67,7 @@ struct ast_geoloc_location {
|
|||
);
|
||||
enum ast_geoloc_format format;
|
||||
struct ast_variable *location_info;
|
||||
struct ast_variable *confidence;
|
||||
};
|
||||
|
||||
struct ast_geoloc_profile {
|
||||
|
@ -65,13 +75,19 @@ struct ast_geoloc_profile {
|
|||
AST_DECLARE_STRING_FIELDS(
|
||||
AST_STRING_FIELD(location_reference);
|
||||
AST_STRING_FIELD(notes);
|
||||
AST_STRING_FIELD(method);
|
||||
AST_STRING_FIELD(location_source);
|
||||
);
|
||||
enum ast_geoloc_pidf_element pidf_element;
|
||||
enum ast_geoloc_action action;
|
||||
int geolocation_routing;
|
||||
enum ast_geoloc_precedence precedence;
|
||||
int allow_routing_use;
|
||||
struct ast_variable *location_refinement;
|
||||
struct ast_variable *location_variables;
|
||||
struct ast_variable *usage_rules;
|
||||
int suppress_empty_ca_elements;
|
||||
enum ast_geoloc_format format;
|
||||
struct ast_variable *location_info;
|
||||
struct ast_variable *confidence;
|
||||
};
|
||||
|
||||
struct ast_geoloc_eprofile {
|
||||
|
@ -83,14 +99,16 @@ struct ast_geoloc_eprofile {
|
|||
AST_STRING_FIELD(notes);
|
||||
);
|
||||
enum ast_geoloc_pidf_element pidf_element;
|
||||
enum ast_geoloc_action action;
|
||||
int geolocation_routing;
|
||||
enum ast_geoloc_precedence precedence;
|
||||
int allow_routing_use;
|
||||
enum ast_geoloc_format format;
|
||||
struct ast_variable *location_info;
|
||||
struct ast_variable *location_refinement;
|
||||
struct ast_variable *location_variables;
|
||||
struct ast_variable *effective_location;
|
||||
struct ast_variable *usage_rules;
|
||||
struct ast_variable *confidence;
|
||||
int suppress_empty_ca_elements;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -147,7 +165,7 @@ const char *ast_geoloc_validate_result_to_str(enum ast_geoloc_validate_result re
|
|||
* \brief Validate that the names of the variables in the list are valid codes or synonyms
|
||||
*
|
||||
* \param varlist Variable list to check.
|
||||
* \param result[OUT] Pointer to char * to receive failing item.
|
||||
* \param[out] result Pointer to char * to receive failing item.
|
||||
*
|
||||
* \return result code.
|
||||
*/
|
||||
|
@ -158,7 +176,7 @@ enum ast_geoloc_validate_result ast_geoloc_civicaddr_validate_varlist(
|
|||
* \brief Validate that the variables in the list represent a valid GML shape
|
||||
*
|
||||
* \param varlist Variable list to check.
|
||||
* \param result[OUT] Pointer to char * to receive failing item.
|
||||
* \param[out] result Pointer to char * to receive failing item.
|
||||
*
|
||||
* \return result code.
|
||||
*/
|
||||
|
@ -299,6 +317,15 @@ struct ast_datastore *ast_geoloc_datastore_find(struct ast_channel *chan);
|
|||
*/
|
||||
struct ast_geoloc_eprofile *ast_geoloc_eprofile_alloc(const char *name);
|
||||
|
||||
/*!
|
||||
* \brief Duplicate an effective profile.
|
||||
*
|
||||
* \param src The eprofile to duplicate.
|
||||
*
|
||||
* \return The duplicated effective profile ao2 object.
|
||||
*/
|
||||
struct ast_geoloc_eprofile *ast_geoloc_eprofile_dup(struct ast_geoloc_eprofile *src);
|
||||
|
||||
/*!
|
||||
* \brief Allocate a new effective profile from an existing profile.
|
||||
*
|
||||
|
@ -331,12 +358,45 @@ struct ast_geoloc_eprofile *ast_geoloc_eprofile_create_from_pidf(
|
|||
struct ast_geoloc_eprofile *ast_geoloc_eprofile_create_from_uri(const char *uri,
|
||||
const char *reference_string);
|
||||
|
||||
/*!
|
||||
* \brief Convert a URI eprofile to a URI string
|
||||
*
|
||||
* \param eprofile Effective profile to convert
|
||||
* \param chan Channel to use to resolve variables
|
||||
* \param buf Pointer to ast_str pointer to use for work
|
||||
* \param ref_string An identifying string to use in error messages.
|
||||
*
|
||||
* \return String representation of URI allocated from buf or NULL on failure
|
||||
*/
|
||||
const char *ast_geoloc_eprofile_to_uri(struct ast_geoloc_eprofile *eprofile,
|
||||
struct ast_channel *chan, struct ast_str **buf, const char *ref_string);
|
||||
|
||||
/*!
|
||||
* \brief Convert a datastore containing eprofiles to a PIDF-LO document
|
||||
*
|
||||
* \param ds Datastore containing effective profiles to convert
|
||||
* \param chan Channel to use to resolve variables
|
||||
* \param buf Pointer to ast_str pointer to use for work
|
||||
* \param ref_string An identifying string to use in error messages.
|
||||
*
|
||||
* \return String representation PIDF-LO allocated from buf or NULL on failure.
|
||||
*/
|
||||
const char *ast_geoloc_eprofiles_to_pidf(struct ast_datastore *ds,
|
||||
struct ast_channel *chan, struct ast_str **buf, const char * ref_string);
|
||||
|
||||
/*!
|
||||
* \brief Convert a single eprofile to a PIDF-LO document
|
||||
*
|
||||
* \param eprofile Effective profile to convert
|
||||
* \param chan Channel to use to resolve variables
|
||||
* \param buf Pointer to ast_str pointer to use for work
|
||||
* \param ref_string An identifying string to use in error messages.
|
||||
*
|
||||
* \return String representation PIDF-LO allocated from buf or NULL on failure.
|
||||
*/
|
||||
const char *ast_geoloc_eprofile_to_pidf(struct ast_geoloc_eprofile *eprofile,
|
||||
struct ast_channel *chan, struct ast_str **buf, const char * ref_string);
|
||||
|
||||
/*!
|
||||
* \brief Refresh the effective profile with any changed info.
|
||||
*
|
||||
|
|
|
@ -51,6 +51,11 @@
|
|||
#include "asterisk/stasis_endpoints.h"
|
||||
#include "asterisk/stream.h"
|
||||
|
||||
#ifdef HAVE_PJSIP_TLS_TRANSPORT_RESTART
|
||||
/* Needed for knowing if the cert or priv key files changed */
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#define PJSIP_MINVERSION(m,n,p) (((m << 24) | (n << 16) | (p << 8)) >= PJ_VERSION_NUM)
|
||||
|
||||
#ifndef PJSIP_EXPIRES_NOT_SPECIFIED
|
||||
|
@ -82,6 +87,26 @@
|
|||
#define AST_STIR_SHAKEN_RESPONSE_STR_UNSUPPORTED_CREDENTIAL "Unsupported Credential"
|
||||
#define AST_STIR_SHAKEN_RESPONSE_STR_INVALID_IDENTITY_HEADER "Invalid Identity Header"
|
||||
|
||||
/* ":12345" */
|
||||
#define COLON_PORT_STRLEN 6
|
||||
/*
|
||||
* "<ipaddr>:<port>"
|
||||
* PJ_INET6_ADDRSTRLEN includes the NULL terminator
|
||||
*/
|
||||
#define IP6ADDR_COLON_PORT_BUFLEN (PJ_INET6_ADDRSTRLEN + COLON_PORT_STRLEN)
|
||||
|
||||
/*!
|
||||
* \brief Fill a buffer with a pjsip transport's remote ip address and port
|
||||
*
|
||||
* \param _transport The pjsip_transport to use
|
||||
* \param _dest The destination buffer of at least IP6ADDR_COLON_PORT_BUFLEN bytes
|
||||
*/
|
||||
#define AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR(_transport, _dest) \
|
||||
snprintf(_dest, IP6ADDR_COLON_PORT_BUFLEN, \
|
||||
PJSTR_PRINTF_SPEC ":%d", \
|
||||
PJSTR_PRINTF_VAR(_transport->remote_name.host), \
|
||||
_transport->remote_name.port);
|
||||
|
||||
/* Forward declarations of PJSIP stuff */
|
||||
struct pjsip_rx_data;
|
||||
struct pjsip_module;
|
||||
|
@ -100,6 +125,8 @@ struct pjsip_tpselector;
|
|||
|
||||
AST_VECTOR(ast_sip_service_route_vector, char *);
|
||||
|
||||
static const pj_str_t AST_PJ_STR_EMPTY = { "", 0 };
|
||||
|
||||
/*!
|
||||
* \brief Structure for SIP transport information
|
||||
*/
|
||||
|
@ -184,6 +211,16 @@ struct ast_sip_transport_state {
|
|||
* If true, fail if server certificate cannot verify (TLS only)
|
||||
*/
|
||||
int verify_server;
|
||||
#ifdef HAVE_PJSIP_TLS_TRANSPORT_RESTART
|
||||
/*!
|
||||
* The stats information for the certificate file, if configured
|
||||
*/
|
||||
struct stat cert_file_stat;
|
||||
/*!
|
||||
* The stats information for the private key file, if configured
|
||||
*/
|
||||
struct stat privkey_file_stat;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define ast_sip_transport_is_nonlocal(transport_state, addr) \
|
||||
|
@ -302,6 +339,45 @@ struct ast_sip_nat_hook {
|
|||
void (*outgoing_external_message)(struct pjsip_tx_data *tdata, struct ast_sip_transport *transport);
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief The kind of security negotiation
|
||||
*/
|
||||
enum ast_sip_security_negotiation {
|
||||
/*! No security mechanism negotiation */
|
||||
AST_SIP_SECURITY_NEG_NONE = 0,
|
||||
/*! Use mediasec security mechanism negotiation */
|
||||
AST_SIP_SECURITY_NEG_MEDIASEC,
|
||||
/* Add RFC 3329 (sec-agree) mechanism negotiation in the future */
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief The security mechanism type
|
||||
*/
|
||||
enum ast_sip_security_mechanism_type {
|
||||
AST_SIP_SECURITY_MECH_NONE = 0,
|
||||
/* Use msrp-tls as security mechanism */
|
||||
AST_SIP_SECURITY_MECH_MSRP_TLS,
|
||||
/* Use sdes-srtp as security mechanism */
|
||||
AST_SIP_SECURITY_MECH_SDES_SRTP,
|
||||
/* Use dtls-srtp as security mechanism */
|
||||
AST_SIP_SECURITY_MECH_DTLS_SRTP,
|
||||
/* Add RFC 3329 (sec-agree) mechanisms like tle, digest, ipsec-ike in the future */
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Structure representing a security mechanism as defined in RFC 3329
|
||||
*/
|
||||
struct ast_sip_security_mechanism {
|
||||
/* Used to determine which security mechanism to use. */
|
||||
enum ast_sip_security_mechanism_type type;
|
||||
/* The preference of this security mechanism. The higher the value, the more preferred. */
|
||||
float qvalue;
|
||||
/* Optional mechanism parameters. */
|
||||
struct ast_vector_string mechanism_parameters;
|
||||
};
|
||||
|
||||
AST_VECTOR(ast_sip_security_mechanism_vector, struct ast_sip_security_mechanism *);
|
||||
|
||||
/*!
|
||||
* \brief Contact associated with an address of record
|
||||
*/
|
||||
|
@ -373,6 +449,13 @@ struct ast_sip_contact_status {
|
|||
);
|
||||
/*! The round trip time in microseconds */
|
||||
int64_t rtt;
|
||||
/*!
|
||||
* The security mechanism list of the contact (RFC 3329).
|
||||
* Stores the values of Security-Server headers in 401/421/494 responses to an
|
||||
* in-dialog request or successful outbound registration which will be used to
|
||||
* set the Security-Verify headers of all subsequent requests to the contact.
|
||||
*/
|
||||
struct ast_sip_security_mechanism_vector security_mechanisms;
|
||||
/*! Current status for a contact (default - unavailable) */
|
||||
enum ast_sip_contact_status_type status;
|
||||
/*! Last status for a contact (default - unavailable) */
|
||||
|
@ -432,6 +515,20 @@ struct ast_sip_contact_wrapper {
|
|||
struct ast_sip_contact *contact;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief 100rel modes for SIP endpoints
|
||||
*/
|
||||
enum ast_sip_100rel_mode {
|
||||
/*! Do not support 100rel. (no) */
|
||||
AST_SIP_100REL_UNSUPPORTED = 0,
|
||||
/*! As UAC, indicate 100rel support in Supported header. (yes) */
|
||||
AST_SIP_100REL_SUPPORTED,
|
||||
/*! As UAS, send 1xx responses reliably, if the UAC indicated its support. Otherwise same as AST_SIP_100REL_SUPPORTED. (peer_supported) */
|
||||
AST_SIP_100REL_PEER_SUPPORTED,
|
||||
/*! Require the use of 100rel. (required) */
|
||||
AST_SIP_100REL_REQUIRED,
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief DTMF modes for SIP endpoints
|
||||
*/
|
||||
|
@ -956,6 +1053,10 @@ struct ast_sip_endpoint {
|
|||
unsigned int suppress_q850_reason_headers;
|
||||
/*! Ignore 183 if no SDP is present */
|
||||
unsigned int ignore_183_without_sdp;
|
||||
/*! Type of security negotiation to use (RFC 3329). */
|
||||
enum ast_sip_security_negotiation security_negotiation;
|
||||
/*! Client security mechanisms (RFC 3329). */
|
||||
struct ast_sip_security_mechanism_vector security_mechanisms;
|
||||
/*! Set which STIR/SHAKEN behaviors we want on this endpoint */
|
||||
unsigned int stir_shaken;
|
||||
/*! Should we authenticate OPTIONS requests per RFC 3261? */
|
||||
|
@ -964,6 +1065,12 @@ struct ast_sip_endpoint {
|
|||
AST_STRING_FIELD_EXTENDED(geoloc_incoming_call_profile);
|
||||
/*! The name of the geoloc profile to apply when Asterisk sends a call to this endpoint */
|
||||
AST_STRING_FIELD_EXTENDED(geoloc_outgoing_call_profile);
|
||||
/*! The context to use for overlap dialing, if different from the endpoint's context */
|
||||
AST_STRING_FIELD_EXTENDED(overlap_context);
|
||||
/*! 100rel mode to use with this endpoint */
|
||||
enum ast_sip_100rel_mode rel100;
|
||||
/*! Send Advice-of-Charge messages */
|
||||
unsigned int send_aoc;
|
||||
};
|
||||
|
||||
/*! URI parameter for symmetric transport */
|
||||
|
@ -987,8 +1094,8 @@ extern pjsip_media_type pjsip_media_type_text_plain;
|
|||
/*!
|
||||
* \brief Compare pjsip media types
|
||||
*
|
||||
* \param pjsip_media_type a
|
||||
* \param pjsip_media_type b
|
||||
* \param a the first media type
|
||||
* \param b the second media type
|
||||
* \retval 1 Media types are equal
|
||||
* \retval 0 Media types are not equal
|
||||
*/
|
||||
|
@ -1004,6 +1111,87 @@ int ast_sip_are_media_types_equal(pjsip_media_type *a, pjsip_media_type *b);
|
|||
*/
|
||||
int ast_sip_is_media_type_in(pjsip_media_type *a, ...) attribute_sentinel;
|
||||
|
||||
/*!
|
||||
* \brief Add security headers to transmission data
|
||||
*
|
||||
* \param security_mechanisms Vector of security mechanisms.
|
||||
* \param header_name The header name under which to add the security mechanisms.
|
||||
* One of Security-Client, Security-Server, Security-Verify.
|
||||
* \param add_qval If zero, don't add the q-value to the header.
|
||||
* \param tdata The transmission data.
|
||||
* \retval 0 Success
|
||||
* \retval non-zero Failure
|
||||
*/
|
||||
int ast_sip_add_security_headers(struct ast_sip_security_mechanism_vector *security_mechanisms,
|
||||
const char *header_name, int add_qval, pjsip_tx_data *tdata);
|
||||
|
||||
/*!
|
||||
* \brief Append to security mechanism vector from SIP header
|
||||
*
|
||||
* \param hdr The header of the security mechanisms.
|
||||
* \param security_mechanisms Vector of security mechanisms to append to.
|
||||
* Header name must be one of Security-Client, Security-Server, Security-Verify.
|
||||
*/
|
||||
void ast_sip_header_to_security_mechanism(const pjsip_generic_string_hdr *hdr,
|
||||
struct ast_sip_security_mechanism_vector *security_mechanisms);
|
||||
|
||||
/*!
|
||||
* \brief Initialize security mechanism vector from string of security mechanisms.
|
||||
*
|
||||
* \param security_mechanism Pointer to vector of security mechanisms to initialize.
|
||||
* \param value String of security mechanisms as defined in RFC 3329.
|
||||
* \retval 0 Success
|
||||
* \retval non-zero Failure
|
||||
*/
|
||||
int ast_sip_security_mechanism_vector_init(struct ast_sip_security_mechanism_vector *security_mechanism, const char *value);
|
||||
|
||||
/*!
|
||||
* \brief Removes all headers of a specific name and value from a pjsip_msg.
|
||||
*
|
||||
* \param msg PJSIP message from which to remove headers.
|
||||
* \param hdr_name Name of the header to remove.
|
||||
* \param value Optional string value of the header to remove.
|
||||
* If NULL, remove all headers of given hdr_name.
|
||||
*/
|
||||
void ast_sip_remove_headers_by_name_and_value(pjsip_msg *msg, const pj_str_t *hdr_name, const char* value);
|
||||
|
||||
/*!
|
||||
* \brief Duplicate a security mechanism.
|
||||
*
|
||||
* \param dst Security mechanism to duplicate to.
|
||||
* \param src Security mechanism to duplicate.
|
||||
*/
|
||||
void ast_sip_security_mechanisms_vector_copy(struct ast_sip_security_mechanism_vector *dst,
|
||||
const struct ast_sip_security_mechanism_vector *src);
|
||||
|
||||
/*!
|
||||
* \brief Free contents of a security mechanism vector.
|
||||
*
|
||||
* \param security_mechanisms Vector whose contents are to be freed
|
||||
*/
|
||||
void ast_sip_security_mechanisms_vector_destroy(struct ast_sip_security_mechanism_vector *security_mechanisms);
|
||||
|
||||
/*!
|
||||
* \brief Allocate a security mechanism from a string.
|
||||
*
|
||||
* \param security_mechanism Pointer-pointer to the security mechanism to allocate.
|
||||
* \param value The security mechanism string as defined in RFC 3329 (section 2.2)
|
||||
* \param ... in the form \<mechanism_name>;q=\<q_value>;\<mechanism_parameters>
|
||||
* \retval 0 Success
|
||||
* \retval non-zero Failure
|
||||
*/
|
||||
int ast_sip_str_to_security_mechanism(struct ast_sip_security_mechanism **security_mechanism, const char *value);
|
||||
|
||||
/*!
|
||||
* \brief Set the security negotiation based on a given string.
|
||||
*
|
||||
* \param security_negotiation Security negotiation enum to set.
|
||||
* \param val String that represents a security_negotiation value.
|
||||
* \retval 0 Success
|
||||
* \retval non-zero Failure
|
||||
*/
|
||||
int ast_sip_set_security_negotiation(enum ast_sip_security_negotiation *security_negotiation, const char *val);
|
||||
|
||||
/*!
|
||||
* \brief Initialize an auth vector with the configured values.
|
||||
*
|
||||
|
@ -3417,6 +3605,25 @@ struct ast_sip_service_route_vector *ast_sip_service_route_vector_alloc(void);
|
|||
*/
|
||||
void ast_sip_service_route_vector_destroy(struct ast_sip_service_route_vector *service_routes);
|
||||
|
||||
/*!
|
||||
* \brief Set the ID for a connected line update
|
||||
*
|
||||
* \retval -1 on failure, 0 on success
|
||||
*/
|
||||
int ast_sip_set_id_connected_line(struct pjsip_rx_data *rdata, struct ast_party_id *id);
|
||||
|
||||
/*!
|
||||
* \brief Set the ID from an INVITE
|
||||
*
|
||||
* \param rdata
|
||||
* \param id ID structure to fill
|
||||
* \param default_id Default ID structure with data to use (for non-trusted endpoints)
|
||||
* \param trust_inbound Whether or not the endpoint is trusted (controls whether PAI or RPID can be used)
|
||||
*
|
||||
* \retval -1 on failure, 0 on success
|
||||
*/
|
||||
int ast_sip_set_id_from_invite(struct pjsip_rx_data *rdata, struct ast_party_id *id, struct ast_party_id *default_id, int trust_inbound);
|
||||
|
||||
/*!
|
||||
* \brief Set name and number information on an identity header.
|
||||
*
|
||||
|
@ -3581,6 +3788,7 @@ enum ast_transport_monitor_reg {
|
|||
|
||||
/*!
|
||||
* \brief Register a reliable transport shutdown monitor callback.
|
||||
* \deprecated Replaced with ast_sip_transport_monitor_register_key().
|
||||
* \since 13.20.0
|
||||
*
|
||||
* \param transport Transport to monitor for shutdown.
|
||||
|
@ -3598,8 +3806,29 @@ enum ast_transport_monitor_reg {
|
|||
enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transport *transport,
|
||||
ast_transport_monitor_shutdown_cb cb, void *ao2_data);
|
||||
|
||||
/*!
|
||||
* \brief Register a reliable transport shutdown monitor callback.
|
||||
*
|
||||
* \param transport_key Key for the transport to monitor for shutdown.
|
||||
* Create the key with AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR.
|
||||
* \param cb Who to call when transport is shutdown.
|
||||
* \param ao2_data Data to pass with the callback.
|
||||
*
|
||||
* \note The data object passed will have its reference count automatically
|
||||
* incremented by this call and automatically decremented after the callback
|
||||
* runs or when the callback is unregistered.
|
||||
*
|
||||
* There is no checking for duplicate registrations.
|
||||
*
|
||||
* \return enum ast_transport_monitor_reg
|
||||
*/
|
||||
enum ast_transport_monitor_reg ast_sip_transport_monitor_register_key(
|
||||
const char *transport_key, ast_transport_monitor_shutdown_cb cb,
|
||||
void *ao2_data);
|
||||
|
||||
/*!
|
||||
* \brief Register a reliable transport shutdown monitor callback replacing any duplicate.
|
||||
* \deprecated Replaced with ast_sip_transport_monitor_register_replace_key().
|
||||
* \since 13.26.0
|
||||
* \since 16.3.0
|
||||
*
|
||||
|
@ -3621,8 +3850,32 @@ enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transpor
|
|||
enum ast_transport_monitor_reg ast_sip_transport_monitor_register_replace(pjsip_transport *transport,
|
||||
ast_transport_monitor_shutdown_cb cb, void *ao2_data, ast_transport_monitor_data_matcher matches);
|
||||
|
||||
/*!
|
||||
* \brief Register a reliable transport shutdown monitor callback replacing any duplicate.
|
||||
*
|
||||
* \param transport_key Key for the transport to monitor for shutdown.
|
||||
* Create the key with AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR.
|
||||
* \param cb Who to call when transport is shutdown.
|
||||
* \param ao2_data Data to pass with the callback.
|
||||
* \param matches Matcher function that returns true if data matches a previously
|
||||
* registered data object
|
||||
*
|
||||
* \note The data object passed will have its reference count automatically
|
||||
* incremented by this call and automatically decremented after the callback
|
||||
* runs or when the callback is unregistered.
|
||||
*
|
||||
* This function checks for duplicates, and overwrites/replaces the old monitor
|
||||
* with the given one.
|
||||
*
|
||||
* \return enum ast_transport_monitor_reg
|
||||
*/
|
||||
enum ast_transport_monitor_reg ast_sip_transport_monitor_register_replace_key(
|
||||
const char *transport_key, ast_transport_monitor_shutdown_cb cb,
|
||||
void *ao2_data, ast_transport_monitor_data_matcher matches);
|
||||
|
||||
/*!
|
||||
* \brief Unregister a reliable transport shutdown monitor
|
||||
* \deprecated Replaced with ast_sip_transport_monitor_unregister_key().
|
||||
* \since 13.20.0
|
||||
*
|
||||
* \param transport Transport to monitor for shutdown.
|
||||
|
@ -3638,6 +3891,23 @@ enum ast_transport_monitor_reg ast_sip_transport_monitor_register_replace(pjsip_
|
|||
void ast_sip_transport_monitor_unregister(pjsip_transport *transport,
|
||||
ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches);
|
||||
|
||||
/*!
|
||||
* \brief Unregister a reliable transport shutdown monitor
|
||||
*
|
||||
* \param transport_key Key for the transport to monitor for shutdown.
|
||||
* Create the key with AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR.
|
||||
* \param cb The callback that was used for the original register.
|
||||
* \param data Data to pass to the matcher. May be NULL and does NOT need to be an ao2 object.
|
||||
* If NULL, all monitors with the provided callback are unregistered.
|
||||
* \param matches Matcher function that returns true if data matches the previously
|
||||
* registered data object. If NULL, a simple pointer comparison is done.
|
||||
*
|
||||
* \note The data object passed into the original register will have its reference count
|
||||
* automatically decremented.
|
||||
*/
|
||||
void ast_sip_transport_monitor_unregister_key(const char *transport_key,
|
||||
ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches);
|
||||
|
||||
/*!
|
||||
* \brief Unregister a transport shutdown monitor from all reliable transports
|
||||
* \since 13.20.0
|
||||
|
@ -3677,4 +3947,95 @@ void ast_sip_transport_state_register(struct ast_sip_tpmgr_state_callback *eleme
|
|||
*/
|
||||
void ast_sip_transport_state_unregister(struct ast_sip_tpmgr_state_callback *element);
|
||||
|
||||
/*!
|
||||
* \brief Check whether a pjsip_uri is SIP/SIPS or not
|
||||
* \since 16.28.0
|
||||
*
|
||||
* \param uri The pjsip_uri to check
|
||||
*
|
||||
* \retval 1 if true
|
||||
* \retval 0 if false
|
||||
*/
|
||||
int ast_sip_is_uri_sip_sips(pjsip_uri *uri);
|
||||
|
||||
/*!
|
||||
* \brief Check whether a pjsip_uri is allowed or not
|
||||
* \since 16.28.0
|
||||
*
|
||||
* \param uri The pjsip_uri to check
|
||||
*
|
||||
* \retval 1 if allowed
|
||||
* \retval 0 if not allowed
|
||||
*/
|
||||
int ast_sip_is_allowed_uri(pjsip_uri *uri);
|
||||
|
||||
/*!
|
||||
* \brief Get the user portion of the pjsip_uri
|
||||
* \since 16.28.0
|
||||
*
|
||||
* \param uri The pjsip_uri to get the user from
|
||||
*
|
||||
* \note This function will check what kind of URI it receives and return
|
||||
* the user based off of that
|
||||
*
|
||||
* \return User string or empty string if not present
|
||||
*/
|
||||
const pj_str_t *ast_sip_pjsip_uri_get_username(pjsip_uri *uri);
|
||||
|
||||
/*!
|
||||
* \brief Get the host portion of the pjsip_uri
|
||||
* \since 16.28.0
|
||||
*
|
||||
* \param uri The pjsip_uri to get the host from
|
||||
*
|
||||
* \note This function will check what kind of URI it receives and return
|
||||
* the host based off of that
|
||||
*
|
||||
* \return Host string or empty string if not present
|
||||
*/
|
||||
const pj_str_t *ast_sip_pjsip_uri_get_hostname(pjsip_uri *uri);
|
||||
|
||||
/*!
|
||||
* \brief Find an 'other' SIP/SIPS URI parameter by name
|
||||
* \since 16.28.0
|
||||
*
|
||||
* A convenience function to find a named parameter from a SIP/SIPS URI. This
|
||||
* function will not find the following standard SIP/SIPS URI parameters which
|
||||
* are stored separately by PJSIP:
|
||||
*
|
||||
* \li `user`
|
||||
* \li `method`
|
||||
* \li `transport`
|
||||
* \li `ttl`
|
||||
* \li `lr`
|
||||
* \li `maddr`
|
||||
*
|
||||
* \param uri The pjsip_uri to get the parameter from
|
||||
* \param param_str The name of the parameter to find
|
||||
*
|
||||
* \note This function will check what kind of URI it receives and return
|
||||
* the parameter based off of that
|
||||
*
|
||||
* \return Find parameter or NULL if not present
|
||||
*/
|
||||
struct pjsip_param *ast_sip_pjsip_uri_get_other_param(pjsip_uri *uri, const pj_str_t *param_str);
|
||||
|
||||
/*!
|
||||
* \brief Retrieve the system setting 'all_codecs_on_empty_reinvite'.
|
||||
*
|
||||
* \retval non zero if we should return all codecs on empty re-INVITE
|
||||
*/
|
||||
unsigned int ast_sip_get_all_codecs_on_empty_reinvite(void);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Convert SIP hangup causes to Asterisk hangup causes
|
||||
*
|
||||
* \param cause SIP cause
|
||||
*
|
||||
* \retval matched cause code from causes.h
|
||||
*/
|
||||
const int ast_sip_hangup_sip2cause(int cause);
|
||||
|
||||
|
||||
#endif /* _RES_PJSIP_H */
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
/* Needed for pjmedia_sdp_session and pjsip_inv_session */
|
||||
#include <pjsip_ua.h>
|
||||
|
||||
/* Needed for ast_sip_security_mechanism_vector */
|
||||
#include "asterisk/res_pjsip.h"
|
||||
|
||||
/* Forward declarations */
|
||||
struct ast_sip_endpoint;
|
||||
|
|
|
@ -156,7 +156,7 @@ struct ast_stir_shaken_payload *ast_stir_shaken_verify2(const char *header, cons
|
|||
* \param signature The payload signature
|
||||
* \param algorithm The signature algorithm
|
||||
* \param public_cert_url The public key URL
|
||||
* \param failure_code Additional failure information
|
||||
* \param failure Additional failure information
|
||||
* \param profile The stir_shaken_profile
|
||||
*
|
||||
* \retval ast_stir_shaken_payload on success
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue