Compare commits
No commits in common. "master" and "old-master-svn" have entirely different histories.
master
...
old-master
|
@ -1,29 +0,0 @@
|
|||
*.cache
|
||||
Makefile
|
||||
YateLocal*
|
||||
configure
|
||||
config.*
|
||||
*-stamp
|
||||
yatepaths.h
|
||||
yateversn.h
|
||||
yateiss.inc
|
||||
run
|
||||
yate.spec
|
||||
yate
|
||||
yate-config
|
||||
yate-config.in
|
||||
yate.pc
|
||||
core*
|
||||
yate.core*
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.so.*
|
||||
*.yate
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
||||
*.log
|
||||
*.out
|
||||
*.csv
|
||||
*.tsv
|
52
Makefile.in
52
Makefile.in
|
@ -81,10 +81,6 @@ DOCGEN := $(DOCGEN_D)
|
|||
APIDOCS := apidocs
|
||||
endif
|
||||
|
||||
GIT_TAG := $(shell LANG=C LC_MESSAGES=C git tag 2>/dev/null | tail -1)
|
||||
GIT_HASH := $(shell LANG=C LC_MESSAGES=C git rev-list -n1 HEAD 2>/dev/null)
|
||||
|
||||
|
||||
.PHONY: all everything debug ddebug xdebug ndebug
|
||||
all: engine modules clients ilibs
|
||||
|
||||
|
@ -295,9 +291,9 @@ uninstall uninstall-root:
|
|||
|
||||
install-root uninstall-root: LDCONFIG:=ldconfig
|
||||
|
||||
.PHONY: snapshot tarball rpm srpm rpm-tag srpm-tag rpm-head srpm-head revision check-gittag
|
||||
.PHONY: snapshot tarball rpm srpm revision
|
||||
snapshot tarball: check-topdir revision clean windows apidocs
|
||||
@if [ $@ = snapshot ]; then ver="`date '+GIT-%Y%m%d'`"; else ver="@PACKAGE_VERSION@-@PACKAGE_STATUS@@PACKAGE_RELEASE@"; fi ; \
|
||||
@if [ $@ = snapshot ]; then ver="`date '+SVN-%Y%m%d'`"; else ver="@PACKAGE_VERSION@-@PACKAGE_STATUS@@PACKAGE_RELEASE@"; fi ; \
|
||||
wd=`pwd|sed 's,^.*/,,'`; \
|
||||
mkdir -p packing/tarballs; cd ..; \
|
||||
echo $$wd/tar-exclude >$$wd/tar-exclude; \
|
||||
|
@ -311,8 +307,6 @@ snapshot tarball: check-topdir revision clean windows apidocs
|
|||
find $$wd -name .svn >>$$wd/tar-exclude; \
|
||||
find $$wd -name CVS >>$$wd/tar-exclude; \
|
||||
find $$wd -name .cvsignore >>$$wd/tar-exclude; \
|
||||
find $$wd -name .gitignore >>$$wd/tar-exclude; \
|
||||
find $$wd -name .git >>$$wd/tar-exclude; \
|
||||
else \
|
||||
echo "$$wd/packing/rpm/yate.spec" >>$$wd/tar-exclude; \
|
||||
fi ; \
|
||||
|
@ -329,47 +323,15 @@ snapshot tarball: check-topdir revision clean windows apidocs
|
|||
$$wd; \
|
||||
rm $$wd/tar-exclude
|
||||
|
||||
# rpm and sprm will check that head is at the last tag
|
||||
rpm: check-gittag tarball
|
||||
rpm: tarball
|
||||
rpmbuild -tb $(RPMOPT) packing/tarballs/@PACKAGE_TARNAME@-@PACKAGE_VERSION@-@PACKAGE_STATUS@@PACKAGE_RELEASE@.tar.gz
|
||||
|
||||
srpm: check-gittag tarball
|
||||
srpm: tarball
|
||||
rpmbuild -ta $(RPMOPT) packing/tarballs/@PACKAGE_TARNAME@-@PACKAGE_VERSION@-@PACKAGE_STATUS@@PACKAGE_RELEASE@.tar.gz
|
||||
|
||||
|
||||
#build rpm/srpm with tag in revision number
|
||||
rpm-tag: check-gittag tarball
|
||||
rpmbuild -tb --define 'revision $(GIT_TAG)git' $(RPMOPT) packing/tarballs/@PACKAGE_TARNAME@-@PACKAGE_VERSION@-@PACKAGE_STATUS@@PACKAGE_RELEASE@.tar.gz
|
||||
|
||||
srpm-tag: check-gittag tarball
|
||||
rpmbuild -ta --define 'revision $(GIT_TAG)git' $(RPMOPT) packing/tarballs/@PACKAGE_TARNAME@-@PACKAGE_VERSION@-@PACKAGE_STATUS@@PACKAGE_RELEASE@.tar.gz
|
||||
|
||||
|
||||
# build packages from GIT HEAD
|
||||
rpm-head: tarball
|
||||
rpmbuild -tb --define 'revision $(GIT_HASH)git' $(RPMOPT) packing/tarballs/@PACKAGE_TARNAME@-@PACKAGE_VERSION@-@PACKAGE_STATUS@@PACKAGE_RELEASE@.tar.gz
|
||||
|
||||
srpm-head: tarball
|
||||
rpmbuild -ta --define 'revision $(GIT_HASH)git' $(RPMOPT) packing/tarballs/@PACKAGE_TARNAME@-@PACKAGE_VERSION@-@PACKAGE_STATUS@@PACKAGE_RELEASE@.tar.gz
|
||||
|
||||
|
||||
check-gittag revision: check-topdir
|
||||
@tag_hash=""; \
|
||||
if [ "" != "$(GIT_TAG)" ]; then \
|
||||
tag_hash=`LANG=C LC_MESSAGES=C git rev-list -n1 $(GIT_TAG) 2>/dev/null`; \
|
||||
elif [ $@ = check-gittag ]; then \
|
||||
echo "No available GIT tag"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
if [ "x$(GIT_HASH)" != "x$$tag_hash" ]; then \
|
||||
if [ $@ = check-gittag ]; then \
|
||||
echo "Current commit hash $(GIT_HASH) different from expected hash for tag $(GIT_TAG) ($$tag_hash)"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
tag=""; \
|
||||
fi; \
|
||||
test -z "$(GIT_TAG)" || echo "$(GIT_TAG)" > packing/revision.txt ; \
|
||||
test -z "$(GIT_HASH)" || echo "$(GIT_HASH)" > packing/git_commit.txt
|
||||
revision: check-topdir
|
||||
@-rev=`LANG=C LC_MESSAGES=C svn info 2>/dev/null | sed -n 's,^Last Changed Rev: *,,p'`; \
|
||||
test -z "$$rev" || echo "$$rev" > packing/revision.txt
|
||||
|
||||
%.o: @srcdir@/%.cpp $(MKDEPS) @srcdir@/yatengine.h
|
||||
$(COMPILE) -c $<
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
Makefile
|
||||
YateLocal*
|
||||
.xvpics
|
||||
core*
|
||||
yate-*
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -5,7 +5,7 @@
|
|||
* A Qt-4 based universal telephony client
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
Makefile
|
||||
YateLocal.mak
|
||||
core*
|
||||
*.moc
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -5,7 +5,7 @@
|
|||
* A Qt-4 based universal telephony client
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* A Qt-4 based universal telephony client
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
*.bind
|
||||
Makefile
|
||||
YateLocal.mak
|
||||
core*
|
||||
*.conf
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -39,13 +39,6 @@
|
|||
; Default true
|
||||
; sips: Boolean. Use SIPS URI for register/contact. Transport defaults to TLS if enabled
|
||||
;
|
||||
; For TCP SIP HEP3 capturing, setup the following parameters:
|
||||
; capture_filter: Boolean, default false. Enable it if you want HEP3 capture of packets
|
||||
; capture_agent: String, mandatory if capture_filter is set to true. Name of capture
|
||||
; capture_server: String, mandatory if capture_filter is set to true. Name of HEP3 server where to send packets
|
||||
; capture_compress: Boolean, default false. Set to true to compress captured packets
|
||||
; If not set, capture settings will default to SIP global capture settings.
|
||||
;
|
||||
; NOTE: Default port is 5060 for udp/tcp and 5061 for tls
|
||||
;
|
||||
; Jabber:
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
|
||||
; each section starting with 'server' configures a connection
|
||||
; to a HEP3 server
|
||||
; The string after 'server' is the server name
|
||||
[server hep_server]
|
||||
|
||||
; enable: boolean: True to enable this connection. False to disable it
|
||||
; This setting is applicable on reload.
|
||||
;enable=yes
|
||||
|
||||
; auth_key: string: Authentication key string. If not set, it will look at
|
||||
; value of auth_key_hex.
|
||||
; This setting is applicable on reload.
|
||||
;auth_key=
|
||||
|
||||
; auth_key_hex: Hexified string: Authentication key in hexadecimal octet string.
|
||||
; If auth_key is not set and neither is this, authentication key will not be set
|
||||
; in HEP3 packets.
|
||||
; This setting is applicable on reload.
|
||||
;auth_key_hex=
|
||||
|
||||
; capture_id: unsigned 4 byte integer: HEP3 Capture agent ID for this connection
|
||||
; This setting is applicable on reload.
|
||||
;capture_id=0
|
||||
|
||||
; compress: boolean: Compress HEP3 packet payload
|
||||
; This setting can be overridden by entities that request a HEP3 capture
|
||||
; for its own instance.
|
||||
;compress=false
|
||||
|
||||
; socket_type: keyword (udp, tcp). Type of socket to create for communication
|
||||
; with this server.
|
||||
; Not applicable on reload.
|
||||
;socket_type=udp
|
||||
|
||||
; remote_host: destination IPv4 address, mandatory to set. IPv4 address of the
|
||||
; server where to send HEP3 packets
|
||||
; Not applicable on reload.
|
||||
;remote_host=
|
||||
|
||||
; remote_port: destination port, mandatory to set. Port where to send HEP3 packets
|
||||
; Not applicable on reload.
|
||||
;remote_port=
|
||||
|
||||
; local_host: local IPv4 address, mandatory to set. IPv4 address to use for
|
||||
; sending HEP3 packets
|
||||
; Not applicable on reload.
|
||||
;local_host=
|
||||
|
||||
; remote_port: source port, mandatory to set. Port to use for sending HEP3 packets
|
||||
; Not applicable on reload.
|
||||
;local_port=
|
|
@ -74,46 +74,3 @@ eliza=eliza.js
|
|||
; These scripts are loaded only after the engine and modules have initialized, immediately
|
||||
; after the dispatching of the "engine.start" message.
|
||||
; The names must be unique and different from any in the [scripts] section.
|
||||
|
||||
|
||||
[handlers]
|
||||
; Install singleton message handlers
|
||||
; These handlers are running using a separate context for each handled message
|
||||
;
|
||||
; Description:
|
||||
; name=filename,callback,priority,trackname,parameters_prefix,filter,context,script_name
|
||||
;
|
||||
; Parameters (optional, unless otherwise specified):
|
||||
; name: Required. Name of the message to handle
|
||||
; Names starting with 'handlerparam:' are ignored
|
||||
; filename: Required. Script to load
|
||||
; callback: Required. Callback function. Function is required to be present in script code
|
||||
; priority: Handler priority. Default: 100
|
||||
; trackname: Track name to be put in handled message 'handlers' parameter
|
||||
; parameters_prefix: Prefix for handler parameters specified in separate section parameters
|
||||
; filter: Message handler filter.
|
||||
; Format: filter_param=filter_value. Ignored if 'filter_param' is empty.
|
||||
; filter_value starting with '^' char is handled as regular expression
|
||||
; context: String to be passed to callback function
|
||||
; script_name: Name of the script. Used internally for debug purposes. Use 'script_file_name' if empty
|
||||
;
|
||||
; Notes:
|
||||
; - The following parameters are used to identify a handler:
|
||||
; name,filename,callback,priority,trackname,filter,context,script_name
|
||||
; An existing handler whose identity changed (not found in config) is removed at reload
|
||||
; - Multiple handlers for the same message may be installed
|
||||
|
||||
; handlerparam:<parameters_prefix>:<param_name>: string: Configure a parameters for a handler
|
||||
; Some of these parameters may be set in message handler description also (ignored here if so)
|
||||
; They may be configured here since they may contain ',' in their contents
|
||||
; Parameters:
|
||||
; debug: string: Script debug (e.g. 'level 10'). This parameter is applied on reload
|
||||
; context: string: Context to be passed to callback function
|
||||
; filter: string: Message handler filter. See handler description for format
|
||||
; track_priority: boolean: Add priority to tracked name. Default: true
|
||||
; load_extensions: boolean: Load extension in script context when a message is handled
|
||||
; This parameter is applied on reload
|
||||
; Default: [general] 'auto_extensions'
|
||||
; keep_old_on_fail: boolean: Keep old code if failed to parse the new one
|
||||
; This parameter is used when handler is re-loaded and script changed
|
||||
; Default: [general] 'keep_old_on_fail'
|
||||
|
|
|
@ -53,9 +53,3 @@
|
|||
; poolsize: int: Number of connections to establish for this account
|
||||
; Minimum number of connections is 1
|
||||
;poolsize=1
|
||||
|
||||
; warn_query_duration: integer: Warn if query duration (database query and result fetch)
|
||||
; exceeds this value (in milliseconds)
|
||||
; This parameter is applied on reload and can be overridden in query database message
|
||||
; Minium allowed interval is 50
|
||||
;warn_query_duration=0
|
||||
|
|
|
@ -148,14 +148,10 @@
|
|||
[extra]
|
||||
; This section allows installing handlers for any message name.
|
||||
; Each line must be of the form:
|
||||
; message.name=priority[,[paramname][,context][,filter_param][,filter_match]]]
|
||||
; message.name=priority[,[paramname][,context]]
|
||||
; For each handler create a corresponding [context] or [message.name] section
|
||||
; in which implement handling for that specific message. If paramname is not
|
||||
; set you will need to match parameters explicitely or set a new match string.
|
||||
; For filters, filter_param is the name of the parameter you want to match.
|
||||
; filter_match is the value that filter_param parameter has to match. It can be
|
||||
; an exact value to match or a regular expression. For regular expressions,
|
||||
; filter_match must start with the character '^'.
|
||||
; Examples:
|
||||
; engine.command=90
|
||||
; call.execute=120,callto
|
||||
|
|
|
@ -21,13 +21,9 @@
|
|||
; If empty it will match all messages
|
||||
; Example for a filter matching all chan.Anything messages and engine.halt:
|
||||
; filter=^\(chan\.\|engine\.halt$\)
|
||||
;filter=
|
||||
;filter
|
||||
|
||||
|
||||
|
||||
; timer: boolean: True to sniff engine.timer messages, false otherwise
|
||||
;timer=false
|
||||
|
||||
; max_buf_size: integer: Maximum admitted length of an encoded message.
|
||||
; If encoded message length exceeds this length, message will not be sniffed
|
||||
; Acceptable range is 2048 .. 65507
|
||||
;max_buf_size=2048
|
||||
|
||||
|
|
|
@ -110,18 +110,16 @@
|
|||
; filtersniff: regexp: Default filter to apply to message sniffer at initialization
|
||||
; If empty it will match all messages except engine.timer which is never displayed
|
||||
; Example for a filter matching all chan.Anything messages and engine.halt:
|
||||
; filtersniff=^\(chan\.\|engine\.halt\)$
|
||||
; filtersniff=^\(chan\.\|engine\.halt$\)
|
||||
;filtersniff=
|
||||
|
||||
; agesniff: float: Display only messages whose age or delay is higher than this value
|
||||
; This is a floating point number in seconds (1.5 means 1500msec)
|
||||
;agesniff=0
|
||||
|
||||
; filtersniffparams: string: Default parameter(s) filter to apply to message sniffer at initialization
|
||||
; If empty it will not attempt to match message parameters
|
||||
; Format: [any] [negated] param1=value1 [param2=value2 ...]
|
||||
; any: message matches if at least one configured parameter matches
|
||||
; negated: message matches if list does not match
|
||||
; Format: [any] param1=value1 [param2=value2 ...]
|
||||
; 'any' indicates that message matches if at least one configured parameter matches
|
||||
; Value to match is handled as regexp. It may end with '^' to revert match (i.e. matches if regexp don't match)
|
||||
; Value may be empty. In this case the parameter matches if missing in message or present with empty value
|
||||
; Example for a filter matching messages with empty route_type or route_type=call
|
||||
|
@ -130,16 +128,6 @@
|
|||
; filtersniffparams=any caller=^123$ called=^123$
|
||||
;filtersniffparams=
|
||||
|
||||
; msgsniff:<NAME>: string: Add a message sniffer rule
|
||||
; <NAME> is optional. If missing the rule is handled as the one set using filtersniff/agesniff/filtersniffparams
|
||||
; This parameter may be repeated with different <NAME> value to add multiple rules
|
||||
; A rule will replace a previously defined rule with the same name
|
||||
; Format: [filter=[value]] [age=[value]] [params [any negated] [name=value]]
|
||||
; Examples:
|
||||
; msgsniff:=filter=^\(chan\.\) age=0.5 params id=^sip/
|
||||
; msgsniff:extra=filter=^\(call\.cdr\)$ params any caller=123 called=^2
|
||||
;msgsniff:<NAME>=
|
||||
|
||||
; trace_msg_time: boolean: Instruct message dispatcher to set message event(s) time (enqueue / dispatch)
|
||||
;trace_msg_time=no
|
||||
|
||||
|
@ -241,19 +229,3 @@ h323chan.yate=yes
|
|||
|
||||
; dtmfdups: bool: Allow duplicate DTMFs (detected with different methods)
|
||||
;dtmfdups=disable
|
||||
|
||||
|
||||
[configuration]
|
||||
; Options for Configuration files
|
||||
; These parameters are handled on first load only (repeated parameters are ignored)
|
||||
; This section should be the first handled section if you need to apply parameters on this file also
|
||||
|
||||
; max_depth: integer: Maximum file include depth
|
||||
; Allowed interval: 3..10
|
||||
;max_depth=3
|
||||
|
||||
; disable_include_silent: boolean: Disable silent include in Configuration files
|
||||
; The '$includesilent' directives of configuration file will be handled as '$include' if this
|
||||
; parameter is set to boolean true
|
||||
; Same applies for '$includesectionsilent': handled as '$includesection'
|
||||
;disable_include_silent=no
|
||||
|
|
|
@ -155,9 +155,6 @@
|
|||
; OBSOLETE - please use "enable" in section [options]
|
||||
;options=enable
|
||||
|
||||
; update: bool: Enable receiving UPDATE transactions (RFC 3311)
|
||||
;update=disable
|
||||
|
||||
; prack: bool: Enable acknowledging provisional 1xx answers (RFC 3262)
|
||||
;prack=disable
|
||||
|
||||
|
@ -250,15 +247,10 @@
|
|||
; Defaults to enable
|
||||
;honor_dtmf_detect=enable
|
||||
|
||||
; rfc2833: bool: Offer RFC2833 telephone-event 8KHz by default
|
||||
; rfc2833: bool: Offer RFC2833 telephone-event by default
|
||||
; A numeric payload >= 96 can be provided
|
||||
;rfc2833=yes
|
||||
|
||||
; rfc2833_RATE: bool: Offer RFC2833 telephone-event for specific rate (non 8KHz) by default
|
||||
; A numeric payload >= 96 can be provided
|
||||
; Supported rates (parameters): rfc2833_16000, rfc2833_32000
|
||||
;rfc2833_RATE=yes
|
||||
|
||||
; privacy: bool: Process and generate privacy related SIP headers
|
||||
;privacy=disable
|
||||
|
||||
|
@ -268,9 +260,6 @@
|
|||
; forward_sdp: bool: Include the raw SDP body to be used as-is for forwarding RTP
|
||||
;forward_sdp=disable
|
||||
|
||||
; forward_gpmd: bool: Propagate GPMD even when not forwarding RTP
|
||||
;forward_gpmd=disable
|
||||
|
||||
; rtp_start: bool: Start RTP when sending 200 on incoming instead of receiving ACK
|
||||
;rtp_start=disable
|
||||
|
||||
|
@ -381,73 +370,12 @@
|
|||
; If set this parameter must be less than 'tcp_keepalive'
|
||||
;tcp_keepalive_first=0
|
||||
|
||||
; ssdp_prefix: string: Prefix to use when handling SDP session level parameters
|
||||
; This parameter is used when setting them in yate messages or handling them from there
|
||||
; This parameter is applied on reload
|
||||
; Prefix used to set parsed SDP: <ssdp_prefix>_ (default: ssdp_)
|
||||
; Prefix used to update from yate messages: o<ssdp_prefix>_ (default: ossdp_)
|
||||
; When updated from yate messages the prefix must be set in 'ossdp-prefix' message parameter
|
||||
;ssdp_prefix=ssdp
|
||||
|
||||
; initial_headers: boolean: Put all headers from initial requests in yate message
|
||||
; Handled for incoming channel preroute, user (un)register and messages sent on SIP
|
||||
; requests received outside a dialog
|
||||
; This parameter is applied on reload
|
||||
;initial_headers=no
|
||||
|
||||
; reinvite_wait_initial: boolean: Wait for answered initial transaction termination when need to send
|
||||
; a re-INVITE and initial transaction was not terminated
|
||||
; Applicable for the inbound call leg
|
||||
; This parameter is handled when answer (200 OK) was sent to initial transaction
|
||||
; If enabled the module will not send an UPDATE even if supported by remote
|
||||
; This parameter can be overridden from routing
|
||||
; This parameter is applied on reload
|
||||
;reinvite_wait_initial=no
|
||||
|
||||
; mixed_provisional: boolean: Accept mixed (non)reliable provisional responses to initial transaction
|
||||
; When enabled (default) the dialog will accept non reliable provisional messages
|
||||
; after receiving a reliable one
|
||||
; This parameter can be overridden from routing
|
||||
; This parameter is applied on reload
|
||||
;mixed_provisional=yes
|
||||
|
||||
; warn_bind_fail_delay: integer/string: Delay failed to bind debug message
|
||||
; This parameter may be used when listener is going to bind on an IP which may become
|
||||
; available later
|
||||
; Values:
|
||||
; integer: Delay in milliseconds. Interval: 500..60000
|
||||
; 'start': Delay until engine starts (engine.start message is handled by module)
|
||||
;warn_bind_fail_delay=0
|
||||
|
||||
; warn_no_default_udp_transport: boolean: Warn if there is not default UDP transport
|
||||
; This parameter is applied on reload
|
||||
;warn_no_default_udp_transport=yes
|
||||
|
||||
; capture_filter: boolean. Enable global HEP3 capture of SIP packets
|
||||
; NOTE: This setting can be overridden by listener settings or by account settings
|
||||
; in case of outgoing TCP connections.
|
||||
; This setting applies on reload.
|
||||
;capture_filter=false
|
||||
|
||||
; capture_agent: string, mandatory if capture_filter is set to true. Name of capture agent.
|
||||
; NOTE: This setting can be overridden by listener settings or by account settings
|
||||
; in case of outgoing TCP connections.
|
||||
; This is for internal tracking.
|
||||
;capture_agent=
|
||||
|
||||
; capture_server: string, mandatory if capture_filter is set to true.
|
||||
; Name of HEP3 server where to send packets. The server with this name must be configured
|
||||
; in HEP3 module configuration.
|
||||
; NOTE: This setting can be overridden by listener settings or by account settings
|
||||
; in case of outgoing TCP connections.
|
||||
;capture_server=
|
||||
|
||||
; capture_compress: boolean. Set to true to compress captured packets.
|
||||
; If not set, it will use the HEP server configuration 'compress' configured value.
|
||||
; NOTE: This setting can be overridden by listener settings or by account settings
|
||||
; in case of outgoing TCP connections.
|
||||
;capture_compress=false
|
||||
|
||||
|
||||
[options]
|
||||
; Controls the behaviour for SIP options retrieval
|
||||
|
@ -623,7 +551,6 @@
|
|||
; The following parameters can be overridden from 'general' section:
|
||||
; UDP: maxpkt, buffer
|
||||
; TCP/TLS: tcp_maxpkt
|
||||
; All: warn_bind_fail_delay
|
||||
|
||||
; type: keyword: Listener type
|
||||
; Allowed values:
|
||||
|
@ -688,21 +615,3 @@
|
|||
; role: string: Role to be set in messages sent by connections using this listener
|
||||
; This parameter is applied on reload
|
||||
;role=
|
||||
|
||||
; capture_filter: boolean. Enable HEP3 capture of packets on this listener.
|
||||
; NOTE: for outgoing TCP connections, these settings must be made in accfile.conf.
|
||||
; This setting applies on reload.
|
||||
;capture_filter=false
|
||||
|
||||
; capture_agent: string, mandatory if capture_filter is set to true. Name of capture agent
|
||||
; This is for internal tracking.
|
||||
;capture_agent=
|
||||
|
||||
; capture_server: string, mandatory if capture_filter is set to true.
|
||||
; Name of HEP3 server where to send packets. The server with this name must be configured
|
||||
; in HEP3 module configuration
|
||||
;capture_server=
|
||||
|
||||
; capture_compress: boolean. Set to true to compress captured packets.
|
||||
; If not set, it will use the HEP server configuration 'compress' configured value
|
||||
;capture_compress=false
|
||||
|
|
35
configure.ac
35
configure.ac
|
@ -9,11 +9,8 @@ fi
|
|||
|
||||
PACKAGE_RELEASE="1"
|
||||
PACKAGE_STATUS="devel"
|
||||
PACKAGE_REVISION=`cd "$srcdir"; LANG=C LC_MESSAGES=C git rev-list -n 1 HEAD 2>/dev/null`
|
||||
PACKAGE_REVISION=`cd "$srcdir"; LANG=C LC_MESSAGES=C svn info 2>/dev/null | sed -n 's,^Last Changed Rev: *,,p'`
|
||||
test -z "$PACKAGE_REVISION" && PACKAGE_REVISION=`cat "$srcdir/packing/revision.txt" 2>/dev/null`
|
||||
test -z "$PACKAGE_REVISION" && PACKAGE_REVISION=`cat "$srcdir/packing/git_commit.txt" 2>/dev/null`
|
||||
PACKAGE_GIT_HASH=`cd "$srcdir"; LANG=C LC_MESSAGES=C git rev-list -n 1 HEAD 2>/dev/null`
|
||||
test -z "$PACKAGE_REVISION" && PACKAGE_REVISION=`cat "$srcdir/packing/git_commit.txt" 2>/dev/null`
|
||||
AC_ARG_WITH(status,AC_HELP_STRING([--with-status=NAME],[use NAME as package status]),[PACKAGE_STATUS=$withval])
|
||||
|
||||
PACKAGE_VERSION_MAJOR="${PACKAGE_VERSION%%.*}"
|
||||
|
@ -28,7 +25,6 @@ AC_SUBST(PACKAGE_VERSION_RELEASE)
|
|||
AC_SUBST(PACKAGE_RELEASE)
|
||||
AC_SUBST(PACKAGE_STATUS)
|
||||
AC_SUBST(PACKAGE_REVISION)
|
||||
AC_SUBST(PACKAGE_GIT_HASH)
|
||||
|
||||
# We may need the host OS type but avoid the overhead of AC_CANONICAL_SYSTEM
|
||||
AC_MSG_CHECKING([for local operating system type])
|
||||
|
@ -313,25 +309,6 @@ MUTEX_HACK="$MUTEX_HACK -DHAVE_TIMEDWAIT"
|
|||
fi
|
||||
AC_MSG_RESULT([$have_sem_timedwait])
|
||||
|
||||
have_rd_timedlock=""
|
||||
AC_CHECK_LIB([pthread], [pthread_rwlock_timedrdlock], [have_rd_timedlock="yes"])
|
||||
if [[ "x$have_rd_timedlock" = "x" ]]; then
|
||||
AC_CHECK_LIB([c], [pthread_rwlock_timedrdlock],[have_rd_timedlock="yes"])
|
||||
fi
|
||||
if [[ "x$have_rd_timedlock" = "xyes" ]]; then
|
||||
MUTEX_HACK="$MUTEX_HACK -DHAVE_TIMEDRDLOCK"
|
||||
fi
|
||||
|
||||
have_wr_timedlock=""
|
||||
AC_CHECK_LIB([pthread], [pthread_rwlock_timedwrlock], [have_wr_timedlock="yes"])
|
||||
if [[ "x$have_wr_timedlock" = "x" ]]; then
|
||||
AC_CHECK_LIB([c], [pthread_rwlock_timedwrlock], [have_wr_timedlock="yes"])
|
||||
fi
|
||||
if [[ "x$have_rd_timedlock" = "xyes" ]]; then
|
||||
MUTEX_HACK="$MUTEX_HACK -DHAVE_TIMEDWRLOCK"
|
||||
fi
|
||||
|
||||
|
||||
CFLAGS="$SAVE_CFLAGS"
|
||||
LIBS="$SAVE_LIBS"
|
||||
AC_LANG_RESTORE
|
||||
|
@ -1204,7 +1181,7 @@ AC_SUBST(SPEEX_LIB)
|
|||
|
||||
HAVE_AMRNB=no
|
||||
AMRNB_INC=""
|
||||
AMRNB_LIB="-lopencore-amrnb"
|
||||
AMRNB_LIB="-lamrnb"
|
||||
AC_ARG_WITH(amrnb,AC_HELP_STRING([--with-amrnb=DIR],[use AMR-NB if available (default)]),[ac_cv_use_amrnb=$withval],[ac_cv_use_amrnb=/usr])
|
||||
if [[ "x$ac_cv_use_amrnb" = "xstatic" ]]; then
|
||||
ac_cv_use_amrnb=/usr
|
||||
|
@ -1213,9 +1190,9 @@ fi
|
|||
if [[ "x$ac_cv_use_amrnb" != "xno" ]]; then
|
||||
AC_MSG_CHECKING([for AMR-NB in $ac_cv_use_amrnb])
|
||||
local_lib="$ARCHLIB"
|
||||
amrinc="$ac_cv_use_amrnb/include/opencore-amrnb"
|
||||
test -f "$ac_cv_use_amrnb/$local_lib/libopencore-amrnb.so" || local_lib="lib"
|
||||
if [[ -f "$ac_cv_use_amrnb/$local_lib/libopencore-amrnb.so" -a -f "$amrinc/interf_dec.h" ]]; then
|
||||
amrinc="$ac_cv_use_amrnb/include/amrnb"
|
||||
test -f "$ac_cv_use_amrnb/$local_lib/libamrnb.so" || local_lib="lib"
|
||||
if [[ -f "$ac_cv_use_amrnb/$local_lib/libamrnb.so" -a -f "$amrinc/interf_rom.h" ]]; then
|
||||
HAVE_AMRNB=yes
|
||||
AMRNB_LIB="-L$ac_cv_use_amrnb/$local_lib $AMRNB_LIB"
|
||||
AMRNB_INC="-I$amrinc"
|
||||
|
@ -1824,7 +1801,7 @@ AC_SUBST(INSTALL_L)
|
|||
|
||||
INSTALL_D="install -D"
|
||||
CFLAGS=`echo "$CFLAGS" | sed 's/\(^\| \+\)-g[[0-9]]*//' | sed 's/[[[:space:]]]\{2,\}/ /g'`
|
||||
MODULE_CFLAGS="-fno-exceptions -fPIC $HAVE_GCC_FORMAT_CHECK $HAVE_BLOCK_RETURN $ATOMIC_OPS"
|
||||
MODULE_CFLAGS="-fno-exceptions -fPIC $HAVE_GCC_FORMAT_CHECK $HAVE_BLOCK_RETURN"
|
||||
MODULE_CPPFLAGS="$HAVE_NO_OVERLOAD_VIRT_WARN $RTTI_OPT $MODULE_CFLAGS"
|
||||
MODULE_LDRELAX="-rdynamic -shared"
|
||||
MODULE_SYMBOLS="-Wl,--retain-symbols-file,/dev/null"
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
core*
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,3 +0,0 @@
|
|||
core*
|
||||
*.*
|
||||
*~
|
|
@ -1,8 +0,0 @@
|
|||
Makefile
|
||||
YateLocal*
|
||||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Base64 data encoding and decoding
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
@ -1333,17 +1333,28 @@ bool Module::setDebug(Message& msg, const String& target)
|
|||
return false;
|
||||
|
||||
NamedCounter* counter = objectsCounter();
|
||||
const String& line = msg[YSTRING("line")];
|
||||
debugSet(line);
|
||||
String str = line;
|
||||
if (str.startSkip("level"))
|
||||
;
|
||||
else if (str == YSTRING("reset")) {
|
||||
String str = msg.getValue("line");
|
||||
if (str.startSkip("level")) {
|
||||
int dbg = debugLevel();
|
||||
str >> dbg;
|
||||
if (str == "+") {
|
||||
if (debugLevel() > dbg)
|
||||
dbg = debugLevel();
|
||||
}
|
||||
else if (str == "-") {
|
||||
if (debugLevel() < dbg)
|
||||
dbg = debugLevel();
|
||||
}
|
||||
debugLevel(dbg);
|
||||
}
|
||||
else if (str == "reset") {
|
||||
debugLevel(TelEngine::debugLevel());
|
||||
debugEnabled(true);
|
||||
if (counter)
|
||||
counter->enable(getObjCounting());
|
||||
}
|
||||
else if (str.startSkip("objects")) {
|
||||
bool dbg = (str == YSTRING("reset")) ? getObjCounting() : (counter && counter->enabled());
|
||||
bool dbg = (str == "reset") ? getObjCounting() : (counter && counter->enabled());
|
||||
str >> dbg;
|
||||
if (counter)
|
||||
counter->enable(dbg);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Default client logic
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2020 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
@ -22,159 +22,10 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_DEPTH 3
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
static unsigned int s_maxDepth = 3;
|
||||
static int s_disableIncludeSilent = -1;
|
||||
|
||||
class ConfigurationPrivate
|
||||
{
|
||||
public:
|
||||
enum Include {
|
||||
IncludeNone = 0,
|
||||
Include = 1,
|
||||
IncludeSilent = 2,
|
||||
IncludeRequire = 3,
|
||||
};
|
||||
inline ConfigurationPrivate(Configuration& cfg, bool isMain)
|
||||
: m_cfg(cfg), m_main(isMain)
|
||||
{}
|
||||
inline void addingParam(const String& sect, const String& name, const String& value) {
|
||||
if (!m_main || sect != YSTRING("configuration"))
|
||||
return;
|
||||
if (s_maxDepthInit && name == YSTRING("max_depth")) {
|
||||
s_maxDepthInit = false;
|
||||
s_maxDepth = value.toInteger(3,0,3,10);
|
||||
}
|
||||
else if (s_disableIncludeSilent < 0 && name == YSTRING("disable_include_silent"))
|
||||
s_disableIncludeSilent = value.toBoolean() ? 1 : 0;
|
||||
}
|
||||
inline bool prepareIncludeSection(const String& sect, String& s, const char* file, bool warn,
|
||||
bool& ok) {
|
||||
int inc = getIncludeSect(s);
|
||||
if (!inc)
|
||||
return false;
|
||||
NamedList* nl = sect ? m_cfg.getSection(sect) : 0;
|
||||
if (nl) {
|
||||
nl->addParam("[]",s);
|
||||
if (!m_includeSections.find(nl))
|
||||
m_includeSections.append(nl)->setDelete(false);
|
||||
XDebug(DebugAll,"Config '%s' prepared section '%s' include '%s' file='%s'",
|
||||
m_cfg.safe(),sect.safe(),s.safe(),(file == m_cfg.c_str() ? "<same>" : file));
|
||||
}
|
||||
else {
|
||||
if (inc == IncludeRequire)
|
||||
ok = false;
|
||||
if (getWarn(warn,inc == IncludeSilent)) {
|
||||
String tmp;
|
||||
if (file != m_cfg.c_str())
|
||||
tmp.printf(" in included file '%s'",file);
|
||||
Debug(DebugNote,"Config '%s' found '%s' outside any section%s",
|
||||
m_cfg.safe(),s.safe(),tmp.safe());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
inline void processIncludeSections(bool warn, bool& ok) {
|
||||
for (ObjList* o = m_includeSections.skipNull(); o; o = o->skipNext()) {
|
||||
ObjList stack;
|
||||
processInclude(static_cast<NamedList*>(o->get()),stack,warn,ok);
|
||||
}
|
||||
}
|
||||
inline bool getWarn(bool warn, bool silent)
|
||||
{ return (warn && silent) ? (s_disableIncludeSilent > 0) : warn; }
|
||||
|
||||
static inline int getIncludeSect(String& buf, bool setName = false) {
|
||||
if (buf.startsWith("$includesection",true))
|
||||
{ if (setName) buf = buf.substr(16,buf.length() - 16); return Include; }
|
||||
if (buf.startsWith("$includesectionsilent",true))
|
||||
{ if (setName) buf = buf.substr(22,buf.length() - 22); return IncludeSilent; }
|
||||
if (buf.startsWith("$requiresection",true))
|
||||
{ if (setName) buf = buf.substr(16,buf.length() - 16); return IncludeRequire; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool s_maxDepthInit;
|
||||
|
||||
private:
|
||||
void processInclude(NamedList* sect, ObjList& stack, bool warn, bool& ok);
|
||||
|
||||
Configuration& m_cfg;
|
||||
bool m_main;
|
||||
ObjList m_includeSections;
|
||||
ObjList m_includeSectProcessed;
|
||||
};
|
||||
bool ConfigurationPrivate::s_maxDepthInit = true;
|
||||
|
||||
void ConfigurationPrivate::processInclude(NamedList* sect, ObjList& stack, bool warn, bool& ok)
|
||||
{
|
||||
if (!sect || m_includeSectProcessed.find(sect))
|
||||
return;
|
||||
stack.append(sect)->setDelete(false);
|
||||
#ifdef XDEBUG
|
||||
String tmp;
|
||||
tmp.append(stack," -> ");
|
||||
Debug(DebugInfo,"Config '%s' processing include section stack: %s",
|
||||
m_cfg.safe(),tmp.safe());
|
||||
#endif
|
||||
for (ObjList* o = sect->paramList()->skipNull(); o;) {
|
||||
NamedString* s = static_cast<NamedString*>(o->get());
|
||||
int inc = 0;
|
||||
if ('[' == s->name()[0] && ']' == s->name()[1])
|
||||
inc = getIncludeSect(*s,true);
|
||||
if (!inc) {
|
||||
o = o->skipNext();
|
||||
continue;
|
||||
}
|
||||
Engine::runParams().replaceParams(*s);
|
||||
if (*s) {
|
||||
String error;
|
||||
if (!stack[*s]) {
|
||||
// NOTE: We are adding current section to processed after processing it
|
||||
// Handle already processed sections whithout checking for recursive include
|
||||
NamedList* incSect = static_cast<NamedList*>(m_includeSectProcessed[*s]);
|
||||
if (!incSect) {
|
||||
incSect = m_cfg.getSection(*s);
|
||||
if (incSect && incSect != sect)
|
||||
processInclude(incSect,stack,warn,ok);
|
||||
else
|
||||
error = incSect ? "recursive include" : "not found";
|
||||
}
|
||||
if (!error) {
|
||||
XDebug(DebugAll,"Config '%s' including section '%s' in '%s'",
|
||||
m_cfg.safe(),incSect->safe(),sect->safe());
|
||||
for (ObjList* p = incSect->paramList()->skipNull(); p; p = p->skipNext()) {
|
||||
NamedString* ns = static_cast<NamedString*>(p->get());
|
||||
o->insert(new NamedString(ns->name(),*ns));
|
||||
// Update current element (replaced by insert)
|
||||
o = o->next();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
error.append(stack," -> ");
|
||||
error = "recursive include stack=" + error;
|
||||
}
|
||||
if (error) {
|
||||
if (inc == IncludeRequire)
|
||||
ok = false;
|
||||
if (getWarn(warn,inc == IncludeSilent))
|
||||
Debug(DebugNote,"Config '%s' not including section '%s' in '%s': %s",
|
||||
m_cfg.safe(),s->safe(),sect->safe(),error.c_str());
|
||||
}
|
||||
}
|
||||
o->remove();
|
||||
o = o->skipNull();
|
||||
if (o)
|
||||
continue;
|
||||
sect->paramList()->compact();
|
||||
break;
|
||||
}
|
||||
stack.remove(sect,false);
|
||||
m_includeSectProcessed.insert(sect)->setDelete(false);
|
||||
}
|
||||
|
||||
|
||||
// Text sort callback
|
||||
static int textSort(GenObject* obj1, GenObject* obj2, void* context)
|
||||
{
|
||||
|
@ -189,12 +40,11 @@ static int textSort(GenObject* obj1, GenObject* obj2, void* context)
|
|||
|
||||
|
||||
Configuration::Configuration()
|
||||
: m_main(false)
|
||||
{
|
||||
}
|
||||
|
||||
Configuration::Configuration(const char* filename, bool warn)
|
||||
: String(filename), m_main(false)
|
||||
: String(filename)
|
||||
{
|
||||
load(warn);
|
||||
}
|
||||
|
@ -335,8 +185,7 @@ bool Configuration::load(bool warn)
|
|||
m_sections.clear();
|
||||
if (null())
|
||||
return false;
|
||||
ConfigurationPrivate priv(*this,m_main);
|
||||
return loadFile(c_str(),"",0,warn,&priv);
|
||||
return loadFile(c_str(),"",0,warn);
|
||||
}
|
||||
|
||||
static inline char* cfgReadLine(FILE* f, char* buf, int rd,
|
||||
|
@ -391,14 +240,12 @@ static inline char* cfgReadLine(FILE* f, char* buf, int rd,
|
|||
return pc;
|
||||
}
|
||||
|
||||
bool Configuration::loadFile(const char* file, String sect, unsigned int depth, bool warn, void* priv)
|
||||
bool Configuration::loadFile(const char* file, String sect, unsigned int depth, bool warn)
|
||||
{
|
||||
ConfigurationPrivate& cfg = *(ConfigurationPrivate*)priv;
|
||||
DDebug(DebugInfo,"Configuration::loadFile(\"%s\",[%s],%u,%s)",
|
||||
file,sect.c_str(),depth,String::boolText(warn));
|
||||
if (depth > s_maxDepth) {
|
||||
Debug(DebugWarn,"Config '%s' refusing to load config file '%s' at include depth %u",
|
||||
c_str(),file,depth);
|
||||
if (depth > MAX_DEPTH) {
|
||||
Debug(DebugWarn,"Refusing to open config file '%s' at include depth %u",file,depth);
|
||||
return false;
|
||||
}
|
||||
FILE *f = ::fopen(file,"r");
|
||||
|
@ -453,12 +300,8 @@ bool Configuration::loadFile(const char* file, String sect, unsigned int depth,
|
|||
}
|
||||
if (!enabled)
|
||||
continue;
|
||||
if (cfg.prepareIncludeSection(sect,s,file,warn,ok))
|
||||
continue;
|
||||
bool noerr = false;
|
||||
bool silent = false;
|
||||
if (s.startSkip("$require") || (noerr = s.startSkip("$include"))
|
||||
|| (silent = noerr = s.startSkip("$includesilent"))) {
|
||||
if (s.startSkip("$require") || (noerr = s.startSkip("$include"))) {
|
||||
Engine::runParams().replaceParams(s);
|
||||
String path;
|
||||
if (!s.startsWith(Engine::pathSeparator())) {
|
||||
|
@ -483,7 +326,6 @@ bool Configuration::loadFile(const char* file, String sect, unsigned int depth,
|
|||
}
|
||||
path << s;
|
||||
ObjList files;
|
||||
bool doWarn = cfg.getWarn(warn,silent);
|
||||
if (File::listDirectory(path,0,&files)) {
|
||||
path << Engine::pathSeparator();
|
||||
DDebug(DebugAll,"Configuration loading up to %u files from '%s'",
|
||||
|
@ -492,7 +334,7 @@ bool Configuration::loadFile(const char* file, String sect, unsigned int depth,
|
|||
while (String* it = static_cast<String*>(files.remove(false))) {
|
||||
if (!(it->startsWith(".") || it->endsWith("~")
|
||||
|| it->endsWith(".bak") || it->endsWith(".tmp")))
|
||||
ok = (loadFile(path + *it,sect,depth+1,doWarn,priv) || noerr) && ok;
|
||||
ok = (loadFile(path + *it,sect,depth+1,warn) || noerr) && ok;
|
||||
#ifdef DEBUG
|
||||
else
|
||||
Debug(DebugAll,"Configuration skipping over file '%s'",it->c_str());
|
||||
|
@ -501,7 +343,7 @@ bool Configuration::loadFile(const char* file, String sect, unsigned int depth,
|
|||
}
|
||||
}
|
||||
else
|
||||
ok = (loadFile(path,sect,depth+1,doWarn,priv) || noerr) && ok;
|
||||
ok = (loadFile(path,sect,depth+1,warn) || noerr) && ok;
|
||||
continue;
|
||||
}
|
||||
Engine::runParams().replaceParams(s);
|
||||
|
@ -529,20 +371,16 @@ bool Configuration::loadFile(const char* file, String sect, unsigned int depth,
|
|||
break;
|
||||
s += pc;
|
||||
}
|
||||
s.trimBlanks();
|
||||
cfg.addingParam(sect,key,s);
|
||||
addValue(sect,key,s);
|
||||
addValue(sect,key,s.trimBlanks());
|
||||
}
|
||||
::fclose(f);
|
||||
if (!depth)
|
||||
cfg.processIncludeSections(warn,ok);
|
||||
return ok;
|
||||
}
|
||||
if (warn) {
|
||||
int err = errno;
|
||||
if (depth)
|
||||
Debug(DebugNote,"Config '%s' failed to open included config file '%s' (%d: %s)",
|
||||
c_str(),file,err,strerror(err));
|
||||
Debug(DebugNote,"Failed to open included config file '%s' (%d: %s)",
|
||||
file,err,strerror(err));
|
||||
else
|
||||
Debug(DebugNote,"Failed to open config file '%s', using defaults (%d: %s)",
|
||||
file,err,strerror(err));
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
@ -77,14 +77,6 @@ static InitG711 s_initG711;
|
|||
|
||||
static const DataBlock s_empty;
|
||||
|
||||
static inline void* dbAlloc(unsigned int n, void* oldBuf = 0)
|
||||
{
|
||||
void* data = ::realloc(oldBuf,n);
|
||||
if (!data)
|
||||
Debug("DataBlock",DebugFail,"realloc(%u) returned NULL!",n);
|
||||
return data;
|
||||
}
|
||||
|
||||
const DataBlock& DataBlock::empty()
|
||||
{
|
||||
return s_empty;
|
||||
|
@ -130,7 +122,6 @@ void* DataBlock::getObject(const String& name) const
|
|||
void DataBlock::clear(bool deleteData)
|
||||
{
|
||||
m_length = 0;
|
||||
m_allocated = 0;
|
||||
if (m_data) {
|
||||
void *data = m_data;
|
||||
m_data = 0;
|
||||
|
@ -139,91 +130,6 @@ void DataBlock::clear(bool deleteData)
|
|||
}
|
||||
}
|
||||
|
||||
// Change (insert or append data) the current block
|
||||
bool DataBlock::change(unsigned int pos, const void* buf, unsigned int bufLen,
|
||||
unsigned int extra, int extraVal, bool mayOverlap)
|
||||
{
|
||||
unsigned int addLen = (buf ? bufLen : 0) + extra;
|
||||
if (!addLen)
|
||||
return true;
|
||||
XDebug("DataBlock",DebugAll,
|
||||
"change(%u,%p,%u,%d,%d,%u) add_lenlen=%u m_data=%p m_length=%u allocated=%u [%p]",
|
||||
pos,buf,bufLen,extra,extraVal,mayOverlap,addLen,m_data,m_length,m_allocated,this);
|
||||
if (!(buf && bufLen)) {
|
||||
buf = 0;
|
||||
bufLen = 0;
|
||||
}
|
||||
if (pos > m_length)
|
||||
pos = m_length;
|
||||
unsigned int newLen = m_length + addLen;
|
||||
void* data = 0;
|
||||
unsigned int aLen = 0;
|
||||
// Allocate a new buffer if input data may overlap with existing
|
||||
bool overlap = buf && (mayOverlap || buf == m_data);
|
||||
if (!m_data || overlap || newLen > m_allocated) {
|
||||
aLen = allocLen(newLen);
|
||||
// Append to existing: Realloc data. Avoid free
|
||||
void* reallocAppend = (!overlap && pos == m_length) ? m_data : 0;
|
||||
data = dbAlloc(aLen,reallocAppend);
|
||||
if (!data)
|
||||
return false;
|
||||
if (reallocAppend)
|
||||
clear(false);
|
||||
else
|
||||
copyData(data,m_data,m_length,pos,addLen);
|
||||
}
|
||||
else {
|
||||
moveData(m_data,m_length,pos,addLen);
|
||||
data = m_data;
|
||||
}
|
||||
if (bufLen)
|
||||
::memcpy((uint8_t*)data + pos,buf,bufLen);
|
||||
if (extra)
|
||||
::memset((uint8_t*)data + pos + bufLen,extraVal,extra);
|
||||
if (aLen)
|
||||
assign(data,newLen,false,aLen);
|
||||
else
|
||||
m_length = newLen;
|
||||
return true;
|
||||
}
|
||||
|
||||
#define DB_CHANGE_UINT_FUNC \
|
||||
unsigned int n = 0; \
|
||||
if (lsb) { \
|
||||
while (len--) { \
|
||||
buf[n++] = (uint8_t)value; \
|
||||
value = value >> 8; \
|
||||
} \
|
||||
} \
|
||||
else { \
|
||||
uint8_t sh = (len - 1) * 8; \
|
||||
while (len--) { \
|
||||
buf[n++] = (uint8_t)(value >> sh); \
|
||||
sh -= 8; \
|
||||
} \
|
||||
} \
|
||||
return change(pos,(const void*)buf,n,0,0,false)
|
||||
|
||||
bool DataBlock::change8(unsigned int pos, uint64_t value, unsigned int len, bool lsb)
|
||||
{
|
||||
if (!len)
|
||||
return true;
|
||||
if (len > 8)
|
||||
len = 8;
|
||||
uint8_t buf[8] = {0,0,0,0,0,0,0,0};
|
||||
DB_CHANGE_UINT_FUNC;
|
||||
}
|
||||
|
||||
bool DataBlock::change4(unsigned int pos, uint32_t value, unsigned int len, bool lsb)
|
||||
{
|
||||
if (!len)
|
||||
return true;
|
||||
if (len > 4)
|
||||
len = 4;
|
||||
uint8_t buf[4] = {0,0,0,0};
|
||||
DB_CHANGE_UINT_FUNC;
|
||||
}
|
||||
|
||||
DataBlock& DataBlock::assign(void* value, unsigned int len, bool copyData, unsigned int allocated)
|
||||
{
|
||||
if ((value != m_data) || (len != m_length)) {
|
||||
|
@ -292,6 +198,76 @@ DataBlock& DataBlock::operator=(const DataBlock& value)
|
|||
return *this;
|
||||
}
|
||||
|
||||
void DataBlock::append(const DataBlock& value)
|
||||
{
|
||||
if (m_length) {
|
||||
if (value.length()) {
|
||||
unsigned int len = m_length+value.length();
|
||||
if (len <= m_allocated) {
|
||||
::memcpy(m_length+(char*)m_data,value.data(),value.length());
|
||||
m_length = len;
|
||||
return;
|
||||
}
|
||||
unsigned int aLen = allocLen(len);
|
||||
void *data = ::malloc(aLen);
|
||||
if (data) {
|
||||
::memcpy(data,m_data,m_length);
|
||||
::memcpy(m_length+(char*)data,value.data(),value.length());
|
||||
assign(data,len,false,aLen);
|
||||
}
|
||||
else
|
||||
Debug("DataBlock",DebugFail,"malloc(%d) returned NULL!",aLen);
|
||||
}
|
||||
}
|
||||
else
|
||||
assign(value.data(),value.length());
|
||||
}
|
||||
|
||||
void DataBlock::append(const String& value)
|
||||
{
|
||||
if (m_length) {
|
||||
if (value.length()) {
|
||||
unsigned int len = m_length+value.length();
|
||||
if (len <= m_allocated) {
|
||||
::memcpy(m_length+(char*)m_data,value.safe(),value.length());
|
||||
m_length = len;
|
||||
return;
|
||||
}
|
||||
unsigned int aLen = allocLen(len);
|
||||
void *data = ::malloc(aLen);
|
||||
if (data) {
|
||||
::memcpy(data,m_data,m_length);
|
||||
::memcpy(m_length+(char*)data,value.safe(),value.length());
|
||||
assign(data,len,false,aLen);
|
||||
}
|
||||
else
|
||||
Debug("DataBlock",DebugFail,"malloc(%d) returned NULL!",aLen);
|
||||
}
|
||||
}
|
||||
else
|
||||
assign((void*)value.c_str(),value.length());
|
||||
}
|
||||
|
||||
void DataBlock::insert(const DataBlock& value)
|
||||
{
|
||||
unsigned int vl = value.length();
|
||||
if (m_length) {
|
||||
if (vl) {
|
||||
unsigned int len = m_length+vl;
|
||||
void *data = ::malloc(len);
|
||||
if (data) {
|
||||
::memcpy(data,value.data(),vl);
|
||||
::memcpy(vl+(char*)data,m_data,m_length);
|
||||
assign(data,len,false);
|
||||
}
|
||||
else
|
||||
Debug("DataBlock",DebugFail,"malloc(%d) returned NULL!",len);
|
||||
}
|
||||
}
|
||||
else
|
||||
assign(value.data(),vl);
|
||||
}
|
||||
|
||||
unsigned int DataBlock::allocLen(unsigned int len) const
|
||||
{
|
||||
// allocate a multiple of 8 bytes
|
||||
|
@ -390,24 +366,60 @@ inline signed char hexDecode(char c)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static inline bool retResult(bool ok, int result, int* res)
|
||||
{
|
||||
if (res)
|
||||
*res = result;
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Change data from a hexadecimal string representation.
|
||||
// Build this data block from a hexadecimal string representation.
|
||||
// Each octet must be represented in the input string with 2 hexadecimal characters.
|
||||
// If a separator is specified, the octets in input string must be separated using
|
||||
// exactly 1 separator. Only 1 leading or 1 trailing separators are allowed
|
||||
bool DataBlock::changeHex(unsigned int pos, const char* data, unsigned int len, char sep,
|
||||
bool guessSep, bool emptyOk, int* res)
|
||||
bool DataBlock::unHexify(const char* data, unsigned int len, char sep)
|
||||
{
|
||||
clear();
|
||||
if (!(data && len))
|
||||
return retResult(emptyOk,0,res);
|
||||
return true;
|
||||
|
||||
if (!sep && guessSep && len > 2) {
|
||||
// Calculate the destination buffer length
|
||||
unsigned int n = 0;
|
||||
if (!sep) {
|
||||
if (0 != (len % 2))
|
||||
return false;
|
||||
n = len / 2;
|
||||
}
|
||||
else {
|
||||
// Remove leading and trailing separators
|
||||
if (data[0] == sep) {
|
||||
data++;
|
||||
len--;
|
||||
}
|
||||
if (len && data[len-1] == sep)
|
||||
len--;
|
||||
// No more leading and trailing separators allowed
|
||||
if (2 != (len % 3))
|
||||
return (bool)(len == 0);
|
||||
n = (len + 1) / 3;
|
||||
}
|
||||
if (!n)
|
||||
return true;
|
||||
|
||||
char* buf = (char*)::malloc(n);
|
||||
unsigned int iBuf = 0;
|
||||
for (unsigned int i = 0; i < len; i += (sep ? 3 : 2)) {
|
||||
signed char c1 = hexDecode(data[i]);
|
||||
signed char c2 = hexDecode(data[i+1]);
|
||||
if (c1 == -1 || c2 == -1 || (sep && (iBuf != n - 1) && (sep != data[i+2])))
|
||||
break;
|
||||
buf[iBuf++] = (c1 << 4) | c2;
|
||||
}
|
||||
if (iBuf >= n)
|
||||
assign(buf,n,false);
|
||||
else
|
||||
::free(buf);
|
||||
return (iBuf >= n);
|
||||
}
|
||||
|
||||
// This variant of unHexify automatically detects presence of separators
|
||||
bool DataBlock::unHexify(const char* data, unsigned int len)
|
||||
{
|
||||
char sep = 0;
|
||||
if (len > 2) {
|
||||
const char* s = " :;.,-/|";
|
||||
while (char c = *s++) {
|
||||
unsigned int offs = 2;
|
||||
|
@ -419,140 +431,38 @@ bool DataBlock::changeHex(unsigned int pos, const char* data, unsigned int len,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the destination buffer length
|
||||
unsigned int n = 0;
|
||||
if (!sep) {
|
||||
if (0 != (len % 2))
|
||||
return retResult(false,-3,res);
|
||||
n = len / 2;
|
||||
}
|
||||
else {
|
||||
// Remove leading and trailing separators
|
||||
if (data[0] == sep) {
|
||||
data++;
|
||||
len--;
|
||||
}
|
||||
if (len && data[len - 1] == sep)
|
||||
len--;
|
||||
// No more leading and trailing separators allowed
|
||||
if (!len)
|
||||
return retResult(emptyOk,0,res);
|
||||
if (2 != (len % 3))
|
||||
return retResult(false,-3,res);
|
||||
n = (len + 1) / 3;
|
||||
}
|
||||
if (!n)
|
||||
return retResult(emptyOk,0,res);
|
||||
|
||||
unsigned int newLen = m_length + n;
|
||||
unsigned int aLen = allocLen(newLen);
|
||||
void* newData = dbAlloc(aLen);
|
||||
if (!newData)
|
||||
return retResult(false,-1,res);
|
||||
if (pos > m_length)
|
||||
pos = m_length;
|
||||
char* buf = (char*)newData + pos;
|
||||
unsigned int iBuf = 0;
|
||||
for (unsigned int i = 0; i < len; i += (sep ? 3 : 2)) {
|
||||
signed char c1 = hexDecode(*data++);
|
||||
signed char c2 = hexDecode(*data++);
|
||||
if (c1 == -1 || c2 == -1 || (sep && (iBuf != n - 1) && (sep != *data++)))
|
||||
break;
|
||||
buf[iBuf++] = (c1 << 4) | c2;
|
||||
}
|
||||
if (iBuf < n) {
|
||||
::free(newData);
|
||||
return retResult(false,-2,res);
|
||||
}
|
||||
copyData(newData,m_data,m_length,pos,n);
|
||||
assign(newData,newLen,false,aLen);
|
||||
return retResult(true,n,res);
|
||||
return unHexify(data,len,sep);
|
||||
}
|
||||
|
||||
static inline bool dbIsEscape(char c, char extraEsc)
|
||||
String DataBlock::sqlEscape(char extraEsc) const
|
||||
{
|
||||
return c == '\0' || c == '\r' || c == '\n' || c == '\\' || c == '\'' || c == extraEsc;
|
||||
}
|
||||
|
||||
String& DataBlock::sqlEscape(String& str, const void* data, unsigned int len, char extraEsc)
|
||||
{
|
||||
if (!(data && len))
|
||||
return str;
|
||||
unsigned int useLen = len;
|
||||
char* ds = (char*)data;
|
||||
for (unsigned int i = 0; i < len; i++) {
|
||||
if (dbIsEscape(*ds++,extraEsc))
|
||||
useLen++;
|
||||
unsigned int len = m_length;
|
||||
unsigned int i;
|
||||
for (i = 0; i < m_length; i++) {
|
||||
char c = static_cast<char*>(m_data)[i];
|
||||
if (c == '\0' || c == '\r' || c == '\n' || c == '\\' || c == '\'' || c == extraEsc)
|
||||
len++;
|
||||
}
|
||||
// No escape needed ?
|
||||
if (useLen == len)
|
||||
return str.append((const char*)data,len);
|
||||
unsigned int sLen = str.length();
|
||||
str.append(' ',useLen);
|
||||
char* d = ((char*)(str.c_str())) + sLen;
|
||||
ds = (char*)data;
|
||||
for (unsigned int i = 0; i < len; i++) {
|
||||
char c = *ds++;
|
||||
if (dbIsEscape(c,extraEsc)) {
|
||||
String tmp(' ',len);
|
||||
char* d = const_cast<char*>(tmp.c_str());
|
||||
for (i = 0; i < m_length; i++) {
|
||||
char c = static_cast<char*>(m_data)[i];
|
||||
if (c == '\0' || c == '\r' || c == '\n' || c == '\\' || c == '\'' || c == extraEsc)
|
||||
*d++ = '\\';
|
||||
switch (c) {
|
||||
case '\0':
|
||||
c = '0';
|
||||
break;
|
||||
case '\r':
|
||||
c = 'r';
|
||||
break;
|
||||
case '\n':
|
||||
c = 'n';
|
||||
break;
|
||||
}
|
||||
switch (c) {
|
||||
case '\0':
|
||||
c = '0';
|
||||
break;
|
||||
case '\r':
|
||||
c = 'r';
|
||||
break;
|
||||
case '\n':
|
||||
c = 'n';
|
||||
break;
|
||||
}
|
||||
*d++ = c;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
void DataBlock::moveData(void* buf, unsigned int len, unsigned int pos, unsigned int space)
|
||||
{
|
||||
if (!buf || pos >= len)
|
||||
return;
|
||||
unsigned int delta = pos + space;
|
||||
if (!delta)
|
||||
return;
|
||||
uint8_t* src = (uint8_t*)buf;
|
||||
uint8_t* dest = (uint8_t*)buf + delta;
|
||||
if (pos) {
|
||||
// Insert middle. Keep old data until pos. Copy the rest
|
||||
len -= pos;
|
||||
src += pos;
|
||||
}
|
||||
if (delta < len)
|
||||
::memmove(dest,src,len);
|
||||
else
|
||||
::memcpy(dest,src,len);
|
||||
}
|
||||
|
||||
void DataBlock::copyData(void* dest, const void* src, unsigned int len, unsigned int pos,
|
||||
unsigned int space)
|
||||
{
|
||||
if (!(src && dest && len))
|
||||
return;
|
||||
uint8_t* d = (uint8_t*)dest;
|
||||
const uint8_t* s = (const uint8_t*)src;
|
||||
if (!pos)
|
||||
// Data insert before existing, copy old data after it
|
||||
::memcpy(d + space,s,len);
|
||||
else if (pos == len)
|
||||
// Data added to existing, copy old at start
|
||||
::memcpy(d,s,len);
|
||||
else if (space) {
|
||||
// Insert middle
|
||||
::memcpy(d,s,pos);
|
||||
::memcpy(d + pos + space,s + pos,len - pos);
|
||||
}
|
||||
else
|
||||
::memcpy(d,s,len);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2020 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
@ -305,7 +305,6 @@ static int s_exit = -1;
|
|||
unsigned int Engine::s_congestion = 0;
|
||||
static Mutex s_congMutex(false,"Congestion");
|
||||
static bool s_debug = true;
|
||||
static NamedList s_debugInit("");
|
||||
static bool s_capture = CAPTURE_EVENTS;
|
||||
static int s_maxevents = 25;
|
||||
static Mutex s_eventsMutex(false,"EventsList");
|
||||
|
@ -544,35 +543,11 @@ bool EngineStatusHandler::received(Message &msg)
|
|||
objects(msg.retValue(),details);
|
||||
return true;
|
||||
}
|
||||
if (sel.startSkip("dispatcher")) {
|
||||
bool byMsg = sel.startSkip("handlers");
|
||||
if ((byMsg || sel.startSkip("handlers-trackname")) && sel) {
|
||||
String str;
|
||||
unsigned int count = 0;
|
||||
unsigned int total = 0;
|
||||
MessageDispatcher* d = Engine::dispatcher();
|
||||
if (d) {
|
||||
if (sel[0] == '^')
|
||||
count = d->fillHandlersInfo(byMsg,Regexp(sel),details ? &str : 0,&total);
|
||||
else
|
||||
count = d->fillHandlersInfo(byMsg,sel,details ? &str : 0,&total);
|
||||
}
|
||||
msg.retValue()
|
||||
<< "name=dispatcher,type=system,format=Priority|TrackName|Filtered;"
|
||||
<< "handlers=" << total << ",count=" << count;
|
||||
if (details)
|
||||
msg.retValue() << ';' << str;
|
||||
msg.retValue() << "\r\n";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
msg.retValue() << "name=engine,type=system";
|
||||
msg.retValue() << ",version=" << YATE_VERSION;
|
||||
msg.retValue() << ",revision=" << YATE_REVISION;
|
||||
msg.retValue() << ",githash=" << YATE_GIT_HASH;
|
||||
msg.retValue() << ",nodename=" << Engine::nodeName();
|
||||
msg.retValue() << ";plugins=" << plugins.count();
|
||||
msg.retValue() << ",inuse=" << Engine::self()->usedPlugins();
|
||||
|
@ -674,8 +649,6 @@ static const char s_runpOpt[] = " runparam name=value\r\n";
|
|||
static const char s_runpMsg[] = "Add a new parameter to the Engine's runtime list\r\n";
|
||||
static const char s_dispatcherOpt[] = " dispatcher {trace_msg_time|trace_msg_handler_time} <on|off>\r\n";
|
||||
static const char s_dispatcherMsg[] = "Enable or disable dispatcher debugging options\r\n";
|
||||
static const char s_dispatcherStatusOpt[] = " status dispatcher {handlers|handlers-trackname} <match>\r\n";
|
||||
static const char s_dispatcherStatusMsg[] = "Show installed handlers by message name or track name. Matching value starting with ^ is handled as basic regular expression\r\n";
|
||||
|
||||
// get the base name of a module file
|
||||
static String moduleBase(const String& fname)
|
||||
|
@ -778,32 +751,27 @@ void completeModule(String& ret, const String& part, ObjList& mods, bool reload,
|
|||
void EngineCommand::doCompletion(Message &msg, const String& partLine, const String& partWord)
|
||||
{
|
||||
if (partLine.null() || (partLine == YSTRING("help"))) {
|
||||
completeOne(msg.retValue(),YSTRING("module"),partWord);
|
||||
completeOne(msg.retValue(),YSTRING("events"),partWord);
|
||||
completeOne(msg.retValue(),YSTRING("logview"),partWord);
|
||||
completeOne(msg.retValue(),YSTRING("runparam"),partWord);
|
||||
completeOne(msg.retValue(),YSTRING("dispatcher"),partWord);
|
||||
completeOne(msg.retValue(),"module",partWord);
|
||||
completeOne(msg.retValue(),"events",partWord);
|
||||
completeOne(msg.retValue(),"logview",partWord);
|
||||
completeOne(msg.retValue(),"runparam",partWord);
|
||||
completeOne(msg.retValue(),"dispatcher",partWord);
|
||||
}
|
||||
else if (partLine == YSTRING("status")) {
|
||||
completeOne(msg.retValue(),YSTRING("engine"),partWord);
|
||||
completeOne(msg.retValue(),YSTRING("objects"),partWord);
|
||||
completeOne(msg.retValue(),YSTRING("dispatcher"),partWord);
|
||||
completeOne(msg.retValue(),"engine",partWord);
|
||||
completeOne(msg.retValue(),"objects",partWord);
|
||||
}
|
||||
else if (partLine == YSTRING("status objects")) {
|
||||
for (ObjList* l = getObjCounters().skipNull();l;l = l->skipNext())
|
||||
completeOne(msg.retValue(),l->get()->toString(),partWord);
|
||||
}
|
||||
else if (partLine == YSTRING("status dispatcher")) {
|
||||
completeOne(msg.retValue(),YSTRING("handlers"),partWord);
|
||||
completeOne(msg.retValue(),YSTRING("handlers-trackname"),partWord);
|
||||
}
|
||||
else if (partLine == YSTRING("module")) {
|
||||
completeOne(msg.retValue(),YSTRING("load"),partWord);
|
||||
completeOne(msg.retValue(),"load",partWord);
|
||||
if (!s_nounload) {
|
||||
completeOne(msg.retValue(),YSTRING("unload"),partWord);
|
||||
completeOne(msg.retValue(),YSTRING("reload"),partWord);
|
||||
completeOne(msg.retValue(),"unload",partWord);
|
||||
completeOne(msg.retValue(),"reload",partWord);
|
||||
}
|
||||
completeOne(msg.retValue(),YSTRING("list"),partWord);
|
||||
completeOne(msg.retValue(),"list",partWord);
|
||||
}
|
||||
else if (partLine == YSTRING("module load"))
|
||||
completeModule(msg.retValue(),partWord,Engine::self()->m_libs,false);
|
||||
|
@ -828,29 +796,28 @@ void EngineCommand::doCompletion(Message &msg, const String& partLine, const Str
|
|||
const EngineEventList* e = static_cast<const EngineEventList*>(l->get());
|
||||
completeOne(msg.retValue(),e->toString(),partWord);
|
||||
}
|
||||
completeOne(msg.retValue(),YSTRING("log"),partWord);
|
||||
completeOne(msg.retValue(),"log",partWord);
|
||||
if (partLine == YSTRING("events"))
|
||||
completeOne(msg.retValue(),YSTRING("clear"),partWord);
|
||||
completeOne(msg.retValue(),"clear",partWord);
|
||||
}
|
||||
else if (partLine == YSTRING("dispatcher")) {
|
||||
completeOne(msg.retValue(),YSTRING("trace_msg_time"),partWord);
|
||||
completeOne(msg.retValue(),YSTRING("trace_msg_handler_time"),partWord);
|
||||
completeOne(msg.retValue(),"trace_msg_time",partWord);
|
||||
completeOne(msg.retValue(),"trace_msg_handler_time",partWord);
|
||||
}
|
||||
else if ((partLine == YSTRING("dispatcher trace_msg_time"))
|
||||
|| (partLine == YSTRING("dispatcher trace_msg_handler_time"))) {
|
||||
completeOne(msg.retValue(),YSTRING("on"),partWord);
|
||||
completeOne(msg.retValue(),YSTRING("off"),partWord);
|
||||
completeOne(msg.retValue(),"on",partWord);
|
||||
completeOne(msg.retValue(),"off",partWord);
|
||||
}
|
||||
}
|
||||
|
||||
bool EngineCommand::received(Message &msg)
|
||||
{
|
||||
const String& l = msg[YSTRING("line")];
|
||||
if (!l) {
|
||||
String line = msg.getValue("line");
|
||||
if (line.null()) {
|
||||
doCompletion(msg,msg.getValue("partline"),msg.getValue("partword"));
|
||||
return false;
|
||||
}
|
||||
String line = l;
|
||||
if (line.startSkip("control")) {
|
||||
int pos = line.find(' ');
|
||||
String id = line.substr(0,pos).trimBlanks();
|
||||
|
@ -914,19 +881,17 @@ bool EngineCommand::received(Message &msg)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
if (line.startSkip("dispatcher")) {
|
||||
bool traceMsgTime = line.startSkip("trace_msg_time");
|
||||
if (traceMsgTime || line.startSkip("trace_msg_handler_time")) {
|
||||
MessageDispatcher* d = Engine::dispatcher();
|
||||
if (d) {
|
||||
if (traceMsgTime)
|
||||
d->traceTime(line.toBoolean());
|
||||
else
|
||||
d->traceHandlerTime(line.toBoolean());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
if (line.startSkip("dispatcher trace_msg_time")) {
|
||||
MessageDispatcher* d = Engine::dispatcher();
|
||||
if (d)
|
||||
d->traceTime(line.toBoolean());
|
||||
return 0 != d;
|
||||
}
|
||||
if (line.startSkip("dispatcher trace_msg_handler_time")) {
|
||||
MessageDispatcher* d = Engine::dispatcher();
|
||||
if (d)
|
||||
d->traceHandlerTime(line.toBoolean());
|
||||
return 0 != d;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1007,8 +972,7 @@ bool EngineHelp::received(Message &msg)
|
|||
else if (line == YSTRING("runparam"))
|
||||
msg.retValue() << s_runpOpt << s_runpMsg;
|
||||
else if (line == YSTRING("dispatcher"))
|
||||
msg.retValue() << s_dispatcherOpt << s_dispatcherMsg
|
||||
<< s_dispatcherStatusOpt << s_dispatcherStatusMsg;
|
||||
msg.retValue() << s_dispatcherOpt << s_dispatcherMsg;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
|
@ -1619,7 +1583,7 @@ int Engine::engineInit()
|
|||
#endif
|
||||
CapturedEvent::capturing(s_capture);
|
||||
s_cfg = configFile(s_cfgfile);
|
||||
s_cfg.loadMain();
|
||||
s_cfg.load();
|
||||
s_capture = s_cfg.getBoolValue("general","startevents",s_capture);
|
||||
CapturedEvent::capturing(s_capture);
|
||||
if (s_capture && s_startMsg)
|
||||
|
@ -1790,9 +1754,6 @@ int Engine::engineInit()
|
|||
}
|
||||
// Reload configuration file so conditionals will take into account runtime parameters
|
||||
s_cfg.load();
|
||||
NamedList* sect = s_cfg.getSection(YSTRING("debug"));
|
||||
if (sect)
|
||||
s_debugInit.copyParams(false,*sect);
|
||||
vars = s_cfg.getSection("variables");
|
||||
if (vars) {
|
||||
unsigned int n = vars->length();
|
||||
|
@ -1898,14 +1859,18 @@ int Engine::run()
|
|||
if (s_debug) {
|
||||
// one-time sending of debug setup messages
|
||||
s_debug = false;
|
||||
for (ObjList* o = s_debugInit.paramList()->skipNull(); o; o = o->skipNext()) {
|
||||
const NamedString* str = static_cast<NamedString*>(o->get());
|
||||
if (!(str->name() && *str))
|
||||
continue;
|
||||
Message* m = new Message("engine.debug");
|
||||
m->addParam("module",str->name());
|
||||
m->addParam("line",*str);
|
||||
enqueue(m);
|
||||
const NamedList* sect = s_cfg.getSection("debug");
|
||||
if (sect) {
|
||||
unsigned int n = sect->length();
|
||||
for (unsigned int i = 0; i < n; i++) {
|
||||
const NamedString* str = sect->getParam(i);
|
||||
if (!(str && str->name() && *str))
|
||||
continue;
|
||||
Message* m = new Message("engine.debug");
|
||||
m->addParam("module",str->name());
|
||||
m->addParam("line",*str);
|
||||
enqueue(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (s_capture) {
|
||||
|
@ -2313,8 +2278,6 @@ void Engine::initPlugins()
|
|||
for (; l; l = l->skipNext()) {
|
||||
Plugin *p = static_cast<Plugin *>(l->get());
|
||||
TempObjectCounter cnt(p->objectsCounter(),true);
|
||||
if (s_debug)
|
||||
p->debugSet(s_debugInit[p->toString()]);
|
||||
p->initialize();
|
||||
if (exiting()) {
|
||||
Output("Initialization aborted, exiting...");
|
||||
|
@ -2600,7 +2563,6 @@ void Engine::initLibrary(const String& line, String* output)
|
|||
ENGINE_SET_VAL_BREAK('s',s_lateabrt,true);
|
||||
ENGINE_INSTR_BREAK('m',setLockableWait());
|
||||
ENGINE_INSTR_BREAK('d',Lockable::enableSafety());
|
||||
ENGINE_INSTR_BREAK('r',RWLock::disableRWLock(true));
|
||||
default:
|
||||
unkArgs.append("-D" + String(*pc)," ");
|
||||
}
|
||||
|
@ -2939,9 +2901,6 @@ int Engine::main(int argc, const char** argv, const char** env, RunMode mode, En
|
|||
case 'd':
|
||||
Lockable::enableSafety();
|
||||
break;
|
||||
case 'r':
|
||||
RWLock::disableRWLock(true);
|
||||
break;
|
||||
#ifdef RTLD_GLOBAL
|
||||
case 'l':
|
||||
s_localsymbol = true;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* Idea and initial implementation (as HashTable) by Maciek Kaminski
|
||||
*
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2013-2023 Null Team
|
||||
* Copyright (C) 2013-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -115,7 +115,7 @@ Thread.o: @srcdir@/Thread.cpp $(MKDEPS) $(CINC)
|
|||
$(COMPILE) @THREAD_KILL@ @THREAD_AFFINITY@ @HAVE_PRCTL@ -c $<
|
||||
|
||||
TelEngine.o: @srcdir@/TelEngine.cpp $(MKDEPS) $(CINC)
|
||||
$(COMPILE) @HAVE_GMTOFF@ @HAVE_INT_TZ@ -c $<
|
||||
$(COMPILE) @ATOMIC_OPS@ @HAVE_GMTOFF@ @HAVE_INT_TZ@ -c $<
|
||||
|
||||
Client.o: @srcdir@/Client.cpp $(MKDEPS) $(CLINC)
|
||||
$(COMPILE) -c $<
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2015-2023 Null Team
|
||||
* Copyright (C) 2015 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
@ -57,14 +57,10 @@ static inline void unpackMsb8(uint8_t*& d, uint8_t val)
|
|||
}
|
||||
|
||||
// Copy string, advance dest and src, return src
|
||||
static inline const char* copyInc(char*& dest, const char* src, unsigned int n,
|
||||
bool strLen = false)
|
||||
static inline const char* copyInc(char*& dest, const char* src, unsigned int n)
|
||||
{
|
||||
if (n) {
|
||||
if (strLen)
|
||||
::strcpy(dest,src);
|
||||
else
|
||||
::strncpy(dest,src,n);
|
||||
::strncpy(dest,src,n);
|
||||
dest += n;
|
||||
}
|
||||
return src + n;
|
||||
|
@ -104,11 +100,11 @@ String& RefStorage::dumpSplit(String& buf, const String& str, unsigned int lineL
|
|||
const char* src = str.c_str();
|
||||
src = copyInc(dest,src,firstLineLen);
|
||||
for (; nFullLines; nFullLines--) {
|
||||
copyInc(dest,linePrefix,linePrefLen,true);
|
||||
copyInc(dest,linePrefix,linePrefLen);
|
||||
src = copyInc(dest,src,lineLen);
|
||||
}
|
||||
if (lastLineLen) {
|
||||
copyInc(dest,linePrefix,linePrefLen,true);
|
||||
copyInc(dest,linePrefix,linePrefLen);
|
||||
src = copyInc(dest,src,lastLineLen);
|
||||
}
|
||||
copyInc(dest,suffix,suffixLen);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
@ -237,8 +237,8 @@ int Message::commonDecode(const char* str, int offs)
|
|||
MessageHandler::MessageHandler(const char* name, unsigned priority,
|
||||
const char* trackName, bool addPriority)
|
||||
: String(name),
|
||||
m_trackName(trackName), m_trackNameOnly(trackName), m_priority(priority),
|
||||
m_dispatcher(0), m_filter(0), m_counter(0)
|
||||
m_trackName(trackName), m_priority(priority),
|
||||
m_unsafe(0), m_dispatcher(0), m_filter(0), m_filterRegexp(0), m_counter(0)
|
||||
{
|
||||
DDebug(DebugAll,"MessageHandler::MessageHandler('%s',%u,'%s',%s) [%p]",
|
||||
name,priority,trackName,String::boolText(addPriority),this);
|
||||
|
@ -271,12 +271,9 @@ void MessageHandler::destruct()
|
|||
|
||||
void MessageHandler::safeNowInternal()
|
||||
{
|
||||
WLock lck(m_dispatcher ? &m_dispatcher->handlersLock() : 0);
|
||||
Lock lock(m_dispatcher);
|
||||
// when the unsafe counter reaches zero we're again safe to destroy
|
||||
int v = --m_unsafe;
|
||||
if (v < 0)
|
||||
Debug(DebugFail,"MessageHandler(%s) unsafe=%d dispatcher=(%p) [%p]",
|
||||
safe(),v,m_dispatcher,this);
|
||||
m_unsafe--;
|
||||
}
|
||||
|
||||
bool MessageHandler::receivedInternal(Message& msg)
|
||||
|
@ -288,23 +285,19 @@ bool MessageHandler::receivedInternal(Message& msg)
|
|||
|
||||
void MessageHandler::setFilter(NamedString* filter)
|
||||
{
|
||||
Regexp* r = YOBJECT(Regexp,filter);
|
||||
if (r)
|
||||
setFilter(new MatchingItemRegexp(filter->name(),*r));
|
||||
else if (filter)
|
||||
setFilter(new MatchingItemString(filter->name(),*filter));
|
||||
else
|
||||
clearFilter();
|
||||
TelEngine::destruct(filter);
|
||||
clearFilter();
|
||||
m_filter = filter;
|
||||
m_filterRegexp = YOBJECT(Regexp,filter);
|
||||
}
|
||||
|
||||
void MessageHandler::clearFilter()
|
||||
{
|
||||
if (!m_filter)
|
||||
return;
|
||||
MatchingItemBase* tmp = m_filter;
|
||||
m_filter = 0;
|
||||
TelEngine::destruct(tmp);
|
||||
if (m_filter) {
|
||||
NamedString* tmp = m_filter;
|
||||
m_filter = 0;
|
||||
m_filterRegexp = 0;
|
||||
delete tmp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -318,8 +311,8 @@ bool MessageRelay::receivedInternal(Message& msg)
|
|||
|
||||
|
||||
MessageDispatcher::MessageDispatcher(const char* trackParam)
|
||||
: m_handlersLock("DispatcherHandlers"), m_messagesLock("DispatcherMsgs"),
|
||||
m_hooksLock("DispatcherHooks"),
|
||||
: Mutex(false,"MessageDispatcher"),
|
||||
m_hookMutex(false,"PostHooks"),
|
||||
m_msgAppend(&m_messages), m_hookAppend(&m_hooks),
|
||||
m_trackParam(trackParam), m_changes(0), m_warnTime(0),
|
||||
m_enqueueCount(0), m_dequeueCount(0), m_dispatchCount(0),
|
||||
|
@ -333,16 +326,9 @@ MessageDispatcher::MessageDispatcher(const char* trackParam)
|
|||
MessageDispatcher::~MessageDispatcher()
|
||||
{
|
||||
XDebug(DebugInfo,"MessageDispatcher::~MessageDispatcher() [%p]",this);
|
||||
lock();
|
||||
clear();
|
||||
}
|
||||
|
||||
void MessageDispatcher::clear()
|
||||
{
|
||||
WLock lck(m_handlersLock);
|
||||
m_handlers.clear();
|
||||
lck.acquire(m_hooksLock);
|
||||
m_hookAppend = &m_hooks;
|
||||
m_hooks.clear();
|
||||
unlock();
|
||||
}
|
||||
|
||||
bool MessageDispatcher::install(MessageHandler* handler)
|
||||
|
@ -350,7 +336,7 @@ bool MessageDispatcher::install(MessageHandler* handler)
|
|||
DDebug(DebugAll,"MessageDispatcher::install(%p)",handler);
|
||||
if (!handler)
|
||||
return false;
|
||||
WLock lck(m_handlersLock);
|
||||
Lock lock(this);
|
||||
ObjList *l = m_handlers.find(handler);
|
||||
if (l)
|
||||
return false;
|
||||
|
@ -386,7 +372,7 @@ bool MessageDispatcher::install(MessageHandler* handler)
|
|||
bool MessageDispatcher::uninstall(MessageHandler* handler)
|
||||
{
|
||||
DDebug(DebugAll,"MessageDispatcher::uninstall(%p)",handler);
|
||||
WLock lck(m_handlersLock);
|
||||
lock();
|
||||
handler = static_cast<MessageHandler *>(m_handlers.remove(handler,false));
|
||||
if (handler) {
|
||||
m_changes++;
|
||||
|
@ -395,15 +381,16 @@ bool MessageDispatcher::uninstall(MessageHandler* handler)
|
|||
handler,handler->c_str());
|
||||
// wait until handler is again safe to destroy
|
||||
do {
|
||||
lck.drop();
|
||||
unlock();
|
||||
Thread::yield();
|
||||
lck.acquire(m_handlersLock);
|
||||
lock();
|
||||
} while (handler->m_unsafe > 0);
|
||||
}
|
||||
if (handler->m_unsafe != 0)
|
||||
Debug(DebugFail,"MessageHandler %p has unsafe=%d",handler,(int)handler->m_unsafe);
|
||||
Debug(DebugFail,"MessageHandler %p has unsafe=%d",handler,handler->m_unsafe);
|
||||
handler->m_dispatcher = 0;
|
||||
}
|
||||
unlock();
|
||||
return (handler != 0);
|
||||
}
|
||||
|
||||
|
@ -429,13 +416,19 @@ bool MessageDispatcher::dispatch(Message& msg)
|
|||
unsigned int hTrackPos = 0;
|
||||
bool hTrackTime = m_traceHandlerTime;
|
||||
ObjList *l = &m_handlers;
|
||||
RLock lck(m_handlersLock);
|
||||
Lock mylock(this);
|
||||
m_dispatchCount++;
|
||||
for (; l; l=l->next()) {
|
||||
MessageHandler *h = static_cast<MessageHandler*>(l->get());
|
||||
if (h && (h->null() || *h == msg)) {
|
||||
if (h->filter() && !h->filter()->matchListParam(msg))
|
||||
continue;
|
||||
if (h->filter()) {
|
||||
if (h->filterRegexp()) {
|
||||
if (!h->filterRegexp()->matches(msg.getValue(h->filter()->name())))
|
||||
continue;
|
||||
}
|
||||
else if (*(h->filter()) != msg[h->filter()->name()])
|
||||
continue;
|
||||
}
|
||||
if (counting)
|
||||
Thread::setCurrentObjCounter(h->objectsCounter());
|
||||
|
||||
|
@ -454,7 +447,7 @@ bool MessageDispatcher::dispatch(Message& msg)
|
|||
}
|
||||
// mark handler as unsafe to destroy / uninstall
|
||||
h->m_unsafe++;
|
||||
lck.drop();
|
||||
mylock.drop();
|
||||
|
||||
u_int64_t tm = (m_warnTime || hTrackTime) ? Time::now() : 0;
|
||||
|
||||
|
@ -463,7 +456,7 @@ bool MessageDispatcher::dispatch(Message& msg)
|
|||
if (tm) {
|
||||
tm = Time::now() - tm;
|
||||
if (m_warnTime && tm > m_warnTime) {
|
||||
lck.acquire(m_handlersLock);
|
||||
mylock.acquire(this);
|
||||
const char* name = (c == m_changes) ? h->trackName().c_str() : 0;
|
||||
Debug(DebugInfo,"Message '%s' [%p] passed through %p%s%s%s in " FMT64U " usec",
|
||||
msg.c_str(),&msg,h,
|
||||
|
@ -489,7 +482,7 @@ bool MessageDispatcher::dispatch(Message& msg)
|
|||
|
||||
if (retv && !msg.broadcast())
|
||||
break;
|
||||
lck.acquire(m_handlersLock);
|
||||
mylock.acquire(this);
|
||||
if (c == m_changes)
|
||||
continue;
|
||||
// the handler list has changed - find again
|
||||
|
@ -518,7 +511,7 @@ bool MessageDispatcher::dispatch(Message& msg)
|
|||
break;
|
||||
}
|
||||
}
|
||||
lck.drop();
|
||||
mylock.drop();
|
||||
if (counting)
|
||||
Thread::setCurrentObjCounter(msg.getObjCounter());
|
||||
msg.dispatched(retv);
|
||||
|
@ -541,7 +534,7 @@ bool MessageDispatcher::dispatch(Message& msg)
|
|||
}
|
||||
}
|
||||
|
||||
lck.acquire(m_hooksLock);
|
||||
m_hookMutex.lock();
|
||||
if (m_hookHole && !m_hookCount) {
|
||||
// compact the list, remove the holes
|
||||
for (l = &m_hooks; l; l = l->next()) {
|
||||
|
@ -559,16 +552,16 @@ bool MessageDispatcher::dispatch(Message& msg)
|
|||
for (l = m_hooks.skipNull(); l; l = l->skipNext()) {
|
||||
RefPointer<MessagePostHook> ph = static_cast<MessagePostHook*>(l->get());
|
||||
if (ph) {
|
||||
lck.drop();
|
||||
m_hookMutex.unlock();
|
||||
if (counting)
|
||||
Thread::setCurrentObjCounter(ph->getObjCounter());
|
||||
ph->dispatched(msg,retv);
|
||||
ph = 0;
|
||||
lck.acquire(m_hooksLock);
|
||||
m_hookMutex.lock();
|
||||
}
|
||||
}
|
||||
m_hookCount--;
|
||||
lck.drop();
|
||||
m_hookMutex.unlock();
|
||||
if (counting)
|
||||
Thread::setCurrentObjCounter(saved);
|
||||
|
||||
|
@ -577,7 +570,7 @@ bool MessageDispatcher::dispatch(Message& msg)
|
|||
|
||||
bool MessageDispatcher::enqueue(Message* msg)
|
||||
{
|
||||
WLock lck(m_messagesLock);
|
||||
Lock lock(this);
|
||||
if (!msg || m_messages.find(msg))
|
||||
return false;
|
||||
if (m_traceTime)
|
||||
|
@ -591,17 +584,19 @@ bool MessageDispatcher::enqueue(Message* msg)
|
|||
|
||||
bool MessageDispatcher::dequeueOne()
|
||||
{
|
||||
WLock lck(m_messagesLock);
|
||||
lock();
|
||||
if (m_messages.next() == m_msgAppend)
|
||||
m_msgAppend = &m_messages;
|
||||
Message* msg = static_cast<Message *>(m_messages.remove(false));
|
||||
if (msg) {
|
||||
m_dequeueCount++;
|
||||
uint64_t age = Time::now() - msg->msgTime();
|
||||
if (age < 60000000)
|
||||
m_msgAvgAge = (3 * m_msgAvgAge + age) >> 2;
|
||||
}
|
||||
unlock();
|
||||
if (!msg)
|
||||
return false;
|
||||
m_dequeueCount++;
|
||||
uint64_t age = Time::now() - msg->msgTime();
|
||||
if (age < 60000000)
|
||||
m_msgAvgAge = (3 * m_msgAvgAge + age) >> 2;
|
||||
lck.drop();
|
||||
dispatch(*msg);
|
||||
msg->destruct();
|
||||
return true;
|
||||
|
@ -615,35 +610,35 @@ void MessageDispatcher::dequeue()
|
|||
|
||||
unsigned int MessageDispatcher::messageCount()
|
||||
{
|
||||
RLock lck(m_messagesLock);
|
||||
Lock lock(this);
|
||||
return (unsigned int)(m_enqueueCount - m_dequeueCount);
|
||||
}
|
||||
|
||||
unsigned int MessageDispatcher::handlerCount()
|
||||
{
|
||||
RLock lck(m_handlersLock);
|
||||
Lock lock(this);
|
||||
return m_handlers.count();
|
||||
}
|
||||
|
||||
unsigned int MessageDispatcher::postHookCount()
|
||||
{
|
||||
RLock lck(m_hooksLock);
|
||||
Lock lock(m_hookMutex);
|
||||
return m_hooks.count();
|
||||
}
|
||||
|
||||
void MessageDispatcher::getStats(u_int64_t& enqueued, u_int64_t& dequeued, u_int64_t& dispatched, u_int64_t& queueMax)
|
||||
{
|
||||
RLock lck(m_messagesLock);
|
||||
lock();
|
||||
enqueued = m_enqueueCount;
|
||||
dequeued = m_dequeueCount;
|
||||
queueMax = m_queuedMax;
|
||||
lck.acquire(m_handlersLock);
|
||||
dispatched = m_dispatchCount;
|
||||
queueMax = m_queuedMax;
|
||||
unlock();
|
||||
}
|
||||
|
||||
void MessageDispatcher::setHook(MessagePostHook* hook, bool remove)
|
||||
{
|
||||
WLock lck(m_hooksLock);
|
||||
m_hookMutex.lock();
|
||||
if (remove) {
|
||||
// zero the hook, we'll compact it later when safe
|
||||
ObjList* l = m_hooks.find(hook);
|
||||
|
@ -654,30 +649,7 @@ void MessageDispatcher::setHook(MessagePostHook* hook, bool remove)
|
|||
}
|
||||
else
|
||||
m_hookAppend = m_hookAppend->append(hook);
|
||||
}
|
||||
|
||||
unsigned int MessageDispatcher::fillHandlersInfo(bool byName, const String& match,
|
||||
String* details, unsigned int* total)
|
||||
{
|
||||
unsigned int n = 0;
|
||||
unsigned int matched = 0;
|
||||
String tmp;
|
||||
RLock lck(m_handlersLock);
|
||||
for (ObjList* o = m_handlers.skipNull(); o; o = o->skipNext()) {
|
||||
n++;
|
||||
MessageHandler *h = static_cast<MessageHandler*>(o->get());
|
||||
if (!match.matches(byName ? (const String&)(*h): h->trackNameOnly()))
|
||||
continue;
|
||||
matched++;
|
||||
if (!details)
|
||||
continue;
|
||||
tmp.printf("%s=%u|%s|%s",h->safe(),h->priority(),h->trackNameOnly().safe(),
|
||||
h->filter() ? "yes" : "no");
|
||||
details->append(tmp,",");
|
||||
}
|
||||
if (total)
|
||||
*total = n;
|
||||
return matched;
|
||||
m_hookMutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
488
engine/Mutex.cpp
488
engine/Mutex.cpp
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
@ -43,7 +43,6 @@ extern int pthread_mutexattr_settype(pthread_mutexattr_t *__attr, int __kind) __
|
|||
|
||||
typedef pthread_mutex_t HMUTEX;
|
||||
typedef sem_t HSEMAPHORE;
|
||||
typedef pthread_rwlock_t rwlock_t;
|
||||
|
||||
#endif /* ! _WINDOWS */
|
||||
|
||||
|
@ -56,32 +55,7 @@ typedef pthread_rwlock_t rwlock_t;
|
|||
|
||||
namespace TelEngine {
|
||||
|
||||
class LockablePrivateBase
|
||||
{
|
||||
public:
|
||||
inline LockablePrivateBase(const char* name)
|
||||
: m_name(name ? name : ""), m_owner(0), m_ownerName(0)
|
||||
{}
|
||||
inline const char* name() const
|
||||
{ return m_name; }
|
||||
inline Thread* owner() const
|
||||
{ return m_owner; }
|
||||
inline const char* ownerName() const
|
||||
{ return m_ownerName; }
|
||||
|
||||
protected:
|
||||
inline void setOwner(Thread* th = 0) {
|
||||
m_owner = th;
|
||||
m_ownerName = th ? th->name() : "";
|
||||
}
|
||||
private:
|
||||
const char* m_name;
|
||||
Thread* m_owner;
|
||||
const char* m_ownerName;
|
||||
};
|
||||
|
||||
class MutexPrivate : public LockablePrivateBase
|
||||
{
|
||||
class MutexPrivate {
|
||||
public:
|
||||
MutexPrivate(bool recursive, const char* name);
|
||||
~MutexPrivate();
|
||||
|
@ -91,6 +65,10 @@ public:
|
|||
{ if (!--m_refcount) delete this; }
|
||||
inline bool recursive() const
|
||||
{ return m_recursive; }
|
||||
inline const char* name() const
|
||||
{ return m_name; }
|
||||
inline const char* owner() const
|
||||
{ return m_owner; }
|
||||
bool locked() const
|
||||
{ return (m_locked > 0); }
|
||||
bool lock(long maxwait);
|
||||
|
@ -103,6 +81,8 @@ private:
|
|||
volatile unsigned int m_locked;
|
||||
volatile unsigned int m_waiting;
|
||||
bool m_recursive;
|
||||
const char* m_name;
|
||||
const char* m_owner;
|
||||
};
|
||||
|
||||
class SemaphorePrivate {
|
||||
|
@ -129,40 +109,6 @@ private:
|
|||
const char* m_name;
|
||||
};
|
||||
|
||||
class RWLockPrivate : public LockablePrivateBase
|
||||
{
|
||||
public:
|
||||
RWLockPrivate(const char* name);
|
||||
~RWLockPrivate();
|
||||
inline void ref()
|
||||
{ ++m_refcount; }
|
||||
inline void deref()
|
||||
{ if (!--m_refcount) delete this; }
|
||||
inline Thread* owner() const
|
||||
{ return m_nonRWLck ? m_nonRWLck->owner() : LockablePrivateBase::owner(); }
|
||||
inline const char* ownerName() const
|
||||
{ return m_nonRWLck ? m_nonRWLck->ownerName() : LockablePrivateBase::ownerName(); }
|
||||
inline bool locked() const
|
||||
{ return m_nonRWLck ? m_nonRWLck->locked() : (m_locked > 0); }
|
||||
bool readLock(long maxWait = -1);
|
||||
bool writeLock(long maxWwait = -1);
|
||||
bool unlock();
|
||||
static volatile int s_count;
|
||||
static volatile int s_locks;
|
||||
private:
|
||||
#ifdef _WINDOWS
|
||||
// we use m_nonRWLck
|
||||
#else
|
||||
rwlock_t m_lock;
|
||||
#endif
|
||||
MutexPrivate* m_nonRWLck;
|
||||
int m_refcount;
|
||||
unsigned int m_locked;
|
||||
#ifndef ATOMIC_OPS
|
||||
Mutex m_mutex;
|
||||
#endif
|
||||
};
|
||||
|
||||
class GlobalMutex {
|
||||
public:
|
||||
GlobalMutex();
|
||||
|
@ -183,18 +129,11 @@ static GlobalMutex s_global;
|
|||
static unsigned long s_maxwait = 0;
|
||||
static bool s_unsafe = MUTEX_STATIC_UNSAFE;
|
||||
static bool s_safety = false;
|
||||
#ifdef _WINDOWS
|
||||
static bool s_rwLockDisabled = true;
|
||||
#else
|
||||
static bool s_rwLockDisabled = false;
|
||||
#endif
|
||||
|
||||
volatile int MutexPrivate::s_count = 0;
|
||||
volatile int MutexPrivate::s_locks = 0;
|
||||
volatile int SemaphorePrivate::s_count = 0;
|
||||
volatile int SemaphorePrivate::s_locks = 0;
|
||||
volatile int RWLockPrivate::s_count = 0;
|
||||
volatile int RWLockPrivate::s_locks = 0;
|
||||
bool GlobalMutex::s_init = true;
|
||||
|
||||
// WARNING!!!
|
||||
|
@ -247,8 +186,8 @@ void GlobalMutex::unlock()
|
|||
|
||||
|
||||
MutexPrivate::MutexPrivate(bool recursive, const char* name)
|
||||
: LockablePrivateBase(name),
|
||||
m_refcount(1), m_locked(0), m_waiting(0), m_recursive(recursive)
|
||||
: m_refcount(1), m_locked(0), m_waiting(0), m_recursive(recursive),
|
||||
m_name(name), m_owner(0)
|
||||
{
|
||||
GlobalMutex::lock();
|
||||
s_count++;
|
||||
|
@ -293,11 +232,11 @@ MutexPrivate::~MutexPrivate()
|
|||
#endif
|
||||
GlobalMutex::unlock();
|
||||
if (m_locked || m_waiting)
|
||||
Debug(DebugFail,"MutexPrivate '%s' owned by '%s' (%p) destroyed with %u locks, %u waiting [%p]",
|
||||
name(),ownerName(),owner(),m_locked,m_waiting,this);
|
||||
Debug(DebugFail,"MutexPrivate '%s' owned by '%s' destroyed with %u locks, %u waiting [%p]",
|
||||
m_name,m_owner,m_locked,m_waiting,this);
|
||||
else if (warn)
|
||||
Debug(DebugCrit,"MutexPrivate '%s' owned by '%s' (%p) unlocked in destructor [%p]",
|
||||
name(),ownerName(),owner(),this);
|
||||
Debug(DebugCrit,"MutexPrivate '%s' owned by '%s' unlocked in destructor [%p]",
|
||||
m_name,m_owner,this);
|
||||
}
|
||||
|
||||
bool MutexPrivate::lock(long maxwait)
|
||||
|
@ -368,16 +307,18 @@ bool MutexPrivate::lock(long maxwait)
|
|||
if (safety)
|
||||
s_locks++;
|
||||
m_locked++;
|
||||
setOwner(thr);
|
||||
if (thr)
|
||||
if (thr) {
|
||||
thr->m_locks++;
|
||||
m_owner = thr->name();
|
||||
}
|
||||
else
|
||||
m_owner = 0;
|
||||
}
|
||||
if (safety)
|
||||
GlobalMutex::unlock();
|
||||
if (warn && !rval)
|
||||
Debug(DebugFail,
|
||||
"Thread '%s' could not lock mutex '%s' owned by '%s' (%p) waited by %u others for %lu usec!",
|
||||
Thread::currentName(),name(),ownerName(),owner(),m_waiting,maxwait);
|
||||
Debug(DebugFail,"Thread '%s' could not lock mutex '%s' owned by '%s' waited by %u others for %lu usec!",
|
||||
Thread::currentName(),m_name,m_owner,m_waiting,maxwait);
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
@ -393,10 +334,11 @@ bool MutexPrivate::unlock()
|
|||
if (thr)
|
||||
thr->m_locks--;
|
||||
if (!--m_locked) {
|
||||
if (thr != owner())
|
||||
Debug(DebugFail,"MutexPrivate '%s' unlocked by '%s' (%p) but owned by '%s' (%p) [%p]",
|
||||
name(),thr ? thr->name() : "",thr,ownerName(),owner(),this);
|
||||
setOwner();
|
||||
const char* tname = thr ? thr->name() : 0;
|
||||
if (tname != m_owner)
|
||||
Debug(DebugFail,"MutexPrivate '%s' unlocked by '%s' but owned by '%s' [%p]",
|
||||
m_name,tname,m_owner,this);
|
||||
m_owner = 0;
|
||||
}
|
||||
if (safety) {
|
||||
int locks = --s_locks;
|
||||
|
@ -413,10 +355,10 @@ bool MutexPrivate::unlock()
|
|||
ok = s_unsafe || !::pthread_mutex_unlock(&m_mutex);
|
||||
#endif
|
||||
if (!ok)
|
||||
Debug(DebugFail,"Failed to unlock mutex '%s' [%p]",name(),this);
|
||||
Debug(DebugFail,"Failed to unlock mutex '%s' [%p]",m_name,this);
|
||||
}
|
||||
else
|
||||
Debug(DebugFail,"MutexPrivate::unlock called on unlocked '%s' [%p]",name(),this);
|
||||
Debug(DebugFail,"MutexPrivate::unlock called on unlocked '%s' [%p]",m_name,this);
|
||||
if (safety)
|
||||
GlobalMutex::unlock();
|
||||
return ok;
|
||||
|
@ -664,7 +606,7 @@ bool Mutex::locked() const
|
|||
|
||||
const char* Mutex::owner() const
|
||||
{
|
||||
return m_private ? m_private->ownerName() : static_cast<const char*>(0);
|
||||
return m_private ? m_private->owner() : static_cast<const char*>(0);
|
||||
}
|
||||
|
||||
int Mutex::count()
|
||||
|
@ -827,376 +769,4 @@ void Lock2::drop()
|
|||
mx1->unlock();
|
||||
}
|
||||
|
||||
RWLockPrivate::RWLockPrivate(const char* name)
|
||||
: LockablePrivateBase(name),
|
||||
m_nonRWLck(0), m_refcount(1), m_locked(0)
|
||||
#ifndef ATOMIC_OPS
|
||||
, m_mutex(true,"RWLockPrivate")
|
||||
#endif
|
||||
{
|
||||
if (s_rwLockDisabled) {
|
||||
m_nonRWLck = new MutexPrivate(true,name);
|
||||
return;
|
||||
}
|
||||
|
||||
GlobalMutex::lock();
|
||||
s_count++;
|
||||
#ifdef _WINDOWS
|
||||
// not implemented, uses m_nonRWLck
|
||||
#else
|
||||
::pthread_rwlock_init(&m_lock,0);
|
||||
#endif
|
||||
GlobalMutex::unlock();
|
||||
}
|
||||
|
||||
RWLockPrivate::~RWLockPrivate()
|
||||
{
|
||||
if (m_nonRWLck) {
|
||||
delete m_nonRWLck;
|
||||
m_nonRWLck = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
bool warn = false;
|
||||
GlobalMutex::lock();
|
||||
if (m_locked) {
|
||||
warn = true;
|
||||
--m_locked;
|
||||
if (s_safety)
|
||||
--s_locks;
|
||||
#ifdef _WINDOWS
|
||||
// not implemented, uses m_nonRWLck
|
||||
#else
|
||||
::pthread_rwlock_unlock(&m_lock);
|
||||
#endif
|
||||
}
|
||||
s_count--;
|
||||
#ifdef _WINDOWS
|
||||
// not implemented, uses m_nonRWLck
|
||||
#else
|
||||
::pthread_rwlock_destroy(&m_lock);
|
||||
#endif
|
||||
GlobalMutex::unlock();
|
||||
if (m_locked)
|
||||
Debug(DebugFail,"RWLockPrivate '%s' owned by '%s' (%p) destroyed with %u locks [%p]",
|
||||
name(),ownerName(),owner(),m_locked,this);
|
||||
else if (warn)
|
||||
Debug(DebugCrit,"RWLockPrivate '%s' owned by '%s' (%p) unlocked in destructor [%p]",
|
||||
name(),ownerName(),owner(),this);
|
||||
}
|
||||
|
||||
bool RWLockPrivate::readLock(long maxwait)
|
||||
{
|
||||
if (m_nonRWLck)
|
||||
return m_nonRWLck->lock(maxwait);
|
||||
|
||||
int ret = -1;
|
||||
bool warn = false;
|
||||
if (s_maxwait && (maxwait < 0)) {
|
||||
maxwait = (long)s_maxwait;
|
||||
warn = true;
|
||||
}
|
||||
bool safety = s_safety;
|
||||
if (safety)
|
||||
GlobalMutex::lock();
|
||||
Thread* thr = Thread::current();
|
||||
if (thr)
|
||||
thr->m_locking = true;
|
||||
if (safety)
|
||||
GlobalMutex::unlock();
|
||||
|
||||
#ifdef _WINDOWS
|
||||
// not implemented, uses m_nonRWLck
|
||||
#else
|
||||
if (s_unsafe)
|
||||
ret = 0;
|
||||
if (maxwait < 0)
|
||||
ret = ::pthread_rwlock_rdlock(&m_lock);
|
||||
else if (!maxwait)
|
||||
ret = ::pthread_rwlock_tryrdlock(&m_lock);
|
||||
else {
|
||||
u_int64_t t = Time::now() + maxwait;
|
||||
#ifdef HAVE_TIMEDRDLOCK
|
||||
struct timeval tv;
|
||||
struct timespec ts;
|
||||
Time::toTimeval(&tv,t);
|
||||
ts.tv_sec = tv.tv_sec;
|
||||
ts.tv_nsec = 1000 * tv.tv_usec;
|
||||
ret = ::pthread_rwlock_timedrdlock(&m_lock,&ts);
|
||||
#else
|
||||
bool dead = false;
|
||||
do {
|
||||
if (!dead) {
|
||||
dead = Thread::check(false);
|
||||
// give up only if caller asked for a limited wait
|
||||
if (dead && !warn)
|
||||
break;
|
||||
}
|
||||
ret = ::pthread_rwlock_tryrdlock(&m_lock);
|
||||
if (!ret)
|
||||
break;
|
||||
Thread::yield();
|
||||
} while (t > Time::now());
|
||||
#endif
|
||||
}
|
||||
#endif // _WINDOWS
|
||||
if (safety)
|
||||
GlobalMutex::lock();
|
||||
if (thr)
|
||||
thr->m_locking = false;
|
||||
if (!ret) {
|
||||
if (safety)
|
||||
++s_locks;
|
||||
#ifdef ATOMIC_OPS
|
||||
#ifdef _WINDOWS
|
||||
InterlockedIncrement((LONG*)&m_locked);
|
||||
#else
|
||||
__sync_add_and_fetch(&m_locked,1);
|
||||
#endif
|
||||
#else
|
||||
m_mutex.lock();
|
||||
++m_locked;
|
||||
m_mutex.unlock();
|
||||
#endif
|
||||
if (thr)
|
||||
++thr->m_locks;
|
||||
}
|
||||
if (safety)
|
||||
GlobalMutex::unlock();
|
||||
if (warn && ret)
|
||||
Debug(DebugFail,"Thread '%s' could not lock for read RW lock '%s'"
|
||||
" writing-owned by '%s' (%p) after waiting for %ld usec! [%p]",
|
||||
Thread::currentName(),name(),ownerName(),owner(),maxwait,this);
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
bool RWLockPrivate::writeLock(long maxwait)
|
||||
{
|
||||
if (m_nonRWLck)
|
||||
return m_nonRWLck->lock(maxwait);
|
||||
|
||||
int ret = -1;
|
||||
bool warn = false;
|
||||
if (s_maxwait && (maxwait < 0)) {
|
||||
maxwait = (long)s_maxwait;
|
||||
warn = true;
|
||||
}
|
||||
bool safety = s_safety;
|
||||
if (safety)
|
||||
GlobalMutex::lock();
|
||||
Thread* thr = Thread::current();
|
||||
if (thr)
|
||||
thr->m_locking = true;
|
||||
if (safety)
|
||||
GlobalMutex::unlock();
|
||||
#ifdef _WINDOWS
|
||||
// not implemented, uses m_nonRWLck
|
||||
#else
|
||||
if (s_unsafe)
|
||||
ret = 0;
|
||||
if (maxwait < 0)
|
||||
ret = ::pthread_rwlock_wrlock(&m_lock);
|
||||
else if (!maxwait)
|
||||
ret = ::pthread_rwlock_trywrlock(&m_lock);
|
||||
else {
|
||||
u_int64_t t = Time::now() + maxwait;
|
||||
#ifdef HAVE_TIMEDWRLOCK
|
||||
struct timeval tv;
|
||||
struct timespec ts;
|
||||
Time::toTimeval(&tv,t);
|
||||
ts.tv_sec = tv.tv_sec;
|
||||
ts.tv_nsec = 1000 * tv.tv_usec;
|
||||
ret = ::pthread_rwlock_timedwrlock(&m_lock,&ts);
|
||||
#else
|
||||
bool dead = false;
|
||||
do {
|
||||
if (!dead) {
|
||||
dead = Thread::check(false);
|
||||
// give up only if caller asked for a limited wait
|
||||
if (dead && !warn)
|
||||
break;
|
||||
}
|
||||
ret = ::pthread_rwlock_trywrlock(&m_lock);
|
||||
if (!ret)
|
||||
break;
|
||||
Thread::yield();
|
||||
} while (t > Time::now());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (safety)
|
||||
GlobalMutex::lock();
|
||||
if (thr)
|
||||
thr->m_locking = false;
|
||||
if (!ret) {
|
||||
if (safety)
|
||||
++s_locks;
|
||||
#ifdef ATOMIC_OPS
|
||||
#ifdef _WINDOWS
|
||||
InterlockedIncrement((LONG*)&m_locked);
|
||||
#else
|
||||
__sync_add_and_fetch(&m_locked,1);
|
||||
#endif
|
||||
#else
|
||||
m_mutex.lock();
|
||||
++m_locked;
|
||||
m_mutex.unlock();
|
||||
#endif
|
||||
setOwner(thr);
|
||||
if (thr)
|
||||
++thr->m_locks;
|
||||
}
|
||||
if (safety)
|
||||
GlobalMutex::unlock();
|
||||
if (warn && ret)
|
||||
Debug(DebugFail,"Thread '%s' could not lock for write RW lock '%s'"
|
||||
" writing-owned by '%s' (%p) after waiting for %ld usec! [%p]",
|
||||
Thread::currentName(),name(),ownerName(),name(),maxwait,this);
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
bool RWLockPrivate::unlock()
|
||||
{
|
||||
if (m_nonRWLck)
|
||||
return m_nonRWLck->unlock();
|
||||
|
||||
int ok = -1;
|
||||
bool safety = s_safety;
|
||||
if (safety)
|
||||
GlobalMutex::lock();
|
||||
|
||||
if (m_locked) {
|
||||
Thread* thr = Thread::current();
|
||||
if (thr)
|
||||
--thr->m_locks;
|
||||
#ifdef ATOMIC_OPS
|
||||
#ifdef _WINDOWS
|
||||
int l = InterlockedDecrement((LONG*)&m_locked);
|
||||
#else
|
||||
int l = __sync_sub_and_fetch(&m_locked,1);
|
||||
#endif
|
||||
#else
|
||||
m_mutex.lock();
|
||||
int l = --m_locked;
|
||||
m_mutex.unlock();
|
||||
#endif
|
||||
|
||||
if (!l) {
|
||||
if (owner() && owner() != thr)
|
||||
Debug(DebugFail,"RWLockPrivate '%s' unlocked by '%s' (%p) but owned by '%s' (%p) [%p]",
|
||||
name(),thr ? thr->name() : "",thr,ownerName(),owner(),this);
|
||||
setOwner();
|
||||
}
|
||||
if (safety) {
|
||||
int locks = --s_locks;
|
||||
if (locks < 0) {
|
||||
// this is very very bad - abort right now
|
||||
abortOnBug(true);
|
||||
s_locks = 0;
|
||||
Debug(DebugFail,"RWLockPrivate::locks() is %d [%p]",locks,this);
|
||||
}
|
||||
}
|
||||
#ifdef _WINDOWS
|
||||
// not implemented, uses m_nonRWLck
|
||||
#else
|
||||
ok = s_unsafe ? 0 : ::pthread_rwlock_unlock(&m_lock);
|
||||
#endif
|
||||
if (ok)
|
||||
Debug(DebugFail,"Thread '%s' failed to unlock RW lock '%s' owned by '%s' (%p) [%p]",
|
||||
Thread::currentName(),name(),ownerName(),owner(),this);
|
||||
}
|
||||
else {
|
||||
Debug(DebugFail,
|
||||
"Thread '%s' could not unlock already unlocked RW lock '%s' writing-owned by '%s' (%p) [%p]",
|
||||
Thread::currentName(),name(),ownerName(),owner(),this);
|
||||
}
|
||||
if (safety)
|
||||
GlobalMutex::unlock();
|
||||
return ok == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* class RWLock
|
||||
*/
|
||||
RWLock::RWLock(const char* name)
|
||||
{
|
||||
m_private = new RWLockPrivate(name ? name : "?");
|
||||
}
|
||||
|
||||
RWLock::RWLock(const RWLock& original)
|
||||
: Lockable(),
|
||||
m_private(original.privDataCopy())
|
||||
{
|
||||
}
|
||||
|
||||
RWLock::~RWLock()
|
||||
{
|
||||
RWLockPrivate* priv = m_private;
|
||||
m_private = 0;
|
||||
if (priv)
|
||||
priv->deref();
|
||||
}
|
||||
|
||||
bool RWLock::readLock(long maxwait)
|
||||
{
|
||||
return m_private && m_private->readLock(maxwait);
|
||||
}
|
||||
|
||||
bool RWLock::writeLock(long maxwait)
|
||||
{
|
||||
return m_private && m_private->writeLock(maxwait);
|
||||
}
|
||||
|
||||
bool RWLock::unlock()
|
||||
{
|
||||
return m_private && m_private->unlock();
|
||||
}
|
||||
|
||||
bool RWLock::locked() const
|
||||
{
|
||||
return m_private && m_private->locked();
|
||||
}
|
||||
|
||||
void RWLock::disableRWLock(bool disable)
|
||||
{
|
||||
#ifdef _WINDOWS
|
||||
// we disable RWLock usage as it is not implemented
|
||||
s_rwLockDisabled = true;
|
||||
#else
|
||||
s_rwLockDisabled = disable;
|
||||
#endif
|
||||
}
|
||||
|
||||
RWLockPrivate* RWLock::privDataCopy() const
|
||||
{
|
||||
if (m_private)
|
||||
m_private->ref();
|
||||
return m_private;
|
||||
}
|
||||
|
||||
|
||||
RWLockPool::RWLockPool(unsigned int len, const char* name)
|
||||
: m_name(0), m_data(0), m_length(len ? len : 1)
|
||||
{
|
||||
if (TelEngine::null(name))
|
||||
name = "Pool";
|
||||
m_name = new String[m_length];
|
||||
m_data = new RWLock*[m_length];
|
||||
for (unsigned int i = 0; i < m_length; i++) {
|
||||
m_name[i] << name << "::" << (i + 1);
|
||||
m_data[i] = new RWLock(m_name[i]);
|
||||
}
|
||||
}
|
||||
|
||||
RWLockPool::~RWLockPool()
|
||||
{
|
||||
if (m_data) {
|
||||
for (unsigned int i = 0; i < m_length; i++)
|
||||
delete m_data[i];
|
||||
delete[] m_data;
|
||||
}
|
||||
if (m_name)
|
||||
delete[] m_name;
|
||||
}
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
|
||||
#include "yateclass.h"
|
||||
#include "yatexml.h"
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
|
@ -37,7 +36,11 @@ NamedList::NamedList(const char* name)
|
|||
NamedList::NamedList(const NamedList& original)
|
||||
: String(original)
|
||||
{
|
||||
copyParams(false,original);
|
||||
ObjList* dest = &m_params;
|
||||
for (const ObjList* l = original.m_params.skipNull(); l; l = l->skipNext()) {
|
||||
const NamedString* p = static_cast<const NamedString*>(l->get());
|
||||
dest = dest->append(new NamedString(p->name(),*p));
|
||||
}
|
||||
}
|
||||
|
||||
NamedList::NamedList(const char* name, const NamedList& original, const String& prefix)
|
||||
|
@ -77,135 +80,30 @@ NamedList& NamedList::addParam(const char* name, const char* value, bool emptyOK
|
|||
return *this;
|
||||
}
|
||||
|
||||
NamedList& NamedList::setParam(NamedString* param)
|
||||
NamedList& NamedList::setParam(const String& name, const char* value)
|
||||
{
|
||||
XDebug(DebugAll,"NamedList::setParam(%p) [%p]",param,this);
|
||||
if (!param)
|
||||
return *this;
|
||||
ObjList* o = m_params.skipNull();
|
||||
while (o) {
|
||||
NamedString* s = static_cast<NamedString*>(o->get());
|
||||
if (s->name() == param->name()) {
|
||||
o->set(param);
|
||||
XDebug(DebugInfo,"NamedList::setParam(\"%s\",\"%s\")",name.c_str(),value);
|
||||
ObjList *p = m_params.skipNull();
|
||||
while (p) {
|
||||
NamedString *s = static_cast<NamedString*>(p->get());
|
||||
if (s->name() == name) {
|
||||
*s = value;
|
||||
return *this;
|
||||
}
|
||||
ObjList* next = o->skipNext();
|
||||
ObjList* next = p->skipNext();
|
||||
if (next)
|
||||
o = next;
|
||||
p = next;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (o)
|
||||
o->append(param);
|
||||
if (p)
|
||||
p->append(new NamedString(name,value));
|
||||
else
|
||||
m_params.append(param);
|
||||
return *this;
|
||||
m_params.append(new NamedString(name,value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
static inline NamedString* nlSetParamCreate(NamedList& list, const String& name, ObjList*& append)
|
||||
{
|
||||
append = list.paramList()->skipNull();
|
||||
while (append) {
|
||||
NamedString* ns = static_cast<NamedString*>(append->get());
|
||||
if (ns->name() == name) {
|
||||
append = 0;
|
||||
return ns;
|
||||
}
|
||||
ObjList* next = append->skipNext();
|
||||
if (!next)
|
||||
return new NamedString(name);
|
||||
append = next;
|
||||
}
|
||||
append = list.paramList();
|
||||
return new NamedString(name);
|
||||
}
|
||||
|
||||
NamedList& NamedList::setParam(const String& name, unsigned int flags, const TokenDict* tokens,
|
||||
bool unknownflag)
|
||||
{
|
||||
XDebug(DebugAll,"NamedList::setParam(%s) flags=%u tokens=%p unkFlag=%u [%p]",
|
||||
name.safe(),flags,tokens,unknownflag,this);
|
||||
ObjList* append = 0;
|
||||
NamedString* ns = nlSetParamCreate(*this,name,append);
|
||||
*static_cast<String*>(ns) = "";
|
||||
ns->decodeFlags(flags,tokens,unknownflag);
|
||||
if (append)
|
||||
append->append(ns);
|
||||
return *this;
|
||||
}
|
||||
|
||||
NamedList& NamedList::setParam(const String& name, uint64_t flags, const TokenDict64* tokens,
|
||||
bool unknownflag)
|
||||
{
|
||||
XDebug(DebugAll,"NamedList::setParam(%s) flags64=" FMT64U " tokens=%p unkFlag=%u [%p]",
|
||||
name.safe(),flags,tokens,unknownflag,this);
|
||||
ObjList* append = 0;
|
||||
NamedString* ns = nlSetParamCreate(*this,name,append);
|
||||
*static_cast<String*>(ns) = "";
|
||||
ns->decodeFlags(flags,tokens,unknownflag);
|
||||
if (append)
|
||||
append->append(ns);
|
||||
return *this;
|
||||
}
|
||||
|
||||
NamedList& NamedList::setParamHex(const String& name, const void* buf, unsigned int len, char sep)
|
||||
{
|
||||
XDebug(DebugAll,"NamedList::setParamHex(%s,%p,%u,%c) [%p]",name.safe(),buf,len,sep,this);
|
||||
ObjList* append = 0;
|
||||
NamedString* ns = nlSetParamCreate(*this,name,append);
|
||||
ns->hexify((void*)buf,len,sep);
|
||||
if (append)
|
||||
append->append(ns);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Obj> NamedList& nlSetParamValue(NamedList& list, const String& name, Obj& value)
|
||||
{
|
||||
ObjList* append = 0;
|
||||
NamedString* ns = nlSetParamCreate(list,name,append);
|
||||
*static_cast<String*>(ns) = value;
|
||||
if (append)
|
||||
append->append(ns);
|
||||
return list;
|
||||
}
|
||||
|
||||
NamedList& NamedList::setParam(const String& name, const char* value)
|
||||
{
|
||||
XDebug(DebugAll,"NamedList::setParam('%s','%s') [%p]",name.c_str(),value,this);
|
||||
return nlSetParamValue(*this,name,value);
|
||||
}
|
||||
|
||||
NamedList& NamedList::setParam(const String& name, int64_t value)
|
||||
{
|
||||
XDebug(DebugAll,"NamedList::setParam(%s) INT64=" FMT64 " [%p]",name.c_str(),value,this);
|
||||
return nlSetParamValue(*this,name,value);
|
||||
}
|
||||
|
||||
NamedList& NamedList::setParam(const String& name, uint64_t value)
|
||||
{
|
||||
XDebug(DebugAll,"NamedList::setParam(%s) UINT64=" FMT64U " [%p]",name.c_str(),value,this);
|
||||
return nlSetParamValue(*this,name,value);
|
||||
}
|
||||
|
||||
NamedList& NamedList::setParam(const String& name, int32_t value)
|
||||
{
|
||||
XDebug(DebugAll,"NamedList::setParam(%s) INT32=%d [%p]",name.c_str(),value,this);
|
||||
return nlSetParamValue(*this,name,value);
|
||||
}
|
||||
|
||||
NamedList& NamedList::setParam(const String& name, uint32_t value)
|
||||
{
|
||||
XDebug(DebugAll,"NamedList::setParam(%s) UINT32=%u [%p]",name.c_str(),value,this);
|
||||
return nlSetParamValue(*this,name,value);
|
||||
}
|
||||
|
||||
NamedList& NamedList::setParam(const String& name, double value)
|
||||
{
|
||||
XDebug(DebugAll,"NamedList::setParam(%s) DOUBLE=%f [%p]",name.c_str(),value,this);
|
||||
return nlSetParamValue(*this,name,value);
|
||||
}
|
||||
|
||||
NamedList& NamedList::clearParam(const String& name, char childSep, const String* value)
|
||||
NamedList& NamedList::clearParam(const String& name, char childSep)
|
||||
{
|
||||
XDebug(DebugInfo,"NamedList::clearParam(\"%s\",'%.1s')",
|
||||
name.c_str(),&childSep);
|
||||
|
@ -215,8 +113,7 @@ NamedList& NamedList::clearParam(const String& name, char childSep, const String
|
|||
ObjList *p = &m_params;
|
||||
while (p) {
|
||||
NamedString *s = static_cast<NamedString *>(p->get());
|
||||
if (s && ((s->name() == name) || s->name().startsWith(tmp))
|
||||
&& (!value || value->matches(*s)))
|
||||
if (s && ((s->name() == name) || s->name().startsWith(tmp)))
|
||||
p->remove();
|
||||
else
|
||||
p = p->next();
|
||||
|
@ -257,37 +154,12 @@ NamedList& NamedList::copyParam(const NamedList& original, const String& name, c
|
|||
return *this;
|
||||
}
|
||||
|
||||
static inline NamedString* nlCopyParam(const NamedString& param)
|
||||
NamedList& NamedList::copyParams(const NamedList& original)
|
||||
{
|
||||
NamedPointer* np = YOBJECT(NamedPointer,¶m);
|
||||
if (!(np && np->userData()))
|
||||
return 0;
|
||||
GenObject* ud = 0;
|
||||
#define NP_COPY_PARAM_INTERNAL(Type) \
|
||||
ud = YOBJECT(Type,np->userData()); \
|
||||
if (ud) \
|
||||
return new NamedPointer(np->name(),new Type(*(Type*)ud),*np)
|
||||
NP_COPY_PARAM_INTERNAL(DataBlock);
|
||||
NP_COPY_PARAM_INTERNAL(XmlElement);
|
||||
#undef NP_COPY_PARAM_INTERNAL
|
||||
return 0;
|
||||
}
|
||||
|
||||
NamedList& NamedList::copyParams(bool replace, const NamedList& original, bool copyUserData)
|
||||
{
|
||||
XDebug(DebugInfo,"NamedList::copyParams(%p,%u) [%p]",&original,replace,this);
|
||||
ObjList* append = replace ? 0 : &m_params;
|
||||
XDebug(DebugInfo,"NamedList::copyParams(%p) [%p]",&original,this);
|
||||
for (const ObjList* l = original.m_params.skipNull(); l; l = l->skipNext()) {
|
||||
const NamedString* p = static_cast<const NamedString*>(l->get());
|
||||
NamedString* ns = 0;
|
||||
if (copyUserData)
|
||||
ns = nlCopyParam(*p);
|
||||
if (!ns)
|
||||
ns = new NamedString(p->name(),*p);
|
||||
if (append)
|
||||
append = append->append(ns);
|
||||
else
|
||||
setParam(ns);
|
||||
setParam(p->name(),*p);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
|
||||
#include "yateclass.h"
|
||||
#include <string.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
|
@ -487,47 +486,6 @@ unsigned int ObjVector::assign(ObjList& list, bool move, unsigned int maxLen)
|
|||
return maxLen;
|
||||
}
|
||||
|
||||
static inline void clearObjVector(GenObject** objs, unsigned int len)
|
||||
{
|
||||
while (len--) {
|
||||
TelEngine::destruct(*objs);
|
||||
objs++;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ObjVector::resize(unsigned int len, bool keepData)
|
||||
{
|
||||
if (!len) {
|
||||
clear();
|
||||
return length();
|
||||
}
|
||||
if (len == length()) {
|
||||
if (!keepData) {
|
||||
if (m_delete)
|
||||
clearObjVector(m_objects,length());
|
||||
::memset(m_objects,0,length() * sizeof(GenObject*));
|
||||
}
|
||||
return length();
|
||||
}
|
||||
GenObject** buf = new GenObject*[len];
|
||||
if (!(keepData && length()))
|
||||
::memset(buf,0,len * sizeof(GenObject*));
|
||||
else if (len < length()) {
|
||||
::memcpy(buf,m_objects,len * sizeof(GenObject*));
|
||||
::memset(m_objects,0,len * sizeof(GenObject*));
|
||||
}
|
||||
else {
|
||||
::memcpy(buf,m_objects,length() * sizeof(GenObject*));
|
||||
::memset(m_objects,0,length() * sizeof(GenObject*));
|
||||
if (len > length())
|
||||
::memset(buf + length(),0,(len - length()) * sizeof(GenObject*));
|
||||
}
|
||||
clear();
|
||||
m_objects = buf;
|
||||
m_length = len;
|
||||
return length();
|
||||
}
|
||||
|
||||
unsigned int ObjVector::count() const
|
||||
{
|
||||
if (!m_objects)
|
||||
|
@ -600,8 +558,10 @@ void ObjVector::clear()
|
|||
unsigned int len = m_length;
|
||||
m_length = 0;
|
||||
m_objects = 0;
|
||||
if (m_delete && objs)
|
||||
clearObjVector(objs,len);
|
||||
if (m_delete && objs) {
|
||||
for (unsigned int i = 0; i < len; i++)
|
||||
TelEngine::destruct(objs[i]);
|
||||
}
|
||||
delete[] objs;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
@ -231,8 +231,6 @@ const TokenDict SocketAddr::s_familyName[] = {
|
|||
{0,0},
|
||||
};
|
||||
|
||||
const char* SocketAddr::s_ifaceNameExtraEscape = ".:[]";
|
||||
|
||||
SocketAddr::SocketAddr(const struct sockaddr* addr, socklen_t len)
|
||||
: m_address(0), m_length(0)
|
||||
{
|
||||
|
@ -267,9 +265,7 @@ void SocketAddr::clear()
|
|||
{
|
||||
m_length = 0;
|
||||
m_host.clear();
|
||||
m_iface.clear();
|
||||
m_addr.clear();
|
||||
m_addrFull.clear();
|
||||
void* tmp = m_address;
|
||||
m_address = 0;
|
||||
if (tmp)
|
||||
|
@ -436,15 +432,7 @@ bool SocketAddr::host(const String& name)
|
|||
}
|
||||
switch (family()) {
|
||||
case AF_INET:
|
||||
if (name.find('%') >= 0) {
|
||||
String tmp, ifc;
|
||||
splitIface(name,tmp,&ifc);
|
||||
if (host(tmp)) {
|
||||
iface(ifc,true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
{
|
||||
in_addr_t a = inet_addr(name);
|
||||
if (a == INADDR_NONE) {
|
||||
#ifdef HAVE_GHBN_R
|
||||
|
@ -470,7 +458,6 @@ bool SocketAddr::host(const String& name)
|
|||
}
|
||||
if (a != INADDR_NONE) {
|
||||
((struct sockaddr_in*)m_address)->sin_addr.s_addr = a;
|
||||
m_iface.clear();
|
||||
stringify();
|
||||
return true;
|
||||
}
|
||||
|
@ -479,30 +466,25 @@ bool SocketAddr::host(const String& name)
|
|||
#ifdef AF_INET6
|
||||
case AF_INET6:
|
||||
if (name.find('%') >= 0) {
|
||||
String tmp, ifc;
|
||||
splitIface(name,tmp,&ifc);
|
||||
String tmp, iface;
|
||||
splitIface(name,tmp,&iface);
|
||||
if (!host(tmp))
|
||||
break;
|
||||
if (ifc && iface(ifc,true)) {
|
||||
return false;
|
||||
if (iface)
|
||||
#ifndef _WINDOWS
|
||||
scopeId(if_nametoindex(m_iface));
|
||||
scopeId(if_nametoindex(iface));
|
||||
#else
|
||||
scopeId(m_iface.toInteger(0,0,0));
|
||||
scopeId(iface.toInteger(0,0,0));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
m_iface.clear();
|
||||
return true;
|
||||
}
|
||||
#ifdef HAVE_PTON
|
||||
if (inet_pton(family(),name,&((struct sockaddr_in6*)m_address)->sin6_addr) > 0) {
|
||||
m_iface.clear();
|
||||
stringify();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (resolveIPv6(m_address,name)) {
|
||||
m_iface.clear();
|
||||
stringify();
|
||||
return true;
|
||||
}
|
||||
|
@ -511,12 +493,11 @@ bool SocketAddr::host(const String& name)
|
|||
#ifdef HAS_AF_UNIX
|
||||
case AF_UNIX:
|
||||
if (name.length() >= (UNIX_PATH_MAX-1))
|
||||
break;
|
||||
return false;
|
||||
::strcpy(((struct sockaddr_un*)m_address)->sun_path,name.c_str());
|
||||
stringify();
|
||||
return true;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -527,20 +508,15 @@ int SocketAddr::family(const String& addr)
|
|||
if (!addr)
|
||||
return Unknown;
|
||||
bool ipv6 = false;
|
||||
int percent = -1;
|
||||
for (unsigned int i = 0; i < addr.length(); i++) {
|
||||
if (addr[i] == '/')
|
||||
return Unix;
|
||||
if (addr[i] == ':')
|
||||
ipv6 = true;
|
||||
else if (percent < 0 && addr[i] == '%')
|
||||
percent = i;
|
||||
}
|
||||
if (ipv6)
|
||||
return IPv6;
|
||||
if (!percent)
|
||||
return Unknown;
|
||||
in_addr_t a = percent < 0 ? inet_addr(addr) : inet_addr(addr.substr(0,percent));
|
||||
in_addr_t a = inet_addr(addr);
|
||||
if (a != INADDR_NONE || addr == YSTRING("255.255.255.255"))
|
||||
return IPv4;
|
||||
return Unknown;
|
||||
|
@ -607,23 +583,13 @@ int SocketAddr::copyAddr(uint8_t* buf, struct sockaddr* addr)
|
|||
}
|
||||
|
||||
// Append an address to a buffer
|
||||
String& SocketAddr::appendAddr(String& buf, const String& addr, int family, const String& iface)
|
||||
String& SocketAddr::appendAddr(String& buf, const String& addr, int family)
|
||||
{
|
||||
if (!addr)
|
||||
return buf;
|
||||
// Address already starts with [
|
||||
if (addr[0] == '[') {
|
||||
if (!iface)
|
||||
return buf << addr;
|
||||
char last = (addr[addr.length() - 1] == ']') ? ']' : 0;
|
||||
if (last)
|
||||
buf.append(addr.c_str(),addr.length() - 1);
|
||||
else
|
||||
buf << addr;
|
||||
buf << '%';
|
||||
escapeIface(buf,iface);
|
||||
if (last)
|
||||
buf << last;
|
||||
buf << addr;
|
||||
return buf;
|
||||
}
|
||||
if (family == Unknown) {
|
||||
|
@ -635,19 +601,10 @@ String& SocketAddr::appendAddr(String& buf, const String& addr, int family, cons
|
|||
family = IPv6;
|
||||
}
|
||||
}
|
||||
if (!iface) {
|
||||
if (family != IPv6)
|
||||
return buf << addr;
|
||||
return buf << '[' << addr << ']';
|
||||
}
|
||||
char last = (family != IPv6) ? 0 : ']';
|
||||
if (last)
|
||||
buf << '[' << addr << '%';
|
||||
if (family != IPv6)
|
||||
buf << addr;
|
||||
else
|
||||
buf << addr << '%';
|
||||
escapeIface(buf,iface);
|
||||
if (last)
|
||||
buf << last;
|
||||
buf << "[" << addr << "]";
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
@ -683,8 +640,8 @@ void SocketAddr::splitIface(const String& buf, String& addr, String* iface)
|
|||
}
|
||||
else {
|
||||
if (iface)
|
||||
iface->assign(buf.c_str() + pos + 1,buf.length() - pos - 1);
|
||||
addr.assign(buf.c_str(),pos);
|
||||
*iface = buf.substr(pos + 1);
|
||||
addr = buf.substr(0,pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -699,7 +656,7 @@ void SocketAddr::split(const String& buf, String& addr, int& port, bool portPres
|
|||
if (buf[0] == '[') {
|
||||
int p = buf.find(']',1);
|
||||
if (p >= 1) {
|
||||
if (buf[p + 1] == ':')
|
||||
if (p < ((int)buf.length() - 1) && buf[p + 1] == ':')
|
||||
port = buf.substr(p + 2).toInteger();
|
||||
addr.assign(buf.c_str() + 1,p - 1);
|
||||
return;
|
||||
|
@ -740,22 +697,15 @@ void SocketAddr::stringify()
|
|||
{
|
||||
m_host.clear();
|
||||
m_addr.clear();
|
||||
m_addrFull.clear();
|
||||
if (m_length && m_address)
|
||||
stringify(m_host,m_address);
|
||||
}
|
||||
|
||||
// Store host:port in m_addr
|
||||
void SocketAddr::updateAddr(bool full) const
|
||||
void SocketAddr::updateAddr() const
|
||||
{
|
||||
if (full) {
|
||||
m_addrFull.clear();
|
||||
appendTo(m_addrFull,host(),port(),family(),m_iface);
|
||||
}
|
||||
else {
|
||||
m_addr.clear();
|
||||
appendTo(m_addr,host(),port(),family());
|
||||
}
|
||||
m_addr.clear();
|
||||
appendTo(m_addr,host(),port(),family());
|
||||
}
|
||||
|
||||
int SocketAddr::port() const
|
||||
|
@ -790,7 +740,6 @@ bool SocketAddr::port(int newport)
|
|||
return false;
|
||||
}
|
||||
m_addr.clear();
|
||||
m_addrFull.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1624,24 +1573,6 @@ bool File::listDirectory(const char* path, ObjList* dirs, ObjList* files, int* e
|
|||
}
|
||||
|
||||
|
||||
unsigned int Socket::s_features = 0
|
||||
#ifdef IPPROTO_IPV6
|
||||
| FProtoIpv6
|
||||
#endif
|
||||
#ifdef IPV6_V6ONLY
|
||||
| FIpv6Only
|
||||
#endif
|
||||
#ifdef SO_BINDTODEVICE
|
||||
| FBindToIface
|
||||
#endif
|
||||
#if defined(_WINDOWS) || defined(HAVE_POLL)
|
||||
| FEfficientSelect
|
||||
#endif
|
||||
#ifdef SO_EXCLUSIVEADDRUSE
|
||||
| FExclusiveAddrUse
|
||||
#endif
|
||||
;
|
||||
|
||||
Socket::Socket()
|
||||
: m_handle(invalidHandle())
|
||||
{
|
||||
|
@ -1814,32 +1745,6 @@ bool Socket::bind(struct sockaddr* addr, socklen_t addrlen)
|
|||
return checkError(::bind(m_handle,addr,addrlen));
|
||||
}
|
||||
|
||||
bool Socket::bind(struct sockaddr* addr, socklen_t addrlen,
|
||||
const char* iface, int ifLen)
|
||||
{
|
||||
if (iface && ifLen &&
|
||||
!bindIface(iface,ifLen,addr ? addr->sa_family : SocketAddr::Unknown))
|
||||
return false;
|
||||
return bind(addr,addrlen);
|
||||
}
|
||||
|
||||
bool Socket::bindIface(const char* name, int len, int family)
|
||||
{
|
||||
// IPv6 interface should be set in address header
|
||||
if (!(name && len) || family == SocketAddr::IPv6)
|
||||
return true;
|
||||
#ifdef SO_BINDTODEVICE
|
||||
if (len < 0)
|
||||
len = strlen(name);
|
||||
if (!setOption(SOL_SOCKET,SO_BINDTODEVICE,name,len))
|
||||
return false;
|
||||
#else
|
||||
m_error = EINVAL;
|
||||
return false;
|
||||
#endif // SO_BINDTODEVICE
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Socket::listen(unsigned int backlog)
|
||||
{
|
||||
if ((backlog == 0) || (backlog > SOMAXCONN))
|
||||
|
@ -1979,19 +1884,6 @@ bool Socket::getPeerName(SocketAddr& addr)
|
|||
return ok;
|
||||
}
|
||||
|
||||
bool Socket::getBoundIface(String& buf)
|
||||
{
|
||||
#ifdef SO_BINDTODEVICE
|
||||
char tmp[IF_NAMESIZE];
|
||||
socklen_t len = IF_NAMESIZE;
|
||||
if (getOption(SOL_SOCKET,SO_BINDTODEVICE,tmp,&len)) {
|
||||
buf << tmp;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
int Socket::sendTo(const void* buffer, int length, const struct sockaddr* addr, socklen_t adrlen, int flags)
|
||||
{
|
||||
if (!addr)
|
||||
|
@ -2000,7 +1892,6 @@ int Socket::sendTo(const void* buffer, int length, const struct sockaddr* addr,
|
|||
length = 0;
|
||||
int res = ::sendto(m_handle,(const char*)buffer,length,flags,addr,adrlen);
|
||||
checkError(res,true);
|
||||
applyFilters(buffer,res,flags,addr,adrlen,false);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -2010,7 +1901,6 @@ int Socket::send(const void* buffer, int length, int flags)
|
|||
length = 0;
|
||||
int res = ::send(m_handle,(const char*)buffer,length,flags);
|
||||
checkError(res,true);
|
||||
applyFilters(buffer,res,flags,0,0,false);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -2023,7 +1913,6 @@ int Socket::writeData(const void* buffer, int length)
|
|||
length = 0;
|
||||
int res = ::write(m_handle,buffer,length);
|
||||
checkError(res,true);
|
||||
applyFilters(buffer,res,0,0,0,false);
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
@ -2073,7 +1962,6 @@ int Socket::readData(void* buffer, int length)
|
|||
length = 0;
|
||||
int res = ::read(m_handle,buffer,length);
|
||||
checkError(res,true);
|
||||
applyFilters(buffer,res,0);
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
@ -2333,18 +2221,12 @@ void Socket::removeFilter(SocketFilter* filter, bool delobj)
|
|||
filter->m_socket = 0;
|
||||
}
|
||||
|
||||
void Socket::clearFilters(bool del)
|
||||
void Socket::clearFilters()
|
||||
{
|
||||
for (ObjList* l = m_filters.skipNull(); l; l = l->skipNext()) {
|
||||
SocketFilter* filter = static_cast<SocketFilter*>(l->get());
|
||||
filter->m_socket = 0;
|
||||
}
|
||||
m_filters.setDelete(del);
|
||||
m_filters.clear();
|
||||
}
|
||||
|
||||
bool Socket::applyFilters(const void* buffer, int length, int flags, const struct sockaddr* addr,
|
||||
socklen_t adrlen, bool rx)
|
||||
bool Socket::applyFilters(void* buffer, int length, int flags, const struct sockaddr* addr, socklen_t adrlen)
|
||||
{
|
||||
if ((length <= 0) || !buffer)
|
||||
return false;
|
||||
|
@ -2352,9 +2234,7 @@ bool Socket::applyFilters(const void* buffer, int length, int flags, const struc
|
|||
adrlen = 0;
|
||||
for (ObjList* l = &m_filters; l; l = l->next()) {
|
||||
SocketFilter* filter = static_cast<SocketFilter*>(l->get());
|
||||
if (filter &&
|
||||
(rx ? filter->received(buffer, length, flags, addr, adrlen)
|
||||
: filter->sent(buffer, length, flags, addr, adrlen)))
|
||||
if (filter && filter->received(buffer,length,flags,addr,adrlen))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
@ -36,14 +36,6 @@
|
|||
|
||||
namespace TelEngine {
|
||||
|
||||
static inline char* strAlloc(unsigned int n, char* old = 0)
|
||||
{
|
||||
char* data = (char*)::realloc(old,n + 1);
|
||||
if (!data)
|
||||
Debug("String",DebugFail,"realloc(%u) returned NULL!",n + 1);
|
||||
return data;
|
||||
}
|
||||
|
||||
// String to regular integer conversion, takes into account overflows
|
||||
static int strtoi(const char* nptr, char** endptr, int base)
|
||||
{
|
||||
|
@ -684,7 +676,7 @@ String& String::assign(char value, unsigned int repeat)
|
|||
return *this;
|
||||
}
|
||||
|
||||
String& String::hexify(const void* data, unsigned int len, char sep, bool upCase)
|
||||
String& String::hexify(void* data, unsigned int len, char sep, bool upCase)
|
||||
{
|
||||
const char* hex = upCase ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
if (data && len) {
|
||||
|
@ -1263,35 +1255,6 @@ String& String::insert(unsigned int pos, const char* value, int len)
|
|||
return *this;
|
||||
}
|
||||
|
||||
// Insert characters in string into current string
|
||||
String& String::insert(unsigned int pos, char value, unsigned int len)
|
||||
{
|
||||
if (!(value && len))
|
||||
return *this;
|
||||
|
||||
if (pos > m_length)
|
||||
pos = m_length;
|
||||
unsigned int newLen = len + m_length;
|
||||
char* data = strAlloc(newLen,pos < m_length ? 0 : m_string);
|
||||
if (!data)
|
||||
return *this;
|
||||
if (m_string) {
|
||||
if (!pos)
|
||||
// Insert before existing, copy old data after it
|
||||
::memcpy(data + len,m_string,m_length);
|
||||
else if (pos == m_length)
|
||||
// Data reallocated. Reset held pointer
|
||||
m_string = 0;
|
||||
else {
|
||||
// Insert middle
|
||||
::memcpy(data,m_string,pos);
|
||||
::memcpy(data + pos + len,m_string + pos,m_length - pos);
|
||||
}
|
||||
}
|
||||
::memset(data + pos,value,len);
|
||||
return changeStringData(data,newLen);
|
||||
}
|
||||
|
||||
static char* string_printf(unsigned int& length, const char* format, va_list& va)
|
||||
{
|
||||
if (TelEngine::null(format) || !length)
|
||||
|
@ -1376,33 +1339,6 @@ String& String::printf(const char* format, ...)
|
|||
return *this;
|
||||
}
|
||||
|
||||
String& String::printfAppend(unsigned int length, const char* format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va,format);
|
||||
char* buf = string_printf(length,format,va);
|
||||
va_end(va);
|
||||
if (buf) {
|
||||
*this << buf;
|
||||
::free(buf);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::printfAppend(const char* format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va,format);
|
||||
unsigned int len = TelEngine::null(format) ? 0 : (128 + ::strlen(format));
|
||||
char* buf = string_printf(len,format,va);
|
||||
va_end(va);
|
||||
if (buf) {
|
||||
*this << buf;
|
||||
::free(buf);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::appendFixed(unsigned int fixedLength, const char* str, unsigned int len, char fill, int align)
|
||||
{
|
||||
if (len == (unsigned int)-1)
|
||||
|
@ -1758,147 +1694,71 @@ String String::sqlEscape(const char* str, char extraEsc)
|
|||
return s;
|
||||
}
|
||||
|
||||
static inline bool isUriNoEsc(char c, const char* noEsc)
|
||||
{
|
||||
return (c == ' ' || c == '+' || c == '?' || c == '&') && !(noEsc && ::strchr(noEsc,c));
|
||||
}
|
||||
|
||||
static inline char isUriEscape(char c, char extraEsc, const char* noEsc)
|
||||
{
|
||||
if ((unsigned char)c < ' ' || c == '%' || c == extraEsc || isUriNoEsc(c,noEsc))
|
||||
return c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline char isUriEscape(char c, const char* extraEsc, const char* noEsc)
|
||||
{
|
||||
if ((unsigned char)c < ' ' || c == '%' || (extraEsc && ::strchr(extraEsc,c))
|
||||
|| isUriNoEsc(c,noEsc))
|
||||
return c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline String& uriEscapeFunc(String& buf, const char* str, const char* noEsc,
|
||||
char extraCh, const char* extraStr)
|
||||
String String::uriEscape(const char* str, char extraEsc, const char* noEsc)
|
||||
{
|
||||
String s;
|
||||
if (TelEngine::null(str))
|
||||
return buf;
|
||||
unsigned int escape = 0;
|
||||
return s;
|
||||
char c;
|
||||
const char* strPtr = str;
|
||||
if (extraStr) {
|
||||
while ((c = *strPtr++)) {
|
||||
if (isUriEscape(c,extraStr,noEsc))
|
||||
escape++;
|
||||
}
|
||||
while ((c=*str++)) {
|
||||
if ((unsigned char)c < ' ' || c == '%' || c == extraEsc ||
|
||||
((c == ' ' || c == '+' || c == '?' || c == '&') && !(noEsc && ::strchr(noEsc,c))))
|
||||
s << '%' << hexEncode(c >> 4) << hexEncode(c);
|
||||
else
|
||||
s += c;
|
||||
}
|
||||
else {
|
||||
while ((c = *strPtr++)) {
|
||||
if (isUriEscape(c,extraCh,noEsc))
|
||||
escape++;
|
||||
}
|
||||
}
|
||||
if (!escape)
|
||||
return buf << str;
|
||||
unsigned int oldLen = buf.length();
|
||||
buf.append(' ',(escape * 2) + (strPtr - str - 1));
|
||||
if (buf.length() == oldLen)
|
||||
return buf;
|
||||
char* dest = (char*)buf.c_str() + oldLen;
|
||||
if (extraStr) {
|
||||
while ((c = *str++)) {
|
||||
if (isUriEscape(c,extraStr,noEsc)) {
|
||||
*dest++ = '%';
|
||||
*dest++ = hexEncode(c >> 4);
|
||||
*dest++ = hexEncode(c);
|
||||
}
|
||||
else
|
||||
*dest++ = c;
|
||||
}
|
||||
}
|
||||
else {
|
||||
while ((c = *str++)) {
|
||||
if (isUriEscape(c,extraCh,noEsc)) {
|
||||
*dest++ = '%';
|
||||
*dest++ = hexEncode(c >> 4);
|
||||
*dest++ = hexEncode(c);
|
||||
}
|
||||
else
|
||||
*dest++ = c;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
return s;
|
||||
}
|
||||
|
||||
String& String::uriEscapeTo(String& buf, const char* str, char extraEsc, const char* noEsc)
|
||||
{
|
||||
return uriEscapeFunc(buf,str,noEsc,extraEsc,0);
|
||||
}
|
||||
|
||||
String& String::uriEscapeTo(String& buf, const char* str, const char* extraEsc, const char* noEsc)
|
||||
{
|
||||
return uriEscapeFunc(buf,str,noEsc,0,extraEsc);
|
||||
}
|
||||
|
||||
String& String::uriUnescapeTo(String& buf, const char* str, bool setPartial, int* errptr)
|
||||
String String::uriEscape(const char* str, const char* extraEsc, const char* noEsc)
|
||||
{
|
||||
String s;
|
||||
if (TelEngine::null(str))
|
||||
return buf;
|
||||
return s;
|
||||
char c;
|
||||
bool unescape = false;
|
||||
const char* pos = str;
|
||||
while ((c = *pos++)) {
|
||||
if ((unsigned char)c < ' ' || c == '%') {
|
||||
unescape = true;
|
||||
break;
|
||||
}
|
||||
while ((c=*str++)) {
|
||||
if ((unsigned char)c < ' ' || c == '%' || (extraEsc && ::strchr(extraEsc,c)) ||
|
||||
((c == ' ' || c == '+' || c == '?' || c == '&') && !(noEsc && ::strchr(noEsc,c))))
|
||||
s << '%' << hexEncode(c >> 4) << hexEncode(c);
|
||||
else
|
||||
s += c;
|
||||
}
|
||||
int ePtr = -1;
|
||||
if (unescape) {
|
||||
char* newData = strAlloc(::strlen(str));
|
||||
if (!newData) {
|
||||
return s;
|
||||
}
|
||||
|
||||
String String::uriUnescape(const char* str, int* errptr)
|
||||
{
|
||||
String s;
|
||||
if (TelEngine::null(str))
|
||||
return s;
|
||||
const char *pos = str;
|
||||
char c;
|
||||
while ((c=*pos++)) {
|
||||
if ((unsigned char)c < ' ') {
|
||||
if (errptr)
|
||||
*errptr = 0;
|
||||
return buf;
|
||||
*errptr = (pos-str) - 1;
|
||||
return s;
|
||||
}
|
||||
char* set = newData;
|
||||
pos = str;
|
||||
while ((c = *pos++)) {
|
||||
if ((unsigned char)c < ' ') {
|
||||
ePtr = (pos - str) - 1;
|
||||
break;
|
||||
else if (c == '%') {
|
||||
int hiNibble = hexDecode(*pos++);
|
||||
if (hiNibble < 0) {
|
||||
if (errptr)
|
||||
*errptr = (pos-str) - 1;
|
||||
return s;
|
||||
}
|
||||
if (c == '%') {
|
||||
int hiNibble = hexDecode(*pos++);
|
||||
if (hiNibble < 0) {
|
||||
ePtr = (pos - str) - 1;
|
||||
break;
|
||||
}
|
||||
int loNibble = hexDecode(*pos++);
|
||||
if (loNibble < 0) {
|
||||
ePtr = (pos - str) - 1;
|
||||
break;
|
||||
}
|
||||
c = ((hiNibble << 4) | loNibble) & 0xff;
|
||||
int loNibble = hexDecode(*pos++);
|
||||
if (loNibble < 0) {
|
||||
if (errptr)
|
||||
*errptr = (pos-str) - 1;
|
||||
return s;
|
||||
}
|
||||
*set++ = c;
|
||||
c = ((hiNibble << 4) | loNibble) & 0xff;
|
||||
}
|
||||
if (ePtr < 0 || setPartial) {
|
||||
*set = 0;
|
||||
if (buf.c_str() != str)
|
||||
buf << newData;
|
||||
else
|
||||
buf = newData;
|
||||
}
|
||||
::free(newData);
|
||||
s += c;
|
||||
}
|
||||
else if (buf.c_str() != str)
|
||||
buf << str;
|
||||
else
|
||||
buf = str;
|
||||
if (errptr)
|
||||
*errptr = ePtr;
|
||||
return buf;
|
||||
*errptr = -1;
|
||||
return s;
|
||||
}
|
||||
|
||||
unsigned int String::hash(const char* value, unsigned int h)
|
||||
|
@ -2120,90 +1980,6 @@ const String* String::atom(const String*& str, const char* val)
|
|||
return str;
|
||||
}
|
||||
|
||||
static unsigned int c_find_str(bool start, const char* str, const char* what,
|
||||
int lenStr, int lenWhat, bool caseInsensitive)
|
||||
{
|
||||
if (!lenStr || !lenWhat || TelEngine::null(str) || TelEngine::null(what))
|
||||
return 0;
|
||||
if (lenStr < 0)
|
||||
lenStr = ::strlen(str);
|
||||
if (lenWhat < 0)
|
||||
lenWhat = ::strlen(what);
|
||||
if (lenStr < lenWhat)
|
||||
return 0;
|
||||
if (!start)
|
||||
str += lenStr - lenWhat - 1;
|
||||
if (caseInsensitive) {
|
||||
if (::strncasecmp(str,what,lenWhat))
|
||||
return 0;
|
||||
}
|
||||
else if (::strncmp(str,what,lenWhat))
|
||||
return 0;
|
||||
return lenWhat;
|
||||
}
|
||||
|
||||
unsigned int String::c_starts_with(const char* str, const char* what, int lenStr, int lenWhat,
|
||||
bool caseInsensitive)
|
||||
{
|
||||
return c_find_str(true,str,what,lenStr,lenWhat,caseInsensitive);
|
||||
}
|
||||
|
||||
unsigned int String::c_ends_with(const char* str, const char* what, int lenStr, int lenWhat,
|
||||
bool caseInsensitive)
|
||||
{
|
||||
return c_find_str(false,str,what,lenStr,lenWhat,caseInsensitive);
|
||||
}
|
||||
|
||||
unsigned int String::c_skip_chars(const char*& str, const char* what, int len, bool skipFound)
|
||||
{
|
||||
if (!len || TelEngine::null(str) || TelEngine::null(what))
|
||||
return 0;
|
||||
const char* orig = str;
|
||||
if (skipFound) {
|
||||
if (len < 0) {
|
||||
if (what[1])
|
||||
while (*str) {
|
||||
if (!::strchr(what,*str))
|
||||
break;
|
||||
str++;
|
||||
}
|
||||
else
|
||||
while (*str == *what)
|
||||
str++;
|
||||
}
|
||||
else if (what[1])
|
||||
while (len-- && *str) {
|
||||
if (!::strchr(what,*str))
|
||||
break;
|
||||
str++;
|
||||
}
|
||||
else
|
||||
while (len-- && *str == *what)
|
||||
str++;
|
||||
}
|
||||
else if (len < 0) {
|
||||
if (what[1])
|
||||
while (*str) {
|
||||
if (::strchr(what,*str))
|
||||
break;
|
||||
str++;
|
||||
}
|
||||
else
|
||||
while (*str && *str != *what)
|
||||
str++;
|
||||
}
|
||||
else if (what[1])
|
||||
while (len-- && *str) {
|
||||
if (::strchr(what,*str))
|
||||
break;
|
||||
str++;
|
||||
}
|
||||
else
|
||||
while (len-- && *str && *str != *what)
|
||||
str++;
|
||||
return (unsigned int)(str - orig);
|
||||
}
|
||||
|
||||
|
||||
Regexp::Regexp()
|
||||
: m_regexp(0), m_compile(true), m_flags(0)
|
||||
|
@ -2440,348 +2216,4 @@ const String& String::decodeFlags(uint64_t flags, const TokenDict64* tokens, boo
|
|||
return *this;
|
||||
}
|
||||
|
||||
String& String::changeStringData(char* data, unsigned int len)
|
||||
{
|
||||
char* tmp = m_string;
|
||||
if (data)
|
||||
data[len] = 0;
|
||||
m_string = data;
|
||||
m_length = len;
|
||||
if (tmp)
|
||||
::free(tmp);
|
||||
changed();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// MatchingItemDump
|
||||
//
|
||||
static inline void addFlags(String& buf, const String& flags)
|
||||
{
|
||||
if (flags)
|
||||
buf << '[' << flags << "] ";
|
||||
}
|
||||
|
||||
static inline const char* miType(const MatchingItemBase* item)
|
||||
{
|
||||
if (!item)
|
||||
return "";
|
||||
if (item->itemList())
|
||||
return "list";
|
||||
if (item->itemString())
|
||||
return "string";
|
||||
if (item->itemRegexp())
|
||||
return "regexp";
|
||||
if (item->itemRandom())
|
||||
return "random";
|
||||
if (item->itemCustom())
|
||||
return item->itemCustom()->type().safe("custom");
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static const TokenDict s_miDumpFlags[] = {
|
||||
{"no_initial_list_desc", MatchingItemDump::NoInitialListDesc},
|
||||
{0,0}
|
||||
};
|
||||
|
||||
static inline String dumpIndent(const String& buf)
|
||||
{
|
||||
String s;
|
||||
for (const char* ss = buf; (ss && *ss); ++ss)
|
||||
if (*ss == '\r')
|
||||
s << "\\r";
|
||||
else if (*ss == '\n')
|
||||
s << "\\n";
|
||||
else
|
||||
s << *ss;
|
||||
return s;
|
||||
}
|
||||
|
||||
void MatchingItemDump::init(const NamedList& params)
|
||||
{
|
||||
for (ObjList* o = params.paramList()->skipNull(); o; o = o->skipNext()) {
|
||||
NamedString* ns = static_cast<NamedString*>(o->get());
|
||||
if (ns->name() == YSTRING("flags"))
|
||||
m_flags = ns->encodeFlags(s_miDumpFlags);
|
||||
else if (ns->name() == YSTRING("rex_enclose"))
|
||||
m_rexEnclose = (*ns)[0];
|
||||
else if (ns->name() == YSTRING("str_enclose"))
|
||||
m_strEnclose = (*ns)[0];
|
||||
else if (ns->name() == YSTRING("name_value_sep"))
|
||||
m_nameValueSep = *ns;
|
||||
else if (ns->name() == YSTRING("prop_negated"))
|
||||
m_negated = (*ns)[0];
|
||||
else if (ns->name() == YSTRING("prop_caseinsensitive"))
|
||||
m_caseInsentive = (*ns)[0];
|
||||
else if (ns->name() == YSTRING("prop_rex_basic"))
|
||||
m_regexpBasic = (*ns)[0];
|
||||
else if (ns->name() == YSTRING("prop_rex_extended"))
|
||||
m_regexpExtended = (*ns)[0];
|
||||
}
|
||||
}
|
||||
|
||||
String& MatchingItemDump::dumpValue(const MatchingItemBase* item, String& buf,
|
||||
const String& indent, const String& origIndent, unsigned int depth) const
|
||||
{
|
||||
if (!item)
|
||||
return buf;
|
||||
String tmp;
|
||||
// Done if already dumped (item implements dumpValue())
|
||||
if (item->dumpValue(tmp,this,indent,origIndent,depth))
|
||||
return buf << tmp;
|
||||
XDebug("MatchingItemDump",DebugAll,
|
||||
"dumpValue (%p) %s '%s' depth=%u indent='%s'/'%s' [%p]",
|
||||
item,miType(item),item->name().safe(),depth,dumpIndent(indent).safe(),
|
||||
dumpIndent(origIndent).safe(),this);
|
||||
if (item->itemList()) {
|
||||
for (unsigned int i = 0; i < item->itemList()->length(); ++i) {
|
||||
String tmp;
|
||||
buf << dump(item->itemList()->at(i),tmp,indent,origIndent,depth);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const MatchingItemString* str = item->itemString();
|
||||
const MatchingItemRegexp* rex = str ? 0 : item->itemRegexp();
|
||||
String flags;
|
||||
if (item->negated())
|
||||
flags << m_negated;
|
||||
if (str) {
|
||||
if (str->caseInsensitive())
|
||||
flags << m_caseInsentive;
|
||||
addFlags(buf,flags);
|
||||
buf << m_strEnclose << item->itemString()->value() << m_strEnclose;
|
||||
}
|
||||
else if (rex) {
|
||||
if (rex->value().isCaseInsensitive())
|
||||
flags << m_caseInsentive;
|
||||
if (rex->value().isExtended())
|
||||
flags << m_regexpExtended;
|
||||
else
|
||||
flags << m_regexpBasic;
|
||||
addFlags(buf,flags);
|
||||
buf << m_rexEnclose << item->itemRegexp()->value() << m_rexEnclose;
|
||||
}
|
||||
else {
|
||||
addFlags(buf,flags);
|
||||
if (item->itemRandom()) {
|
||||
buf << "RANDOM " << item->itemRandom()->value();
|
||||
if (item->itemRandom()->maxValue() == 100)
|
||||
buf << '%';
|
||||
else
|
||||
buf << '/' << item->itemRandom()->maxValue();
|
||||
}
|
||||
else if (item->itemCustom())
|
||||
buf << "<CUSTOM " << item->itemCustom()->type() << '>';
|
||||
else
|
||||
buf << "<UNKNOWN>";
|
||||
}
|
||||
}
|
||||
XDebug("MatchingItemDump",DebugAll,"Dumped value (%p) '%s' [%p]\r\n-----\r\n%s\r\n-----",
|
||||
item,item->name().safe(),this,buf.safe());
|
||||
return buf;
|
||||
}
|
||||
|
||||
String& MatchingItemDump::dump(const MatchingItemBase* item, String& buf,
|
||||
const String& indent, const String& origIndent, unsigned int depth) const
|
||||
{
|
||||
if (!item)
|
||||
return buf;
|
||||
|
||||
XDebug("MatchingItemDump",DebugAll,
|
||||
"dump (%p) %s '%s' flags=0x%x depth=%u indent='%s'/'%s' [%p]",
|
||||
item,miType(item),item->name().safe(),m_flags,depth,
|
||||
dumpIndent(indent).safe(),dumpIndent(origIndent).safe(),this);
|
||||
unsigned int oLen = buf.length();
|
||||
item->dump(buf,this,indent,origIndent,depth);
|
||||
// Done if already dumped (item implements dump())
|
||||
if (oLen != buf.length())
|
||||
return buf;
|
||||
|
||||
const MatchingItemList* list = item->itemList();
|
||||
if (list) {
|
||||
String tmp;
|
||||
if (depth || 0 == (m_flags & NoInitialListDesc)) {
|
||||
String flags;
|
||||
if (list->negated())
|
||||
flags.append("negated",",");
|
||||
if (!list->matchAll())
|
||||
flags.append("any",",");
|
||||
if (flags)
|
||||
flags.printf(" [%s]",flags.safe());
|
||||
if (depth || flags || item->name())
|
||||
tmp << item->name().safe("List") << ':' << flags;
|
||||
}
|
||||
// Nothing dumped at first level:
|
||||
// print the rest of the list at same alignment
|
||||
String newIndent = indent;
|
||||
if (tmp) {
|
||||
buf << indent << tmp;
|
||||
newIndent += origIndent;
|
||||
}
|
||||
for (unsigned int i = 0; i < list->length(); ++i) {
|
||||
tmp.clear();
|
||||
buf << dump(list->at(i),tmp,newIndent,origIndent,depth + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
String val;
|
||||
dumpValue(item,val);
|
||||
if (item->name() || val) {
|
||||
buf << indent;
|
||||
if (item->name())
|
||||
buf << item->name() << m_nameValueSep.safe("=");
|
||||
buf << val;
|
||||
}
|
||||
}
|
||||
XDebug("MatchingItemDump",DebugAll,"Dumped (%p) '%s' [%p]\r\n-----\r\n%s\r\n-----",
|
||||
item,item->name().safe(),this,buf.safe());
|
||||
return buf;
|
||||
}
|
||||
|
||||
//
|
||||
// MatchingItemRegexp
|
||||
//
|
||||
MatchingItemRegexp* MatchingItemRegexp::build(const char* name, const String& str,
|
||||
int negated, bool insensitive, bool extended, int fail)
|
||||
{
|
||||
Regexp rex(0,extended,insensitive);
|
||||
if (str) {
|
||||
if (negated >= 0)
|
||||
rex.assign(str);
|
||||
else {
|
||||
unsigned int pos = str.length() - 1;
|
||||
negated = (str[pos] == '^') ? 1 : 0;
|
||||
if (negated)
|
||||
rex.assign(str.substr(0,pos));
|
||||
else
|
||||
rex.assign(str);
|
||||
}
|
||||
}
|
||||
else if (negated < 0)
|
||||
negated = 0;
|
||||
if (fail > 1) {
|
||||
if (!rex.compile())
|
||||
return 0;
|
||||
}
|
||||
else if (fail < 0 && !rex.c_str())
|
||||
return 0;
|
||||
return new MatchingItemRegexp(name,rex,negated);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// MatchingItemList
|
||||
//
|
||||
bool MatchingItemList::change(MatchingItemBase* item, int pos, bool ins, unsigned int overAlloc)
|
||||
{
|
||||
if (!item) {
|
||||
unsigned int n = count();
|
||||
if (ins || pos < 0 || pos >= (int)n)
|
||||
return false;
|
||||
// Remove
|
||||
GenObject* gen = m_value.take(pos);
|
||||
if (gen) {
|
||||
for (; pos < (int)n; ++pos)
|
||||
m_value.set(m_value.take(pos + 1),pos);
|
||||
TelEngine::destruct(gen);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Detect first free position
|
||||
unsigned int firstFree = 0;
|
||||
while (m_value.at(firstFree))
|
||||
firstFree++;
|
||||
if (firstFree >= m_value.length()) {
|
||||
if (firstFree >= m_value.resize(m_value.length() + 1 + overAlloc,true)) {
|
||||
TelEngine::destruct(item);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (pos < 0 || pos >= (int)firstFree)
|
||||
pos = firstFree;
|
||||
else if (ins) {
|
||||
for (; (int)firstFree > pos; --firstFree)
|
||||
m_value.set(m_value.take(firstFree - 1),firstFree);
|
||||
}
|
||||
m_value.set(item,pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
MatchingItemBase* MatchingItemList::copy() const
|
||||
{
|
||||
MatchingItemList* lst = new MatchingItemList(name(),matchAll(),negated());
|
||||
if (length()) {
|
||||
unsigned int overAlloc = length() - 1;
|
||||
for (unsigned int i = 0; i < length(); ++i) {
|
||||
const MatchingItemBase* it = at(i);
|
||||
MatchingItemBase* item = it ? it->copy() : 0;
|
||||
if (item) {
|
||||
lst->append(item,overAlloc);
|
||||
overAlloc = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return lst;
|
||||
}
|
||||
|
||||
static inline bool matchingListRun(const MatchingItemList& mil, MatchingParams* params,
|
||||
const NamedList* list, const String& str = String::empty())
|
||||
{
|
||||
bool allMatch = mil.matchAll();
|
||||
#ifdef XDEBUG
|
||||
Debugger dbg(DebugAll,"MatchingItemList MATCHING",
|
||||
" name='%s' all=%s negated=%s input='%s' params=(%p) [%p]",
|
||||
mil.name().safe(),String::boolText(allMatch),String::boolText(mil.negated()),
|
||||
list ? list->safe("list") : "<string>",params,&mil);
|
||||
#endif
|
||||
int pos = -1;
|
||||
while (true) {
|
||||
const MatchingItemBase* item = const_cast<MatchingItemBase*>(mil.at(++pos));
|
||||
if (!item)
|
||||
break;
|
||||
bool ok = list ? item->matchListParam(*list,params) : item->matchString(str,params);
|
||||
XDebug("MatchingItemList",DebugAll,"matched [%s] idx=%d (%p) '%s' type=%s [%p]",
|
||||
String::boolText(ok),pos,item,item->name().safe(),miType(item),&mil);
|
||||
// Matched: done if not all match (any match)
|
||||
// Not matched: done if all match is required
|
||||
if (ok) {
|
||||
if (!allMatch)
|
||||
return true;
|
||||
}
|
||||
else if (allMatch)
|
||||
return false;
|
||||
}
|
||||
// End of list reached
|
||||
// Empty list or match any: not matched
|
||||
// Otherwise: matched
|
||||
XDebug("MatchingItemList",DebugAll,"End of matching [%s] pos=%d [%p]",
|
||||
String::boolText(pos && allMatch),pos,&mil);
|
||||
return pos && allMatch;
|
||||
}
|
||||
|
||||
bool MatchingItemList::runMatchString(const String& str, MatchingParams* params) const
|
||||
{
|
||||
return matchingListRun(*this,params,0,str);
|
||||
}
|
||||
|
||||
bool MatchingItemList::runMatchListParam(const NamedList& list, MatchingParams* params) const
|
||||
{
|
||||
return matchingListRun(*this,params,&list);
|
||||
}
|
||||
|
||||
MatchingItemBase* MatchingItemList::optimize(MatchingItemList* list)
|
||||
{
|
||||
if (!list || list->at(1))
|
||||
return list;
|
||||
MatchingItemBase* ret = static_cast<MatchingItemBase*>(list->m_value.take(0));
|
||||
if (ret) {
|
||||
// Reverse item (not)negated flag if list is negated to keep the same matching behaviour
|
||||
if (list->negated())
|
||||
ret->m_notNegated = !ret->m_notNegated;
|
||||
}
|
||||
TelEngine::destruct(list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
@ -104,15 +104,7 @@ namespace TelEngine {
|
|||
#define DebugDef DebugNote
|
||||
#define DebugMax DebugAll
|
||||
|
||||
#ifndef OUT_BUFFER_SIZE
|
||||
#define OUT_BUFFER_SIZE 16384
|
||||
#elif OUT_BUFFER_SIZE < 4096
|
||||
#undef OUT_BUFFER_SIZE
|
||||
#define OUT_BUFFER_SIZE 4096
|
||||
#elif OUT_BUFFER_SIZE > 32768
|
||||
#undef OUT_BUFFER_SIZE
|
||||
#define OUT_BUFFER_SIZE 32768
|
||||
#endif
|
||||
#define OUT_BUFFER_SIZE 8192
|
||||
#define OUT_HEADER_SIZE 112
|
||||
|
||||
// RefObject mutex pool array size
|
||||
|
@ -692,31 +684,6 @@ void DebugEnabler::debugCopy(const DebugEnabler* original)
|
|||
m_chain = 0;
|
||||
}
|
||||
|
||||
void DebugEnabler::debugSet(const char* desc)
|
||||
{
|
||||
if (TelEngine::null(desc))
|
||||
return;
|
||||
String s(desc);
|
||||
if (s.startSkip("level")) {
|
||||
int dbg = debugLevel();
|
||||
s >> dbg;
|
||||
if (s == YSTRING("+")) {
|
||||
if (debugLevel() > dbg)
|
||||
dbg = debugLevel();
|
||||
}
|
||||
else if (s == YSTRING("-")) {
|
||||
if (debugLevel() < dbg)
|
||||
dbg = debugLevel();
|
||||
}
|
||||
debugLevel(dbg);
|
||||
}
|
||||
else if (s == YSTRING("reset")) {
|
||||
debugLevel(TelEngine::debugLevel());
|
||||
debugEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Debugger::Debugger(const char* name, const char* format, ...)
|
||||
: m_name(name), m_level(DebugAll)
|
||||
{
|
||||
|
@ -1295,6 +1262,7 @@ ObjList& GenObject::getObjCounters()
|
|||
return s_counters;
|
||||
}
|
||||
|
||||
|
||||
#ifndef ATOMIC_OPS
|
||||
static MutexPool s_refMutex(REFOBJECT_MUTEX_COUNT,false,"RefObject");
|
||||
#endif
|
||||
|
@ -1432,6 +1400,58 @@ void RefPointerBase::assign(RefObject* oldptr, RefObject* newptr, void* pointer)
|
|||
}
|
||||
|
||||
|
||||
NamedCounter::NamedCounter(const String& name)
|
||||
: String(name), m_count(0), m_enabled(getObjCounting()), m_mutex(0)
|
||||
{
|
||||
#ifndef ATOMIC_OPS
|
||||
m_mutex = s_refMutex.mutex(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
int NamedCounter::inc()
|
||||
{
|
||||
#ifdef ATOMIC_OPS
|
||||
#ifdef _WINDOWS
|
||||
return InterlockedIncrement((LONG*)&m_count);
|
||||
#else
|
||||
return __sync_add_and_fetch(&m_count,1);
|
||||
#endif
|
||||
#else
|
||||
Lock lock(m_mutex);
|
||||
return ++m_count;
|
||||
#endif
|
||||
}
|
||||
|
||||
int NamedCounter::dec()
|
||||
{
|
||||
#ifdef ATOMIC_OPS
|
||||
#ifdef _WINDOWS
|
||||
return InterlockedDecrement((LONG*)&m_count);
|
||||
#else
|
||||
return __sync_fetch_and_sub(&m_count,1);
|
||||
#endif
|
||||
#else
|
||||
Lock lock(m_mutex);
|
||||
return --m_count;
|
||||
#endif
|
||||
}
|
||||
|
||||
int NamedCounter::add(int val)
|
||||
{
|
||||
#ifdef ATOMIC_OPS
|
||||
#ifdef _WINDOWS
|
||||
return InterlockedExchangeAdd((LONG*)&m_count,(LONG)val);
|
||||
#else
|
||||
return __sync_add_and_fetch(&m_count,val);
|
||||
#endif
|
||||
#else
|
||||
Lock lock(m_mutex);
|
||||
m_count += val;
|
||||
return m_count;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void SysUsage::init()
|
||||
{
|
||||
if (!s_startTime)
|
||||
|
@ -1505,35 +1525,6 @@ double SysUsage::runTime(Type type)
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// AtomicOp
|
||||
//
|
||||
#ifndef ATOMIC_OP_LOCK_COUNT
|
||||
#define ATOMIC_OP_LOCK_COUNT 101
|
||||
#endif
|
||||
|
||||
#ifdef YATOMIC_LOCK
|
||||
static RWLockPool s_atomicLock(ATOMIC_OP_LOCK_COUNT,"AtomicOp");
|
||||
#endif
|
||||
|
||||
AtomicOp::AtomicOp()
|
||||
: m_lock(0)
|
||||
{
|
||||
#ifdef YATOMIC_LOCK
|
||||
m_lock = s_atomicLock.lock(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AtomicOp::efficient()
|
||||
{
|
||||
#ifdef YATOMIC_LOCK
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
423
engine/XML.cpp
423
engine/XML.cpp
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
@ -1965,6 +1965,14 @@ unsigned int XmlElement::copyAttributes(NamedList& list, const String& prefix) c
|
|||
return copy;
|
||||
}
|
||||
|
||||
void XmlElement::setAttributes(NamedList& list, const String& prefix, bool skipPrefix)
|
||||
{
|
||||
if (prefix)
|
||||
m_element.copySubParams(list,prefix,skipPrefix);
|
||||
else
|
||||
m_element.copyParams(list);
|
||||
}
|
||||
|
||||
// Retrieve a namespace attribute. Search in parent or inherited for it
|
||||
String* XmlElement::xmlnsAttribute(const String& name) const
|
||||
{
|
||||
|
@ -2278,7 +2286,7 @@ void XmlDoctype::toString(String& dump, const String& indent) const
|
|||
*/
|
||||
// Maximul number of predicates in step
|
||||
#ifndef XPATH_MAX_PREDICATES
|
||||
#define XPATH_MAX_PREDICATES 10
|
||||
#define XPATH_MAX_PREDICATES 5
|
||||
#endif
|
||||
|
||||
#ifdef XDEBUG
|
||||
|
@ -2591,15 +2599,10 @@ protected:
|
|||
|
||||
class XPathEscapedString
|
||||
{
|
||||
YNOCOPY(XPathEscapedString);
|
||||
public:
|
||||
inline XPathEscapedString(String* str, bool literal = false)
|
||||
: m_delimiter(0), m_esc(false), m_literal(literal), m_str(str)
|
||||
{}
|
||||
inline XPathEscapedString(const XPathEscapedString& other, String* str)
|
||||
: m_delimiter(other.m_delimiter), m_esc(other.m_esc), m_literal(other.m_literal),
|
||||
m_str(str)
|
||||
{}
|
||||
inline void setLiteral(bool on)
|
||||
{ m_literal = on; }
|
||||
inline char delimiter() const
|
||||
|
@ -2642,12 +2645,6 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
inline void copy(const XPathEscapedString& other) {
|
||||
m_delimiter = other.m_delimiter;
|
||||
m_esc = other.m_esc;
|
||||
m_literal = other.m_literal;
|
||||
}
|
||||
|
||||
char m_delimiter; // Delimiter. Non 0 indicates data is used
|
||||
bool m_esc; // String contains escaped chars
|
||||
bool m_literal; // String is an XPATH literal
|
||||
|
@ -2661,17 +2658,8 @@ public:
|
|||
inline XPathString(bool literal = false)
|
||||
: XPathEscapedString(this,literal)
|
||||
{}
|
||||
inline XPathString(const XPathString& other)
|
||||
: String(*static_cast<const String*>(&other)),
|
||||
XPathEscapedString(*static_cast<const XPathEscapedString*>(&other),this)
|
||||
{}
|
||||
inline String& dump(String& buf, bool escape = false) const
|
||||
{ return dumpString(buf,escape); }
|
||||
inline XPathString& operator=(const XPathString& other) {
|
||||
String::assign(other.c_str());
|
||||
XPathEscapedString::copy(other);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// XPath regexp literal
|
||||
|
@ -2682,12 +2670,6 @@ public:
|
|||
: XPathEscapedString(this),
|
||||
m_match(true)
|
||||
{}
|
||||
inline XPathRegexp(const XPathRegexp& other)
|
||||
: Regexp(*static_cast<const Regexp*>(&other)),
|
||||
XPathEscapedString(*static_cast<const XPathEscapedString*>(&other),this),
|
||||
m_match(other.m_match),
|
||||
m_flags(other.m_flags)
|
||||
{}
|
||||
inline bool matches(const char* value) const
|
||||
{ return m_match == Regexp::matches(value); }
|
||||
inline const XPathString& flags() const
|
||||
|
@ -2723,15 +2705,6 @@ public:
|
|||
}
|
||||
return buf;
|
||||
}
|
||||
inline XPathRegexp& operator=(const XPathRegexp& other) {
|
||||
String::assign(other.c_str());
|
||||
Regexp::setFlags(other.isExtended(),other.isCaseInsensitive());
|
||||
Regexp::assign(other.c_str());
|
||||
m_match = other.m_match;
|
||||
m_flags = other.m_flags;
|
||||
XPathEscapedString::copy(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool m_match; // (Reverse) match
|
||||
|
@ -2739,103 +2712,6 @@ protected:
|
|||
};
|
||||
|
||||
namespace TelEngine {
|
||||
|
||||
class XPathStep;
|
||||
|
||||
class XPathNodeCheck
|
||||
{
|
||||
public:
|
||||
enum Process {
|
||||
Xml,
|
||||
Attribute,
|
||||
Text,
|
||||
ChildText,
|
||||
};
|
||||
inline XPathNodeCheck(XPathStep* step, ObjList* resList)
|
||||
: m_step(step), m_nameCheck(0), m_resList(resList),
|
||||
m_type(Xml), m_index(0), m_count(0), m_next(0), m_nextChild(0)
|
||||
{}
|
||||
inline unsigned int index() const
|
||||
{ return m_index; }
|
||||
inline void setType(int t)
|
||||
{ m_type = t; }
|
||||
inline XmlElement* startXml(const XmlElement& xml, bool root = false) {
|
||||
if (!root) {
|
||||
m_next = xml.getChildren().skipNull();
|
||||
return advanceXml();
|
||||
}
|
||||
m_count = 1;
|
||||
return (!m_nameCheck || *m_nameCheck == xml.getTag()) ? (XmlElement*)&xml : 0;
|
||||
}
|
||||
inline XmlText* startText(const XmlElement& xml) {
|
||||
m_nextChild = xml.getChildren().skipNull();
|
||||
return advanceText();
|
||||
}
|
||||
inline NamedString* startAttr(const XmlElement& xml) {
|
||||
m_next = xml.attributes().paramList()->skipNull();
|
||||
return advanceAttr();
|
||||
}
|
||||
inline XmlElement* advanceXml()
|
||||
{ return XmlFragment::getElement(m_next,m_nameCheck); }
|
||||
inline XmlText* advanceText()
|
||||
{ return XmlFragment::getText(m_nextChild); }
|
||||
inline NamedString* advanceAttr() {
|
||||
m_next = findAttr(m_next,m_nameCheck);
|
||||
if (!m_next)
|
||||
return 0;
|
||||
ObjList* tmp = m_next;
|
||||
m_next = m_next->skipNext();
|
||||
return static_cast<NamedString*>(tmp->get());
|
||||
}
|
||||
inline void advanceIndex()
|
||||
{ m_index++; }
|
||||
inline int checkIndex(unsigned int idx) {
|
||||
if (m_index == idx)
|
||||
return XPathProcHandleStop;
|
||||
return m_index < idx ? XPathProcCont : XPathProcStop;
|
||||
}
|
||||
inline int checkPosLast() {
|
||||
if (Xml == m_type) {
|
||||
if (m_count)
|
||||
return checkIndex(m_count);
|
||||
return procNotLastPos(XmlFragment::findElement(m_next,m_nameCheck,0));
|
||||
}
|
||||
if (Attribute == m_type)
|
||||
return procNotLastPos(findAttr(m_next,m_nameCheck));
|
||||
// Text process
|
||||
if (ChildText == m_type && m_next) {
|
||||
XmlElement* next = XmlFragment::findElement(m_next,0,0);
|
||||
if (next && XmlFragment::findText(next->getChildren().skipNull()))
|
||||
return XPathProcCont;
|
||||
// No next element or next element has no text child. Check current element
|
||||
}
|
||||
return procNotLastPos(XmlFragment::findText(m_nextChild));
|
||||
}
|
||||
|
||||
static inline int procNotLastPos(bool notLast)
|
||||
{ return notLast ? XPathProcCont : XPathProcHandleStop; }
|
||||
static inline ObjList* findAttr(ObjList* lst, const String* name) {
|
||||
if (!(lst && name))
|
||||
return lst;
|
||||
for (; lst; lst = lst->skipNext()) {
|
||||
if (*name == static_cast<NamedString*>(lst->get())->name())
|
||||
return lst;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XPathStep* m_step;
|
||||
const String* m_nameCheck;
|
||||
ObjList* m_resList;
|
||||
|
||||
protected:
|
||||
int m_type;
|
||||
unsigned int m_index;
|
||||
unsigned int m_count;
|
||||
ObjList* m_next; // Next item (Xml or Attribute)
|
||||
ObjList* m_nextChild; // Next child (XML text)
|
||||
};
|
||||
|
||||
class XPathPredicate
|
||||
{
|
||||
public:
|
||||
|
@ -2849,8 +2725,6 @@ public:
|
|||
XmlName = 0x10,
|
||||
Attribute, // XML element attribute match
|
||||
Child, // XML child match (presence or text),
|
||||
Position = 0x20,
|
||||
PosLast, // Node position in list: last item
|
||||
};
|
||||
// Operators
|
||||
enum Opc {
|
||||
|
@ -2863,10 +2737,6 @@ public:
|
|||
inline XPathPredicate()
|
||||
: m_type(0), m_opc(0)
|
||||
{}
|
||||
inline XPathPredicate(const XPathPredicate& other)
|
||||
: m_type(other.m_type), m_opc(other.m_opc),
|
||||
m_name(other.m_name), m_value(other.m_value), m_regexp(other.m_regexp)
|
||||
{}
|
||||
inline int type() const
|
||||
{ return m_type; }
|
||||
inline unsigned int opc() const
|
||||
|
@ -2878,15 +2748,13 @@ public:
|
|||
inline bool valid() const
|
||||
{ return 0 != type(); }
|
||||
inline bool isPosition() const
|
||||
{ return 0 != (Position & type()); }
|
||||
inline bool sameIndex(const XPathPredicate& other) const
|
||||
{ return opc() == other.opc(); }
|
||||
inline int check(XPathNodeCheck& data, const XmlElement* xml = 0,
|
||||
const NamedString* attr = 0) const {
|
||||
if (Index == m_type)
|
||||
return data.checkIndex(m_opc);
|
||||
if (PosLast == m_type)
|
||||
return data.checkPosLast();
|
||||
{ return Index == type(); }
|
||||
inline int check(unsigned int index, const XmlElement* xml = 0, const NamedString* attr = 0) const {
|
||||
if (Index == m_type) {
|
||||
if (index == m_opc)
|
||||
return XPathProcHandleStop;
|
||||
return index < m_opc ? XPathProcCont : XPathProcStop;
|
||||
}
|
||||
if (Text == m_type || Child == m_type) {
|
||||
const String* txt = xml ?
|
||||
((Child == m_type) ? xml->childText(m_name) : &(xml->getText())) : 0;
|
||||
|
@ -2954,14 +2822,6 @@ public:
|
|||
m_value.dump(buf,escape);
|
||||
}
|
||||
}
|
||||
inline XPathPredicate& operator=(const XPathPredicate& other) {
|
||||
m_type = other.m_type;
|
||||
m_opc = other.m_opc;
|
||||
m_name = other.m_name;
|
||||
m_value = other.m_value;
|
||||
m_regexp = other.m_regexp;
|
||||
return *this;
|
||||
}
|
||||
|
||||
static inline const char* opcName(int opc)
|
||||
{ return lookup(opc,s_opcAll); }
|
||||
|
@ -3011,44 +2871,36 @@ const TokenDict XPathPredicate::s_typeName[] = {
|
|||
{"attribute", Attribute},
|
||||
{"child", Child},
|
||||
{"text", Text},
|
||||
{"last", PosLast},
|
||||
{0,0},
|
||||
};
|
||||
|
||||
typedef GenericVector<XPathPredicate> XPathPredicateVector;
|
||||
|
||||
class XPathPredicateList
|
||||
{
|
||||
public:
|
||||
inline XPathPredicateList()
|
||||
: m_predicates(2), m_indexPredicate(0), m_stopProc(false)
|
||||
: m_indexPredicate(0), m_stopProc(false)
|
||||
{}
|
||||
inline XPathPredicateList(const XPathPredicateList& other)
|
||||
: m_predicates(other.m_predicates), m_indexPredicate(0), m_stopProc(other.m_stopProc)
|
||||
{
|
||||
if (other.m_indexPredicate) {
|
||||
const XPathPredicate* p = other.m_predicates.data();
|
||||
for (unsigned int i = 0; i < other.m_predicates.length(); ++i, ++p)
|
||||
if (p == other.m_indexPredicate) {
|
||||
m_indexPredicate = m_predicates.data(i,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline int check(XPathNodeCheck& data, const XmlElement* xml = 0,
|
||||
inline bool valid() const
|
||||
{ return m_predicates[0].valid(); }
|
||||
inline XPathPredicate* first()
|
||||
{ return m_predicates; }
|
||||
inline const XPathPredicate* first() const
|
||||
{ return m_predicates; }
|
||||
inline int check(unsigned int& index, const XmlElement* xml = 0,
|
||||
const NamedString* attr = 0) const {
|
||||
const XPathPredicate* f = m_predicates.first();
|
||||
if (!f)
|
||||
if (!valid())
|
||||
return XPathProcHandleCont;
|
||||
data.advanceIndex();
|
||||
index++;
|
||||
int rProc = XPathProcHandleCont;
|
||||
if (!m_stopProc) {
|
||||
// Always evalute predicates with position first. May lead to fast return
|
||||
if (m_indexPredicate)
|
||||
check(rProc,true,*m_indexPredicate,data,xml,attr);
|
||||
for (unsigned int i = 0; rProc > 0 && i < m_predicates.length(); ++i, ++f) {
|
||||
if (f != m_indexPredicate)
|
||||
check(rProc,!(m_indexPredicate || i),*f,data,xml,attr);
|
||||
check(rProc,true,*m_indexPredicate,index,xml,attr);
|
||||
const XPathPredicate* f = first();
|
||||
for (unsigned int i = 0; rProc > 0 && i < XPATH_MAX_PREDICATES && f->valid(); ++i, ++f) {
|
||||
if (!f->isPosition())
|
||||
check(rProc,!(m_indexPredicate || i),*f,index,xml,attr);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -3059,13 +2911,36 @@ public:
|
|||
"Checked %s '%s' idx=%u. Predicate(s) not matched proc=%s",
|
||||
(xml ? "xml" : (attr ? "attribute" : "???")),
|
||||
(xml ? xml->tag() : ((attr ? attr->name().c_str() : ""))),
|
||||
data.index(),lookup(rProc,s_xpathProcAct));
|
||||
index,lookup(rProc,s_xpathProcAct));
|
||||
#endif
|
||||
return rProc;
|
||||
}
|
||||
inline bool check(int& rProc, bool first, const XPathPredicate& pred,
|
||||
unsigned int index, const XmlElement* xml, const NamedString* attr) const {
|
||||
#ifdef XPATH_XDEBUG_FIND
|
||||
String tmp;
|
||||
pred.dump(tmp) << " index=" << index;
|
||||
#endif
|
||||
if (first) {
|
||||
rProc = pred.check(index,xml,attr);
|
||||
#ifdef XPATH_XDEBUG_FIND
|
||||
Debug("XPath",DebugAll,"Predicate %s check returned %s",
|
||||
tmp.safe(),lookup(rProc,s_xpathProcAct));
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
int proc = pred.check(index,xml,attr);
|
||||
rProc = filterProc(rProc,proc);
|
||||
#ifdef XPATH_XDEBUG_FIND
|
||||
Debug("XPath",DebugAll,"Predicate %s check returned %s. Filtered: %s",
|
||||
tmp.safe(),lookup(proc,s_xpathProcAct),lookup(rProc,s_xpathProcAct));
|
||||
#endif
|
||||
}
|
||||
return rProc > 0;
|
||||
}
|
||||
inline String& dump(String& buf, bool escape = false) const {
|
||||
const XPathPredicate* f = m_predicates.first();
|
||||
for (unsigned int i = 0; i < m_predicates.length(); ++i, ++f)
|
||||
const XPathPredicate* f = first();
|
||||
for (unsigned int i = 0; i < XPATH_MAX_PREDICATES && f->valid(); ++i, ++f)
|
||||
f->dump(buf,escape);
|
||||
return buf;
|
||||
}
|
||||
|
@ -3090,34 +2965,9 @@ public:
|
|||
return XPathProcCont;
|
||||
}
|
||||
|
||||
XPathPredicateVector m_predicates; // Predicate expression(s)
|
||||
XPathPredicate m_predicates[XPATH_MAX_PREDICATES]; // Predicate expression(s)
|
||||
XPathPredicate* m_indexPredicate;
|
||||
bool m_stopProc;
|
||||
|
||||
protected:
|
||||
inline bool check(int& rProc, bool first, const XPathPredicate& pred,
|
||||
XPathNodeCheck& data, const XmlElement* xml, const NamedString* attr) const {
|
||||
#ifdef XPATH_XDEBUG_FIND
|
||||
String tmp;
|
||||
pred.dump(tmp) << " index=" << data.index();
|
||||
#endif
|
||||
if (first) {
|
||||
rProc = pred.check(data,xml,attr);
|
||||
#ifdef XPATH_XDEBUG_FIND
|
||||
Debug("XPath",DebugAll,"Predicate %s check returned %s",
|
||||
tmp.safe(),lookup(rProc,s_xpathProcAct));
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
int proc = pred.check(data,xml,attr);
|
||||
rProc = filterProc(rProc,proc);
|
||||
#ifdef XPATH_XDEBUG_FIND
|
||||
Debug("XPath",DebugAll,"Predicate %s check returned %s. Filtered: %s",
|
||||
tmp.safe(),lookup(proc,s_xpathProcAct),lookup(rProc,s_xpathProcAct));
|
||||
#endif
|
||||
}
|
||||
return rProc > 0;
|
||||
}
|
||||
};
|
||||
|
||||
namespace TelEngine {
|
||||
|
@ -3145,7 +2995,7 @@ public:
|
|||
{}
|
||||
inline XPathStep(const XPathStep& other)
|
||||
: String(other.c_str()),
|
||||
m_nodeType(other.m_nodeType), m_predicates(other.m_predicates)
|
||||
m_nodeType(other.m_nodeType)
|
||||
{}
|
||||
inline int nodeType() const
|
||||
{ return m_nodeType; }
|
||||
|
@ -3158,7 +3008,7 @@ public:
|
|||
inline const String* valueMatch() const
|
||||
{ return valueMatchAny() ? 0 : (const String*)this; }
|
||||
inline const XPathPredicateList* predicates() const
|
||||
{ return m_predicates.m_predicates.length() ? &m_predicates : 0; }
|
||||
{ return m_predicates.valid() ? &m_predicates : 0; }
|
||||
inline String& dump(String& buf, bool escape = false) {
|
||||
switch (m_nodeType) {
|
||||
case Xml:
|
||||
|
@ -3176,6 +3026,17 @@ public:
|
|||
}
|
||||
return m_predicates.dump(buf,escape);
|
||||
}
|
||||
// Check if a given item should be added to result set
|
||||
inline int checkHandle(const XPath* path, unsigned int& resultIdx,
|
||||
const XmlElement* xml = 0, const NamedString* attr = 0,
|
||||
const String& name = String::empty(), const String* nameCheck = 0) const {
|
||||
if (nameCheck && name != *nameCheck) {
|
||||
XPathDebugFind("XPath",DebugAll,"Checked %s '%s': not matched [%p]",
|
||||
(xml ? "xml" : "attribute"),name.c_str(),path);
|
||||
return XPathProcCont;
|
||||
}
|
||||
return m_predicates.check(resultIdx,xml,attr);
|
||||
}
|
||||
|
||||
static inline bool matchAny(const char* buf, unsigned int len)
|
||||
{ return 1 == len && '*' == *buf; }
|
||||
|
@ -3228,17 +3089,20 @@ XPath::XPath(const char* value, unsigned int flags)
|
|||
}
|
||||
|
||||
XPath::XPath(const XPath& other)
|
||||
: m_flags(0),
|
||||
m_status(NotParsed),
|
||||
m_errorItem(0)
|
||||
: String(other.c_str()),
|
||||
m_flags(other.m_flags),
|
||||
m_status(other.m_status),
|
||||
m_errorItem(other.m_errorItem),
|
||||
m_error(other.m_error)
|
||||
{
|
||||
XDebug(DebugAll,"XPath(%p) [%p]",&other,this);
|
||||
copy(other,true);
|
||||
XDebug(DebugAll,"XPath(%s,0x%x) [%p]",c_str(),m_flags,this);
|
||||
ObjList* itAppend = &m_items;
|
||||
for (ObjList* o = other.m_items.skipNull(); o; o = o->skipNext())
|
||||
itAppend->append(new XPathStep(*static_cast<XPathStep*>(o->get())));
|
||||
}
|
||||
|
||||
XPath::~XPath()
|
||||
{
|
||||
XDebug(DebugAll,"~XPath [%p]",this);
|
||||
reset();
|
||||
}
|
||||
|
||||
|
@ -3343,21 +3207,26 @@ int XPath::find(unsigned int& total, const XmlElement& src, const GenObject*& re
|
|||
ObjList* lstAppend = list;
|
||||
unsigned int n = 0;
|
||||
bool stop = false;
|
||||
XPathNodeCheck info(&it,list);
|
||||
unsigned int resultIdx = 0;
|
||||
while (true) {
|
||||
if (it.isElementNode()) {
|
||||
ObjList* o = 0;
|
||||
XmlElement* x = 0;
|
||||
if (absolute)
|
||||
x = (XmlElement*)&src;
|
||||
else {
|
||||
o = src.getChildren().skipNull();
|
||||
x = XmlFragment::getElement(o);
|
||||
}
|
||||
bool xmlReq = 0 != (what & FindXml);
|
||||
// Last item but no XML/TEXT requested ?
|
||||
if (!nextItem && !xmlReq && 0 == (what & FindText)) {
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
info.setType(XPathNodeCheck::Xml);
|
||||
info.m_nameCheck = it.valueMatch();
|
||||
XPathNodeCheck infoText(0,list);
|
||||
infoText.setType(XPathNodeCheck::Text);
|
||||
for (XmlElement* x = info.startXml(src,absolute); x; x = info.advanceXml()) {
|
||||
int proc = it.m_predicates.check(info,x);
|
||||
const String* tag = it.valueMatch();
|
||||
for ( ; x; x = XmlFragment::getElement(o)) {
|
||||
int proc = it.checkHandle(this,resultIdx,x,0,x->getTag(),tag);
|
||||
if (proc > 0) {
|
||||
if (nextItem)
|
||||
proc = XPathStep::filterProc(proc,find(n,*x,res,list,what,nextItem,step + 1));
|
||||
|
@ -3368,7 +3237,7 @@ int XPath::find(unsigned int& total, const XmlElement& src, const GenObject*& re
|
|||
}
|
||||
else
|
||||
// Last item pointing to an XML but XML was not requested
|
||||
proc = XPathStep::filterProc(proc,getText(n,*x,res,infoText));
|
||||
proc = XPathStep::filterProc(proc,getText(n,*x,0,resultIdx,res,list));
|
||||
}
|
||||
if (proc < 0 || XPathProcHandleStop == proc)
|
||||
break;
|
||||
|
@ -3376,7 +3245,7 @@ int XPath::find(unsigned int& total, const XmlElement& src, const GenObject*& re
|
|||
break;
|
||||
}
|
||||
|
||||
if (XPathStep::Text == it.m_nodeType || XPathStep::ChildText == it.m_nodeType) {
|
||||
if (XPathStep::Text == it.m_nodeType || XPathStep::Text == it.m_nodeType) {
|
||||
// No need to check anything if the requested result set contains other data
|
||||
// type (non XML Text) or we have a next item
|
||||
// If next item is present there is nothing there to match (XmlText has no attributes, children ...)
|
||||
|
@ -3386,14 +3255,12 @@ int XPath::find(unsigned int& total, const XmlElement& src, const GenObject*& re
|
|||
stop = true;
|
||||
break;
|
||||
}
|
||||
if (XPathStep::Text == it.m_nodeType) {
|
||||
info.setType(XPathNodeCheck::Text);
|
||||
getText(n,src,res,info);
|
||||
}
|
||||
if (XPathStep::Text == it.m_nodeType)
|
||||
getText(n,src,&it,resultIdx,res,list);
|
||||
else {
|
||||
info.setType(XPathNodeCheck::ChildText);
|
||||
for (XmlElement* x = info.startXml(src); x; x = info.advanceXml()) {
|
||||
int proc = getText(n,*x,res,info);
|
||||
ObjList* o = src.getChildren().skipNull();
|
||||
for (XmlElement* x = XmlFragment::getElement(o); x; x = XmlFragment::getElement(o)) {
|
||||
int proc = getText(n,*x,&it,resultIdx,res,list);
|
||||
if (proc < 0 || XPathProcHandleStop == proc)
|
||||
break;
|
||||
}
|
||||
|
@ -3408,10 +3275,10 @@ int XPath::find(unsigned int& total, const XmlElement& src, const GenObject*& re
|
|||
stop = true;
|
||||
break;
|
||||
}
|
||||
info.setType(XPathNodeCheck::Attribute);
|
||||
info.m_nameCheck = it.valueMatch();
|
||||
for (NamedString* ns = info.startAttr(src); ns; ns = info.advanceAttr()) {
|
||||
int proc = it.m_predicates.check(info,0,ns);
|
||||
const String* name = it.valueMatch();
|
||||
for (const ObjList* o = src.attributes().paramList()->skipNull(); o; o = o->skipNext()) {
|
||||
NamedString* ns = static_cast<NamedString*>(o->get());
|
||||
int proc = it.checkHandle(this,resultIdx,0,ns,ns->name(),name);
|
||||
if (proc > 0) {
|
||||
n++;
|
||||
if (!xpathAddResult(this,ns,res,lstAppend))
|
||||
|
@ -3438,19 +3305,20 @@ int XPath::find(unsigned int& total, const XmlElement& src, const GenObject*& re
|
|||
return rProc;
|
||||
}
|
||||
|
||||
int XPath::getText(unsigned int& total, const XmlElement& src, const GenObject*& res,
|
||||
XPathNodeCheck& data) const
|
||||
int XPath::getText(unsigned int& total, const XmlElement& src, const XPathStep* step,
|
||||
unsigned int& resultIdx, const GenObject*& res, ObjList* list) const
|
||||
{
|
||||
XPathDebugFind("XPath",DebugAll,"Get text xml '%s' multi=%s step=(%p) [%p]",
|
||||
src.getTag().c_str(),String::boolText(data.m_resList),data.m_step,this);
|
||||
src.getTag().c_str(),String::boolText(list),step,this);
|
||||
unsigned int n = 0;
|
||||
int proc = XPathProcHandleCont;
|
||||
for (XmlText* t = data.startText(src); t; t = data.advanceText()) {
|
||||
if (data.m_step)
|
||||
proc = data.m_step->m_predicates.check(data);
|
||||
ObjList* o = src.getChildren().skipNull();
|
||||
for (XmlText* t = XmlFragment::getText(o); t; t = XmlFragment::getText(o)) {
|
||||
if (step)
|
||||
proc = step->checkHandle(this,resultIdx);
|
||||
if (proc > 0) {
|
||||
n++;
|
||||
if (!xpathAddResult(this,&t->getText(),res,data.m_resList))
|
||||
if (!xpathAddResult(this,&t->getText(),res,list))
|
||||
proc = XPathProcStop;
|
||||
}
|
||||
if (proc < 0 || XPathProcHandleStop == proc)
|
||||
|
@ -3464,8 +3332,7 @@ int XPath::getText(unsigned int& total, const XmlElement& src, const GenObject*&
|
|||
|
||||
void XPath::changed()
|
||||
{
|
||||
if (FCopying != m_flags)
|
||||
parsePath();
|
||||
parsePath();
|
||||
}
|
||||
|
||||
#define XPATH_SET_STATUS_BREAK(code,str) { setStatus(code,data.step(),str); break; }
|
||||
|
@ -3598,7 +3465,8 @@ void XPath::parsePath()
|
|||
prevStep = step;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; ; ++i) {
|
||||
XPathPredicate* p = step->m_predicates.first();
|
||||
for (unsigned int i = 0; ; ++p, ++i) {
|
||||
if (!data.strictParse)
|
||||
data.skipBlanks();
|
||||
if (data.isStepEnd())
|
||||
|
@ -3610,14 +3478,10 @@ void XPath::parsePath()
|
|||
}
|
||||
if (i == XPATH_MAX_PREDICATES)
|
||||
XPATH_SET_STATUS_BREAK(ERange,"Too many predicates");
|
||||
XPathPredicateVector& predicates = step->m_predicates.m_predicates;
|
||||
if (!predicates.resize(1 + predicates.length()))
|
||||
XPATH_SET_STATUS_BREAK(ERange,"Memory allocation failure");
|
||||
XPathPredicate* p = predicates.last();
|
||||
if (!(parseStepPredicate(data,p) && checkStepPredicate(data,step,p))) {
|
||||
predicates.removeLast(n);
|
||||
if (!parseStepPredicate(data,p))
|
||||
break;
|
||||
if (!checkStepPredicate(data,step,p))
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_status)
|
||||
break;
|
||||
|
@ -3687,6 +3551,7 @@ bool XPath::parseStepPredicate(XPathParseData& data, XPathPredicate* pred)
|
|||
XPATH_SET_SYNTAX_RET("Predicate index value invalid or out of range");
|
||||
pred->m_type = XPathPredicate::Index;
|
||||
pred->m_opc = (unsigned int)val;
|
||||
|
||||
XPathDebugParse("XPath",DebugInfo,"Parsed predicate %u '%s' value=%u [%p]",
|
||||
pred->type(),pred->typeName(),pred->opc(),this);
|
||||
return true;
|
||||
|
@ -3847,33 +3712,22 @@ bool XPath::parseStepPredicate(XPathParseData& data, XPathPredicate* pred)
|
|||
case 0:
|
||||
// Function not found. Check for selector type function
|
||||
func = lookup(fn,XPathPredicate::s_typeName);
|
||||
#define XPATH_PARSE_END_FUNC_DONE \
|
||||
if (data.ended() || data != ')') \
|
||||
XPATH_SET_SYNTAX_RET(tmp.printf("Expecting ')' after '%s' start",fn.c_str())); \
|
||||
func = 0; \
|
||||
selector.advance(); \
|
||||
data.advance();
|
||||
switch (func) {
|
||||
case XPathPredicate::Text:
|
||||
XPATH_PARSE_END_FUNC_DONE;
|
||||
if (data.ended() || data != ')')
|
||||
XPATH_SET_SYNTAX_RET("Expecting ')' after predicate input selector");
|
||||
pred->m_type = XPathPredicate::Text;
|
||||
break;
|
||||
case XPathPredicate::PosLast:
|
||||
XPATH_PARSE_END_FUNC_DONE;
|
||||
// Skip now until predicate end: we don't expect anything else
|
||||
XPATH_PARSE_STRICT_BLANK_PREDICATE;
|
||||
if (!data.isPredicatedEnd())
|
||||
tmp.printf("Expecting predicate end after position selector");
|
||||
pred->m_type = XPathPredicate::PosLast;
|
||||
func = 0;
|
||||
selector.advance();
|
||||
data.advance();
|
||||
break;
|
||||
default:
|
||||
if (!func)
|
||||
tmp.printf("Unknown function '%s' in predicate",fn.c_str());
|
||||
else
|
||||
if (func)
|
||||
tmp.printf("Predicate function '%s' not implemented",fn.c_str());
|
||||
else
|
||||
tmp.printf("Unknown function '%s' in predicate",fn.c_str());
|
||||
XPATH_SET_SYNTAX_RET(tmp);
|
||||
}
|
||||
#undef XPATH_PARSE_END_FUNC_DONE
|
||||
break;
|
||||
default:
|
||||
tmp.printf("Predicate function '%s' not implemented",fn.c_str());
|
||||
|
@ -4002,11 +3856,10 @@ bool XPath::checkStepPredicate(XPathParseData& data, XPathStep* step, XPathPredi
|
|||
else {
|
||||
if (data.strictParse)
|
||||
XPATH_SET_STATUS_RET(ESemantic,"Repeated index predicate in step");
|
||||
if (!pred->sameIndex(*(lst.m_indexPredicate))) {
|
||||
if (pred->opc() != lst.m_indexPredicate->opc())
|
||||
if (data.checkEmptyRes)
|
||||
XPATH_SET_STATUS_RET(EEmptyResult,"Path step with different index value in predicate");
|
||||
lst.m_stopProc = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -4025,8 +3878,6 @@ bool XPath::checkStepPredicate(XPathParseData& data, XPathStep* step, XPathPredi
|
|||
}
|
||||
break;
|
||||
case XPathPredicate::Index:
|
||||
case XPathPredicate::Position:
|
||||
case XPathPredicate::PosLast:
|
||||
break;
|
||||
default:
|
||||
Debug("XPath",DebugStub,
|
||||
|
@ -4107,22 +3958,4 @@ bool XPath::setStatus(unsigned int code, unsigned int itemIdx, const char* error
|
|||
return false;
|
||||
}
|
||||
|
||||
XPath& XPath::copy(const XPath& other, bool constr)
|
||||
{
|
||||
if (&other == this)
|
||||
return *this;
|
||||
if (!constr)
|
||||
reset();
|
||||
m_flags = FCopying;
|
||||
String::assign(other.c_str()),
|
||||
m_flags = other.m_flags;
|
||||
m_status = other.m_status;
|
||||
m_errorItem = other.m_errorItem;
|
||||
m_error = other.m_error;
|
||||
ObjList* itAppend = &m_items;
|
||||
for (ObjList* o = other.m_items.skipNull(); o; o = o->skipNext())
|
||||
itAppend->append(new XPathStep(*static_cast<XPathStep*>(o->get())));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Adapted for YATE by Paul Chitescu
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
* Adapted for YATE by Paul Chitescu
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Adapted for YATE by Paul Chitescu
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2013-2023 Null Team
|
||||
* Copyright (C) 2013-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
Makefile
|
||||
YateLocal*
|
||||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -5,7 +5,7 @@
|
|||
* Classes for interfacing with Mac OS X API
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
Makefile
|
||||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,4 +0,0 @@
|
|||
core*
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,7 +0,0 @@
|
|||
Makefile
|
||||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,7 +0,0 @@
|
|||
Makefile
|
||||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,6 +0,0 @@
|
|||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,6 +0,0 @@
|
|||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,6 +0,0 @@
|
|||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,6 +0,0 @@
|
|||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,6 +0,0 @@
|
|||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,6 +0,0 @@
|
|||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,6 +0,0 @@
|
|||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,6 +0,0 @@
|
|||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,6 +0,0 @@
|
|||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,6 +0,0 @@
|
|||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,6 +0,0 @@
|
|||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,6 +0,0 @@
|
|||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,6 +0,0 @@
|
|||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,6 +0,0 @@
|
|||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -1,8 +0,0 @@
|
|||
Makefile
|
||||
YateLocal.mak
|
||||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -5,7 +5,7 @@
|
|||
* ASN.1 Library
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* ASN.1 Library
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
Makefile
|
||||
YateLocal.mak
|
||||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
* Author: Marian Podgoreanu
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
* Author: Marian Podgoreanu
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
* Author: Marian Podgoreanu
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
Makefile
|
||||
YateLocal.mak
|
||||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
Makefile
|
||||
YateLocal.mak
|
||||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
@ -30,10 +30,6 @@ using namespace TelEngine;
|
|||
// Ensure response code string representation is 3 digit long
|
||||
inline void setCode(String& dest, unsigned int code)
|
||||
{
|
||||
if (code >= 1000) {
|
||||
dest = "999";
|
||||
return;
|
||||
}
|
||||
char c[4];
|
||||
sprintf(c,"%03u",code);
|
||||
dest = c;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
Makefile
|
||||
YateLocal.mak
|
||||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -5,7 +5,7 @@
|
|||
* Yet Another Modem
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Yet Another Modem
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Yet Another Modem
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2023 Null Team
|
||||
* Copyright (C) 2004-2014 Null Team
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue