event_handlers/mod_event_socket
#event_handlers/mod_event_zmq
#event_handlers/mod_radius_cdr
+#event_handlers/mod_rayo
#event_handlers/mod_snmp
formats/mod_local_stream
formats/mod_native_file
--- /dev/null
+BASE=../../../..
+
+IKS_DIR=$(BASE)/libs/iksemel
+PCRE_DIR=$(BASE)/libs/pcre
+IKS_LA=$(IKS_DIR)/src/libiksemel.la
+PCRE_LA=$(PCRE_DIR)/libpcre.la
+LOCAL_CFLAGS += -I$(BASE)/libs/iksemel/include -I$(BASE)/libs/pcre
+LOCAL_OBJS= $(IKS_LA) \
+ $(PCRE_LA) \
+ iks_helpers.o \
+ nlsml.o \
+ rayo_components.o \
+ rayo_input_component.o \
+ rayo_output_component.o \
+ rayo_prompt_component.o \
+ rayo_record_component.o \
+ sasl.o \
+ srgs.o \
+ xmpp_streams.o
+LOCAL_SOURCES= \
+ iks_helpers.c \
+ nlsml.c \
+ rayo_components.c \
+ rayo_input_component.c \
+ rayo_output_component.c \
+ rayo_prompt_component.c \
+ rayo_record_component.c \
+ sasl.c \
+ srgs.c \
+ xmpp_streams.c
+include $(BASE)/build/modmake.rules
+
+$(IKS_LA): $(IKS_DIR) $(IKS_DIR)/.update
+ @cd $(IKS_DIR) && $(MAKE)
+ @$(TOUCH_TARGET)
+
+$(PCRE_LA): $(PCRE_DIR) $(PCRE_DIR)/.update
+ @cd $(PCRE_DIR) && $(MAKE)
+ @$(TOUCH_TARGET)
--- /dev/null
+<configuration name="acl.conf" description="Network Lists">
+ <network-lists>
+ <!--
+ These ACL's are automatically created on startup.
+
+ rfc1918.auto - RFC1918 Space
+ nat.auto - RFC1918 Excluding your local lan.
+ localnet.auto - ACL for your local lan.
+ loopback.auto - ACL for your local lan.
+ -->
+
+ <list name="lan" default="allow">
+ <node type="deny" cidr="192.168.42.0/24"/>
+ <node type="allow" cidr="192.168.42.42/32"/>
+ </list>
+
+ <!--
+ This will traverse the directory adding all users
+ with the cidr= tag to this ACL, when this ACL matches
+ the users variables and params apply as if they
+ digest authenticated.
+ -->
+ <list name="domains" default="deny">
+ <!-- domain= is special it scans the domain from the directory to build the ACL -->
+ <node type="allow" domain="$${domain}"/>
+ <!-- use cidr= if you wish to allow ip ranges to this domains acl. -->
+ <!-- <node type="allow" cidr="192.168.0.0/24"/> -->
+ </list>
+
+ <list name="rayo-clients" default="deny">
+ <node type="allow" cidr="192.168.0.39/32"/>
+ <node type="allow" cidr="192.168.0.59/32"/>
+ </list>
+
+ <list name="rayo-servers" default="deny">
+ <node type="allow" cidr="192.168.0.39/32"/>
+ </list>
+
+ </network-lists>
+</configuration>
+
--- /dev/null
+<configuration name="cdr_csv.conf" description="CDR CSV Format">
+ <settings>
+ <!-- 'cdr-csv' will always be appended to log-base -->
+ <!--<param name="log-base" value="/var/log"/>-->
+ <param name="default-template" value="example"/>
+ <!-- This is like the info app but after the call is hung up -->
+ <!--<param name="debug" value="true"/>-->
+ <param name="rotate-on-hup" value="true"/>
+ <!-- may be a b or ab -->
+ <param name="legs" value="a"/>
+ <!-- Only log in Master.csv -->
+ <!-- <param name="master-file-only" value="true"/> -->
+ </settings>
+ <templates>
+ <template name="sql">INSERT INTO cdr VALUES ("${caller_id_name}","${caller_id_number}","${destination_number}","${context}","${start_stamp}","${answer_stamp}","${end_stamp}","${duration}","${billsec}","${hangup_cause}","${uuid}","${bleg_uuid}", "${accountcode}");</template>
+ <template name="example">"${caller_id_name}","${caller_id_number}","${destination_number}","${context}","${start_stamp}","${answer_stamp}","${end_stamp}","${duration}","${billsec}","${hangup_cause}","${uuid}","${bleg_uuid}","${accountcode}","${read_codec}","${write_codec}"</template>
+ <template name="snom">"${caller_id_name}","${caller_id_number}","${destination_number}","${context}","${start_stamp}","${answer_stamp}","${end_stamp}","${duration}","${billsec}","${hangup_cause}","${uuid}","${bleg_uuid}", "${accountcode}","${read_codec}","${write_codec}","${sip_user_agent}","${call_clientcode}","${sip_rtp_rxstat}","${sip_rtp_txstat}","${sofia_record_file}"</template>
+ <template name="linksys">"${caller_id_name}","${caller_id_number}","${destination_number}","${context}","${start_stamp}","${answer_stamp}","${end_stamp}","${duration}","${billsec}","${hangup_cause}","${uuid}","${bleg_uuid}","${accountcode}","${read_codec}","${write_codec}","${sip_user_agent}","${sip_p_rtp_stat}"</template>
+ <template name="asterisk">"${accountcode}","${caller_id_number}","${destination_number}","${context}","${caller_id}","${channel_name}","${bridge_channel}","${last_app}","${last_arg}","${start_stamp}","${answer_stamp}","${end_stamp}","${duration}","${billsec}","${hangup_cause}","${amaflags}","${uuid}","${userfield}"</template>
+ <template name="opencdrrate">"${uuid}","${signal_bond}","${direction}","${ani}","${destination_number}","${answer_stamp}","${end_stamp}","${billsec}","${accountcode}","${userfield}","${network_addr}","${regex('${original_caller_id_name}'|^.)}","${sip_gateway_name}"</template>
+ </templates>
+</configuration>
+
--- /dev/null
+<!-- http://wiki.freeswitch.org/wiki/Mod_conference -->
+<!-- None of these paths are real if you want any of these options you need to really set them up -->
+<configuration name="conference.conf" description="Audio Conference">
+ <!-- Advertise certain presence on startup . -->
+ <advertise>
+ <room name="3001@$${domain}" status="FreeSWITCH"/>
+ </advertise>
+
+ <!-- These are the default keys that map when you do not specify a caller control group -->
+ <!-- Note: none and default are reserved names for group names. Disabled if dist-dtmf member flag is set. -->
+ <caller-controls>
+ <group name="default">
+ <control action="mute" digits="0"/>
+ <control action="deaf mute" digits="*"/>
+ <control action="energy up" digits="9"/>
+ <control action="energy equ" digits="8"/>
+ <control action="energy dn" digits="7"/>
+ <control action="vol talk up" digits="3"/>
+ <control action="vol talk zero" digits="2"/>
+ <control action="vol talk dn" digits="1"/>
+ <control action="vol listen up" digits="6"/>
+ <control action="vol listen zero" digits="5"/>
+ <control action="vol listen dn" digits="4"/>
+ <control action="hangup" digits="#"/>
+ </group>
+ </caller-controls>
+
+ <!-- Profiles are collections of settings you can reference by name. -->
+ <profiles>
+ <!--If no profile is specified it will default to "default"-->
+ <profile name="default">
+ <!-- Directory to drop CDR's
+ 'auto' means $PREFIX/logs/conference_cdr/<confernece_uuid>.cdr.xml
+ a non-absolute path means $PREFIX/logs/<value>/<confernece_uuid>.cdr.xml
+ absolute path means <value>/<confernece_uuid>.cdr.xml
+ -->
+ <!-- <param name="cdr-log-dir" value="auto"/> -->
+
+ <!-- Domain (for presence) -->
+ <param name="domain" value="$${domain}"/>
+ <!-- Sample Rate-->
+ <param name="rate" value="8000"/>
+ <!-- Number of milliseconds per frame -->
+ <param name="interval" value="20"/>
+ <!-- Energy level required for audio to be sent to the other users -->
+ <param name="energy-level" value="300"/>
+
+ <!--Can be | delim of waste|mute|deaf|dist-dtmf waste will always transmit data to each channel
+ even during silence. dist-dtmf propagates dtmfs to all other members, but channel controls
+ via dtmf will be disabled. -->
+ <!--<param name="member-flags" value="waste"/>-->
+
+ <!-- Name of the caller control group to use for this profile -->
+ <!-- <param name="caller-controls" value="some name"/> -->
+ <!-- Name of the caller control group to use for the moderator in this profile -->
+ <!-- <param name="moderator-controls" value="some name"/> -->
+ <!-- TTS Engine to use -->
+ <!--<param name="tts-engine" value="cepstral"/>-->
+ <!-- TTS Voice to use -->
+ <!--<param name="tts-voice" value="david"/>-->
+
+ <!-- If TTS is enabled all audio-file params beginning with -->
+ <!-- 'say:' will be considered text to say with TTS -->
+ <!-- Override the default path here, after which you use relative paths in the other sound params -->
+ <!-- Note: The default path is the conference's first caller's sound_prefix -->
+ <!--<param name="sound-prefix" value="$${sounds_dir}/en/us/callie"/>-->
+ <!-- File to play to acknowledge succees -->
+ <!--<param name="ack-sound" value="beep.wav"/>-->
+ <!-- File to play to acknowledge failure -->
+ <!--<param name="nack-sound" value="beeperr.wav"/>-->
+ <!-- File to play to acknowledge muted -->
+ <param name="muted-sound" value="conference/conf-muted.wav"/>
+ <!-- File to play to acknowledge unmuted -->
+ <param name="unmuted-sound" value="conference/conf-unmuted.wav"/>
+ <!-- File to play if you are alone in the conference -->
+ <param name="alone-sound" value="conference/conf-alone.wav"/>
+ <!-- File to play endlessly (nobody will ever be able to talk) -->
+ <!--<param name="perpetual-sound" value="perpetual.wav"/>-->
+ <!-- File to play when you're alone (music on hold)-->
+ <param name="moh-sound" value="$${hold_music}"/>
+ <!-- File to play when you join the conference -->
+ <param name="enter-sound" value="tone_stream://%(200,0,500,600,700)"/>
+ <!-- File to play when you leave the conference -->
+ <param name="exit-sound" value="tone_stream://%(500,0,300,200,100,50,25)"/>
+ <!-- File to play when you ae ejected from the conference -->
+ <param name="kicked-sound" value="conference/conf-kicked.wav"/>
+ <!-- File to play when the conference is locked -->
+ <param name="locked-sound" value="conference/conf-locked.wav"/>
+ <!-- File to play when the conference is locked during the call-->
+ <param name="is-locked-sound" value="conference/conf-is-locked.wav"/>
+ <!-- File to play when the conference is unlocked during the call-->
+ <param name="is-unlocked-sound" value="conference/conf-is-unlocked.wav"/>
+ <!-- File to play to prompt for a pin -->
+ <param name="pin-sound" value="conference/conf-pin.wav"/>
+ <!-- File to play to when the pin is invalid -->
+ <param name="bad-pin-sound" value="conference/conf-bad-pin.wav"/>
+ <!-- Conference pin -->
+ <!--<param name="pin" value="12345"/>-->
+ <!--<param name="moderator-pin" value="54321"/>-->
+ <!-- Max number of times the user can be prompted for PIN -->
+ <!--<param name="pin-retries" value="3"/>-->
+ <!-- Default Caller ID Name for outbound calls -->
+ <param name="caller-id-name" value="$${outbound_caller_name}"/>
+ <!-- Default Caller ID Number for outbound calls -->
+ <param name="caller-id-number" value="$${outbound_caller_id}"/>
+ <!-- Suppress start and stop talking events -->
+ <!-- <param name="suppress-events" value="start-talking,stop-talking"/> -->
+ <!-- enable comfort noise generation -->
+ <param name="comfort-noise" value="true"/>
+ <!-- Uncomment auto-record to toggle recording every conference call. -->
+ <!-- Another valid value is shout://user:pass@server.com/live.mp3 -->
+ <!--
+ <param name="auto-record" value="$${recordings_dir}/${conference_name}_${strftime(%Y-%m-%d-%H-%M-%S)}.wav"/>
+ -->
+
+ <!-- IVR digit machine timeouts -->
+ <!-- How much to wait between DTMF digits to match caller-controls -->
+ <!-- <param name="ivr-dtmf-timeout" value="500"/> -->
+ <!-- How much to wait for the first DTMF, 0 forever -->
+ <!-- <param name="ivr-input-timeout" value="0" /> -->
+ <!-- Delay before a conference is asked to be terminated -->
+ <!-- <param name="endconf-grace-time" value="120" /> -->
+ <!-- Can be | delim of wait-mod|audio-always|video-bridge|video-floor-only
+ wait_mod will wait until the moderator in,
+ audio-always will always mix audio from all members regardless they are talking or not -->
+ <!-- <param name="conference-flags" value="audio-always"/> -->
+ </profile>
+
+ <profile name="wideband">
+ <param name="domain" value="$${domain}"/>
+ <param name="rate" value="16000"/>
+ <param name="interval" value="20"/>
+ <param name="energy-level" value="300"/>
+ <!--<param name="sound-prefix" value="$${sounds_dir}/en/us/callie"/>-->
+ <param name="muted-sound" value="conference/conf-muted.wav"/>
+ <param name="unmuted-sound" value="conference/conf-unmuted.wav"/>
+ <param name="alone-sound" value="conference/conf-alone.wav"/>
+ <param name="moh-sound" value="$${hold_music}"/>
+ <param name="enter-sound" value="tone_stream://%(200,0,500,600,700)"/>
+ <param name="exit-sound" value="tone_stream://%(500,0,300,200,100,50,25)"/>
+ <param name="kicked-sound" value="conference/conf-kicked.wav"/>
+ <param name="locked-sound" value="conference/conf-locked.wav"/>
+ <param name="is-locked-sound" value="conference/conf-is-locked.wav"/>
+ <param name="is-unlocked-sound" value="conference/conf-is-unlocked.wav"/>
+ <param name="pin-sound" value="conference/conf-pin.wav"/>
+ <param name="bad-pin-sound" value="conference/conf-bad-pin.wav"/>
+ <param name="caller-id-name" value="$${outbound_caller_name}"/>
+ <param name="caller-id-number" value="$${outbound_caller_id}"/>
+ <param name="comfort-noise" value="true"/>
+ <!--<param name="tts-engine" value="flite"/>-->
+ <!--<param name="tts-voice" value="kal16"/>-->
+ </profile>
+
+ <profile name="ultrawideband">
+ <param name="domain" value="$${domain}"/>
+ <param name="rate" value="32000"/>
+ <param name="interval" value="20"/>
+ <param name="energy-level" value="300"/>
+ <!--<param name="sound-prefix" value="$${sounds_dir}/en/us/callie"/>-->
+ <param name="muted-sound" value="conference/conf-muted.wav"/>
+ <param name="unmuted-sound" value="conference/conf-unmuted.wav"/>
+ <param name="alone-sound" value="conference/conf-alone.wav"/>
+ <param name="moh-sound" value="$${hold_music}"/>
+ <param name="enter-sound" value="tone_stream://%(200,0,500,600,700)"/>
+ <param name="exit-sound" value="tone_stream://%(500,0,300,200,100,50,25)"/>
+ <param name="kicked-sound" value="conference/conf-kicked.wav"/>
+ <param name="locked-sound" value="conference/conf-locked.wav"/>
+ <param name="is-locked-sound" value="conference/conf-is-locked.wav"/>
+ <param name="is-unlocked-sound" value="conference/conf-is-unlocked.wav"/>
+ <param name="pin-sound" value="conference/conf-pin.wav"/>
+ <param name="bad-pin-sound" value="conference/conf-bad-pin.wav"/>
+ <param name="caller-id-name" value="$${outbound_caller_name}"/>
+ <param name="caller-id-number" value="$${outbound_caller_id}"/>
+ <param name="comfort-noise" value="true"/>
+ <!--<param name="tts-engine" value="flite"/>-->
+ <!--<param name="tts-voice" value="kal16"/>-->
+ </profile>
+
+ <profile name="cdquality">
+ <param name="domain" value="$${domain}"/>
+ <param name="rate" value="48000"/>
+ <param name="interval" value="10"/>
+ <param name="energy-level" value="300"/>
+ <!--<param name="sound-prefix" value="$${sounds_dir}/en/us/callie"/>-->
+ <param name="muted-sound" value="conference/conf-muted.wav"/>
+ <param name="unmuted-sound" value="conference/conf-unmuted.wav"/>
+ <param name="alone-sound" value="conference/conf-alone.wav"/>
+ <param name="moh-sound" value="$${hold_music}"/>
+ <param name="enter-sound" value="tone_stream://%(200,0,500,600,700)"/>
+ <param name="exit-sound" value="tone_stream://%(500,0,300,200,100,50,25)"/>
+ <param name="kicked-sound" value="conference/conf-kicked.wav"/>
+ <param name="locked-sound" value="conference/conf-locked.wav"/>
+ <param name="is-locked-sound" value="conference/conf-is-locked.wav"/>
+ <param name="is-unlocked-sound" value="conference/conf-is-unlocked.wav"/>
+ <param name="pin-sound" value="conference/conf-pin.wav"/>
+ <param name="bad-pin-sound" value="conference/conf-bad-pin.wav"/>
+ <param name="caller-id-name" value="$${outbound_caller_name}"/>
+ <param name="caller-id-number" value="$${outbound_caller_id}"/>
+ <param name="comfort-noise" value="true"/>
+ </profile>
+
+ <profile name="sla">
+ <param name="domain" value="$${domain}"/>
+ <param name="rate" value="16000"/>
+ <param name="interval" value="20"/>
+ <param name="caller-controls" value="none"/>
+ <param name="energy-level" value="200"/>
+ <param name="moh-sound" value="silence"/>
+ <param name="comfort-noise" value="true"/>
+ </profile>
+
+ </profiles>
+</configuration>
--- /dev/null
+<configuration name="console.conf" description="Console Logger">
+ <!-- pick a file name, a function name or 'all' -->
+ <!-- map as many as you need for specific debugging -->
+ <mappings>
+ <!--
+ name can be a file name, function name or 'all'
+ value is one or more of debug,info,notice,warning,err,crit,alert,all
+ See examples below
+
+
+ The following map is the default, which is all debug levels enabled:
+ <map name="all" value="debug,info,notice,warning,err,crit,alert"/>
+
+
+ Example: the following turns on debugging for error and critical levels only
+ <map name="all" value="err,crit"/>
+
+ NOTE: using map name="all" will override any other settings! If you
+ want a more specific set of console messages then you will need
+ to specify which files and/or functions you want to have debug
+ messages. One option is to turn on just the more critical
+ messages with map name="all", then specify the other types of
+ console messages you want to see for various files and functions.
+
+ Example: turn on ERROR, CRIT, ALERT for all modules, then specify other
+ levels for various modules and functions
+
+ <map name="all" value="err,crit,alert"/>
+ <map name="switch_loadable_module_process" value="all"/>
+ <map name="mod_local_stream.c" value="warning,debug"/>
+ <map name="mod_sndfile.c" value="warning,info,debug"/>
+ -->
+ <map name="all" value="console,debug,info,notice,warning,err,crit,alert"/>
+
+ <!--
+ You can use or modify this sample set of mappings. It turns on higher
+ level messages for all modules and then specifies extra lower level
+ messages for OpenZAP, Sofia, and switch core messages.
+
+ <map name="all" value="warning,err,crit,alert"/>
+ <map name="zap_analog.c" value="all"/>
+ <map name="zap_io.c" value="all"/>
+ <map name="zap_isdn.c" value="all"/>
+ <map name="zap_zt.c" value="all"/>
+ <map name="mod_openzap" value="all"/>
+ <map name="sofia.c" value="notice"/>
+ <map name="switch_core_state_machine.c" value="all"/>
+
+ -->
+ </mappings>
+ <settings>
+ <!-- comment or set to false for no color logging -->
+ <param name="colorize" value="true"/>
+ <param name="loglevel" value="$${console_loglevel}"/>
+ </settings>
+</configuration>
--- /dev/null
+<configuration name="distributor.conf" description="Distributor Configuration">
+ <lists>
+ <!-- every 10 calls to test you will get foo1 once and foo2 9 times...yes NINE TIMES! -->
+ <!-- this is not the same as 100 with 10 and 90 that would do foo1 10 times in a row then foo2 90 times in a row -->
+ <list name="test" total-weight="10">
+ <node name="foo1" weight="1"/>
+ <node name="foo2" weight="9"/>
+ </list>
+ </lists>
+</configuration>
--- /dev/null
+<configuration name="event_socket.conf" description="Socket Client">
+ <settings>
+ <param name="nat-map" value="false"/>
+ <param name="listen-ip" value="127.0.0.1"/>
+ <param name="listen-port" value="8021"/>
+ <param name="password" value="ClueCon"/>
+ <!--<param name="apply-inbound-acl" value="lan"/>-->
+ </settings>
+</configuration>
--- /dev/null
+<configuration name="hash.conf" description="Hash Configuration">
+ <remotes>
+ <!-- List of hosts from where to pull usage data -->
+ <!-- <remote name="Test1" host="10.0.0.10" port="8021" password="ClueCon" interval="1000" /> -->
+ </remotes>
+</configuration>
--- /dev/null
+<configuration name="http_cache.conf" description="HTTP GET cache">
+ <settings>
+ <!-- set to true if you want to enable http:// and https:// formats. Do not use if mod_httapi is also loaded -->
+ <param name="enable-file-formats" value="true"/>
+ <param name="max-urls" value="10000"/>
+ <param name="location" value="$${base_dir}/http_cache"/>
+ <param name="default-max-age" value="86400"/>
+ <param name="prefetch-thread-count" value="8"/>
+ <param name="prefetch-queue-size" value="100"/>
+ <!-- absolute path to CA bundle file -->
+ <param name="ssl-cacert" value="$${base_dir}/conf/cacert.pem"/>
+ <!-- verify certificates -->
+ <param name="ssl-verifypeer" value="true"/>
+ <!-- verify host name matches certificate -->
+ <param name="ssl-verifyhost" value="true"/>
+ </settings>
+</configuration>
--- /dev/null
+<configuration name="local_stream.conf" description="stream files from local dir">
+ <!-- fallback to default if requested moh class isn't found -->
+ <directory name="default" path="$${sounds_dir}/music/8000">
+ <param name="rate" value="8000"/>
+ <param name="shuffle" value="true"/>
+ <param name="channels" value="1"/>
+ <param name="interval" value="20"/>
+ <param name="timer-name" value="soft"/>
+ <!-- list of short files to break in with every so often -->
+ <!--<param name="chime-list" value="file1.wav,file2.wav"/>-->
+ <!-- frequency of break-in (seconds)-->
+ <!--<param name="chime-freq" value="30"/>-->
+ <!-- limit to how many seconds the file will play -->
+ <!--<param name="chime-max" value="500"/>-->
+ </directory>
+
+ <directory name="moh/8000" path="$${sounds_dir}/music/8000">
+ <param name="rate" value="8000"/>
+ <param name="shuffle" value="true"/>
+ <param name="channels" value="1"/>
+ <param name="interval" value="20"/>
+ <param name="timer-name" value="soft"/>
+ </directory>
+
+ <directory name="moh/16000" path="$${sounds_dir}/music/16000">
+ <param name="rate" value="16000"/>
+ <param name="shuffle" value="true"/>
+ <param name="channels" value="1"/>
+ <param name="interval" value="20"/>
+ <param name="timer-name" value="soft"/>
+ </directory>
+
+ <directory name="moh/32000" path="$${sounds_dir}/music/32000">
+ <param name="rate" value="32000"/>
+ <param name="shuffle" value="true"/>
+ <param name="channels" value="1"/>
+ <param name="interval" value="20"/>
+ <param name="timer-name" value="soft"/>
+ </directory>
+ <!--
+ <directory name="moh/48000" path="$${sounds_dir}/music/48000">
+ <param name="rate" value="48000"/>
+ <param name="shuffle" value="true"/>
+ <param name="channels" value="1"/>
+ <param name="interval" value="10"/>
+ <param name="timer-name" value="soft"/>
+ </directory>
+ -->
+</configuration>
--- /dev/null
+<configuration name="logfile.conf" description="File Logging">
+ <settings>
+ <!-- true to auto rotate on HUP, false to open/close -->
+ <param name="rotate-on-hup" value="true"/>
+ </settings>
+ <profiles>
+ <profile name="default">
+ <settings>
+ <!-- File to log to -->
+ <!--<param name="logfile" value="/var/log/freeswitch.log"/>-->
+ <!-- At this length in bytes rotate the log file (0 for never) -->
+ <param name="rollover" value="10485760"/>
+ <!-- Maximum number of log files to keep before wrapping -->
+ <!-- If this parameter is enabled, the log filenames will not include a date stamp -->
+ <!-- <param name="maximum-rotate" value="32"/> -->
+ <!-- Uncomment to prefix all log lines by the session's uuid -->
+ <param name="uuid" value="true" />
+ </settings>
+ <mappings>
+ <!--
+ name can be a file name, function name or 'all'
+ value is one or more of debug,info,notice,warning,err,crit,alert,all
+ Please see comments in console.conf.xml for more information
+ -->
+ <map name="all" value="debug,info,notice,warning,err,crit,alert"/>
+ </mappings>
+ </profile>
+ </profiles>
+</configuration>
--- /dev/null
+<configuration name="lua.conf" description="LUA Configuration">
+ <settings>
+
+ <!--
+ Specify local directories that will be searched for LUA modules
+ These entries will be pre-pended to the LUA_CPATH environment variable
+ -->
+ <!-- <param name="module-directory" value="/usr/lib/lua/5.1/?.so"/> -->
+ <!-- <param name="module-directory" value="/usr/local/lib/lua/5.1/?.so"/> -->
+
+ <!--
+ Specify local directories that will be searched for LUA scripts
+ These entries will be pre-pended to the LUA_PATH environment variable
+ -->
+ <!-- <param name="script-directory" value="/usr/local/lua/?.lua"/> -->
+ <!-- <param name="script-directory" value="$${base_dir}/scripts/?.lua"/> -->
+
+ <!--<param name="xml-handler-script" value="/dp.lua"/>-->
+ <!--<param name="xml-handler-bindings" value="dialplan"/>-->
+
+ <!--
+ The following options identifies a lua script that is launched
+ at startup and may live forever in the background.
+ You can define multiple lines, one for each script you
+ need to run.
+ -->
+ <!--<param name="startup-script" value="startup_script_1.lua"/>-->
+ <!--<param name="startup-script" value="startup_script_2.lua"/>-->
+ </settings>
+</configuration>
--- /dev/null
+<configuration name="memcache.conf" description="memcache Configuration">
+ <settings>
+ <!-- comma sep list of servers: eg: localhost,otherhost:port,anotherone -->
+ <param name="memcache-servers" value="localhost"/>
+ </settings>
+</configuration>
--- /dev/null
+<configuration name="modules.conf" description="Modules">
+ <modules>
+ <!-- Loggers -->
+ <load module="mod_console"/>
+ <load module="mod_logfile"/>
+
+ <!-- Event Handlers -->
+ <load module="mod_cdr_csv"/>
+ <load module="mod_event_socket"/>
+ <load module="mod_rayo"/>
+
+ <!-- Endpoints -->
+ <load module="mod_sofia"/>
+ <load module="mod_loopback"/>
+
+ <!-- Applications -->
+ <load module="mod_commands"/>
+ <load module="mod_conference"/>
+ <load module="mod_dptools"/>
+ <load module="mod_expr"/>
+ <load module="mod_hash"/>
+ <!--<load module="mod_distributor"/>-->
+ <load module="mod_esf"/>
+ <load module="mod_fsv"/>
+ <load module="mod_http_cache"/>
+
+ <!-- Dialplan Interfaces -->
+ <load module="mod_dialplan_xml"/>
+
+ <!-- Codec Interfaces -->
+ <load module="mod_spandsp"/>
+ <load module="mod_celt"/>
+ <load module="mod_speex"/>
+ <load module="mod_opus"/>
+ <load module="mod_ilbc"/>
+
+ <!-- File Format Interfaces -->
+ <load module="mod_sndfile"/>
+ <load module="mod_native_file"/>
+ <load module="mod_shell_stream"/>
+ <!--For icecast/mp3 streams/files-->
+ <load module="mod_shout"/>
+ <!--For local streams (play all the files in a directory)-->
+ <load module="mod_local_stream"/>
+ <load module="mod_tone_stream"/>
+ <load module="mod_ssml"/>
+
+ <!-- Languages -->
+ <load module="mod_lua"/>
+
+ <!-- ASR /TTS -->
+ <load module="mod_flite"/>
+ <load module="mod_pocketsphinx"/>
+ <!-- <load module="mod_tts_commandline"/> -->
+
+ <!-- Say -->
+ <!--load module="mod_say_de"/-->
+ <!--load module="mod_say_es"/-->
+ <!--load module="mod_say_fr"/-->
+ <!--load module="mod_say_hr"/-->
+ <!--load module="mod_say_it"/-->
+ <!--load module="mod_say_nl"/-->
+ <!--load module="mod_say_ru"/-->
+ <!--load module="mod_say_zh"/-->
+ <load module="mod_say_en"/>
+ <!--load module="mod_say_fa"/-->
+ <!--load module="mod_say_he"/-->
+ <!--load module="mod_say_hu"/-->
+ <!--load module="mod_say_ja"/-->
+ <!--load module="mod_say_pt"/-->
+ <!--load module="mod_say_th"/-->
+
+ </modules>
+</configuration>
--- /dev/null
+<configuration name="pocketsphinx.conf" description="PocketSphinx ASR Configuration">
+ <settings>
+ <param name="threshold" value="400"/>
+ <param name="silence-hits" value="25"/>
+ <param name="listen-hits" value="1"/>
+ <param name="auto-reload" value="true"/>
+ <!--<param name="language-weight" value="1"/>-->
+ <!--<param name="narrowband-model" value="communicator"/>-->
+ <!--<param name="wideband-model" value="wsj1"/>-->
+ <!--<param name="dictionary" value="default.dic"/>-->
+ </settings>
+</configuration>
--- /dev/null
+<configuration name="post_load_modules.conf" description="Modules">
+ <modules>
+ </modules>
+</configuration>
--- /dev/null
+<configuration name="presence_map.conf" description="PRESENCE MAP">
+ <domains>
+ <domain name="$${domain}">
+ <exten regex="3\d+" proto="conf"/>
+ </domain>
+ </domains>
+</configuration>
--- /dev/null
+<configuration name="rayo.conf" description="Rayo server config">
+
+ <!-- rayo params -->
+ <settings>
+ <param name="max-idle-sec" value="300"/>
+ <param name="mixer-conf-profile" value="sla"/>
+ </settings>
+
+ <!-- record component params -->
+ <record>
+ <param name="record-file-prefix" value="$${recordings_dir}/"/>
+ </record>
+
+ <!-- domain to use in from attribute of client messages -->
+ <domain name="$${rayo_domain_name}" shared-secret="ClueCon">
+
+ <!-- Listeners for new Rayo client connections -->
+ <!--listen type="c2s" port="5222" address="$${local_ip_v4}" acl="rayo-clients"/-->
+ <listen type="c2s" port="5222" address="$${rayo_ip}" acl=""/>
+
+ <!-- Listeners for new server connections -->
+ <!--listen type="s2s" port="5269" address="$${local_ip_v4}" acl="rayo-servers"/-->
+
+ <!-- servers to connect to -->
+ <!--connect port="5269" address="node.example.com" domain="example.com"/-->
+
+ <!-- Authorized users -->
+ <users>
+ <user name="usera" password="1"/>
+ </users>
+ </domain>
+
+ <!-- URI mapping to endpoint / gateway -->
+ <dial-gateways>
+ <dial-gateway uriprefix="default" dialprefix="sofia/gateway/outbound/" strip="0"/>
+ <dial-gateway uriprefix="sip:" dialprefix="sofia/external/" strip="0"/>
+ <dial-gateway uriprefix="tel:" dialprefix="sofia/gateway/outbound/" strip="4"/>
+ <!-- pass through user and sofia unaltered -->
+ <dial-gateway uriprefix="user" dialprefix="" strip=""/>
+ <dial-gateway uriprefix="sofia" dialprefix="" strip=""/>
+ </dial-gateways>
+
+</configuration>
--- /dev/null
+<configuration name="shout.conf" description="mod shout config">
+ <settings>
+ <!-- Don't change these unless you are insane -->
+ <!--<param name="decoder" value="i586"/>-->
+ <!--<param name="volume" value=".1"/>-->
+ <!--<param name="outscale" value="8192"/>-->
+ </settings>
+</configuration>
--- /dev/null
+<configuration name="sofia.conf" description="sofia Endpoint">
+
+ <global_settings>
+ <param name="log-level" value="0"/>
+ <!-- <param name="auto-restart" value="false"/> -->
+ <param name="debug-presence" value="0"/>
+ <!-- <param name="capture-server" value="udp:homer.domain.com:5060"/> -->
+ </global_settings>
+
+ <!--
+ The rabbit hole goes deep. This includes all the
+ profiles in the sip_profiles directory that is up
+ one level from this directory.
+ -->
+ <profiles>
+ <X-PRE-PROCESS cmd="include" data="../sip_profiles/*.xml"/>
+ </profiles>
+
+</configuration>
--- /dev/null
+<configuration name="spandsp.conf" description="SpanDSP config">
+ <modem-settings>
+<!--
+ total-modems set to N will create that many soft-modems.
+ If you use them with Hylafax you need the following for each one numbered 0..N:
+
+ 1) A line like this in /etc/inittab:
+
+ f0:2345:respawn:/usr/lib/fax/faxgetty /dev/FS0
+
+ 2) copy conf/config.FS0 to /var/spool/hylafax/etc (or wherver the appropriate dir is on your system)
+
+ Subsequent modem configs would incrment the 0 to 1 and so on.
+
+-->
+ <param name="total-modems" value="0"/>
+
+ <!-- Default context and dialplan to use on inbound calls from the modems -->
+ <param name="context" value="default"/>
+ <param name="dialplan" value="XML"/>
+
+ <!-- Extra tracing for debugging -->
+ <param name="verbose" value="false"/>
+ </modem-settings>
+
+ <fax-settings>
+ <param name="use-ecm" value="true"/>
+ <param name="verbose" value="false"/>
+ <param name="disable-v17" value="false"/>
+ <param name="ident" value="SpanDSP Fax Ident"/>
+ <param name="header" value="SpanDSP Fax Header"/>
+
+ <param name="spool-dir" value="/tmp"/>
+ <param name="file-prefix" value="faxrx"/>
+ </fax-settings>
+
+ <descriptors>
+
+ <!-- These tones are defined in Annex to ITU Operational Bulletin No. 781 - 1.II.2003 -->
+ <!-- Various Tones Used in National Networks (According to ITU-T Recommendation E.180)(03/1998) -->
+
+ <!-- North America -->
+ <descriptor name="1">
+ <tone name="CED_TONE">
+ <element freq1="2100" freq2="0" min="700" max="0"/>
+ </tone>
+ <tone name="SIT">
+ <element freq1="950" freq2="0" min="256" max="400"/>
+ <element freq1="1400" freq2="0" min="256" max="400"/>
+ <element freq1="1800" freq2="0" min="256" max="400"/>
+ </tone>
+ <tone name="RING_TONE" description="North America ring">
+ <element freq1="440" freq2="480" min="1200" max="0"/>
+ </tone>
+ <tone name="REORDER_TONE">
+ <element freq1="480" freq2="620" min="224" max="316"/>
+ <element freq1="0" freq2="0" min="168" max="352"/>
+ <element freq1="480" freq2="620" min="224" max="316"/>
+ </tone>
+ <tone name="BUSY_TONE">
+ <element freq1="480" freq2="620" min="464" max="536"/>
+ <element freq1="0" freq2="0" min="464" max="572"/>
+ <element freq1="480" freq2="620" min="464" max="536"/>
+ </tone>
+ </descriptor>
+
+ <!-- United Kingdom -->
+ <descriptor name="44">
+ <tone name="CED_TONE">
+ <element freq1="2100" freq2="0" min="500" max="0"/>
+ </tone>
+ <tone name="SIT">
+ <element freq1="950" freq2="0" min="256" max="400"/>
+ <element freq1="1400" freq2="0" min="256" max="400"/>
+ <element freq1="1800" freq2="0" min="256" max="400"/>
+ </tone>
+ <tone name="REORDER_TONE">
+ <element freq1="400" freq2="0" min="368" max="416"/>
+ <element freq1="0" freq2="0" min="336" max="368"/>
+ <element freq1="400" freq2="0" min="256" max="288"/>
+ <element freq1="0" freq2="0" min="512" max="544"/>
+ </tone>
+ <tone name="BUSY_TONE">
+ <element freq1="400" freq2="0" min="352" max="384"/>
+ <element freq1="0" freq2="0" min="352" max="384"/>
+ <element freq1="400" freq2="0" min="352" max="384"/>
+ <element freq1="0" freq2="0" min="352" max="384"/>
+ </tone>
+ </descriptor>
+
+ <!-- Germany -->
+ <descriptor name="49">
+ <tone name="CED_TONE">
+ <element freq1="2100" freq2="0" min="500" max="0"/>
+ </tone>
+ <tone name="SIT">
+ <element freq1="900" freq2="0" min="256" max="400"/>
+ <element freq1="1400" freq2="0" min="256" max="400"/>
+ <element freq1="1800" freq2="0" min="256" max="400"/>
+ </tone>
+ <tone name="REORDER_TONE">
+ <element freq1="425" freq2="0" min="224" max="272"/>
+ <element freq1="0" freq2="0" min="224" max="272"/>
+ </tone>
+ <tone name="BUSY_TONE">
+ <element freq1="425" freq2="0" min="464" max="516"/>
+ <element freq1="0" freq2="0" min="464" max="516"/>
+ </tone>
+ </descriptor>
+ </descriptors>
+
+</configuration>
+
--- /dev/null
+<configuration name="ssml.conf" description="SSML parser config">
+
+ <!-- voices in order of preference -->
+ <tts-voices>
+ <voice name="slt" language="en-US" gender="female" prefix="tts://flite|slt|"/>
+ <voice name="kal" language="en-US" gender="male" prefix="tts://flite|kal|"/>
+ <voice name="rms" language="en-US" gender="male" prefix="tts://flite|rms|"/>
+ <voice name="awb" language="en-US" gender="male" prefix="tts://flite|awb|"/>
+ </tts-voices>
+
+ <!-- maps ISO language to say module -->
+ <language-map>
+ <language iso="en-US" say-module="en" language="en"/>
+ </language-map>
+
+ <!-- say voices in order of preference -->
+ <say-voices>
+ <voice name="callie" language="en-US" gender="female" prefix="$${sounds_dir}/en/us/callie/"/>
+ </say-voices>
+
+ <!-- map interpret-as to say macros -->
+ <macros>
+ <macro name="cardinal" method="pronounced" type="number"/>
+ </macros>
+
+</configuration>
--- /dev/null
+<configuration name="switch.conf" description="Core Configuration">
+
+ <cli-keybindings>
+ <key name="1" value="help"/>
+ <key name="2" value="status"/>
+ <key name="3" value="show channels"/>
+ <key name="4" value="show calls"/>
+ <key name="5" value="sofia status"/>
+ <key name="6" value="reloadxml"/>
+ <key name="7" value="console loglevel 0"/>
+ <key name="8" value="console loglevel 7"/>
+ <key name="9" value="sofia status profile internal"/>
+ <key name="10" value="sofia profile internal siptrace on"/>
+ <key name="11" value="sofia profile internal siptrace off"/>
+ <key name="12" value="version"/>
+ </cli-keybindings>
+
+ <default-ptimes>
+ <!-- Set this to override the 20ms assumption of various codecs in the sdp with no ptime defined -->
+ <!-- <codec name="G729" ptime="40"/> -->
+ </default-ptimes>
+
+ <settings>
+ <!-- Colorize the Console -->
+ <param name="colorize-console" value="true"/>
+
+ <!-- Run the timer at 20ms by default and drop down as needed unless you set 1m-timer=true which was previous default -->
+ <!-- <param name="1ms-timer" value="true"/> -->
+
+ <!--
+ Set the Switch Name for HA environments.
+ When setting the switch name, it will override the system hostname for all DB and CURL requests
+ allowing cluster environments such as RHCS to have identical FreeSWITCH configurations but run
+ as different hostnames.
+ -->
+ <!-- <param name="switchname" value="freeswitch"/> -->
+
+ <!-- Maximum number of simultaneous DB handles open -->
+ <param name="max-db-handles" value="50"/>
+ <!-- Maximum number of seconds to wait for a new DB handle before failing -->
+ <param name="db-handle-timeout" value="10"/>
+
+ <!-- Minimum idle CPU before refusing calls -->
+ <!-- <param name="min-idle-cpu" value="25"/> -->
+
+ <!--
+ Max number of sessions to allow at any given time.
+
+ NOTICE: If you're driving 28 T1's in a single box you should set this to 644*2 or 1288
+ this will ensure you're able to use the entire DS3 without a problem. Otherwise you'll
+ be 144 channels short of always filling that DS3 up which can translate into waste.
+ -->
+ <param name="max-sessions" value="1000"/>
+ <!--Most channels to create per second -->
+ <param name="sessions-per-second" value="30"/>
+ <!-- Default Global Log Level - value is one of debug,info,notice,warning,err,crit,alert -->
+ <param name="loglevel" value="debug"/>
+
+ <!-- Set the core DEBUG level (0-10) -->
+ <!-- <param name="debug-level" value="10"/> -->
+
+ <!-- SQL Buffer length within rage of 32k to 10m -->
+ <!-- <param name="sql-buffer-len" value="1m"/> -->
+ <!-- Maximum SQL Buffer length must be greater than sql-buffer-len -->
+ <!-- <param name="max-sql-buffer-len" value="2m"/> -->
+
+ <!--
+ The min-dtmf-duration specifies the minimum DTMF duration to use on
+ outgoing events. Events shorter than this will be increased in duration
+ to match min_dtmf_duration. You cannot configure a dtmf duration on a
+ profile that is less than this setting. You may increase this value,
+ but cannot set it lower than 400. This value cannot exceed
+ max-dtmf-duration. -->
+ <!-- <param name="min-dtmf-duration" value="400"/> -->
+
+ <!--
+ The max-dtmf-duration caps the playout of a DTMF event at the specified
+ duration. Events exceeding this duration will be truncated to this
+ duration. You cannot configure a duration on a profile that exceeds
+ this setting. This setting can be lowered, but cannot exceed 192000.
+ This setting cannot be set lower than min_dtmf_duration. -->
+ <!-- <param name="max-dtmf-duration" value="192000"/> -->
+
+ <!--
+ The default_dtmf_duration specifies the DTMF duration to use on
+ originated DTMF events or on events that are received without a
+ duration specified. This value can be increased or lowered. This
+ value is lower-bounded by min_dtmf_duration and upper-bounded by
+ max-dtmf-duration\. -->
+ <!-- <param name="default-dtmf-duration" value="2000"/> -->
+
+ <!--
+ If you want to send out voicemail notifications via Windows you'll need to change the mailer-app
+ variable to the setting below:
+
+ <param name="mailer-app" value="msmtp"/>
+
+ Do not change mailer-app-args.
+ You will also need to download a sendmail clone for Windows (msmtp). This version works without issue:
+ http://msmtp.sourceforge.net/index.html. Download and copy the .exe to %winddir%\system32.
+ You'll need to create a small config file for smtp credentials (host name, authentication, tls, etc.) in
+ %USERPROFILE%\Application Data\ called "msmtprc.txt". Below is a sample copy of this file:
+
+ ###################################
+ # The SMTP server of the provider.
+ account provider
+ host smtp.myisp.com
+ from john@myisp.com
+ auth login
+ user johndoe
+ password mypassword
+
+ # Set a default account
+ account default : provider
+ ###################################
+
+ -->
+
+ <param name="mailer-app" value="sendmail"/>
+ <param name="mailer-app-args" value="-t"/>
+ <param name="dump-cores" value="yes"/>
+
+ <!-- Enable verbose channel events to include every detail about a channel on every event -->
+ <!-- <param name="verbose-channel-events" value="no"/> -->
+
+ <!-- Enable clock nanosleep -->
+ <!-- <param name="enable-clock-nanosleep" value="true"/> -->
+
+ <!-- Enable monotonic timing -->
+ <!-- <param name="enable-monotonic-timing" value="true"/> -->
+
+ <!-- NEEDS DOCUMENTATION -->
+ <!-- <param name="enable-softtimer-timerfd" value="true"/> -->
+ <!-- <param name="enable-cond-yield" value="true"/> -->
+ <!-- <param name="enable-timer-matrix" value="true"/> -->
+ <!-- <param name="threaded-system-exec" value="true"/> -->
+ <!-- <param name="tipping-point" value="0"/> -->
+ <!-- <param name="timer-affinity" value="disabled"/> -->
+ <!-- NEEDS DOCUMENTATION -->
+
+ <!-- RTP port range -->
+ <!-- <param name="rtp-start-port" value="16384"/> -->
+ <!-- <param name="rtp-end-port" value="32768"/> -->
+
+ <param name="rtp-enable-zrtp" value="true"/>
+
+ <!-- <param name="core-db-dsn" value="pgsql://hostaddr=127.0.0.1 dbname=freeswitch user=freeswitch password='' options='-c client_min_messages=NOTICE' application_name='freeswitch'" /> -->
+ <!-- <param name="core-db-dsn" value="dsn:username:password" /> -->
+ <!--
+ Allow to specify the sqlite db at a different location (In this example, move it to ramdrive for
+ better performance on most linux distro (note, you loose the data if you reboot))
+ -->
+ <!-- <param name="core-db-name" value="/dev/shm/core.db" /> -->
+
+ <!-- The system will create all the db schemas automatically, set this to false to avoid this behaviour -->
+ <!-- <param name="auto-create-schemas" value="true"/> -->
+ <!-- <param name="auto-clear-sql" value="true"/> -->
+ <!-- <param name="enable-early-hangup" value="true"/> -->
+
+ <!-- <param name="core-dbtype" value="MSSQL"/> -->
+
+ <!-- Allow multiple registrations to the same account in the central registration table -->
+ <!-- <param name="multiple-registrations" value="true"/> -->
+
+ </settings>
+
+</configuration>
+
--- /dev/null
+<configuration name="timezones.conf" description="Timezones">
+ <timezones>
+ <zone name="Africa/Abidjan" value="GMT0" />
+ <zone name="Africa/Accra" value="GMT0" />
+ <zone name="Africa/Addis_Ababa" value="EAT-3" />
+ <zone name="Africa/Algiers" value="CET-1" />
+ <zone name="Africa/Asmara" value="EAT-3" />
+ <zone name="Africa/Asmera" value="EAT-3" />
+ <zone name="Africa/Bamako" value="GMT0" />
+ <zone name="Africa/Bangui" value="WAT-1" />
+ <zone name="Africa/Banjul" value="GMT0" />
+ <zone name="Africa/Bissau" value="GMT0" />
+ <zone name="Africa/Blantyre" value="CAT-2" />
+ <zone name="Africa/Brazzaville" value="WAT-1" />
+ <zone name="Africa/Bujumbura" value="CAT-2" />
+ <zone name="Africa/Cairo" value="EET-2EEST,M4.5.5/1,M9.5.5/3" />
+ <zone name="Africa/Casablanca" value="WET0" />
+ <zone name="Africa/Ceuta" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Africa/Conakry" value="GMT0" />
+ <zone name="Africa/Dakar" value="GMT0" />
+ <zone name="Africa/Dar_es_Salaam" value="EAT-3" />
+ <zone name="Africa/Djibouti" value="EAT-3" />
+ <zone name="Africa/Douala" value="WAT-1" />
+ <zone name="Africa/El_Aaiun" value="WET0" />
+ <zone name="Africa/Freetown" value="GMT0" />
+ <zone name="Africa/Gaborone" value="CAT-2" />
+ <zone name="Africa/Harare" value="CAT-2" />
+ <zone name="Africa/Johannesburg" value="SAST-2" />
+ <zone name="Africa/Kampala" value="EAT-3" />
+ <zone name="Africa/Khartoum" value="EAT-3" />
+ <zone name="Africa/Kigali" value="CAT-2" />
+ <zone name="Africa/Kinshasa" value="WAT-1" />
+ <zone name="Africa/Lagos" value="WAT-1" />
+ <zone name="Africa/Libreville" value="WAT-1" />
+ <zone name="Africa/Lome" value="GMT0" />
+ <zone name="Africa/Luanda" value="WAT-1" />
+ <zone name="Africa/Lubumbashi" value="CAT-2" />
+ <zone name="Africa/Lusaka" value="CAT-2" />
+ <zone name="Africa/Malabo" value="WAT-1" />
+ <zone name="Africa/Maputo" value="CAT-2" />
+ <zone name="Africa/Maseru" value="SAST-2" />
+ <zone name="Africa/Mbabane" value="SAST-2" />
+ <zone name="Africa/Mogadishu" value="EAT-3" />
+ <zone name="Africa/Monrovia" value="GMT0" />
+ <zone name="Africa/Nairobi" value="EAT-3" />
+ <zone name="Africa/Ndjamena" value="WAT-1" />
+ <zone name="Africa/Niamey" value="WAT-1" />
+ <zone name="Africa/Nouakchott" value="GMT0" />
+ <zone name="Africa/Ouagadougou" value="GMT0" />
+ <zone name="Africa/Porto-Novo" value="WAT-1" />
+ <zone name="Africa/Sao_Tome" value="GMT0" />
+ <zone name="Africa/Timbuktu" value="GMT0" />
+ <zone name="Africa/Tripoli" value="EET-2" />
+ <zone name="Africa/Tunis" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Africa/Windhoek" value="WAT-1WAST,M9.1.0,M4.1.0" />
+
+ <zone name="America/Adak" value="HAST10HADT,M3.2.0,M11.1.0" />
+ <zone name="America/Anchorage" value="AKST9AKDT,M3.2.0,M11.1.0" />
+ <zone name="America/Anguilla" value="AST4" />
+ <zone name="America/Antigua" value="AST4" />
+ <zone name="America/Araguaina" value="BRT3" />
+ <zone name="America/Argentina/Buenos_Aires" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Argentina/Catamarca" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Argentina/ComodRivadavia" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Argentina/Cordoba" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Argentina/Jujuy" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Argentina/La_Rioja" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Argentina/Mendoza" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Argentina/Rio_Gallegos" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Argentina/San_Juan" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Argentina/San_Luis" value="ART3" />
+ <zone name="America/Argentina/Tucuman" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Argentina/Ushuaia" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Aruba" value="AST4" />
+ <zone name="America/Asuncion" value="PYT4PYST,M10.3.0/0,M3.2.0/0" />
+ <zone name="America/Atikokan" value="EST5" />
+ <zone name="America/Atka" value="HAST10HADT,M3.2.0,M11.1.0" />
+ <zone name="America/Bahia" value="BRT3" />
+ <zone name="America/Barbados" value="AST4" />
+ <zone name="America/Belem" value="BRT3" />
+ <zone name="America/Belize" value="CST6" />
+ <zone name="America/Blanc-Sablon" value="AST4" />
+ <zone name="America/Boa_Vista" value="AMT4" />
+ <zone name="America/Bogota" value="COT5" />
+ <zone name="America/Boise" value="MST7MDT,M3.2.0,M11.1.0" />
+ <zone name="America/Buenos_Aires" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Cambridge_Bay" value="MST7MDT,M3.2.0,M11.1.0" />
+ <zone name="America/Campo_Grande" value="AMT4AMST,M10.2.0/0,M2.3.0/0" />
+ <zone name="America/Cancun" value="CST6CDT,M4.1.0,M10.5.0" />
+ <zone name="America/Caracas" value="VET4:30" />
+ <zone name="America/Catamarca" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Cayenne" value="GFT3" />
+ <zone name="America/Cayman" value="EST5" />
+ <zone name="America/Chicago" value="CST6CDT,M3.2.0,M11.1.0" />
+ <zone name="America/Chihuahua" value="MST7MDT,M4.1.0,M10.5.0" />
+ <zone name="America/Coral_Harbour" value="EST5" />
+ <zone name="America/Cordoba" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Costa_Rica" value="CST6" />
+ <zone name="America/Cuiaba" value="AMT4AMST,M10.2.0/0,M2.3.0/0" />
+ <zone name="America/Curacao" value="AST4" />
+ <zone name="America/Danmarkshavn" value="GMT0" />
+ <zone name="America/Dawson" value="PST8PDT,M3.2.0,M11.1.0" />
+ <zone name="America/Dawson_Creek" value="MST7" />
+ <zone name="America/Denver" value="MST7MDT,M3.2.0,M11.1.0" />
+ <zone name="America/Detroit" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Dominica" value="AST4" />
+ <zone name="America/Edmonton" value="MST7MDT,M3.2.0,M11.1.0" />
+ <zone name="America/Eirunepe" value="ACT5" />
+ <zone name="America/El_Salvador" value="CST6" />
+ <zone name="America/Ensenada" value="PST8PDT,M4.1.0,M10.5.0" />
+ <zone name="America/Fortaleza" value="BRT3" />
+ <zone name="America/Fort_Wayne" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Glace_Bay" value="AST4ADT,M3.2.0,M11.1.0" />
+ <zone name="America/Godthab" value="WGST" />
+ <zone name="America/Goose_Bay" value="AST4ADT,M3.2.0/0:01,M11.1.0/0:01" />
+ <zone name="America/Grand_Turk" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Grenada" value="AST4" />
+ <zone name="America/Guadeloupe" value="AST4" />
+ <zone name="America/Guatemala" value="CST6" />
+ <zone name="America/Guayaquil" value="ECT5" />
+ <zone name="America/Guyana" value="GYT4" />
+ <zone name="America/Halifax" value="AST4ADT,M3.2.0,M11.1.0" />
+ <zone name="America/Havana" value="CST5CDT,M3.3.0/0,M10.5.0/1" />
+ <zone name="America/Hermosillo" value="MST7" />
+ <zone name="America/Indiana/Indianapolis" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Indiana/Knox" value="CST6CDT,M3.2.0,M11.1.0" />
+ <zone name="America/Indiana/Marengo" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Indiana/Petersburg" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Indianapolis" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Indiana/Tell_City" value="CST6CDT,M3.2.0,M11.1.0" />
+ <zone name="America/Indiana/Vevay" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Indiana/Vincennes" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Indiana/Winamac" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Inuvik" value="MST7MDT,M3.2.0,M11.1.0" />
+ <zone name="America/Iqaluit" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Jamaica" value="EST5" />
+ <zone name="America/Jujuy" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Juneau" value="AKST9AKDT,M3.2.0,M11.1.0" />
+ <zone name="America/Kentucky/Louisville" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Kentucky/Monticello" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Knox_IN" value="CST6CDT,M3.2.0,M11.1.0" />
+ <zone name="America/La_Paz" value="BOT4" />
+ <zone name="America/Lima" value="PET5" />
+ <zone name="America/Los_Angeles" value="PST8PDT,M3.2.0,M11.1.0" />
+ <zone name="America/Louisville" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Maceio" value="BRT3" />
+ <zone name="America/Managua" value="CST6" />
+ <zone name="America/Manaus" value="AMT4" />
+ <zone name="America/Marigot" value="AST4" />
+ <zone name="America/Martinique" value="AST4" />
+ <zone name="America/Mazatlan" value="MST7MDT,M4.1.0,M10.5.0" />
+ <zone name="America/Mendoza" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Menominee" value="CST6CDT,M3.2.0,M11.1.0" />
+ <zone name="America/Merida" value="CST6CDT,M4.1.0,M10.5.0" />
+ <zone name="America/Mexico_City" value="CST6CDT,M4.1.0,M10.5.0" />
+ <zone name="America/Miquelon" value="PMST3PMDT,M3.2.0,M11.1.0" />
+ <zone name="America/Moncton" value="AST4ADT,M3.2.0,M11.1.0" />
+ <zone name="America/Monterrey" value="CST6CDT,M4.1.0,M10.5.0" />
+ <zone name="America/Montevideo" value="UYT3UYST,M10.1.0,M3.2.0" />
+ <zone name="America/Montreal" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Montserrat" value="AST4" />
+ <zone name="America/Nassau" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/New_York" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Nipigon" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Nome" value="AKST9AKDT,M3.2.0,M11.1.0" />
+ <zone name="America/Noronha" value="FNT2" />
+ <zone name="America/North_Dakota/Center" value="CST6CDT,M3.2.0,M11.1.0" />
+ <zone name="America/North_Dakota/New_Salem" value="CST6CDT,M3.2.0,M11.1.0" />
+ <zone name="America/Panama" value="EST5" />
+ <zone name="America/Pangnirtung" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Paramaribo" value="SRT3" />
+ <zone name="America/Phoenix" value="MST7" />
+ <zone name="America/Port-au-Prince" value="EST5" />
+ <zone name="America/Porto_Acre" value="ACT5" />
+ <zone name="America/Port_of_Spain" value="AST4" />
+ <zone name="America/Porto_Velho" value="AMT4" />
+ <zone name="America/Puerto_Rico" value="AST4" />
+ <zone name="America/Rainy_River" value="CST6CDT,M3.2.0,M11.1.0" />
+ <zone name="America/Rankin_Inlet" value="CST6CDT,M3.2.0,M11.1.0" />
+ <zone name="America/Recife" value="BRT3" />
+ <zone name="America/Regina" value="CST6" />
+ <zone name="America/Resolute" value="EST5" />
+ <zone name="America/Rio_Branco" value="ACT5" />
+ <zone name="America/Rosario" value="ART3ARST,M10.1.0/0,M3.3.0/0" />
+ <zone name="America/Santiago" value="CLST" />
+ <zone name="America/Santo_Domingo" value="AST4" />
+ <zone name="America/Sao_Paulo" value="BRT3BRST,M10.2.0/0,M2.3.0/0" />
+ <zone name="America/Scoresbysund" value="EGT1EGST,M3.5.0/0,M10.5.0/1" />
+ <zone name="America/Shiprock" value="MST7MDT,M3.2.0,M11.1.0" />
+ <zone name="America/St_Barthelemy" value="AST4" />
+ <zone name="America/St_Johns" value="NST3:30NDT,M3.2.0/0:01,M11.1.0/0:01" />
+ <zone name="America/St_Kitts" value="AST4" />
+ <zone name="America/St_Lucia" value="AST4" />
+ <zone name="America/St_Thomas" value="AST4" />
+ <zone name="America/St_Vincent" value="AST4" />
+ <zone name="America/Swift_Current" value="CST6" />
+ <zone name="America/Tegucigalpa" value="CST6" />
+ <zone name="America/Thule" value="AST4ADT,M3.2.0,M11.1.0" />
+ <zone name="America/Thunder_Bay" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Tijuana" value="PST8PDT,M4.1.0,M10.5.0" />
+ <zone name="America/Toronto" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="America/Tortola" value="AST4" />
+ <zone name="America/Vancouver" value="PST8PDT,M3.2.0,M11.1.0" />
+ <zone name="America/Virgin" value="AST4" />
+ <zone name="America/Whitehorse" value="PST8PDT,M3.2.0,M11.1.0" />
+ <zone name="America/Winnipeg" value="CST6CDT,M3.2.0,M11.1.0" />
+ <zone name="America/Yakutat" value="AKST9AKDT,M3.2.0,M11.1.0" />
+ <zone name="America/Yellowknife" value="MST7MDT,M3.2.0,M11.1.0" />
+
+ <zone name="Antarctica/Casey" value="WST-8" />
+ <zone name="Antarctica/Davis" value="DAVT-7" />
+ <zone name="Antarctica/DumontDUrville" value="DDUT-10" />
+ <zone name="Antarctica/Mawson" value="MAWT-6" />
+ <zone name="Antarctica/McMurdo" value="NZST-12NZDT,M9.5.0,M4.1.0/3" />
+ <zone name="Antarctica/Palmer" value="CLST" />
+ <zone name="Antarctica/Rothera" value="ROTT3" />
+ <zone name="Antarctica/South_Pole" value="NZST-12NZDT,M9.5.0,M4.1.0/3" />
+ <zone name="Antarctica/Syowa" value="SYOT-3" />
+ <zone name="Antarctica/Vostok" value="VOST-6" />
+
+ <zone name="Arctic/Longyearbyen" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+
+ <zone name="Asia/Aden" value="AST-3" />
+ <zone name="Asia/Almaty" value="ALMT-6" />
+ <zone name="Asia/Amman" value="EET-2EEST,M3.5.4/0,M10.5.5/1" />
+ <zone name="Asia/Anadyr" value="ANAT-12ANAST,M3.5.0,M10.5.0/3" />
+ <zone name="Asia/Aqtau" value="AQTT-5" />
+ <zone name="Asia/Aqtobe" value="AQTT-5" />
+ <zone name="Asia/Ashgabat" value="TMT-5" />
+ <zone name="Asia/Ashkhabad" value="TMT-5" />
+ <zone name="Asia/Baghdad" value="AST-3" />
+ <zone name="Asia/Bahrain" value="AST-3" />
+ <zone name="Asia/Baku" value="AZT-4AZST,M3.5.0/4,M10.5.0/5" />
+ <zone name="Asia/Bangkok" value="ICT-7" />
+ <zone name="Asia/Beirut" value="EET-2EEST,M3.5.0/0,M10.5.0/0" />
+ <zone name="Asia/Bishkek" value="KGT-6" />
+ <zone name="Asia/Brunei" value="BNT-8" />
+ <zone name="Asia/Calcutta" value="IST-5:30" />
+ <zone name="Asia/Choibalsan" value="CHOT-9" />
+ <zone name="Asia/Chongqing" value="CST-8" />
+ <zone name="Asia/Chungking" value="CST-8" />
+ <zone name="Asia/Colombo" value="IST-5:30" />
+ <zone name="Asia/Dacca" value="BDT-6" />
+ <zone name="Asia/Damascus" value="EET-2EEST,M4.1.5/0,J274/0" />
+ <zone name="Asia/Dhaka" value="BDT-6" />
+ <zone name="Asia/Dili" value="TLT-9" />
+ <zone name="Asia/Dubai" value="GST-4" />
+ <zone name="Asia/Dushanbe" value="TJT-5" />
+ <zone name="Asia/Gaza" value="EET-2EEST,J91/0,M9.2.4" />
+ <zone name="Asia/Harbin" value="CST-8" />
+ <zone name="Asia/Ho_Chi_Minh" value="ICT-7" />
+ <zone name="Asia/Hong_Kong" value="HKT-8" />
+ <zone name="Asia/Hovd" value="HOVT-7" />
+ <zone name="Asia/Irkutsk" value="IRKT-8IRKST,M3.5.0,M10.5.0/3" />
+ <zone name="Asia/Istanbul" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Asia/Jakarta" value="WIT-7" />
+ <zone name="Asia/Jayapura" value="EIT-9" />
+ <zone name="Asia/Jerusalem" value="IDDT" />
+ <zone name="Asia/Kabul" value="AFT-4:30" />
+ <zone name="Asia/Kamchatka" value="PETT-12PETST,M3.5.0,M10.5.0/3" />
+ <zone name="Asia/Karachi" value="PKT-5" />
+ <zone name="Asia/Kashgar" value="CST-8" />
+ <zone name="Asia/Katmandu" value="NPT-5:45" />
+ <zone name="Asia/Kolkata" value="IST-5:30" />
+ <zone name="Asia/Krasnoyarsk" value="KRAT-7KRAST,M3.5.0,M10.5.0/3" />
+ <zone name="Asia/Kuala_Lumpur" value="MYT-8" />
+ <zone name="Asia/Kuching" value="MYT-8" />
+ <zone name="Asia/Kuwait" value="AST-3" />
+ <zone name="Asia/Macao" value="CST-8" />
+ <zone name="Asia/Macau" value="CST-8" />
+ <zone name="Asia/Magadan" value="MAGT-11MAGST,M3.5.0,M10.5.0/3" />
+ <zone name="Asia/Makassar" value="CIT-8" />
+ <zone name="Asia/Manila" value="PHT-8" />
+ <zone name="Asia/Muscat" value="GST-4" />
+ <zone name="Asia/Nicosia" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Asia/Novosibirsk" value="NOVT-6NOVST,M3.5.0,M10.5.0/3" />
+ <zone name="Asia/Omsk" value="OMST-6OMSST,M3.5.0,M10.5.0/3" />
+ <zone name="Asia/Oral" value="ORAT-5" />
+ <zone name="Asia/Phnom_Penh" value="ICT-7" />
+ <zone name="Asia/Pontianak" value="WIT-7" />
+ <zone name="Asia/Pyongyang" value="KST-9" />
+ <zone name="Asia/Qatar" value="AST-3" />
+ <zone name="Asia/Qyzylorda" value="QYZT-6" />
+ <zone name="Asia/Rangoon" value="MMT-6:30" />
+ <zone name="Asia/Riyadh" value="AST-3" />
+ <zone name="Asia/Riyadh87" value="zzz-3:07:04" />
+ <zone name="Asia/Riyadh88" value="zzz-3:07:04" />
+ <zone name="Asia/Riyadh89" value="zzz-3:07:04" />
+ <zone name="Asia/Saigon" value="ICT-7" />
+ <zone name="Asia/Sakhalin" value="SAKT-10SAKST,M3.5.0,M10.5.0/3" />
+ <zone name="Asia/Samarkand" value="UZT-5" />
+ <zone name="Asia/Seoul" value="KST-9" />
+ <zone name="Asia/Shanghai" value="CST-8" />
+ <zone name="Asia/Singapore" value="SGT-8" />
+ <zone name="Asia/Taipei" value="CST-8" />
+ <zone name="Asia/Tashkent" value="UZT-5" />
+ <zone name="Asia/Tbilisi" value="GET-4" />
+ <zone name="Asia/Tehran" value="IRDT" />
+ <zone name="Asia/Tel_Aviv" value="IDDT" />
+ <zone name="Asia/Thimbu" value="BTT-6" />
+ <zone name="Asia/Thimphu" value="BTT-6" />
+ <zone name="Asia/Tokyo" value="JST-9" />
+ <zone name="Asia/Ujung_Pandang" value="CIT-8" />
+ <zone name="Asia/Ulaanbaatar" value="ULAT-8" />
+ <zone name="Asia/Ulan_Bator" value="ULAT-8" />
+ <zone name="Asia/Urumqi" value="CST-8" />
+ <zone name="Asia/Vientiane" value="ICT-7" />
+ <zone name="Asia/Vladivostok" value="VLAT-10VLAST,M3.5.0,M10.5.0/3" />
+ <zone name="Asia/Yakutsk" value="YAKT-9YAKST,M3.5.0,M10.5.0/3" />
+ <zone name="Asia/Yekaterinburg" value="YEKT-5YEKST,M3.5.0,M10.5.0/3" />
+ <zone name="Asia/Yerevan" value="AMT-4AMST,M3.5.0,M10.5.0/3" />
+
+ <zone name="Atlantic/Azores" value="AZOT1AZOST,M3.5.0/0,M10.5.0/1" />
+ <zone name="Atlantic/Bermuda" value="AST4ADT,M3.2.0,M11.1.0" />
+ <zone name="Atlantic/Canary" value="WET0WEST,M3.5.0/1,M10.5.0" />
+ <zone name="Atlantic/Cape_Verde" value="CVT1" />
+ <zone name="Atlantic/Faeroe" value="WET0WEST,M3.5.0/1,M10.5.0" />
+ <zone name="Atlantic/Faroe" value="WET0WEST,M3.5.0/1,M10.5.0" />
+ <zone name="Atlantic/Jan_Mayen" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Atlantic/Madeira" value="WET0WEST,M3.5.0/1,M10.5.0" />
+ <zone name="Atlantic/Reykjavik" value="GMT0" />
+ <zone name="Atlantic/South_Georgia" value="GST2" />
+ <zone name="Atlantic/Stanley" value="FKT4FKST,M9.1.0,M4.3.0" />
+ <zone name="Atlantic/St_Helena" value="GMT0" />
+
+ <zone name="Australia/ACT" value="EST-10EST,M10.1.0,M4.1.0/3" />
+ <zone name="Australia/Adelaide" value="CST-9:30CST,M10.1.0,M4.1.0/3" />
+ <zone name="Australia/Brisbane" value="EST-10" />
+ <zone name="Australia/Broken_Hill" value="CST-9:30CST,M10.1.0,M4.1.0/3" />
+ <zone name="Australia/Canberra" value="EST-10EST,M10.1.0,M4.1.0/3" />
+ <zone name="Australia/Currie" value="EST-10EST,M10.1.0,M4.1.0/3" />
+ <zone name="Australia/Darwin" value="CST-9:30" />
+ <zone name="Australia/Eucla" value="CWST-8:45" />
+ <zone name="Australia/Hobart" value="EST-10EST,M10.1.0,M4.1.0/3" />
+ <zone name="Australia/LHI" value="LHST-10:30LHST-11,M10.1.0,M4.1.0" />
+ <zone name="Australia/Lindeman" value="EST-10" />
+ <zone name="Australia/Lord_Howe" value="LHST-10:30LHST-11,M10.1.0,M4.1.0" />
+ <zone name="Australia/Melbourne" value="EST-10EST,M10.1.0,M4.1.0/3" />
+ <zone name="Australia/North" value="CST-9:30" />
+ <zone name="Australia/NSW" value="EST-10EST,M10.1.0,M4.1.0/3" />
+ <zone name="Australia/Perth" value="WST-8" />
+ <zone name="Australia/Queensland" value="EST-10" />
+ <zone name="Australia/South" value="CST-9:30CST,M10.1.0,M4.1.0/3" />
+ <zone name="Australia/Sydney" value="EST-10EST,M10.1.0,M4.1.0/3" />
+ <zone name="Australia/Tasmania" value="EST-10EST,M10.1.0,M4.1.0/3" />
+ <zone name="Australia/Victoria" value="EST-10EST,M10.1.0,M4.1.0/3" />
+ <zone name="Australia/West" value="WST-8" />
+ <zone name="Australia/Yancowinna" value="CST-9:30CST,M10.1.0,M4.1.0/3" />
+
+ <zone name="Brazil/Acre" value="ACT5" />
+ <zone name="Brazil/DeNoronha" value="FNT2" />
+ <zone name="Brazil/East" value="BRT3BRST,M10.2.0/0,M2.3.0/0" />
+ <zone name="Brazil/West" value="AMT4" />
+
+ <zone name="Canada/Atlantic" value="AST4ADT,M3.2.0,M11.1.0" />
+ <zone name="Canada/Central" value="CST6CDT,M3.2.0,M11.1.0" />
+ <zone name="Canada/Eastern" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="Canada/East-Saskatchewan" value="CST6" />
+ <zone name="Canada/Mountain" value="MST7MDT,M3.2.0,M11.1.0" />
+ <zone name="Canada/Newfoundland" value="NST3:30NDT,M3.2.0/0:01,M11.1.0/0:01" />
+ <zone name="Canada/Pacific" value="PST8PDT,M3.2.0,M11.1.0" />
+ <zone name="Canada/Saskatchewan" value="CST6" />
+ <zone name="Canada/Yukon" value="PST8PDT,M3.2.0,M11.1.0" />
+
+ <zone name="Chile/Continental" value="CLST" />
+ <zone name="Chile/EasterIsland" value="EASST" />
+
+ <zone name="Etc/GMT" value="GMT0" />
+ <zone name="Etc/GMT0" value="GMT0" />
+ <zone name="Etc/GMT-0" value="GMT0" />
+ <zone name="Etc/GMT+0" value="GMT0" />
+ <zone name="Etc/GMT-1" value="GMT-1" />
+ <zone name="Etc/GMT+1" value="GMT+1" />
+ <zone name="Etc/GMT-10" value="GMT-10" />
+ <zone name="Etc/GMT+10" value="GMT+10" />
+ <zone name="Etc/GMT-11" value="GMT-11" />
+ <zone name="Etc/GMT+11" value="GMT+11" />
+ <zone name="Etc/GMT-12" value="GMT-12" />
+ <zone name="Etc/GMT+12" value="GMT+12" />
+ <zone name="Etc/GMT-13" value="GMT-13" />
+ <zone name="Etc/GMT-14" value="GMT-14" />
+ <zone name="Etc/GMT-2" value="GMT-2" />
+ <zone name="Etc/GMT+2" value="GMT+2" />
+ <zone name="Etc/GMT-3" value="GMT-3" />
+ <zone name="Etc/GMT+3" value="GMT+3" />
+ <zone name="Etc/GMT-4" value="GMT-4" />
+ <zone name="Etc/GMT+4" value="GMT+4" />
+ <zone name="Etc/GMT-5" value="GMT-5" />
+ <zone name="Etc/GMT+5" value="GMT+5" />
+ <zone name="Etc/GMT-6" value="GMT-6" />
+ <zone name="Etc/GMT+6" value="GMT+6" />
+ <zone name="Etc/GMT-7" value="GMT-7" />
+ <zone name="Etc/GMT+7" value="GMT+7" />
+ <zone name="Etc/GMT-8" value="GMT-8" />
+ <zone name="Etc/GMT+8" value="GMT+8" />
+ <zone name="Etc/GMT-9" value="GMT-9" />
+ <zone name="Etc/GMT+9" value="GMT+9" />
+ <zone name="Etc/Greenwich" value="GMT0" />
+ <zone name="Etc/UCT" value="UCT0" />
+ <zone name="Etc/Universal" value="UTC0" />
+ <zone name="Etc/UTC" value="UTC0" />
+ <zone name="Etc/Zulu" value="UTC0" />
+
+ <zone name="Europe/Amsterdam" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Andorra" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Athens" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Belfast" value="GMT0BST,M3.5.0/1,M10.5.0" />
+ <zone name="Europe/Belgrade" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Berlin" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Bratislava" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Brussels" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Bucharest" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Budapest" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Chisinau" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Copenhagen" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Dublin" value="GMT0IST,M3.5.0/1,M10.5.0" />
+ <zone name="Europe/Gibraltar" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Guernsey" value="GMT0BST,M3.5.0/1,M10.5.0" />
+ <zone name="Europe/Helsinki" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Isle_of_Man" value="GMT0BST,M3.5.0/1,M10.5.0" />
+ <zone name="Europe/Istanbul" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Jersey" value="GMT0BST,M3.5.0/1,M10.5.0" />
+ <zone name="Europe/Kaliningrad" value="EET-2EEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Kiev" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Lisbon" value="WET0WEST,M3.5.0/1,M10.5.0" />
+ <zone name="Europe/Ljubljana" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/London" value="GMT0BST,M3.5.0/1,M10.5.0" />
+ <zone name="Europe/Luxembourg" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Madrid" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Malta" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Mariehamn" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Minsk" value="EET-2EEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Monaco" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Moscow" value="MSK-3MSD,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Nicosia" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Oslo" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Paris" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Podgorica" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Prague" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Riga" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Rome" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Samara" value="SAMT-4SAMST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/San_Marino" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Sarajevo" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Simferopol" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Skopje" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Sofia" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Stockholm" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Tallinn" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Tirane" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Tiraspol" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Uzhgorod" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Vaduz" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Vatican" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Vienna" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Vilnius" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Volgograd" value="VOLT-3VOLST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Warsaw" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Zagreb" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+ <zone name="Europe/Zaporozhye" value="EET-2EEST,M3.5.0/3,M10.5.0/4" />
+ <zone name="Europe/Zurich" value="CET-1CEST,M3.5.0,M10.5.0/3" />
+
+ <zone name="Indian/Antananarivo" value="EAT-3" />
+ <zone name="Indian/Chagos" value="IOT-6" />
+ <zone name="Indian/Christmas" value="CXT-7" />
+ <zone name="Indian/Cocos" value="CCT-6:30" />
+ <zone name="Indian/Comoro" value="EAT-3" />
+ <zone name="Indian/Kerguelen" value="TFT-5" />
+ <zone name="Indian/Mahe" value="SCT-4" />
+ <zone name="Indian/Maldives" value="MVT-5" />
+ <zone name="Indian/Mauritius" value="MUT-4" />
+ <zone name="Indian/Mayotte" value="EAT-3" />
+ <zone name="Indian/Reunion" value="RET-4" />
+
+ <zone name="Mexico/BajaNorte" value="PST8PDT,M4.1.0,M10.5.0" />
+ <zone name="Mexico/BajaSur" value="MST7MDT,M4.1.0,M10.5.0" />
+ <zone name="Mexico/General" value="CST6CDT,M4.1.0,M10.5.0" />
+
+ <zone name="Mideast/Riyadh87" value="zzz-3:07:04" />
+ <zone name="Mideast/Riyadh88" value="zzz-3:07:04" />
+ <zone name="Mideast/Riyadh89" value="zzz-3:07:04" />
+
+ <zone name="Pacific/Apia" value="WST11" />
+ <zone name="Pacific/Auckland" value="NZST-12NZDT,M9.5.0,M4.1.0/3" />
+ <zone name="Pacific/Chatham" value="CHAST-12:45CHADT,M9.5.0/2:45,M4.1.0/3:45" />
+ <zone name="Pacific/Easter" value="EASST" />
+ <zone name="Pacific/Efate" value="VUT-11" />
+ <zone name="Pacific/Enderbury" value="PHOT-13" />
+ <zone name="Pacific/Fakaofo" value="TKT10" />
+ <zone name="Pacific/Fiji" value="FJT-12" />
+ <zone name="Pacific/Funafuti" value="TVT-12" />
+ <zone name="Pacific/Galapagos" value="GALT6" />
+ <zone name="Pacific/Gambier" value="GAMT9" />
+ <zone name="Pacific/Guadalcanal" value="SBT-11" />
+ <zone name="Pacific/Guam" value="ChST-10" />
+ <zone name="Pacific/Honolulu" value="HST10" />
+ <zone name="Pacific/Johnston" value="HST10" />
+ <zone name="Pacific/Kiritimati" value="LINT-14" />
+ <zone name="Pacific/Kosrae" value="KOST-11" />
+ <zone name="Pacific/Kwajalein" value="MHT-12" />
+ <zone name="Pacific/Majuro" value="MHT-12" />
+ <zone name="Pacific/Marquesas" value="MART9:30" />
+ <zone name="Pacific/Midway" value="SST11" />
+ <zone name="Pacific/Nauru" value="NRT-12" />
+ <zone name="Pacific/Niue" value="NUT11" />
+ <zone name="Pacific/Norfolk" value="NFT-11:30" />
+ <zone name="Pacific/Noumea" value="NCT-11" />
+ <zone name="Pacific/Pago_Pago" value="SST11" />
+ <zone name="Pacific/Palau" value="PWT-9" />
+ <zone name="Pacific/Pitcairn" value="PST8" />
+ <zone name="Pacific/Ponape" value="PONT-11" />
+ <zone name="Pacific/Port_Moresby" value="PGT-10" />
+ <zone name="Pacific/Rarotonga" value="CKT10" />
+ <zone name="Pacific/Saipan" value="ChST-10" />
+ <zone name="Pacific/Samoa" value="SST11" />
+ <zone name="Pacific/Tahiti" value="TAHT10" />
+ <zone name="Pacific/Tarawa" value="GILT-12" />
+ <zone name="Pacific/Tongatapu" value="TOT-13" />
+ <zone name="Pacific/Truk" value="TRUT-10" />
+ <zone name="Pacific/Wake" value="WAKT-12" />
+ <zone name="Pacific/Wallis" value="WFT-12" />
+ <zone name="Pacific/Yap" value="TRUT-10" />
+
+ <zone name="SystemV/AST4" value="AST4" />
+ <zone name="SystemV/AST4ADT" value="AST4ADT,M3.2.0,M11.1.0" />
+ <zone name="SystemV/CST6" value="CST6" />
+ <zone name="SystemV/CST6CDT" value="CST6CDT,M3.2.0,M11.1.0" />
+ <zone name="SystemV/EST5" value="EST5" />
+ <zone name="SystemV/EST5EDT" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="SystemV/HST10" value="HST10" />
+ <zone name="SystemV/MST7" value="MST7" />
+ <zone name="SystemV/MST7MDT" value="MST7MDT,M3.2.0,M11.1.0" />
+ <zone name="SystemV/PST8" value="PST8" />
+ <zone name="SystemV/PST8PDT" value="PST8PDT,M3.2.0,M11.1.0" />
+ <zone name="SystemV/YST9" value="GAMT9" />
+ <zone name="SystemV/YST9YDT" value="AKST9AKDT,M3.2.0,M11.1.0" />
+
+ <zone name="US/Alaska" value="AKST9AKDT,M3.2.0,M11.1.0" />
+ <zone name="US/Aleutian" value="HAST10HADT,M3.2.0,M11.1.0" />
+ <zone name="US/Arizona" value="MST7" />
+ <zone name="US/Central" value="CST6CDT,M3.2.0,M11.1.0" />
+ <zone name="US/Eastern" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="US/East-Indiana" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="US/Hawaii" value="HST10" />
+ <zone name="US/Indiana-Starke" value="CST6CDT,M3.2.0,M11.1.0" />
+ <zone name="US/Michigan" value="EST5EDT,M3.2.0,M11.1.0" />
+ <zone name="US/Mountain" value="MST7MDT,M3.2.0,M11.1.0" />
+ <zone name="US/Pacific" value="PST8PDT,M3.2.0,M11.1.0" />
+ <zone name="US/Samoa" value="SST11" />
+ </timezones>
+</configuration>
--- /dev/null
+##
+## ca-bundle.crt -- Bundle of CA Root Certificates
+##
+## Certificate data from Mozilla as of: Sun Feb 19 04:03:37 2012
+##
+## This is a bundle of X.509 certificates of public Certificate Authorities
+## (CA). These were automatically extracted from Mozilla's root certificates
+## file (certdata.txt). This file can be found in the mozilla source tree:
+## http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1
+##
+## It contains the certificates in PEM format and therefore
+## can be directly used with curl / libcurl / php_curl, or with
+## an Apache+mod_ssl webserver for SSL client authentication.
+## Just configure this file as the SSLCACertificateFile.
+##
+
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1994-2000
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+# @(#) $RCSfile: certdata.txt,v $ $Revision: 1.82 $ $Date: 2012/02/18 21:41:46 $
+
+GTE CyberTrust Global Root
+==========================
+-----BEGIN CERTIFICATE-----
+MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg
+Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG
+A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEz
+MjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL
+Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0
+IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4u
+sJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcql
+HHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQID
+AQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMW
+M4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OF
+NMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/
+-----END CERTIFICATE-----
+
+Thawte Server CA
+================
+-----BEGIN CERTIFICATE-----
+MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT
+DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs
+dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UE
+AxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5j
+b20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNV
+BAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29u
+c3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcG
+A1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0
+ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl
+/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg7
+1CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzAR
+MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9J
+GubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZ
+GCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc=
+-----END CERTIFICATE-----
+
+Thawte Premium Server CA
+========================
+-----BEGIN CERTIFICATE-----
+MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgT
+DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs
+dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UE
+AxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZl
+ckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYT
+AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMU
+VGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2
+aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZ
+cHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2
+aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIh
+Udib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/
+qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAm
+SCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUIhfzJATj/Tb7yFkJD57taRvvBxhEf
+8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7t
+UCemDaYj+bvLpgcUQg==
+-----END CERTIFICATE-----
+
+Equifax Secure CA
+=================
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE
+ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
+MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT
+B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB
+nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR
+fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW
+8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG
+A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE
+CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG
+A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS
+spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB
+Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961
+zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB
+BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95
+70+sB3c4
+-----END CERTIFICATE-----
+
+Digital Signature Trust Co. Global CA 1
+=======================================
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UE
+ChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMTAeFw05ODEy
+MTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFs
+IFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUA
+A4GLADCBhwKBgQCgbIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJE
+NySZj9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlVSn5JTe2i
+o74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBo
+BgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0
+dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw
+IoAPMTk5ODEyMTAxODEwMjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQY
+MBaAFGp5fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i+DAM
+BgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GB
+ACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lNQseSJqBcNJo4cvj9axY+IO6CizEq
+kzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4
+RbyhkwS7hp86W0N6w4pl
+-----END CERTIFICATE-----
+
+Digital Signature Trust Co. Global CA 3
+=======================================
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UE
+ChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMjAeFw05ODEy
+MDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFs
+IFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUA
+A4GLADCBhwKBgQC/k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGOD
+VvsoLeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3oTQPMx7JS
+xhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBo
+BgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0
+dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw
+IoAPMTk5ODEyMDkxOTE3MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQY
+MBaAFB6CTShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5WzAM
+BgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GB
+AEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHRxdf0CiUPPXiBng+xZ8SQTGPdXqfi
+up/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVLB3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1
+mPnHfxsb1gYgAlihw6ID
+-----END CERTIFICATE-----
+
+Verisign Class 3 Public Primary Certification Authority
+=======================================================
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMx
+FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVow
+XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz
+IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94
+f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol
+hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtMEivPLCYA
+TxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59Ah
+WM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2Omuf
+Tqj/ZA1k
+-----END CERTIFICATE-----
+
+Verisign Class 3 Public Primary Certification Authority - G2
+============================================================
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT
+MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy
+eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln
+biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
+dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT
+MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy
+eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln
+biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
+dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCO
+FoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71
+lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQAB
+MA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT
+1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTD
+Oaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9
+-----END CERTIFICATE-----
+
+GlobalSign Root CA
+==================
+-----BEGIN CERTIFICATE-----
+MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx
+GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds
+b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV
+BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD
+VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa
+DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc
+THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb
+Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP
+c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX
+gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF
+AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj
+Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG
+j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH
+hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC
+X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
+-----END CERTIFICATE-----
+
+GlobalSign Root CA - R2
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv
+YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
+bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
+aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
+bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6
+ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp
+s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN
+S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL
+TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C
+ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
+FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i
+YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN
+BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp
+9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu
+01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7
+9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
+TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
+-----END CERTIFICATE-----
+
+ValiCert Class 1 VA
+===================
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp
+b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
+YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh
+bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIy
+MjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0
+d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEg
+UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0
+LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIi
+GQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCm
+DuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwG
+lN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8sogTLDAHkY7FkX
+icnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPwnXS3qT6gpf+2SQMT2iLM7XGCK5nP
+Orf1LXLI
+-----END CERTIFICATE-----
+
+ValiCert Class 2 VA
+===================
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp
+b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
+YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh
+bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw
+MTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0
+d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIg
+UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0
+LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVC
+CSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7Rf
+ZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZ
+SWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbV
+UjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8
+W9ViH0Pd
+-----END CERTIFICATE-----
+
+RSA Root Certificate 1
+======================
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp
+b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
+YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh
+bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw
+MjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0
+d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMg
+UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0
+LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td
+3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89H
+BFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs
+3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0WuPIqpsHEzXcjF
+V9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/APhmcGcwTTYJBtYze4D1gCCAPRX5r
+on+jjBXu
+-----END CERTIFICATE-----
+
+Verisign Class 3 Public Primary Certification Authority - G3
+============================================================
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV
+UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
+cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
+IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
+dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv
+cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg
+Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1
+EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc
+cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw
+EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj
+055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
+ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f
+j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
+/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0
+xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa
+t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
+-----END CERTIFICATE-----
+
+Verisign Class 4 Public Primary Certification Authority - G3
+============================================================
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV
+UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
+cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
+IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
+dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv
+cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkg
+Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaS
+tBO3IFsJ+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM
+8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdLMEYH5IBtptiW
+Lugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XYufTsgsbSPZUd5cBPhMnZo0QoBmrX
+Razwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
+j/ola09b5KROJ1WrIhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt
+mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm
+fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c2NU8Qh0XwRJd
+RTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtG
+UPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg==
+-----END CERTIFICATE-----
+
+Entrust.net Secure Server CA
+============================
+-----BEGIN CERTIFICATE-----
+MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMCVVMxFDASBgNV
+BAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkg
+cmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRl
+ZDE6MDgGA1UEAxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIG
+A1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBi
+eSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1p
+dGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQ
+aO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5
+gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcw
+ggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHYpIHVMIHSMQsw
+CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5l
+dC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
+bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
+dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkw
+NTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0Bow
+HQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA
+BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyN
+Ewr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9
+n9cd2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
+-----END CERTIFICATE-----
+
+Entrust.net Premium 2048 Secure Server CA
+=========================================
+-----BEGIN CERTIFICATE-----
+MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u
+ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp
+bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV
+BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx
+NzUwNTFaFw0xOTEyMjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3
+d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl
+MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u
+ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL
+Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr
+hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW
+nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi
+VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo3QwcjARBglghkgBhvhC
+AQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGAvtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdER
+gL7YibkIozH5oSQJFrlwMB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0B
+AQUFAAOCAQEAWUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo
+oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQh7A6tcOdBTcS
+o8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18f3v/rxzP5tsHrV7bhZ3QKw0z
+2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfNB/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjX
+OP/swNlQ8C5LWK5Gb9Auw2DaclVyvUxFnmG6v4SBkgPR0ml8xQ==
+-----END CERTIFICATE-----
+
+Baltimore CyberTrust Root
+=========================
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE
+ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li
+ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC
+SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs
+dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME
+uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB
+UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C
+G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9
+XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr
+l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI
+VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB
+BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh
+cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5
+hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa
+Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H
+RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
+-----END CERTIFICATE-----
+
+Equifax Secure Global eBusiness CA
+==================================
+-----BEGIN CERTIFICATE-----
+MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
+RXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNp
+bmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMx
+HDAaBgNVBAoTE0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEds
+b2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRV
+PEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzN
+qfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxn
+hcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j
+BBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hs
+MA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okEN
+I7SS+RkAZ70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIY
+NMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV
+-----END CERTIFICATE-----
+
+Equifax Secure eBusiness CA 1
+=============================
+-----BEGIN CERTIFICATE-----
+MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
+RXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENB
+LTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UE
+ChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNz
+IENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ
+1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4a
+IZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBk
+MBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4MlIR21kW
+Nl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQF
+AAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5
+lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+
+KpYrtWKmpj29f5JZzVoqgrI3eQ==
+-----END CERTIFICATE-----
+
+Equifax Secure eBusiness CA 2
+=============================
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEXMBUGA1UE
+ChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0y
+MB4XDTk5MDYyMzEyMTQ0NVoXDTE5MDYyMzEyMTQ0NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoT
+DkVxdWlmYXggU2VjdXJlMSYwJAYDVQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0EtMjCB
+nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF7Y6yEb3+6+e0dMKP/wXn
+2Z0GvxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKDpkWNYmi7hRsgcDKqQM2mll/EcTc/BPO3QSQ5
+BxoeLmFYoBIL5aXfxavqN3HMHMg3OrmXUqesxWoklE6ce8/AatbfIb0CAwEAAaOCAQkwggEFMHAG
+A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUx
+JjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0yMQ0wCwYDVQQDEwRDUkwxMBoG
+A1UdEAQTMBGBDzIwMTkwNjIzMTIxNDQ1WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9e
+uSBIplBqy/3YIHqngnYwHQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQFMAMB
+Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAAyGgq3oThr1
+jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia
+78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkty3D1E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUm
+V+GRMOrN
+-----END CERTIFICATE-----
+
+AddTrust Low-Value Services Root
+================================
+-----BEGIN CERTIFICATE-----
+MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
+QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU
+cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw
+CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO
+ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6
+54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr
+oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1
+Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui
+GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w
+HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD
+AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw
+HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt
+ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph
+iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY
+eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr
+mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj
+ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk=
+-----END CERTIFICATE-----
+
+AddTrust External Root
+======================
+-----BEGIN CERTIFICATE-----
+MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
+QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD
+VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw
+NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU
+cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg
+Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821
++iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw
+Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo
+aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy
+2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7
+7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P
+BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL
+VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk
+VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB
+IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl
+j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
+6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355
+e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u
+G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
+-----END CERTIFICATE-----
+
+AddTrust Public Services Root
+=============================
+-----BEGIN CERTIFICATE-----
+MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
+QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU
+cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ
+BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l
+dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu
+nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i
+d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG
+Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw
+HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G
+A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux
+FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G
+A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4
+JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL
++YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao
+GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9
+Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H
+EufOX1362KqxMy3ZdvJOOjMMK7MtkAY=
+-----END CERTIFICATE-----
+
+AddTrust Qualified Certificates Root
+====================================
+-----BEGIN CERTIFICATE-----
+MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
+QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU
+cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx
+CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ
+IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx
+64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3
+KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o
+L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR
+wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU
+MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/
+BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE
+BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y
+azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD
+ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG
+GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X
+dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze
+RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB
+iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE=
+-----END CERTIFICATE-----
+
+Entrust Root Certification Authority
+====================================
+-----BEGIN CERTIFICATE-----
+MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV
+BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw
+b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG
+A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0
+MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu
+MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu
+Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v
+dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz
+A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww
+Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68
+j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN
+rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw
+DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1
+MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH
+hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
+A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM
+Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa
+v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS
+W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
+tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
+-----END CERTIFICATE-----
+
+RSA Security 2048 v3
+====================
+-----BEGIN CERTIFICATE-----
+MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK
+ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy
+MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb
+BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7
+Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb
+WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH
+KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP
++Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/
+MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E
+FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY
+v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj
+0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj
+VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395
+nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA
+pKnXwiJPZ9d37CAFYd4=
+-----END CERTIFICATE-----
+
+GeoTrust Global CA
+==================
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
+Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw
+MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j
+LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo
+BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet
+8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc
+T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU
+vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD
+AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk
+DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q
+zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4
+d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2
+mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p
+XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm
+Mw==
+-----END CERTIFICATE-----
+
+GeoTrust Global CA 2
+====================
+-----BEGIN CERTIFICATE-----
+MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
+R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw
+MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j
+LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/
+NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k
+LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA
+Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b
+HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF
+MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH
+K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7
+srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh
+ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL
+OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC
+x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF
+H4z1Ir+rzoPz4iIprn2DQKi6bA==
+-----END CERTIFICATE-----
+
+GeoTrust Universal CA
+=====================
+-----BEGIN CERTIFICATE-----
+MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
+R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1
+MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu
+Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t
+JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e
+RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs
+7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d
+8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V
+qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga
+Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB
+Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu
+KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08
+ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0
+XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB
+hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc
+aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2
+qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL
+oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK
+xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF
+KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2
+DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK
+xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU
+p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI
+P/rmMuGNG2+k5o7Y+SlIis5z/iw=
+-----END CERTIFICATE-----
+
+GeoTrust Universal CA 2
+=======================
+-----BEGIN CERTIFICATE-----
+MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
+R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0
+MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg
+SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0
+DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17
+j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q
+JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a
+QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2
+WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP
+20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn
+ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC
+SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG
+8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2
++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E
+BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z
+dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ
+4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+
+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq
+A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg
+Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP
+pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d
+FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp
+gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm
+X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
+-----END CERTIFICATE-----
+
+America Online Root Certification Authority 1
+=============================================
+-----BEGIN CERTIFICATE-----
+MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
+QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp
+Y2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkG
+A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg
+T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CG
+v2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44z
+DyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145LcxVR5lu9Rh
+sCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP
+8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0T
+AQH/BAUwAwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Z
+o/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQB8itEf
+GDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkFZu90821fnZmv9ov761KyBZiibyrF
+VL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft
+3OJvx8Fi8eNy1gTIdGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g
+Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds
+sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7
+-----END CERTIFICATE-----
+
+America Online Root Certification Authority 2
+=============================================
+-----BEGIN CERTIFICATE-----
+MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
+QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp
+Y2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkG
+A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg
+T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC206B89en
+fHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8
+f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE18aO6lhO
+qKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JN
+RvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0
+gBe4lL8BPeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn
+6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9W6Wa6897Gqid
+FEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZo2C7HK2JNDJiuEMhBnIMoVxtRsX6
+Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnj
+B453cMor9H124HhnAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op
+aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE
+AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmnxPBUlgtk87FY
+T15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p
++DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXg
+JXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//Zoy
+zH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgO
+ZtMADjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh
+1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZZLF0Kjhf
+GEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y3WRayhgoPmMEEf0cjQAPuDff
+Z4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuP
+cX/9XhmgD0uRuMRUvAawRY8mkaKO/qk=
+-----END CERTIFICATE-----
+
+Visa eCommerce Root
+===================
+-----BEGIN CERTIFICATE-----
+MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG
+EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug
+QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2
+WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm
+VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv
+bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL
+F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b
+RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0
+TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI
+/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs
+GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG
+MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc
+CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW
+YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz
+zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu
+YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt
+398znM/jra6O1I7mT1GvFpLgXPYHDw==
+-----END CERTIFICATE-----
+
+Certum Root CA
+==============
+-----BEGIN CERTIFICATE-----
+MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK
+ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla
+Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u
+by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x
+wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL
+kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ
+89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K
+Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P
+NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+
+GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg
+GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/
+0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS
+qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw==
+-----END CERTIFICATE-----
+
+Comodo AAA Services root
+========================
+-----BEGIN CERTIFICATE-----
+MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
+R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
+TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw
+MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl
+c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
+BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG
+C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs
+i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW
+Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH
+Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK
+Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f
+BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl
+cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz
+LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm
+7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
+Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z
+8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C
+12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
+-----END CERTIFICATE-----
+
+Comodo Secure Services root
+===========================
+-----BEGIN CERTIFICATE-----
+MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
+R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
+TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw
+MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu
+Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi
+BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP
+9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc
+rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC
+oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V
+p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E
+FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
+gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj
+YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm
+aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm
+4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj
+Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL
+DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw
+pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H
+RR3B7Hzs/Sk=
+-----END CERTIFICATE-----
+
+Comodo Trusted Services root
+============================
+-----BEGIN CERTIFICATE-----
+MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
+R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
+TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw
+MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h
+bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw
+IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7
+3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y
+/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6
+juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS
+ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud
+DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp
+ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl
+cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw
+uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32
+pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA
+BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l
+R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O
+9y5Xt5hwXsjEeLBi
+-----END CERTIFICATE-----
+
+QuoVadis Root CA
+================
+-----BEGIN CERTIFICATE-----
+MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE
+ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
+eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz
+MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp
+cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD
+EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk
+J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL
+F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL
+YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen
+AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w
+PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y
+ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7
+MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj
+YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs
+ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
+Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW
+Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu
+BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw
+FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6
+tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo
+fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul
+LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x
+gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi
+5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi
+5nrQNiOKSnQ2+Q==
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 2
+==================
+-----BEGIN CERTIFICATE-----
+MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
+EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx
+ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6
+XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk
+lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB
+lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy
+lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt
+66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn
+wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh
+D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy
+BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie
+J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud
+DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU
+a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
+ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv
+Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3
+UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm
+VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK
++JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW
+IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1
+WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X
+f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II
+4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8
+VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 3
+==================
+-----BEGIN CERTIFICATE-----
+MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
+EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx
+OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg
+DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij
+KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K
+DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv
+BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp
+p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8
+nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX
+MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM
+Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz
+uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT
+BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj
+YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
+aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB
+BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD
+VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4
+ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE
+AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV
+qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s
+hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z
+POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2
+Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp
+8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC
+bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu
+g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p
+vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr
+qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto=
+-----END CERTIFICATE-----
+
+Security Communication Root CA
+==============================
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
+U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
+HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
+U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw
+8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM
+DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX
+5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd
+DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2
+JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw
+DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g
+0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a
+mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ
+s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ
+6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi
+FL39vmwLAw==
+-----END CERTIFICATE-----
+
+Sonera Class 2 Root CA
+======================
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG
+U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw
+NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh
+IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3
+/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT
+dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG
+f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P
+tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH
+nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT
+XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt
+0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI
+cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph
+Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx
+EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH
+llpwrN9M
+-----END CERTIFICATE-----
+
+Staat der Nederlanden Root CA
+=============================
+-----BEGIN CERTIFICATE-----
+MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwGA1UE
+ChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJsYW5kZW4g
+Um9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4w
+HAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxh
+bmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFt
+vsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02P
+jLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGca
+C1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7lr7HcsBth
+vJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn6
+22r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRV
+HSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9v
+dC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DAN
+BgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtR
+EytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbw
+MVcoEoJz6TMvplW0C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y
+nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR
+iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw==
+-----END CERTIFICATE-----
+
+TDC Internet Root CA
+====================
+-----BEGIN CERTIFICATE-----
+MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJESzEVMBMGA1UE
+ChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTAeFw0wMTA0MDUx
+NjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJu
+ZXQxHTAbBgNVBAsTFFREQyBJbnRlcm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAxLhAvJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20j
+xsNuZp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a0vnRrEvL
+znWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc14izbSysseLlJ28TQx5yc
+5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGNeGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6
+otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcDR0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZI
+AYb4QgEBBAQDAgAHMGUGA1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMM
+VERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxMEQ1JM
+MTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3WjALBgNVHQ8EBAMC
+AQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAwHQYDVR0OBBYEFGxkAcf9hW2syNqe
+UAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJKoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0G
+CSqGSIb3DQEBBQUAA4IBAQBOQ8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540m
+gwV5dOy0uaOXwTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+
+2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm899qNLPg7kbWzb
+O0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrU
+Cbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l
+-----END CERTIFICATE-----
+
+TDC OCES Root CA
+================
+-----BEGIN CERTIFICATE-----
+MIIFGTCCBAGgAwIBAgIEPki9xDANBgkqhkiG9w0BAQUFADAxMQswCQYDVQQGEwJESzEMMAoGA1UE
+ChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTAeFw0wMzAyMTEwODM5MzBaFw0zNzAyMTEwOTA5
+MzBaMDExCzAJBgNVBAYTAkRLMQwwCgYDVQQKEwNUREMxFDASBgNVBAMTC1REQyBPQ0VTIENBMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArGL2YSCyz8DGhdfjeebM7fI5kqSXLmSjhFuH
+nEz9pPPEXyG9VhDr2y5h7JNp46PMvZnDBfwGuMo2HP6QjklMxFaaL1a8z3sM8W9Hpg1DTeLpHTk0
+zY0s2RKY+ePhwUp8hjjEqcRhiNJerxomTdXkoCJHhNlktxmW/OwZ5LKXJk5KTMuPJItUGBxIYXvV
+iGjaXbXqzRowwYCDdlCqT9HU3Tjw7xb04QxQBr/q+3pJoSgrHPb8FTKjdGqPqcNiKXEx5TukYBde
+dObaE+3pHx8b0bJoc8YQNHVGEBDjkAB2QMuLt0MJIf+rTpPGWOmlgtt3xDqZsXKVSQTwtyv6e1mO
+3QIDAQABo4ICNzCCAjMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwgewGA1UdIASB
+5DCB4TCB3gYIKoFQgSkBAQEwgdEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2VydGlmaWthdC5k
+ay9yZXBvc2l0b3J5MIGdBggrBgEFBQcCAjCBkDAKFgNUREMwAwIBARqBgUNlcnRpZmlrYXRlciBm
+cmEgZGVubmUgQ0EgdWRzdGVkZXMgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEuMS4xLiBDZXJ0aWZp
+Y2F0ZXMgZnJvbSB0aGlzIENBIGFyZSBpc3N1ZWQgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEuMS4x
+LjARBglghkgBhvhCAQEEBAMCAAcwgYEGA1UdHwR6MHgwSKBGoESkQjBAMQswCQYDVQQGEwJESzEM
+MAoGA1UEChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTENMAsGA1UEAxMEQ1JMMTAsoCqgKIYm
+aHR0cDovL2NybC5vY2VzLmNlcnRpZmlrYXQuZGsvb2Nlcy5jcmwwKwYDVR0QBCQwIoAPMjAwMzAy
+MTEwODM5MzBagQ8yMDM3MDIxMTA5MDkzMFowHwYDVR0jBBgwFoAUYLWF7FZkfhIZJ2cdUBVLc647
++RIwHQYDVR0OBBYEFGC1hexWZH4SGSdnHVAVS3OuO/kSMB0GCSqGSIb2fQdBAAQQMA4bCFY2LjA6
+NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEACromJkbTc6gJ82sLMJn9iuFXehHTuJTXCRBuo7E4
+A9G28kNBKWKnctj7fAXmMXAnVBhOinxO5dHKjHiIzxvTkIvmI/gLDjNDfZziChmPyQE+dF10yYsc
+A+UYyAFMP8uXBV2YcaaYb7Z8vTd/vuGTJW1v8AqtFxjhA7wHKcitJuj4YfD9IQl+mo6paH1IYnK9
+AOoBmbgGglGBTvH1tJFUuSN6AJqfXY3gPGS5GhKSKseCRHI53OI8xthV9RVOyAUO28bQYqbsFbS1
+AoLbrIyigfCbmTH1ICCoiGEKB5+U/NDXG8wuF/MEJ3Zn61SD/aSQfgY9BKNDLdr8C2LqL19iUw==
+-----END CERTIFICATE-----
+
+UTN DATACorp SGC Root CA
+========================
+-----BEGIN CERTIFICATE-----
+MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UE
+BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
+IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZ
+BgNVBAMTElVUTiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBa
+MIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4w
+HAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRy
+dXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ys
+raP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlo
+wHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA
+9P4yPykqlXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv
+33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4GrMIGoMAsGA1Ud
+DwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRTMtGzz3/64PGgXYVOktKeRR20TzA9
+BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dD
+LmNybDAqBgNVHSUEIzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3
+DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft
+Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0
+I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXx
+EZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwP
+DPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI
+-----END CERTIFICATE-----
+
+UTN USERFirst Hardware Root CA
+==============================
+-----BEGIN CERTIFICATE-----
+MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE
+BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
+IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd
+BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx
+OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0
+eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz
+ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI
+wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd
+tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8
+i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf
+Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw
+gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF
+lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF
+UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF
+BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM
+//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW
+XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2
+lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn
+iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67
+nfhmqA==
+-----END CERTIFICATE-----
+
+Camerfirma Chambers of Commerce Root
+====================================
+-----BEGIN CERTIFICATE-----
+MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe
+QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i
+ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx
+NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp
+cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn
+MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC
+AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU
+xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH
+NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW
+DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV
+d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud
+EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v
+cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P
+AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh
+bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD
+VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz
+aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi
+fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD
+L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN
+UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n
+ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1
+erfutGWaIZDgqtCYvDi1czyL+Nw=
+-----END CERTIFICATE-----
+
+Camerfirma Global Chambersign Root
+==================================
+-----BEGIN CERTIFICATE-----
+MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe
+QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i
+ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx
+NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt
+YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg
+MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw
+ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J
+1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O
+by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl
+6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c
+8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/
+BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j
+aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B
+Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj
+aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y
+ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh
+bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA
+PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y
+gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ
+PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4
+IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes
+t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
+-----END CERTIFICATE-----
+
+NetLock Notary (Class A) Root
+=============================
+-----BEGIN CERTIFICATE-----
+MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQI
+EwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6
+dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9j
+ayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oX
+DTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQH
+EwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYD
+VQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFz
+cyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSM
+D7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZ
+z+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC
+/tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7
+tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR6
+4sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIG
+A1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaC
+Ak1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pv
+bGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu
+IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2Vn
+LWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0
+ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFz
+IGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBh
+IGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVu
+b3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBh
+bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sg
+Q1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFp
+bCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5
+ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjP
+ytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJB
+CWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdr
+KuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM
+8CgHrTwXZoi1/baI
+-----END CERTIFICATE-----
+
+NetLock Business (Class B) Root
+===============================
+-----BEGIN CERTIFICATE-----
+MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUxETAPBgNVBAcT
+CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV
+BAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQDEylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikg
+VGFudXNpdHZhbnlraWFkbzAeFw05OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYD
+VQQGEwJIVTERMA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRv
+bnNhZ2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5ldExvY2sg
+VXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
+iQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2S
+o/1bXHQawEfKOml2mrriRBf8TKPV/riXiK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr
+1nGTLbO/CVRY7QbrqHvcQ7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV
+HQ8BAf8EBAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZ
+RUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRh
+dGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0
+ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRv
+c2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUg
+YXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh
+c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBz
+Oi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6ZXNA
+bmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhl
+IHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2
+YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBj
+cHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06sPgzTEdM
+43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXan3BukxowOR0w2y7jfLKR
+stE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKSNitjrFgBazMpUIaD8QFI
+-----END CERTIFICATE-----
+
+NetLock Express (Class C) Root
+==============================
+-----BEGIN CERTIFICATE-----
+MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUxETAPBgNVBAcT
+CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV
+BAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQDEytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBD
+KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJ
+BgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6
+dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMrTmV0TG9j
+ayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNAOoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3Z
+W3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63
+euyucYT2BDMIJTLrdKwWRMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQw
+DgYDVR0PAQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEWggJN
+RklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0YWxhbm9zIFN6b2xn
+YWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBB
+IGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBOZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1i
+aXp0b3NpdGFzYSB2ZWRpLiBBIGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0
+ZWxlIGF6IGVsb2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs
+ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25sYXBqYW4gYSBo
+dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kga2VyaGV0byBheiBlbGxlbm9y
+emVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4gSU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5k
+IHRoZSB1c2Ugb2YgdGhpcyBjZXJ0aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQ
+UyBhdmFpbGFibGUgYXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwg
+YXQgY3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmYta3UzbM2
+xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2gpO0u9f38vf5NNwgMvOOW
+gyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4Fp1hBWeAyNDYpQcCNJgEjTME1A==
+-----END CERTIFICATE-----
+
+XRamp Global CA Root
+====================
+-----BEGIN CERTIFICATE-----
+MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE
+BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj
+dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx
+HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg
+U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu
+IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx
+foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE
+zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs
+AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry
+xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
+EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap
+oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC
+AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc
+/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt
+qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n
+nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz
+8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw=
+-----END CERTIFICATE-----
+
+Go Daddy Class 2 CA
+===================
+-----BEGIN CERTIFICATE-----
+MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY
+VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG
+A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
+RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD
+ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv
+2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32
+qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j
+YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY
+vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O
+BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o
+atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu
+MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG
+A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim
+PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt
+I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
+HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI
+Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b
+vZ8=
+-----END CERTIFICATE-----
+
+Starfield Class 2 CA
+====================
+-----BEGIN CERTIFICATE-----
+MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc
+U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg
+Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo
+MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG
+A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG
+SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY
+bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ
+JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm
+epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN
+F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF
+MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f
+hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo
+bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs
+afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM
+PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD
+KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3
+QBFGmh95DmK/D5fs4C8fF5Q=
+-----END CERTIFICATE-----
+
+StartCom Certification Authority
+================================
+-----BEGIN CERTIFICATE-----
+MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
+U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu
+ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0
+NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk
+LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg
+U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y
+o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/
+Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d
+eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt
+2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z
+6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ
+osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/
+untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc
+UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT
+37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
+FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0
+Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj
+YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH
+AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw
+Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg
+U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5
+LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh
+cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT
+dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC
+AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh
+3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm
+vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk
+fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3
+fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ
+EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
+yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl
+1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/
+lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro
+g14=
+-----END CERTIFICATE-----
+
+Taiwan GRCA
+===========
+-----BEGIN CERTIFICATE-----
+MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG
+EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X
+DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv
+dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN
+w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5
+BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O
+1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO
+htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov
+J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7
+Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t
+B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB
+O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8
+lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV
+HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2
+09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ
+TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj
+Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2
+Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU
+D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz
+DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk
+Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk
+7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ
+CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy
++fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS
+-----END CERTIFICATE-----
+
+Firmaprofesional Root CA
+========================
+-----BEGIN CERTIFICATE-----
+MIIEVzCCAz+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCRVMxIjAgBgNVBAcT
+GUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1dG9yaWRhZCBkZSBDZXJ0aWZp
+Y2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FA
+ZmlybWFwcm9mZXNpb25hbC5jb20wHhcNMDExMDI0MjIwMDAwWhcNMTMxMDI0MjIwMDAwWjCBnTEL
+MAkGA1UEBhMCRVMxIjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMT
+OUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2
+ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20wggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQDnIwNvbyOlXnjOlSztlB5uCp4Bx+ow0Syd3Tfom5h5VtP8c9/Qit5V
+j1H5WuretXDE7aTt/6MNbg9kUDGvASdYrv5sp0ovFy3Tc9UTHI9ZpTQsHVQERc1ouKDAA6XPhUJH
+lShbz++AbOCQl4oBPB3zhxAwJkh91/zpnZFx/0GaqUC1N5wpIE8fUuOgfRNtVLcK3ulqTgesrBlf
+3H5idPayBQC6haD9HThuy1q7hryUZzM1gywfI834yJFxzJeL764P3CkDG8A563DtwW4O2GcLiam8
+NeTvtjS0pbbELaW+0MOUJEjb35bTALVmGotmBQ/dPz/LP6pemkr4tErvlTcbAgMBAAGjgZ8wgZww
+KgYDVR0RBCMwIYYfaHR0cDovL3d3dy5maXJtYXByb2Zlc2lvbmFsLmNvbTASBgNVHRMBAf8ECDAG
+AQH/AgEBMCsGA1UdEAQkMCKADzIwMDExMDI0MjIwMDAwWoEPMjAxMzEwMjQyMjAwMDBaMA4GA1Ud
+DwEB/wQEAwIBBjAdBgNVHQ4EFgQUMwugZtHq2s7eYpMEKFK1FH84aLcwDQYJKoZIhvcNAQEFBQAD
+ggEBAEdz/o0nVPD11HecJ3lXV7cVVuzH2Fi3AQL0M+2TUIiefEaxvT8Ub/GzR0iLjJcG1+p+o1wq
+u00vR+L4OQbJnC4xGgN49Lw4xiKLMzHwFgQEffl25EvXwOaD7FnMP97/T2u3Z36mhoEyIwOdyPdf
+wUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzflZKG+TQyTmAyX9odtsz/ny4Cm
+7YjHX1BiAuiZdBbQ5rQ58SfLyEDW44YQqSMSkuBpQWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YG
+VM+h4k0460tQtcsm9MracEpqoeJ5quGnM/b9Sh/22WA=
+-----END CERTIFICATE-----
+
+Wells Fargo Root CA
+===================
+-----BEGIN CERTIFICATE-----
+MIID5TCCAs2gAwIBAgIEOeSXnjANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UEBhMCVVMxFDASBgNV
+BAoTC1dlbGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eTEvMC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN
+MDAxMDExMTY0MTI4WhcNMjEwMTE0MTY0MTI4WjCBgjELMAkGA1UEBhMCVVMxFDASBgNVBAoTC1dl
+bGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEv
+MC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVqDM7Jvk0/82bfuUER84A4n135zHCLielTWi5MbqNQ1mX
+x3Oqfz1cQJ4F5aHiidlMuD+b+Qy0yGIZLEWukR5zcUHESxP9cMIlrCL1dQu3U+SlK93OvRw6esP3
+E48mVJwWa2uv+9iWsWCaSOAlIiR5NM4OJgALTqv9i86C1y8IcGjBqAr5dE8Hq6T54oN+J3N0Prj5
+OEL8pahbSCOz6+MlsoCultQKnMJ4msZoGK43YjdeUXWoWGPAUe5AeH6orxqg4bB4nVCMe+ez/I4j
+sNtlAHCEAQgAFG5Uhpq6zPk3EPbg3oQtnaSFN9OH4xXQwReQfhkhahKpdv0SAulPIV4XAgMBAAGj
+YTBfMA8GA1UdEwEB/wQFMAMBAf8wTAYDVR0gBEUwQzBBBgtghkgBhvt7hwcBCzAyMDAGCCsGAQUF
+BwIBFiRodHRwOi8vd3d3LndlbGxzZmFyZ28uY29tL2NlcnRwb2xpY3kwDQYJKoZIhvcNAQEFBQAD
+ggEBANIn3ZwKdyu7IvICtUpKkfnRLb7kuxpo7w6kAOnu5+/u9vnldKTC2FJYxHT7zmu1Oyl5GFrv
+m+0fazbuSCUlFLZWohDo7qd/0D+j0MNdJu4HzMPBJCGHHt8qElNvQRbn7a6U+oxy+hNH8Dx+rn0R
+OhPs7fpvcmR7nX1/Jv16+yWt6j4pf0zjAFcysLPp7VMX2YuyFA4w6OXVE8Zkr8QA1dhYJPz1j+zx
+x32l2w8n0cbyQIjmH/ZhqPRCyLk306m+LFZ4wnKbWV01QIroTmMatukgalHizqSQ33ZwmVxwQ023
+tqcZZE6St8WRPH9IFmV7Fv3L/PvZ1dZPIWU7Sn9Ho/s=
+-----END CERTIFICATE-----
+
+Swisscom Root CA 1
+==================
+-----BEGIN CERTIFICATE-----
+MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQG
+EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy
+dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4
+MTgyMjA2MjBaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln
+aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIIC
+IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9m2BtRsiM
+MW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdihFvkcxC7mlSpnzNApbjyF
+NDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/TilftKaNXXsLmREDA/7n29uj/x2lzZAe
+AR81sH8A25Bvxn570e56eqeqDFdvpG3FEzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkC
+b6dJtDZd0KTeByy2dbcokdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn
+7uHbHaBuHYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNFvJbN
+cA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo19AOeCMgkckkKmUp
+WyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjCL3UcPX7ape8eYIVpQtPM+GP+HkM5
+haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJWbjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNY
+MUJDLXT5xp6mig/p/r+D5kNXJLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw
+HQYDVR0hBBYwFDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j
+BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzcK6FptWfUjNP9
+MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzfky9NfEBWMXrrpA9gzXrzvsMn
+jgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7IkVh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQ
+MbFamIp1TpBcahQq4FJHgmDmHtqBsfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4H
+VtA4oJVwIHaM190e3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtl
+vrsRls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ipmXeascCl
+OS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HHb6D0jqTsNFFbjCYDcKF3
+1QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksfrK/7DZBaZmBwXarNeNQk7shBoJMBkpxq
+nvy5JMWzFYJ+vq6VK+uxwNrjAWALXmmshFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCy
+x/yP2FS1k2Kdzs9Z+z0YzirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMW
+NY6E0F/6MBr1mmz0DlP5OlvRHA==
+-----END CERTIFICATE-----
+
+DigiCert Assured ID Root CA
+===========================
+-----BEGIN CERTIFICATE-----
+MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw
+IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx
+MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL
+ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO
+9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy
+UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW
+/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy
+oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf
+GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF
+66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq
+hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc
+EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn
+SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i
+8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
+-----END CERTIFICATE-----
+
+DigiCert Global Root CA
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw
+HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw
+MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
+dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn
+TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5
+BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H
+4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y
+7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB
+o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm
+8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF
+BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr
+EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt
+tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886
+UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
+CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
+-----END CERTIFICATE-----
+
+DigiCert High Assurance EV Root CA
+==================================
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw
+KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw
+MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
+MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu
+Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t
+Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS
+OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3
+MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ
+NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe
+h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB
+Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY
+JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ
+V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp
+myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK
+mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
+vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K
+-----END CERTIFICATE-----
+
+Certplus Class 2 Primary CA
+===========================
+-----BEGIN CERTIFICATE-----
+MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE
+BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN
+OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy
+dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR
+5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ
+Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO
+YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e
+e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME
+CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ
+YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t
+L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD
+P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R
+TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+
+7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW
+//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7
+l7+ijrRU
+-----END CERTIFICATE-----
+
+DST Root CA X3
+==============
+-----BEGIN CERTIFICATE-----
+MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK
+ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X
+DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1
+cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT
+rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9
+UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy
+xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d
+utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T
+AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ
+MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug
+dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE
+GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw
+RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS
+fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
+-----END CERTIFICATE-----
+
+DST ACES CA X6
+==============
+-----BEGIN CERTIFICATE-----
+MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG
+EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT
+MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha
+MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE
+CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI
+DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa
+pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow
+GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy
+MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud
+EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu
+Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy
+dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU
+CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2
+5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t
+Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq
+nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs
+vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3
+oKfN5XozNmr6mis=
+-----END CERTIFICATE-----
+
+TURKTRUST Certificate Services Provider Root 1
+==============================================
+-----BEGIN CERTIFICATE-----
+MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOcUktUUlVTVCBF
+bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGDAJUUjEP
+MA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykgMjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0
+acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMx
+MDI3MTdaFw0xNTAzMjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsg
+U2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYDVQQHDAZB
+TktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBC
+aWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GX
+yGl8hMW0kWxsE2qkVa2kheiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8i
+Si9BB35JYbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5CurKZ
+8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1JuTm5Rh8i27fbMx4
+W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51b0dewQIDAQABoxAwDjAMBgNVHRME
+BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46
+sWrv7/hg0Uw2ZkUd82YCdAR7kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxE
+q8Sn5RTOPEFhfEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy
+B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdAaLX/7KfS0zgY
+nNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKSRGQDJereW26fyfJOrN3H
+-----END CERTIFICATE-----
+
+TURKTRUST Certificate Services Provider Root 2
+==============================================
+-----BEGIN CERTIFICATE-----
+MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBF
+bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP
+MA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg
+QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcN
+MDUxMTA3MTAwNzU3WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVr
+dHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEPMA0G
+A1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls
+acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqe
+LCDe2JAOCtFp0if7qnefJ1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKI
+x+XlZEdhR3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJQv2g
+QrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGXJHpsmxcPbe9TmJEr
+5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1pzpwACPI2/z7woQ8arBT9pmAPAgMB
+AAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58SFq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8G
+A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/ntt
+Rbj2hWyfIvwqECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4
+Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFzgw2lGh1uEpJ+
+hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotHuFEJjOp9zYhys2AzsfAKRO8P
+9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LSy3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5
+UrbnBEI=
+-----END CERTIFICATE-----
+
+SwissSign Gold CA - G2
+======================
+-----BEGIN CERTIFICATE-----
+MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw
+EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN
+MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp
+c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq
+t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C
+jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg
+vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF
+ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR
+AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend
+jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO
+peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR
+7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi
+GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64
+OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
+L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm
+5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr
+44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf
+Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m
+Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp
+mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk
+vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf
+KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br
+NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj
+viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
+-----END CERTIFICATE-----
+
+SwissSign Silver CA - G2
+========================
+-----BEGIN CERTIFICATE-----
+MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT
+BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X
+DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3
+aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG
+9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644
+N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm
++/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH
+6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu
+MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h
+qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5
+FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs
+ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc
+celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X
+CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
+BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB
+tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
+cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P
+4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F
+kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L
+3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx
+/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa
+DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP
+e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu
+WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ
+DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub
+DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
+-----END CERTIFICATE-----
+
+GeoTrust Primary Certification Authority
+========================================
+-----BEGIN CERTIFICATE-----
+MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx
+CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ
+cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN
+b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9
+nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge
+RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt
+tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI
+hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K
+Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN
+NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa
+Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG
+1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
+-----END CERTIFICATE-----
+
+thawte Primary Root CA
+======================
+-----BEGIN CERTIFICATE-----
+MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE
+BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2
+aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv
+cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3
+MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg
+SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv
+KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT
+FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs
+oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ
+1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc
+q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K
+aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p
+afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
+VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF
+AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE
+uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
+xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89
+jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH
+z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA==
+-----END CERTIFICATE-----
+
+VeriSign Class 3 Public Primary Certification Authority - G5
+============================================================
+-----BEGIN CERTIFICATE-----
+MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE
+BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
+ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk
+IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln
+biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh
+dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt
+YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz
+j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD
+Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
+Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r
+fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/
+BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv
+Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
+aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG
+SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+
+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE
+KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC
+Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE
+ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
+-----END CERTIFICATE-----
+
+SecureTrust CA
+==============
+-----BEGIN CERTIFICATE-----
+MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG
+EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy
+dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe
+BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX
+OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t
+DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH
+GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b
+01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH
+ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/
+BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj
+aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ
+KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu
+SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf
+mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ
+nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
+3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
+-----END CERTIFICATE-----
+
+Secure Global CA
+================
+-----BEGIN CERTIFICATE-----
+MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG
+EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH
+bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg
+MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg
+Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx
+YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ
+bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g
+8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV
+HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi
+0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
+EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn
+oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA
+MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+
+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn
+CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5
+3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
+f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
+-----END CERTIFICATE-----
+
+COMODO Certification Authority
+==============================
+-----BEGIN CERTIFICATE-----
+MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE
+BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG
+A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb
+MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD
+T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH
++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww
+xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV
+4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA
+1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI
+rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k
+b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC
+AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP
+OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
+RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc
+IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN
++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ==
+-----END CERTIFICATE-----
+
+Network Solutions Certificate Authority
+=======================================
+-----BEGIN CERTIFICATE-----
+MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG
+EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr
+IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx
+MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
+MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx
+jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT
+aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT
+crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc
+/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB
+AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv
+bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA
+A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q
+4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/
+GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
+wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD
+ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
+-----END CERTIFICATE-----
+
+WellsSecure Public Root Certificate Authority
+=============================================
+-----BEGIN CERTIFICATE-----
+MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM
+F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw
+NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN
+MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl
+bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD
+VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1
+iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13
+i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8
+bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB
+K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB
+AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu
+cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm
+lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB
+i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww
+GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg
+Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI
+K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0
+bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj
+qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es
+E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ
+tylv2G0xffX8oRAHh84vWdw+WNs=
+-----END CERTIFICATE-----
+
+COMODO ECC Certification Authority
+==================================
+-----BEGIN CERTIFICATE-----
+MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC
+R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE
+ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix
+GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
+Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo
+b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X
+4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni
+wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG
+FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA
+U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
+-----END CERTIFICATE-----
+
+IGC/A
+=====
+-----BEGIN CERTIFICATE-----
+MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD
+VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE
+Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy
+MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI
+EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT
+STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2
+TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW
+So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy
+HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd
+frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ
+tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB
+egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC
+iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK
+q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q
+MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg
+Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI
+lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF
+0mBWWg==
+-----END CERTIFICATE-----
+
+Security Communication EV RootCA1
+=================================
+-----BEGIN CERTIFICATE-----
+MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
+U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh
+dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE
+BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl
+Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO
+/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX
+WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z
+ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4
+bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK
+9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
+SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm
+iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG
+Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW
+mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW
+T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490
+-----END CERTIFICATE-----
+
+OISTE WISeKey Global Root GA CA
+===============================
+-----BEGIN CERTIFICATE-----
+MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE
+BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG
+A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH
+bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD
+VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw
+IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5
+IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9
+Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg
+Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD
+d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ
+/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R
+LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ
+KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm
+MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4
++vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa
+hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY
+okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0=
+-----END CERTIFICATE-----
+
+Microsec e-Szigno Root CA
+=========================
+-----BEGIN CERTIFICATE-----
+MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE
+BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL
+EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0
+MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz
+dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT
+GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG
+d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N
+oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc
+QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ
+PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb
+MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG
+IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD
+VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3
+LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A
+dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn
+AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA
+4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg
+AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA
+egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6
+Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO
+PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv
+c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h
+cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw
+IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT
+WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV
+MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER
+MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp
+Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal
+HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT
+nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE
+aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a
+86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK
+yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB
+S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU=
+-----END CERTIFICATE-----
+
+Certigna
+========
+-----BEGIN CERTIFICATE-----
+MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw
+EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3
+MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI
+Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q
+XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH
+GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p
+ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg
+DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf
+Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ
+tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ
+BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J
+SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA
+hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+
+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu
+PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY
+1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
+WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
+-----END CERTIFICATE-----
+
+AC Ra\xC3\xADz Certic\xC3\xA1mara S.A.
+======================================
+-----BEGIN CERTIFICATE-----
+MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNVBAYT
+AkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRpZmljYWNpw7NuIERpZ2l0YWwg
+LSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwaQUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4w
+HhcNMDYxMTI3MjA0NjI5WhcNMzAwNDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+
+U29jaWVkYWQgQ2FtZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJh
+IFMuQS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeGqentLhM0R7LQcNzJPNCN
+yu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzLfDe3fezTf3MZsGqy2IiKLUV0qPezuMDU
+2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQY5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU3
+4ojC2I+GdV75LaeHM/J4Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP
+2yYe68yQ54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+bMMCm
+8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48jilSH5L887uvDdUhf
+HjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++EjYfDIJss2yKHzMI+ko6Kh3VOz3vCa
+Mh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/ztA/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK
+5lw1omdMEWux+IBkAC1vImHFrEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1b
+czwmPS9KvqfJpxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
+AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCBlTCBkgYEVR0g
+ADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFyYS5jb20vZHBjLzBaBggrBgEF
+BQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW507WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2Ug
+cHVlZGVuIGVuY29udHJhciBlbiBsYSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEf
+AygPU3zmpFmps4p6xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuX
+EpBcunvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/Jre7Ir5v
+/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dpezy4ydV/NgIlqmjCMRW3
+MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42gzmRkBDI8ck1fj+404HGIGQatlDCIaR4
+3NAvO2STdPCWkPHv+wlaNECW8DYSwaN0jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wk
+eZBWN7PGKX6jD/EpOe9+XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f
+/RWmnkJDW2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/RL5h
+RqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35rMDOhYil/SrnhLecU
+Iw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxkBYn8eNZcLCZDqQ==
+-----END CERTIFICATE-----
+
+TC TrustCenter Class 2 CA II
+============================
+-----BEGIN CERTIFICATE-----
+MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC
+REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy
+IENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYw
+MTEyMTQzODQzWhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1
+c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UE
+AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jftMjWQ+nEdVl//OEd+DFw
+IxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKguNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2
+xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2JXjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQ
+Xa7pIXSSTYtZgo+U4+lK8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7u
+SNQZu+995OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1UdEwEB
+/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3kUrL84J6E1wIqzCB
+7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90
+Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU
+cnVzdENlbnRlciUyMENsYXNzJTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i
+SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
+TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iSGNn3Bzn1LL4G
+dXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprtZjluS5TmVfwLG4t3wVMTZonZ
+KNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8au0WOB9/WIFaGusyiC2y8zl3gK9etmF1Kdsj
+TYjKUCjLhdLTEKJZbtOTVAB6okaVhgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kP
+JOzHdiEoZa5X6AeIdUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfk
+vQ==
+-----END CERTIFICATE-----
+
+TC TrustCenter Class 3 CA II
+============================
+-----BEGIN CERTIFICATE-----
+MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC
+REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy
+IENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYw
+MTEyMTQ0MTU3WhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1
+c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UE
+AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJWHt4bNwcwIi9v8Qbxq63W
+yKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+QVl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo
+6SI7dYnWRBpl8huXJh0obazovVkdKyT21oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZ
+uV3bOx4a+9P/FRQI2AlqukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk
+2ZyqBwi1Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1UdEwEB
+/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NXXAek0CSnwPIA1DCB
+7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90
+Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU
+cnVzdENlbnRlciUyMENsYXNzJTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i
+SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
+TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlNirTzwppVMXzE
+O2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8TtXqluJucsG7Kv5sbviRmEb8
+yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9
+IJqDnxrcOfHFcqMRA/07QlIp2+gB95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal
+092Y+tTmBvTwtiBjS+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc
+5A==
+-----END CERTIFICATE-----
+
+TC TrustCenter Universal CA I
+=============================
+-----BEGIN CERTIFICATE-----
+MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMC
+REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy
+IFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcN
+MDYwMzIyMTU1NDI4WhcNMjUxMjMxMjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMg
+VHJ1c3RDZW50ZXIgR21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYw
+JAYDVQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSRJJZ4Hgmgm5qVSkr1YnwC
+qMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3TfCZdzHd55yx4Oagmcw6iXSVphU9VDprv
+xrlE4Vc93x9UIuVvZaozhDrzznq+VZeujRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtw
+ag+1m7Z3W0hZneTvWq3zwZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9O
+gdwZu5GQfezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYDVR0j
+BBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0GCSqGSIb3DQEBBQUAA4IBAQAo0uCG
+1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X17caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/Cy
+vwbZ71q+s2IhtNerNXxTPqYn8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3
+ghUJGooWMNjsydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT
+ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/2TYcuiUaUj0a
+7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY
+-----END CERTIFICATE-----
+
+Deutsche Telekom Root CA 2
+==========================
+-----BEGIN CERTIFICATE-----
+MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT
+RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG
+A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5
+MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G
+A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS
+b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5
+bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI
+KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY
+AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK
+Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV
+jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV
+HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr
+E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy
+zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8
+rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G
+dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU
+Cm26OWMohpLzGITY+9HPBVZkVw==
+-----END CERTIFICATE-----
+
+ComSign Secured CA
+==================
+-----BEGIN CERTIFICATE-----
+MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAwPDEbMBkGA1UE
+AxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0w
+NDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBD
+QTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDGtWhfHZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs
+49ohgHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sWv+bznkqH
+7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ueMv5WJDmyVIRD9YTC2LxB
+kMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d1
+9guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUw
+AwEB/zBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29t
+U2lnblNlY3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58ADsA
+j8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkqhkiG9w0BAQUFAAOC
+AQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7piL1DRYHjZiM/EoZNGeQFsOY3wo3a
+BijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtCdsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtp
+FhpFfTMDZflScZAmlaxMDPWLkz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP
+51qJThRv4zdLhfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz
+OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw==
+-----END CERTIFICATE-----
+
+Cybertrust Global Root
+======================
+-----BEGIN CERTIFICATE-----
+MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li
+ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4
+MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD
+ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW
+0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL
+AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin
+89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT
+8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2
+MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G
+A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO
+lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi
+5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2
+hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T
+X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW
+WL1WMRJOEcgh4LMRkWXbtKaIOM5V
+-----END CERTIFICATE-----
+
+ePKI Root Certification Authority
+=================================
+-----BEGIN CERTIFICATE-----
+MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG
+EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg
+Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx
+MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq
+MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs
+IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi
+lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv
+qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX
+12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O
+WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+
+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao
+lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/
+vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi
+Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi
+MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH
+ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0
+1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq
+KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV
+xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP
+NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r
+GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE
+xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx
+gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy
+sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD
+BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw=
+-----END CERTIFICATE-----
+
+T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3
+=============================================================================================================================
+-----BEGIN CERTIFICATE-----
+MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH
+DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q
+aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry
+b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV
+BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg
+S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4
+MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl
+IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF
+n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl
+IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft
+dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl
+cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO
+Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1
+xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR
+6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL
+hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd
+BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
+MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4
+N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT
+y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh
+LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M
+dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI=
+-----END CERTIFICATE-----
+
+Buypass Class 2 CA 1
+====================
+-----BEGIN CERTIFICATE-----
+MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
+QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2
+MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh
+c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M
+cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83
+0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4
+0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R
+uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P
+AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV
+1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt
+7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2
+fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w
+wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho
+-----END CERTIFICATE-----
+
+Buypass Class 3 CA 1
+====================
+-----BEGIN CERTIFICATE-----
+MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
+QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMyBDQSAxMB4XDTA1
+MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh
+c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKx
+ifZgisRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//zNIqeKNc0
+n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI+MkcVyzwPX6UvCWThOia
+AJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2RhzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c
+1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0P
+AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFPBdy7
+pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27sEzNxZy5p+qksP2bA
+EllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2mSlf56oBzKwzqBwKu5HEA6BvtjT5
+htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yCe/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQj
+el/wroQk5PMr+4okoyeYZdowdXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915
+-----END CERTIFICATE-----
+
+EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1
+==========================================================================
+-----BEGIN CERTIFICATE-----
+MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNVBAMML0VCRyBF
+bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMTcwNQYDVQQKDC5FQkcg
+QmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAe
+Fw0wNjA4MTcwMDIxMDlaFw0xNjA4MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25p
+ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2lt
+IFRla25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIiMA0GCSqG
+SIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h4fuXd7hxlugTlkaDT7by
+X3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAktiHq6yOU/im/+4mRDGSaBUorzAzu8T2b
+gmmkTPiab+ci2hC6X5L8GCcKqKpE+i4stPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfr
+eYteIAbTdgtsApWjluTLdlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZ
+TqNGFav4c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8UmTDGy
+Y5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z+kI2sSXFCjEmN1Zn
+uqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0OLna9XvNRiYuoP1Vzv9s6xiQFlpJI
+qkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMWOeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vm
+ExH8nYQKE3vwO9D8owrXieqWfo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0
+Nokb+Clsi7n2l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
+/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgwFoAU587GT/wW
+Z5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+8ygjdsZs93/mQJ7ANtyVDR2t
+FcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgm
+zJNSroIBk5DKd8pNSe/iWtkqvTDOTLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64k
+XPBfrAowzIpAoHMEwfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqT
+bCmYIai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJnxk1Gj7sU
+RT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4QDgZxGhBM/nV+/x5XOULK
+1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9qKd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt
+2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11thie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQ
+Y9iJSrSq3RZj9W6+YKH47ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9
+AahH3eU7QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT
+-----END CERTIFICATE-----
+
+certSIGN ROOT CA
+================
+-----BEGIN CERTIFICATE-----
+MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD
+VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa
+Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE
+CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I
+JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH
+rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2
+ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD
+0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943
+AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
+Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB
+AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8
+SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0
+x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt
+vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz
+TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD
+-----END CERTIFICATE-----
+
+CNNIC ROOT
+==========
+-----BEGIN CERTIFICATE-----
+MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE
+ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw
+OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD
+o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz
+VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT
+VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or
+czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK
+y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC
+wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S
+lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5
+Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM
+O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8
+BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2
+G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m
+mxE=
+-----END CERTIFICATE-----
+
+ApplicationCA - Japanese Government
+===================================
+-----BEGIN CERTIFICATE-----
+MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT
+SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw
+MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl
+cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4
+fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN
+wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE
+jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu
+nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU
+WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV
+BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD
+vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs
+o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g
+/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD
+io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW
+dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL
+rosot4LKGAfmt1t06SAZf7IbiVQ=
+-----END CERTIFICATE-----
+
+GeoTrust Primary Certification Authority - G3
+=============================================
+-----BEGIN CERTIFICATE-----
+MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE
+BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0
+IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy
+eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz
+NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo
+YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT
+LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j
+K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE
+c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C
+IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu
+dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr
+2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9
+cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE
+Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD
+AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s
+t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt
+-----END CERTIFICATE-----
+
+thawte Primary Root CA - G2
+===========================
+-----BEGIN CERTIFICATE-----
+MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC
+VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu
+IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg
+Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV
+MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG
+b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt
+IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS
+LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5
+8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU
+mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN
+G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K
+rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==
+-----END CERTIFICATE-----
+
+thawte Primary Root CA - G3
+===========================
+-----BEGIN CERTIFICATE-----
+MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE
+BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2
+aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv
+cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w
+ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh
+d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD
+VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG
+A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At
+P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC
++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY
+7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW
+vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ
+KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK
+A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu
+t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC
+8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm
+er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A=
+-----END CERTIFICATE-----
+
+GeoTrust Primary Certification Authority - G2
+=============================================
+-----BEGIN CERTIFICATE-----
+MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC
+VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu
+Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1
+OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg
+MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl
+b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG
+BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc
+KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD
+VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+
+EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m
+ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2
+npaqBA+K
+-----END CERTIFICATE-----
+
+VeriSign Universal Root Certification Authority
+===============================================
+-----BEGIN CERTIFICATE-----
+MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE
+BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
+ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk
+IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u
+IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV
+UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
+cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
+IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj
+1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP
+MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72
+9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I
+AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR
+tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G
+CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O
+a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud
+DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3
+Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx
+Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx
+P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P
+wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4
+mJO37M2CYfE45k+XmCpajQ==
+-----END CERTIFICATE-----
+
+VeriSign Class 3 Public Primary Certification Authority - G4
+============================================================
+-----BEGIN CERTIFICATE-----
+MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC
+VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3
+b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz
+ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo
+b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8
+Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz
+rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB
+/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw
+HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u
+Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD
+A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx
+AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
+-----END CERTIFICATE-----
+
+NetLock Arany (Class Gold) Főtanúsítvány
+============================================
+-----BEGIN CERTIFICATE-----
+MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G
+A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610
+dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB
+cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx
+MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO
+ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv
+biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6
+c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu
+0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw
+/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk
+H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw
+fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1
+neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB
+BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW
+qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta
+YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC
+bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna
+NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu
+dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
+-----END CERTIFICATE-----
+
+Staat der Nederlanden Root CA - G2
+==================================
+-----BEGIN CERTIFICATE-----
+MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE
+CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g
+Um9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oXDTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMC
+TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l
+ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ
+5291qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8SpuOUfiUtn
+vWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPUZ5uW6M7XxgpT0GtJlvOj
+CwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvEpMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiil
+e7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCR
+OME4HYYEhLoaJXhena/MUGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpI
+CT0ugpTNGmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy5V65
+48r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv6q012iDTiIJh8BIi
+trzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEKeN5KzlW/HdXZt1bv8Hb/C3m1r737
+qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMB
+AAGjgZcwgZQwDwYDVR0TAQH/BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcC
+ARYxaHR0cDovL3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV
+HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqGSIb3DQEBCwUA
+A4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLySCZa59sCrI2AGeYwRTlHSeYAz
++51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwj
+f/ST7ZwaUb7dRUG/kSS0H4zpX897IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaN
+kqbG9AclVMwWVxJKgnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfk
+CpYL+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxLvJxxcypF
+URmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkmbEgeqmiSBeGCc1qb3Adb
+CG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvkN1trSt8sV4pAWja63XVECDdCcAz+3F4h
+oKOKwJCcaNpQ5kUQR3i2TtJlycM33+FCY7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoV
+IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm
+66+KAQ==
+-----END CERTIFICATE-----
+
+CA Disig
+========
+-----BEGIN CERTIFICATE-----
+MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMK
+QnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwHhcNMDYw
+MzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlz
+bGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgm
+GErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnXmjxUizkD
+Pw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYDXcDtab86wYqg6I7ZuUUo
+hwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhWS8+2rT+MitcE5eN4TPWGqvWP+j1scaMt
+ymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8w
+gfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0P
+AQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlz
+aWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2Ff
+ZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAa
+BgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59t
+WDYcPQuBDRIrRhCA/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3
+mkkp7M5+cTxqEEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/
+CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6K
+ezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA
+4Z7CRneC9VkGjCFMhwnN5ag=
+-----END CERTIFICATE-----
+
+Juur-SK
+=======
+-----BEGIN CERTIFICATE-----
+MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA
+c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw
+DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG
+SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy
+aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf
+TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC
++Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw
+UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa
+Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF
+MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD
+HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh
+AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA
+cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr
+AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw
+cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE
+FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G
+A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo
+ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL
+abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678
+IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh
+Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2
+yyqcjg==
+-----END CERTIFICATE-----
+
+Hongkong Post Root CA 1
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT
+DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx
+NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n
+IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1
+ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr
+auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh
+qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY
+V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV
+HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i
+h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio
+l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei
+IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps
+T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT
+c4afU9hDDl3WY4JxHYB0yvbiAmvZWg==
+-----END CERTIFICATE-----
+
+SecureSign RootCA11
+===================
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi
+SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS
+b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw
+KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1
+cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL
+TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO
+wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq
+g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP
+O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA
+bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX
+t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh
+OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r
+bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ
+Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01
+y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061
+lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I=
+-----END CERTIFICATE-----
+
+ACEDICOM Root
+=============
+-----BEGIN CERTIFICATE-----
+MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UEAwwNQUNFRElD
+T00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMB4XDTA4
+MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEWMBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoG
+A1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHk
+WLn709gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7XBZXehuD
+YAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5PGrjm6gSSrj0RuVFCPYew
+MYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAKt0SdE3QrwqXrIhWYENiLxQSfHY9g5QYb
+m8+5eaA9oiM/Qj9r+hwDezCNzmzAv+YbX79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbk
+HQl/Sog4P75n/TSW9R28MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTT
+xKJxqvQUfecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI2Sf2
+3EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyHK9caUPgn6C9D4zq9
+2Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEaeZAwUswdbxcJzbPEHXEUkFDWug/Fq
+TYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz
+4SsrSbbXc6GqlPUB53NlTKxQMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU
+9QHnc2VMrFAwRAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv
+bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWImfQwng4/F9tqg
+aHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3gvoFNTPhNahXwOf9jU8/kzJP
+eGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKeI6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1Pwk
+zQSulgUV1qzOMPPKC8W64iLgpq0i5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1
+ThCojz2GuHURwCRiipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oI
+KiMnMCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZo5NjEFIq
+nxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6zqylfDJKZ0DcMDQj3dcE
+I2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacNGHk0vFQYXlPKNFHtRQrmjseCNj6nOGOp
+MCwXEGCSn1WHElkQwg9naRHMTh5+Spqtr0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3o
+tkYNbn5XOmeUwssfnHdKZ05phkOTOPu220+DkdRgfks+KzgHVZhepA==
+-----END CERTIFICATE-----
+
+Verisign Class 3 Public Primary Certification Authority
+=======================================================
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx
+FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow
+XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz
+IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94
+f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol
+hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABByUqkFFBky
+CEHwxWsKzH4PIRnN5GfcX6kb5sroc50i2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWX
+bj9T/UWZYB2oK0z5XqcJ2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/
+D/xwzoiQ
+-----END CERTIFICATE-----
+
+Microsec e-Szigno Root CA 2009
+==============================
+-----BEGIN CERTIFICATE-----
+MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER
+MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv
+c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o
+dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE
+BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt
+U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA
+fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG
+0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA
+pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm
+1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC
+AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf
+QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE
+FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o
+lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX
+I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775
+tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02
+yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi
+LXpUq3DDfSJlgnCW
+-----END CERTIFICATE-----
+
+E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi
+===================================================
+-----BEGIN CERTIFICATE-----
+MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG
+EwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxpZ2kgQS5TLjE8MDoGA1UEAxMz
+ZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3
+MDEwNDExMzI0OFoXDTE3MDEwNDExMzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0
+cm9uaWsgQmlsZ2kgR3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9u
+aWsgU2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdUMZTe1RK6UxYC6lhj71vY
+8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlTL/jDj/6z/P2douNffb7tC+Bg62nsM+3Y
+jfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAI
+JjjcJRFHLfO6IxClv7wC90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk
+9Ok0oSy1c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/BAQD
+AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoEVtstxNulMA0GCSqG
+SIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLPqk/CaOv/gKlR6D1id4k9CnU58W5d
+F4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwq
+D2fK/A+JYZ1lpTzlvBNbCNvj/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4
+Vwpm+Vganf2XKWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq
+fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX
+-----END CERTIFICATE-----
+
+GlobalSign Root CA - R3
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv
+YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
+bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
+aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
+bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt
+iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ
+0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3
+rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl
+OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2
+xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
+FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7
+lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8
+EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E
+bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18
+YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r
+kpeDMdmztcpHWD9f
+-----END CERTIFICATE-----
+
+TC TrustCenter Universal CA III
+===============================
+-----BEGIN CERTIFICATE-----
+MIID4TCCAsmgAwIBAgIOYyUAAQACFI0zFQLkbPQwDQYJKoZIhvcNAQEFBQAwezELMAkGA1UEBhMC
+REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy
+IFVuaXZlcnNhbCBDQTEoMCYGA1UEAxMfVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIElJSTAe
+Fw0wOTA5MDkwODE1MjdaFw0yOTEyMzEyMzU5NTlaMHsxCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNU
+QyBUcnVzdENlbnRlciBHbWJIMSQwIgYDVQQLExtUQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0Ex
+KDAmBgNVBAMTH1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQSBJSUkwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQDC2pxisLlxErALyBpXsq6DFJmzNEubkKLF5+cvAqBNLaT6hdqbJYUt
+QCggbergvbFIgyIpRJ9Og+41URNzdNW88jBmlFPAQDYvDIRlzg9uwliT6CwLOunBjvvya8o84pxO
+juT5fdMnnxvVZ3iHLX8LR7PH6MlIfK8vzArZQe+f/prhsq75U7Xl6UafYOPfjdN/+5Z+s7Vy+Eut
+CHnNaYlAJ/Uqwa1D7KRTyGG299J5KmcYdkhtWyUB0SbFt1dpIxVbYYqt8Bst2a9c8SaQaanVDED1
+M4BDj5yjdipFtK+/fz6HP3bFzSreIMUWWMv5G/UPyw0RUmS40nZid4PxWJ//AgMBAAGjYzBhMB8G
+A1UdIwQYMBaAFFbn4VslQ4Dg9ozhcbyO5YAvxEjiMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
+BAQDAgEGMB0GA1UdDgQWBBRW5+FbJUOA4PaM4XG8juWAL8RI4jANBgkqhkiG9w0BAQUFAAOCAQEA
+g8ev6n9NCjw5sWi+e22JLumzCecYV42FmhfzdkJQEw/HkG8zrcVJYCtsSVgZ1OK+t7+rSbyUyKu+
+KGwWaODIl0YgoGhnYIg5IFHYaAERzqf2EQf27OysGh+yZm5WZ2B6dF7AbZc2rrUNXWZzwCUyRdhK
+BgePxLcHsU0GDeGl6/R1yrqc0L2z0zIkTO5+4nYES0lT2PLpVDP85XEfPRRclkvxOvIAu2y0+pZV
+CIgJwcyRGSmwIC3/yzikQOEXvnlhgP8HA4ZMTnsGnxGGjYnuJ8Tb4rwZjgvDwxPHLQNjO9Po5KIq
+woIIlBZU8O8fJ5AluA0OKBtHd0e9HKgl8ZS0Zg==
+-----END CERTIFICATE-----
+
+Autoridad de Certificacion Firmaprofesional CIF A62634068
+=========================================================
+-----BEGIN CERTIFICATE-----
+MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA
+BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
+MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw
+QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB
+NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD
+Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P
+B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY
+7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH
+ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI
+plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX
+MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX
+LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK
+bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU
+vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud
+EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH
+DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
+cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA
+bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx
+ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx
+51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk
+R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP
+T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f
+Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl
+osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR
+crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR
+saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD
+KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi
+6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
+-----END CERTIFICATE-----
+
+Izenpe.com
+==========
+-----BEGIN CERTIFICATE-----
+MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG
+EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz
+MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu
+QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ
+03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK
+ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU
++zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC
+PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT
+OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK
+F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK
+0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+
+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB
+leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID
+AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+
+SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG
+NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
+MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O
+BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l
+Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga
+kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q
+hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs
+g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5
+aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5
+nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC
+ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo
+Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z
+WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
+-----END CERTIFICATE-----
+
+Chambers of Commerce Root - 2008
+================================
+-----BEGIN CERTIFICATE-----
+MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD
+MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv
+bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu
+QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy
+Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl
+ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF
+EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl
+cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
+AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA
+XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj
+h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/
+ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk
+NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g
+D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331
+lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ
+0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj
+ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2
+EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI
+G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ
+BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh
+bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh
+bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC
+CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH
+AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1
+wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH
+3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU
+RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6
+M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1
+YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF
+9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK
+zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG
+nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg
+OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ
+-----END CERTIFICATE-----
+
+Global Chambersign Root - 2008
+==============================
+-----BEGIN CERTIFICATE-----
+MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD
+MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv
+bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu
+QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx
+NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg
+Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ
+QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD
+aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf
+VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf
+XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0
+ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB
+/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA
+TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M
+H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe
+Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF
+HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh
+wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB
+AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT
+BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE
+BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm
+aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm
+aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp
+1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0
+dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG
+/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6
+ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s
+dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg
+9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH
+foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du
+qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr
+P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq
+c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z
+09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B
+-----END CERTIFICATE-----
+
+Go Daddy Root Certificate Authority - G2
+========================================
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu
+MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
+MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
+b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G
+A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq
+9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD
++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd
+fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl
+NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9
+BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac
+vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r
+5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV
+N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
+LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1
+-----END CERTIFICATE-----
+
+Starfield Root Certificate Authority - G2
+=========================================
+-----BEGIN CERTIFICATE-----
+MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s
+b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0
+eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw
+DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg
+VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB
+dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv
+W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs
+bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk
+N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf
+ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU
+JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol
+TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx
+4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw
+F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
+pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ
+c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
+-----END CERTIFICATE-----
+
+Starfield Services Root Certificate Authority - G2
+==================================================
+-----BEGIN CERTIFICATE-----
+MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s
+b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl
+IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV
+BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT
+dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg
+Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2
+h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa
+hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP
+LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB
+rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw
+AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG
+SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP
+E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy
+xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
+iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza
+YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6
+-----END CERTIFICATE-----
+
+AffirmTrust Commercial
+======================
+-----BEGIN CERTIFICATE-----
+MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS
+BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw
+MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
+bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb
+DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV
+C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6
+BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww
+MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV
+HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG
+hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi
+qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv
+0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh
+sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
+-----END CERTIFICATE-----
+
+AffirmTrust Networking
+======================
+-----BEGIN CERTIFICATE-----
+MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS
+BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw
+MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
+bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE
+Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI
+dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24
+/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb
+h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV
+HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu
+UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6
+12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23
+WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9
+/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
+-----END CERTIFICATE-----
+
+AffirmTrust Premium
+===================
+-----BEGIN CERTIFICATE-----
+MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS
+BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy
+OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy
+dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn
+BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV
+5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs
++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd
+GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R
+p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI
+S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04
+6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5
+/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo
++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB
+/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv
+MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
+Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC
+6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S
+L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK
++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV
+BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg
+IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60
+g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb
+zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw==
+-----END CERTIFICATE-----
+
+AffirmTrust Premium ECC
+=======================
+-----BEGIN CERTIFICATE-----
+MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV
+BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx
+MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U
+cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ
+N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW
+BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK
+BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X
+57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM
+eQ==
+-----END CERTIFICATE-----
+
+Certum Trusted Network CA
+=========================
+-----BEGIN CERTIFICATE-----
+MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK
+ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy
+MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU
+ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC
+l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J
+J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4
+fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0
+cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB
+Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw
+DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj
+jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1
+mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj
+Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
+03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
+-----END CERTIFICATE-----
+
+Certinomis - Autorité Racine
+=============================
+-----BEGIN CERTIFICATE-----
+MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK
+Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg
+LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG
+A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw
+JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa
+wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly
+Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw
+2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N
+jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q
+c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC
+lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb
+xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g
+530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna
+4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
+A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ
+KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x
+WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva
+R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40
+nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B
+CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv
+JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE
+qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b
+WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE
+wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/
+vgt2Fl43N+bYdJeimUV5
+-----END CERTIFICATE-----
+
+Root CA Generalitat Valenciana
+==============================
+-----BEGIN CERTIFICATE-----
+MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE
+ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290
+IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3
+WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE
+CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2
+F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B
+ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ
+D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte
+JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB
+AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n
+dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB
+ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl
+AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA
+YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy
+AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA
+aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt
+AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA
+YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu
+AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA
+OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0
+dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV
+BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G
+A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S
+b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh
+TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz
+Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63
+NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH
+iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt
++GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM=
+-----END CERTIFICATE-----
+
+A-Trust-nQual-03
+================
+-----BEGIN CERTIFICATE-----
+MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJBVDFIMEYGA1UE
+Cgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBpbSBlbGVrdHIuIERhdGVudmVy
+a2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5RdWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5R
+dWFsLTAzMB4XDTA1MDgxNzIyMDAwMFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgw
+RgYDVQQKDD9BLVRydXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0
+ZW52ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMMEEEtVHJ1
+c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtPWFuA/OQO8BBC4SA
+zewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUjlUC5B3ilJfYKvUWG6Nm9wASOhURh73+n
+yfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZznF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPE
+SU7l0+m0iKsMrmKS1GWH2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4
+iHQF63n1k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs2e3V
+cuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0OBAoECERqlWdV
+eRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAVdRU0VlIXLOThaq/Yy/kgM40
+ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fGKOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmr
+sQd7TZjTXLDR8KdCoLXEjq/+8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZd
+JXDRZslo+S4RFGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS
+mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmEDNuxUCAKGkq6
+ahq97BvIxYSazQ==
+-----END CERTIFICATE-----
+
+TWCA Root Certification Authority
+=================================
+-----BEGIN CERTIFICATE-----
+MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ
+VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG
+EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB
+IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx
+QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC
+oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP
+4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r
+y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB
+BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG
+9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC
+mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW
+QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY
+T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny
+Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==
+-----END CERTIFICATE-----
+
+Security Communication RootCA2
+==============================
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
+U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh
+dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC
+SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy
+aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++
++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R
+3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV
+spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K
+EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8
+QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB
+CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj
+u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk
+3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q
+tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29
+mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
+-----END CERTIFICATE-----
+
+EC-ACC
+======
+-----BEGIN CERTIFICATE-----
+MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE
+BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w
+ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD
+VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE
+CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT
+BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7
+MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt
+SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl
+Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh
+cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK
+w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT
+ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4
+HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a
+E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw
+0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD
+VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0
+Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l
+dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ
+lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa
+Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe
+l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2
+E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D
+5EI=
+-----END CERTIFICATE-----
--- /dev/null
+<include>
+ <context name="public">
+
+ <extension name="unloop">
+ <condition field="${unroll_loops}" expression="^true$"/>
+ <condition field="${sip_looped_call}" expression="^true$">
+ <action application="deflect" data="${destination_number}"/>
+ </condition>
+ </extension>
+ <!--
+ Tag anything pass thru here as an outside_call so you can make sure not
+ to create any routing loops based on the conditions that it came from
+ the outside of the switch.
+ -->
+ <extension name="outside_call" continue="true">
+ <condition>
+ <action application="set" data="outside_call=true"/>
+ <action application="export" data="RFC2822_DATE=${strftime(%a, %d %b %Y %T %z)}"/>
+ </condition>
+ </extension>
+
+ <extension name="call_debug" continue="true">
+ <condition field="${call_debug}" expression="^true$" break="never">
+ <action application="info"/>
+ </condition>
+ </extension>
+
+ <extension name="adhearsion">
+ <condition>
+ <action application="rayo" data=""/>
+ </condition>
+ </extension>
+
+ </context>
+</include>
+
--- /dev/null
+<!--
+ NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
+
+ FreeSWITCH works off the concept of users and domains just like email.
+ You have users that are in domains for example 1000@domain.com.
+
+ When freeswitch gets a register packet it looks for the user in the directory
+ based on the from or to domain in the packet depending on how your sofia profile
+ is configured. Out of the box the default domain will be the IP address of the
+ machine running FreeSWITCH. This IP can be found by typing "sofia status" at the
+ CLI. You will register your phones to the IP and not the hostname by default.
+ If you wish to register using the domain please open vars.xml in the root conf
+ directory and set the default domain to the hostname you desire. Then you would
+ use the domain name in the client instead of the IP address to register
+ with FreeSWITCH.
+
+ NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
+-->
+
+<include>
+ <!--the domain or ip (the right hand side of the @ in the addr-->
+ <domain name="$${domain}">
+ <params>
+ <param name="dial-string" value="{^^:sip_invite_domain=${dialed_domain}:presence_id=${dialed_user}@${dialed_domain}}${sofia_contact(*/${dialed_user}@${dialed_domain})}"/>
+ </params>
+
+ <variables>
+ </variables>
+
+ <groups>
+ <group name="default">
+ <users>
+ <X-PRE-PROCESS cmd="include" data="default/*.xml"/>
+ </users>
+ </group>
+ </groups>
+
+ </domain>
+</include>
--- /dev/null
+<include>
+ <user id="usera">
+ <!-- Outbound Registrations Related to this user -->
+ <gateways>
+ </gateways>
+ <params>
+ <!-- omit password for authless registration -->
+ <param name="password" value="1"/>
+ </params>
+ <variables>
+ <!--all variables here will be set on all inbound calls that originate from this user -->
+ <variable name="user_context" value="public"/>
+ <variable name="effective_caller_id_name" value="User A"/>
+ <variable name="effective_caller_id_number" value="9009095333"/>
+ </variables>
+
+ <vcard>
+ <!-- insert optional compliant vcard xml here-->
+ </vcard>
+ </user>
+</include>
--- /dev/null
+<include>
+ <user id="userb">
+ <!-- Outbound Registrations Related to this user -->
+ <gateways>
+ </gateways>
+ <params>
+ <!-- omit password for authless registration -->
+ <param name="password" value="1"/>
+ </params>
+ <variables>
+ <!--all variables here will be set on all inbound calls that originate from this user -->
+ <variable name="user_context" value="public"/>
+ <variable name="effective_caller_id_name" value="User B"/>
+ <variable name="effective_caller_id_number" value="9009093733"/>
+ </variables>
+
+ <vcard>
+ <!-- insert optional compliant vcard xml here-->
+ </vcard>
+ </user>
+</include>
--- /dev/null
+<include>
+ <user id="userc">
+ <!-- Outbound Registrations Related to this user -->
+ <gateways>
+ </gateways>
+ <params>
+ <!-- omit password for authless registration -->
+ <param name="password" value="1"/>
+ </params>
+ <variables>
+ <!--all variables here will be set on all inbound calls that originate from this user -->
+ <variable name="user_context" value="public"/>
+ <variable name="effective_caller_id_name" value="User C"/>
+ <variable name="effective_caller_id_number" value="8006492568"/>
+ </variables>
+
+ <vcard>
+ <!-- insert optional compliant vcard xml here-->
+ </vcard>
+ </user>
+</include>
--- /dev/null
+<include>
+ <user id="userd">
+ <!-- Outbound Registrations Related to this user -->
+ <gateways>
+ </gateways>
+ <params>
+ <!-- omit password for authless registration -->
+ <param name="password" value="1"/>
+ </params>
+ <variables>
+ <!--all variables here will be set on all inbound calls that originate from this user -->
+ <variable name="user_context" value="public"/>
+ <variable name="effective_caller_id_name" value="Jenny"/>
+ <variable name="effective_caller_id_number" value="2128675309"/>
+ </variables>
+
+ <vcard>
+ <!-- insert optional compliant vcard xml here-->
+ </vcard>
+ </user>
+</include>
--- /dev/null
+<?xml version="1.0"?>
+<!--
+ NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
+
+ This is the FreeSWITCH default config. Everything you see before you now traverses
+ down into all the directories including files which include more files. The default
+ config comes out of the box already working in most situations as a PBX. This will
+ allow you to get started testing and playing with various things in FreeSWITCH.
+
+ Before you start to modify this default please visit this wiki page:
+
+ http://wiki.freeswitch.org/wiki/Getting_Started_Guide#Some_stuff_to_try_out.21
+
+ If all else fails you can read our FAQ located at:
+
+ http://wiki.freeswitch.org/wiki/FreeSwitch_FAQ
+
+ NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
+-->
+<document type="freeswitch/xml">
+ <!--#comment
+ All comments starting with #command will be preprocessed and never sent to the xml parser
+ Valid instructions:
+ #include ==> Include another file to this exact point
+ (partial xml should be encased in <include></include> tags)
+ #set ==> Set a global variable (can be expanded during preprocessing with $$ variables)
+ (note the double $$ which denotes preprocessor variables)
+ #comment ==> A general comment such as this
+
+ The preprocessor will compile the full xml document to ${prefix}/log/freeswitch.xml.fsxml
+ Don't modify it while freeswitch is running cos it is mem mapped in most cases =D
+
+ The same can be achieved with the <X-PRE-PROCESS> tag where the attrs 'cmd' and 'data' are
+ parsed in the same way.
+ -->
+ <!--#comment
+ vars.xml contains all the #set directives for the preprocessor.
+ -->
+ <X-PRE-PROCESS cmd="include" data="vars.xml"/>
+
+ <section name="configuration" description="Various Configuration">
+ <X-PRE-PROCESS cmd="include" data="autoload_configs/*.xml"/>
+ </section>
+
+ <section name="dialplan" description="Regex/XML Dialplan">
+ <X-PRE-PROCESS cmd="include" data="dialplan/*.xml"/>
+ </section>
+
+ <!-- mod_dingaling is reliant on the vcard data in the "directory" section. -->
+ <!-- mod_sofia is reliant on the user data for authorization -->
+ <section name="directory" description="User Directory">
+ <X-PRE-PROCESS cmd="include" data="directory/*.xml"/>
+ </section>
+
+ <!-- languages section (under development still) -->
+ <section name="languages" description="Language Management">
+ <X-PRE-PROCESS cmd="include" data="lang/de/*.xml"/>
+ <X-PRE-PROCESS cmd="include" data="lang/en/*.xml"/>
+ <X-PRE-PROCESS cmd="include" data="lang/fr/*.xml"/>
+ <X-PRE-PROCESS cmd="include" data="lang/ru/*.xml"/>
+ <X-PRE-PROCESS cmd="include" data="lang/he/*.xml"/>
+ <X-PRE-PROCESS cmd="include" data="lang/es/es_ES.xml"/>
+ <X-NO-PRE-PROCESS cmd="include" data="lang/es/es_MX.xml"/>
+ <X-PRE-PROCESS cmd="include" data="lang/pt/pt_BR.xml"/>
+ <X-NO-PRE-PROCESS cmd="include" data="lang/pt/pt_PT.xml"/>
+ </section>
+
+</document>
--- /dev/null
+<include>
+ <language name="de" sound-prefix="/snds" tts-engine="cepstral" tts-voice="david">
+ <phrases>
+ <macros>
+ <X-PRE-PROCESS cmd="include" data="demo/demo.xml"/>
+ <!--voicemail_de_tts is purely implemented with tts, we need a files based implementation too -->
+ <X-PRE-PROCESS cmd="include" data="vm/tts.xml"/>
+ </macros>
+ </phrases>
+ </language>
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+ <macro name="msgcount">
+ <input pattern="(.*)">
+ <match>
+ <action function="execute" data="sleep(1000)"/>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ <!-- or -->
+ <!--<action function="speak-text" data="Sie haben $1 Nachrichten"/>-->
+ </match>
+ </input>
+ </macro>
+ <macro name="saydate">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="timespec">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="time_measurement"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="ip-addr">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="iterated" type="ip_address"/>
+ <action function="say" data="$1" method="pronounced" type="ip_address"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell-phonetic">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="tts-timeleft">
+ <!-- The parser will visit each <input> tag and execute the actions in <match> or <nomatch> depending on the pattern param -->
+ <!-- If the function "break" is encountered all parsing will cease -->
+ <input pattern="(\d+):(\d+)">
+ <match>
+ <action function="speak-text" data="Sie haben $1 Minuten, $2 Sekunden übrig $strftime(%Y-%m-%d)"/>
+ <action function="break"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="Die Eingabe war ungültig."/>
+ </nomatch>
+ </input>
+ <input pattern="(\d+) min (\d+) sek">
+ <match>
+ <action function="speak-text" data="Sie haben $1 Minuten, $2 Sekunden übrig $strftime(%Y-%m-%d)"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="Die Eingabe war ungültig."/>
+ </nomatch>
+ </input>
+ </macro>
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_id.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_pass.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-fail_auth.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <!--<action function="play-file" data="voicemail/vm-hello.wav"/> -->
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-goodbye.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-abort.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^(1):(.*)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action application="log" data="INFO $1 $2"/>
+ <action function="say" data="$1" method="pronounced" type="items" gender="feminine"/>
+ <action function="play-file" data="voicemail/vm-$2.wav"/>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ </match>
+ </input>
+ <input pattern="^(\d+):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items" gender="feminine"/>
+ <action function="play-file" data="voicemail/vm-$2.wav"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <!-- To listen to new messages -->
+ <action function="play-file" data="voicemail/vm-listen_new.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <!-- To listen to saved messages -->
+ <action function="play-file" data="voicemail/vm-listen_saved.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <!-- For advanced options -->
+ <action function="play-file" data="voicemail/vm-advanced.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <!-- To exit -->
+ <action function="play-file" data="voicemail/vm-to_exit.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <!-- To record a greeting -->
+ <action function="play-file" data="voicemail/vm-to_record_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <!-- To choose greeting -->
+ <action function="play-file" data="voicemail/vm-choose_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <!-- To record your name -->
+ <action function="play-file" data="voicemail/vm-record_name2.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <!-- To change password -->
+ <action function="play-file" data="voicemail/vm-change_password.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <!-- To return to main menu -->
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_name1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-rerecord.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mark-urgent.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-continue.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_prepend">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_add_intro.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-send_message_now.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_enter_ext.wav"/>
+ <action function="play-file" data="voicemail/vm-followed_by.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_invalid_extension">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-that_was_an_invalid_ext.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-forward_to_email.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_choose.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_fail.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_greeting.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_message.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-greeting.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-selected.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-person.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-not_available.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ <action function="play-file" data="voicemail/vm-message_number.wav"/>
+ <action application="log" data="INFO $1 $2"/>
+ <action function="say" data="$2" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <!-- Note: Update this to marked-urgent,emailed and saved once new sound files are recorded -->
+ <macro name="voicemail_ack">
+ <input pattern="^(too-small)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-too-small.wav"/>
+ </match>
+ </input>
+ <input pattern="^(deleted)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(saved)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(emailed)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(marked-urgent)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action application="log" data="INFO $1"/>
+ <action function="say" data="$1" method="counted" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_disk_quota_exceeded">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mailbox_full.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_announce_ext">
+ <input pattern="^([^\:]+):(.*)$">
+ <match>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_lot_full">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_lot_empty">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Bitte geben Sie Ihren Benutzernamen ein, gefolgt von $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Bitte geben Sie Ihr Passwort ein, gefolgt von $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Falsche Benutzerdaten."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Willkommen in Ihrem Postfach."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Auf Wiedersehen."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Zu viele Fehlversuche."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^1:(.*)$">
+ <match>
+ <action function="speak-text" data="Sie haben 1 $2 Nachricht im Ordner ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ <input pattern="^([0,2-9]+):(.*)$">
+ <match>
+ <action function="speak-text" data="Sie haben $1 $2 Nachrichten im Ordner ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Um neue Nachrichten zu hören, drücken Sie $1. Um gespeicherte Nachrichten zu hören, drücken Sie $2, Für erweiterte Optionen, drücken Sie $3. Zum beenden drücken Sie $4."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Um eine Ansage aufzunehmen, drücken Sie $1. Um eine Ansage auszuwählen, drücken Sie $2. Um ihren Namen aufzunehmen, drücken Sie $3. Um zum Hauptmenü zurückzukehren, drücken Sie $4."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Sprechen Sie Ihren Namen nach dem Ton, drücken Sie eine beliebige Taste oder hören Sie auf zu sprechen um die Aufnahme zu beenden."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Um die Aufzeichnung anzuhören, drücken Sie $1. Um die Aufzeichnung zu speichern, drücken Sie $2. Für eine erneute Aufnahme drücken Sie $3."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Drücken Sie $1 um diese Nachricht als wichtig zu markieren. Um fortzufahren drücken Sie $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Drücken Sie $1 um die Nachricht erneut zu hören. Um die Nachricht zu speichern, drücken Sie $2. Zum löschen der Nachricht drücken Sie $3. Für die Weiterleitung als Email, drücken Sie $4."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Wählen Sie eine Ansage zwischen 1 und 3."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Ungültige Eingabe."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Zeichnen Sie Ihre Ansage nach dem Ton auf. Drücken Sie eine beliebige Taste oder hören Sie auf zu sprechen um die Aufnahme zu beenden."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Sprechen Sie nach dem Ton. Drücken Sie eine beliebige Taste oder hören Sie auf zu sprechen um die Aufnahme zu beenden."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Ansage $1 ausgewählt."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 ist nicht verfügbar."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(.*)$">
+ <match>
+ <action function="speak-text" data="$1 Nachricht Nummer $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_ack">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Nachricht $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$strftime($1|%A, %B %d %Y, %I %M %p)"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="demo_ivr_count">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="demo_ivr_main_menu" pause="100"> <!-- See conf/autoload_config/ivr.conf.xml for an example on how to use this macro in an IVR -->
+ <input pattern="(.*)">
+ <match>
+ <!-- string together several existing sound files to create one long greeting -->
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="ivr/ivr-this_ivr_will_let_you_test_features.wav"/>
+ <action function="play-file" data="ivr/ivr-you_may_exit_by_hanging_up.wav"/>
+ <!-- note that you can do more than just play files, e.g. have pauses and do TTS -->
+
+ <!-- Menu option 1: Call FreeSWITCH conference-->
+ <action function="play-file" data="ivr/ivr-enter_ext_pound.wav"/>
+ <action function="play-file" data="silence_stream://1500"/>
+ <action function="play-file" data="ivr/ivr-to_call_the_freeswitch_conference.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: Do FreeSWITCH echo test -->
+ <action function="play-file" data="ivr/ivr-to_do_a_freeswitch_echo_test.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: Listen to Music on Hold -->
+ <action function="play-file" data="ivr/ivr-to_listen_to_moh.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: Register for ClueCon -->
+ <action function="play-file" data="ivr/ivr-register_for_cluecon.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 5: Listen to screaming monkeys -->
+ <action function="play-file" data="ivr/ivr-to_hear_screaming_monkeys.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/5.wav"/>
+
+ <!-- Menu option 6: Hear a sample submenu -->
+ <action function="play-file" data="ivr/ivr-to_hear_sample_submenu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/6.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as demo_ivr_main_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="demo_ivr_main_menu_short" pause="100">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option 1: Call FreeSWITCH conference-->
+ <action function="play-file" data="silence_stream://1000"/>
+ <action function="play-file" data="ivr/ivr-to_call_the_freeswitch_conference.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: Do FreeSWITCH echo test -->
+ <action function="play-file" data="ivr/ivr-to_do_a_freeswitch_echo_test.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: Listen to Music on Hold -->
+ <action function="play-file" data="ivr/ivr-to_listen_to_moh.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: Hear a sample submenu -->
+ <action function="play-file" data="ivr/ivr-to_hear_sample_submenu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 5: Listen to screaming monkeys -->
+ <action function="play-file" data="ivr/ivr-to_hear_screaming_monkeys.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/5.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the "long" greeting for the demo_ivr_sub_menu -->
+ <macro name="demo_ivr_sub_menu">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="ivr/ivr-sample_submenu.wav"/>
+
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as demo_ivr_sub_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="demo_ivr_sub_menu_short">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+ <macro name="msgcount">
+ <input pattern="(.*)">
+ <match>
+ <action function="execute" data="sleep(1000)"/>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ <!-- or -->
+ <!--<action function="speak-text" data="you have $1 messages"/>-->
+ </match>
+ </input>
+ </macro>
+ <macro name="saydate">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="timespec">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="time_measurement"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="ip-addr">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="iterated" type="ip_address"/>
+ <action function="say" data="$1" method="pronounced" type="ip_address"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell-phonetic">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="tts-timeleft">
+ <!-- The parser will visit each <input> tag and execute the actions in <match> or <nomatch> depending on the pattern param -->
+ <!-- If the function "break" is encountered all parsing will cease -->
+ <input pattern="(\d+):(\d+)">
+ <match>
+ <action function="speak-text" data="You have $1 minutes, $2 seconds remaining $strftime(%Y-%m-%d)"/>
+ <action function="break"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="That input was invalid."/>
+ </nomatch>
+ </input>
+ <input pattern="(\d+) min (\d+) sec">
+ <match>
+ <action function="speak-text" data="You have $1 minutes, $2 seconds remaining $strftime(%Y-%m-%d)"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="That input was invalid."/>
+ </nomatch>
+ </input>
+ </macro>
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+ <macro name="funny_prompts" pause="750">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="ivr/ivr-wakey_wakey_sunshine.wav"/>
+ <action function="play-file" data="ivr/ivr-no_no_no.wav"/>
+ <action function="play-file" data="ivr/ivr-did_you_mean_to_press_key.wav"/>
+ <action function="play-file" data="ivr/ivr-seriously_mean_to_press_key.wav"/>
+ <action function="play-file" data="ivr/ivr-oh_whatever.wav"/>
+ <action function="play-file" data="ivr/ivr-one_more_mistake.wav"/>
+ <action function="play-file" data="ivr/ivr-congratulations_you_pressed_star.wav"/>
+ <action function="play-file" data="ivr/ivr-engineers_busy_assisting_other_sales.wav"/>
+ <action function="play-file" data="ivr/ivr-message_self_destruct.wav"/>
+ <action function="play-file" data="ivr/ivr-all_your_call_are_belong_to_us.wav"/>
+ <action function="play-file" data="ivr/ivr-love_those_touch_tones.wav"/>
+ <action function="play-file" data="ivr/ivr-yes_we_have_no_bananas.wav"/>
+ <action function="play-file" data="ivr/ivr-dude_you_suck.wav"/>
+ <action function="play-file" data="ivr/ivr-on_hold_indefinitely.wav"/>
+ <action function="play-file" data="ivr/ivr-youre_doing_it_wrong.wav"/>
+ <action function="play-file" data="ivr/ivr-were_asterisk_free.wav"/>
+ <action function="play-file" data="ivr/ivr-douche_telecom.wav"/>
+ <action function="play-file" data="ivr/ivr-asterisk_like_syphilis.wav"/>
+ <action function="play-file" data="ivr/ivr-freeguipy.wav"/>
+ <action function="play-file" data="ivr/ivr-terribly_wrong_awkward.wav"/>
+ <action function="play-file" data="ivr/ivr-it_was_that_bug.wav"/>
+ <action function="play-file" data="ivr/ivr-concentrate.wav"/>
+ <action function="play-file" data="ivr/ivr-founder_of_freesource.wav"/>
+ <action function="play-file" data="ivr/ivr-cold_foolish.wav"/>
+ <action function="play-file" data="ivr/ivr-trollover_minutes.wav"/>
+ <action function="play-file" data="ivr/ivr-yuno_silent_drill.wav"/>
+ <action function="play-file" data="ivr/ivr-beacuase.wav"/>
+ </match>
+ </input>
+ </macro>
+</include>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+
+ <macro name="new_demo_ivr_main_menu" pause="100"> <!-- See conf/ivr_menus/ivr.conf.xml for an example on how to use this macro in an IVR -->
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="silence_stream://500"/>
+
+ <!-- Menu option 1: For information about FreeSWITCH... -->
+ <action function="play-file" data="misc/misc-information_about_freeswitch.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: To learn more about FreeSWITCH Solutions... -->
+ <action function="play-file" data="misc/misc-learn_more_about_freeswitch_solutions.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: To hear about ClueCon -->
+ <action function="play-file" data="misc/misc-to_hear_about_cluecon.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: For other options -->
+ <action function="play-file" data="ivr/ivr-for_other_options.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as new_demo_ivr_main_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="new_demo_ivr_main_menu_short" pause="100">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option 1: For information about FreeSWITCH... -->
+ <action function="play-file" data="misc/misc-information_about_freeswitch.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: To learn more about FreeSWITCH Solutions... -->
+ <action function="play-file" data="misc/misc-learn_more_about_freeswitch_solutions.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: To hear about ClueCon -->
+ <action function="play-file" data="misc/misc-to_hear_about_cluecon.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: For other options -->
+ <action function="play-file" data="ivr/ivr-for_other_options.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+
+ </match>
+ </input>
+ </macro>
+
+ <!-- More information about FreeSWITCH... -->
+ <macro name="learn_about_freeswitch_sub_menu">
+ <input pattern="(.*)">
+ <match>
+ <!-- Information about FreeSWITCH and OSTAG... -->
+ <action function="play-file" data="misc-freeswitch_is_state_of_the_art.wav"/>
+ <action function="play-file" data="silence_stream://50"/>
+ <action function="play-file" data="misc-it_is_stable_scalable_extensible.wav"/>
+ <action function="play-file" data="silence_stream://50"/>
+ <action function="play-file" data="misc-free_to_download.wav"/>
+ <action function="play-file" data="silence_stream://50"/>
+ <action function="play-file" data="misc-freeswitch_sponsored_by_ostag.wav"/>
+ <action function="play-file" data="silence_stream://50"/>
+ <action function="play-file" data="misc-ostag_learn_more.wav"/>
+ <action function="play-file" data="silence_stream://50"/>
+
+ <!-- Menu option 9: Repeat this information -->
+ <action function="play-file" data="ivr/ivr-repeat_this_information.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+ <!-- More information about FreeSWITCH Solutions... -->
+ <macro name="learn_about_freeswitch_solutions_sub_menu">
+ <input pattern="(.*)">
+ <match>
+ <!-- Information about FreeSWITCH Solutions... -->
+ <action function="play-file" data="[[sounds from tony/brian]]"/>
+ <action function="play-file" data="silence_stream://50"/>
+ <action function="play-file" data="[[sounds from tony/brian]]"/>
+ <action function="play-file" data="silence_stream://50"/>
+ <action function="play-file" data="[[sounds from tony/brian]]"/>
+ <action function="play-file" data="silence_stream://50"/>
+ <action function="play-file" data="[[sounds from tony/brian]]"/>
+ <action function="play-file" data="silence_stream://50"/>
+ <action function="play-file" data="[[sounds from tony/brian]]"/>
+ <action function="play-file" data="silence_stream://50"/>
+
+ <!-- Menu option 9: Repeat this information -->
+ <action function="play-file" data="ivr/ivr-repeat_this_information.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+ <!-- More information about ClueCon -->
+ <macro name="learn_about_cluecon_sub_menu">
+ <input pattern="(.*)">
+ <match>
+ <!-- Information about ClueCon... -->
+ <action function="play-file" data="misc-cluecon_is_premier_conference.wav"/>
+ <action function="play-file" data="silence_stream://50"/>
+ <action function="play-file" data="misc-chicago_each_summer.wav"/>
+ <action function="play-file" data="silence_stream://50"/>
+ <action function="play-file" data="misc-wide_range_of_persons.wav"/>
+ <action function="play-file" data="silence_stream://50"/>
+ <action function="play-file" data="misc-support_open_source_by_attending.wav"/>
+ <action function="play-file" data="silence_stream://500"/>
+ <action function="play-file" data="ivr-register_for_cluecon.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+ <action function="play-file" data="silence_stream://500"/>
+
+ <!-- Menu option 9: Repeat this information -->
+ <action function="play-file" data="ivr/ivr-repeat_this_information.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include>
+
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter_person.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter_person.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-specify_mininum.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-letters_of_person_name.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="play-file" data="directory/dir-no_matching_results.wav"/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-result_match.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-too_many_result.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-no_more_results.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-result_number.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-at_extension.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="play-file" data="directory/dir-to_select_entry.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_next.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_prev.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-start_new_search.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Please enter the first few digit of the person last name"/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Please enter the first few digit of the person first name"/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="speak-text" data="to search by first name, press $2"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="speak-text" data="to search by last name, press $2"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="You need to specify a minimum the first $1 letters of the person name, try again."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="speak-text" data="Your search match no user on this system, try again."/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 result match your search"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Your search returned too many result, please try again"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="No more result"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Result number $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="speak-text" data="To select this entry press $1, for the next entry press $2, for the previous entry press $3, to make a new search press $4"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="at extension $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+ <language name="en" say-module="en" sound-prefix="$${sounds_dir}/en/us/callie" tts-engine="cepstral" tts-voice="callie">
+ <phrases>
+ <macros>
+ <X-PRE-PROCESS cmd="include" data="demo/*.xml"/> <!-- Note: this now grabs whole subdir, previously grabbed only demo.xml -->
+ <!-- voicemail_en_tts is purely implemented with tts, we have the files based one that is the default. -->
+ <X-PRE-PROCESS cmd="include" data="vm/sounds.xml"/> <!-- vm/tts.xml if you want to use tts and have cepstral -->
+ <X-PRE-PROCESS cmd="include" data="dir/sounds.xml"/> <!-- dir/tts.xml if you want to use tts and have cepstral -->
+ <X-PRE-PROCESS cmd="include" data="ivr/*.xml"/> <!-- IVR and custom phrases go here -->
+ </macros>
+ <X-PRE-PROCESS cmd="include" data="vm/voicemail_ivr.xml"/>
+ </phrases>
+ </language>
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+ <macro name="queue_position">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="ivr/ivr-you_are_number.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="ivr/ivr-in_line.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="has_called_conf">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="$1"/>
+ <action function="sleep" data="100"/>
+ <action function="play-file" data="conference/conf-has_joined.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="has_left_conf">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="$1"/>
+ <action function="sleep" data="100"/>
+ <action function="play-file" data="conference/conf-has_left.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="callers_in_conf">
+ <input pattern="^1$" break_on_match="true">
+ <match>
+ <action function="sleep" data="500"/>
+ <!--<action function="play-file" data="ivr/ivr-there_is.wav"/>-->
+ <action function="play-file" data="digits/1.wav"/>
+ <action function="sleep" data="100"/>
+ <action function="play-file" data="conference/conf-listener_in_conference.wav"/>
+ </match>
+ </input>
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="sleep" data="500"/>
+ <action function="play-file" data="ivr/ivr-there_are.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="sleep" data="100"/>
+ <action function="play-file" data="conference/conf-members_in_conference.wav"/>
+ </match>
+ <nomatch>
+ <action function="sleep" data="500"/>
+ <action function="play-file" data="ivr/ivr-there_are.wav"/>
+ <action function="play-file" data="digits/0.wav"/>
+ <action function="sleep" data="100"/>
+ <action function="play-file" data="conference/conf-members_in_conference.wav"/>
+ </nomatch>
+ </input>
+ </macro>
+
+ <macro name="enter_dest_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="sleep" data="1000"/>
+ <action function="play-file" data="ivr/ivr-enter_destination_telephone_number.wav"/>
+ <action function="sleep" data="1000"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="enter_src_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="sleep" data="1000"/>
+ <action function="play-file" data="ivr/ivr-enter_source_telephone_number.wav"/>
+ <action function="sleep" data="1000"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="call_forward_set">
+ <input pattern="^(\d+):(\d+)$">
+ <match>
+ <action function="sleep" data="1000"/>
+ <action function="play-file" data="ivr/ivr-extension_number.wav"/>
+ <action function="sleep" data="400"/>
+ <action function="say" data="$1" method="iterated" type="number"/>
+ <action function="sleep" data="400"/>
+ <action function="play-file" data="digits/2.wav"/>
+ <action function="sleep" data="1000"/>
+ <action function="play-file" data="ivr/ivr-extension_number.wav"/>
+ <action function="sleep" data="400"/>
+ <action function="say" data="$2" method="iterated" type="number"/>
+ <action function="sleep" data="1000"/>
+ <action function="play-file" data="ivr/ivr-call_forwarding_has_been_set.wav"/>
+ <action function="sleep" data="1500"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="call_forward_cancel">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="sleep" data="1000"/>
+ <action function="play-file" data="ivr/ivr-extension_number.wav"/>
+ <action function="sleep" data="400"/>
+ <action function="say" data="$1" method="iterated" type="number"/>
+ <action function="play-file" data="ivr/ivr-call_forwarding_has_been_cancelled.wav"/>
+ <action function="sleep" data="1500"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="screen_confirm">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="sleep" data="500"/>
+ <action function="play-file" data="ivr/ivr-call_from.wav"/>
+ <action function="sleep" data="250"/>
+ <!-- Note, be sure to pass in the full path to the file or else!! -->
+ <action function="play-file" data="$1"/>
+ <action function="sleep" data="500"/>
+ <action function="play-file" data="ivr/ivr-to_accept_press_one.wav"/>
+ <action function="sleep" data="500"/>
+ <action function="play-file" data="ivr/ivr-to_reject.wav"/>
+ <action function="sleep" data="50"/>
+ <action function="play-file" data="ivr/ivr-hang_up.wav"/>
+ <action function="sleep" data="1500"/>
+ <action function="play-file" data="ivr/ivr-call_from.wav"/>
+ <action function="sleep" data="250"/>
+ <action function="play-file" data="$1"/>
+ <action function="sleep" data="500"/>
+ <action function="play-file" data="ivr/ivr-to_accept_press_one.wav"/>
+ <action function="sleep" data="500"/>
+ <action function="play-file" data="ivr/ivr-to_reject.wav"/>
+ <action function="sleep" data="50"/>
+ <action function="play-file" data="ivr/ivr-hang_up.wav"/>
+ <action function="sleep" data="1500"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_id.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_pass.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-fail_auth.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_change_pass_success">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-password_has_been_changed.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_change_pass_fail">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-password_not_valid.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <!--<action function="play-file" data="voicemail/vm-hello.wav"/> -->
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-goodbye.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-abort.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^(1):(.*)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-$2.wav"/>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ </match>
+ </input>
+ <input pattern="^(\d+):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-$2.wav"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <!-- To listen to new messages -->
+ <action function="play-file" data="voicemail/vm-listen_new.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To listen to saved messages -->
+ <action function="play-file" data="voicemail/vm-listen_saved.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- For advanced options -->
+ <action function="play-file" data="voicemail/vm-advanced.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To exit -->
+ <action function="play-file" data="voicemail/vm-to_exit.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <!-- To record a greeting -->
+ <action function="play-file" data="voicemail/vm-to_record_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To choose greeting -->
+ <action function="play-file" data="voicemail/vm-choose_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To record your name -->
+ <action function="play-file" data="voicemail/vm-record_name2.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To change password -->
+ <action function="play-file" data="voicemail/vm-change_password.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To return to main menu -->
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_name1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-rerecord.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mark-urgent.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-continue.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_prepend">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_add_intro.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-send_message_now.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_enter_ext.wav"/>
+ <action function="play-file" data="voicemail/vm-followed_by.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_invalid_extension">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-that_was_an_invalid_ext.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-forward_to_email.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_choose.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_fail.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_greeting.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_message.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-greeting.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-selected.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-person.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-not_available.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ <action function="play-file" data="voicemail/vm-message_number.wav"/>
+ <action function="say" data="$2" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^000|^$|^[Aa]non|^[Pp]rivate" break_on_match="true">
+ <match>
+ <!-- add 'anonymous caller' sound here -->
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="ivr/ivr-this_is_a_call_from.wav"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(500)"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <!-- Note: Update this to marked-urgent,emailed and saved once new sound files are recorded -->
+ <macro name="voicemail_ack">
+ <input pattern="^(too-small)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-too-small.wav"/>
+ </match>
+ </input>
+ <input pattern="^(deleted)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(saved)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(emailed)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(marked-urgent)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="short_date_time"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_disk_quota_exceeded">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mailbox_full.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_announce_ext">
+ <input pattern="^([^\:]+):(.*)$">
+ <match>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_lot_full">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_lot_empty">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="please enter your i d, followed by $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="please enter your password, followed by $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="login incorrect."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="welcome to your voicemail."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="goodbye."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="too many failed attempts."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^1:(.*)$" break_on_match="true">
+ <match>
+ <action function="speak-text" data="you have 1 $1 message in folder ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ <input pattern="^(\d+):(.*)$">
+ <match>
+ <action function="speak-text" data="you have $1 $2 messages in folder ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="To listen to new messages, press $1, To listen to saved messages, press $2, For advanced options, press $3, to exit, press $4."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="To record a greeting, press $1, To choose a greeting, press $2, To record your name, press $3, to change your password, press $5, to return to the main menu, press $5."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="at the tone, please record your name, press any key or stop talking to end the recording."/>
+
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="To listen to the recording, press $1, To save the recording, press $2, To re record, press $3."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="To mark this message urgent, press $1, To continue, press $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_invalid_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="$1 is not a valid extension."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="enter the extension you wish to forward to, then press $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_prepend">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="To record an announcement, press $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="To listen to the recording again, press $1, To save the recording, press $2, To delete the recording, press $3, to forward the recording to your email, press $4, to call the caller now, press $5, To forward this message to another extension, press $6."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="choose a greeting between 1 and 3."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="invalid value."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="record your greeting at the tone, press any key or stop talking to end the recording."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="record your message at the tone, press any key or stop talking to end the recording."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="greeting $1 selected."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 is not available."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(.*)$">
+ <match>
+ <action function="speak-text" data="$1 message number $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_ack">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="message $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="${strftime($1|%A, %B %d %Y, %I:%M %p)}"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macros name="voicemail_ivr">
+ <macro name="press_key">
+ <input pattern="^(.*):(.*)$">
+ <match>
+ <action function="play-file" data="$2"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="plural_msg">
+ <input pattern="^[1]:(.*):(.*)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="$1"/>
+ </match>
+ </input>
+ <input pattern="^.*:(.*):(.*)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="$2"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="enter_id">
+ <input pattern="(.+)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_id.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ <nomatch>
+ <action function="play-file" data="voicemail/vm-enter_id.wav"/>
+ <action function="say" data="${VM-Key-Terminator}" method="pronounced" type="name_spelled"/>
+ </nomatch>
+ </input>
+ </macro>
+
+ <macro name="enter_pass">
+ <input pattern="(.+)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_pass.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ <nomatch>
+ <action function="play-file" data="voicemail/vm-enter_pass.wav"/>
+ <action function="say" data="${VM-Key-Terminator}" method="pronounced" type="name_spelled"/>
+ </nomatch>
+
+ </input>
+ </macro>
+
+ <macro name="fail_auth">
+ <input>
+ <match>
+ <action function="play-file" data="voicemail/vm-fail_auth.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="hello">
+ <input>
+ <match>
+ <!--<action function="play-file" data="voicemail/vm-hello.wav"/> -->
+ </match>
+ </input>
+ </macro>
+
+ <macro name="goodbye">
+ <input>
+ <match>
+ <action function="play-file" data="voicemail/vm-goodbye.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="abort">
+ <input>
+ <match>
+ <action function="play-file" data="voicemail/vm-abort.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="message_count">
+ <input field="${VM-Total-New-Urgent-Messages}" pattern="^(0)$">
+ <nomatch>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="${VM-Total-New-Urgent-Messages}" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-urgent-new.wav"/>
+ <action function="phrase" phrase="plural_msg@voicemail_ivr" data="${VM-Total-New-Urgent-Messages}:voicemail/vm-message.wav:voicemail/vm-messages.wav"/>
+ </nomatch>
+ </input>
+ <input field="${VM-Total-New-Messages}" pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="${VM-Total-New-Messages}" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-new.wav"/>
+ <action function="phrase" phrase="plural_msg@voicemail_ivr" data="${VM-Total-New-Messages}:voicemail/vm-message.wav:voicemail/vm-messages.wav"/>
+ </match>
+ </input>
+ <input field="${VM-Total-Saved-Messages}" pattern="^(0)$">
+ <nomatch>
+ <action function="play-file" data="currency/and.wav"/>
+ <action function="say" data="${VM-Total-Saved-Messages}" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-saved.wav"/>
+ <action function="phrase" phrase="plural_msg@voicemail_ivr" data="${VM-Total-Saved-Messages}:voicemail/vm-message.wav:voicemail/vm-messages.wav"/>
+ </nomatch>
+ </input>
+ </macro>
+
+ <macro name="menu">
+ <input field="${VM-Total-New-Messages}" pattern="^(0)$">
+ <nomatch>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Play-New-Messages}:voicemail/vm-listen_new.wav"/>
+ </nomatch>
+ </input>
+ <input field="${VM-Total-Saved-Messages}" pattern="^(0)$">
+ <nomatch>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Play-Saved-Messages}:voicemail/vm-listen_saved.wav"/>
+ </nomatch>
+ </input>
+ <input>
+ <match>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Config-Menu}:voicemail/vm-advanced.wav"/>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Terminator}:voicemail/vm-to_exit.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="config_menu">
+ <input>
+ <match>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Record-Greeting}:voicemail/vm-to_record_greeting.wav"/>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Choose-Greeting}:voicemail/vm-choose_greeting.wav"/>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Record-Name}:voicemail/vm-record_name2.wav"/>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Change-Password}:voicemail/vm-change_password.wav"/>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Main-Menu}:voicemail/vm-main_menu.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="record_name">
+ <input>
+ <match>
+ <action function="play-file" data="voicemail/vm-record_name1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="forward_ask_prepend">
+ <input>
+ <match>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Prepend}:voicemail/vm-forward_add_intro.wav"/>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Forward}:voicemail/vm-send_message_now.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="forward_ask_extension">
+ <input>
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_enter_ext.wav"/>
+ <!--<action function="phrase" phrase="play-file" data="voicemail/vm-followed_by.wav"/>-->
+ <!--<action function="say" data="${VM-Key-Terminate}" method="pronounced" type="name_spelled"/>-->
+ </match>
+ </input>
+ </macro>
+
+ <macro name="record_file_check">
+ <input>
+ <match>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Listen-File}:voicemail/vm-listen_to_recording.wav"/>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Save-File}:voicemail/vm-save_recording.wav"/>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Record-File}:voicemail/vm-rerecord.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="record_urgent_check">
+ <input>
+ <match>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Urgent}:voicemail/vm-mark-urgent.wav"/>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Terminator}:voicemail/vm-continue.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="forward_prepend">
+ <input>
+ <match>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Prepend}:voicemail/vm-forward_add_intro.wav"/>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Forward}:voicemail/vm-send_message_now.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_enter_ext.wav"/>
+ <action function="play-file" data="voicemail/vm-followed_by.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="invalid_extension">
+ <input>
+ <match>
+ <action function="play-file" data="voicemail/vm-that_was_an_invalid_ext.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="listen_file_check">
+ <input>
+ <match>
+ <!--<action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Main-Next-Msg}:voicemail/vm-for_next_msg.wav"/>--> <!-- Not existant in callie recordings -->
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Main-Listen-File}:voicemail/vm-listen_to_recording.wav"/>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Main-Save-File}:voicemail/vm-save_recording.wav"/>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Main-Delete-File}:voicemail/vm-delete_recording.wav"/>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Main-Forward}:voicemail/vm-to_forward.wav"/>
+ </match>
+ </input>
+ <input field="${VM-Message-Email}" pattern="^$">
+ <nomatch>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Main-Email}:voicemail/vm-forward_to_email.wav"/>
+ </nomatch>
+ </input>
+ <input>
+ <match>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Main-Callback}:voicemail/vm-return_call.wav"/>
+ <action function="phrase" phrase="press_key@voicemail_ivr" data="${VM-Key-Main-Forward}:voicemail/vm-to_forward.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="choose_greeting">
+ <input>
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_choose.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="choose_greeting_fail">
+ <input>
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_fail.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="record_greeting">
+ <input>
+ <match>
+ <action function="play-file" data="voicemail/vm-record_greeting.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="record_message">
+ <input>
+ <match>
+ <action function="play-file" data="voicemail/vm-record_message.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="greeting_selected">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="${VM-Preference-Greeting-File-Path}"/>
+ <action function="play-file" data="voicemail/vm-greeting.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-selected.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-person.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-not_available.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="say_number">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="say_message_number">
+ <input>
+ <match>
+ <action function="play-file" data="voicemail/vm-${VM-Message-Type}.wav"/>
+ <action function="play-file" data="voicemail/vm-message_number.wav"/>
+ <action function="say" data="${VM-Message-Number}" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <!-- Note: Update this to marked-urgent,emailed and saved once new sound files are recorded -->
+ <macro name="ack">
+ <input pattern="^(too-small)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-too-small.wav"/>
+ </match>
+ </input>
+ <input pattern="^(undeleted)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(deleted)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(saved)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(emailed)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(marked-urgent)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="short_date_time"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="say_date_event">
+ <input>
+ <match>
+ <action function="say" data="${VM-Message-Received-Epoch}" method="pronounced" type="short_date_time"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="play_message">
+ <input>
+ <match>
+ <action function="play-file" data="${VM-Message-File-Path}"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="play_recording">
+ <input>
+ <match>
+ <action function="play-file" data="${VM-Record-File-Path}"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="disk_quota_exceeded">
+ <input>
+ <match>
+ <action function="play-file" data="voicemail/vm-mailbox_full.wav"/>
+ </match>
+ </input>
+ </macro>
+ </macros>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="msgcount">
+ <input pattern="(.*)">
+ <match>
+ <action function="execute" data="sleep(1000)"/>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ <!-- or -->
+ <!--<action function="speak-text" data="Usted tiene $1 mensajes"/>-->
+ </match>
+ </input>
+ </macro>
+ <macro name="saydate">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="timespec">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="time_measurement"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="ip-addr">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="iterated" type="ip_address"/>
+ <action function="say" data="$1" method="pronounced" type="ip_address"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell-phonetic">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="tts-timeleft">
+ <!-- The parser will visit each <input> tag and execute the actions in <match> or <nomatch> depending on the pattern param -->
+ <!-- If the function "break" is encountered all parsing will cease -->
+ <input pattern="(\d+):(\d+)">
+ <match>
+ <action function="speak-text" data="Quedan $1 minutos y $2 segundos $strftime(%Y-%m-%d)"/>
+ <action function="break"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="Esa fue una entrada inválida."/>
+ </nomatch>
+ </input>
+ <input pattern="(\d+) min (\d+) sec">
+ <match>
+ <action function="speak-text" data="Quedan $1 minutos y $2 segundos $strftime(%Y-%m-%d)"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="Esa fue una entrada inválida."/>
+ </nomatch>
+ </input>
+ </macro>
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="msgcount">
+ <input pattern="(.*)">
+ <match>
+ <action function="execute" data="sleep(1000)"/>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ <!-- or -->
+ <!--<action function="speak-text" data="Usted tiene $1 mensajes"/>-->
+ </match>
+ </input>
+ </macro>
+ <macro name="saydate">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="timespec">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="time_measurement"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="ip-addr">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="iterated" type="ip_address"/>
+ <action function="say" data="$1" method="pronounced" type="ip_address"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell-phonetic">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="tts-timeleft">
+ <!-- The parser will visit each <input> tag and execute the actions in <match> or <nomatch> depending on the pattern param -->
+ <!-- If the function "break" is encountered all parsing will cease -->
+ <input pattern="(\d+):(\d+)">
+ <match>
+ <action function="speak-text" data="Quedan $1 minutos y $2 segundos $strftime(%Y-%m-%d)"/>
+ <action function="break"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="Esa fue una entrada inválida."/>
+ </nomatch>
+ </input>
+ <input pattern="(\d+) min (\d+) sec">
+ <match>
+ <action function="speak-text" data="Quedan $1 minutos y $2 segundos $strftime(%Y-%m-%d)"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="Esa fue una entrada inválida."/>
+ </nomatch>
+ </input>
+ </macro>
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+
+
+ <macro name="demo_ivr_count">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="demo_ivr_main_menu" pause="100"> <!-- See conf/autoload_config/ivr.conf.xml for an example on how to use this macro in an IVR -->
+ <input pattern="(.*)">
+ <match>
+ <!-- string together several existing sound files to create one long greeting -->
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="ivr/ivr-this_ivr_will_let_you_test_features.wav"/>
+ <action function="play-file" data="ivr/ivr-you_may_exit_by_hanging_up.wav"/>
+ <!-- note that you can do more than just play files, e.g. have pauses and do TTS -->
+
+ <!-- Menu option 1: Call FreeSWITCH conference-->
+ <action function="play-file" data="ivr/ivr-enter_ext_pound.wav"/>
+ <action function="play-file" data="silence_stream://1500"/>
+ <action function="play-file" data="ivr/ivr-to_call_the_freeswitch_conference.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: Do FreeSWITCH echo test -->
+ <action function="play-file" data="ivr/ivr-to_do_a_freeswitch_echo_test.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: Listen to Music on Hold -->
+ <action function="play-file" data="ivr/ivr-to_listen_to_moh.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: Register for ClueCon -->
+ <action function="play-file" data="ivr/ivr-register_for_cluecon.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 5: Listen to screaming monkeys -->
+ <action function="play-file" data="ivr/ivr-to_hear_screaming_monkeys.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/5.wav"/>
+
+ <!-- Menu option 6: Hear a sample submenu -->
+ <action function="play-file" data="ivr/ivr-to_hear_sample_submenu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/6.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as demo_ivr_main_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="demo_ivr_main_menu_short" pause="100">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option 1: Call FreeSWITCH conference-->
+ <action function="play-file" data="silence_stream://1000"/>
+ <action function="play-file" data="ivr/ivr-to_call_the_freeswitch_conference.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: Do FreeSWITCH echo test -->
+ <action function="play-file" data="ivr/ivr-to_do_a_freeswitch_echo_test.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: Listen to Music on Hold -->
+ <action function="play-file" data="ivr/ivr-to_listen_to_moh.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: Hear a sample submenu -->
+ <action function="play-file" data="ivr/ivr-to_hear_sample_submenu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 5: Listen to screaming monkeys -->
+ <action function="play-file" data="ivr/ivr-to_hear_screaming_monkeys.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/5.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the "long" greeting for the demo_ivr_sub_menu -->
+ <macro name="demo_ivr_sub_menu">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="ivr/ivr-sample_submenu.wav"/>
+
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as demo_ivr_sub_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="demo_ivr_sub_menu_short">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+
+
+ <macro name="demo_ivr_count">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="demo_ivr_main_menu" pause="100"> <!-- See conf/autoload_config/ivr.conf.xml for an example on how to use this macro in an IVR -->
+ <input pattern="(.*)">
+ <match>
+ <!-- string together several existing sound files to create one long greeting -->
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="ivr/ivr-this_ivr_will_let_you_test_features.wav"/>
+ <action function="play-file" data="ivr/ivr-you_may_exit_by_hanging_up.wav"/>
+ <!-- note that you can do more than just play files, e.g. have pauses and do TTS -->
+
+ <!-- Menu option 1: Call FreeSWITCH conference-->
+ <action function="play-file" data="ivr/ivr-enter_ext_pound.wav"/>
+ <action function="play-file" data="silence_stream://1500"/>
+ <action function="play-file" data="ivr/ivr-to_call_the_freeswitch_conference.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: Do FreeSWITCH echo test -->
+ <action function="play-file" data="ivr/ivr-to_do_a_freeswitch_echo_test.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: Listen to Music on Hold -->
+ <action function="play-file" data="ivr/ivr-to_listen_to_moh.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: Register for ClueCon -->
+ <action function="play-file" data="ivr/ivr-register_for_cluecon.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 5: Listen to screaming monkeys -->
+ <action function="play-file" data="ivr/ivr-to_hear_screaming_monkeys.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/5.wav"/>
+
+ <!-- Menu option 6: Hear a sample submenu -->
+ <action function="play-file" data="ivr/ivr-to_hear_sample_submenu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/6.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as demo_ivr_main_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="demo_ivr_main_menu_short" pause="100">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option 1: Call FreeSWITCH conference-->
+ <action function="play-file" data="silence_stream://1000"/>
+ <action function="play-file" data="ivr/ivr-to_call_the_freeswitch_conference.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: Do FreeSWITCH echo test -->
+ <action function="play-file" data="ivr/ivr-to_do_a_freeswitch_echo_test.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: Listen to Music on Hold -->
+ <action function="play-file" data="ivr/ivr-to_listen_to_moh.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: Hear a sample submenu -->
+ <action function="play-file" data="ivr/ivr-to_hear_sample_submenu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 5: Listen to screaming monkeys -->
+ <action function="play-file" data="ivr/ivr-to_hear_screaming_monkeys.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/5.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the "long" greeting for the demo_ivr_sub_menu -->
+ <macro name="demo_ivr_sub_menu">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="ivr/ivr-sample_submenu.wav"/>
+
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as demo_ivr_sub_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="demo_ivr_sub_menu_short">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter_person.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter_person.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-specify_mininum.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-letters_of_person_name.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="play-file" data="directory/dir-no_matching_results.wav"/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-result_match.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-too_many_result.wav"/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-no_more_results.wav"/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-result_number.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-at_extension.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="play-file" data="directory/dir-to_select_entry.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_next.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_prev.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-start_new_search.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter_person.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter_person.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-specify_mininum.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-letters_of_person_name.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="play-file" data="directory/dir-no_matching_results.wav"/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-result_match.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-too_many_result.wav"/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-no_more_results.wav"/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-result_number.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-at_extension.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="play-file" data="directory/dir-to_select_entry.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_next.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_prev.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-start_new_search.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Introduzca las primeras letras del apellido de la persona."/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Introduzca las primeras letras del nombre de la persona."/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Para buscar por apellido, pulse $2"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Para buscar por nombre, pulse $2"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Necesita especificar un mínimo de $1 letras del nombre buscado. Por favor intente de nuevo."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="speak-text" data="No hay resultados que coincidan con su busqueda. Por favor intente de nuevo."/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 resultados coinciden con su busqueda."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Su busqueda genera demasiados resultados. Por favor intente de nuevo."/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="No hay más resultados."/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Resultado número $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="speak-text" data="Para seleccionar este resultado pulse $1, para el resultado siguiente pulse $2, para el resultado previo pulse $3, para empezar una nueva busqueda pulse $4"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="en la extensión $1"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Ingrese las primeras letras del apellido de la persona."/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Ingrese las primeras letras del nombre de la persona."/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Para buscar por apellido, pulse $2"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Para buscar por nombre, pulse $2"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Necesita especificar un mínimo de $1 letras del nombre buscado. Por favor intente de nuevo."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="speak-text" data="No hay resultados que coincidan con su busqueda. Por favor intente de nuevo."/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 resultados coinciden con su busqueda."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Su busqueda genera demasiados resultados. Por favor intente de nuevo."/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="No hay más resultados."/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Resultado número $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="speak-text" data="Para seleccionar este resultado pulse $1, para el resultado siguiente pulse $2, para el resultado previo pulse $3, para empezar una nueva busqueda pulse $4"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="en la extensión $1"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include>
+ <language name="es" sound-path="$${sounds_dir}/es/ES/cristina" tts-engine="cepstral" tts-voice="marta">
+ <X-PRE-PROCESS cmd="include" data="demo/*-es-ES.xml"/> <!-- Note: this now grabs whole subdir, previously grabbed only demo.xml -->
+ <!--voicemail_es_ES_tts is purely implemented with tts, we have the files based one that is the default. -->
+ <X-PRE-PROCESS cmd="include" data="vm/sounds-es-ES.xml"/> <!-- vm/tts.xml if you want to use tts and have cepstral -->
+ <X-PRE-PROCESS cmd="include" data="dir/sounds-es-ES.xml"/> <!-- dir/tts.xml if you want to use tts and have cepstral -->
+ </language>
+</include>
--- /dev/null
+<include>
+ <language name="es" sound-path="$${sounds_dir}/es/mx/maria" tts-engine="cepstral" tts-voice="marta">
+ <X-PRE-PROCESS cmd="include" data="demo/*-es-MX.xml"/> <!-- Note: this now grabs whole subdir, previously grabbed only demo.xml -->
+ <!--voicemail_es_MX_tts is purely implemented with tts, we have the files based one that is the default. -->
+ <X-PRE-PROCESS cmd="include" data="vm/sounds-es-MX.xml"/> <!-- vm/tts.xml if you want to use tts and have cepstral -->
+ <X-PRE-PROCESS cmd="include" data="dir/sounds-es-MX.xml"/> <!-- dir/tts.xml if you want to use tts and have cepstral -->
+ </language>
+</include>
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_id.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_pass.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-fail_auth.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <!--<action function="play-file" data="voicemail/vm-hello.wav"/> -->
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-goodbye.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-abort.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^(1):(.*)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="play-file" data="digits/un.wav"/>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$2.wav"/>
+ </match>
+ </input>
+ <input pattern="^(\d+):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ <action function="play-file" data="voicemail/vm-$2_s.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <!-- To listen to new messages -->
+ <action function="play-file" data="voicemail/vm-listen_new.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To listen to saved messages -->
+ <action function="play-file" data="voicemail/vm-listen_saved.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- For advanced options -->
+ <action function="play-file" data="voicemail/vm-advanced.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To exit -->
+ <action function="play-file" data="voicemail/vm-to_exit.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <!-- To record a greeting -->
+ <action function="play-file" data="voicemail/vm-to_record_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To choose greeting -->
+ <action function="play-file" data="voicemail/vm-choose_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To record your name -->
+ <action function="play-file" data="voicemail/vm-record_name2.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To change password -->
+ <action function="play-file" data="voicemail/vm-change_password.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To return to main menu -->
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_name1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-rerecord.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mark-urgent.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-continue.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_prepend">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_add_intro.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-send_message_now.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_enter_ext.wav"/>
+ <action function="play-file" data="voicemail/vm-followed_by.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_invalid_extension">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-that_was_an_invalid_ext.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-forward_to_email.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_choose.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_fail.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_greeting.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_message.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-greeting.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-selected.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-person.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-not_available.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ <action function="play-file" data="voicemail/vm-message_number.wav"/>
+ <action function="say" data="$2" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <!-- Note: Update this to marked-urgent,emailed and saved once new sound files are recorded -->
+ <macro name="voicemail_ack">
+ <input pattern="^(too-small)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-too-small.wav"/>
+ </match>
+ </input>
+ <input pattern="^(deleted)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(saved)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(emailed)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(marked-urgent)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_disk_quota_exceeded">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mailbox_full.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_announce_ext">
+ <input pattern="^([^\:]+):(.*)$">
+ <match>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_lot_full">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_lot_empty">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/>
+ </match>
+ </input>
+ </macro>
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_id.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_pass.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-fail_auth.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <!--<action function="play-file" data="voicemail/vm-hello.wav"/> -->
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-goodbye.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-abort.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^(1):(.*)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="play-file" data="digits/un.wav"/>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$2.wav"/>
+ </match>
+ </input>
+ <input pattern="^(\d+):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ <action function="play-file" data="voicemail/vm-$2.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <!-- To listen to new messages -->
+ <action function="play-file" data="voicemail/vm-listen_new.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To listen to saved messages -->
+ <action function="play-file" data="voicemail/vm-listen_saved.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- For advanced options -->
+ <action function="play-file" data="voicemail/vm-advanced.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To exit -->
+ <action function="play-file" data="voicemail/vm-to_exit.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <!-- To record a greeting -->
+ <action function="play-file" data="voicemail/vm-to_record_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To choose greeting -->
+ <action function="play-file" data="voicemail/vm-choose_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To record your name -->
+ <action function="play-file" data="voicemail/vm-record_name2.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To change password -->
+ <action function="play-file" data="voicemail/vm-change_password.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To return to main menu -->
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_name1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-rerecord.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mark-urgent.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-continue.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_prepend">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_add_intro.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-send_message_now.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_enter_ext.wav"/>
+ <action function="play-file" data="voicemail/vm-followed_by.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_invalid_extension">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-that_was_an_invalid_ext.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-forward_to_email.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_choose.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_fail.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_greeting.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_message.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-greeting.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-selected.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-person.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-not_available.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ <action function="play-file" data="voicemail/vm-message_number.wav"/>
+ <action function="say" data="$2" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <!-- Note: Update this to marked-urgent,emailed and saved once new sound files are recorded -->
+ <macro name="voicemail_ack">
+ <input pattern="^(too-small)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-too-small.wav"/>
+ </match>
+ </input>
+ <input pattern="^(deleted)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(saved)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(emailed)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(marked-urgent)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_disk_quota_exceeded">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mailbox_full.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_announce_ext">
+ <input pattern="^([^\:]+):(.*)$">
+ <match>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_lot_full">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_lot_empty">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/>
+ </match>
+ </input>
+ </macro>
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Por favor introduzca su número de usuario, seguido por $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Por favor introduzca su contraseña, seguido por $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Inicio de sesión incorrecto."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Bienvenido a su buzón de voz."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Adiós."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Demasiados intentos fallidos."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^1:(.*)$" break_on_match="true">
+ <match>
+ <action function="speak-text" data="Usted tiene 1 $1 mensaje en la bandeja de entrada ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ <input pattern="^(\d+):(.*)$">
+ <match>
+ <action function="speak-text" data="Usted tiene $1 $2 mensajes en la bandeja de entrada ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para escuchar mensajes nuevos, pulse $1. Para escuchar mensajes guardados, pulse $2. Para opciones avanzadas, pulse $3. Para salir, pulse $4."/>
+ </match>
+ </input>
+ </macro>
+
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para grabar un saludo, pulse $1. Para elegir un saludo, pulse $2. Para grabar su nombre, pulse $3. Para cambiar su contraseña, pulse $5. Para el menú principal, pulse $5."/>
+ </match>
+ </input>
+ </macro>
+
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Por favor grabe su nombre después de la señal, pulse cualquier tecla o deje de hablar para poner fin a la grabación."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para escuchar la grabación, pulse $1. Para guardar la grabación, pulse $2. Para regrabar, pulse $3."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para marcar este mensaje como urgente, pulse $1. Para continuar, pulse $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_invalid_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="$1 no es una extensión válida."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="Introduzca la extensión a la cual quiere enviar este mensaje, seguido de $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_prepend">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="Para grabar un saludo, pulse $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para escuchar la grabación, pulse $1. Para guardar la grabación, pulse $2. Para eliminar la grabación, pulse $3. Para enviar la grabación a su e-mail, pulse $4. Para devolver la llamada, pulse $5. Para enviar este mensaje a otra extensión, pulse $6."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Elija un saludo entre 1 y 9."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Valor inválido."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Grabe su saludo después de la señal, pulse cualquier tecla o deje de hablar para poner fin a la grabación."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Grabe su mensaje después de la señal, pulse cualquier tecla o deje de hablar para poner fin a la grabación."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Saludo $1 seleccionado."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 no está disponible."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(.*)$">
+ <match>
+ <action function="speak-text" data="$1 mensaje número $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_ack">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="mensaje $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="${strftime($1|%A, %B %d %Y, %I:%M %p)}"/>
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Por favor ingrese su número de usuario, seguido por $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Por favor ingrese su contraseña, seguido por $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Inicio de sesión incorrecto."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Bienvenido a su buzón de voz."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Adiós."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Demasiados intentos fallidos."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^1:(.*)$" break_on_match="true">
+ <match>
+ <action function="speak-text" data="Usted tiene 1 $1 mensaje en la bandeja de entrada ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ <input pattern="^(\d+):(.*)$">
+ <match>
+ <action function="speak-text" data="Usted tiene $1 $2 mensajes en la bandeja de entrada ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para escuchar mensajes nuevos, pulse $1. Para escuchar mensajes guardados, pulse $2. Para opciones avanzadas, pulse $3. Para salir, pulse $4."/>
+ </match>
+ </input>
+ </macro>
+
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para grabar un saludo, pulse $1. Para elegir un saludo, pulse $2. Para grabar su nombre, pulse $3. Para cambiar su contraseña, pulse $5. Para el menú principal, pulse $5."/>
+ </match>
+ </input>
+ </macro>
+
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Después del tono, por favor anote su nombre, pulse cualquier tecla o deje de hablar para poner fin a la grabación."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para escuchar la grabación, pulse $1. Para guardar la grabación, pulse $2. Para regrabar, pulse $3."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para marcar este mensaje como urgente, pulse $1. Para continuar, pulse $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_invalid_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="$1 no es una extensión válida."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="Introduzca la extensión a la cual quiere enviar este mensaje, seguido de $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_prepend">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="Para grabar un saludo, pulse $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para escuchar la grabación, pulse $1. Para guardar la grabación, pulse $2. Para eliminar la grabación, pulse $3. Para enviar la grabación a su e-mail, pulse $4. Para devolver la llamada, pulse $5. Para enviar este mensaje a otra extensión, pulse $6."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Elija un saludo entre 1 y 9."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Valor inválido."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Grabe su saludo después del tono, pulse cualquier tecla o deje de hablar para poner fin a la grabación."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Grabe su mensaje después del tono, pulse cualquier tecla o deje de hablar para poner fin a la grabación."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Saludo $1 seleccionado."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 no está disponible."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(.*)$">
+ <match>
+ <action function="speak-text" data="$1 mensaje número $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_ack">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="mensaje $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="${strftime($1|%A, %B %d %Y, %I:%M %p)}"/>
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include>
+ <macro name="msgcount">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="tuas.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="messages.wav"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="timeleft">
+ <input pattern="(\d+):(\d+)">
+ <match>
+ <action function="speak-text" data="il reste $1 minutes et $2 secondes"/>
+ </match>
+ </input>
+ </macro>
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter-person.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter-person.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-specify_mininum_first.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-letters_of_person_name.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="play-file" data="directory/dir-no_match_entry.wav"/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-result_match.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-to_many_result.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-no_more_result.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-result_number.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-at_extension.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="play-file" data="directory/dir-to_select_entry.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_next.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_prev.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-to_make_new_search.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Veuillez entrer les premières lettres du nom de famille"/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Veuillez entrer les premières lettres du prénom"/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="speak-text" data="pour chercher par prénom, tapez $2"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="speak-text" data="pour chercher par nom de famille, tapez $2"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(1)$">
+ <match>
+ <action function="speak-text" data="Vous devez entrer au minimum une lettre du nom de la personne, essayez encore"/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Vous devez entrer au minimum $1 lettres du nom de la personne, essayer encore"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="speak-text" data="Votre recherche n'a retournée aucun résultat, essayez encore"/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 résultats correspondent à votre recherche"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Votre recherche retourne trop de résultats, essayer encore"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Fin des résultats."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Résultat numéro $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="speak-text" data="Pour sélectionner ce nom, tapez $1, pour le nom suivant tapez $2, pour le nom précédent, tapez $3, pour faire une nouvelle recherche, tapez $4"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="au poste $1"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+ <language name="fr" say-module="fr" sound-prefix="$${sounds_dir}/fr/ca/june" tts-engine="cepstral" tts-voice="david">
+ <phrases>
+ <macros>
+ <X-PRE-PROCESS cmd="include" data="demo/demo.xml"/>
+ <!-- voicemail_fr_tts is purely implemented with tts, we need a files based implementation too -->
+ <X-PRE-PROCESS cmd="include" data="vm/sounds.xml"/>
+ <X-PRE-PROCESS cmd="include" data="dir/sounds.xml"/> <!-- dir/tts.xml if you want to use tts and have cepstral -->
+ </macros>
+ </phrases>
+ </language>
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Entrez votre Identification, suivi par $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Entrez votre code, suivi par $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Identification incorrecte."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Bienvenue sur votre répondeur."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Au revoir."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Trop de tentatives ont échouées."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^([^:]+):urgent-new">
+ <match>
+ <action function="speak-text" data="Vous avez $1 nouveaux messages urgents dans le répertoire ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ <input pattern="^([^:]+):new">
+ <match>
+ <action function="speak-text" data="Vous avez $1 nouveaux messages dans le répertoire ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ <input pattern="^([^:]+):saved">
+ <match>
+ <action function="speak-text" data="Vous avez $1 messages sauvegardés dans le répertoire ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Pour écouter les nouveaux messages, tapez $1, Pour écouter les messages enregistrés, tapez $2, Pour les options avancées, tapez $3, pour sortir, tapez $4."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="pour enregistrer un message d'accueil, tapez $1, Pour choisir votre message d'accueil, tapez $2, Pour enregistrer votre nom, tapez $3, Pour changer votre mot de passe, tapez $4, Pour retourner au menu principal, tapez $5."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="enregistrez votre nom après le bip, puis tapez une touche, ou arrêtez de parler pour arrêter l'enregistrement."/>
+
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Pour écouter l'enregistrement, tapez $1, pour sauvegarder l'enregistrement, tapez $2, Pour réenregistrer, tapez $3."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Pour indiquer que ce message est urgent, tapez $1, Pour continuer, tapez $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])(:(.*))?$">
+ <match>
+ <action function="speak-text"
+ data="Pour réécouter l'enregistrement à nouveau, tapez $1, Pour sauvegarder l'enregistrement, tapez $2, Pour supprimer l'enregistrement, tapez $3, pour transférer l'enregistrement à votre email $8, tapez $4, Pour appeler l'auteur du message, tapez $5, Pour transférer ce message à un autre numéro, tapez $6."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="choisissez un message d'accueil entre 1 et 3."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="valeur incorrecte."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="enregistrez votre message d'accueil après le bip, puis tapez une touche ou arrêtez de parler pour arrêter l'enregistrement."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="enregistrez votre message après le bip, puis tapez une touche ou arrêtez de parler pour arrêter l'enregistrement."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="message d'accueil numéro $1 sélectionné."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 n'est pas disponible."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^new:(.*)$">
+ <match>
+ <action function="speak-text" data="nouveau message numéro $1."/>
+ </match>
+ </input>
+ <input pattern="^saved:(.*)$">
+ <match>
+ <action function="speak-text" data="message sauvegardé numéro $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_ack">
+ <input pattern="^(too-small)$">
+ <match>
+ <action function="speak-text" data="message trop court"/>
+ </match>
+ </input>
+ <input pattern="^(deleted)$">
+ <match>
+ <action function="speak-text" data="message supprimé"/>
+ </match>
+ </input>
+ <input pattern="^(saved)$">
+ <match>
+ <action function="speak-text" data="message sauvegardé"/>
+ </match>
+ </input>
+ <input pattern="^(emailed)$">
+ <match>
+ <action function="speak-text" data="message envoyé"/>
+ </match>
+ </input>
+ <input pattern="^(marked-urgent)$">
+ <match>
+ <action function="speak-text" data="message marqué urgent"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="${strftime($1|%e/%m/%Y, %H heures %M)}"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="demo_ivr_count">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="demo_ivr_main_menu" pause="100"> <!-- See conf/autoload_config/ivr.conf.xml for an example on how to use this macro in an IVR -->
+ <input pattern="(.*)">
+ <match>
+ <!-- string together several existing sound files to create one long greeting -->
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="ivr/ivr-this_ivr_will_let_you_test_features.wav"/>
+ <action function="play-file" data="ivr/ivr-you_may_exit_by_hanging_up.wav"/>
+ <!-- note that you can do more than just play files, e.g. have pauses and do TTS -->
+
+ <!-- Menu option 1: Call FreeSWITCH conference-->
+ <action function="play-file" data="ivr/ivr-enter_ext_pound.wav"/>
+ <action function="play-file" data="silence_stream://1500"/>
+ <action function="play-file" data="ivr/ivr-to_call_the_freeswitch_conference.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: Do FreeSWITCH echo test -->
+ <action function="play-file" data="ivr/ivr-to_do_a_freeswitch_echo_test.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: Listen to Music on Hold -->
+ <action function="play-file" data="ivr/ivr-to_listen_to_moh.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: Register for ClueCon -->
+ <action function="play-file" data="ivr/ivr-register_for_cluecon.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 5: Listen to screaming monkeys -->
+ <action function="play-file" data="ivr/ivr-to_hear_screaming_monkeys.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/5.wav"/>
+
+ <!-- Menu option 6: Hear a sample submenu -->
+ <action function="play-file" data="ivr/ivr-to_hear_sample_submenu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/6.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as demo_ivr_main_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="demo_ivr_main_menu_short" pause="100">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option 1: Call FreeSWITCH conference-->
+ <action function="play-file" data="silence_stream://1000"/>
+ <action function="play-file" data="ivr/ivr-to_call_the_freeswitch_conference.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: Do FreeSWITCH echo test -->
+ <action function="play-file" data="ivr/ivr-to_do_a_freeswitch_echo_test.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: Listen to Music on Hold -->
+ <action function="play-file" data="ivr/ivr-to_listen_to_moh.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: Hear a sample submenu -->
+ <action function="play-file" data="ivr/ivr-to_hear_sample_submenu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 5: Listen to screaming monkeys -->
+ <action function="play-file" data="ivr/ivr-to_hear_screaming_monkeys.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/5.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the "long" greeting for the demo_ivr_sub_menu -->
+ <macro name="demo_ivr_sub_menu">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="ivr/ivr-sample_submenu.wav"/>
+
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as demo_ivr_sub_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="demo_ivr_sub_menu_short">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+ <macro name="msgcount">
+ <input pattern="(.*)">
+ <match>
+ <action function="execute" data="sleep(1000)"/>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ <!-- or -->
+ <!--<action function="speak-text" data="you have $1 messages"/>-->
+ </match>
+ </input>
+ </macro>
+ <macro name="saydate">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="timespec">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="time_measurement"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="ip-addr">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="iterated" type="ip_address"/>
+ <action function="say" data="$1" method="pronounced" type="ip_address"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell-phonetic">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="tts-timeleft">
+ <!-- The parser will visit each <input> tag and execute the actions in <match> or <nomatch> depending on the pattern param -->
+ <!-- If the function "break" is encountered all parsing will cease -->
+ <input pattern="(\d+):(\d+)">
+ <match>
+ <action function="speak-text" data="You have $1 minutes, $2 seconds remaining $strftime(%Y-%m-%d)"/>
+ <action function="break"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="That input was invalid."/>
+ </nomatch>
+ </input>
+ <input pattern="(\d+) min (\d+) sec">
+ <match>
+ <action function="speak-text" data="You have $1 minutes, $2 seconds remaining $strftime(%Y-%m-%d)"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="That input was invalid."/>
+ </nomatch>
+ </input>
+ </macro>
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter_person.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter_person.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-specify_mininum.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-letters_of_person_name.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="play-file" data="directory/dir-no_matching_results.wav"/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-result_match.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-too_many_result.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-no_more_results.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-result_number.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-at_extension.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="play-file" data="directory/dir-to_select_entry.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_next.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_prev.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-start_new_search.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+ <language name="he" sound-prefix="$${sounds_dir}/he/daniel" tts-engine="cepstral" tts-voice="daniel">
+ <phrases>
+ <macros>
+ <X-PRE-PROCESS cmd="include" data="demo/*.xml"/> <!-- Note: this now grabs whole subdir, previously grabbed only demo.xml -->
+ <X-PRE-PROCESS cmd="include" data="vm/sounds.xml"/>
+ <X-PRE-PROCESS cmd="include" data="dir/sounds.xml"/>
+ </macros>
+ </phrases>
+ </language>
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_id.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_pass.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-fail_auth.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <!--<action function="play-file" data="voicemail/vm-hello.wav"/> -->
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-goodbye.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-abort.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^(1):(.*)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-$2.wav"/>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ </match>
+ </input>
+ <input pattern="^(\d+):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-$2.wav"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <!-- To listen to new messages -->
+ <action function="play-file" data="voicemail/vm-listen_new.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To listen to saved messages -->
+ <action function="play-file" data="voicemail/vm-listen_saved.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- For advanced options -->
+ <action function="play-file" data="voicemail/vm-advanced.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To exit -->
+ <action function="play-file" data="voicemail/vm-to_exit.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <!-- To record a greeting -->
+ <action function="play-file" data="voicemail/vm-to_record_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To choose greeting -->
+ <action function="play-file" data="voicemail/vm-choose_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To record your name -->
+ <action function="play-file" data="voicemail/vm-record_name2.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To change password -->
+ <action function="play-file" data="voicemail/vm-change_password.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To return to main menu -->
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_name1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-rerecord.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mark-urgent.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-continue.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_prepend">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_add_intro.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-send_message_now.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_enter_ext.wav"/>
+ <action function="play-file" data="voicemail/vm-followed_by.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_invalid_extension">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-that_was_an_invalid_ext.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-forward_to_email.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_choose.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_fail.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_greeting.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_message.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-greeting.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-selected.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-person.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-not_available.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ <action function="play-file" data="voicemail/vm-message_number.wav"/>
+ <action function="say" data="$2" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <!-- Note: Update this to marked-urgent,emailed and saved once new sound files are recorded -->
+ <macro name="voicemail_ack">
+ <input pattern="^(too-small)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-too-small.wav"/>
+ </match>
+ </input>
+ <input pattern="^(deleted)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(saved)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(emailed)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(marked-urgent)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_disk_quota_exceeded">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mailbox_full.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_announce_ext">
+ <input pattern="^([^\:]+):(.*)$">
+ <match>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_lot_full">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_lot_empty">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+
+
+ <macro name="demo_ivr_count">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="demo_ivr_main_menu" pause="100"> <!-- See conf/autoload_config/ivr.conf.xml for an example on how to use this macro in an IVR -->
+ <input pattern="(.*)">
+ <match>
+ <!-- string together several existing sound files to create one long greeting -->
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="ivr/ivr-this_ivr_will_let_you_test_features.wav"/>
+ <action function="play-file" data="ivr/ivr-you_may_exit_by_hanging_up.wav"/>
+ <!-- note that you can do more than just play files, e.g. have pauses and do TTS -->
+
+ <!-- Menu option 1: Call FreeSWITCH conference-->
+ <action function="play-file" data="ivr/ivr-enter_ext_pound.wav"/>
+ <action function="play-file" data="silence_stream://1500"/>
+ <action function="play-file" data="ivr/ivr-to_call_the_freeswitch_conference.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: Do FreeSWITCH echo test -->
+ <action function="play-file" data="ivr/ivr-to_do_a_freeswitch_echo_test.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: Listen to Music on Hold -->
+ <action function="play-file" data="ivr/ivr-to_listen_to_moh.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: Register for ClueCon -->
+ <action function="play-file" data="ivr/ivr-register_for_cluecon.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 5: Listen to screaming monkeys -->
+ <action function="play-file" data="ivr/ivr-to_hear_screaming_monkeys.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/5.wav"/>
+
+ <!-- Menu option 6: Hear a sample submenu -->
+ <action function="play-file" data="ivr/ivr-to_hear_sample_submenu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/6.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as demo_ivr_main_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="demo_ivr_main_menu_short" pause="100">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option 1: Call FreeSWITCH conference-->
+ <action function="play-file" data="silence_stream://1000"/>
+ <action function="play-file" data="ivr/ivr-to_call_the_freeswitch_conference.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: Do FreeSWITCH echo test -->
+ <action function="play-file" data="ivr/ivr-to_do_a_freeswitch_echo_test.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: Listen to Music on Hold -->
+ <action function="play-file" data="ivr/ivr-to_listen_to_moh.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: Hear a sample submenu -->
+ <action function="play-file" data="ivr/ivr-to_hear_sample_submenu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 5: Listen to screaming monkeys -->
+ <action function="play-file" data="ivr/ivr-to_hear_screaming_monkeys.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/5.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the "long" greeting for the demo_ivr_sub_menu -->
+ <macro name="demo_ivr_sub_menu">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="ivr/ivr-sample_submenu.wav"/>
+
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as demo_ivr_sub_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="demo_ivr_sub_menu_short">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+
+
+ <macro name="demo_ivr_count">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="demo_ivr_main_menu" pause="100"> <!-- See conf/autoload_config/ivr.conf.xml for an example on how to use this macro in an IVR -->
+ <input pattern="(.*)">
+ <match>
+ <!-- string together several existing sound files to create one long greeting -->
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="ivr/ivr-this_ivr_will_let_you_test_features.wav"/>
+ <action function="play-file" data="ivr/ivr-you_may_exit_by_hanging_up.wav"/>
+ <!-- note that you can do more than just play files, e.g. have pauses and do TTS -->
+
+ <!-- Menu option 1: Call FreeSWITCH conference-->
+ <action function="play-file" data="ivr/ivr-enter_ext_pound.wav"/>
+ <action function="play-file" data="silence_stream://1500"/>
+ <action function="play-file" data="ivr/ivr-to_call_the_freeswitch_conference.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: Do FreeSWITCH echo test -->
+ <action function="play-file" data="ivr/ivr-to_do_a_freeswitch_echo_test.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: Listen to Music on Hold -->
+ <action function="play-file" data="ivr/ivr-to_listen_to_moh.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: Register for ClueCon -->
+ <action function="play-file" data="ivr/ivr-register_for_cluecon.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 5: Listen to screaming monkeys -->
+ <action function="play-file" data="ivr/ivr-to_hear_screaming_monkeys.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/5.wav"/>
+
+ <!-- Menu option 6: Hear a sample submenu -->
+ <action function="play-file" data="ivr/ivr-to_hear_sample_submenu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/6.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as demo_ivr_main_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="demo_ivr_main_menu_short" pause="100">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option 1: Call FreeSWITCH conference-->
+ <action function="play-file" data="silence_stream://1000"/>
+ <action function="play-file" data="ivr/ivr-to_call_the_freeswitch_conference.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: Do FreeSWITCH echo test -->
+ <action function="play-file" data="ivr/ivr-to_do_a_freeswitch_echo_test.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: Listen to Music on Hold -->
+ <action function="play-file" data="ivr/ivr-to_listen_to_moh.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: Hear a sample submenu -->
+ <action function="play-file" data="ivr/ivr-to_hear_sample_submenu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 5: Listen to screaming monkeys -->
+ <action function="play-file" data="ivr/ivr-to_hear_screaming_monkeys.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/5.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="play-file" data="silence_stream://2000"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the "long" greeting for the demo_ivr_sub_menu -->
+ <macro name="demo_ivr_sub_menu">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="ivr/ivr-sample_submenu.wav"/>
+
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as demo_ivr_sub_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="demo_ivr_sub_menu_short">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="msgcount">
+ <input pattern="(.*)">
+ <match>
+ <action function="execute" data="sleep(1000)"/>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ <!-- or -->
+ <!--<action function="speak-text" data="you have $1 messages"/>-->
+ </match>
+ </input>
+ </macro>
+ <macro name="saydate">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="timespec">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="time_measurement"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="ip-addr">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="iterated" type="ip_address"/>
+ <action function="say" data="$1" method="pronounced" type="ip_address"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell-phonetic">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="tts-timeleft">
+ <!-- The parser will visit each <input> tag and execute the actions in <match> or <nomatch> depending on the pattern param -->
+ <!-- If the function "break" is encountered all parsing will cease -->
+ <input pattern="(\d+):(\d+)">
+ <match>
+ <action function="speak-text" data="Faltam $1 minutos, $2 segundos $strftime(%Y-%m-%d)"/>
+ <action function="break"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="Essa opção não é valida."/>
+ </nomatch>
+ </input>
+ <input pattern="(\d+) min (\d+) sec">
+ <match>
+ <action function="speak-text" data="Faltam $1 minutos, $2 segundos $strftime(%Y-%m-%d)"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="Essa opção não é valida."/>
+ </nomatch>
+ </input>
+ </macro>
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="msgcount">
+ <input pattern="(.*)">
+ <match>
+ <action function="execute" data="sleep(1000)"/>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ <!-- or -->
+ <!--<action function="speak-text" data="Você tem $1 mensagens"/>-->
+ </match>
+ </input>
+ </macro>
+ <macro name="saydate">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="timespec">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="time_measurement"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="ip-addr">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="iterated" type="ip_address"/>
+ <action function="say" data="$1" method="pronounced" type="ip_address"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell-phonetic">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="tts-timeleft">
+ <!-- The parser will visit each <input> tag and execute the actions in <match> or <nomatch> depending on the pattern param -->
+ <!-- If the function "break" is encountered all parsing will cease -->
+ <input pattern="(\d+):(\d+)">
+ <match>
+ <action function="speak-text" data="Faltam $1 minutos, $2 segundos $strftime(%Y-%m-%d)"/>
+ <action function="break"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="Essa era uma entrada inválida."/>
+ </nomatch>
+ </input>
+ <input pattern="(\d+) min (\d+) sec">
+ <match>
+ <action function="speak-text" data="Faltam $1 minutos, $2 segundos $strftime(%Y-%m-%d)"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="Essa era uma entrada inválida."/>
+ </nomatch>
+ </input>
+ </macro>
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter_person.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter_person.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-specify_mininum.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-letters_of_person_name.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="play-file" data="directory/dir-no_matching_results.wav"/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-result_match.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-too_many_result.wav"/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-no_more_results.wav"/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-result_number.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-at_extension.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="play-file" data="directory/dir-to_select_entry.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_next.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_prev.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-start_new_search.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter_person.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter_person.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-specify_mininum.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-letters_of_person_name.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="play-file" data="directory/dir-no_matching_results.wav"/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-result_match.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-too_many_result.wav"/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-no_more_results.wav"/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-result_number.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-at_extension.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="play-file" data="directory/dir-to_select_entry.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_next.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_prev.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-start_new_search.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Por favor marque as primeiras letras do apelido da pessoa"/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Por favor marque as primeiras letras do nome da pessoa"/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Para buscar por primeiro nome, digite $2"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Para buscar por apelido, digite $2"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Você precisa dizer um mínimo de $1 letras do nome da pessoa, tente novamente."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="speak-text" data="Não foram encontrados resultados, tente novamente."/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 resultados encontrados para a sua busca."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="A sua busca devolveu muitos resultados, por favor tente novamente."/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Não há mais resultados."/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Resultado número $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="speak-text" data="Para escolher este resultado digite $1, Para o resultado seguinte digite $2, Para o resultado anterior digite $3, Para uma nova busca digite $4"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="No ramal $1"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Por favor introduza os primeiras letras do apelido da pessoa"/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Por favor introduza os primeiros dígitos do primeiro nome da pessoa"/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Para pesquisar por primeiro nome, marque $2"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Para pesquisar por apelido, marque $2"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Você necessita de especificar um mínimo de $1 letras do nome da pessoa, tente novamente."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="speak-text" data="Não foram encontrados resultados, tente novamente."/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 resultados encontrados para a sua pesquisa."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="A sua pesquisa devolveu demasiados resultados, por favor tente novamente."/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Não há mais resultados"/>
+ </match>
+ </input>
+
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Resultado número $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="speak-text" data="Para seleccionar este resultado marque $1, Para o resultado seguinte marque $2, Para o resultado anterior marque $3, Para iniciar uma nova pesquisa marque $4"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Na extensão $1"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include>
+ <language name="pt" sound-path="$${sounds_dir}/pt/BR/karina" tts-engine="cepstral" tts-voice="marta">
+ <X-PRE-PROCESS cmd="include" data="demo/*-pt-BR.xml"/> <!-- Note: this now grabs whole subdir, previously grabbed only demo.xml -->
+ <!--voicemail_pt_BR_tts is purely implemented with tts, we have the files based one that is the default. -->
+ <X-PRE-PROCESS cmd="include" data="vm/sounds-pt-BR.xml"/> <!-- vm/tts.xml if you want to use tts and have cepstral -->
+ <X-PRE-PROCESS cmd="include" data="dir/sounds-pt-BR.xml"/> <!-- dir/tts.xml if you want to use tts and have cepstral -->
+ </language>
+</include>
--- /dev/null
+<include>
+ <language name="pt" sound-path="$${sounds_dir}/pt/PT/karina" tts-engine="cepstral" tts-voice="marta">
+ <X-PRE-PROCESS cmd="include" data="demo/*-pt-PT.xml"/> <!-- Note: this now grabs whole subdir, previously grabbed only demo.xml -->
+ <!--voicemail_pt_PT_tts is purely implemented with tts, we have the files based one that is the default. -->
+ <X-PRE-PROCESS cmd="include" data="vm/sounds-pt-PT.xml"/> <!-- vm/tts.xml if you want to use tts and have cepstral -->
+ <X-PRE-PROCESS cmd="include" data="dir/sounds-pt-PT.xml"/> <!-- dir/tts.xml if you want to use tts and have cepstral -->
+ </language>
+</include>
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_id.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_pass.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-fail_auth.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <!--<action function="play-file" data="voicemail/vm-hello.wav"/> -->
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-goodbye.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-abort.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^(1):(.*)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="play-file" data="digits/uma.wav"/>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$2.wav"/>
+ </match>
+ </input>
+ <input pattern="^(\d+):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ <action function="play-file" data="voicemail/vm-$2_s.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <!-- To listen to new messages -->
+ <action function="play-file" data="voicemail/vm-listen_new.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To listen to saved messages -->
+ <action function="play-file" data="voicemail/vm-listen_saved.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- For advanced options -->
+ <action function="play-file" data="voicemail/vm-advanced.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To exit -->
+ <action function="play-file" data="voicemail/vm-to_exit.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <!-- To record a greeting -->
+ <action function="play-file" data="voicemail/vm-to_record_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To choose greeting -->
+ <action function="play-file" data="voicemail/vm-choose_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To record your name -->
+ <action function="play-file" data="voicemail/vm-record_name2.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To change password -->
+ <action function="play-file" data="voicemail/vm-change_password.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To return to main menu -->
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_name1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-rerecord.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mark-urgent.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-continue.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_prepend">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_add_intro.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-send_message_now.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_enter_ext.wav"/>
+ <action function="play-file" data="voicemail/vm-followed_by.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_invalid_extension">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-that_was_an_invalid_ext.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-forward_to_email.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_choose.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_fail.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_greeting.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_message.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-greeting.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-selected.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-person.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-not_available.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ <action function="play-file" data="voicemail/vm-message_number.wav"/>
+ <action function="say" data="$2" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <!-- Note: Update this to marked-urgent,emailed and saved once new sound files are recorded -->
+ <macro name="voicemail_ack">
+ <input pattern="^(too-small)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-too-small.wav"/>
+ </match>
+ </input>
+ <input pattern="^(deleted)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(saved)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(emailed)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(marked-urgent)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_disk_quota_exceeded">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mailbox_full.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_announce_ext">
+ <input pattern="^([^\:]+):(.*)$">
+ <match>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_lot_full">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_lot_empty">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/>
+ </match>
+ </input>
+ </macro>
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_id.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_pass.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-fail_auth.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <!--<action function="play-file" data="voicemail/vm-hello.wav"/> -->
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-goodbye.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-abort.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^(1):(.*)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="play-file" data="digits/uma.wav"/>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$2.wav"/>
+ </match>
+ </input>
+ <input pattern="^(\d+):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ <action function="play-file" data="voicemail/vm-$2_s.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <!-- To listen to new messages -->
+ <action function="play-file" data="voicemail/vm-listen_new.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To listen to saved messages -->
+ <action function="play-file" data="voicemail/vm-listen_saved.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- For advanced options -->
+ <action function="play-file" data="voicemail/vm-advanced.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To exit -->
+ <action function="play-file" data="voicemail/vm-to_exit.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <!-- To record a greeting -->
+ <action function="play-file" data="voicemail/vm-to_record_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To choose greeting -->
+ <action function="play-file" data="voicemail/vm-choose_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To record your name -->
+ <action function="play-file" data="voicemail/vm-record_name2.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To change password -->
+ <action function="play-file" data="voicemail/vm-change_password.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+
+ <!-- To return to main menu -->
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_name1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-rerecord.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mark-urgent.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-continue.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_prepend">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_add_intro.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-send_message_now.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_enter_ext.wav"/>
+ <action function="play-file" data="voicemail/vm-followed_by.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_invalid_extension">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-that_was_an_invalid_ext.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-forward_to_email.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_choose.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_fail.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_greeting.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_message.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-greeting.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-selected.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-person.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-not_available.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ <action function="play-file" data="voicemail/vm-message_number.wav"/>
+ <action function="say" data="$2" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <!-- Note: Update this to marked-urgent,emailed and saved once new sound files are recorded -->
+ <macro name="voicemail_ack">
+ <input pattern="^(too-small)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-too-small.wav"/>
+ </match>
+ </input>
+ <input pattern="^(deleted)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(saved)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(emailed)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(marked-urgent)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_disk_quota_exceeded">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mailbox_full.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_announce_ext">
+ <input pattern="^([^\:]+):(.*)$">
+ <match>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_lot_full">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="valet_lot_empty">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/>
+ </match>
+ </input>
+ </macro>
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Por favor digite o seu número de usuario, e depois $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Por favor digite a sua contrasenha, e depois $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Inicio incorreto da sessão."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Bem-vindo ao seu correio de voz."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Até logo."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Muitas tentativas fracassadas."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^1:(.*)$" break_on_match="true">
+ <match>
+ <action function="speak-text" data="Você tem 1 $1 mensagem no directório ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ <input pattern="^(\d+):(.*)$">
+ <match>
+ <action function="speak-text" data="Você tem $1 $2 mensagens no directório ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para ouvir as mensagens novas, digite $1, Para ouvir as mensagens armazenadas, digite $2, Para opções avançadas, digite $3, Para sair, digite $4."/>
+ </match>
+ </input>
+ </macro>
+
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para gravar a sua saudação, digite $1, Para escolher a sua saudação, digite $2, Para gravar o seu nome, digite $3, Para mudar a sua senha, digite $5, Para o menu principal, digite $5."/>
+ </match>
+ </input>
+ </macro>
+
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Após o sinal grave o seu nome, digite qualquer tecla ou deixe de falar para finalizar a gravação."/>
+
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para ouvir a gravação, digite $1, Para guardar a gravação, digite $2, Para gravar novamente, digite $3."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para marcar esta mensagem como urgente, digite $1, Para continuar, digite $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_invalid_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="$1 não é um ramal válido."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="Marque o ramal para o qual pretende encaminhar esta mensagem, e depois $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_prepend">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="Para gravar um anúncio, digite $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para ouvir a gravação novamente, press $1, Para guardar a gravação, press $2, Para borrar a gravação, press $3, Para enviar a gravação para o seu email, press $4, Para devolver a ligação agora, press $5, Para encaminhar esta mensagem para outro ramal, press $6."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Escolha uma saudação entre as opções 1 e 3."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Valor inválido."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Após o sinal grave a sua saudação, digite qualquer tecla ou deixe de falar para finalizar a gravação."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Após o sinal grave a sua mensagem, digite qualquer tecla ou deixe de falar para finalizar a gravação."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Saudação $1 selecionada."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 não está disponível."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(.*)$">
+ <match>
+ <action function="speak-text" data="$1 mensagem número $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_ack">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Mensagem $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="${strftime($1|%A, %B %d %Y, %I:%M %p)}"/>
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Por favor introduza o seu número de utilizador, seguido de $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Por favor introduza a sua palavra passe, seguida de $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Falha na autenticação."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Bem-vindo ao seu correio de voz."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Até breve."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="Demasiadas tentativas falhadas."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^1:(.*)$" break_on_match="true">
+ <match>
+ <action function="speak-text" data="Você tem 1 $1 mensagen no directório ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ <input pattern="^(\d+):(.*)$">
+ <match>
+ <action function="speak-text" data="Você tem $1 $2 mensagens no directório ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para ouvir as mensagens novas, marque $1, Para ouvir as mensagens guardadas, marque $2, Para opções avançadas, marque $3, Para sair, marque $4."/>
+ </match>
+ </input>
+ </macro>
+
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para gravar a sua saudação, marque $1, Para escolher a sua saudação, marque $2, Para gravar o seu nome, marque $3, Para alterar a sua palavra passe, marque $5, Para o menu principal, marque $5."/>
+ </match>
+ </input>
+ </macro>
+
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Após o sinal grave o seu nome, pressione qualquer tecla ou deixe de falar para parar a gravação."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para ouvir a gravação, marque $1, Para guardar a gravação, marque $2, Para gravar novamente, marque $3."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para marcar esta mensagem como urgente, marque $1, Para continuar, marque $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_invalid_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="$1 não é uma extensão válida."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="Introduza a extensão para a qual pretende encaminhar esta mensagem, seguido de $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_prepend">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="Para gravar um anúncio, marque $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="Para ouvir a gravação novamente, marque $1, Para guardar a gravação, marque $2, Para apagar a gravação, marque $3, Para encaminhar a gravação para o seu email, marque $4, Para devolver a chamada agora, marque $5, Para encaminhar esta mensagem para outra extensão, marque $6."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Seleccione uma saudação entre 1 e 3."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="valor inválido."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Após o sinal grave a sua saudação, pressione qualquer tecla ou deixe de falar para parar a gravação."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Após o sinal grave a sua mensagem, pressione qualquer tecla ou deixe de falar para parar a gravação."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Saudação $1 seleccionada."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 não está disponível."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(.*)$">
+ <match>
+ <action function="speak-text" data="$1 mensagem número $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_ack">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Mensagem $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="${strftime($1|%A, %B %d %Y, %I:%M %p)}"/>
+ </match>
+ </input>
+ </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->
--- /dev/null
+<include>
+
+ <macro name="demo_ivr_count">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="demo_ivr_main_menu" pause="250"> <!-- See conf/autoload_config/ivr.conf.xml for an example on how to use this macro in an IVR -->
+ <input pattern="(.*)">
+ <match>
+ <!-- string together several existing sound files to create one long greeting -->
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="ivr/ivr-this_ivr_will_let_you_test_features.wav"/>
+ <action function="play-file" data="ivr/ivr-you_may_exit_by_hanging_up.wav"/>
+ <!-- note that you can do more than just play files, e.g. have pauses and do TTS -->
+
+ <!-- Menu option 1: Call FreeSWITCH conference-->
+ <action function="play-file" data="ivr/ivr-enter_ext_pound.wav"/>
+ <action function="execute" data="sleep(1500)"/>
+ <action function="play-file" data="ivr/ivr-to_call_the_freeswitch_conference.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: Do FreeSWITCH echo test -->
+ <action function="play-file" data="ivr/ivr-to_do_a_freeswitch_echo_test.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: Listen to Music on Hold -->
+ <action function="play-file" data="ivr/ivr-to_listen_to_moh.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: Hear a sample submenu -->
+ <action function="play-file" data="ivr/ivr-to_hear_sample_submenu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 5: Listen to screaming monkeys -->
+ <action function="play-file" data="ivr/ivr-to_hear_screaming_monkeys.wav"/>
+
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/5.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="execute" data="sleep(2000)"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as demo_ivr_main_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="demo_ivr_main_menu_short" pause="250">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option 1: Call FreeSWITCH conference-->
+ <action function="execute" data="sleep(1000)"/>
+ <action function="play-file" data="ivr/ivr-to_call_the_freeswitch_conference.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/1.wav"/>
+
+ <!-- Menu option 2: Do FreeSWITCH echo test -->
+ <action function="play-file" data="ivr/ivr-to_do_a_freeswitch_echo_test.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/2.wav"/>
+
+ <!-- Menu option 3: Listen to Music on Hold -->
+ <action function="play-file" data="ivr/ivr-to_listen_to_moh.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/3.wav"/>
+
+ <!-- Menu option 4: Hear a sample submenu -->
+ <action function="play-file" data="ivr/ivr-to_hear_sample_submenu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/4.wav"/>
+
+ <!-- Menu option 5: Listen to screaming monkeys -->
+ <action function="play-file" data="ivr/ivr-to_hear_screaming_monkeys.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/5.wav"/>
+
+ <!-- Menu option 9: Repeat these options -->
+ <action function="play-file" data="ivr/ivr-to_repeat_these_options.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/9.wav"/>
+ <action function="execute" data="sleep(2000)"/>
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the "long" greeting for the demo_ivr_sub_menu -->
+ <macro name="demo_ivr_sub_menu">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="ivr/ivr-welcome_to_freeswitch.wav"/>
+ <action function="play-file" data="ivr/ivr-sample_submenu.wav"/>
+
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+ <!-- The following macro is the same as demo_ivr_sub_menu except it is the "short" version -->
+ <!-- The short version has all the options but not the initial greeting -->
+ <macro name="demo_ivr_sub_menu_short">
+ <input pattern="(.*)">
+ <match>
+ <!-- Menu option *: Return to top menu -->
+ <action function="play-file" data="ivr/ivr-to_return_to_previous_menu.wav"/>
+ <action function="play-file" data="ivr/ivr-please.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="play-file" data="digits/star.wav"/>
+
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+ <macro name="saymoney">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="currency"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="msgcount">
+ <input pattern="(.*)">
+ <match>
+ <action function="execute" data="sleep(1000)"/>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ <!-- or -->
+ <!--<action function="speak-text" data="you have $1 messages"/>-->
+ </match>
+ </input>
+ </macro>
+ <macro name="saydate">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="saydatetime">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="timespec">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="time_measurement"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="timespec2">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="CURRENT_DATE_TIME"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="ip-addr-interated">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="iterated" type="ip_address"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="ip-addr">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="ip_address"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="spell-phonetic">
+ <input pattern="(.*)">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="tts-timeleft">
+ <!-- The parser will visit each <input> tag and execute the actions in <match> or <nomatch> depending on the pattern param -->
+ <!-- If the function "break" is encountered all parsing will cease -->
+ <input pattern="(\d+):(\d+)">
+ <match>
+ <action function="speak-text" data="You have $1 minutes, $2 seconds remaining $strftime(%Y-%m-%d)"/>
+ <action function="break"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="That input was invalid."/>
+ </nomatch>
+ </input>
+ <input pattern="(\d+) min (\d+) sec">
+ <match>
+ <action function="speak-text" data="You have $1 minutes, $2 seconds remaining $strftime(%Y-%m-%d)"/>
+ </match>
+ <nomatch>
+ <action function="speak-text" data="That input was invalid."/>
+ </nomatch>
+ </input>
+ </macro>
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter-person.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-enter-person.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-first_name.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="play-file" data="directory/dir-to_search_by.wav"/>
+ <action function="play-file" data="directory/dir-last_name.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-specify_mininum_first.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-letters_of_person_name.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="play-file" data="directory/dir-no_match_entry.wav"/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="directory/dir-result_match.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-to_many_result.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-no_more_result.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-result_number.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="directory/dir-at_extension.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="play-file" data="directory/dir-to_select_entry.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_next.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-for_prev.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="directory/dir-to_make_new_search.wav"/>
+ <action function="play-file" data="directory/dir-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="directory_intro">
+ <input pattern="^(last_name)" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Please enter the first few digit of the person last name"/>
+ </match>
+ </input>
+ <input pattern="^(first_name)" break_on_match="false">
+ <match>
+ <action function="speak-text" data="Please enter the first few digit of the person first name"/>
+ </match>
+ </input>
+ <input pattern="^(last_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="speak-text" data="to search by first name, press $2"/>
+ </match>
+ </input>
+ <input pattern="^(first_name):([0-9#*])$" break_on_match="false">
+ <match>
+ <action function="speak-text" data="to search by last name, press $2"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_min_search_digits">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="You need to specify a minimum the first $1 letters of the person name, try again."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count">
+ <input pattern="^0$" break_on_match="true">
+ <match>
+ <action function="speak-text" data="Your search match no user on this system, try again."/>
+ </match>
+ </input>
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 result match your search"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_count_too_large">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Your search returned too many result, please try again"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_last">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="No more result"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_item">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="Result number $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_menu">
+ <input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
+ <match>
+ <action function="speak-text" data="To select this entry press $1, for the next entry press $2, for the previous entry press $3, to make a new search press $4"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="directory_result_at">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="at extension $1"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="directory_result_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<!--тестовые файлы Вы звуковые файлы можно взять тут svn co http://svn.freeswitch.ru/bbv/mod_say_ru/ru/ -->
+<include>
+ <language name="ru" sound-prefix="$${sounds_dir}/ru/RU/elena" tts-engine="cepstral" tts-voice="elena">
+ <phrases>
+ <macros>
+ <X-PRE-PROCESS cmd="include" data="demo/*.xml"/> <!-- Note: this now grabs whole subdir, previously grabbed only demo.xml -->
+ <!--voicemail_en_tts is purely implemented with tts, we have the files based one that is the default. -->
+ <X-PRE-PROCESS cmd="include" data="vm/sounds.xml"/> <!-- vm/tts.xml if you want to use tts and have cepstral -->
+ </macros>
+ </phrases>
+ </language>
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_id.wav"/>
+ <!--<action function="say" data="$1" method="pronounced" type="name_spelled"/>-->
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-enter_pass.wav"/>
+ <!-- лишнее и так долго <action function="say" data="$1" method="pronounced" type="name_spelled"/>-->
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-fail_auth.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-hello.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-goodbye.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="play-file" data="voicemail/vm-abort.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^(\d+[0,2-9]1|[2-9]1|1):(.*)$"> <!--1, и всё что больше 20-ти 21,31,41 .. -->
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="MESSAGES"/>
+ <action function="play-file" data="voicemail/vm-$2.wav"/>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <!--<action function="play-file" data="voicemail/vm-in_folder.wav"/>-->
+ </match>
+ </input>
+ <!-- от 10 до 19 и ноль --> <!-- от 5до 9 и больше 20-ти 25-29 -->
+ <input pattern="^(\d+1[0-9]|1[0-9]|0|\d+[0-9][0,5-9]|[2-9][0,5-9]|[0,5-9]):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="MESSAGES"/>
+ <action function="play-file" data="voicemail/vm-$2x.wav"/>
+ <action function="play-file" data="voicemail/vm-messagex.wav"/>
+ <!--<action function="play-file" data="voicemail/vm-in_folder.wav"/>-->
+ </match>
+ </input>
+ <input pattern="^(\d+[0,2-9][2-4]|[2-9][2-4]|[2-4]):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-you_have.wav"/>
+ <action function="say" data="$1" method="pronounced" type="MESSAGES"/>
+ <action function="play-file" data="voicemail/vm-$2x.wav"/>
+ <action function="play-file" data="voicemail/vm-messages.wav"/>
+ <action function="play-file" data="voicemail/vm-in_folder.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-listen_new.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-listen_saved.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-advanced.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_phonetic"/>
+ <action function="play-file" data="voicemail/vm-to_exit.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_record_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-choose_greeting.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-record_name2.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-change_password.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_name1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-rerecord.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-mark-urgent.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-continue.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_prepend">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_add_intro.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-send_message_now.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-forward_enter_ext.wav"/>
+ <action function="play-file" data="voicemail/vm-followed_by.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_invalid_extension">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-that_was_an_invalid_ext.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-forward_to_email.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-save_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-delete_recording.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-return_call.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-to_forward.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$6" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_choose.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-choose_greeting_fail.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_greeting.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-record_message.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-greeting.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-selected.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-person.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="play-file" data="voicemail/vm-not_available.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(\d+)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(\d+)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ <action function="play-file" data="voicemail/vm-message_number.wav"/>
+ <action function="say" data="$2" method="pronounced" type="items"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ </match>
+ </input>
+ </macro>
+ <!-- Note: Update this to marked-urgent,emailed and saved once new sound files are recorded -->
+ <macro name="voicemail_ack">
+ <input pattern="^(too-small)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-too-small.wav"/>
+ </match>
+ </input>
+ <input pattern="^(deleted)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(saved)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(emailed)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ <input pattern="^(marked-urgent)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-message.wav"/>
+ <action function="play-file" data="voicemail/vm-$1.wav"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="say" data="$1" method="pronounced" type="current_date_time"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+<include>
+
+ <macro name="voicemail_enter_id">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="please enter your i d, followed by $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_enter_pass">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="please enter your password, followed by $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_fail_auth">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="login incorrect."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_hello">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="welcome to your voicemail."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_goodbye">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="goodbye."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_abort">
+ <input pattern="(.*)">
+ <match>
+ <action function="speak-text" data="too many failed attempts."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_message_count">
+ <input pattern="^1:(.*)$" break_on_match="true">
+ <match>
+ <action function="speak-text" data="you have 1 $1 message in folder ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ <input pattern="^(\d+):(.*)$">
+ <match>
+ <action function="speak-text" data="you have $1 $2 messages in folder ${voicemail_current_folder}."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="To listen to new messages, press $1, To listen to saved messages, press $2, For advanced options, press $3, to exit, press $4."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_config_menu">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="To record a greeting, press $1, To choose a greeting, press $2, To record your name, press $3, to change your password, press $5, to return to the main menu, press $5."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="at the tone, please record your name, press any key or stop talking to end the recording."/>
+
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="To listen to the recording, press $1, To save the recording, press $2, To re record, press $3."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_urgent_check">
+ <input pattern="^([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="To mark this message urgent, press $1, To continue, press $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_invalid_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="$1 is not a valid extension."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_message_enter_extension">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="enter the extension you wish to forward to, then press $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_forward_prepend">
+ <input pattern="^([0-9#*])$">
+ <match>
+ <action function="speak-text" data="To record an announcement, press $1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_listen_file_check">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="speak-text"
+ data="To listen to the recording again, press $1, To save the recording, press $2, To delete the recording, press $3, to forward the recording to your email, press $4, to call the caller now, press $5, To forward this message to another extension, press $6."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="choose a greeting between 1 and 3."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_choose_greeting_fail">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="invalid value."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="record your greeting at the tone, press any key or stop talking to end the recording."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_record_message">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="record your message at the tone, press any key or stop talking to end the recording."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_greeting_selected">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="greeting $1 selected."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_play_greeting">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1 is not available."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_message_number">
+ <input pattern="^([a-z]+):(.*)$">
+ <match>
+ <action function="speak-text" data="$1 message number $2."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_phone_number">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_name">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="$1."/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_ack">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="message $1"/>
+ </match>
+ </input>
+ </macro>
+
+ <macro name="voicemail_say_date">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="speak-text" data="${strftime($1|%A, %B %d %Y, %I:%M %p)}"/>
+ </match>
+ </input>
+ </macro>
+
+</include>
+<!--
+For Emacs:
+Local Variables:
+mode:xml
+indent-tabs-mode:nil
+tab-width:2
+c-basic-offset:2
+End:
+For VIM:
+vim:set softtabstop=2 shiftwidth=2 tabstop=2 expandtab:
+-->
--- /dev/null
+# This is a comment. I love comments.
+
+# This file controls what Internet media types are sent to the client for
+# given file extension(s). Sending the correct media type to the client
+# is important so they know how to handle the content of the file.
+# Extra types can either be added here or by using an AddType directive
+# in your config files. For more information about Internet media types,
+# please read RFC 2045, 2046, 2047, 2048, and 2077. The Internet media type
+# registry is at <http://www.iana.org/assignments/media-types/>.
+
+# MIME type Extensions
+application/activemessage
+application/andrew-inset ez
+application/applefile
+application/atom+xml atom
+application/atomcat+xml atomcat
+application/atomicmail
+application/atomsvc+xml atomsvc
+application/auth-policy+xml
+application/batch-smtp
+application/beep+xml
+application/cals-1840
+application/ccxml+xml ccxml
+application/cellml+xml
+application/cnrp+xml
+application/commonground
+application/conference-info+xml
+application/cpl+xml
+application/csta+xml
+application/cstadata+xml
+application/cybercash
+application/davmount+xml davmount
+application/dca-rft
+application/dec-dx
+application/dialog-info+xml
+application/dicom
+application/dns
+application/dvcs
+application/ecmascript ecma
+application/edi-consent
+application/edi-x12
+application/edifact
+application/epp+xml
+application/eshop
+application/fastinfoset
+application/fastsoap
+application/fits
+application/font-tdpfr pfr
+application/h224
+application/http
+application/hyperstudio stk
+application/iges
+application/im-iscomposing+xml
+application/index
+application/index.cmd
+application/index.obj
+application/index.response
+application/index.vnd
+application/iotp
+application/ipp
+application/isup
+application/javascript js
+application/json json
+application/kpml-request+xml
+application/kpml-response+xml
+application/mac-binhex40 hqx
+application/mac-compactpro cpt
+application/macwriteii
+application/marc mrc
+application/mathematica ma nb mb
+application/mathml+xml mathml
+application/mbms-associated-procedure-description+xml
+application/mbms-deregister+xml
+application/mbms-envelope+xml
+application/mbms-msk+xml
+application/mbms-msk-response+xml
+application/mbms-protection-description+xml
+application/mbms-reception-report+xml
+application/mbms-register+xml
+application/mbms-register-response+xml
+application/mbms-user-service-description+xml
+application/mbox mbox
+application/mediaservercontrol+xml mscml
+application/mikey
+application/mp4 mp4s
+application/mpeg4-generic
+application/mpeg4-iod
+application/mpeg4-iod-xmt
+application/msword doc dot
+application/mxf mxf
+application/nasdata
+application/news-message-id
+application/news-transmission
+application/nss
+application/ocsp-request
+application/ocsp-response
+application/octet-stream bin dms lha lzh class so iso dmg dist distz pkg bpk dump elc
+application/oda oda
+application/oebps-package+xml
+application/ogg ogg
+application/parityfec
+application/pdf pdf
+application/pgp-encrypted pgp
+application/pgp-keys
+application/pgp-signature asc sig
+application/pics-rules prf
+application/pidf+xml
+application/pkcs10 p10
+application/pkcs7-mime p7m p7c
+application/pkcs7-signature p7s
+application/pkix-cert cer
+application/pkix-crl crl
+application/pkix-pkipath pkipath
+application/pkixcmp pki
+application/pls+xml pls
+application/poc-settings+xml
+application/postscript ai eps ps
+application/prs.alvestrand.titrax-sheet
+application/prs.cww cww
+application/prs.nprend
+application/prs.plucker
+application/qsig
+application/rdf+xml rdf
+application/reginfo+xml rif
+application/relax-ng-compact-syntax rnc
+application/remote-printing
+application/resource-lists+xml rl
+application/riscos
+application/rlmi+xml
+application/rls-services+xml rs
+application/rsd+xml rsd
+application/rss+xml rss
+application/rtf rtf
+application/rtx
+application/samlassertion+xml
+application/samlmetadata+xml
+application/sbml+xml sbml
+application/sdp sdp
+application/set-payment
+application/set-payment-initiation setpay
+application/set-registration
+application/set-registration-initiation setreg
+application/sgml
+application/sgml-open-catalog
+application/shf+xml shf
+application/sieve
+application/simple-filter+xml
+application/simple-message-summary
+application/simplesymbolcontainer
+application/slate
+application/smil
+application/smil+xml smi smil
+application/soap+fastinfoset
+application/soap+xml
+application/spirits-event+xml
+application/srgs gram
+application/srgs+xml grxml
+application/ssml+xml ssml
+application/timestamp-query
+application/timestamp-reply
+application/tve-trigger
+application/vemmi
+application/vividence.scriptfile
+application/vnd.3gpp.bsf+xml
+application/vnd.3gpp.pic-bw-large plb
+application/vnd.3gpp.pic-bw-small psb
+application/vnd.3gpp.pic-bw-var pvb
+application/vnd.3gpp.sms
+application/vnd.3gpp2.bcmcsinfo+xml
+application/vnd.3gpp2.sms
+application/vnd.3m.post-it-notes pwn
+application/vnd.accpac.simply.aso aso
+application/vnd.accpac.simply.imp imp
+application/vnd.acucobol acu
+application/vnd.acucorp atc acutc
+application/vnd.adobe.xdp+xml xdp
+application/vnd.adobe.xfdf xfdf
+application/vnd.aether.imp
+application/vnd.amiga.ami ami
+application/vnd.anser-web-certificate-issue-initiation cii
+application/vnd.anser-web-funds-transfer-initiation fti
+application/vnd.antix.game-component atx
+application/vnd.apple.installer+xml mpkg
+application/vnd.audiograph aep
+application/vnd.autopackage
+application/vnd.avistar+xml
+application/vnd.blueice.multipass mpm
+application/vnd.bmi bmi
+application/vnd.businessobjects rep
+application/vnd.cab-jscript
+application/vnd.canon-cpdl
+application/vnd.canon-lips
+application/vnd.cendio.thinlinc.clientconf
+application/vnd.chemdraw+xml cdxml
+application/vnd.chipnuts.karaoke-mmd mmd
+application/vnd.cinderella cdy
+application/vnd.cirpack.isdn-ext
+application/vnd.claymore cla
+application/vnd.clonk.c4group c4g c4d c4f c4p c4u
+application/vnd.commerce-battelle
+application/vnd.commonspace csp cst
+application/vnd.contact.cmsg cdbcmsg
+application/vnd.cosmocaller cmc
+application/vnd.crick.clicker clkx
+application/vnd.crick.clicker.keyboard clkk
+application/vnd.crick.clicker.palette clkp
+application/vnd.crick.clicker.template clkt
+application/vnd.crick.clicker.wordbank clkw
+application/vnd.criticaltools.wbs+xml wbs
+application/vnd.ctc-posml pml
+application/vnd.cups-pdf
+application/vnd.cups-postscript
+application/vnd.cups-ppd ppd
+application/vnd.cups-raster
+application/vnd.cups-raw
+application/vnd.curl curl
+application/vnd.cybank
+application/vnd.data-vision.rdz rdz
+application/vnd.denovo.fcselayout-link fe_launch
+application/vnd.dna dna
+application/vnd.dolby.mlp mlp
+application/vnd.dpgraph dpg
+application/vnd.dreamfactory dfac
+application/vnd.dvb.esgcontainer
+application/vnd.dvb.ipdcesgaccess
+application/vnd.dxr
+application/vnd.ecdis-update
+application/vnd.ecowin.chart mag
+application/vnd.ecowin.filerequest
+application/vnd.ecowin.fileupdate
+application/vnd.ecowin.series
+application/vnd.ecowin.seriesrequest
+application/vnd.ecowin.seriesupdate
+application/vnd.enliven nml
+application/vnd.epson.esf esf
+application/vnd.epson.msf msf
+application/vnd.epson.quickanime qam
+application/vnd.epson.salt slt
+application/vnd.epson.ssf ssf
+application/vnd.ericsson.quickcall
+application/vnd.eszigno3+xml es3 et3
+application/vnd.eudora.data
+application/vnd.ezpix-album ez2
+application/vnd.ezpix-package ez3
+application/vnd.fdf fdf
+application/vnd.ffsns
+application/vnd.fints
+application/vnd.flographit gph
+application/vnd.fluxtime.clip ftc
+application/vnd.framemaker fm frame maker
+application/vnd.frogans.fnc fnc
+application/vnd.frogans.ltf ltf
+application/vnd.fsc.weblaunch fsc
+application/vnd.fujitsu.oasys oas
+application/vnd.fujitsu.oasys2 oa2
+application/vnd.fujitsu.oasys3 oa3
+application/vnd.fujitsu.oasysgp fg5
+application/vnd.fujitsu.oasysprs bh2
+application/vnd.fujixerox.art-ex
+application/vnd.fujixerox.art4
+application/vnd.fujixerox.hbpl
+application/vnd.fujixerox.ddd ddd
+application/vnd.fujixerox.docuworks xdw
+application/vnd.fujixerox.docuworks.binder xbd
+application/vnd.fut-misnet
+application/vnd.fuzzysheet fzs
+application/vnd.genomatix.tuxedo txd
+application/vnd.google-earth.kml+xml kml
+application/vnd.google-earth.kmz kmz
+application/vnd.grafeq gqf gqs
+application/vnd.gridmp
+application/vnd.groove-account gac
+application/vnd.groove-help ghf
+application/vnd.groove-identity-message gim
+application/vnd.groove-injector grv
+application/vnd.groove-tool-message gtm
+application/vnd.groove-tool-template tpl
+application/vnd.groove-vcard vcg
+application/vnd.handheld-entertainment+xml zmm
+application/vnd.hbci hbci
+application/vnd.hcl-bireports
+application/vnd.hhe.lesson-player les
+application/vnd.hp-hpgl hpgl
+application/vnd.hp-hpid hpid
+application/vnd.hp-hps hps
+application/vnd.hp-jlyt jlt
+application/vnd.hp-pcl pcl
+application/vnd.hp-pclxl pclxl
+application/vnd.httphone
+application/vnd.hzn-3d-crossword x3d
+application/vnd.ibm.afplinedata
+application/vnd.ibm.electronic-media
+application/vnd.ibm.minipay mpy
+application/vnd.ibm.modcap afp listafp list3820
+application/vnd.ibm.rights-management irm
+application/vnd.ibm.secure-container sc
+application/vnd.igloader igl
+application/vnd.immervision-ivp ivp
+application/vnd.immervision-ivu ivu
+application/vnd.informedcontrol.rms+xml
+application/vnd.intercon.formnet xpw xpx
+application/vnd.intertrust.digibox
+application/vnd.intertrust.nncp
+application/vnd.intu.qbo qbo
+application/vnd.intu.qfx qfx
+application/vnd.ipunplugged.rcprofile rcprofile
+application/vnd.irepository.package+xml irp
+application/vnd.is-xpr xpr
+application/vnd.jam jam
+application/vnd.japannet-directory-service
+application/vnd.japannet-jpnstore-wakeup
+application/vnd.japannet-payment-wakeup
+application/vnd.japannet-registration
+application/vnd.japannet-registration-wakeup
+application/vnd.japannet-setstore-wakeup
+application/vnd.japannet-verification
+application/vnd.japannet-verification-wakeup
+application/vnd.jcp.javame.midlet-rms rms
+application/vnd.jisp jisp
+application/vnd.kahootz ktz ktr
+application/vnd.kde.karbon karbon
+application/vnd.kde.kchart chrt
+application/vnd.kde.kformula kfo
+application/vnd.kde.kivio flw
+application/vnd.kde.kontour kon
+application/vnd.kde.kpresenter kpr kpt
+application/vnd.kde.kspread ksp
+application/vnd.kde.kword kwd kwt
+application/vnd.kenameaapp htke
+application/vnd.kidspiration kia
+application/vnd.kinar kne knp
+application/vnd.koan skp skd skt skm
+application/vnd.liberty-request+xml
+application/vnd.llamagraphics.life-balance.desktop lbd
+application/vnd.llamagraphics.life-balance.exchange+xml lbe
+application/vnd.lotus-1-2-3 123
+application/vnd.lotus-approach apr
+application/vnd.lotus-freelance pre
+application/vnd.lotus-notes nsf
+application/vnd.lotus-organizer org
+application/vnd.lotus-screencam scm
+application/vnd.lotus-wordpro lwp
+application/vnd.macports.portpkg portpkg
+application/vnd.marlin.drm.actiontoken+xml
+application/vnd.marlin.drm.conftoken+xml
+application/vnd.marlin.drm.mdcf
+application/vnd.mcd mcd
+application/vnd.medcalcdata mc1
+application/vnd.mediastation.cdkey cdkey
+application/vnd.meridian-slingshot
+application/vnd.mfer mwf
+application/vnd.mfmp mfm
+application/vnd.micrografx.flo flo
+application/vnd.micrografx.igx igx
+application/vnd.mif mif
+application/vnd.minisoft-hp3000-save
+application/vnd.mitsubishi.misty-guard.trustweb
+application/vnd.mobius.daf daf
+application/vnd.mobius.dis dis
+application/vnd.mobius.mbk mbk
+application/vnd.mobius.mqy mqy
+application/vnd.mobius.msl msl
+application/vnd.mobius.plc plc
+application/vnd.mobius.txf txf
+application/vnd.mophun.application mpn
+application/vnd.mophun.certificate mpc
+application/vnd.motorola.flexsuite
+application/vnd.motorola.flexsuite.adsi
+application/vnd.motorola.flexsuite.fis
+application/vnd.motorola.flexsuite.gotap
+application/vnd.motorola.flexsuite.kmr
+application/vnd.motorola.flexsuite.ttc
+application/vnd.motorola.flexsuite.wem
+application/vnd.mozilla.xul+xml xul
+application/vnd.ms-artgalry cil
+application/vnd.ms-asf asf
+application/vnd.ms-cab-compressed cab
+application/vnd.ms-excel xls xlm xla xlc xlt xlw
+application/vnd.ms-fontobject eot
+application/vnd.ms-htmlhelp chm
+application/vnd.ms-ims ims
+application/vnd.ms-lrm lrm
+application/vnd.ms-playready.initiator+xml
+application/vnd.ms-powerpoint ppt pps pot
+application/vnd.ms-project mpp mpt
+application/vnd.ms-tnef
+application/vnd.ms-wmdrm.lic-chlg-req
+application/vnd.ms-wmdrm.lic-resp
+application/vnd.ms-wmdrm.meter-chlg-req
+application/vnd.ms-wmdrm.meter-resp
+application/vnd.ms-works wps wks wcm wdb
+application/vnd.ms-wpl wpl
+application/vnd.ms-xpsdocument xps
+application/vnd.mseq mseq
+application/vnd.msign
+application/vnd.music-niff
+application/vnd.musician mus
+application/vnd.ncd.control
+application/vnd.nervana
+application/vnd.netfpx
+application/vnd.neurolanguage.nlu nlu
+application/vnd.noblenet-directory nnd
+application/vnd.noblenet-sealer nns
+application/vnd.noblenet-web nnw
+application/vnd.nokia.catalogs
+application/vnd.nokia.conml+wbxml
+application/vnd.nokia.conml+xml
+application/vnd.nokia.isds-radio-presets
+application/vnd.nokia.iptv.config+xml
+application/vnd.nokia.landmark+wbxml
+application/vnd.nokia.landmark+xml
+application/vnd.nokia.landmarkcollection+xml
+application/vnd.nokia.n-gage.ac+xml
+application/vnd.nokia.n-gage.data ngdat
+application/vnd.nokia.n-gage.symbian.install n-gage
+application/vnd.nokia.ncd
+application/vnd.nokia.pcd+wbxml
+application/vnd.nokia.pcd+xml
+application/vnd.nokia.radio-preset rpst
+application/vnd.nokia.radio-presets rpss
+application/vnd.novadigm.edm edm
+application/vnd.novadigm.edx edx
+application/vnd.novadigm.ext ext
+application/vnd.oasis.opendocument.chart odc
+application/vnd.oasis.opendocument.chart-template otc
+application/vnd.oasis.opendocument.formula odf
+application/vnd.oasis.opendocument.formula-template otf
+application/vnd.oasis.opendocument.graphics odg
+application/vnd.oasis.opendocument.graphics-template otg
+application/vnd.oasis.opendocument.image odi
+application/vnd.oasis.opendocument.image-template oti
+application/vnd.oasis.opendocument.presentation odp
+application/vnd.oasis.opendocument.presentation-template otp
+application/vnd.oasis.opendocument.spreadsheet ods
+application/vnd.oasis.opendocument.spreadsheet-template ots
+application/vnd.oasis.opendocument.text odt
+application/vnd.oasis.opendocument.text-master otm
+application/vnd.oasis.opendocument.text-template ott
+application/vnd.oasis.opendocument.text-web oth
+application/vnd.obn
+application/vnd.olpc-sugar xo
+application/vnd.oma-scws-config
+application/vnd.oma-scws-http-request
+application/vnd.oma-scws-http-response
+application/vnd.oma.bcast.associated-procedure-parameter+xml
+application/vnd.oma.bcast.drm-trigger+xml
+application/vnd.oma.bcast.imd+xml
+application/vnd.oma.bcast.notification+xml
+application/vnd.oma.bcast.sgboot
+application/vnd.oma.bcast.sgdd+xml
+application/vnd.oma.bcast.sgdu
+application/vnd.oma.bcast.simple-symbol-container
+application/vnd.oma.bcast.smartcard-trigger+xml
+application/vnd.oma.bcast.sprov+xml
+application/vnd.oma.dd2+xml dd2
+application/vnd.oma.drm.risd+xml
+application/vnd.oma.group-usage-list+xml
+application/vnd.oma.poc.groups+xml
+application/vnd.oma.xcap-directory+xml
+application/vnd.omads-email+xml
+application/vnd.omads-file+xml
+application/vnd.omads-folder+xml
+application/vnd.omaloc-supl-init
+application/vnd.openofficeorg.extension oxt
+application/vnd.osa.netdeploy
+application/vnd.osgi.dp dp
+application/vnd.otps.ct-kip+xml
+application/vnd.palm prc pdb pqa oprc
+application/vnd.paos.xml
+application/vnd.pg.format str
+application/vnd.pg.osasli ei6
+application/vnd.piaccess.application-licence
+application/vnd.picsel efif
+application/vnd.poc.group-advertisement+xml
+application/vnd.pocketlearn plf
+application/vnd.powerbuilder6 pbd
+application/vnd.powerbuilder6-s
+application/vnd.powerbuilder7
+application/vnd.powerbuilder7-s
+application/vnd.powerbuilder75
+application/vnd.powerbuilder75-s
+application/vnd.preminet
+application/vnd.previewsystems.box box
+application/vnd.proteus.magazine mgz
+application/vnd.publishare-delta-tree qps
+application/vnd.pvi.ptid1 ptid
+application/vnd.pwg-multiplexed
+application/vnd.pwg-xhtml-print+xml
+application/vnd.qualcomm.brew-app-res
+application/vnd.quark.quarkxpress qxd qxt qwd qwt qxl qxb
+application/vnd.rapid
+application/vnd.recordare.musicxml mxl
+application/vnd.recordare.musicxml+xml
+application/vnd.renlearn.rlprint
+application/vnd.rn-realmedia rm
+application/vnd.ruckus.download
+application/vnd.s3sms
+application/vnd.scribus
+application/vnd.sealed.3df
+application/vnd.sealed.csf
+application/vnd.sealed.doc
+application/vnd.sealed.eml
+application/vnd.sealed.mht
+application/vnd.sealed.net
+application/vnd.sealed.ppt
+application/vnd.sealed.tiff
+application/vnd.sealed.xls
+application/vnd.sealedmedia.softseal.html
+application/vnd.sealedmedia.softseal.pdf
+application/vnd.seemail see
+application/vnd.sema sema
+application/vnd.semd semd
+application/vnd.semf semf
+application/vnd.shana.informed.formdata ifm
+application/vnd.shana.informed.formtemplate itp
+application/vnd.shana.informed.interchange iif
+application/vnd.shana.informed.package ipk
+application/vnd.simtech-mindmapper twd twds
+application/vnd.smaf mmf
+application/vnd.solent.sdkm+xml sdkm sdkd
+application/vnd.spotfire.dxp dxp
+application/vnd.spotfire.sfs sfs
+application/vnd.sss-cod
+application/vnd.sss-dtf
+application/vnd.sss-ntf
+application/vnd.street-stream
+application/vnd.sun.wadl+xml
+application/vnd.sus-calendar sus susp
+application/vnd.svd svd
+application/vnd.swiftview-ics
+application/vnd.syncml+xml xsm
+application/vnd.syncml.dm+wbxml bdm
+application/vnd.syncml.dm+xml xdm
+application/vnd.syncml.ds.notification
+application/vnd.tao.intent-module-archive tao
+application/vnd.tmobile-livetv tmo
+application/vnd.trid.tpt tpt
+application/vnd.triscape.mxs mxs
+application/vnd.trueapp tra
+application/vnd.truedoc
+application/vnd.ufdl ufd ufdl
+application/vnd.uiq.theme utz
+application/vnd.umajin umj
+application/vnd.unity unityweb
+application/vnd.uoml+xml uoml
+application/vnd.uplanet.alert
+application/vnd.uplanet.alert-wbxml
+application/vnd.uplanet.bearer-choice
+application/vnd.uplanet.bearer-choice-wbxml
+application/vnd.uplanet.cacheop
+application/vnd.uplanet.cacheop-wbxml
+application/vnd.uplanet.channel
+application/vnd.uplanet.channel-wbxml
+application/vnd.uplanet.list
+application/vnd.uplanet.list-wbxml
+application/vnd.uplanet.listcmd
+application/vnd.uplanet.listcmd-wbxml
+application/vnd.uplanet.signal
+application/vnd.vcx vcx
+application/vnd.vd-study
+application/vnd.vectorworks
+application/vnd.vidsoft.vidconference
+application/vnd.visio vsd vst vss vsw
+application/vnd.visionary vis
+application/vnd.vividence.scriptfile
+application/vnd.vsf vsf
+application/vnd.wap.sic
+application/vnd.wap.slc
+application/vnd.wap.wbxml wbxml
+application/vnd.wap.wmlc wmlc
+application/vnd.wap.wmlscriptc wmlsc
+application/vnd.webturbo wtb
+application/vnd.wfa.wsc
+application/vnd.wordperfect wpd
+application/vnd.wqd wqd
+application/vnd.wrq-hp3000-labelled
+application/vnd.wt.stf stf
+application/vnd.wv.csp+wbxml
+application/vnd.wv.csp+xml
+application/vnd.wv.ssp+xml
+application/vnd.xara xar
+application/vnd.xfdl xfdl
+application/vnd.xmpie.cpkg
+application/vnd.xmpie.dpkg
+application/vnd.xmpie.plan
+application/vnd.xmpie.ppkg
+application/vnd.xmpie.xlim
+application/vnd.yamaha.hv-dic hvd
+application/vnd.yamaha.hv-script hvs
+application/vnd.yamaha.hv-voice hvp
+application/vnd.yamaha.smaf-audio saf
+application/vnd.yamaha.smaf-phrase spf
+application/vnd.yellowriver-custom-menu cmp
+application/vnd.zzazz.deck+xml zaz
+application/voicexml+xml vxml
+application/watcherinfo+xml
+application/whoispp-query
+application/whoispp-response
+application/winhlp hlp
+application/wita
+application/wordperfect5.1
+application/wsdl+xml wsdl
+application/wspolicy+xml wspolicy
+application/x-ace-compressed ace
+application/x-bcpio bcpio
+application/x-bittorrent torrent
+application/x-bzip bz
+application/x-bzip2 bz2 boz
+application/x-cdlink vcd
+application/x-chat chat
+application/x-chess-pgn pgn
+application/x-compress
+application/x-cpio cpio
+application/x-csh csh
+application/x-director dcr dir dxr fgd
+application/x-dvi dvi
+application/x-futuresplash spl
+application/x-gtar gtar
+application/x-gzip
+application/x-hdf hdf
+application/x-latex latex
+application/x-ms-wmd wmd
+application/x-ms-wmz wmz
+application/x-msaccess mdb
+application/x-msbinder obd
+application/x-mscardfile crd
+application/x-msclip clp
+application/x-msdownload exe dll com bat msi
+application/x-msmediaview mvb m13 m14
+application/x-msmetafile wmf
+application/x-msmoney mny
+application/x-mspublisher pub
+application/x-msschedule scd
+application/x-msterminal trm
+application/x-mswrite wri
+application/x-netcdf nc cdf
+application/x-pkcs12 p12 pfx
+application/x-pkcs7-certificates p7b spc
+application/x-pkcs7-certreqresp p7r
+application/x-rar-compressed rar
+application/x-sh sh
+application/x-shar shar
+application/x-shockwave-flash swf
+application/x-stuffit sit
+application/x-stuffitx sitx
+application/x-sv4cpio sv4cpio
+application/x-sv4crc sv4crc
+application/x-tar tar
+application/x-tcl tcl
+application/x-tex tex
+application/x-texinfo texinfo texi
+application/x-ustar ustar
+application/x-wais-source src
+application/x-x509-ca-cert der crt
+application/x400-bp
+application/xcap-att+xml
+application/xcap-caps+xml
+application/xcap-el+xml
+application/xcap-error+xml
+application/xcap-ns+xml
+application/xenc+xml xenc
+application/xhtml+xml xhtml xht
+application/xml xml xsl
+application/xml-dtd dtd
+application/xml-external-parsed-entity
+application/xmpp+xml
+application/xop+xml xop
+application/xslt+xml xslt
+application/xspf+xml xspf
+application/xv+xml mxml xhvml xvml xvm
+application/zip zip
+audio/32kadpcm
+audio/3gpp
+audio/3gpp2
+audio/ac3
+audio/amr
+audio/amr-wb
+audio/amr-wb+
+audio/asc
+audio/basic au snd
+audio/bv16
+audio/bv32
+audio/clearmode
+audio/cn
+audio/dat12
+audio/dls
+audio/dsr-es201108
+audio/dsr-es202050
+audio/dsr-es202211
+audio/dsr-es202212
+audio/dvi4
+audio/eac3
+audio/evrc
+audio/evrc-qcp
+audio/evrc0
+audio/evrc1
+audio/evrcb
+audio/evrcb0
+audio/evrcb1
+audio/g722
+audio/g7221
+audio/g723
+audio/g726-16
+audio/g726-24
+audio/g726-32
+audio/g726-40
+audio/g728
+audio/g729
+audio/g7291
+audio/g729d
+audio/g729e
+audio/gsm
+audio/gsm-efr
+audio/ilbc
+audio/l16
+audio/l20
+audio/l24
+audio/l8
+audio/lpc
+audio/midi mid midi kar rmi
+audio/mobile-xmf
+audio/mp4 mp4a
+audio/mp4a-latm
+audio/mpa
+audio/mpa-robust
+audio/mpeg mpga mp2 mp2a mp3 m2a m3a
+audio/mpeg4-generic
+audio/parityfec
+audio/pcma
+audio/pcmu
+audio/prs.sid
+audio/qcelp
+audio/red
+audio/rtp-enc-aescm128
+audio/rtp-midi
+audio/rtx
+audio/smv
+audio/smv0
+audio/smv-qcp
+audio/sp-midi
+audio/t140c
+audio/t38
+audio/telephone-event
+audio/tone
+audio/vdvi
+audio/vmr-wb
+audio/vnd.3gpp.iufp
+audio/vnd.4sb
+audio/vnd.audiokoz
+audio/vnd.celp
+audio/vnd.cisco.nse
+audio/vnd.cmles.radio-events
+audio/vnd.cns.anp1
+audio/vnd.cns.inf1
+audio/vnd.digital-winds eol
+audio/vnd.dlna.adts
+audio/vnd.dolby.mlp
+audio/vnd.everad.plj
+audio/vnd.hns.audio
+audio/vnd.lucent.voice lvp
+audio/vnd.nokia.mobile-xmf
+audio/vnd.nortel.vbk
+audio/vnd.nuera.ecelp4800 ecelp4800
+audio/vnd.nuera.ecelp7470 ecelp7470
+audio/vnd.nuera.ecelp9600 ecelp9600
+audio/vnd.octel.sbc
+audio/vnd.qcelp
+audio/vnd.rhetorex.32kadpcm
+audio/vnd.sealedmedia.softseal.mpeg
+audio/vnd.vmx.cvsd
+audio/wav wav
+audio/x-aiff aif aiff aifc
+audio/x-mpegurl m3u
+audio/x-ms-wax wax
+audio/x-ms-wma wma
+audio/x-pn-realaudio ram ra
+audio/x-pn-realaudio-plugin rmp
+audio/x-wav wav
+chemical/x-cdx cdx
+chemical/x-cif cif
+chemical/x-cmdf cmdf
+chemical/x-cml cml
+chemical/x-csml csml
+chemical/x-pdb pdb
+chemical/x-xyz xyz
+image/bmp bmp
+image/cgm cgm
+image/fits
+image/g3fax g3
+image/gif gif
+image/ief ief
+image/jp2
+image/jpeg jpeg jpg jpe
+image/jpm
+image/jpx
+image/naplps
+image/png png
+image/prs.btif btif
+image/prs.pti
+image/svg+xml svg svgz
+image/t38
+image/tiff tiff tif
+image/tiff-fx
+image/vnd.adobe.photoshop psd
+image/vnd.cns.inf2
+image/vnd.djvu djvu djv
+image/vnd.dwg dwg
+image/vnd.dxf dxf
+image/vnd.fastbidsheet fbs
+image/vnd.fpx fpx
+image/vnd.fst fst
+image/vnd.fujixerox.edmics-mmr mmr
+image/vnd.fujixerox.edmics-rlc rlc
+image/vnd.globalgraphics.pgb
+image/vnd.microsoft.icon ico
+image/vnd.mix
+image/vnd.ms-modi mdi
+image/vnd.net-fpx npx
+image/vnd.sealed.png
+image/vnd.sealedmedia.softseal.gif
+image/vnd.sealedmedia.softseal.jpg
+image/vnd.svf
+image/vnd.wap.wbmp wbmp
+image/vnd.xiff xif
+image/x-cmu-raster ras
+image/x-cmx cmx
+image/x-icon
+image/x-pcx pcx
+image/x-pict pic pct
+image/x-portable-anymap pnm
+image/x-portable-bitmap pbm
+image/x-portable-graymap pgm
+image/x-portable-pixmap ppm
+image/x-rgb rgb
+image/x-xbitmap xbm
+image/x-xpixmap xpm
+image/x-xwindowdump xwd
+message/cpim
+message/delivery-status
+message/disposition-notification
+message/external-body
+message/http
+message/news
+message/partial
+message/rfc822 eml mime
+message/s-http
+message/sip
+message/sipfrag
+message/tracking-status
+model/iges igs iges
+model/mesh msh mesh silo
+model/vnd.dwf dwf
+model/vnd.flatland.3dml
+model/vnd.gdl gdl
+model/vnd.gs.gdl
+model/vnd.gtw gtw
+model/vnd.moml+xml
+model/vnd.mts mts
+model/vnd.parasolid.transmit.binary
+model/vnd.parasolid.transmit.text
+model/vnd.vtu vtu
+model/vrml wrl vrml
+multipart/alternative
+multipart/appledouble
+multipart/byteranges
+multipart/digest
+multipart/encrypted
+multipart/form-data
+multipart/header-set
+multipart/mixed
+multipart/parallel
+multipart/related
+multipart/report
+multipart/signed
+multipart/voice-message
+text/calendar ics ifb
+text/css css
+text/csv csv
+text/directory
+text/dns
+text/enriched
+text/html html htm
+text/parityfec
+text/plain txt text conf def list log in
+text/prs.fallenstein.rst
+text/prs.lines.tag dsc
+text/red
+text/rfc822-headers
+text/richtext rtx
+text/rtf
+text/rtp-enc-aescm128
+text/rtx
+text/sgml sgml sgm
+text/t140
+text/tab-separated-values tsv
+text/troff t tr roff man me ms
+text/uri-list uri uris urls
+text/vnd.abc
+text/vnd.curl
+text/vnd.dmclientscript
+text/vnd.esmertec.theme-descriptor
+text/vnd.fly fly
+text/vnd.fmi.flexstor flx
+text/vnd.in3d.3dml 3dml
+text/vnd.in3d.spot spot
+text/vnd.iptc.newsml
+text/vnd.iptc.nitf
+text/vnd.latex-z
+text/vnd.motorola.reflex
+text/vnd.ms-mediapackage
+text/vnd.net2phone.commcenter.command
+text/vnd.sun.j2me.app-descriptor jad
+text/vnd.trolltech.linguist
+text/vnd.wap.si
+text/vnd.wap.sl
+text/vnd.wap.wml wml
+text/vnd.wap.wmlscript wmls
+text/x-asm s asm
+text/x-c c cc cxx cpp h hh dic
+text/x-fortran f for f77 f90
+text/x-pascal p pas
+text/x-java-source java
+text/x-setext etx
+text/x-uuencode uu
+text/x-vcalendar vcs
+text/x-vcard vcf
+text/xml
+text/xml-external-parsed-entity
+video/3gpp 3gp
+video/3gpp-tt
+video/3gpp2 3g2
+video/bmpeg
+video/bt656
+video/celb
+video/dv
+video/h261 h261
+video/h263 h263
+video/h263-1998
+video/h263-2000
+video/h264 h264
+video/jpeg jpgv
+video/jpm jpm jpgm
+video/mj2 mj2 mjp2
+video/mp1s
+video/mp2p
+video/mp2t
+video/mp4 mp4 mp4v mpg4
+video/mp4v-es
+video/mpeg mpeg mpg mpe m1v m2v
+video/mpeg4-generic
+video/mpv
+video/nv
+video/parityfec
+video/pointer
+video/quicktime qt mov
+video/raw
+video/rtp-enc-aescm128
+video/rtx
+video/smpte292m
+video/vc1
+video/vnd.dlna.mpeg-tts
+video/vnd.fvt fvt
+video/vnd.hns.video
+video/vnd.motorola.video
+video/vnd.motorola.videop
+video/vnd.mpegurl mxu m4u
+video/vnd.nokia.interleaved-multimedia
+video/vnd.nokia.videovoip
+video/vnd.objectvideo
+video/vnd.sealed.mpeg1
+video/vnd.sealed.mpeg4
+video/vnd.sealed.swf
+video/vnd.sealedmedia.softseal.mov
+video/vnd.vivo viv
+video/x-fli fli
+video/x-ms-asf asf asx
+video/x-ms-wm wm
+video/x-ms-wmv wmv
+video/x-ms-wmx wmx
+video/x-ms-wvx wvx
+video/x-msvideo avi
+video/x-sgi-movie movie
+x-conference/x-cooltalk ice
--- /dev/null
+<profile name="external">
+ <!-- http://wiki.freeswitch.org/wiki/Sofia_Configuration_Files -->
+ <!-- This profile is only for outbound registrations to providers -->
+ <gateways>
+ <X-PRE-PROCESS cmd="include" data="external/*.xml"/>
+ </gateways>
+
+ <aliases>
+ <!--
+ <alias name="outbound"/>
+ <alias name="nat"/>
+ -->
+ </aliases>
+
+ <domains>
+ <domain name="all" alias="false" parse="true"/>
+ </domains>
+
+ <settings>
+ <param name="debug" value="0"/>
+ <!-- If you want FreeSWITCH to shutdown if this profile fails to load, uncomment the next line. -->
+ <!-- <param name="shutdown-on-fail" value="true"/> -->
+ <param name="sip-trace" value="no"/>
+ <param name="sip-capture" value="no"/>
+ <param name="rfc2833-pt" value="101"/>
+ <!-- RFC 5626 : Send reg-id and sip.instance -->
+ <!--<param name="enable-rfc-5626" value="true"/> -->
+ <param name="sip-port" value="$${external_sip_port}"/>
+ <param name="dialplan" value="XML"/>
+ <param name="context" value="public"/>
+ <param name="dtmf-duration" value="2000"/>
+ <param name="inbound-codec-prefs" value="$${global_codec_prefs}"/>
+ <param name="outbound-codec-prefs" value="$${outbound_codec_prefs}"/>
+ <param name="hold-music" value="$${hold_music}"/>
+ <param name="rtp-timer-name" value="soft"/>
+ <!--<param name="enable-100rel" value="true"/>-->
+ <!--<param name="disable-srv503" value="true"/>-->
+ <!-- This could be set to "passive" -->
+ <param name="local-network-acl" value="localnet.auto"/>
+ <param name="manage-presence" value="false"/>
+
+ <!-- used to share presence info across sofia profiles
+ manage-presence needs to be set to passive on this profile
+ if you want it to behave as if it were the internal profile
+ for presence.
+ -->
+ <!-- Name of the db to use for this profile -->
+ <!--<param name="dbname" value="share_presence"/>-->
+ <!--<param name="presence-hosts" value="$${domain}"/>-->
+ <!--<param name="force-register-domain" value="$${domain}"/>-->
+ <!--all inbound reg will stored in the db using this domain -->
+ <!--<param name="force-register-db-domain" value="$${domain}"/>-->
+ <!-- ************************************************* -->
+
+ <!--<param name="aggressive-nat-detection" value="true"/>-->
+ <param name="inbound-codec-negotiation" value="generous"/>
+ <param name="nonce-ttl" value="60"/>
+ <param name="auth-calls" value="$${external_auth_calls}"/>
+ <param name="inbound-late-negotiation" value="true"/>
+ <param name="inbound-zrtp-passthru" value="true"/> <!-- (also enables late negotiation) -->
+ <!--
+ DO NOT USE HOSTNAMES, ONLY IP ADDRESSES IN THESE SETTINGS!
+ -->
+ <param name="rtp-ip" value="$${external_rtp_ip}"/>
+ <param name="sip-ip" value="$${external_sip_ip}"/>
+ <param name="ext-rtp-ip" value="auto-nat"/>
+ <param name="ext-sip-ip" value="auto-nat"/>
+ <param name="rtp-timeout-sec" value="300"/>
+ <param name="rtp-hold-timeout-sec" value="1800"/>
+ <!--<param name="enable-3pcc" value="true"/>-->
+
+ <!-- TLS: disabled by default, set to "true" to enable -->
+ <param name="tls" value="$${external_ssl_enable}"/>
+ <!-- Set to true to not bind on the normal sip-port but only on the TLS port -->
+ <param name="tls-only" value="false"/>
+ <!-- additional bind parameters for TLS -->
+ <param name="tls-bind-params" value="transport=tls"/>
+ <!-- Port to listen on for TLS requests. (5081 will be used if unspecified) -->
+ <param name="tls-sip-port" value="$${external_tls_port}"/>
+ <!-- Location of the agent.pem and cafile.pem ssl certificates (needed for TLS server) -->
+ <param name="tls-cert-dir" value="$${external_ssl_dir}"/>
+ <!-- Optionally set the passphrase password used by openSSL to encrypt/decrypt TLS private key files -->
+ <param name="tls-passphrase" value=""/>
+ <!-- Verify the date on TLS certificates -->
+ <param name="tls-verify-date" value="true"/>
+ <!-- TLS verify policy, when registering/inviting gateways with other servers (outbound) or handling inbound registration/invite requests how should we verify their certificate -->
+ <!-- set to 'in' to only verify incoming connections, 'out' to only verify outgoing connections, 'all' to verify all connections, also 'in_subjects', 'out_subjects' and 'all_subjects' for subject validation. Multiple policies can be split with a '|' pipe -->
+ <param name="tls-verify-policy" value="none"/>
+ <!-- Certificate max verify depth to use for validating peer TLS certificates when the verify policy is not none -->
+ <param name="tls-verify-depth" value="2"/>
+ <!-- If the tls-verify-policy is set to subjects_all or subjects_in this sets which subjects are allowed, multiple subjects can be split with a '|' pipe -->
+ <param name="tls-verify-in-subjects" value=""/>
+ <!-- TLS version ("sslv23" (default), "tlsv1"). NOTE: Phones may not work with TLSv1 -->
+ <param name="tls-version" value="$${sip_tls_version}"/>
+ </settings>
+</profile>
--- /dev/null
+<include>
+ <!--<gateway name="asterlink.com">-->
+ <!--/// account username *required* ///-->
+ <!--<param name="username" value="cluecon"/>-->
+ <!--/// auth realm: *optional* same as gateway name, if blank ///-->
+ <!--<param name="realm" value="asterlink.com"/>-->
+ <!--/// username to use in from: *optional* same as username, if blank ///-->
+ <!--<param name="from-user" value="cluecon"/>-->
+ <!--/// domain to use in from: *optional* same as realm, if blank ///-->
+ <!--<param name="from-domain" value="asterlink.com"/>-->
+ <!--/// account password *required* ///-->
+ <!--<param name="password" value="2007"/>-->
+ <!--/// extension for inbound calls: *optional* same as username, if blank ///-->
+ <!--<param name="extension" value="cluecon"/>-->
+ <!--/// proxy host: *optional* same as realm, if blank ///-->
+ <!--<param name="proxy" value="asterlink.com"/>-->
+ <!--/// send register to this proxy: *optional* same as proxy, if blank ///-->
+ <!--<param name="register-proxy" value="mysbc.com"/>-->
+ <!--/// expire in seconds: *optional* 3600, if blank ///-->
+ <!--<param name="expire-seconds" value="60"/>-->
+ <!--/// do not register ///-->
+ <!--<param name="register" value="false"/>-->
+ <!-- which transport to use for register -->
+ <!--<param name="register-transport" value="udp"/>-->
+ <!--How many seconds before a retry when a failure or timeout occurs -->
+ <!--<param name="retry-seconds" value="30"/>-->
+ <!--Use the callerid of an inbound call in the from field on outbound calls via this gateway -->
+ <!--<param name="caller-id-in-from" value="false"/>-->
+ <!--extra sip params to send in the contact-->
+ <!--<param name="contact-params" value="tport=tcp"/>-->
+ <!--send an options ping every x seconds, failure will unregister and/or mark it down-->
+ <!--<param name="ping" value="25"/>-->
+ <!--</gateway>-->
+ <!--rfc5626 : Abilitazione rfc5626 ///-->
+ <!--<param name="rfc-5626" value="true"/>-->
+ <!--rfc5626 : extra sip params to send in the contact-->
+ <!--<param name="reg-id" value="1"/>-->
+</include>
--- /dev/null
+<include>
+ <X-PRE-PROCESS cmd="set" data="sound_prefix=$${sounds_dir}/en/us/callie"/>
+
+ <!--
+ This setting is what sets the default domain FreeSWITCH will use if all else fails.
+
+ FreeSWICH will default to $${local_ip_v4} unless changed. Changing this setting does
+ affect the sip authentication. Please review conf/directory/default.xml for more
+ information on this topic.
+ -->
+ <X-PRE-PROCESS cmd="set" data="domain=$${local_ip_v4}"/>
+ <X-PRE-PROCESS cmd="set" data="domain_name=$${domain}"/>
+ <X-PRE-PROCESS cmd="set" data="hold_music=local_stream://moh"/>
+ <X-PRE-PROCESS cmd="set" data="use_profile=external"/>
+
+ <!--
+ Enable ZRTP globally you can override this on a per channel basis
+
+ http://wiki.freeswitch.org/wiki/ZRTP (on how to enable zrtp)
+ -->
+ <X-PRE-PROCESS cmd="set" data="zrtp_secure_media=true"/>
+
+ <X-PRE-PROCESS cmd="set" data="global_codec_prefs=PCMU,PCMA"/>
+ <X-PRE-PROCESS cmd="set" data="outbound_codec_prefs=PCMU,PCMA"/>
+
+ <!--
+ THIS IS ONLY USED FOR DINGALING
+
+ bind_server_ip
+
+ Can be an ip address, a dns name, or "auto".
+ This determines an ip address available on this host to bind.
+ If you are separating RTP and SIP traffic, you will want to have
+ use different addresses where this variable appears.
+ Used by: dingaling.conf.xml
+ -->
+ <X-PRE-PROCESS cmd="set" data="bind_server_ip=auto"/>
+
+ <!-- unroll-loops
+ Used to turn on sip loopback unrolling.
+ -->
+ <X-PRE-PROCESS cmd="set" data="unroll_loops=true"/>
+
+ <X-PRE-PROCESS cmd="set" data="outbound_caller_name=FreeSWITCH"/>
+ <X-PRE-PROCESS cmd="set" data="outbound_caller_id=0000000000"/>
+
+ <!-- various debug and defaults -->
+ <X-PRE-PROCESS cmd="set" data="call_debug=false"/>
+ <X-PRE-PROCESS cmd="set" data="console_loglevel=info"/>
+ <X-PRE-PROCESS cmd="set" data="default_areacode=617"/>
+ <X-PRE-PROCESS cmd="set" data="default_country=US"/>
+
+ <!-- if false or undefined, the destination number is included in presence NOTIFY dm:note.
+ if true, the destination number is not included -->
+ <X-PRE-PROCESS cmd="set" data="presence_privacy=false"/>
+
+ <!--
+ SIP and TLS settings. http://wiki.freeswitch.org/wiki/Tls
+ -->
+ <X-PRE-PROCESS cmd="set" data="sip_tls_version=tlsv1"/>
+
+ <!-- Rayo -->
+ <X-PRE-PROCESS cmd="set" data="rayo_ip=$${local_ip_v4}"/>
+ <X-PRE-PROCESS cmd="set" data="rayo_domain_name=$${rayo_ip}"/>
+
+ <!-- External SIP Profile -->
+ <X-PRE-PROCESS cmd="set" data="external_rtp_ip=$${local_ip_v4}"/>
+ <X-PRE-PROCESS cmd="set" data="external_sip_ip=$${local_ip_v4}"/>
+ <X-PRE-PROCESS cmd="set" data="external_sip_port=5060"/>
+ <X-PRE-PROCESS cmd="set" data="external_tls_port=5061"/>
+ <X-PRE-PROCESS cmd="set" data="external_tls_enable=false"/>
+ <X-PRE-PROCESS cmd="set" data="external_tls_dir=$${base_dir}/conf/ssl"/>
+ <X-PRE-PROCESS cmd="set" data="external_auth_calls=false"/>
+</include>
--- /dev/null
+# Still a work in progress...
+
+# Compile on CentOS 6.4
+erlc -I /usr/lib64/ejabberd/include mod_rayo_gateway.erl
+
+# Install in ejabberd
+cp mod_rayo_gateway.beam /usr/lib64/ejabberd/ebin/
+
+vi /etc/ejabberd/ejabberd.cfg
+ %%
+ %% Modules enabled in all ejabberd virtual hosts
+ %%
+ {modules,
+ [
+ ...
+ {mod_rayo_gateway,[]},
+ ...
+ ]}.
+
+ejabberdctl debug
+(ejabberd@jabber)1> l(mod_rayo_gateway).
+{module,mod_rayo_gateway}
+(ejabberd@jabber)2>
+
--- /dev/null
+%%
+%% Copyright (c) 2013 Grasshopper
+%%
+%% Permission is hereby granted, free of charge, to any person obtaining a copy
+%% of this software and associated documentation files (the "Software"), to deal
+%% in the Software without restriction, including without limitation the rights
+%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+%% copies of the Software, and to permit persons to whom the Software is
+%% furnished to do so, subject to the following conditions:
+%%
+%% The above copyright notice and this permission notice shall be included in
+%% all copies or substantial portions of the Software.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+%% THE SOFTWARE.
+%%
+%% Contributors:
+%% Chris Rienzo <chris.rienzo@grasshopper.com>
+%%
+%% Maintainer: Chris Rienzo <chris.rienzo@grasshopper.com>
+%%
+%% mod_rayo_gateway.erl -- ejabberd Rayo gateway module
+%%
+-module(mod_rayo_gateway).
+
+-include("ejabberd.hrl").
+-include("jlib.hrl").
+
+-behavior(gen_server).
+-behavior(gen_mod).
+
+%% JID mappings
+%%
+%% Entity Internal JID Mapped JID
+%% ====== =============== ===============
+%% Client user@domain/resource gateway@internal_domain/gw-resource
+%% Node node_domain external_domain
+%% Call uuid@node_domain node_domain|uuid@external_domain
+%% Call Resource uuid@node_domain/resource node_domain|uuid@external_domain/resource
+%% Mixer name@node_domain node_domain|name@external_domain
+%% Mixer Resource name@node_domain/resource node_domain|name@external_domain/resource
+
+%% TODO don't allow nodes to act as clients
+%% TODO don't allow clients to act as nodes
+
+-export([
+ start_link/2,
+ start/2,
+ stop/1,
+ init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3,
+ route_internal/3,
+ route_external/3
+]).
+
+-define(PROCNAME, ejabberd_mod_rayo_gateway).
+-define(NS_RAYO, "urn:xmpp:rayo:1").
+-define(NS_PING, "urn:xmpp:ping").
+
+-record(rayo_config, {name, value}).
+-record(rayo_clients, {jid, status}).
+-record(rayo_nodes, {jid, status}).
+-record(rayo_entities, {external_jid, internal_jid, dcp_jid, type}).
+
+start_link(Host, Opts) ->
+ Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
+ gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
+
+% Start the module process
+start(Host, Opts) ->
+ Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
+ ChildSpec = {Proc,
+ {?MODULE, start_link, [Host, Opts]},
+ temporary,
+ 1000,
+ worker,
+ [?MODULE]},
+ supervisor:start_child(ejabberd_sup, ChildSpec).
+
+% Shutdown the module
+stop(Host) ->
+ Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
+ gen_server:call(Proc, stop),
+ supervisor:terminate_child(ejabberd_sup, Proc),
+ supervisor:delete_child(ejabberd_sup, Proc).
+
+% Initialize the module
+init([Host, Opts]) ->
+ ?DEBUG("MOD_RAYO_GATEWAY: Starting", []),
+
+ mnesia:delete_table(rayo_clients),
+ mnesia:create_table(rayo_clients, [{attributes, record_info(fields, rayo_clients)}]),
+ mnesia:delete_table(rayo_nodes),
+ mnesia:create_table(rayo_nodes, [{attributes, record_info(fields, rayo_nodes)}]),
+ mnesia:delete_table(rayo_entities),
+ mnesia:create_table(rayo_entities, [{attributes, record_info(fields, rayo_entities)}, {index, [internal_jid]}]),
+ mnesia:delete_table(rayo_config),
+ mnesia:create_table(rayo_config, [{attributes, record_info(fields, rayo_config)}]),
+
+ {A1,A2,A3} = now(),
+ random:seed(A1, A2, A3),
+
+ % create virtual domains
+ InternalDomain = gen_mod:get_opt_host(Host, Opts, "rayo-int.@HOST@"),
+ ExternalDomain = gen_mod:get_opt_host(Host, Opts, "rayo.@HOST@"),
+ {ok, Hostname} = inet:gethostname(),
+ InternalClient = "gateway@" ++ InternalDomain ++ "/" ++ Hostname ++ "-" ++ integer_to_list(random:uniform(65535)),
+ ?DEBUG("MOD_RAYO_GATEWAY: InternalDomain = ~p, ExternalDomain = ~p, InternalClient = ~p", [InternalDomain, ExternalDomain, InternalClient]),
+ mnesia:transaction(
+ fun() ->
+ mnesia:write(#rayo_config{name = "internal_domain", value = InternalDomain}),
+ mnesia:write(#rayo_config{name = "internal_client", value = InternalClient}),
+ mnesia:write(#rayo_config{name = "external_domain", value = ExternalDomain})
+ end
+ ),
+
+ % set up routes to virtual domains
+ ejabberd_router:register_route(InternalDomain, {apply, ?MODULE, route_internal}),
+ ejabberd_router:register_route(ExternalDomain, {apply, ?MODULE, route_external}),
+ {ok, Host}.
+
+handle_call(stop, _From, Host) ->
+ {stop, normal, ok, Host}.
+
+handle_cast(_Msg, Host) ->
+ {noreply, Host}.
+
+handle_info(_Msg, Host) ->
+ {noreply, Host}.
+
+terminate(_Reason, Host) ->
+ ejabberd_router:unregister_route(Host),
+ ok.
+
+code_change(_OldVsn, Host, _Extra) ->
+ {ok, Host}.
+
+register_rayo_node(Jid) ->
+ Write = fun() ->
+ mnesia:write(#rayo_nodes{jid = Jid, status = "online" })
+ end,
+ Result = mnesia:transaction(Write),
+ ?DEBUG("MOD_RAYO_GATEWAY: register node: ~p, result = ~p, ~p nodes total", [jlib:jid_to_string(Jid), Result, num_rayo_nodes()]),
+ case num_clients() >= 1 of
+ true ->
+ ejabberd_router:route(internal_client(), Jid, online_presence());
+ _ ->
+ ok
+ end,
+ ok.
+
+% TODO call this when s2s connection is dropped
+unregister_rayo_node(Jid) ->
+ Delete = fun() ->
+ mnesia:delete({rayo_nodes, Jid})
+ end,
+ Result = mnesia:transaction(Delete),
+ Size = mnesia:table_info(rayo_nodes, size),
+ ?DEBUG("MOD_RAYO_GATEWAY: unregister node: ~p, result = ~p, ~p nodes total", [jlib:jid_to_string(Jid), Result, Size]),
+ ok.
+
+% Add client
+register_rayo_client(Jid) ->
+ Write = fun() ->
+ mnesia:write(#rayo_clients{jid = Jid, status = "online" })
+ end,
+ Result = mnesia:transaction(Write),
+ Size = num_clients(),
+ ?DEBUG("MOD_RAYO_GATEWAY: register client: ~p, result = ~p, ~p clients total", [jlib:jid_to_string(Jid), Result, Size]),
+ case Size of
+ 1 ->
+ route_to_list(internal_client(), all_rayo_nodes(), online_presence());
+ _ ->
+ ok
+ end,
+ ok.
+
+% Remove client
+% TODO call this when c2s connection is dropped
+unregister_rayo_client(Jid) ->
+ Delete = fun() ->
+ mnesia:delete({rayo_clients, Jid})
+ end,
+ Result = mnesia:transaction(Delete),
+ Size = num_clients(),
+ ?DEBUG("MOD_RAYO_GATEWAY: unregister client: ~p, result = ~p, ~p clients total", [jlib:jid_to_string(Jid), Result, Size]),
+ case Size of
+ 0 ->
+ route_to_list(internal_client(), all_rayo_nodes(), offline_presence());
+ _ ->
+ ok
+ end,
+ ok.
+
+% Add node entity
+register_rayo_node_entity(ExtJid, IntJid, DcpJid, Type) ->
+ Write = fun() ->
+ mnesia:write(#rayo_entities{external_jid = ExtJid, internal_jid = IntJid, dcp_jid = DcpJid, type = Type})
+ end,
+ Result = mnesia:transaction(Write),
+ Size = mnesia:table_info(rayo_entities, size),
+ ?DEBUG("MOD_RAYO_GATEWAY: register entity: ~p, result = ~p, ~p entities total", [jlib:jid_to_string(ExtJid), Result, Size]),
+ ok.
+
+% Remove node entity
+unregister_rayo_node_entity(ExtJid) ->
+ Delete = fun() ->
+ mnesia:delete({rayo_entities, ExtJid})
+ end,
+ Result = mnesia:transaction(Delete),
+ Size = mnesia:table_info(rayo_entities, size),
+ ?DEBUG("MOD_RAYO_GATEWAY: unregister entity: ~p, result = ~p, ~p entities total", [jlib:jid_to_string(ExtJid), Result, Size]),
+ ok.
+
+% find node entity given enitity's (or its component's) internal JID
+find_rayo_node_entity_by_int_jid(IntJid) ->
+ % remove resource from JID to find component's parent call/mixer
+ case mnesia:dirty_index_read(rayo_entities, jlib:jid_remove_resource(IntJid), #rayo_entities.internal_jid) of
+ [Entity | _] ->
+ Entity;
+ _ ->
+ none
+ end.
+
+% find node entity given enitity's (or its component's) external JID
+find_rayo_node_entity_by_ext_jid(ExtJid) ->
+ % remove resource from JID to find component's parent call/mixer
+ case mnesia:dirty_read(rayo_entities, jlib:jid_remove_resource(ExtJid)) of
+ [Entity | _] ->
+ Entity;
+ _ ->
+ none
+ end.
+
+% find entity Definitive Controlling Party JID given entity external JID
+find_rayo_node_entity_dcp_by_ext_jid(ExtJid) ->
+ case find_rayo_node_entity_by_ext_jid(ExtJid) of
+ {rayo_entities, _, _, DcpJid, _} ->
+ DcpJid;
+ _ ->
+ none
+ end.
+
+% find entity Definitive Controlling Party JID given entity internal JID
+find_rayo_node_entity_dcp_by_int_jid(IntJid) ->
+ case find_rayo_node_entity_by_int_jid(IntJid) of
+ {rayo_entities, _, _, DcpJid, _} ->
+ DcpJid;
+ _ ->
+ none
+ end.
+
+% create External JID from Internal JID
+% intnode@intdomain/resource -> intdomain-intnode@extdomain/resource
+create_external_jid({jid, Node, Domain, Resource, _, _, _}) ->
+ jlib:make_jid(Domain ++ "|" ++ Node, jlib:jid_to_string(external_domain()), Resource).
+
+% create Internal JID from External JID
+% intdomain-intnode@extdomain/resource -> intnode@intdomain/resource
+create_internal_jid({jid, Node, _Domain, Resource, _, _, _}) ->
+ % TODO use rayo_entities to lookup node... it's safer
+ Idx = string:str(Node, "|"),
+ case Idx > 0 of
+ true ->
+ jlib:make_jid(string:substr(Node, Idx + 1), string:substr(Node, 1, Idx - 1), Resource);
+ false ->
+ none
+ end.
+
+% Take control of entity
+% Return {true, internal entity JID} if successful
+set_entity_dcp(PcpJid, EntityJid) ->
+ SetDcp = fun() ->
+ case mnesia:wread(rayo_entities, EntityJid) of
+ [{rayo_entities, EntityJid, InternalJid, none, Type}] ->
+ % take control
+ case mnesia:write(#rayo_entities{external_jid = EntityJid, internal_jid = InternalJid, dcp_jid = PcpJid, type = Type}) of
+ ok ->
+ {true, InternalJid};
+ Else ->
+ {error, Else}
+ end;
+ _ ->
+ {false, []}
+ end
+ end,
+ {_, Result} = mnesia:transaction(SetDcp),
+ Result.
+
+% Check if PCP has control of entity
+% Return {true, internal entity JID} if true
+is_entity_dcp(PcpJid, EntityJid) ->
+ % quick check first
+ case mnesia:dirty_read(rayo_entities, EntityJid) of
+ [{rayo_entities, EntityJid, _, none, _}] ->
+ % take control
+ set_entity_dcp(PcpJid, EntityJid);
+ [{rayo_entities, EntityJid, InternalJid, PcpJid, _}] ->
+ {true, InternalJid};
+ [{rayo_entities, EntityJid, InternalJid, _, _}] ->
+ {false, InternalJid};
+ [] ->
+ ?DEBUG("MOD_RAYO_GATEWAY: no match for EntityJid ~p", [EntityJid]),
+ {false, none}
+ end.
+
+% Handle presence to external domain
+route_external(From, {jid, [], _Domain, [], [], _LDomain, []} = To, {xmlelement, "presence", _Attrs, _Els} = Presence) ->
+ ?DEBUG("MOD_RAYO_GATEWAY: got client presence ~n~p", [Presence]),
+ route_client_presence(From, To, Presence),
+ ok;
+
+% Handle presence to external domain resource
+route_external(From, To, {xmlelement, "presence", _Attrs, _Els} = Presence) ->
+ ?DEBUG("MOD_RAYO_GATEWAY: got client presence to mixer ~n~p", [Presence]),
+ % TODO check if actually being sent to mixer...
+ route_client_presence_to_mixer(From, To, Presence),
+ ok;
+
+% Handle <message> to external domain
+route_external(_From, _To, {xmlelement, "message", _Attrs, _Els} = Message) ->
+ % ignore
+ ?DEBUG("MOD_RAYO_GATEWAY: got client message ~n~p", [Message]),
+ ok;
+
+% Handle <iq> to external domain
+route_external(From, {jid, [], _Domain, [], [], _LDomain, []} = To, {xmlelement, "iq", _Attrs, _Els} = IQ) ->
+ ?DEBUG("MOD_RAYO_GATEWAY: got client iq to gateway ~n~p", [IQ]),
+ case get_attribute_as_list(IQ, "type", "") of
+ "get" ->
+ case get_element(IQ, ?NS_PING, "ping") of
+ undefined ->
+ route_error_reply(To, From, IQ, ?ERR_BAD_REQUEST);
+ _ ->
+ route_result_reply(To, From, IQ)
+ end;
+ "set" ->
+ case get_element(IQ, ?NS_RAYO, "dial") of
+ undefined->
+ route_error_reply(To, From, IQ, ?ERR_BAD_REQUEST);
+ _ ->
+ route_dial_call(To, From, IQ)
+ end;
+ "" ->
+ route_error_reply(To, From, IQ, ?ERR_BAD_REQUEST)
+ end,
+ ok;
+
+% Handle <iq> to external domain resource
+route_external(From, To, {xmlelement, "iq", _Attrs, _Els} = IQ) ->
+ ?DEBUG("MOD_RAYO_GATEWAY: got client iq ~n~p", [IQ]),
+ case is_entity_dcp(From, To) of
+ {true, _} ->
+ IntFrom = internal_client(),
+ IntTo = create_internal_jid(To),
+ route_iq_request(IntFrom, IntTo, IQ, fun(IQReply) -> route_iq_response(From, To, IQ, IQReply) end);
+ {false, _} ->
+ route_error_reply(To, From, IQ, ?ERR_CONFLICT);
+ _ ->
+ route_error_reply(To, From, IQ, ?ERR_BAD_REQUEST)
+ end,
+ ok.
+
+% Handle <presence> to internal domain
+route_internal(From, {jid, [], _Domain, [], [], _LDomain, []} = To, {xmlelement, "presence", _Attrs, _Els} = Presence) ->
+ ?DEBUG("MOD_RAYO_GATEWAY: got node presence to internal domain ~n~p", [Presence]),
+ route_server_presence(From, To, Presence),
+ ok;
+
+% Handle <presence> to internal domain resource
+route_internal(From, To, {xmlelement, "presence", _Attrs, _Els} = Presence) ->
+ ?DEBUG("MOD_RAYO_GATEWAY: got node presence to internal domain ~n~p", [Presence]),
+ case To =:= internal_client() of
+ true ->
+ route_server_presence(From, To, Presence);
+ false ->
+ % TODO implement
+ ok
+ end,
+ ok;
+
+% Handle <message> to internal domain
+route_internal(_From, _To, {xmlelement, "message", _Attrs, _Els} = Message) ->
+ ?DEBUG("MOD_RAYO_GATEWAY: got node message ~n~p", [Message]),
+ % ignore
+ ok;
+
+% Handle <iq> to internal domain.
+route_internal(From, {jid, [], _Domain, [], [], _LDomain, []} = To, {xmlelement, "iq", _Attrs, _Els} = IQ) ->
+ ?DEBUG("MOD_RAYO_GATEWAY: got node iq ~n~p", [IQ]),
+ case get_attribute_as_list(IQ, "type", "") of
+ "get" ->
+ case get_element(IQ, ?NS_PING, "ping") of
+ undefined ->
+ route_error_reply(To, From, IQ, ?ERR_BAD_REQUEST);
+ _ ->
+ route_result_reply(To, From, IQ)
+ end;
+ "result" ->
+ ejabberd_local:process_iq_reply(From, To, jlib:iq_query_or_response_info(IQ));
+ "error" ->
+ ejabberd_local:process_iq_reply(From, To, jlib:iq_query_or_response_info(IQ));
+ "" ->
+ % don't allow get/set from nodes
+ route_error_reply(To, From, IQ, ?ERR_BAD_REQUEST)
+ end,
+ ok;
+
+% Handle <iq> to internal domain resource.
+route_internal(From, To, {xmlelement, "iq", _Attrs, _Els} = IQ) ->
+ ?DEBUG("MOD_RAYO_GATEWAY: got node iq ~n~p", [IQ]),
+ case get_attribute_as_list(IQ, "type", "") of
+ "result" ->
+ ejabberd_local:process_iq_reply(From, To, jlib:iq_query_or_response_info(IQ));
+ "error" ->
+ ejabberd_local:process_iq_reply(From, To, jlib:iq_query_or_response_info(IQ));
+ _ ->
+ % Don't allow get/set from nodes
+ route_error_reply(To, From, IQ, ?ERR_BAD_REQUEST)
+ end,
+ ok.
+
+% Process presence message from rayo node
+route_rayo_node_presence(From, _To, Presence) ->
+ case get_attribute_as_list(Presence, "type", "") of
+ "" ->
+ case get_element(Presence, "show") of
+ undefined ->
+ ?DEBUG("MOD_RAYO_GATEWAY: ignoring empty presence", []);
+ Show ->
+ case get_cdata_as_list(Show) of
+ "chat" ->
+ register_rayo_node(From);
+ "dnd" ->
+ unregister_rayo_node(From);
+ "xa" ->
+ unregister_rayo_node(From);
+ "" ->
+ unregister_rayo_node(From)
+ end
+ end;
+ "unavailable" ->
+ %TODO broadcast end instead?
+ unregister_rayo_node(From)
+ end,
+ ok.
+
+% Process presence from call
+route_call_presence(From, _To, Presence) ->
+ %TODO join/unjoin mixer events
+ case get_attribute_as_list(Presence, "type", "") of
+ "" ->
+ case get_element(Presence, ?NS_RAYO, "offer") of
+ undefined ->
+ route_rayo_entity_stanza(From, Presence);
+ _ ->
+ route_offer_call(From, Presence)
+ end;
+ "unavailable" ->
+ case get_element(Presence, ?NS_RAYO, "end") of
+ undefined ->
+ route_rayo_entity_stanza(From, Presence);
+ _ ->
+ route_rayo_entity_stanza(From, Presence),
+ unregister_rayo_node_entity(create_external_jid(From))
+ end
+ end,
+ ok.
+
+% presence from node
+route_server_presence({jid, [], _Domain, [], [], _LDomain, []} = From, To, Presence) ->
+ route_rayo_node_presence(From, To, Presence),
+ ok;
+
+% presence from call/mixer
+route_server_presence(From, To, Presence) ->
+ % TODO mixer
+ route_call_presence(From, To, Presence),
+ ok.
+
+% presence from Rayo Client
+route_client_presence(From, _To, Presence) ->
+ case get_attribute_as_list(Presence, "type", "") of
+ "" ->
+ case get_element(Presence, "show") of
+ undefined ->
+ ?DEBUG("MOD_RAYO_GATEWAY: ignoring empty presence", []);
+ Show ->
+ case get_cdata_as_list(Show) of
+ "chat" ->
+ register_rayo_client(From);
+ "dnd" ->
+ unregister_rayo_client(From);
+ _ ->
+ unregister_rayo_client(From)
+ end
+ end;
+ "unavailable" ->
+ unregister_rayo_client(From);
+ _ ->
+ ok
+ end,
+ ok.
+
+% route client directed presence to mixer
+route_client_presence_to_mixer(_From, _To, _Presence) ->
+ % TODO
+ ok.
+
+% Handle offer to client
+route_offer_call(From, Offer) ->
+ % Any clients available?
+ case pick_client() of
+ none ->
+ % TODO reject?
+ ok;
+ ClientDcp ->
+ % Remember call
+ ExtFrom = create_external_jid(From),
+ register_rayo_node_entity(ExtFrom, From, ClientDcp, call),
+ ejabberd_router:route(ExtFrom, ClientDcp, Offer)
+ end,
+ ok.
+
+% convert URI to a JID
+uri_to_jid(Uri) ->
+ JidString = case string:str(Uri, "xmpp:") of
+ 1 ->
+ string:substr(Uri, 6);
+ _ ->
+ Uri
+ end,
+ jlib:string_to_jid(JidString).
+
+% convert internal IQ reply to an external reply
+create_external_iq_reply(OrigIQ, {xmlelement, _, _, Els} = IQReply) ->
+ IQId = get_attribute_as_list(OrigIQ, "id", ""),
+ IQType = get_attribute_as_list(IQReply, "type", ""),
+ {xmlelement, "iq", [{"id", IQId}, {"type", IQType}], Els}.
+
+% Process dial response
+route_dial_call_response(OrigFrom, OrigTo, OrigIQ, timeout) ->
+ % TODO retry on different node?
+ route_iq_response(OrigFrom, OrigTo, OrigIQ, timeout);
+
+route_dial_call_response(OrigFrom, OrigTo, OrigIQ, IQReply) ->
+ ?DEBUG("MOD_RAYO_GATEWAY: IQ response for ~p", [OrigIQ]),
+ IQReplyPacket = jlib:iq_to_xml(IQReply),
+ case get_element(IQReplyPacket, "error") of
+ undefined ->
+ case get_element(IQReplyPacket, "ref") of
+ undefined ->
+ ok;
+ Ref ->
+ IntJid = uri_to_jid(get_attribute_as_list(Ref, "uri", "")),
+ register_rayo_node_entity(create_external_jid(IntJid), IntJid, OrigFrom, call)
+ end;
+ _ ->
+ ok
+ end,
+ ejabberd_router:route(OrigTo, OrigFrom, create_external_iq_reply(OrigIQ, IQReplyPacket)),
+ ok.
+
+% Forward dial to node
+route_dial_call(From, To, Dial) ->
+ % any nodes available?
+ case num_rayo_nodes() > 0 of
+ true ->
+ IntFrom = internal_client(),
+ case pick_rayo_node() of
+ none ->
+ route_error_reply(To, From, Dial, ?ERR_SERVICE_UNAVAILABLE);
+ NodeJid ->
+ route_iq_request(IntFrom, NodeJid, Dial, fun(IQReply) -> route_dial_call_response(From, To, Dial, IQReply) end)
+ end;
+ _ ->
+ route_error_reply(To, From, Dial, ?ERR_RESOURCE_CONSTRAINT)
+ end.
+
+% return configuration value given name
+config_value(Name) ->
+ case catch mnesia:dirty_read(rayo_config, Name) of
+ [{rayo_config, Name, Value}] ->
+ Value;
+ _ ->
+ ""
+ end.
+
+% return internal client name
+internal_client() ->
+ jlib:string_to_jid(config_value("internal_client")).
+
+% return internal domain name
+internal_domain() ->
+ jlib:string_to_jid(config_value("internal_domain")).
+
+% return external domain name
+external_domain() ->
+ jlib:string_to_jid(config_value("external_domain")).
+
+% return number of registered clients
+num_clients() ->
+ mnesia:table_info(rayo_clients, size).
+
+% return all registered client JIDs
+all_clients() ->
+ case mnesia:transaction(fun() -> mnesia:all_keys(rayo_clients) end) of
+ {atomic, Keys} ->
+ Keys;
+ _ ->
+ []
+ end.
+
+% pick a registered client
+pick_client() ->
+ % pick at random for now...
+ case all_clients() of
+ [] ->
+ none;
+ AllClients ->
+ lists:nth(random:uniform(length(AllClients)), AllClients)
+ end.
+
+% pick a registered node
+pick_rayo_node() ->
+ % pick at random for now...
+ case all_rayo_nodes() of
+ [] ->
+ none;
+ AllNodes ->
+ lists:nth(random:uniform(length(AllNodes)), AllNodes)
+ end.
+
+% return number of registered rayo nodes
+num_rayo_nodes() ->
+ mnesia:table_info(rayo_nodes, size).
+
+% return all rayo node JIDs
+all_rayo_nodes() ->
+ case mnesia:transaction(fun() -> mnesia:all_keys(rayo_nodes) end) of
+ {atomic, Keys} ->
+ Keys;
+ _ ->
+ []
+ end.
+
+presence(Status) ->
+ {xmlelement, "presence", [], [
+ {xmlelement, "show", [], [
+ {xmlcdata, Status}
+ ]}
+ ]}.
+
+online_presence() ->
+ presence(<<"chat">>).
+
+offline_presence() ->
+ presence(<<"dnd">>).
+
+route_to_list(From, ToList, Stanza) ->
+ lists:map(fun(To) -> ejabberd_router:route(From, To, Stanza) end, ToList),
+ ok.
+
+% route stanza from entity
+route_rayo_entity_stanza(From, Stanza) ->
+ case find_rayo_node_entity_dcp_by_int_jid(From) of
+ none ->
+ ?DEBUG("MOD_RAYO_GATEWAY: Failed to find DCP for ~p", [From]),
+ ok;
+ DcpJid ->
+ ejabberd_router:route(create_external_jid(From), DcpJid, Stanza)
+ end,
+ ok.
+
+% route IQ response from node to client
+route_iq_response(OrigFrom, OrigTo, OrigIQ, timeout) ->
+ route_error_reply(OrigTo, OrigFrom, OrigIQ, ?ERR_REMOTE_SERVER_TIMEOUT),
+ ok;
+
+route_iq_response(OrigFrom, OrigTo, OrigIQ, IQReply) ->
+ ?DEBUG("MOD_RAYO_GATEWAY: IQ response for ~p", [OrigIQ]),
+ ejabberd_router:route(OrigTo, OrigFrom, create_external_iq_reply(OrigIQ, jlib:iq_to_xml(IQReply))),
+ ok.
+
+% route IQ from client to node
+route_iq_request(From, To, {xmlelement, "iq", _Atts, Els}, ResponseCallback) ->
+ ejabberd_local:route_iq(From, To, #iq{type = set, sub_el = Els}, ResponseCallback),
+ ok.
+
+% route IQ error given request
+route_error_reply(From, To, IQ, Reason) ->
+ ejabberd_router:route(From, To, jlib:make_error_reply(IQ, Reason)),
+ ok.
+
+% route IQ result given request
+route_result_reply(From, To, IQ) ->
+ ejabberd_router:route(From, To, jlib:make_result_iq_reply(IQ)),
+ ok.
+
+% XML parsing helpers
+
+get_element(Element, Name) ->
+ case xml:get_subtag(Element, Name) of
+ false ->
+ undefined;
+ Subtag ->
+ Subtag
+ end.
+
+get_element(Element, NS, Name) ->
+ case get_element(Element, Name) of
+ undefined ->
+ undefined;
+ Subtag ->
+ case get_attribute_as_list(Subtag, "xmlns", "") of
+ "" ->
+ undefined;
+ NS ->
+ Subtag
+ end
+ end.
+
+get_cdata_as_list(undefined) ->
+ "";
+
+get_cdata_as_list(Element) ->
+ xml:get_tag_cdata(Element).
+
+get_element_cdata_as_list(Element, Name) ->
+ get_cdata_as_list(get_element(Element, Name)).
+
+get_element_cdata_as_list(Element, NS, Name) ->
+ get_cdata_as_list(get_element(Element, NS, Name)).
+
+get_element_attribute_as_list(Element, Name, AttrName, Default) ->
+ get_attribute_as_list(get_element(Element, Name), AttrName, Default).
+
+get_element_attribute_as_list(Element, NS, Name, AttrName, Default) ->
+ get_attribute_as_list(get_element(Element, NS, Name), AttrName, Default).
+
+get_attribute_as_list(undefined, _Name, _Default) ->
+ undefined;
+
+get_attribute_as_list(Element, Name, Default) ->
+ case xml:get_tag_attr_s(Name, Element) of
+ "" ->
+ Default;
+ Attr ->
+ Attr
+ end.
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * iks_helpers.c -- iksemel helpers
+ *
+ */
+#include "iks_helpers.h"
+#include <switch.h>
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+#undef XMPP_ERROR
+#define XMPP_ERROR(def_name, name, type) \
+ const struct xmpp_error def_name##_val = { name, type }; \
+ const struct xmpp_error *def_name = &def_name##_val;
+#include "xmpp_errors.def"
+
+/**
+ * Create a <presence> event
+ * @param name the event name
+ * @param namespace the event namespace
+ * @param from
+ * @param to
+ * @return the event XML node
+ */
+iks *iks_new_presence(const char *name, const char *namespace, const char *from, const char *to)
+{
+ iks *event = iks_new("presence");
+ iks *x;
+ /* iks makes copies of attrib name and value */
+ iks_insert_attrib(event, "from", from);
+ iks_insert_attrib(event, "to", to);
+ x = iks_insert(event, name);
+ if (!zstr(namespace)) {
+ iks_insert_attrib(x, "xmlns", namespace);
+ }
+ return event;
+}
+
+/**
+ * Create error response from request
+ * @param req the request
+ * @param from
+ * @param to
+ * @param err the XMPP stanza error
+ * @return the error response
+ */
+iks *iks_new_error(iks *req, const struct xmpp_error *err)
+{
+ iks *response = iks_copy(req);
+ iks *x;
+
+ /* <iq> */
+ iks_insert_attrib(response, "from", iks_find_attrib(req, "to"));
+ iks_insert_attrib(response, "to", iks_find_attrib(req, "from"));
+ iks_insert_attrib(response, "type", "error");
+
+ /* <error> */
+ x = iks_insert(response, "error");
+ iks_insert_attrib(x, "type", err->type);
+
+ /* e.g. <feature-not-implemented> */
+ x = iks_insert(x, err->name);
+ iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_STANZAS);
+
+ return response;
+}
+
+/**
+ * Create error response from request
+ * @param req the request
+ * @param from
+ * @param to
+ * @param err the XMPP stanza error
+ * @param detail_text optional text to include in message
+ * @return the <iq> error response
+ */
+iks *iks_new_error_detailed(iks *req, const struct xmpp_error *err, const char *detail_text)
+{
+ iks *reply = iks_new_error(req, err);
+ if (!zstr(detail_text)) {
+ iks *error = iks_find(reply, "error");
+ iks *text = iks_insert(error, "text");
+ iks_insert_attrib(text, "xml:lang", "en");
+ iks_insert_attrib(text, "xmlns", IKS_NS_XMPP_STANZAS);
+ iks_insert_cdata(text, detail_text, strlen(detail_text));
+ }
+ return reply;
+}
+
+/**
+ * Create error response from request
+ * @param req the request
+ * @param from
+ * @param to
+ * @param err the XMPP stanza error
+ * @param detail_text_format format string
+ * @param ...
+ * @return the error response
+ */
+iks *iks_new_error_detailed_printf(iks *req, const struct xmpp_error *err, const char *detail_text_format, ...)
+{
+ iks *reply = NULL;
+ char *data;
+ va_list ap;
+ int ret;
+
+ va_start(ap, detail_text_format);
+ ret = switch_vasprintf(&data, detail_text_format, ap);
+ va_end(ap);
+
+ if (ret == -1) {
+ return NULL;
+ }
+ reply = iks_new_error_detailed(req, err, data);
+ free(data);
+ return reply;
+}
+
+/**
+ * Create <iq> result response from request
+ * @param iq the request
+ * @return the result response
+ */
+iks *iks_new_iq_result(iks *iq)
+{
+ iks *response = iks_new("iq");
+ iks_insert_attrib(response, "from", iks_find_attrib(iq, "to"));
+ iks_insert_attrib(response, "to", iks_find_attrib(iq, "from"));
+ iks_insert_attrib(response, "type", "result");
+ iks_insert_attrib(response, "id", iks_find_attrib(iq, "id"));
+ return response;
+}
+
+/**
+ * Get attribute value of node, returning empty string if non-existent or not set.
+ * @param xml the XML node to search
+ * @param attrib the Attribute name
+ * @return the attribute value
+ */
+const char *iks_find_attrib_soft(iks *xml, const char *attrib)
+{
+ char *value = iks_find_attrib(xml, attrib);
+ return zstr(value) ? "" : value;
+}
+
+/**
+ * Get attribute value of node, returning default value if missing. The default value
+ * is set in the node if missing.
+ * @param xml the XML node to search
+ * @param attrib the Attribute name
+ * @return the attribute value
+ */
+const char *iks_find_attrib_default(iks *xml, const char *attrib, const char *def)
+{
+ char *value = iks_find_attrib(xml, attrib);
+ if (!value) {
+ iks_insert_attrib(xml, attrib, def);
+ return def;
+ }
+ return value;
+}
+
+/**
+ * Get attribute integer value of node
+ * @param xml the XML node to search
+ * @param attrib the Attribute name
+ * @return the attribute value
+ */
+int iks_find_int_attrib(iks *xml, const char *attrib)
+{
+ return atoi(iks_find_attrib_soft(xml, attrib));
+}
+
+/**
+ * Get attribute boolean value of node
+ * @param xml the XML node to search
+ * @param attrib the Attribute name
+ * @return the attribute value
+ */
+int iks_find_bool_attrib(iks *xml, const char *attrib)
+{
+ return switch_true(iks_find_attrib_soft(xml, attrib));
+}
+
+/**
+ * Get attribute double value of node
+ * @param xml the XML node to search
+ * @param attrib the Attribute name
+ * @return the attribute value
+ */
+double iks_find_decimal_attrib(iks *xml, const char *attrib)
+{
+ return atof(iks_find_attrib_soft(xml, attrib));
+}
+
+/**
+ * Convert iksemel XML node type to string
+ * @param type the XML node type
+ * @return the string value of type or "UNKNOWN"
+ */
+const char *iks_node_type_to_string(int type)
+{
+ switch(type) {
+ case IKS_NODE_START: return "NODE_START";
+ case IKS_NODE_NORMAL: return "NODE_NORMAL";
+ case IKS_NODE_ERROR: return "NODE_ERROR";
+ case IKS_NODE_STOP: return "NODE_START";
+ default: return "NODE_UNKNOWN";
+ }
+}
+
+/**
+ * Convert iksemel error code to string
+ * @param err the iksemel error code
+ * @return the string value of error or "UNKNOWN"
+ */
+const char *iks_net_error_to_string(int err)
+{
+ switch (err) {
+ case IKS_OK: return "OK";
+ case IKS_NOMEM: return "NOMEM";
+ case IKS_BADXML: return "BADXML";
+ case IKS_HOOK: return "HOOK";
+ case IKS_NET_NODNS: return "NET_NODNS";
+ case IKS_NET_NOSOCK: return "NET_NOSOCK";
+ case IKS_NET_NOCONN: return "NET_NOCONN";
+ case IKS_NET_RWERR: return "NET_RWERR";
+ case IKS_NET_NOTSUPP: return "NET_NOTSUPP";
+ case IKS_NET_TLSFAIL: return "NET_TLSFAIL";
+ case IKS_NET_DROPPED: return "NET_DROPPED";
+ case IKS_NET_UNKNOWN: return "NET_UNKNOWN";
+ default: return "UNKNOWN";
+ }
+}
+
+/**
+ * Insert attribute using format string
+ * @param xml node to insert attribute into
+ * @param name of attribute
+ * @param fmt format string
+ * @param ... format string args
+ */
+iks *iks_insert_attrib_printf(iks *xml, const char *name, const char *fmt, ...)
+{
+ iks *node;
+ char *data;
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = switch_vasprintf(&data, fmt, ap);
+ va_end(ap);
+
+ if (ret == -1) {
+ return NULL;
+ }
+ node = iks_insert_attrib(xml, name, data);
+ free(data);
+
+ return node;
+}
+
+/**
+ * @param value to match
+ * @param rule to check
+ * @return true if value is one of the comma-separated values in rule
+ */
+int value_matches(const char *value, const char *rule)
+{
+ if (rule && *rule && value && *value && !strchr(value, ',')) {
+ const char *begin = strstr(rule, value);
+ const char *end = begin + strlen(value);
+ if (!begin) {
+ return 0;
+ }
+ if ((begin == rule || *(begin - 1) == ',') && (*end == ',' || *end == '\0')) {
+ return 1;
+ }
+ /* substring matched... try farther down the string */
+ return value_matches(value, end);
+ }
+ return 0;
+}
+
+#define IKS_SHA256_HEX_DIGEST_LENGTH ((SHA256_DIGEST_LENGTH * 2) + 1)
+
+/**
+ * Convert hash to a hex string.
+ * @param hash hash to convert
+ * @param str buffer to store hash - this buffer must be hashlen * 2 + 1 in size.
+ */
+static void iks_hash_to_hex_string(unsigned char *hash, int hashlen, unsigned char *str)
+{
+ static const char *HEX = "0123456789abcdef";
+ int i;
+
+ /* convert to hex string with in-place algorithm */
+ for (i = hashlen - 1; i >= 0; i--) {
+ str[i * 2 + 1] = HEX[hash[i] & 0x0f];
+ str[i * 2] = HEX[(hash[i] >> 4) & 0x0f];
+ }
+ str[hashlen * 2] = '\0';
+}
+
+/**
+ * Generate SHA-256 hash of value as hex string
+ * @param data to hash
+ * @param datalen length of data to hash
+ * @return hash as a hex string
+ */
+static void iks_sha256_hex_string(const unsigned char *data, int datalen, unsigned char *hash)
+{
+ /* hash data */
+ SHA256(data, datalen, hash);
+ iks_hash_to_hex_string(hash, SHA256_DIGEST_LENGTH, hash);
+}
+
+/**
+ * Generate HMAC SHA-256
+ * @param key the key
+ * @param keylen length of key
+ * @param message the message
+ * @param messagelen length of message
+ * @param hash buffer to store the hash - must be IKS_SHA256_HEX_DIGEST_LENGTH
+ */
+static void iks_hmac_sha256_hex_string(const unsigned char *key, int keylen, const unsigned char *message, int messagelen, unsigned char *hash)
+{
+ unsigned int hash_len = SHA256_DIGEST_LENGTH;
+ HMAC(EVP_sha256(), key, keylen, message, messagelen, hash, &hash_len);
+ iks_hash_to_hex_string(hash, SHA256_DIGEST_LENGTH, hash);
+}
+
+/**
+ * Generate server dialback key. free() the returned value
+ * @param secret originating server shared secret
+ * @param receiving_server domain
+ * @param originating_server domain
+ * @param stream_id stream ID
+ * @return the dialback key
+ */
+char *iks_server_dialback_key(const char *secret, const char *receiving_server, const char *originating_server, const char *stream_id)
+{
+ if (!zstr(secret) && !zstr(receiving_server) && !zstr(originating_server) && !zstr(stream_id)) {
+ unsigned char secret_hash[IKS_SHA256_HEX_DIGEST_LENGTH];
+ unsigned char *message = NULL;
+ unsigned char *dialback_key = malloc(sizeof(unsigned char *) * IKS_SHA256_HEX_DIGEST_LENGTH);
+ iks_sha256_hex_string((unsigned char *)secret, strlen(secret), secret_hash);
+ message = (unsigned char *)switch_mprintf("%s %s %s", receiving_server, originating_server, stream_id);
+ iks_hmac_sha256_hex_string(secret_hash, strlen((char *)secret_hash), message, strlen((char *)message), dialback_key);
+ free(message);
+ return (char *)dialback_key;
+ }
+ return NULL;
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * iks_helpers.h -- iksemel constants and helpers
+ *
+ */
+#ifndef IKS_EXT_H
+#define IKS_EXT_H
+
+#include <iksemel.h>
+#include <switch.h>
+
+#define IKS_JABBER_SERVER_PORT 5269
+
+#define IKS_NS_XMPP_DISCO "http://jabber.org/protocol/disco#info"
+#define IKS_NS_XMPP_PING "urn:xmpp:ping"
+#define IKS_NS_XMPP_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas"
+#define IKS_NS_XMPP_STREAMS "http://etherx.jabber.org/streams"
+#define IKS_NS_XMPP_DIALBACK "jabber:server:dialback"
+#define IKS_NS_BIDI_FEATURE "urn:xmpp:features:bidi"
+#define IKS_NS_BIDI "urn:xmpp:bidi"
+
+struct xmpp_error {
+ const char *name;
+ const char *type;
+};
+
+#undef XMPP_ERROR
+#define XMPP_ERROR(def_name, name, type) \
+ extern const struct xmpp_error def_name##_val; \
+ extern const struct xmpp_error *def_name;
+#include "xmpp_errors.def"
+
+/* See RFC-3920 XMPP core for error definitions */
+
+extern iks *iks_new_presence(const char *name, const char *namespace, const char *from, const char *to);
+extern iks *iks_new_error(iks *iq, const struct xmpp_error *err);
+extern iks *iks_new_error_detailed(iks *iq, const struct xmpp_error *err, const char *detail_text);
+extern iks *iks_new_error_detailed_printf(iks *iq, const struct xmpp_error *err, const char *detail_text_format, ...);
+extern iks *iks_new_iq_result(iks *iq);
+extern const char *iks_find_attrib_soft(iks *xml, const char *attrib);
+extern const char *iks_find_attrib_default(iks *xml, const char *attrib, const char *def);
+extern int iks_find_bool_attrib(iks *xml, const char *attrib);
+extern int iks_find_int_attrib(iks *xml, const char *attrib);
+extern double iks_find_decimal_attrib(iks *xml, const char *attrib);
+extern const char *iks_node_type_to_string(int type);
+extern const char *iks_net_error_to_string(int err);
+extern iks *iks_insert_attrib_printf(iks *xml, const char *name, const char *fmt, ...);
+
+extern char *iks_server_dialback_key(const char *secret, const char *receiving_server, const char *originating_server, const char *stream_id);
+
+/** A function to validate attribute value */
+typedef int (*iks_attrib_validation_function)(const char *);
+
+#define ELEMENT(name) inline int VALIDATE_##name(iks *node) { int result = 1; if (!node) return 0;
+#define ATTRIB(name, def, rule) result &= iks_attrib_is_##rule(iks_find_attrib_default(node, #name, #def));
+#define STRING_ATTRIB(name, def, rule) result &= value_matches(iks_find_attrib_default(node, #name, #def), rule);
+#define ELEMENT_END return result; }
+
+extern int value_matches(const char *value, const char *rule);
+
+#define ATTRIB_RULE(rule) inline int iks_attrib_is_ ## rule (const char *value)
+
+/**
+ * Validate boolean
+ * @param value
+ * @return SWTICH_TRUE if boolean
+ */
+ATTRIB_RULE(bool)
+{
+ if (value && *value && (!strcasecmp("true", value) || !strcasecmp("false", value))) {
+ return SWITCH_TRUE;
+ }
+ return SWITCH_FALSE;
+}
+
+/**
+ * Validate integer
+ * @param value
+ * @return SWTICH_TRUE if not negative
+ */
+ATTRIB_RULE(not_negative)
+{
+ if (value && *value && switch_is_number(value)) {
+ int value_i = atoi(value);
+ if (value_i >= 0) {
+ return SWITCH_TRUE;
+ }
+ }
+ return SWITCH_FALSE;
+}
+
+/**
+ * Validate integer
+ * @param value
+ * @return SWTICH_TRUE if positive
+ */
+ATTRIB_RULE(positive)
+{
+ if (value && *value && switch_is_number(value)) {
+ int value_i = atoi(value);
+ if (value_i > 0) {
+ return SWITCH_TRUE;
+ }
+ }
+ return SWITCH_FALSE;
+}
+
+/**
+ * Validate integer
+ * @param value
+ * @return SWTICH_TRUE if positive or -1
+ */
+ATTRIB_RULE(positive_or_neg_one)
+{
+ if (value && *value && switch_is_number(value)) {
+ int value_i = atoi(value);
+ if (value_i == -1 || value_i > 0) {
+ return SWITCH_TRUE;
+ }
+ }
+ return SWITCH_FALSE;
+}
+
+/**
+ * Validate string
+ * @param value
+ * @return SWTICH_TRUE
+ */
+ATTRIB_RULE(any)
+{
+ return SWITCH_TRUE;
+}
+
+/**
+ * Validate decimal
+ * @param value
+ * @return SWTICH_TRUE if 0.0 <= x <= 1.0
+ */
+ATTRIB_RULE(decimal_between_zero_and_one)
+{
+ if (value && *value && switch_is_number(value)) {
+ double value_d = atof(value);
+ if (value_d >= 0.0 || value_d <= 1.0) {
+ return SWITCH_TRUE;
+ }
+ }
+ return SWITCH_FALSE;
+}
+
+#endif
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * mod_rayo.c -- Rayo server / node implementation. Allows MxN clustering of FreeSWITCH and Rayo Clients (like Adhearsion)
+ *
+ */
+#include <switch.h>
+#include <iksemel.h>
+
+#include "mod_rayo.h"
+#include "rayo_components.h"
+#include "rayo_elements.h"
+#include "xmpp_streams.h"
+
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_rayo_shutdown);
+SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load);
+SWITCH_MODULE_DEFINITION(mod_rayo, mod_rayo_load, mod_rayo_shutdown, NULL);
+
+#define RAYO_CAUSE_HANGUP SWITCH_CAUSE_NORMAL_CLEARING
+#define RAYO_CAUSE_DECLINE SWITCH_CAUSE_CALL_REJECTED
+#define RAYO_CAUSE_BUSY SWITCH_CAUSE_USER_BUSY
+#define RAYO_CAUSE_ERROR SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE
+
+#define RAYO_END_REASON_HANGUP "hangup"
+#define RAYO_END_REASON_ERROR "error"
+#define RAYO_END_REASON_BUSY "busy"
+#define RAYO_END_REASON_REJECT "reject"
+#define RAYO_END_REASON_TIMEOUT "timeout"
+
+#define RAYO_SIP_REQUEST_HEADER "sip_r_"
+#define RAYO_SIP_RESPONSE_HEADER "sip_rh_"
+#define RAYO_SIP_PROVISIONAL_RESPONSE_HEADER "sip_ph_"
+#define RAYO_SIP_BYE_RESPONSE_HEADER "sip_bye_h_"
+
+#define RAYO_CONFIG_FILE "rayo.conf"
+
+struct rayo_actor;
+struct rayo_client;
+struct rayo_call;
+
+#define rayo_call_get_uuid(call) RAYO_ID(call)
+
+/**
+ * Function pointer wrapper for the handlers hash
+ */
+struct rayo_xmpp_handler {
+ enum rayo_actor_type from_type;
+ const char *from_subtype;
+ enum rayo_actor_type to_type;
+ const char *to_subtype;
+ rayo_actor_xmpp_handler fn;
+};
+
+/**
+ * Client availability
+ */
+enum presence_status {
+ PS_UNKNOWN = -1,
+ PS_OFFLINE = 0,
+ PS_ONLINE = 1
+};
+
+/**
+ * A xmpp peer server that routes messages to/from clients
+ */
+struct rayo_peer_server {
+ /** base class */
+ struct rayo_actor base;
+ /** clients connected via this server */
+ switch_hash_t *clients;
+};
+#define RAYO_PEER_SERVER(x) ((struct rayo_peer_server *)x)
+
+/**
+ * A Rayo client that controls calls
+ */
+struct rayo_client {
+ /** base class */
+ struct rayo_actor base;
+ /** availability */
+ enum presence_status availability;
+ /** true if superuser */
+ int is_admin;
+ /** set if reachable via s2s */
+ struct rayo_peer_server *peer_server;
+ /** domain or full JID to route to */
+ const char *route;
+ /** time when last probe was sent */
+ switch_time_t last_probe;
+};
+#define RAYO_CLIENT(x) ((struct rayo_client *)x)
+
+/**
+ * A call controlled by a Rayo client
+ */
+struct rayo_call {
+ /** actor base class */
+ struct rayo_actor base;
+ /** Definitive controlling party JID */
+ char *dcp_jid;
+ /** Potential controlling parties */
+ switch_hash_t *pcps;
+ /** current idle start time */
+ switch_time_t idle_start_time;
+ /** true if joined */
+ int joined;
+ /** set if response needs to be sent to IQ request */
+ const char *dial_id;
+ /** channel destroy event */
+ switch_event_t *end_event;
+};
+
+/**
+ * A conference
+ */
+struct rayo_mixer {
+ /** actor base class */
+ struct rayo_actor base;
+ /** member JIDs */
+ switch_hash_t *members;
+ /** subscriber JIDs */
+ switch_hash_t *subscribers;
+};
+
+/**
+ * A member of a mixer
+ */
+struct rayo_mixer_member {
+ /** JID of member */
+ const char *jid;
+ /** Controlling party JID */
+ const char *dcp_jid;
+};
+
+/**
+ * A subscriber to mixer events
+ */
+struct rayo_mixer_subscriber {
+ /** JID of subscriber */
+ const char *jid;
+ /** Number of controlled parties in mixer */
+ int ref_count;
+};
+
+/**
+ * Module state
+ */
+static struct {
+ /** module memory pool */
+ switch_memory_pool_t *pool;
+ /** Rayo <iq> set commands mapped to functions */
+ switch_hash_t *command_handlers;
+ /** Rayo <presence> events mapped to functions */
+ switch_hash_t *event_handlers;
+ /** Active Rayo actors mapped by JID */
+ switch_hash_t *actors;
+ /** Rayo actors pending destruction */
+ switch_hash_t *destroy_actors;
+ /** Active Rayo actors mapped by internal ID */
+ switch_hash_t *actors_by_id;
+ /** synchronizes access to actors */
+ switch_mutex_t *actors_mutex;
+ /** map of DCP JID to client */
+ switch_hash_t *clients_roster;
+ /** synchronizes access to available clients */
+ switch_mutex_t *clients_mutex;
+ /** server for calls/mixers/etc */
+ struct rayo_actor *server;
+ /** Maximum idle time before call is considered abandoned */
+ int max_idle_ms;
+ /** Conference profile to use for mixers */
+ char *mixer_conf_profile;
+ /** to URI prefixes mapped to gateways */
+ switch_hash_t *dial_gateways;
+ /** console command aliases */
+ switch_hash_t *cmd_aliases;
+ /** global console */
+ struct rayo_client *console;
+ /** XMPP context */
+ struct xmpp_stream_context *xmpp_context;
+} globals;
+
+/**
+ * An outbound dial gateway
+ */
+struct dial_gateway {
+ /** URI prefix to match */
+ const char *uri_prefix;
+ /** dial prefix to match */
+ const char *dial_prefix;
+ /** number of digits to strip from dialstring */
+ int strip;
+};
+
+static struct rayo_message *rayo_call_send(struct rayo_actor *client, struct rayo_actor *call, struct rayo_message *msg, const char *file, int line);
+static struct rayo_message *rayo_server_send(struct rayo_actor *client, struct rayo_actor *server, struct rayo_message *msg, const char *file, int line);
+static struct rayo_message *rayo_mixer_send(struct rayo_actor *client, struct rayo_actor *mixer, struct rayo_message *msg, const char *file, int line);
+static struct rayo_message *rayo_component_send(struct rayo_actor *client, struct rayo_actor *component, struct rayo_message *msg, const char *file, int line);
+static struct rayo_message *rayo_client_send(struct rayo_actor *from, struct rayo_actor *client, struct rayo_message *msg, const char *file, int line);
+static struct rayo_message *rayo_console_client_send(struct rayo_actor *from, struct rayo_actor *client, struct rayo_message *msg, const char *file, int line);
+
+static void on_client_presence(struct rayo_client *rclient, iks *node);
+
+
+/**
+ * Presence status
+ * @param status the presence status
+ * @return the string value of status
+ */
+static const char *presence_status_to_string(enum presence_status status)
+{
+ switch(status) {
+ case PS_OFFLINE: return "OFFLINE";
+ case PS_ONLINE: return "ONLINE";
+ case PS_UNKNOWN:
+ default: return "UNKNOWN";
+ }
+ return "UNKNOWN";
+}
+
+/**
+ * Convert Rayo actor type to string
+ * @param type the Rayo actor type
+ * @return the string value of type or "UNKNOWN"
+ */
+static const char *rayo_actor_type_to_string(enum rayo_actor_type type)
+{
+ switch(type) {
+ case RAT_PEER_SERVER: return "PEER_SERVER";
+ case RAT_CLIENT: return "CLIENT";
+ case RAT_CALL: return "CALL";
+ case RAT_CALL_COMPONENT: return "CALL_COMPONENT";
+ case RAT_MIXER: return "MIXER";
+ case RAT_MIXER_COMPONENT: return "MIXER_COMPONENT";
+ case RAT_SERVER: return "SERVER";
+ }
+ return "UNKNOWN";
+}
+
+/**
+ * Get rayo cause code from FS hangup cause
+ * @param cause FS hangup cause
+ * @return rayo end cause
+ */
+static const char *switch_cause_to_rayo_cause(switch_call_cause_t cause)
+{
+ switch (cause) {
+ case SWITCH_CAUSE_NONE:
+ case SWITCH_CAUSE_NORMAL_CLEARING:
+ return RAYO_END_REASON_HANGUP;
+
+ case SWITCH_CAUSE_UNALLOCATED_NUMBER:
+ case SWITCH_CAUSE_NO_ROUTE_TRANSIT_NET:
+ case SWITCH_CAUSE_NO_ROUTE_DESTINATION:
+ case SWITCH_CAUSE_CHANNEL_UNACCEPTABLE:
+ return RAYO_END_REASON_ERROR;
+
+ case SWITCH_CAUSE_CALL_AWARDED_DELIVERED:
+ return RAYO_END_REASON_HANGUP;
+
+ case SWITCH_CAUSE_USER_BUSY:
+ return RAYO_END_REASON_BUSY;
+
+ case SWITCH_CAUSE_NO_USER_RESPONSE:
+ case SWITCH_CAUSE_NO_ANSWER:
+ return RAYO_END_REASON_TIMEOUT;
+
+ case SWITCH_CAUSE_SUBSCRIBER_ABSENT:
+ return RAYO_END_REASON_ERROR;
+
+ case SWITCH_CAUSE_CALL_REJECTED:
+ return RAYO_END_REASON_REJECT;
+
+ case SWITCH_CAUSE_NUMBER_CHANGED:
+ case SWITCH_CAUSE_REDIRECTION_TO_NEW_DESTINATION:
+ case SWITCH_CAUSE_EXCHANGE_ROUTING_ERROR:
+ case SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER:
+ case SWITCH_CAUSE_INVALID_NUMBER_FORMAT:
+ return RAYO_END_REASON_ERROR;
+
+ case SWITCH_CAUSE_FACILITY_REJECTED:
+ return RAYO_END_REASON_REJECT;
+
+ case SWITCH_CAUSE_RESPONSE_TO_STATUS_ENQUIRY:
+ case SWITCH_CAUSE_NORMAL_UNSPECIFIED:
+ return RAYO_END_REASON_HANGUP;
+
+ case SWITCH_CAUSE_NORMAL_CIRCUIT_CONGESTION:
+ case SWITCH_CAUSE_NETWORK_OUT_OF_ORDER:
+ case SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE:
+ case SWITCH_CAUSE_SWITCH_CONGESTION:
+ case SWITCH_CAUSE_ACCESS_INFO_DISCARDED:
+ case SWITCH_CAUSE_REQUESTED_CHAN_UNAVAIL:
+ case SWITCH_CAUSE_PRE_EMPTED:
+ case SWITCH_CAUSE_FACILITY_NOT_SUBSCRIBED:
+ case SWITCH_CAUSE_OUTGOING_CALL_BARRED:
+ case SWITCH_CAUSE_INCOMING_CALL_BARRED:
+ case SWITCH_CAUSE_BEARERCAPABILITY_NOTAUTH:
+ case SWITCH_CAUSE_BEARERCAPABILITY_NOTAVAIL:
+ case SWITCH_CAUSE_SERVICE_UNAVAILABLE:
+ case SWITCH_CAUSE_BEARERCAPABILITY_NOTIMPL:
+ case SWITCH_CAUSE_CHAN_NOT_IMPLEMENTED:
+ case SWITCH_CAUSE_FACILITY_NOT_IMPLEMENTED:
+ case SWITCH_CAUSE_SERVICE_NOT_IMPLEMENTED:
+ case SWITCH_CAUSE_INVALID_CALL_REFERENCE:
+ case SWITCH_CAUSE_INCOMPATIBLE_DESTINATION:
+ case SWITCH_CAUSE_INVALID_MSG_UNSPECIFIED:
+ case SWITCH_CAUSE_MANDATORY_IE_MISSING:
+ return RAYO_END_REASON_ERROR;
+
+ case SWITCH_CAUSE_MESSAGE_TYPE_NONEXIST:
+ case SWITCH_CAUSE_WRONG_MESSAGE:
+ case SWITCH_CAUSE_IE_NONEXIST:
+ case SWITCH_CAUSE_INVALID_IE_CONTENTS:
+ case SWITCH_CAUSE_WRONG_CALL_STATE:
+ case SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE:
+ case SWITCH_CAUSE_MANDATORY_IE_LENGTH_ERROR:
+ case SWITCH_CAUSE_PROTOCOL_ERROR:
+ return RAYO_END_REASON_ERROR;
+
+ case SWITCH_CAUSE_INTERWORKING:
+ case SWITCH_CAUSE_SUCCESS:
+ case SWITCH_CAUSE_ORIGINATOR_CANCEL:
+ return RAYO_END_REASON_HANGUP;
+
+ case SWITCH_CAUSE_CRASH:
+ case SWITCH_CAUSE_SYSTEM_SHUTDOWN:
+ case SWITCH_CAUSE_LOSE_RACE:
+ case SWITCH_CAUSE_MANAGER_REQUEST:
+ case SWITCH_CAUSE_BLIND_TRANSFER:
+ case SWITCH_CAUSE_ATTENDED_TRANSFER:
+ case SWITCH_CAUSE_ALLOTTED_TIMEOUT:
+ case SWITCH_CAUSE_USER_CHALLENGE:
+ case SWITCH_CAUSE_MEDIA_TIMEOUT:
+ case SWITCH_CAUSE_PICKED_OFF:
+ case SWITCH_CAUSE_USER_NOT_REGISTERED:
+ case SWITCH_CAUSE_PROGRESS_TIMEOUT:
+ case SWITCH_CAUSE_INVALID_GATEWAY:
+ case SWITCH_CAUSE_GATEWAY_DOWN:
+ case SWITCH_CAUSE_INVALID_URL:
+ case SWITCH_CAUSE_INVALID_PROFILE:
+ case SWITCH_CAUSE_NO_PICKUP:
+ return RAYO_END_REASON_ERROR;
+ }
+ return RAYO_END_REASON_HANGUP;
+}
+
+/**
+ * Add <header> to node
+ * @param node to add <header> to
+ * @param name of header
+ * @param value of header
+ */
+static void add_header(iks *node, const char *name, const char *value)
+{
+ if (!zstr(name) && !zstr(value)) {
+ iks *header = iks_insert(node, "header");
+ iks_insert_attrib(header, "name", name);
+ iks_insert_attrib(header, "value", value);
+ }
+}
+
+/**
+ * Add an outbound dialing gateway
+ * @param uri_prefix to match
+ * @param dial_prefix to use
+ * @param strip number of digits to strip from dialstring
+ */
+static void dial_gateway_add(const char *uri_prefix, const char *dial_prefix, int strip)
+{
+ struct dial_gateway *gateway = switch_core_alloc(globals.pool, sizeof(*gateway));
+ gateway->uri_prefix = uri_prefix ? switch_core_strdup(globals.pool, uri_prefix) : "";
+ gateway->dial_prefix = dial_prefix ? switch_core_strdup(globals.pool, dial_prefix) : "";
+ gateway->strip = strip > 0 ? strip : 0;
+ switch_core_hash_insert(globals.dial_gateways, uri_prefix, gateway);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "dial-gateway uriprefix = %s, dialprefix = %s, strip = %i\n", uri_prefix, dial_prefix, strip);
+}
+
+/**
+ * Find outbound dial gateway for the specified dialstring
+ */
+static struct dial_gateway *dial_gateway_find(const char *uri)
+{
+ switch_hash_index_t *hi = NULL;
+ int match_len = 0;
+ struct dial_gateway *gateway = (struct dial_gateway *)switch_core_hash_find(globals.dial_gateways, "default");
+
+ /* find longest prefix match */
+ for (hi = switch_core_hash_first(globals.dial_gateways); hi; hi = switch_core_hash_next(hi)) {
+ struct dial_gateway *candidate = NULL;
+ const void *prefix;
+ int prefix_len = 0;
+ void *val;
+ switch_core_hash_this(hi, &prefix, NULL, &val);
+ candidate = (struct dial_gateway *)val;
+ switch_assert(candidate);
+
+ prefix_len = strlen(prefix);
+ if (!zstr(prefix) && !strncmp(prefix, uri, prefix_len) && prefix_len > match_len) {
+ match_len = prefix_len;
+ gateway = candidate;
+ }
+ }
+ return gateway;
+}
+
+/**
+ * Add command handler function
+ * @param name the command name
+ * @param handler the command handler function
+ */
+static void rayo_command_handler_add(const char *name, struct rayo_xmpp_handler *handler)
+{
+ char full_name[1024];
+ full_name[1023] = '\0';
+ snprintf(full_name, sizeof(full_name) - 1, "%i:%s:%s", handler->to_type, handler->to_subtype, name);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding command: %s\n", full_name);
+ switch_core_hash_insert(globals.command_handlers, full_name, handler);
+}
+
+/**
+ * Add command handler function
+ * @param type the actor type
+ * @param subtype the actor subtype
+ * @param name the command name
+ * @param fn the command callback function
+ */
+void rayo_actor_command_handler_add(enum rayo_actor_type type, const char *subtype, const char *name, rayo_actor_xmpp_handler fn)
+{
+ struct rayo_xmpp_handler *handler = switch_core_alloc(globals.pool, sizeof (*handler));
+ handler->to_type = type;
+ handler->to_subtype = zstr(subtype) ? "" : switch_core_strdup(globals.pool, subtype);
+ handler->fn = fn;
+ rayo_command_handler_add(name, handler);
+}
+
+/**
+ * Get command handler function from hash
+ * @param hash the hash to search
+ * @param iq
+ * @return the command handler function or NULL
+ */
+rayo_actor_xmpp_handler rayo_actor_command_handler_find(struct rayo_actor *actor, iks *iq)
+{
+ const char *iq_type = iks_find_attrib_soft(iq, "type");
+ iks *command = iks_first_tag(iq);
+ const char *name = "";
+ const char *namespace = "";
+ struct rayo_xmpp_handler *handler = NULL;
+ char full_name[1024];
+
+ full_name[1023] = '\0';
+ if (command) {
+ name = iks_name(command);
+ namespace = iks_find_attrib_soft(command, "xmlns");
+ if (zstr(name)) {
+ name = "";
+ }
+ }
+
+ snprintf(full_name, sizeof(full_name) - 1, "%i:%s:%s:%s:%s", actor->type, actor->subtype, iq_type, namespace, name);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, looking for %s command\n", RAYO_JID(actor), full_name);
+ handler = (struct rayo_xmpp_handler *)switch_core_hash_find(globals.command_handlers, full_name);
+ if (handler) {
+ return handler->fn;
+ }
+
+ return NULL;
+}
+
+/**
+ * Add event handler function
+ * @param name the event name
+ * @param handler the event handler function
+ */
+static void rayo_event_handler_add(const char *name, struct rayo_xmpp_handler *handler)
+{
+ char full_name[1024];
+ full_name[1023] = '\0';
+ snprintf(full_name, sizeof(full_name) - 1, "%i:%s:%i:%s:%s", handler->from_type, handler->from_subtype, handler->to_type, handler->to_subtype, name);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding event: %s\n", full_name);
+ switch_core_hash_insert(globals.event_handlers, full_name, handler);
+}
+
+/**
+ * Add event handler function
+ * @param from_type the source actor type
+ * @param from_subtype the source actor subtype
+ * @param to_type the destination actor type
+ * @param to_subtype the destination actor subtype
+ * @param name the event name
+ * @param fn the event callback function
+ */
+void rayo_actor_event_handler_add(enum rayo_actor_type from_type, const char *from_subtype, enum rayo_actor_type to_type, const char *to_subtype, const char *name, rayo_actor_xmpp_handler fn)
+{
+ struct rayo_xmpp_handler *handler = switch_core_alloc(globals.pool, sizeof (*handler));
+ handler->from_type = from_type;
+ handler->from_subtype = zstr(from_subtype) ? "" : switch_core_strdup(globals.pool, from_subtype);
+ handler->to_type = to_type;
+ handler->to_subtype = zstr(to_subtype) ? "" : switch_core_strdup(globals.pool, to_subtype);
+ handler->fn = fn;
+ rayo_event_handler_add(name, handler);
+}
+
+/**
+ * Get event handler function from hash
+ * @param from the event source
+ * @param actor the event destination
+ * @param presence the event
+ * @return the event handler function or NULL
+ */
+rayo_actor_xmpp_handler rayo_actor_event_handler_find(struct rayo_actor *from, struct rayo_actor *actor, iks *presence)
+{
+ iks *event = iks_first_tag(presence);
+ if (event) {
+ struct rayo_xmpp_handler *handler = NULL;
+ const char *presence_type = iks_find_attrib_soft(presence, "type");
+ const char *event_name = iks_name(event);
+ const char *event_namespace = iks_find_attrib_soft(event, "xmlns");
+ char full_name[1024];
+ full_name[1023] = '\0';
+ if (zstr(event_name)) {
+ return NULL;
+ }
+ snprintf(full_name, sizeof(full_name) - 1, "%i:%s:%i:%s:%s:%s:%s", from->type, from->subtype, actor->type, actor->subtype, presence_type, event_namespace, event_name);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s => %s, looking for %s event handler\n", RAYO_JID(from), RAYO_JID(actor), full_name);
+ handler = (struct rayo_xmpp_handler *)switch_core_hash_find(globals.event_handlers, full_name);
+ if (handler) {
+ return handler->fn;
+ }
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s => %s, event missing child element\n", RAYO_JID(from), RAYO_JID(actor));
+ }
+ return NULL;
+}
+
+/**
+ * Create a new xml message for delivery to an actor.
+ * @param xml to create message from. This value will be freed upon message delivery.
+ * @return the message
+ */
+struct rayo_message *rayo_message_create(iks *xml)
+{
+ struct rayo_message *msg = malloc(sizeof(*msg));
+ msg->payload = xml;
+ return msg;
+}
+
+/**
+ * Create a new xml message for delivery to an actor. The XML is duplicated before delivery.
+ * @param xml to create message from. This value will not be freed.
+ * @return the message
+ */
+struct rayo_message *rayo_message_create_dup(iks *xml)
+{
+ struct rayo_message *msg = malloc(sizeof(*msg));
+ msg->payload = iks_copy(xml);
+ return msg;
+}
+
+/**
+ * Clean up a message
+ * @param msg to destroy
+ */
+void rayo_message_destroy(struct rayo_message *msg)
+{
+ if (msg) {
+ if (msg->payload) {
+ iks_delete(msg->payload);
+ }
+ free(msg);
+ }
+}
+
+/**
+ * Remove payload from message
+ */
+iks *rayo_message_remove_payload(struct rayo_message *msg)
+{
+ iks *payload = msg->payload;
+ msg->payload = NULL;
+ return payload;
+}
+
+/**
+ * Send message to actor
+ */
+struct rayo_message *rayo_actor_send(struct rayo_actor *from, struct rayo_actor *actor, struct rayo_message *msg, const char *file, int line)
+{
+ struct rayo_message *reply = NULL;
+ iks *payload = msg->payload;
+ char *msg_str = iks_string(iks_stack(payload), payload);
+
+ switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, "", line, "", SWITCH_LOG_DEBUG, "%s, %s\n", RAYO_JID(from), msg_str);
+ switch_mutex_lock(actor->mutex);
+ reply = actor->send_fn(from, actor, msg, file, line);
+ switch_mutex_unlock(actor->mutex);
+
+ rayo_message_destroy(msg);
+
+ return reply;
+}
+
+/**
+ * Send message to actor addressed by JID
+ */
+struct rayo_message *rayo_actor_send_by_jid(struct rayo_actor *from, const char *jid, struct rayo_message *msg, const char *file, int line)
+{
+ struct rayo_message *reply = NULL;
+ struct rayo_actor *actor = RAYO_LOCATE(jid);
+ if (actor) {
+ reply = rayo_actor_send(from, actor, msg, file, line);
+ RAYO_UNLOCK(actor);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, "", line, "", SWITCH_LOG_DEBUG, "%s, failed to locate %s.\n", RAYO_JID(from), jid);
+ rayo_message_destroy(msg);
+ }
+ return reply;
+}
+
+/**
+ * Get access to Rayo actor with JID.
+ * @param jid the JID
+ * @return the actor or NULL. Call RAYO_UNLOCK() when done with pointer.
+ */
+struct rayo_actor *rayo_actor_locate(const char *jid, const char *file, int line)
+{
+ struct rayo_actor *actor = NULL;
+ switch_mutex_lock(globals.actors_mutex);
+ actor = (struct rayo_actor *)switch_core_hash_find(globals.actors, jid);
+ if (actor) {
+ if (!actor->destroy) {
+ actor->ref_count++;
+ switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, "", line, "", SWITCH_LOG_DEBUG, "Locate %s: ref count = %i\n", RAYO_JID(actor), actor->ref_count);
+ } else {
+ actor = NULL;
+ }
+ }
+ switch_mutex_unlock(globals.actors_mutex);
+ return actor;
+}
+
+/**
+ * Get exclusive access to Rayo actor with internal ID
+ * @param id the internal ID
+ * @return the actor or NULL. Call RAYO_UNLOCK() when done with pointer.
+ */
+struct rayo_actor *rayo_actor_locate_by_id(const char *id, const char *file, int line)
+{
+ struct rayo_actor *actor = NULL;
+ if (!zstr(id)) {
+ switch_mutex_lock(globals.actors_mutex);
+ actor = (struct rayo_actor *)switch_core_hash_find(globals.actors_by_id, id);
+ if (actor) {
+ if (!actor->destroy) {
+ actor->ref_count++;
+ switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, "", line, "", SWITCH_LOG_DEBUG, "Locate %s: ref count = %i\n", RAYO_JID(actor), actor->ref_count);
+ } else {
+ actor = NULL;
+ }
+ }
+ switch_mutex_unlock(globals.actors_mutex);
+ }
+ return actor;
+}
+
+/**
+ * Destroy a rayo actor
+ */
+void rayo_actor_destroy(struct rayo_actor *actor, const char *file, int line)
+{
+ switch_memory_pool_t *pool = actor->pool;
+ switch_mutex_lock(globals.actors_mutex);
+ if (!actor->destroy) {
+ switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, "", line, "", SWITCH_LOG_DEBUG, "Destroy %s requested: ref_count = %i\n", RAYO_JID(actor), actor->ref_count);
+ switch_core_hash_delete(globals.actors, RAYO_JID(actor));
+ if (!zstr(actor->id)) {
+ switch_core_hash_delete(globals.actors_by_id, actor->id);
+ }
+ }
+ actor->destroy = 1;
+ if (actor->ref_count <= 0) {
+ switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, "", line, "", SWITCH_LOG_DEBUG, "Destroying %s\n", RAYO_JID(actor));
+ if (actor->cleanup_fn) {
+ actor->cleanup_fn(actor);
+ }
+ switch_core_hash_delete(globals.destroy_actors, RAYO_JID(actor));
+ switch_core_destroy_memory_pool(&pool);
+ } else {
+ switch_core_hash_insert(globals.destroy_actors, RAYO_JID(actor), actor);
+ }
+ switch_mutex_unlock(globals.actors_mutex);
+}
+
+/**
+ * Increment actor ref count - locks from destruction.
+ */
+void rayo_actor_rdlock(struct rayo_actor *actor, const char *file, int line)
+{
+ if (actor) {
+ switch_mutex_lock(globals.actors_mutex);
+ actor->ref_count++;
+ switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, "", line, "", SWITCH_LOG_DEBUG, "Lock %s: ref count = %i\n", RAYO_JID(actor), actor->ref_count);
+ switch_mutex_unlock(globals.actors_mutex);
+ }
+}
+
+/**
+ * Unlock rayo actor
+ */
+void rayo_actor_unlock(struct rayo_actor *actor, const char *file, int line)
+{
+ if (actor) {
+ switch_mutex_lock(globals.actors_mutex);
+ actor->ref_count--;
+ switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, "", line, "", SWITCH_LOG_DEBUG, "Unlock %s: ref count = %i\n", RAYO_JID(actor), actor->ref_count);
+ if (actor->ref_count <= 0 && actor->destroy) {
+ rayo_actor_destroy(actor, file, line);
+ }
+ switch_mutex_unlock(globals.actors_mutex);
+ }
+}
+
+/**
+ * Get next number in sequence
+ */
+int rayo_actor_seq_next(struct rayo_actor *actor)
+{
+ int seq;
+ switch_mutex_lock(actor->mutex);
+ seq = actor->seq++;
+ switch_mutex_unlock(actor->mutex);
+ return seq;
+}
+
+#define RAYO_CALL_LOCATE(call_uuid) rayo_call_locate(call_uuid, __FILE__, __LINE__)
+/**
+ * Get exclusive access to Rayo call data. Use to access call data outside channel thread.
+ * @param call_uuid the FreeSWITCH call UUID
+ * @return the call or NULL.
+ */
+static struct rayo_call *rayo_call_locate(const char *call_uuid, const char *file, int line)
+{
+ struct rayo_actor *actor = rayo_actor_locate_by_id(call_uuid, file, line);
+ if (actor && actor->type == RAT_CALL) {
+ return RAYO_CALL(actor);
+ } else if (actor) {
+ RAYO_UNLOCK(actor);
+ }
+ return NULL;
+}
+
+/**
+ * Fire <end> event when call is cleaned up completely
+ */
+static void rayo_call_cleanup(struct rayo_actor *actor)
+{
+ struct rayo_call *call = RAYO_CALL(actor);
+ switch_event_t *event = call->end_event;
+ char *cause_str;
+ switch_call_cause_t cause = SWITCH_CAUSE_NONE;
+ int no_offered_clients = 1;
+ switch_hash_index_t *hi = NULL;
+ iks *revent;
+ iks *end;
+
+ if (!event) {
+ /* destroyed before FS session was created (in originate, for example) */
+ return;
+ }
+
+ cause_str = switch_event_get_header(event, "variable_hangup_cause");
+ revent = iks_new_presence("end", RAYO_NS,
+ RAYO_JID(call),
+ rayo_call_get_dcp_jid(call));
+ iks_insert_attrib(revent, "type", "unavailable");
+ end = iks_find(revent, "end");
+
+ if (cause_str) {
+ cause = switch_channel_str2cause(cause_str);
+ }
+ iks_insert(end, switch_cause_to_rayo_cause(cause));
+
+ #if 0
+ {
+ char *event_str;
+ if (switch_event_serialize(event, &event_str, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "%s\n", event_str);
+ switch_safe_free(event_str);
+ }
+ }
+ #endif
+
+ /* add signaling headers */
+ {
+ switch_event_header_t *header;
+ /* get all variables prefixed with sip_r_ */
+ for (header = event->headers; header; header = header->next) {
+ if (!strncmp("variable_sip_r_", header->name, 15)) {
+ add_header(end, header->name + 15, header->value);
+ }
+ }
+ }
+
+ /* send <end> to all offered clients */
+ for (hi = switch_hash_first(NULL, call->pcps); hi; hi = switch_hash_next(hi)) {
+ const void *key;
+ void *val;
+ const char *client_jid = NULL;
+ switch_hash_this(hi, &key, NULL, &val);
+ client_jid = (const char *)key;
+ switch_assert(client_jid);
+ iks_insert_attrib(revent, "to", client_jid);
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "Sending <end> to offered client %s\n", client_jid);
+ RAYO_SEND_BY_JID(actor, client_jid, rayo_message_create_dup(revent));
+ no_offered_clients = 0;
+ }
+
+ if (no_offered_clients) {
+ /* send to DCP only */
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "Sending <end> to DCP %s\n", rayo_call_get_dcp_jid(call));
+ RAYO_SEND_BY_JID(actor, rayo_call_get_dcp_jid(call), rayo_message_create_dup(revent));
+ }
+
+ iks_delete(revent);
+ switch_event_destroy(&event);
+}
+
+/**
+ * @param call the Rayo call
+ * @return the Rayo call DCP JID
+ */
+const char *rayo_call_get_dcp_jid(struct rayo_call *call)
+{
+ return call->dcp_jid;
+}
+
+/**
+ * @param call the Rayo call
+ * @return true if joined
+ */
+static int rayo_call_is_joined(struct rayo_call *call)
+{
+ return call->joined;
+}
+
+#define RAYO_MIXER_LOCATE(mixer_name) rayo_mixer_locate(mixer_name, __FILE__, __LINE__)
+/**
+ * Get access to Rayo mixer data.
+ * @param mixer_name the mixer name
+ * @return the mixer or NULL. Call RAYO_UNLOCK() when done with mixer pointer.
+ */
+static struct rayo_mixer *rayo_mixer_locate(const char *mixer_name, const char *file, int line)
+{
+ struct rayo_actor *actor = rayo_actor_locate_by_id(mixer_name, file, line);
+ if (actor && actor->type == RAT_MIXER) {
+ return RAYO_MIXER(actor);
+ } else if (actor) {
+ RAYO_UNLOCK(actor);
+ }
+ return NULL;
+}
+
+/**
+ * Default message handler - drops messages
+ */
+static struct rayo_message *rayo_actor_send_ignore(struct rayo_actor *from, struct rayo_actor *to, struct rayo_message *msg, const char *file, int line)
+{
+ switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, "", line, "", SWITCH_LOG_WARNING, "%s, dropping unexpected message to %s.\n", RAYO_JID(from), RAYO_JID(to));
+ return NULL;
+}
+
+#define RAYO_ACTOR_INIT(actor, pool, type, subtype, id, jid, cleanup, send) rayo_actor_init(actor, pool, type, subtype, id, jid, cleanup, send, __FILE__, __LINE__)
+
+/**
+ * Initialize a rayo actor
+ * @param pool to use
+ * @param type of actor (MIXER, CALL, SERVER, COMPONENT)
+ * @param subtype of actor (input/output/prompt)
+ * @param id internal ID
+ * @param jid external ID
+ * @param cleanup function
+ * @param file that called this function
+ * @param line that called this function
+ * @return the actor
+ */
+static struct rayo_actor *rayo_actor_init(struct rayo_actor *actor, switch_memory_pool_t *pool, enum rayo_actor_type type, const char *subtype, const char *id, const char *jid, rayo_actor_cleanup_fn cleanup, rayo_actor_send_fn send, const char *file, int line)
+{
+ char *domain;
+ actor->type = type;
+ actor->subtype = switch_core_strdup(pool, subtype);
+ actor->pool = pool;
+ if (!zstr(id)) {
+ actor->id = switch_core_strdup(pool, id);
+ }
+ /* TODO validate JID with regex */
+ if (!zstr(jid)) {
+ RAYO_JID(actor) = switch_core_strdup(pool, jid);
+ if (!(domain = strrchr(RAYO_JID(actor), '@'))) {
+ RAYO_DOMAIN(actor) = RAYO_JID(actor);
+ } else if (!zstr(++domain)) {
+ RAYO_DOMAIN(actor) = switch_core_strdup(pool, domain);
+ /* strip resource from domain if it exists */
+ domain = strrchr(RAYO_DOMAIN(actor), '/');
+ if (domain) {
+ *domain = '\0';
+ }
+ }
+ }
+ actor->seq = 1;
+ actor->ref_count = 1;
+ actor->destroy = 0;
+ switch_mutex_init(&actor->mutex, SWITCH_MUTEX_NESTED, pool);
+ actor->cleanup_fn = cleanup;
+ if (send == NULL) {
+ actor->send_fn = rayo_actor_send_ignore;
+ } else {
+ actor->send_fn = send;
+ }
+
+ /* add to hash of actors, so commands can route to call */
+ switch_mutex_lock(globals.actors_mutex);
+ if (!zstr(id)) {
+ switch_core_hash_insert(globals.actors_by_id, actor->id, actor);
+ }
+ if (!zstr(jid)) {
+ switch_core_hash_insert(globals.actors, RAYO_JID(actor), actor);
+ }
+ switch_mutex_unlock(globals.actors_mutex);
+
+ switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, "", line, "", SWITCH_LOG_DEBUG, "Init %s\n", RAYO_JID(actor));
+
+ return actor;
+}
+
+/**
+ * Initialize rayo call
+ */
+static struct rayo_call *rayo_call_init(struct rayo_call *call, switch_memory_pool_t *pool, const char *uuid, const char *file, int line)
+{
+ char *call_jid;
+ char uuid_id_buf[SWITCH_UUID_FORMATTED_LENGTH + 1];
+
+ if (zstr(uuid)) {
+ switch_uuid_str(uuid_id_buf, sizeof(uuid_id_buf));
+ uuid = uuid_id_buf;
+ }
+ call_jid = switch_mprintf("%s@%s", uuid, RAYO_JID(globals.server));
+
+ rayo_actor_init(RAYO_ACTOR(call), pool, RAT_CALL, "", uuid, call_jid, rayo_call_cleanup, rayo_call_send, file, line);
+ call->dcp_jid = "";
+ call->idle_start_time = switch_micro_time_now();
+ call->joined = 0;
+ switch_core_hash_init(&call->pcps, pool);
+
+ switch_safe_free(call_jid);
+
+ return call;
+}
+
+#define rayo_call_create(uuid) _rayo_call_create(uuid, __FILE__, __LINE__)
+/**
+ * Create Rayo call
+ * @param uuid uuid to assign call, if NULL one is picked
+ * @param file file that called this function
+ * @param line number of file that called this function
+ * @return the call
+ */
+static struct rayo_call *_rayo_call_create(const char *uuid, const char *file, int line)
+{
+ switch_memory_pool_t *pool;
+ struct rayo_call *call;
+ switch_core_new_memory_pool(&pool);
+ call = switch_core_alloc(pool, sizeof(*call));
+ return rayo_call_init(call, pool, uuid, file, line);
+}
+
+/**
+ * Initialize mixer
+ */
+static struct rayo_mixer *rayo_mixer_init(struct rayo_mixer *mixer, switch_memory_pool_t *pool, const char *name, const char *file, int line)
+{
+ char *mixer_jid = switch_mprintf("%s@%s", name, RAYO_JID(globals.server));
+ rayo_actor_init(RAYO_ACTOR(mixer), pool, RAT_MIXER, "", name, mixer_jid, NULL, rayo_mixer_send, file, line);
+ switch_core_hash_init(&mixer->members, pool);
+ switch_core_hash_init(&mixer->subscribers, pool);
+ switch_safe_free(mixer_jid);
+ return mixer;
+}
+
+#define rayo_mixer_create(name) _rayo_mixer_create(name, __FILE__, __LINE__)
+/**
+ * Create Rayo mixer
+ * @param name of this mixer
+ * @return the mixer
+ */
+static struct rayo_mixer *_rayo_mixer_create(const char *name, const char *file, int line)
+{
+ switch_memory_pool_t *pool;
+ struct rayo_mixer *mixer = NULL;
+ switch_core_new_memory_pool(&pool);
+ mixer = switch_core_alloc(pool, sizeof(*mixer));
+ return rayo_mixer_init(mixer, pool, name, file, line);
+}
+
+/**
+ * Clean up component before destruction
+ */
+static void rayo_component_cleanup(struct rayo_actor *actor)
+{
+ /* parent can now be destroyed */
+ RAYO_UNLOCK(RAYO_COMPONENT(actor)->parent);
+}
+
+/**
+ * Initialize Rayo component
+ * @param type of this component
+ * @param id internal ID of this component
+ * @param parent the parent that owns this component
+ * @param client_jid the client that created this component
+ * @return the component
+ */
+struct rayo_component *_rayo_component_init(struct rayo_component *component, switch_memory_pool_t *pool, const char *type, const char *id, struct rayo_actor *parent, const char *client_jid, const char *file, int line)
+{
+ enum rayo_actor_type actor_type;
+ char *ref = switch_mprintf("%s-%d", type, rayo_actor_seq_next(parent));
+ char *jid = switch_mprintf("%s/%s", RAYO_JID(parent), ref);
+ if (zstr(id)) {
+ id = jid;
+ }
+ if (parent->type == RAT_CALL || parent->type == RAT_CALL_COMPONENT) {
+ actor_type = RAT_CALL_COMPONENT;
+ } else if (parent->type == RAT_MIXER) {
+ actor_type = RAT_MIXER_COMPONENT;
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Created component with parent %s, type (%s)\n",
+ RAYO_JID(parent), rayo_actor_type_to_string(parent->type));
+ return NULL;
+ }
+
+ rayo_actor_init(RAYO_ACTOR(component), pool, actor_type, type, id, jid, rayo_component_cleanup, rayo_component_send, file, line);
+
+ RAYO_RDLOCK(parent);
+ component->client_jid = switch_core_strdup(pool, client_jid);
+ component->ref = switch_core_strdup(pool, ref);
+ component->parent = parent;
+
+ switch_safe_free(ref);
+ switch_safe_free(jid);
+ return component;
+}
+
+/**
+ * Send XMPP message to client
+ */
+static struct rayo_message *rayo_client_send(struct rayo_actor *from, struct rayo_actor *client, struct rayo_message *msg, const char *file, int line)
+{
+ xmpp_stream_context_send(globals.xmpp_context, RAYO_CLIENT(client)->route, msg->payload);
+ return NULL;
+}
+
+/**
+ * Cleanup rayo client
+ */
+static void rayo_client_cleanup(struct rayo_actor *actor)
+{
+ /* remove session from map */
+ switch_mutex_lock(globals.clients_mutex);
+ if (!zstr(RAYO_JID(actor))) {
+ switch_core_hash_delete(globals.clients_roster, RAYO_JID(actor));
+ if (RAYO_CLIENT(actor)->peer_server) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Removing %s from peer server %s\n", RAYO_JID(actor), RAYO_JID(RAYO_CLIENT(actor)->peer_server));
+ switch_core_hash_delete(RAYO_CLIENT(actor)->peer_server->clients, RAYO_JID(actor));
+ }
+ }
+ switch_mutex_unlock(globals.clients_mutex);
+}
+
+/**
+ * Initialize rayo client
+ * @param pool the memory pool for this client
+ * @param jid for this client
+ * @param route to this client
+ * @param availability of client
+ * @param send message transmission function
+ * @param is_admin true if admin client
+ * @param peer_server NULL if locally connected client
+ * @return the new client
+ */
+static struct rayo_client *rayo_client_init(struct rayo_client *client, switch_memory_pool_t *pool, const char *jid, const char *route, enum presence_status availability, rayo_actor_send_fn send, int is_admin, struct rayo_peer_server *peer_server)
+{
+ RAYO_ACTOR_INIT(RAYO_ACTOR(client), pool, RAT_CLIENT, "", jid, jid, rayo_client_cleanup, send);
+ client->is_admin = is_admin;
+ client->availability = availability;
+ client->peer_server = peer_server;
+ client->last_probe = 0;
+ if (route) {
+ client->route = switch_core_strdup(pool, route);
+ }
+
+ /* make client available for offers */
+ switch_mutex_lock(globals.clients_mutex);
+ switch_core_hash_insert(globals.clients_roster, RAYO_JID(client), client);
+ if (peer_server) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Adding %s to peer server %s\n", RAYO_JID(client), RAYO_JID(peer_server));
+ switch_core_hash_insert(peer_server->clients, RAYO_JID(client), client);
+ }
+ switch_mutex_unlock(globals.clients_mutex);
+
+ return client;
+}
+
+/**
+ * Create a new Rayo client
+ * @param jid for this client
+ * @param route to this client
+ * @param availability of client
+ * @param send message transmission function
+ * @param is_admin true if admin client
+ * @param peer_server NULL if locally connected client
+ * @return the new client or NULL
+ */
+static struct rayo_client *rayo_client_create(const char *jid, const char *route, enum presence_status availability, rayo_actor_send_fn send, int is_admin, struct rayo_peer_server *peer_server)
+{
+ switch_memory_pool_t *pool;
+ struct rayo_client *rclient = NULL;
+
+ switch_core_new_memory_pool(&pool);
+ if (!(rclient = switch_core_alloc(pool, sizeof(*rclient)))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error\n");
+ return NULL;
+ }
+ return rayo_client_init(rclient, pool, jid, route, availability, send, is_admin, peer_server);
+}
+
+/**
+ * Send XMPP message to peer server
+ */
+static struct rayo_message *rayo_peer_server_send(struct rayo_actor *from, struct rayo_actor *server, struct rayo_message *msg, const char *file, int line)
+{
+ xmpp_stream_context_send(globals.xmpp_context, RAYO_JID(server), msg->payload);
+ return NULL;
+}
+
+/**
+ * Destroy peer server and its associated clients
+ */
+static void rayo_peer_server_cleanup(struct rayo_actor *actor)
+{
+ switch_hash_index_t *hi;
+ struct rayo_peer_server *rserver = RAYO_PEER_SERVER(actor);
+
+ /* a little messy... client will remove itself from the peer server when it is destroyed,
+ * however, there is no guarantee the client will actually be destroyed now so
+ * the server must remove the client.
+ */
+ switch_mutex_lock(globals.clients_mutex);
+ while ((hi = switch_core_hash_first(rserver->clients))) {
+ const void *key;
+ void *client;
+ switch_core_hash_this(hi, &key, NULL, &client);
+ switch_assert(client);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Removing %s from peer server %s\n", RAYO_JID(client), RAYO_JID(rserver));
+ switch_core_hash_delete(rserver->clients, key);
+ RAYO_CLIENT(client)->peer_server = NULL;
+ RAYO_UNLOCK(client);
+ RAYO_DESTROY(client);
+ }
+ switch_mutex_unlock(globals.clients_mutex);
+}
+
+/**
+ * Create a new Rayo peer server
+ * @param jid of this server
+ * @return the peer server
+ */
+static struct rayo_peer_server *rayo_peer_server_create(const char *jid)
+{
+ switch_memory_pool_t *pool;
+ struct rayo_peer_server *rserver = NULL;
+
+ switch_core_new_memory_pool(&pool);
+ if (!(rserver = switch_core_alloc(pool, sizeof(*rserver)))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error\n");
+ return NULL;
+ }
+ RAYO_ACTOR_INIT(RAYO_ACTOR(rserver), pool, RAT_PEER_SERVER, "", jid, jid, rayo_peer_server_cleanup, rayo_peer_server_send);
+ switch_core_hash_init(&rserver->clients, pool);
+ return rserver;
+}
+
+/**
+ * Check if client has control of offered call. Take control if nobody else does.
+ * @param rclient the Rayo client
+ * @param call the Rayo call
+ * @param session the session
+ * @param call_jid the call JID
+ * @param call_uuid the internal call UUID
+ * @return 1 if session has call control
+ */
+static int rayo_client_has_call_control(struct rayo_client *rclient, struct rayo_call *call, switch_core_session_t *session)
+{
+ int control = 0;
+
+ if (zstr(RAYO_JID(rclient))) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Null client JID!!\n");
+ return 0;
+ }
+
+ /* nobody in charge */
+ if (zstr(call->dcp_jid)) {
+ /* was offered to this session? */
+ if (switch_core_hash_find(call->pcps, RAYO_JID(rclient))) {
+ /* take charge */
+ call->dcp_jid = switch_core_strdup(RAYO_POOL(call), RAYO_JID(rclient));
+ switch_channel_set_variable(switch_core_session_get_channel(session), "rayo_dcp_jid", rayo_call_get_dcp_jid(call));
+ control = 1;
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_INFO, "%s has control of call\n", rayo_call_get_dcp_jid(call));
+ }
+ } else if (rclient->is_admin || !strcmp(rayo_call_get_dcp_jid(call), RAYO_JID(rclient))) {
+ control = 1;
+ }
+
+ if (!control) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_INFO, "%s does not have control of call\n", RAYO_JID(rclient));
+ }
+
+ return control;
+}
+
+/**
+ * Check Rayo server command for errors.
+ * @param rclient the Rayo client
+ * @param node the <iq> node
+ * @return 1 if OK
+ */
+static iks *rayo_server_command_ok(struct rayo_actor *rclient, struct rayo_actor *server, iks *node)
+{
+ iks *response = NULL;
+ int bad = zstr(iks_find_attrib(node, "id"));
+
+ if (bad) {
+ response = iks_new_error(node, STANZA_ERROR_BAD_REQUEST);
+ } else if (rclient->type != RAT_CLIENT) {
+ /* not a rayo client request */
+ response = iks_new_error(node, STANZA_ERROR_NOT_ALLOWED);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, %s not a client\n", RAYO_JID(rclient), RAYO_JID(server));
+ }
+
+ return response;
+}
+
+/**
+ * Check Rayo call command for errors.
+ * @param rclient the Rayo client
+ * @param call the Rayo call
+ * @param session the session
+ * @param node the <iq> node
+ * @return 1 if OK
+ */
+static iks *rayo_call_command_ok(struct rayo_actor *rclient, struct rayo_call *call, switch_core_session_t *session, iks *node)
+{
+ iks *response = NULL;
+ int bad = zstr(iks_find_attrib(node, "id"));
+
+ if (bad) {
+ response = iks_new_error(node, STANZA_ERROR_BAD_REQUEST);
+ } else if (rclient->type == RAT_CALL_COMPONENT) {
+ struct rayo_actor *client = RAYO_LOCATE(RAYO_COMPONENT(rclient)->client_jid);
+ if (client) {
+ iks *response = rayo_call_command_ok(client, call, session, node);
+ RAYO_UNLOCK(client);
+ return response;
+ }
+ /* not a client request */
+ response = iks_new_error(node, STANZA_ERROR_NOT_ALLOWED);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, %s call component client is gone\n", RAYO_JID(rclient), RAYO_JID(call));
+ } else if (rclient->type != RAT_CLIENT) {
+ /* not a client request */
+ response = iks_new_error(node, STANZA_ERROR_NOT_ALLOWED);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, %s not a client request\n", RAYO_JID(rclient), RAYO_JID(call));
+ } else if (!rayo_client_has_call_control(RAYO_CLIENT(rclient), call, session)) {
+ response = iks_new_error(node, STANZA_ERROR_CONFLICT);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, %s conflict\n", RAYO_JID(rclient), RAYO_JID(call));
+ }
+
+ return response;
+}
+
+/**
+ * Check Rayo component command for errors.
+ * @param rclient the client
+ * @param component the component
+ * @param node the <iq> node
+ * @return 0 if error
+ */
+static iks *rayo_component_command_ok(struct rayo_actor *rclient, struct rayo_component *component, iks *node)
+{
+ iks *response = NULL;
+ char *from = iks_find_attrib(node, "from");
+ int bad = zstr(iks_find_attrib(node, "id"));
+
+ if (bad) {
+ response = iks_new_error(node, STANZA_ERROR_BAD_REQUEST);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, %s bad request\n", RAYO_JID(rclient), RAYO_JID(component));
+ } else if (rclient->type != RAT_CLIENT) {
+ /* internal message is ok */
+ return NULL;
+ } else if (!RAYO_CLIENT(rclient)->is_admin && strcmp(component->client_jid, from)) {
+ /* does not have control of this component */
+ response = iks_new_error(node, STANZA_ERROR_CONFLICT);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, %s conflict\n", RAYO_JID(rclient), RAYO_JID(component));
+ }
+
+ return response;
+}
+
+/**
+ * Handle server message
+ */
+static struct rayo_message *rayo_server_send(struct rayo_actor *client, struct rayo_actor *server, struct rayo_message *msg, const char *file, int line)
+{
+ rayo_actor_xmpp_handler handler = NULL;
+ iks *iq = msg->payload;
+ iks *response = NULL;
+
+ if (!strcmp("presence", iks_name(iq))) {
+ on_client_presence(RAYO_CLIENT(client), iq);
+ return NULL;
+ }
+
+ /* is this a command a server supports? */
+ handler = rayo_actor_command_handler_find(server, iq);
+ if (!handler) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, no handler function for command to %s\n", RAYO_JID(client), RAYO_JID(server));
+ return rayo_message_create(iks_new_error(iq, STANZA_ERROR_FEATURE_NOT_IMPLEMENTED));
+ }
+
+ /* is the command valid? */
+ if (!(response = rayo_server_command_ok(client, server, iq))) {
+ response = handler(client, server, iq, NULL);
+ }
+
+ if (response) {
+ return rayo_message_create(response);
+ }
+ return NULL;
+}
+
+/**
+ * Handle call message
+ */
+static struct rayo_message *rayo_call_send(struct rayo_actor *client, struct rayo_actor *call, struct rayo_message *msg, const char *file, int line)
+{
+ rayo_actor_xmpp_handler handler = NULL;
+ iks *iq = msg->payload;
+ switch_core_session_t *session;
+ iks *response = NULL;
+
+ /* is this a command a call supports? */
+ handler = rayo_actor_command_handler_find(call, iq);
+ if (!handler) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, no handler function for command\n", RAYO_JID(call));
+ return rayo_message_create(iks_new_error(iq, STANZA_ERROR_FEATURE_NOT_IMPLEMENTED));
+ }
+
+ /* is the session still available? */
+ session = switch_core_session_locate(rayo_call_get_uuid(RAYO_CALL(call)));
+ if (!session) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, session not found\n", RAYO_JID(call));
+ return rayo_message_create(iks_new_error(iq, STANZA_ERROR_SERVICE_UNAVAILABLE));
+ }
+
+ /* is the command valid? */
+ if (!(response = rayo_call_command_ok(client, RAYO_CALL(call), session, iq))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, executing command\n", RAYO_JID(call));
+ response = handler(client, call, iq, session);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, done executing command\n", RAYO_JID(call));
+ RAYO_CALL(call)->idle_start_time = switch_micro_time_now();
+ }
+ switch_core_session_rwunlock(session);
+
+ if (response) {
+ return rayo_message_create(response);
+ }
+ return NULL;
+}
+
+/**
+ * Handle mixer message
+ */
+static struct rayo_message *rayo_mixer_send(struct rayo_actor *client, struct rayo_actor *mixer, struct rayo_message *msg, const char *file, int line)
+{
+ rayo_actor_xmpp_handler handler = NULL;
+ iks *iq = msg->payload;
+ iks *response = NULL;
+
+ /* is this a command a mixer supports? */
+ handler = rayo_actor_command_handler_find(mixer, iq);
+ if (!handler) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, no handler function for command\n", RAYO_JID(mixer));
+ return rayo_message_create(iks_new_error(iq, STANZA_ERROR_FEATURE_NOT_IMPLEMENTED));
+ }
+
+ /* execute the command */
+ response = handler(client, mixer, iq, NULL);
+ if (response) {
+ return rayo_message_create(response);
+ }
+ return NULL;
+}
+
+/**
+ * Handle mixer message
+ */
+static struct rayo_message *rayo_component_send(struct rayo_actor *client, struct rayo_actor *component, struct rayo_message *msg, const char *file, int line)
+{
+ rayo_actor_xmpp_handler handler = NULL;
+ iks *xml_msg = msg->payload;
+ iks *response = NULL;
+
+ if (!strcmp("iq", iks_name(xml_msg))) {
+ /* is this a command a component supports? */
+ handler = rayo_actor_command_handler_find(component, xml_msg);
+ if (!handler) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, no component handler function for command\n", RAYO_JID(component));
+ return rayo_message_create(iks_new_error(xml_msg, STANZA_ERROR_FEATURE_NOT_IMPLEMENTED));
+ }
+
+ /* is the command valid? */
+ if (!(response = rayo_component_command_ok(client, RAYO_COMPONENT(component), xml_msg))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, executing command\n", RAYO_JID(component));
+ response = handler(client, component, xml_msg, NULL);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, done executing command\n", RAYO_JID(component));
+ }
+
+ if (response) {
+ return rayo_message_create(response);
+ }
+ } else if (!strcmp("presence", iks_name(xml_msg))) {
+ /* is this an event the component wants? */
+ handler = rayo_actor_event_handler_find(client, component, xml_msg);
+ if (!handler) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, no component handler function for event\n", RAYO_JID(component));
+ return NULL;
+ }
+
+ /* forward the event */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, forwarding event\n", RAYO_JID(component));
+ handler(client, component, xml_msg, NULL);
+ }
+
+ return NULL;
+}
+
+/**
+ * Add signaling headers to channel -- only works on SIP
+ * @param session the channel
+ * @param iq_cmd the request containing <header>
+ * @param type header type
+ */
+static void add_signaling_headers(switch_core_session_t *session, iks *iq_cmd, const char *type)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ iks *header = NULL;
+ for (header = iks_find(iq_cmd, "header"); header; header = iks_next_tag(header)) {
+ if (!strcmp("header", iks_name(header))) {
+ const char *name = iks_find_attrib_soft(header, "name");
+ const char *value = iks_find_attrib_soft(header, "value");
+ if (!zstr(name) && !zstr(value)) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Adding header: %s: %s\n", name, value);
+ switch_channel_set_variable_name_printf(channel, value, "%s%s", type, name);
+ }
+ }
+ }
+}
+
+/**
+ * Handle <iq><accept> request
+ * @param call the Rayo call
+ * @param session the session
+ * @param node the <iq> node
+ */
+static iks *on_rayo_accept(struct rayo_actor *client, struct rayo_actor *call, iks *node, void *session_data)
+{
+ switch_core_session_t *session = (switch_core_session_t *)session_data;
+ iks *response = NULL;
+
+ /* send ringing */
+ add_signaling_headers(session, iks_find(node, "accept"), RAYO_SIP_RESPONSE_HEADER);
+ switch_channel_pre_answer(switch_core_session_get_channel(session));
+ response = iks_new_iq_result(node);
+ return response;
+}
+
+/**
+ * Handle <iq><answer> request
+ * @param call the Rayo call
+ * @param session the session
+ * @param node the <iq> node
+ */
+static iks *on_rayo_answer(struct rayo_actor *client, struct rayo_actor *call, iks *node, void *session_data)
+{
+ switch_core_session_t *session = (switch_core_session_t *)session_data;
+ iks *response = NULL;
+
+ /* send answer to call */
+ add_signaling_headers(session, iks_find(node, "answer"), RAYO_SIP_RESPONSE_HEADER);
+ switch_channel_answer(switch_core_session_get_channel(session));
+ response = iks_new_iq_result(node);
+ return response;
+}
+
+/**
+ * Handle <iq><redirect> request
+ * @param call the Rayo call
+ * @param session the session
+ * @param node the <iq> node
+ */
+static iks *on_rayo_redirect(struct rayo_actor *client, struct rayo_actor *call, iks *node, void *session_data)
+{
+ switch_core_session_t *session = (switch_core_session_t *)session_data;
+ iks *response = NULL;
+ iks *redirect = iks_find(node, "redirect");
+ char *redirect_to = iks_find_attrib(redirect, "to");
+
+ if (zstr(redirect_to)) {
+ response = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "Missing redirect to attrib");
+ } else {
+ switch_core_session_message_t msg = { 0 };
+ add_signaling_headers(session, redirect, RAYO_SIP_RESPONSE_HEADER);
+
+ /* Tell the channel to deflect the call */
+ msg.from = __FILE__;
+ msg.string_arg = switch_core_session_strdup(session, redirect_to);
+ msg.message_id = SWITCH_MESSAGE_INDICATE_DEFLECT;
+ switch_core_session_receive_message(session, &msg);
+ response = iks_new_iq_result(node);
+ }
+ return response;
+}
+
+/**
+ * Handle <iq><hangup> or <iq><reject> request
+ * @param call the Rayo call
+ * @param session the session
+ * @param node the <iq> node
+ */
+static iks *on_rayo_hangup(struct rayo_actor *client, struct rayo_actor *call, iks *node, void *session_data)
+{
+ switch_core_session_t *session = (switch_core_session_t *)session_data;
+ iks *response = NULL;
+ iks *hangup = iks_first_tag(node);
+ iks *reason = iks_first_tag(hangup);
+ int hangup_cause = RAYO_CAUSE_HANGUP;
+
+ /* get hangup cause */
+ if (!reason && !strcmp("hangup", iks_name(hangup))) {
+ /* no reason in <hangup> */
+ hangup_cause = RAYO_CAUSE_HANGUP;
+ } else if (reason && !strcmp("reject", iks_name(hangup))) {
+ char *reason_name = iks_name(reason);
+ /* reason required for <reject> */
+ if (!strcmp("busy", reason_name)) {
+ hangup_cause = RAYO_CAUSE_BUSY;
+ } else if (!strcmp("decline", reason_name)) {
+ hangup_cause = RAYO_CAUSE_DECLINE;
+ } else if (!strcmp("error", reason_name)) {
+ hangup_cause = RAYO_CAUSE_ERROR;
+ } else {
+ response = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "invalid reject reason");
+ }
+ } else {
+ response = iks_new_error(node, STANZA_ERROR_BAD_REQUEST);
+ }
+
+ /* do hangup */
+ if (!response) {
+ add_signaling_headers(session, hangup, RAYO_SIP_REQUEST_HEADER);
+ add_signaling_headers(session, hangup, RAYO_SIP_RESPONSE_HEADER);
+ switch_ivr_kill_uuid(rayo_call_get_uuid(call), hangup_cause);
+ response = iks_new_iq_result(node);
+ }
+
+ return response;
+}
+
+/**
+ * Join calls together
+ * @param call the call that joins
+ * @param session the session
+ * @param node the join request
+ * @param call_id to join
+ * @param media mode (direct/bridge)
+ * @return the response
+ */
+static iks *join_call(struct rayo_call *call, switch_core_session_t *session, iks *node, const char *call_id, const char *media)
+{
+ iks *response = NULL;
+ /* take call out of media path if media = "direct" */
+ const char *bypass = !strcmp("direct", media) ? "true" : "false";
+
+ /* check if joining to rayo call */
+ struct rayo_call *b_call = RAYO_CALL_LOCATE(call_id);
+ if (!b_call) {
+ /* not a rayo call */
+ response = iks_new_error_detailed(node, STANZA_ERROR_SERVICE_UNAVAILABLE, "b-leg is not a rayo call");
+ } else if (b_call->joined) {
+ /* don't support multiple joined calls */
+ response = iks_new_error_detailed(node, STANZA_ERROR_CONFLICT, "multiple joined calls not supported");
+ RAYO_UNLOCK(b_call);
+ } else {
+ RAYO_UNLOCK(b_call);
+
+ /* bridge this call to call-id */
+ switch_channel_set_variable(switch_core_session_get_channel(session), "bypass_media", bypass);
+ if (switch_false(bypass)) {
+ switch_channel_pre_answer(switch_core_session_get_channel(session));
+ }
+ if (switch_ivr_uuid_bridge(rayo_call_get_uuid(call), call_id) == SWITCH_STATUS_SUCCESS) {
+ response = iks_new_iq_result(node);
+ } else {
+ response = iks_new_error_detailed(node, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to bridge call");
+ }
+ }
+ return response;
+}
+
+/**
+ * Join call to a mixer
+ * @param call the call that joins
+ * @param session the session
+ * @param node the join request
+ * @return the response
+ */
+static iks *join_mixer(struct rayo_call *call, switch_core_session_t *session, iks *node, const char *mixer_name)
+{
+ iks *response = NULL;
+ char *conf_args = switch_mprintf("%s@%s", mixer_name, globals.mixer_conf_profile);
+ if (switch_core_session_execute_application_async(session, "conference", conf_args) == SWITCH_STATUS_SUCCESS) {
+ response = iks_new_iq_result(node);
+ } else {
+ response = iks_new_error_detailed(node, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed execute conference app");
+ }
+ switch_safe_free(conf_args);
+ return response;
+}
+
+/**
+ * Handle <iq><join> request
+ * @param call the Rayo call
+ * @param session the session
+ * @param node the <iq> node
+ */
+static iks *on_rayo_join(struct rayo_actor *client, struct rayo_actor *call, iks *node, void *session_data)
+{
+ switch_core_session_t *session = (switch_core_session_t *)session_data;
+ iks *response = NULL;
+ iks *join = iks_find(node, "join");
+ const char *mixer_name;
+ const char *call_id;
+
+ /* validate input attributes */
+ if (!VALIDATE_RAYO_JOIN(join)) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Bad join attrib\n");
+ response = iks_new_error(node, STANZA_ERROR_BAD_REQUEST);
+ goto done;
+ }
+ mixer_name = iks_find_attrib(join, "mixer-name");
+ call_id = iks_find_attrib(join, "call-id");
+
+ /* can't join both mixer and call */
+ if (!zstr(mixer_name) && !zstr(call_id)) {
+ response = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "mixer-name and call-id are mutually exclusive");
+ goto done;
+ }
+
+ /* need to join *something* */
+ if (zstr(mixer_name) && zstr(call_id)) {
+ response = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "mixer-name or call-id is required");
+ goto done;
+ }
+
+ if (RAYO_CALL(call)->joined) {
+ /* already joined */
+ response = iks_new_error_detailed(node, STANZA_ERROR_CONFLICT, "call is already joined");
+ goto done;
+ }
+
+ if (!zstr(mixer_name)) {
+ /* join conference */
+ response = join_mixer(RAYO_CALL(call), session, node, mixer_name);
+ } else {
+ /* bridge calls */
+ response = join_call(RAYO_CALL(call), session, node, call_id, iks_find_attrib(join, "media"));
+ }
+
+done:
+ return response;
+}
+
+/**
+ * unjoin call to a bridge
+ * @param call the call that unjoined
+ * @param session the session
+ * @param node the unjoin request
+ * @param call_id the b-leg uuid
+ * @return the response
+ */
+static iks *unjoin_call(struct rayo_actor *client, struct rayo_call *call, switch_core_session_t *session, iks *node, const char *call_id)
+{
+ iks *response = NULL;
+ const char *bleg = switch_channel_get_variable(switch_core_session_get_channel(session), SWITCH_BRIDGE_UUID_VARIABLE);
+
+ /* bleg must match call_id */
+ if (!zstr(bleg) && !strcmp(bleg, call_id)) {
+ /* unbridge call */
+ response = iks_new_iq_result(node);
+ switch_ivr_park_session(session);
+ } else {
+ /* not bridged or wrong b-leg UUID */
+ response = iks_new_error(node, STANZA_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ return response;
+}
+
+/**
+ * unjoin call to a mixer
+ * @param call the call that unjoined
+ * @param session the session
+ * @param node the unjoin request
+ * @param mixer_name the mixer name
+ * @return the response
+ */
+static iks *unjoin_mixer(struct rayo_actor *client, struct rayo_call *call, switch_core_session_t *session, iks *node, const char *mixer_name)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ const char *conf_member_id = switch_channel_get_variable(channel, "conference_member_id");
+ const char *conf_name = switch_channel_get_variable(channel, "conference_name");
+ char *kick_command;
+ iks *response = NULL;
+ switch_stream_handle_t stream = { 0 };
+ SWITCH_STANDARD_STREAM(stream);
+
+ /* not conferenced, or wrong conference */
+ if (zstr(conf_name) || strcmp(mixer_name, conf_name)) {
+ response = iks_new_error_detailed_printf(node, STANZA_ERROR_SERVICE_UNAVAILABLE, "not joined to %s", mixer_name);
+ goto done;
+ } else if (zstr(conf_member_id)) {
+ /* shouldn't happen */
+ response = iks_new_error_detailed(node, STANZA_ERROR_SERVICE_UNAVAILABLE, "channel doesn't have conference member ID");
+ goto done;
+ }
+
+ /* ack command */
+ response = iks_new_iq_result(node);
+
+ /* kick the member */
+ kick_command = switch_core_session_sprintf(session, "%s hup %s", mixer_name, conf_member_id);
+ switch_api_execute("conference", kick_command, NULL, &stream);
+
+done:
+ switch_safe_free(stream.data);
+
+ return response;
+}
+
+/**
+ * Handle <iq><unjoin> request
+ * @param call the Rayo call
+ * @param session the session
+ * @param node the <iq> node
+ */
+static iks *on_rayo_unjoin(struct rayo_actor *client, struct rayo_actor *call, iks *node, void *session_data)
+{
+ switch_core_session_t *session = (switch_core_session_t *)session_data;
+ iks *response = NULL;
+ iks *unjoin = iks_find(node, "unjoin");
+ const char *call_id = iks_find_attrib(unjoin, "call-id");
+ const char *mixer_name = iks_find_attrib(unjoin, "mixer-name");
+
+ if (!zstr(call_id) && !zstr(mixer_name)) {
+ response = iks_new_error(node, STANZA_ERROR_BAD_REQUEST);
+ } else if (!RAYO_CALL(call)->joined) {
+ /* not joined to anything */
+ response = iks_new_error(node, STANZA_ERROR_SERVICE_UNAVAILABLE);
+ } else if (!zstr(call_id)) {
+ response = unjoin_call(client, RAYO_CALL(call), session, node, call_id);
+ } else if (!zstr(mixer_name)) {
+ response = unjoin_mixer(client, RAYO_CALL(call), session, node, mixer_name);
+ } else {
+ /* missing mixer or call */
+ response = iks_new_error(node, STANZA_ERROR_BAD_REQUEST);
+ }
+
+ return response;
+}
+
+/**
+ * Thread that handles originating new calls
+ * @param thread this thread
+ * @param obj the Rayo client
+ * @return NULL
+ */
+static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void *node)
+{
+ iks *iq = (iks *)node;
+ iks *dial = iks_find(iq, "dial");
+ iks *response = NULL;
+ const char *dcp_jid = iks_find_attrib(iq, "from");
+ const char *dial_to = iks_find_attrib(dial, "to");
+ const char *dial_from = iks_find_attrib(dial, "from");
+ const char *dial_timeout_ms = iks_find_attrib(dial, "timeout");
+ struct dial_gateway *gateway = NULL;
+ struct rayo_call *call = NULL;
+ switch_stream_handle_t stream = { 0 };
+ SWITCH_STANDARD_STREAM(stream);
+
+ /* create call and link to DCP */
+ call = rayo_call_create(NULL);
+ call->dcp_jid = switch_core_strdup(RAYO_POOL(call), dcp_jid);
+ call->dial_id = iks_find_attrib(iq, "id");
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_INFO, "%s has control of call\n", dcp_jid);
+
+ /* set rayo channel variables so channel originate event can be identified as coming from Rayo */
+ stream.write_function(&stream, "{origination_uuid=%s,rayo_dcp_jid=%s,rayo_call_jid=%s",
+ rayo_call_get_uuid(call), dcp_jid, RAYO_JID(call));
+
+ /* set originate channel variables */
+ if (!zstr(dial_from)) {
+ /* caller ID */
+ /* TODO parse caller ID name and number from URI */
+ stream.write_function(&stream, ",origination_caller_id_number=%s,origination_caller_id_name=%s", dial_from, dial_from);
+ }
+ if (!zstr(dial_timeout_ms) && switch_is_number(dial_timeout_ms)) {
+ /* timeout */
+ int dial_timeout_sec = round((double)atoi(dial_timeout_ms) / 1000.0);
+ stream.write_function(&stream, ",originate_timeout=%i", dial_timeout_sec);
+ }
+
+ /* set outbound signaling headers - only works on SIP */
+ {
+ iks *header = NULL;
+ for (header = iks_find(dial, "header"); header; header = iks_next_tag(header)) {
+ if (!strcmp("header", iks_name(header))) {
+ const char *name = iks_find_attrib_soft(header, "name");
+ const char *value = iks_find_attrib_soft(header, "value");
+ if (!zstr(name) && !zstr(value)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "Adding header: %s: %s\n", name, value);
+ stream.write_function(&stream, ",%s%s=%s", RAYO_SIP_REQUEST_HEADER, name, value);
+ }
+ }
+ }
+ }
+
+ stream.write_function(&stream, "}");
+
+ /* build dialstring and dial call */
+ gateway = dial_gateway_find(dial_to);
+ if (gateway) {
+ iks *join = iks_find(dial, "join");
+ const char *dial_to_stripped = dial_to + gateway->strip;
+ switch_stream_handle_t api_stream = { 0 };
+ SWITCH_STANDARD_STREAM(api_stream);
+
+ if (join) {
+ /* check join args */
+ const char *call_id = iks_find_attrib(join, "call-id");
+ const char *mixer_name = iks_find_attrib(join, "mixer-name");
+
+ if (!zstr(call_id) && !zstr(mixer_name)) {
+ /* can't join both */
+ response = iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
+ goto done;
+ } else if (zstr(call_id) && zstr(mixer_name)) {
+ /* nobody to join to? */
+ response = iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
+ goto done;
+ } else if (!zstr(call_id)) {
+ /* bridge */
+ struct rayo_call *b_call = RAYO_CALL_LOCATE(call_id);
+ /* is b-leg available? */
+ if (!b_call) {
+ response = iks_new_error_detailed(iq, STANZA_ERROR_SERVICE_UNAVAILABLE, "b-leg not found");
+ goto done;
+ } else if (b_call->joined) {
+ response = iks_new_error_detailed(iq, STANZA_ERROR_SERVICE_UNAVAILABLE, "b-leg already joined to another call");
+ RAYO_UNLOCK(b_call);
+ goto done;
+ }
+ RAYO_UNLOCK(b_call);
+ stream.write_function(&stream, "%s%s &rayo(bridge %s)", gateway->dial_prefix, dial_to_stripped, call_id);
+ } else {
+ /* conference */
+ stream.write_function(&stream, "%s%s &rayo(conference %s@%s)", gateway->dial_prefix, dial_to_stripped, mixer_name, globals.mixer_conf_profile);
+ }
+ } else {
+ stream.write_function(&stream, "%s%s &rayo", gateway->dial_prefix, dial_to_stripped);
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "Using dialstring: %s\n", (char *)stream.data);
+
+ /* <iq><ref> response will be sent when originate event is received- otherwise error is returned */
+ if (switch_api_execute("originate", stream.data, NULL, &api_stream) == SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "Got originate result: %s\n", (char *)api_stream.data);
+
+ /* check for failure */
+ if (strncmp("+OK", api_stream.data, strlen("+OK"))) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_INFO, "Failed to originate call\n");
+
+ if (call->dial_id) {
+ /* map failure reason to iq error */
+ if (!strncmp("-ERR DESTINATION_OUT_OF_ORDER", api_stream.data, strlen("-ERR DESTINATION_OUT_OF_ORDER"))) {
+ /* this -ERR is received when out of sessions */
+ response = iks_new_error(iq, STANZA_ERROR_RESOURCE_CONSTRAINT);
+ } else {
+ response = iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, api_stream.data);
+ }
+ }
+ }
+ } else if (call->dial_id) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Failed to exec originate API\n");
+ response = iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Failed to execute originate API");
+ }
+
+ switch_safe_free(api_stream.data);
+ } else {
+ /* will only happen if misconfigured */
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_CRIT, "No dial gateway found for %s!\n", dial_to);
+ response = iks_new_error_detailed_printf(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "No dial gateway found for %s!\n", dial_to);
+ goto done;
+ }
+
+done:
+
+ /* response when error */
+ if (response) {
+ /* send response to client */
+ RAYO_SEND_BY_JID(call, iks_find_attrib(response, "to"), rayo_message_create(response));
+
+ /* destroy call */
+ if (call) {
+ RAYO_DESTROY(call);
+ RAYO_UNLOCK(call);
+ }
+ }
+
+ iks_delete(dial);
+ switch_safe_free(stream.data);
+
+ return NULL;
+}
+
+/**
+ * Dial a new call
+ * @param rclient requesting the call
+ * @param server handling the call
+ * @param node the request
+ */
+static iks *on_rayo_dial(struct rayo_actor *client, struct rayo_actor *server, iks *node, void *data)
+{
+ struct rayo_client *rclient = RAYO_CLIENT(client);
+ switch_thread_t *thread;
+ switch_threadattr_t *thd_attr = NULL;
+ iks *dial = iks_find(node, "dial");
+ iks *response = NULL;
+
+ if (!zstr(iks_find_attrib(dial, "to"))) {
+ iks *node_dup = iks_copy(node);
+ iks_insert_attrib(node_dup, "from", RAYO_JID(rclient)); /* save DCP jid in case it isn't specified */
+
+ /* start dial thread */
+ switch_threadattr_create(&thd_attr, RAYO_POOL(rclient));
+ switch_threadattr_detach_set(thd_attr, 1);
+ switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+ switch_thread_create(&thread, thd_attr, rayo_dial_thread, node_dup, RAYO_POOL(rclient));
+ } else {
+ response = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "missing dial to attribute");
+ }
+
+ return response;
+}
+
+/**
+ * Handle <iq><ping> request
+ * @param rclient the Rayo client
+ * @param server the Rayo server
+ * @param node the <iq> node
+ * @return NULL
+ */
+static iks *on_iq_xmpp_ping(struct rayo_actor *rclient, struct rayo_actor *server, iks *node, void *data)
+{
+ iks *pong = iks_new("iq");
+ char *from = iks_find_attrib(node, "from");
+ char *to = iks_find_attrib(node, "to");
+
+ if (zstr(from)) {
+ from = RAYO_JID(rclient);
+ }
+
+ if (zstr(to)) {
+ to = RAYO_JID(server);
+ }
+
+ iks_insert_attrib(pong, "type", "result");
+ iks_insert_attrib(pong, "from", to);
+ iks_insert_attrib(pong, "to", from);
+ iks_insert_attrib(pong, "id", iks_find_attrib(node, "id"));
+
+ return pong;
+}
+
+/**
+ * Handle service discovery request
+ * @param rclient the Rayo client
+ * @param server the Rayo server
+ * @param node the <iq> node
+ * @return NULL
+ */
+static iks *on_iq_get_xmpp_disco(struct rayo_actor *rclient, struct rayo_actor *server, iks *node, void *data)
+{
+ iks *response = NULL;
+ iks *x;
+ response = iks_new_iq_result(node);
+ x = iks_insert(response, "query");
+ iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_DISCO);
+ x = iks_insert(x, "feature");
+ iks_insert_attrib(x, "var", RAYO_NS);
+
+ /* TODO The response MUST also include features for the application formats and transport methods supported by
+ * the responding entity, as described in the relevant specifications.
+ */
+
+ return response;
+}
+
+/**
+ * Handle message from client
+ * @param rclient that sent the command
+ * @param message the message
+ */
+static void on_client_message(struct rayo_client *rclient, iks *message)
+{
+ const char *to = iks_find_attrib(message, "to");
+ struct rayo_actor *actor;
+
+ /* must be directed to a client */
+ if (zstr(to)) {
+ return;
+ }
+
+ /* assume client source */
+ if (zstr(iks_find_attrib(message, "from"))) {
+ iks_insert_attrib(message, "from", RAYO_JID(rclient));
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, recv message, availability = %s\n", RAYO_JID(rclient), presence_status_to_string(rclient->availability));
+
+ actor = RAYO_LOCATE(to);
+ if (actor && actor->type == RAT_CLIENT) {
+ RAYO_SEND(rclient, actor, rayo_message_create_dup(message));
+ }
+ RAYO_UNLOCK(actor);
+}
+
+/**
+ * Handle <presence> message from a client
+ * @param rclient the client
+ * @param node the presence message
+ */
+static void on_client_presence(struct rayo_client *rclient, iks *node)
+{
+ char *type = iks_find_attrib(node, "type");
+ enum presence_status status = PS_UNKNOWN;
+
+ /*
+ From RFC-6121:
+ Entity is available when <presence/> received.
+ Entity is unavailable when <presence type='unavailable'/> is received.
+
+ From Rayo-XEP:
+ Entity is available when <presence to='foo' from='bar'><show>chat</show></presence> is received.
+ Entity is unavailable when <presence to='foo' from='bar'><show>dnd</show></presence> is received.
+ */
+
+ /* figure out if online/offline */
+ if (zstr(type)) {
+ /* <presence><show>chat</show></presence> */
+ char *status_str = iks_find_cdata(node, "show");
+ if (!zstr(status_str)) {
+ if (!strcmp("chat", status_str)) {
+ status = PS_ONLINE;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s got chat presence\n", RAYO_JID(rclient));
+ } else if (!strcmp("dnd", status_str)) {
+ status = PS_OFFLINE;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s got dnd presence\n", RAYO_JID(rclient));
+ }
+ } else {
+ /* <presence/> */
+ status = PS_ONLINE;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s got empty presence\n", RAYO_JID(rclient));
+ }
+ } else if (!strcmp("unavailable", type)) {
+ status = PS_OFFLINE;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s got unavailable presence\n", RAYO_JID(rclient));
+ } else if (!strcmp("error", type)) {
+ /* TODO presence error */
+ } else if (!strcmp("probe", type)) {
+ /* TODO presence probe */
+ } else if (!strcmp("subscribe", type)) {
+ /* TODO presence subscribe */
+ } else if (!strcmp("subscribed", type)) {
+ /* TODO presence subscribed */
+ } else if (!strcmp("unsubscribe", type)) {
+ /* TODO presence unsubscribe */
+ } else if (!strcmp("unsubscribed", type)) {
+ /* TODO presence unsubscribed */
+ }
+
+ if (status == PS_ONLINE && rclient->availability != PS_ONLINE) {
+ rclient->availability = PS_ONLINE;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s is ONLINE\n", RAYO_JID(rclient));
+ } else if (status == PS_OFFLINE && rclient->availability != PS_OFFLINE) {
+ rclient->availability = PS_OFFLINE;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s is OFFLINE\n", RAYO_JID(rclient));
+ }
+
+ /* destroy if not a local client (connected via peer_server) and is OFFLINE */
+ /* TODO rethink this */
+ //if (rclient->peer_server && rclient->availability == PS_OFFLINE) {
+ // RAYO_DESTROY(rclient);
+ // RAYO_UNLOCK(rclient);
+ //}
+}
+
+/**
+ * Handle command from client
+ * @param rclient that sent the command
+ * @param iq the command
+ */
+static void rayo_client_command_recv(struct rayo_client *rclient, iks *iq)
+{
+ iks *command = iks_first_tag(iq);
+ const char *to = iks_find_attrib(iq, "to");
+
+ /* assume server destination */
+ if (zstr(to)) {
+ to = RAYO_JID(globals.server);
+ iks_insert_attrib(iq, "to", to);
+ }
+
+ /* assume client source */
+ if (zstr(iks_find_attrib(iq, "from"))) {
+ iks_insert_attrib(iq, "from", RAYO_JID(rclient));
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, recv iq, availability = %s\n", RAYO_JID(rclient), presence_status_to_string(rclient->availability));
+
+ if (rclient->availability == PS_UNKNOWN) {
+ }
+
+ if (command) {
+ struct rayo_actor *actor = RAYO_LOCATE(to);
+ if (actor) {
+ struct rayo_message *reply = RAYO_SEND(rclient, actor, rayo_message_create_dup(iq));
+ if (reply) {
+ RAYO_SEND(actor, rclient, reply);
+ }
+ RAYO_UNLOCK(actor);
+ } else {
+ RAYO_SEND(globals.server, rclient, rayo_message_create(iks_new_error(iq, STANZA_ERROR_ITEM_NOT_FOUND)));
+ }
+ } else {
+ RAYO_SEND(globals.server, rclient, rayo_message_create(iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "empty IQ request")));
+ }
+}
+
+/**
+ * Send event to mixer subscribers
+ * @param mixer the mixer
+ * @param rayo_event the event to send
+ */
+static void broadcast_mixer_event(struct rayo_mixer *mixer, iks *rayo_event)
+{
+ switch_hash_index_t *hi = NULL;
+ for (hi = switch_core_hash_first(mixer->subscribers); hi; hi = switch_core_hash_next(hi)) {
+ const void *key;
+ void *val;
+ struct rayo_mixer_subscriber *subscriber;
+ switch_core_hash_this(hi, &key, NULL, &val);
+ subscriber = (struct rayo_mixer_subscriber *)val;
+ switch_assert(subscriber);
+ iks_insert_attrib(rayo_event, "to", subscriber->jid);
+ RAYO_SEND_BY_JID(mixer, subscriber->jid, rayo_message_create_dup(rayo_event));
+ }
+}
+
+/**
+ * Handle mixer delete member event
+ */
+static void on_mixer_delete_member_event(struct rayo_mixer *mixer, switch_event_t *event)
+{
+ iks *delete_member_event, *x;
+ const char *uuid = switch_event_get_header(event, "Unique-ID");
+ struct rayo_call *call;
+ struct rayo_mixer_member *member;
+ struct rayo_mixer_subscriber *subscriber;
+
+ /* not a rayo mixer */
+ if (!mixer) {
+ return;
+ }
+
+ /* remove member from mixer */
+ member = (struct rayo_mixer_member *)switch_core_hash_find(mixer->members, uuid);
+ if (!member) {
+ /* not a member */
+ return;
+ }
+ switch_core_hash_delete(mixer->members, uuid);
+
+ /* flag call as available to join another mixer */
+ call = RAYO_CALL_LOCATE(uuid);
+ if (call) {
+ call->joined = 0;
+ RAYO_UNLOCK(call);
+ }
+
+ /* send mixer unjoined event to member DCP */
+ delete_member_event = iks_new_presence("unjoined", RAYO_NS, member->jid, member->dcp_jid);
+ x = iks_find(delete_member_event, "unjoined");
+ iks_insert_attrib(x, "mixer-name", rayo_mixer_get_name(mixer));
+ RAYO_SEND_BY_JID(mixer, member->dcp_jid, rayo_message_create(delete_member_event));
+
+ /* broadcast member unjoined event to subscribers */
+ delete_member_event = iks_new_presence("unjoined", RAYO_NS, RAYO_JID(mixer), "");
+ x = iks_find(delete_member_event, "unjoined");
+ iks_insert_attrib(x, "call-id", uuid);
+ broadcast_mixer_event(mixer, delete_member_event);
+ iks_delete(delete_member_event);
+
+ /* remove member DCP as subscriber to mixer */
+ subscriber = (struct rayo_mixer_subscriber *)switch_core_hash_find(mixer->subscribers, member->dcp_jid);
+ if (subscriber) {
+ subscriber->ref_count--;
+ if (subscriber->ref_count <= 0) {
+ switch_core_hash_delete(mixer->subscribers, member->dcp_jid);
+ }
+ }
+}
+
+/**
+ * Handle mixer destroy event
+ */
+static void on_mixer_destroy_event(struct rayo_mixer *mixer, switch_event_t *event)
+{
+ if (mixer) {
+ /* remove from hash and destroy */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, destroying mixer: %s\n", RAYO_JID(mixer), rayo_mixer_get_name(mixer));
+ RAYO_UNLOCK(mixer); /* release original lock */
+ RAYO_DESTROY(mixer);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "destroy: NULL mixer\n");
+ }
+}
+
+/**
+ * Handle mixer add member event
+ */
+static void on_mixer_add_member_event(struct rayo_mixer *mixer, switch_event_t *event)
+{
+ iks *add_member_event = NULL, *x;
+ const char *uuid = switch_event_get_header(event, "Unique-ID");
+ struct rayo_call *call = RAYO_CALL_LOCATE(uuid);
+
+ if (!mixer) {
+ /* new mixer */
+ const char *mixer_name = switch_event_get_header(event, "Conference-Name");
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "creating mixer: %s\n", mixer_name);
+ mixer = rayo_mixer_create(mixer_name);
+ }
+
+ if (call) {
+ struct rayo_mixer_member *member = NULL;
+ /* add member DCP as subscriber to mixer */
+ struct rayo_mixer_subscriber *subscriber = (struct rayo_mixer_subscriber *)switch_core_hash_find(mixer->subscribers, call->dcp_jid);
+ if (!subscriber) {
+ subscriber = switch_core_alloc(RAYO_POOL(mixer), sizeof(*subscriber));
+ subscriber->ref_count = 0;
+ subscriber->jid = switch_core_strdup(RAYO_POOL(mixer), call->dcp_jid);
+ switch_core_hash_insert(mixer->subscribers, call->dcp_jid, subscriber);
+ }
+ subscriber->ref_count++;
+
+ /* add call as member of mixer */
+ member = switch_core_alloc(RAYO_POOL(mixer), sizeof(*member));
+ member->jid = switch_core_strdup(RAYO_POOL(mixer), RAYO_JID(call));
+ member->dcp_jid = subscriber->jid;
+ switch_core_hash_insert(mixer->members, uuid, member);
+
+ call->joined = 1;
+
+ /* send mixer joined event to member DCP */
+ add_member_event = iks_new_presence("joined", RAYO_NS, RAYO_JID(call), call->dcp_jid);
+ x = iks_find(add_member_event, "joined");
+ iks_insert_attrib(x, "mixer-name", rayo_mixer_get_name(mixer));
+ RAYO_SEND_BY_JID(call, call->dcp_jid, rayo_message_create(add_member_event));
+
+ RAYO_UNLOCK(call);
+ }
+
+ /* broadcast member joined event to subscribers */
+ add_member_event = iks_new_presence("joined", RAYO_NS, RAYO_JID(mixer), "");
+ x = iks_find(add_member_event, "joined");
+ iks_insert_attrib(x, "call-id", uuid);
+ broadcast_mixer_event(mixer, add_member_event);
+ iks_delete(add_member_event);
+}
+
+/**
+ * Receives mixer events from FreeSWITCH core and routes them to the proper Rayo client(s).
+ * @param event received from FreeSWITCH core. It will be destroyed by the core after this function returns.
+ */
+static void route_mixer_event(switch_event_t *event)
+{
+ const char *action = switch_event_get_header(event, "Action");
+ const char *profile = switch_event_get_header(event, "Conference-Profile-Name");
+ const char *mixer_name = switch_event_get_header(event, "Conference-Name");
+ struct rayo_mixer *mixer = NULL;
+
+ if (strcmp(profile, globals.mixer_conf_profile)) {
+ /* don't care about other conferences */
+ goto done;
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "looking for mixer: %s\n", mixer_name);
+ mixer = RAYO_MIXER_LOCATE(mixer_name);
+
+ if (!strcmp("add-member", action)) {
+ on_mixer_add_member_event(mixer, event);
+ } else if (!strcmp("conference-destroy", action)) {
+ on_mixer_destroy_event(mixer, event);
+ } else if (!strcmp("del-member", action)) {
+ on_mixer_delete_member_event(mixer, event);
+ }
+ /* TODO speaking events */
+
+done:
+ RAYO_UNLOCK(mixer);
+}
+
+/**
+ * Handle call originate event - create rayo call and send <iq><ref> to client.
+ * @param rclient The Rayo client
+ * @param event the originate event
+ */
+static void on_call_originate_event(struct rayo_client *rclient, switch_event_t *event)
+{
+ switch_core_session_t *session = NULL;
+ const char *uuid = switch_event_get_header(event, "Unique-ID");
+ struct rayo_call *call = RAYO_CALL_LOCATE(uuid);
+
+ if (call && (session = switch_core_session_locate(uuid))) {
+ iks *response, *ref;
+
+ switch_channel_set_private(switch_core_session_get_channel(session), "rayo_call_private", call);
+ switch_core_session_rwunlock(session);
+
+ /* send response to DCP */
+ response = iks_new("iq");
+ iks_insert_attrib(response, "from", RAYO_JID(globals.server));
+ iks_insert_attrib(response, "to", rayo_call_get_dcp_jid(call));
+ iks_insert_attrib(response, "id", call->dial_id);
+ iks_insert_attrib(response, "type", "result");
+ ref = iks_insert(response, "ref");
+ iks_insert_attrib(ref, "xmlns", RAYO_NS);
+ iks_insert_attrib(ref, "id", uuid);
+ iks_insert_attrib_printf(ref, "uri", "xmpp:%s", RAYO_JID(call));
+ RAYO_SEND(call, rclient, rayo_message_create(response));
+ call->dial_id = NULL;
+ }
+ RAYO_UNLOCK(call);
+}
+
+/**
+ * Handle call end event
+ * @param event the hangup event
+ */
+static void on_call_end_event(switch_event_t *event)
+{
+ struct rayo_call *call = RAYO_CALL_LOCATE(switch_event_get_header(event, "Unique-ID"));
+
+ if (call) {
+#if 0
+ char *event_str;
+ if (switch_event_serialize(event, &event_str, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "%s\n", event_str);
+ switch_safe_free(event_str);
+ }
+#endif
+ switch_event_dup(&call->end_event, event);
+ RAYO_UNLOCK(call); /* decrement ref from creation */
+ RAYO_DESTROY(call);
+ RAYO_UNLOCK(call); /* decrement this ref */
+ }
+}
+
+/**
+ * Handle call answer event
+ * @param rclient the Rayo client
+ * @param event the answer event
+ */
+static void on_call_answer_event(struct rayo_client *rclient, switch_event_t *event)
+{
+ struct rayo_call *call = RAYO_CALL_LOCATE(switch_event_get_header(event, "Unique-ID"));
+ if (call) {
+ iks *revent = iks_new_presence("answered", RAYO_NS,
+ switch_event_get_header(event, "variable_rayo_call_jid"),
+ switch_event_get_header(event, "variable_rayo_dcp_jid"));
+ RAYO_SEND(call, rclient, rayo_message_create(revent));
+ RAYO_UNLOCK(call);
+ }
+}
+
+/**
+ * Handle call ringing event
+ * @param rclient the Rayo client
+ * @param event the ringing event
+ */
+static void on_call_ringing_event(struct rayo_client *rclient, switch_event_t *event)
+{
+ struct rayo_call *call = RAYO_CALL_LOCATE(switch_event_get_header(event, "Unique-ID"));
+ if (call) {
+ iks *revent = iks_new_presence("ringing", RAYO_NS,
+ switch_event_get_header(event, "variable_rayo_call_jid"),
+ switch_event_get_header(event, "variable_rayo_dcp_jid"));
+ RAYO_SEND(call, rclient, rayo_message_create(revent));
+ RAYO_UNLOCK(call);
+ }
+}
+
+/**
+ * Handle call bridge event
+ * @param rclient the Rayo client
+ * @param event the bridge event
+ */
+static void on_call_bridge_event(struct rayo_client *rclient, switch_event_t *event)
+{
+ const char *a_uuid = switch_event_get_header(event, "Unique-ID");
+ const char *b_uuid = switch_event_get_header(event, "Bridge-B-Unique-ID");
+ struct rayo_call *call = RAYO_CALL_LOCATE(a_uuid);
+ struct rayo_call *b_call;
+
+ if (call) {
+ /* send A-leg event */
+ iks *revent = iks_new_presence("joined", RAYO_NS,
+ switch_event_get_header(event, "variable_rayo_call_jid"),
+ switch_event_get_header(event, "variable_rayo_dcp_jid"));
+ iks *joined = iks_find(revent, "joined");
+ iks_insert_attrib(joined, "call-id", b_uuid);
+
+ call->joined = 1;
+
+ RAYO_SEND(call, rclient, rayo_message_create(revent));
+
+ /* send B-leg event */
+ b_call = RAYO_CALL_LOCATE(b_uuid);
+ if (b_call) {
+ revent = iks_new_presence("joined", RAYO_NS, RAYO_JID(b_call), rayo_call_get_dcp_jid(b_call));
+ joined = iks_find(revent, "joined");
+ iks_insert_attrib(joined, "call-id", a_uuid);
+
+ b_call->joined = 1;
+
+ RAYO_SEND_BY_JID(b_call, rayo_call_get_dcp_jid(b_call), rayo_message_create(revent));
+ RAYO_UNLOCK(b_call);
+ }
+ RAYO_UNLOCK(call);
+ }
+}
+
+/**
+ * Handle call unbridge event
+ * @param rclient the Rayo client
+ * @param event the unbridge event
+ */
+static void on_call_unbridge_event(struct rayo_client *rclient, switch_event_t *event)
+{
+ const char *a_uuid = switch_event_get_header(event, "Unique-ID");
+ const char *b_uuid = switch_event_get_header(event, "Bridge-B-Unique-ID");
+ struct rayo_call *call = RAYO_CALL_LOCATE(a_uuid);
+ struct rayo_call *b_call;
+
+ if (call) {
+ /* send A-leg event */
+ iks *revent = iks_new_presence("unjoined", RAYO_NS,
+ switch_event_get_header(event, "variable_rayo_call_jid"),
+ switch_event_get_header(event, "variable_rayo_dcp_jid"));
+ iks *joined = iks_find(revent, "unjoined");
+ iks_insert_attrib(joined, "call-id", b_uuid);
+ RAYO_SEND(call, rclient, rayo_message_create(revent));
+
+ call->joined = 0;
+
+ /* send B-leg event */
+ b_call = RAYO_CALL_LOCATE(b_uuid);
+ if (b_call) {
+ revent = iks_new_presence("unjoined", RAYO_NS, RAYO_JID(b_call), rayo_call_get_dcp_jid(b_call));
+ joined = iks_find(revent, "unjoined");
+ iks_insert_attrib(joined, "call-id", a_uuid);
+ RAYO_SEND_BY_JID(b_call, rayo_call_get_dcp_jid(b_call), rayo_message_create(revent));
+
+ b_call->joined = 0;
+ RAYO_UNLOCK(b_call);
+ }
+ RAYO_UNLOCK(call);
+ }
+}
+
+
+/**
+ * Handle events to deliver to client connection
+ * @param rclient the Rayo client connection to receive the event
+ * @param event the event.
+ */
+static void rayo_client_handle_event(struct rayo_client *rclient, switch_event_t *event)
+{
+ if (event) {
+ switch (event->event_id) {
+ case SWITCH_EVENT_CHANNEL_ORIGINATE:
+ on_call_originate_event(rclient, event);
+ break;
+ case SWITCH_EVENT_CHANNEL_PROGRESS_MEDIA:
+ on_call_ringing_event(rclient, event);
+ break;
+ case SWITCH_EVENT_CHANNEL_ANSWER:
+ on_call_answer_event(rclient, event);
+ break;
+ case SWITCH_EVENT_CHANNEL_BRIDGE:
+ on_call_bridge_event(rclient, event);
+ break;
+ case SWITCH_EVENT_CHANNEL_UNBRIDGE:
+ on_call_unbridge_event(rclient, event);
+ break;
+ default:
+ /* don't care */
+ break;
+ }
+ }
+}
+
+/**
+ * Receives events from FreeSWITCH core and routes them to the proper Rayo client.
+ * @param event received from FreeSWITCH core. It will be destroyed by the core after this function returns.
+ */
+static void route_call_event(switch_event_t *event)
+{
+ char *uuid = switch_event_get_header(event, "unique-id");
+ char *dcp_jid = switch_event_get_header(event, "variable_rayo_dcp_jid");
+ char *event_subclass = switch_event_get_header(event, "Event-Subclass");
+
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "got event %s %s\n", switch_event_name(event->event_id), zstr(event_subclass) ? "" : event_subclass);
+
+ /* this event is for a rayo client */
+ if (!zstr(dcp_jid)) {
+ struct rayo_actor *actor;
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "%s rayo event %s\n", dcp_jid, switch_event_name(event->event_id));
+
+ actor = RAYO_LOCATE(dcp_jid);
+ if (actor && actor->type == RAT_CLIENT) {
+ if (RAYO_CLIENT(actor)->is_admin) {
+ /* FIXME ignore? */
+ } else {
+ /* process event and route to client */
+ rayo_client_handle_event(RAYO_CLIENT(actor), event);
+ }
+ } else {
+ /* TODO orphaned call... maybe allow events to queue so they can be delivered on reconnect? */
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Orphaned call event %s to %s\n", switch_event_name(event->event_id), dcp_jid);
+ }
+ RAYO_UNLOCK(actor);
+ }
+}
+
+/**
+ * Create server.
+ * @param domain the domain name
+ * @return the domain
+ */
+static struct rayo_actor *rayo_server_create(const char *domain)
+{
+ switch_memory_pool_t *pool;
+ struct rayo_actor *new_server = NULL;
+
+ switch_core_new_memory_pool(&pool);
+ new_server = switch_core_alloc(pool, sizeof(*new_server));
+ RAYO_ACTOR_INIT(RAYO_ACTOR(new_server), pool, RAT_SERVER, "", domain, domain, NULL, rayo_server_send);
+
+ return new_server;
+}
+
+/**
+ * Create an offer for a call
+ * @param call the call
+ * @param session the session
+ * @return the offer
+ */
+static iks *rayo_create_offer(struct rayo_call *call, switch_core_session_t *session)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ switch_caller_profile_t *profile = switch_channel_get_caller_profile(channel);
+ iks *presence = iks_new("presence");
+ iks *offer = iks_insert(presence, "offer");
+
+ iks_insert_attrib(presence, "from", RAYO_JID(call));
+ iks_insert_attrib(offer, "from", profile->caller_id_number);
+ iks_insert_attrib(offer, "to", profile->destination_number);
+ iks_insert_attrib(offer, "xmlns", RAYO_NS);
+
+ /* add signaling headers */
+ {
+ switch_event_header_t *var;
+ add_header(offer, "from", switch_channel_get_variable(channel, "sip_full_from"));
+ add_header(offer, "to", switch_channel_get_variable(channel, "sip_full_to"));
+ add_header(offer, "via", switch_channel_get_variable(channel, "sip_full_via"));
+
+ /* get all variables prefixed with sip_r_ */
+ for (var = switch_channel_variable_first(channel); var; var = var->next) {
+ if (!strncmp("sip_r_", var->name, 6)) {
+ add_header(offer, var->name + 6, var->value);
+ }
+ }
+ switch_channel_variable_last(channel);
+ }
+
+ return presence;
+}
+
+/**
+ * Monitor rayo call activity - detect idle
+ */
+static switch_status_t rayo_call_on_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int i)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ struct rayo_call *call = (struct rayo_call *)switch_channel_get_private(channel, "rayo_call_private");
+ if (call) {
+ switch_time_t now = switch_micro_time_now();
+ switch_time_t idle_start = call->idle_start_time;
+ int idle_duration_ms = (now - idle_start) / 1000;
+ /* detect idle session (rayo-client has stopped controlling call) and terminate call */
+ if (!rayo_call_is_joined(call) && idle_duration_ms > globals.max_idle_ms) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Ending abandoned call. idle_duration_ms = %i ms\n", idle_duration_ms);
+ switch_channel_hangup(channel, RAYO_CAUSE_HANGUP);
+ }
+ }
+ return SWITCH_STATUS_SUCCESS;
+}
+
+#define RAYO_USAGE "[bridge <uuid>|conference <name>]"
+/**
+ * Offer call and park channel
+ */
+SWITCH_STANDARD_APP(rayo_app)
+{
+ int ok = 0;
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ struct rayo_call *call = (struct rayo_call *)switch_channel_get_private(channel, "rayo_call_private");
+ const char *app = ""; /* optional app to execute */
+ const char *app_args = ""; /* app args */
+
+ /* is outbound call already under control? */
+ if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
+ /* check origination args */
+ if (!zstr(data)) {
+ char *argv[2] = { 0 };
+ char *args = switch_core_session_strdup(session, data);
+ int argc = switch_separate_string(args, ' ', argv, sizeof(argv) / sizeof(argv[0]));
+ if (argc) {
+ if (!strcmp("conference", argv[0])) {
+ app = "conference";
+ app_args = argv[1];
+ } else if (!strcmp("bridge", argv[0])) {
+ app = "intercept";
+ app_args = argv[1];
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Invalid rayo args: %s\n", data);
+ goto done;
+ }
+ }
+ }
+ if (!call) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Missing rayo call!!\n");
+ goto done;
+ }
+ ok = 1;
+ } else {
+ /* inbound call - offer control */
+ switch_hash_index_t *hi = NULL;
+ iks *offer = NULL;
+ if (call) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Call is already under Rayo 3PCC!\n");
+ goto done;
+ }
+
+ call = rayo_call_create(switch_core_session_get_uuid(session));
+ switch_channel_set_variable(switch_core_session_get_channel(session), "rayo_call_jid", RAYO_JID(call));
+ switch_channel_set_private(switch_core_session_get_channel(session), "rayo_call_private", call);
+
+ offer = rayo_create_offer(call, session);
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Offering call for Rayo 3PCC\n");
+
+ /* Offer call to all ONLINE clients */
+ /* TODO load balance offers so first session doesn't always get offer first? */
+ switch_mutex_lock(globals.clients_mutex);
+ for (hi = switch_hash_first(NULL, globals.clients_roster); hi; hi = switch_hash_next(hi)) {
+ struct rayo_client *rclient;
+ const void *key;
+ void *val;
+ switch_hash_this(hi, &key, NULL, &val);
+ rclient = (struct rayo_client *)val;
+ switch_assert(rclient);
+
+ /* is session available to take call? */
+ if (rclient->availability == PS_ONLINE) {
+ ok = 1;
+ switch_core_hash_insert(call->pcps, RAYO_JID(rclient), "1");
+ iks_insert_attrib(offer, "to", RAYO_JID(rclient));
+ RAYO_SEND_BY_JID(call, RAYO_JID(rclient), rayo_message_create_dup(offer));
+ }
+ }
+ iks_delete(offer);
+ switch_mutex_unlock(globals.clients_mutex);
+
+ /* nobody to offer to */
+ if (!ok) {
+ switch_channel_hangup(channel, RAYO_CAUSE_DECLINE);
+ }
+ }
+
+done:
+
+ if (ok) {
+ switch_channel_set_variable(channel, "hangup_after_bridge", "false");
+ switch_channel_set_variable(channel, "transfer_after_bridge", "false");
+ switch_channel_set_variable(channel, "park_after_bridge", "true");
+ switch_channel_set_variable(channel, SWITCH_SEND_SILENCE_WHEN_IDLE_VARIABLE, "-1"); /* required so that output mixing works */
+ switch_core_event_hook_add_read_frame(session, rayo_call_on_read_frame);
+ if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
+ if (!zstr(app)) {
+ switch_core_session_execute_application(session, app, app_args);
+ }
+ }
+ switch_ivr_park(session, NULL);
+ }
+}
+
+/**
+ * Stream locates client
+ */
+static struct rayo_actor *xmpp_stream_client_locate(struct xmpp_stream *stream, const char *jid)
+{
+ struct rayo_actor *actor = NULL;
+ if (xmpp_stream_is_s2s(stream)) {
+ actor = RAYO_LOCATE(jid);
+ if (!actor) {
+ /* previously unknown client - add it */
+ struct rayo_peer_server *rserver = RAYO_PEER_SERVER(xmpp_stream_get_private(stream));
+ actor = RAYO_ACTOR(rayo_client_create(jid, xmpp_stream_get_jid(stream), PS_UNKNOWN, rayo_client_send, 0, rserver));
+ RAYO_RDLOCK(actor);
+ } else if (actor->type != RAT_CLIENT) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, not a client: %s\n", xmpp_stream_get_jid(stream), jid);
+ RAYO_UNLOCK(actor);
+ actor = NULL;
+ }
+ } else {
+ actor = RAYO_ACTOR(xmpp_stream_get_private(stream));
+ RAYO_RDLOCK(actor);
+ }
+ return actor;
+}
+
+/**
+ * Handle new stream creation
+ * @param stream the new stream
+ */
+static void on_xmpp_stream_ready(struct xmpp_stream *stream)
+{
+ if (xmpp_stream_is_s2s(stream)) {
+ if (xmpp_stream_is_incoming(stream)) {
+ /* peer server belongs to a s2s inbound stream */
+ xmpp_stream_set_private(stream, rayo_peer_server_create(xmpp_stream_get_jid(stream)));
+ } else {
+ /* send directed presence to domain */
+ iks *presence = iks_new("presence");
+ iks *x;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "sending server presence\n");
+
+ iks_insert_attrib(presence, "from", RAYO_JID(globals.server));
+ iks_insert_attrib(presence, "to", xmpp_stream_get_jid(stream));
+ x = iks_insert(presence, "show");
+ iks_insert_cdata(x, "chat", 4);
+ RAYO_SEND_BY_JID(globals.server, xmpp_stream_get_jid(stream), rayo_message_create(presence));
+ }
+ } else {
+ /* client belongs to stream */
+ xmpp_stream_set_private(stream, rayo_client_create(xmpp_stream_get_jid(stream), xmpp_stream_get_jid(stream), PS_OFFLINE, rayo_client_send, 0, NULL));
+ }
+}
+
+/**
+ * Checks client availability. If unknown, client presence is probed.
+ * @param rclient to check
+ */
+static void rayo_client_presence_check(struct rayo_client *rclient)
+{
+ if (rclient->availability == PS_UNKNOWN) {
+ /* for now, set online */
+ rclient->availability = PS_ONLINE;
+#if 0
+ /* send probe */
+ struct rayo_message *reply;
+ switch_time_t now = switch_micro_time_now();
+
+ /* throttle probes... */
+ if (now - rclient->last_probe > 1000 * 1000 * 10) {
+ iks *probe = iks_new("presence");
+ rclient->last_probe = now;
+ iks_insert_attrib(probe, "type", "probe");
+ iks_insert_attrib(probe, "from", RAYO_JID(globals.server));
+ iks_insert_attrib(probe, "to", RAYO_JID(rclient));
+ reply = RAYO_SEND(globals.server, rclient, rayo_message_create(probe));
+ if (reply) {
+ rayo_message_destroy(reply);
+ }
+ }
+ } else {
+ rclient->last_probe = 0;
+#endif
+ }
+}
+
+/**
+ * Handle stream stanza
+ * @param stream the stream
+ * @param stanza the stanza to process
+ */
+static void on_xmpp_stream_recv(struct xmpp_stream *stream, iks *stanza)
+{
+ const char *name = iks_name(stanza);
+ if (!strcmp("iq", name)) {
+ const char *from = iks_find_attrib_soft(stanza, "from");
+ struct rayo_actor *actor = xmpp_stream_client_locate(stream, from);
+ if (actor) {
+ rayo_client_presence_check(RAYO_CLIENT(actor));
+ rayo_client_command_recv(RAYO_CLIENT(actor), stanza);
+ RAYO_UNLOCK(actor);
+ }
+ } else if (!strcmp("presence", name)) {
+ const char *from = iks_find_attrib_soft(stanza, "from");
+ struct rayo_actor *actor = xmpp_stream_client_locate(stream, from);
+ if (actor) {
+ on_client_presence(RAYO_CLIENT(actor), stanza);
+ RAYO_UNLOCK(actor);
+ }
+ } else if (!strcmp("message", name)) {
+ const char *from = iks_find_attrib_soft(stanza, "from");
+ struct rayo_actor *actor = xmpp_stream_client_locate(stream, from);
+ if (actor) {
+ rayo_client_presence_check(RAYO_CLIENT(actor));
+ on_client_message(RAYO_CLIENT(actor), stanza);
+ RAYO_UNLOCK(actor);
+ }
+ }
+}
+
+/**
+ * Handle stream destruction
+ */
+static void on_xmpp_stream_destroy(struct xmpp_stream *stream)
+{
+ /* destroy peer server / client associated with this stream */
+ void *actor = xmpp_stream_get_private(stream);
+ if (actor) {
+ RAYO_UNLOCK(actor);
+ RAYO_DESTROY(actor);
+ }
+}
+
+/**
+ * Process module XML configuration
+ * @param pool memory pool to allocate from
+ * @param config_file to use
+ * @return SWITCH_STATUS_SUCCESS on successful configuration
+ */
+static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_file)
+{
+ switch_xml_t cfg, xml;
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Configuring module\n");
+ if (!(xml = switch_xml_open_cfg(config_file, &cfg, NULL))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", config_file);
+ return SWITCH_STATUS_TERM;
+ }
+
+ /* set defaults */
+ globals.max_idle_ms = 30000;
+ globals.mixer_conf_profile = "sla";
+
+ /* get params */
+ {
+ switch_xml_t settings = switch_xml_child(cfg, "settings");
+ if (settings) {
+ switch_xml_t param;
+ for (param = switch_xml_child(settings, "param"); param; param = param->next) {
+ const char *var = switch_xml_attr_soft(param, "name");
+ const char *val = switch_xml_attr_soft(param, "value");
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "param: %s = %s\n", var, val);
+ if (!strcasecmp(var, "max-idle-sec")) {
+ if (switch_is_number(val)) {
+ int max_idle_sec = atoi(val);
+ if (max_idle_sec > 0) {
+ globals.max_idle_ms = max_idle_sec * 1000;
+ }
+ }
+ } else if (!strcasecmp(var, "mixer-conf-profile")) {
+ if (!zstr(val)) {
+ globals.mixer_conf_profile = switch_core_strdup(pool, val);
+ }
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unsupported param: %s\n", var);
+ }
+ }
+ }
+ }
+
+ /* configure dial gateways */
+ {
+ switch_xml_t dial_gateways = switch_xml_child(cfg, "dial-gateways");
+
+ /* set defaults */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Setting default dial-gateways\n");
+ dial_gateway_add("default", "sofia/gateway/outbound/", 0);
+ dial_gateway_add("tel:", "sofia/gateway/outbound/", 4);
+ dial_gateway_add("user", "", 0);
+ dial_gateway_add("sofia", "", 0);
+
+ if (dial_gateways) {
+ switch_xml_t dg;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Setting configured dial-gateways\n");
+ for (dg = switch_xml_child(dial_gateways, "dial-gateway"); dg; dg = dg->next) {
+ const char *uri_prefix = switch_xml_attr_soft(dg, "uriprefix");
+ const char *dial_prefix = switch_xml_attr_soft(dg, "dialprefix");
+ const char *strip_str = switch_xml_attr_soft(dg, "strip");
+ int strip = 0;
+
+ if (!zstr(strip_str) && switch_is_number(strip_str)) {
+ strip = atoi(strip_str);
+ if (strip < 0) {
+ strip = 0;
+ }
+ }
+ if (!zstr(uri_prefix)) {
+ dial_gateway_add(uri_prefix, dial_prefix, strip);
+ }
+ }
+ }
+ }
+
+ /* configure domain */
+ {
+ switch_xml_t domain = switch_xml_child(cfg, "domain");
+ if (domain) {
+ switch_xml_t l;
+ const char *shared_secret = switch_xml_attr_soft(domain, "shared-secret");
+ const char *name = switch_xml_attr_soft(domain, "name");
+ if (zstr(name)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing <domain name=\"... failed to configure rayo server\n");
+ status = SWITCH_STATUS_FALSE;
+ goto done;
+ }
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Rayo domain set to %s\n", name);
+
+ if (zstr(shared_secret)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing shared secret for %s domain. Server dialback will not work\n", name);
+ }
+
+ globals.xmpp_context = xmpp_stream_context_create(name, shared_secret, on_xmpp_stream_ready, on_xmpp_stream_recv, on_xmpp_stream_destroy);
+ globals.server = rayo_server_create(name);
+
+ /* configure authorized users for this domain */
+ l = switch_xml_child(domain, "users");
+ if (l) {
+ switch_xml_t u;
+ for (u = switch_xml_child(l, "user"); u; u = u->next) {
+ const char *user = switch_xml_attr_soft(u, "name");
+ const char *password = switch_xml_attr_soft(u, "password");
+ xmpp_stream_context_add_user(globals.xmpp_context, user, password);
+ }
+ }
+
+ /* get listeners for this domain */
+ for (l = switch_xml_child(domain, "listen"); l; l = l->next) {
+ const char *address = switch_xml_attr_soft(l, "address");
+ const char *port = switch_xml_attr_soft(l, "port");
+ const char *type = switch_xml_attr_soft(l, "type");
+ const char *acl = switch_xml_attr_soft(l, "acl");
+ int is_s2s = 0;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s listener: %s:%s\n", type, address, port);
+ is_s2s = !strcmp("s2s", type);
+ if (!is_s2s && strcmp("c2s", type)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Type must be \"c2s\" or \"s2s\"!\n");
+ status = SWITCH_STATUS_FALSE;
+ goto done;
+ }
+ if (zstr(address)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing address!\n");
+ status = SWITCH_STATUS_FALSE;
+ goto done;
+ }
+ if (!zstr(port) && !switch_is_number(port)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Port must be an integer!\n");
+ status = SWITCH_STATUS_FALSE;
+ goto done;
+ }
+ if (xmpp_stream_context_listen(globals.xmpp_context, address, atoi(port), is_s2s, acl) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to create %s listener: %s:%s\n", type, address, port);
+ }
+ }
+
+ /* get outbound server connections */
+ for (l = switch_xml_child(domain, "connect"); l; l = l->next) {
+ const char *domain = switch_xml_attr_soft(l, "domain");
+ const char *address = switch_xml_attr_soft(l, "address");
+ const char *port = switch_xml_attr_soft(l, "port");
+ if (!zstr(port) && !switch_is_number(port)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Outbound server port must be an integer!\n");
+ status = SWITCH_STATUS_FALSE;
+ goto done;
+ }
+ if (zstr(address) && zstr(domain)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing outbound server address!\n");
+ status = SWITCH_STATUS_FALSE;
+ goto done;
+ }
+ xmpp_stream_context_connect(globals.xmpp_context, domain, address, atoi(port));
+ }
+ }
+ }
+
+done:
+ switch_xml_free(xml);
+
+ return status;
+}
+
+/**
+ * Dump rayo actor stats
+ */
+static void rayo_actor_dump(struct rayo_actor *actor, switch_stream_handle_t *stream)
+{
+ if (actor->type == RAT_CLIENT) {
+ stream->write_function(stream, "TYPE='%s',SUBTYPE='%s',ID='%s',JID='%s',DOMAIN='%s',REFS=%i,STATUS='%s'", rayo_actor_type_to_string(actor->type), actor->subtype, actor->id, RAYO_JID(actor), RAYO_DOMAIN(actor), actor->ref_count, presence_status_to_string(RAYO_CLIENT(actor)->availability));
+ } else {
+ stream->write_function(stream, "TYPE='%s',SUBTYPE='%s',ID='%s',JID='%s',DOMAIN='%s',REFS=%i", rayo_actor_type_to_string(actor->type), actor->subtype, actor->id, RAYO_JID(actor), RAYO_DOMAIN(actor), actor->ref_count);
+ }
+}
+
+/**
+ * Dump rayo actors
+ */
+static int dump_api(const char *cmd, switch_stream_handle_t *stream)
+{
+ switch_hash_index_t *hi;
+ if (!zstr(cmd)) {
+ return 0;
+ }
+
+ stream->write_function(stream, "\nENTITIES\n");
+ switch_mutex_lock(globals.actors_mutex);
+ for (hi = switch_core_hash_first(globals.actors); hi; hi = switch_core_hash_next(hi)) {
+ struct rayo_actor *actor = NULL;
+ const void *key;
+ void *val;
+ switch_core_hash_this(hi, &key, NULL, &val);
+ actor = (struct rayo_actor *)val;
+ switch_assert(actor);
+ stream->write_function(stream, " ");
+ rayo_actor_dump(actor, stream);
+ stream->write_function(stream, "\n");
+ }
+
+ for (hi = switch_core_hash_first(globals.destroy_actors); hi; hi = switch_core_hash_next(hi)) {
+ struct rayo_actor *actor = NULL;
+ const void *key;
+ void *val;
+ switch_core_hash_this(hi, &key, NULL, &val);
+ actor = (struct rayo_actor *)val;
+ switch_assert(actor);
+ stream->write_function(stream, "(DEAD) ");
+ rayo_actor_dump(actor, stream);
+ stream->write_function(stream, "\n");
+ }
+ switch_mutex_unlock(globals.actors_mutex);
+
+ xmpp_stream_context_dump(globals.xmpp_context, stream);
+
+ return 1;
+}
+
+/**
+ * Process response to console command_api
+ */
+static struct rayo_message *rayo_console_client_send(struct rayo_actor *from, struct rayo_actor *actor, struct rayo_message *msg, const char *file, int line)
+{
+ iks *response = msg->payload;
+
+ if (response) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "\nRECV: from %s, %s\n", RAYO_JID(from), iks_string(iks_stack(response), response));
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "\nRECV: (null) from %s\n", RAYO_JID(from));
+ }
+
+ return NULL;
+}
+
+/**
+ * Create a new Rayo console client
+ * @return the new client or NULL
+ */
+static struct rayo_client *rayo_console_client_create(void)
+{
+ struct rayo_client *client = NULL;
+ char *jid = NULL;
+ char id[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 };
+ switch_uuid_str(id, sizeof(id));
+ jid = switch_mprintf("%s@%s/console", id, RAYO_JID(globals.server));
+ client = rayo_client_create(jid, NULL, PS_OFFLINE, rayo_console_client_send, 1, NULL);
+ free(jid);
+ return client;
+}
+
+/**
+ * Send command from console
+ */
+static void send_console_command(struct rayo_client *client, const char *to, const char *command_str)
+{
+ iks *command = NULL;
+ iksparser *p = iks_dom_new(&command);
+
+ /* check if aliased */
+ const char *alias = switch_core_hash_find(globals.cmd_aliases, command_str);
+ if (!zstr(alias)) {
+ command_str = alias;
+ }
+
+ if (iks_parse(p, command_str, 0, 1) == IKS_OK && command) {
+ char *str;
+ iks *iq = NULL;
+
+ /* is command already wrapped in IQ? */
+ if (!strcmp(iks_name(command), "iq")) {
+ /* command already IQ */
+ iq = command;
+ } else {
+ /* create IQ to wrap command */
+ iq = iks_new_within("iq", iks_stack(command));
+ iks_insert_node(iq, command);
+ }
+
+ /* fill in command attribs */
+ iks_insert_attrib(iq, "to", to);
+ if (!iks_find_attrib(iq, "type")) {
+ iks_insert_attrib(iq, "type", "set");
+ }
+ if (!iks_find_attrib(iq, "id")) {
+ iks_insert_attrib_printf(iq, "id", "console-%i", RAYO_SEQ_NEXT(client));
+ }
+ iks_insert_attrib(iq, "from", RAYO_JID(client));
+
+ /* send command */
+ str = iks_string(iks_stack(iq), iq);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "\nSEND: to %s, %s\n", to, str);
+ rayo_client_command_recv(client, iq);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "bad request xml\n");
+ }
+ iks_parser_delete(p);
+}
+
+/**
+ * Send command to rayo actor
+ */
+static int command_api(const char *cmd, switch_stream_handle_t *stream)
+{
+ char *cmd_dup = strdup(cmd);
+ char *argv[2] = { 0 };
+ int argc = switch_separate_string(cmd_dup, ' ', argv, sizeof(argv) / sizeof(argv[0]));
+
+ if (argc != 2) {
+ free(cmd_dup);
+ return 0;
+ }
+
+ /* send command */
+ send_console_command(globals.console, argv[0], argv[1]);
+ stream->write_function(stream, "+OK\n");
+
+ free(cmd_dup);
+ return 1;
+}
+
+/**
+ * Send message from console
+ */
+static void send_console_message(struct rayo_client *client, const char *to, const char *message_str)
+{
+ struct rayo_actor *actor = RAYO_LOCATE(to);
+ if (actor) {
+ struct rayo_message *reply;
+ iks *message = NULL, *x;
+ message = iks_new("message");
+ iks_insert_attrib(message, "to", to);
+ iks_insert_attrib(message, "from", RAYO_JID(client));
+ iks_insert_attrib_printf(message, "id", "console-%i", RAYO_SEQ_NEXT(client));
+ iks_insert_attrib(message, "type", "chat");
+ x = iks_insert(message, "body");
+ iks_insert_cdata(x, message_str, strlen(message_str));
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "\nSEND: to %s, %s\n", to, iks_string(iks_stack(message), message));
+ reply = RAYO_SEND(client, actor, rayo_message_create(message));
+ if (reply) {
+ /* ignore reply */
+ rayo_message_destroy(reply);
+ }
+ RAYO_UNLOCK(actor);
+ }
+}
+
+/**
+ * Send message to rayo actor
+ */
+static int message_api(const char *msg, switch_stream_handle_t *stream)
+{
+ char *msg_dup = strdup(msg);
+ char *argv[2] = { 0 };
+ int argc = switch_separate_string(msg_dup, ' ', argv, sizeof(argv) / sizeof(argv[0]));
+
+ if (argc != 2) {
+ free(msg_dup);
+ return 0;
+ }
+
+ /* send message */
+ send_console_message(globals.console, argv[0], argv[1]);
+ stream->write_function(stream, "+OK\n");
+
+ free(msg_dup);
+ return 1;
+}
+
+/**
+ * Send presence from console
+ */
+static void send_console_presence(struct rayo_client *client, const char *to, int is_online)
+{
+ struct rayo_actor *actor = RAYO_LOCATE(to);
+ if (actor) {
+ struct rayo_message *reply;
+ iks *presence = NULL, *x;
+ presence = iks_new("presence");
+ iks_insert_attrib(presence, "to", to);
+ iks_insert_attrib(presence, "from", RAYO_JID(client));
+ iks_insert_attrib_printf(presence, "id", "console-%i", RAYO_SEQ_NEXT(client));
+ if (!is_online) {
+ iks_insert_attrib(presence, "type", "unavailable");
+ }
+ x = iks_insert(presence, "show");
+ iks_insert_cdata(x, is_online ? "chat" : "dnd", 0);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "\nSEND: to %s, %s\n", to, iks_string(iks_stack(presence), presence));
+ reply = RAYO_SEND(client, actor, rayo_message_create(presence));
+ if (reply) {
+ /* ignore reply */
+ rayo_message_destroy(reply);
+ }
+ RAYO_UNLOCK(actor);
+ }
+}
+
+/**
+ * Send console presence
+ */
+static int presence_api(const char *cmd, switch_stream_handle_t *stream)
+{
+ char *cmd_dup = strdup(cmd);
+ char *argv[2] = { 0 };
+ int argc = switch_separate_string(cmd_dup, ' ', argv, sizeof(argv) / sizeof(argv[0]));
+ int is_online = 0;
+
+ if (argc != 2) {
+ free(cmd_dup);
+ return 0;
+ }
+
+ if (!strcmp("online", argv[1])) {
+ is_online = 1;
+ } else if (strcmp("offline", argv[1])) {
+ free(cmd_dup);
+ return 0;
+ }
+
+ /* send presence */
+ send_console_presence(globals.console, argv[0], is_online);
+ stream->write_function(stream, "+OK\n");
+ free(cmd_dup);
+ return 1;
+}
+
+#define RAYO_API_SYNTAX "status | (cmd <jid> <command>) | (msg <jid> <message text>) | (presence <jid> <online|offline>)"
+SWITCH_STANDARD_API(rayo_api)
+{
+ int success = 0;
+ if (!strncmp("status", cmd, 6)) {
+ success = dump_api(cmd + 6, stream);
+ } else if (!strncmp("cmd", cmd, 3)) {
+ success = command_api(cmd + 3, stream);
+ } else if (!strncmp("msg", cmd, 3)) {
+ success = message_api(cmd + 3, stream);
+ } else if (!strncmp("presence", cmd, 8)) {
+ success = presence_api(cmd + 8, stream);
+ }
+
+ if (!success) {
+ stream->write_function(stream, "-ERR: USAGE %s\n", RAYO_API_SYNTAX);
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Console auto-completion for all internal actors
+ */
+switch_status_t list_internal(const char *line, const char *cursor, switch_console_callback_match_t **matches)
+{
+ switch_hash_index_t *hi;
+ void *val;
+ const void *vvar;
+ switch_console_callback_match_t *my_matches = NULL;
+ switch_status_t status = SWITCH_STATUS_FALSE;
+ struct rayo_actor *actor;
+
+ switch_mutex_lock(globals.actors_mutex);
+ for (hi = switch_hash_first(NULL, globals.actors); hi; hi = switch_hash_next(hi)) {
+ switch_hash_this(hi, &vvar, NULL, &val);
+
+ actor = (struct rayo_actor *) val;
+ if (actor->type != RAT_CLIENT && actor->type != RAT_PEER_SERVER) {
+ switch_console_push_match(&my_matches, (const char *) vvar);
+ }
+ }
+ switch_mutex_unlock(globals.actors_mutex);
+
+ if (my_matches) {
+ *matches = my_matches;
+ status = SWITCH_STATUS_SUCCESS;
+ }
+
+ return status;
+}
+
+/**
+ * Console auto-completion for all external actors
+ */
+switch_status_t list_external(const char *line, const char *cursor, switch_console_callback_match_t **matches)
+{
+ switch_hash_index_t *hi;
+ void *val;
+ const void *vvar;
+ switch_console_callback_match_t *my_matches = NULL;
+ switch_status_t status = SWITCH_STATUS_FALSE;
+ struct rayo_actor *actor;
+
+ switch_mutex_lock(globals.actors_mutex);
+ for (hi = switch_hash_first(NULL, globals.actors); hi; hi = switch_hash_next(hi)) {
+ switch_hash_this(hi, &vvar, NULL, &val);
+
+ actor = (struct rayo_actor *) val;
+ if (actor->type == RAT_CLIENT || actor->type == RAT_PEER_SERVER) {
+ switch_console_push_match(&my_matches, (const char *) vvar);
+ }
+ }
+ switch_mutex_unlock(globals.actors_mutex);
+
+ if (my_matches) {
+ *matches = my_matches;
+ status = SWITCH_STATUS_SUCCESS;
+ }
+
+ return status;
+}
+
+/**
+ * Console auto-completion for all actors
+ */
+switch_status_t list_all(const char *line, const char *cursor, switch_console_callback_match_t **matches)
+{
+ switch_hash_index_t *hi;
+ void *val;
+ const void *vvar;
+ switch_console_callback_match_t *my_matches = NULL;
+ switch_status_t status = SWITCH_STATUS_FALSE;
+
+ switch_mutex_lock(globals.actors_mutex);
+ for (hi = switch_hash_first(NULL, globals.actors); hi; hi = switch_hash_next(hi)) {
+ switch_hash_this(hi, &vvar, NULL, &val);
+ switch_console_push_match(&my_matches, (const char *) vvar);
+ }
+ switch_mutex_unlock(globals.actors_mutex);
+
+ if (my_matches) {
+ *matches = my_matches;
+ status = SWITCH_STATUS_SUCCESS;
+ }
+
+ return status;
+}
+
+/**
+ * Add an alias to an API command
+ * @param alias_name
+ * @param alias_cmd
+ */
+static void rayo_add_cmd_alias(const char *alias_name, const char *alias_cmd)
+{
+ char *cmd = switch_core_sprintf(globals.pool, "add rayo cmd ::rayo::list_actors %s", alias_name);
+ switch_console_set_complete(cmd);
+ switch_core_hash_insert(globals.cmd_aliases, alias_name, alias_cmd);
+}
+
+/**
+ * Load module
+ */
+SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load)
+{
+ switch_api_interface_t *api_interface;
+ switch_application_interface_t *app_interface;
+
+ *module_interface = switch_loadable_module_create_module_interface(pool, modname);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Loading module\n");
+
+ memset(&globals, 0, sizeof(globals));
+ globals.pool = pool;
+ switch_core_hash_init(&globals.command_handlers, pool);
+ switch_core_hash_init(&globals.event_handlers, pool);
+ switch_core_hash_init(&globals.clients_roster, pool);
+ switch_mutex_init(&globals.clients_mutex, SWITCH_MUTEX_NESTED, pool);
+ switch_core_hash_init(&globals.actors, pool);
+ switch_core_hash_init(&globals.destroy_actors, pool);
+ switch_core_hash_init(&globals.actors_by_id, pool);
+ switch_mutex_init(&globals.actors_mutex, SWITCH_MUTEX_NESTED, pool);
+ switch_core_hash_init(&globals.dial_gateways, pool);
+ switch_core_hash_init(&globals.cmd_aliases, pool);
+
+ /* server commands */
+ rayo_actor_command_handler_add(RAT_SERVER, "", "get:"IKS_NS_XMPP_PING":ping", on_iq_xmpp_ping);
+ rayo_actor_command_handler_add(RAT_SERVER, "", "get:"IKS_NS_XMPP_DISCO":query", on_iq_get_xmpp_disco);
+ rayo_actor_command_handler_add(RAT_SERVER, "", "set:"RAYO_NS":dial", on_rayo_dial);
+
+ /* Rayo call commands */
+ rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_NS":accept", on_rayo_accept);
+ rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_NS":answer", on_rayo_answer);
+ rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_NS":redirect", on_rayo_redirect);
+ rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_NS":reject", on_rayo_hangup); /* handles both reject and hangup */
+ rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_NS":hangup", on_rayo_hangup); /* handles both reject and hangup */
+ rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_NS":join", on_rayo_join);
+ rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_NS":unjoin", on_rayo_unjoin);
+
+ switch_event_bind(modname, SWITCH_EVENT_CHANNEL_ORIGINATE, NULL, route_call_event, NULL);
+ switch_event_bind(modname, SWITCH_EVENT_CHANNEL_PROGRESS_MEDIA, NULL, route_call_event, NULL);
+ switch_event_bind(modname, SWITCH_EVENT_CHANNEL_ANSWER, NULL, route_call_event, NULL);
+ switch_event_bind(modname, SWITCH_EVENT_CHANNEL_BRIDGE, NULL, route_call_event, NULL);
+ switch_event_bind(modname, SWITCH_EVENT_CHANNEL_UNBRIDGE, NULL, route_call_event, NULL);
+
+ switch_event_bind(modname, SWITCH_EVENT_CHANNEL_DESTROY, NULL, on_call_end_event, NULL);
+
+ switch_event_bind(modname, SWITCH_EVENT_CUSTOM, "conference::maintenance", route_mixer_event, NULL);
+
+ SWITCH_ADD_APP(app_interface, "rayo", "Offer call control to Rayo client(s)", "", rayo_app, RAYO_USAGE, SAF_SUPPORT_NOMEDIA);
+ SWITCH_ADD_API(api_interface, "rayo", "Query rayo status", rayo_api, RAYO_API_SYNTAX);
+
+ /* set up rayo components */
+ if (rayo_components_load(module_interface, pool, RAYO_CONFIG_FILE) != SWITCH_STATUS_SUCCESS) {
+ return SWITCH_STATUS_TERM;
+ }
+
+ /* configure / open sockets */
+ if(do_config(globals.pool, RAYO_CONFIG_FILE) != SWITCH_STATUS_SUCCESS) {
+ return SWITCH_STATUS_TERM;
+ }
+
+ /* create admin client */
+ globals.console = rayo_console_client_create();
+
+ switch_console_set_complete("add rayo status");
+ switch_console_set_complete("add rayo cmd ::rayo::list_internal");
+ switch_console_set_complete("add rayo msg ::rayo::list_external");
+ switch_console_set_complete("add rayo presence ::rayo::list_all online");
+ switch_console_set_complete("add rayo presence ::rayo::list_all offline");
+ switch_console_add_complete_func("::rayo::list_internal", list_internal);
+ switch_console_add_complete_func("::rayo::list_external", list_external);
+ switch_console_add_complete_func("::rayo::list_all", list_all);
+
+ rayo_add_cmd_alias("ping", "<iq type=\"get\"><ping xmlns=\""IKS_NS_XMPP_PING"\"/></iq>");
+ rayo_add_cmd_alias("answer", "<answer xmlns=\""RAYO_NS"\"/>");
+ rayo_add_cmd_alias("hangup", "<hangup xmlns=\""RAYO_NS"\"/>");
+ rayo_add_cmd_alias("stop", "<stop xmlns=\""RAYO_EXT_NS"\"/>");
+ rayo_add_cmd_alias("pause", "<pause xmlns=\""RAYO_OUTPUT_NS"\"/>");
+ rayo_add_cmd_alias("resume", "<resume xmlns=\""RAYO_OUTPUT_NS"\"/>");
+ rayo_add_cmd_alias("speed-up", "<speed-up xmlns=\""RAYO_OUTPUT_NS"\"/>");
+ rayo_add_cmd_alias("speed-down", "<speed-down xmlns=\""RAYO_OUTPUT_NS"\"/>");
+ rayo_add_cmd_alias("volume-up", "<volume-up xmlns=\""RAYO_OUTPUT_NS"\"/>");
+ rayo_add_cmd_alias("volume-down", "<volume-down xmlns=\""RAYO_OUTPUT_NS"\"/>");
+ rayo_add_cmd_alias("record", "<record xmlns=\""RAYO_RECORD_NS"\"/>");
+ rayo_add_cmd_alias("record_pause", "<pause xmlns=\""RAYO_RECORD_NS"\"/>");
+ rayo_add_cmd_alias("record_resume", "<resume xmlns=\""RAYO_RECORD_NS"\"/>");
+ rayo_add_cmd_alias("prompt_barge", "<prompt xmlns=\""RAYO_PROMPT_NS"\" barge-in=\"true\">"
+ "<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-times=\"5\"><document content-type=\"application/ssml+xml\"><![CDATA[<speak><p>Please press a digit.</p></speak>]]></document></output>"
+ "<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtmf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\">"
+ "<grammar content-type=\"application/srgs+xml\">"
+ "<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digit\" scope=\"public\"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></rule></grammar>]]>"
+ "</grammar></input>"
+ "</prompt>");
+
+ rayo_add_cmd_alias("prompt_no_barge", "<prompt xmlns=\""RAYO_PROMPT_NS"\" barge-in=\"false\">"
+ "<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-times=\"2\"><document content-type=\"application/ssml+xml\"><![CDATA[<speak><p>Please press a digit.</p></speak>]]></document></output>"
+ "<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtmf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\">"
+ "<grammar content-type=\"application/srgs+xml\">"
+ "<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digit\" scope=\"public\"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></rule></grammar>]]>"
+ "</grammar></input>"
+ "</prompt>");
+
+ rayo_add_cmd_alias("prompt_long", "<prompt xmlns=\""RAYO_PROMPT_NS"\" barge-in=\"true\">"
+ "<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-times=\"100\"><document content-type=\"application/ssml+xml\"><![CDATA[<speak><audio src=\"http://phono.com/audio/troporocks.mp3\"/></speak>]]></document></output>"
+ "<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtmf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\">"
+ "<grammar content-type=\"application/srgs+xml\">"
+ "<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digit\" scope=\"public\"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></rule></grammar>]]>"
+ "</grammar></input>"
+ "</prompt>");
+
+ rayo_add_cmd_alias("prompt_multi_digit", "<prompt xmlns=\""RAYO_PROMPT_NS"\" barge-in=\"true\">"
+ "<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-times=\"100\"><document content-type=\"application/ssml+xml\"><![CDATA[<speak><audio src=\"http://phono.com/audio/troporocks.mp3\"/></speak>]]></document></output>"
+ "<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtmf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\">"
+ "<grammar content-type=\"application/srgs+xml\">"
+ "<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digits\" scope=\"public\"><item repeat=\"1-4\"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]>"
+ "</grammar></input>"
+ "</prompt>");
+
+ rayo_add_cmd_alias("prompt_terminator", "<prompt xmlns=\""RAYO_PROMPT_NS"\" barge-in=\"true\">"
+ "<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-times=\"100\"><document content-type=\"application/ssml+xml\"><![CDATA[<speak><audio src=\"http://phono.com/audio/troporocks.mp3\"/></speak>]]></document></output>"
+ "<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtmf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\" terminator=\"#\">"
+ "<grammar content-type=\"application/srgs+xml\">"
+ "<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digits\" scope=\"public\"><item repeat=\"1-4\"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]>"
+ "</grammar></input>"
+ "</prompt>");
+
+ rayo_add_cmd_alias("prompt_input_bad", "<prompt xmlns=\""RAYO_PROMPT_NS"\" barge-in=\"true\">"
+ "<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-times=\"100\"><document content-type=\"application/ssml+xml\"><![CDATA[<speak><audio src=\"http://phono.com/audio/troporocks.mp3\"/></speak>]]></document></output>"
+ "<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\">"
+ "<grammar content-type=\"application/srgs+xml\">"
+ "<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digits\" scope=\"public\"><item repeat=\"4\"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]>"
+ "</grammar></input>"
+ "</prompt>");
+
+ rayo_add_cmd_alias("prompt_output_bad", "<prompt xmlns=\""RAYO_PROMPT_NS"\" barge-in=\"true\">"
+ "<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-time=\"100\"><document content-type=\"application/ssml+xml\"><![CDATA[<speak><audio src=\"http://phono.com/audio/troporocks.mp3\"/></speak>]]></document></output>"
+ "<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtmf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\">"
+ "<grammar content-type=\"application/srgs+xml\">"
+ "<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digits\" scope=\"public\"><item repeat=\"4\"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]>"
+ "</grammar></input>"
+ "</prompt>");
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Shutdown module. Notifies threads to stop.
+ */
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_rayo_shutdown)
+{
+ if (globals.console) {
+ RAYO_UNLOCK(globals.console);
+ RAYO_DESTROY(globals.console);
+ }
+
+ switch_console_del_complete_func("::rayo::list_actors");
+ switch_console_set_complete("del rayo");
+
+ /* wait for threads to finish */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Waiting for XMPP threads to stop\n");
+ xmpp_stream_context_destroy(globals.xmpp_context);
+
+ rayo_components_shutdown();
+
+ /* cleanup module */
+ switch_event_unbind_callback(route_call_event);
+ switch_event_unbind_callback(on_call_end_event);
+ switch_event_unbind_callback(route_mixer_event);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Module shutdown\n");
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * mod_rayo.h -- Rayo server / node implementation. Allows MxN clustering of FreeSWITCH and Rayo Clients (like Adhearsion)
+ *
+ */
+#ifndef MOD_RAYO_H
+#define MOD_RAYO_H
+
+#include <switch.h>
+#include <iksemel.h>
+
+#include "iks_helpers.h"
+
+#define RAYO_VERSION "1"
+#define RAYO_BASE "urn:xmpp:rayo:"
+
+#define RAYO_NS RAYO_BASE RAYO_VERSION
+#define RAYO_CLIENT_NS RAYO_BASE "client:" RAYO_VERSION
+
+struct rayo_actor;
+struct rayo_call;
+struct rayo_mixer;
+struct rayo_component;
+
+/**
+ * A message sent to an actor
+ */
+struct rayo_message {
+ iks *payload;
+};
+
+typedef void (* rayo_actor_cleanup_fn)(struct rayo_actor *);
+typedef struct rayo_message *(* rayo_actor_send_fn)(struct rayo_actor *, struct rayo_actor *, struct rayo_message *, const char *file, int line);
+
+/**
+ * Type of actor
+ */
+enum rayo_actor_type {
+ RAT_PEER_SERVER,
+ RAT_CLIENT,
+ RAT_SERVER,
+ RAT_CALL,
+ RAT_MIXER,
+ RAT_CALL_COMPONENT,
+ RAT_MIXER_COMPONENT
+};
+
+/**
+ * A rayo actor - this is an entity that can be controlled by a rayo client
+ */
+struct rayo_actor {
+ /** Type of actor */
+ enum rayo_actor_type type;
+ /** Sub-type of actor */
+ char *subtype;
+ /** domain part of JID */
+ char *domain;
+ /** Internal ID */
+ char *id;
+ /** actor JID */
+ char *jid;
+ /** Actor pool */
+ switch_memory_pool_t *pool;
+ /** synchronizes access to this actor */
+ switch_mutex_t *mutex;
+ /** an atomically incrementing sequence for this actor */
+ int seq;
+ /** number of users of this actor */
+ int ref_count;
+ /** destroy flag */
+ int destroy;
+ /** XMPP message handling function */
+ rayo_actor_send_fn send_fn;
+ /** optional cleanup */
+ rayo_actor_cleanup_fn cleanup_fn;
+};
+
+/**
+ * A Rayo component
+ */
+struct rayo_component {
+ /** base actor class */
+ struct rayo_actor base;
+ /** component type (input/output/prompt/etc) */
+ const char *type;
+ /** parent to this component */
+ struct rayo_actor *parent;
+ /** owning client JID */
+ const char *client_jid;
+ /** external ref */
+ const char *ref;
+};
+
+#define RAYO_ACTOR(x) ((struct rayo_actor *)x)
+#define RAYO_COMPONENT(x) ((struct rayo_component *)x)
+#define RAYO_CALL(x) ((struct rayo_call *)x)
+#define RAYO_MIXER(x) ((struct rayo_mixer *)x)
+
+extern struct rayo_message *rayo_message_create(iks *xml);
+extern struct rayo_message *rayo_message_create_dup(iks *xml);
+extern void rayo_message_destroy(struct rayo_message *msg);
+extern iks *rayo_message_remove_payload(struct rayo_message *msg);
+
+extern struct rayo_actor *rayo_actor_locate(const char *jid, const char *file, int line);
+extern struct rayo_actor *rayo_actor_locate_by_id(const char *id, const char *file, int line);
+extern int rayo_actor_seq_next(struct rayo_actor *actor);
+extern struct rayo_message *rayo_actor_send(struct rayo_actor *from, struct rayo_actor *to, struct rayo_message *msg, const char *file, int line);
+extern struct rayo_message *rayo_actor_send_by_jid(struct rayo_actor *from, const char *jid, struct rayo_message *msg, const char *file, int line);
+extern void rayo_actor_rdlock(struct rayo_actor *actor, const char *file, int line);
+extern void rayo_actor_unlock(struct rayo_actor *actor, const char *file, int line);
+extern void rayo_actor_destroy(struct rayo_actor *actor, const char *file, int line);
+
+#define RAYO_LOCATE(jid) rayo_actor_locate(jid, __FILE__, __LINE__)
+#define RAYO_LOCATE_BY_ID(id) rayo_actor_locate_by_id(id, __FILE__, __LINE__)
+#define RAYO_SET_EVENT_FN(actor, event) rayo_actor_set_event_fn(RAYO_ACTOR(actor), event)
+#define RAYO_DOMAIN(x) RAYO_ACTOR(x)->domain
+#define RAYO_JID(x) RAYO_ACTOR(x)->jid
+#define RAYO_ID(x) RAYO_ACTOR(x)->id
+#define RAYO_POOL(x) RAYO_ACTOR(x)->pool
+#define RAYO_RDLOCK(x) rayo_actor_rdlock(RAYO_ACTOR(x), __FILE__, __LINE__)
+#define RAYO_UNLOCK(x) rayo_actor_unlock(RAYO_ACTOR(x), __FILE__, __LINE__)
+#define RAYO_DESTROY(x) rayo_actor_destroy(RAYO_ACTOR(x), __FILE__, __LINE__)
+#define RAYO_SEQ_NEXT(x) rayo_actor_seq_next(RAYO_ACTOR(x))
+#define RAYO_SEND(from, to, msg) rayo_actor_send(RAYO_ACTOR(from), RAYO_ACTOR(to), msg, __FILE__, __LINE__)
+#define RAYO_SEND_BY_JID(from, jid, msg) rayo_actor_send_by_jid(RAYO_ACTOR(from), jid, msg, __FILE__, __LINE__)
+
+extern const char *rayo_call_get_dcp_jid(struct rayo_call *call);
+
+#define rayo_mixer_get_name(mixer) RAYO_ID(mixer)
+
+#define rayo_component_init(component, pool, type, id, parent, client_jid) _rayo_component_init(component, pool, type, id, parent, client_jid, __FILE__, __LINE__)
+extern struct rayo_component *_rayo_component_init(struct rayo_component *component, switch_memory_pool_t *pool, const char *type, const char *id, struct rayo_actor *parent, const char *client_jid, const char *file, int line);
+
+typedef iks *(*rayo_actor_xmpp_handler)(struct rayo_actor *, struct rayo_actor *, iks *, void *);
+extern void rayo_actor_command_handler_add(enum rayo_actor_type type, const char *subtype, const char *name, rayo_actor_xmpp_handler fn);
+extern void rayo_actor_event_handler_add(enum rayo_actor_type from_type, const char *from_subtype, enum rayo_actor_type to_type, const char *to_subtype, const char *name, rayo_actor_xmpp_handler fn);
+
+#endif
+
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * nlsml.c -- Parses / creates NLSML results
+ *
+ */
+#include <switch.h>
+#include <iksemel.h>
+
+#include "nlsml.h"
+
+struct nlsml_parser;
+
+/** function to handle tag attributes */
+typedef int (* tag_attribs_fn)(struct nlsml_parser *, char **);
+/** function to handle tag CDATA */
+typedef int (* tag_cdata_fn)(struct nlsml_parser *, char *, size_t);
+
+/**
+ * Tag definition
+ */
+struct tag_def {
+ tag_attribs_fn attribs_fn;
+ tag_cdata_fn cdata_fn;
+ switch_bool_t is_root;
+ switch_hash_t *children_tags;
+};
+
+/**
+ * library configuration
+ */
+static struct {
+ /** true if initialized */
+ switch_bool_t init;
+ /** Mapping of tag name to definition */
+ switch_hash_t *tag_defs;
+ /** library memory pool */
+ switch_memory_pool_t *pool;
+} globals;
+
+/**
+ * The node in the XML tree
+ */
+struct nlsml_node {
+ /** tag name */
+ const char *name;
+ /** tag definition */
+ struct tag_def *tag_def;
+ /** parent to this node */
+ struct nlsml_node *parent;
+};
+
+/**
+ * The SAX parser state
+ */
+struct nlsml_parser {
+ /** current node */
+ struct nlsml_node *cur;
+ /** optional UUID for logging */
+ const char *uuid;
+ /** true if a match exists */
+ int match;
+ /** true if noinput */
+ int noinput;
+ /** true if nomatch */
+ int nomatch;
+};
+
+/**
+ * Add a definition for a tag
+ * @param tag the name
+ * @param attribs_fn the function to handle the tag attributes
+ * @param cdata_fn the function to handler the tag CDATA
+ * @param children_tags comma-separated list of valid child tag names
+ * @return the definition
+ */
+static struct tag_def *add_tag_def(const char *tag, tag_attribs_fn attribs_fn, tag_cdata_fn cdata_fn, const char *children_tags)
+{
+ struct tag_def *def = switch_core_alloc(globals.pool, sizeof(*def));
+ switch_core_hash_init(&def->children_tags, globals.pool);
+ if (!zstr(children_tags)) {
+ char *children_tags_dup = switch_core_strdup(globals.pool, children_tags);
+ char *tags[32] = { 0 };
+ int tag_count = switch_separate_string(children_tags_dup, ',', tags, sizeof(tags) / sizeof(tags[0]));
+ if (tag_count) {
+ int i;
+ for (i = 0; i < tag_count; i++) {
+ switch_core_hash_insert(def->children_tags, tags[i], tags[i]);
+ }
+ }
+ }
+ def->attribs_fn = attribs_fn;
+ def->cdata_fn = cdata_fn;
+ def->is_root = SWITCH_FALSE;
+ switch_core_hash_insert(globals.tag_defs, tag, def);
+ return def;
+}
+
+/**
+ * Add a definition for a root tag
+ * @param tag the name
+ * @param attribs_fn the function to handle the tag attributes
+ * @param cdata_fn the function to handler the tag CDATA
+ * @param children_tags comma-separated list of valid child tag names
+ * @return the definition
+ */
+static struct tag_def *add_root_tag_def(const char *tag, tag_attribs_fn attribs_fn, tag_cdata_fn cdata_fn, const char *children_tags)
+{
+ struct tag_def *def = add_tag_def(tag, attribs_fn, cdata_fn, children_tags);
+ def->is_root = SWITCH_TRUE;
+ return def;
+}
+
+/**
+ * Handle tag attributes
+ * @param parser the parser
+ * @param name the tag name
+ * @param atts the attributes
+ * @return IKS_OK if OK IKS_BADXML on parse failure
+ */
+static int process_tag(struct nlsml_parser *parser, const char *name, char **atts)
+{
+ struct nlsml_node *cur = parser->cur;
+ if (cur->tag_def->is_root && cur->parent == NULL) {
+ /* no parent for ROOT tags */
+ return cur->tag_def->attribs_fn(parser, atts);
+ } else if (!cur->tag_def->is_root && cur->parent) {
+ /* check if this child is allowed by parent node */
+ struct tag_def *parent_def = cur->parent->tag_def;
+ if (switch_core_hash_find(parent_def->children_tags, "ANY") ||
+ switch_core_hash_find(parent_def->children_tags, name)) {
+ return cur->tag_def->attribs_fn(parser, atts);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser->uuid), SWITCH_LOG_INFO, "<%s> cannot be a child of <%s>\n", name, cur->parent->name);
+ }
+ } else if (cur->tag_def->is_root && cur->parent != NULL) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser->uuid), SWITCH_LOG_INFO, "<%s> must be the root element\n", name);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser->uuid), SWITCH_LOG_INFO, "<%s> cannot be a root element\n", name);
+ }
+ return IKS_BADXML;
+}
+
+/**
+ * Handle tag attributes that are ignored
+ * @param parser the parser
+ * @param atts the attributes
+ * @return IKS_OK
+ */
+static int process_attribs_ignore(struct nlsml_parser *parser, char **atts)
+{
+ return IKS_OK;
+}
+
+/**
+ * Handle CDATA that is ignored
+ * @param parser the parser
+ * @param data the CDATA
+ * @param len the CDATA length
+ * @return IKS_OK
+ */
+static int process_cdata_ignore(struct nlsml_parser *parser, char *data, size_t len)
+{
+ return IKS_OK;
+}
+
+/**
+ * Handle CDATA that is not allowed
+ * @param parser the parser
+ * @param data the CDATA
+ * @param len the CDATA length
+ * @return IKS_BADXML if any printable characters
+ */
+static int process_cdata_bad(struct nlsml_parser *parser, char *data, size_t len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ if (isgraph(data[i])) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser->uuid), SWITCH_LOG_INFO, "Unexpected CDATA for <%s>\n", parser->cur->name);
+ return IKS_BADXML;
+ }
+ }
+ return IKS_OK;
+}
+
+/**
+ * Handle CDATA with match text
+ * @param parser the parser
+ * @param data the CDATA
+ * @param len the CDATA length
+ * @return IKS_OK
+ */
+static int process_cdata_match(struct nlsml_parser *parser, char *data, size_t len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ if (isgraph(data[i])) {
+ parser->match++;
+ return IKS_OK;
+ }
+ }
+ return IKS_OK;
+}
+
+/**
+ * Handle nomatch
+ * @param parser the parser
+ * @param atts the attributes
+ * @return IKS_OK
+ */
+static int process_nomatch(struct nlsml_parser *parser, char **atts)
+{
+ parser->nomatch++;
+ return IKS_OK;
+}
+
+/**
+ * Handle noinput
+ * @param parser the parser
+ * @param atts the attributes
+ * @return IKS_OK
+ */
+static int process_noinput(struct nlsml_parser *parser, char **atts)
+{
+ parser->noinput++;
+ return IKS_OK;
+}
+
+/**
+ * Process a tag
+ */
+static int tag_hook(void *user_data, char *name, char **atts, int type)
+{
+ int result = IKS_OK;
+ struct nlsml_parser *parser = (struct nlsml_parser *)user_data;
+
+ if (type == IKS_OPEN || type == IKS_SINGLE) {
+ struct nlsml_node *child_node = malloc(sizeof(*child_node));
+ child_node->name = name;
+ child_node->tag_def = switch_core_hash_find(globals.tag_defs, name);
+ if (!child_node->tag_def) {
+ child_node->tag_def = switch_core_hash_find(globals.tag_defs, "ANY");
+ }
+ child_node->parent = parser->cur;
+ parser->cur = child_node;
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser->uuid), SWITCH_LOG_DEBUG1, "<%s>\n", name);
+ result = process_tag(parser, name, atts);
+ }
+
+ if (type == IKS_CLOSE || type == IKS_SINGLE) {
+ struct nlsml_node *node = parser->cur;
+ parser->cur = node->parent;
+ free(node);
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser->uuid), SWITCH_LOG_DEBUG1, "</%s>\n", name);
+ }
+
+ return result;
+}
+
+/**
+ * Process cdata
+ * @param user_data the parser
+ * @param data the CDATA
+ * @param len the CDATA length
+ * @return IKS_OK
+ */
+static int cdata_hook(void *user_data, char *data, size_t len)
+{
+ struct nlsml_parser *parser = (struct nlsml_parser *)user_data;
+ if (!parser) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing parser\n");
+ return IKS_BADXML;
+ }
+ if (parser->cur) {
+ struct tag_def *def = parser->cur->tag_def;
+ if (def) {
+ return def->cdata_fn(parser, data, len);
+ }
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser->uuid), SWITCH_LOG_INFO, "Missing definition for <%s>\n", parser->cur->name);
+ return IKS_BADXML;
+ }
+ return IKS_OK;
+}
+
+/**
+ * Parse the result, looking for noinput/nomatch/match
+ * @param result the NLSML result to parse
+ * @param uuid optional UUID for logging
+ * @return true if successful
+ */
+enum nlsml_match_type nlsml_parse(const char *result, const char *uuid)
+{
+ struct nlsml_parser parser = { 0 };
+ parser.uuid = uuid;
+ if (!zstr(result)) {
+ iksparser *p = iks_sax_new(&parser, tag_hook, cdata_hook);
+ if (iks_parse(p, result, 0, 1) == IKS_OK) {
+ /* check result */
+ if (parser.match) {
+ return NMT_MATCH;
+ }
+ if (parser.nomatch) {
+ return NMT_NOMATCH;
+ }
+ if (parser.noinput) {
+ return NMT_NOINPUT;
+ }
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser.uuid), SWITCH_LOG_INFO, "NLSML result does not have match/noinput/nomatch!\n");
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser.uuid), SWITCH_LOG_INFO, "Failed to parse NLSML!\n");
+ }
+ iks_parser_delete(p);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser.uuid), SWITCH_LOG_INFO, "Missing NLSML result\n");
+ }
+ return NMT_BAD_XML;
+}
+
+#define NLSML_NS "http://www.ietf.org/xml/ns/mrcpv2"
+
+/**
+ * Makes NLSML result to conform to mrcpv2
+ * @param result the potentially non-conforming result
+ * @return the conforming result
+ */
+iks *nlsml_normalize(const char *result)
+{
+ iks *result_xml = NULL;
+ iksparser *p = iks_dom_new(&result_xml);
+ if (iks_parse(p, result, 0, 1) == IKS_OK && result_xml) {
+ /* for now, all that is needed is to set the proper namespace */
+ iks_insert_attrib(result_xml, "xmlns", NLSML_NS);
+ } else {
+ /* unexpected ... */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Failed to normalize NLSML result: %s\n", result);
+ if (result_xml) {
+ iks_delete(result_xml);
+ }
+ }
+ iks_parser_delete(p);
+ return result_xml;
+}
+
+/**
+ * Construct an NLSML result for digit match
+ * @param digits the matching digits
+ * @return the NLSML <result>
+ */
+iks *nlsml_create_dtmf_match(const char *digits)
+{
+ iks *result = iks_new("result");
+ iks_insert_attrib(result, "xmlns", NLSML_NS);
+ iks_insert_attrib(result, "xmlns:xf", "http://www.w3.org/2000/xforms");
+ if (!zstr(digits)) {
+ int first = 1;
+ int i;
+ int num_digits = strlen(digits);
+ switch_stream_handle_t stream = { 0 };
+
+ iks *interpretation = iks_insert(result, "interpretation");
+ iks *input = iks_insert(interpretation, "input");
+ input = iks_insert(input, "input");
+ iks_insert_attrib(input, "mode", "dtmf");
+ iks_insert_attrib(input, "confidence", "100");
+
+ SWITCH_STANDARD_STREAM(stream);
+ for (i = 0; i < num_digits; i++) {
+ if (isdigit(digits[i])) {
+ if (first) {
+ stream.write_function(&stream, "%c", digits[i]);
+ first = 0;
+ } else {
+ stream.write_function(&stream, " %c", digits[i]);
+ }
+ }
+ }
+ iks_insert_cdata(input, stream.data, strlen(stream.data));
+ switch_safe_free(stream.data);
+ }
+ return result;
+}
+
+/**
+ * Initialize NLSML parser. This function is not thread safe.
+ */
+int nlsml_init(void)
+{
+ if (globals.init) {
+ return 1;
+ }
+
+ globals.init = SWITCH_TRUE;
+ switch_core_new_memory_pool(&globals.pool);
+ switch_core_hash_init(&globals.tag_defs, globals.pool);
+
+ add_root_tag_def("result", process_attribs_ignore, process_cdata_ignore, "interpretation");
+ add_tag_def("interpretation", process_attribs_ignore, process_cdata_ignore, "input,model,xf:model,instance,xf:instance");
+ add_tag_def("input", process_attribs_ignore, process_cdata_match, "input,nomatch,noinput");
+ add_tag_def("noinput", process_noinput, process_cdata_bad, "");
+ add_tag_def("nomatch", process_nomatch, process_cdata_ignore, "");
+ add_tag_def("model", process_attribs_ignore, process_cdata_ignore, "ANY");
+ add_tag_def("xf:model", process_attribs_ignore, process_cdata_ignore, "ANY");
+ add_tag_def("instance", process_attribs_ignore, process_cdata_ignore, "ANY");
+ add_tag_def("xf:instance", process_attribs_ignore, process_cdata_ignore, "ANY");
+ add_tag_def("ANY", process_attribs_ignore, process_cdata_ignore, "ANY");
+
+ return 1;
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * nlsml.h -- Parses / creates NLSML results
+ *
+ */
+#ifndef NLSML_H
+#define NLSML_H
+
+#include <iksemel.h>
+#include <switch.h>
+
+enum nlsml_match_type {
+ NMT_BAD_XML,
+ NMT_MATCH,
+ NMT_NOINPUT,
+ NMT_NOMATCH
+};
+
+extern int nlsml_init(void);
+enum nlsml_match_type nlsml_parse(const char *result, const char *uuid);
+iks *nlsml_normalize(const char *result);
+extern iks *nlsml_create_dtmf_match(const char *digits);
+
+#endif
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * rayo_components.c -- Rayo component interface
+ *
+ */
+#include "rayo_components.h"
+
+#include <switch.h>
+#include "mod_rayo.h"
+#include "iks_helpers.h"
+
+/**
+ * Get access to Rayo component data.
+ * @param id the component internal ID
+ * @return the component or NULL. Call rayo_component_unlock() when done with component pointer.
+ */
+struct rayo_component *rayo_component_locate(const char *id, const char *file, int line)
+{
+ struct rayo_actor *actor = rayo_actor_locate_by_id(id, file, line);
+ if (actor && (actor->type == RAT_MIXER_COMPONENT || actor->type == RAT_CALL_COMPONENT)) {
+ return RAYO_COMPONENT(actor);
+ } else if (actor) {
+ RAYO_UNLOCK(actor);
+ }
+ return NULL;
+}
+
+/**
+ * Send component start reply
+ * @param component the component
+ * @param iq the start request
+ */
+void rayo_component_send_start(struct rayo_component *component, iks *iq)
+{
+ iks *response = iks_new_iq_result(iq);
+ iks *ref = iks_insert(response, "ref");
+ iks_insert_attrib(ref, "xmlns", RAYO_NS);
+ iks_insert_attrib(ref, "id", component->ref);
+ iks_insert_attrib_printf(ref, "uri", "xmpp:%s", RAYO_JID(component));
+ RAYO_SEND_BY_JID(component, iks_find_attrib(response, "to"), rayo_message_create(response));
+}
+
+/**
+ * Create component complete event
+ * @param component the component
+ * @param reason_str the completion reason
+ * @param reason_namespace the completion reason namespace
+ * @param meta metadata to add as child
+ * @param child_of_complete if true metadata is child of complete instead of reason
+ * @return the event
+ */
+iks *rayo_component_create_complete_event_with_metadata(struct rayo_component *component, const char *reason_str, const char *reason_namespace, iks *meta, int child_of_complete)
+{
+ iks *response = iks_new("presence");
+ iks *complete;
+ iks *reason;
+ iks_insert_attrib(response, "from", RAYO_JID(component));
+ iks_insert_attrib(response, "to", component->client_jid);
+ iks_insert_attrib(response, "type", "unavailable");
+ complete = iks_insert(response, "complete");
+ iks_insert_attrib(complete, "xmlns", RAYO_EXT_NS);
+ reason = iks_insert(complete, reason_str);
+ iks_insert_attrib(reason, "xmlns", reason_namespace);
+ if (meta) {
+ meta = iks_copy_within(meta, iks_stack(response));
+ if (child_of_complete) {
+ iks_insert_node(complete, meta);
+ } else {
+ iks_insert_node(reason, meta);
+ }
+ }
+
+ return response;
+}
+
+/**
+ * Create component complete event
+ * @param component the component
+ * @param reason the completion reason
+ * @param reason_namespace the completion reason namespace
+ * @return the event
+ */
+iks *rayo_component_create_complete_event(struct rayo_component *component, const char *reason, const char *reason_namespace)
+{
+ return rayo_component_create_complete_event_with_metadata(component, reason, reason_namespace, NULL, 0);
+}
+
+/**
+ * Send rayo component complete event
+ */
+void rayo_component_send_complete_event(struct rayo_component *component, iks *response)
+{
+ RAYO_SEND_BY_JID(component, iks_find_attrib(response, "to"), rayo_message_create(response));
+ RAYO_UNLOCK(component);
+ RAYO_DESTROY(component);
+}
+
+/**
+ * Send rayo complete
+ */
+void rayo_component_send_complete(struct rayo_component *component, const char *reason, const char *reason_namespace)
+{
+ rayo_component_send_complete_event(component, rayo_component_create_complete_event(component, reason, reason_namespace));
+}
+
+/**
+ * Send rayo complete
+ */
+void rayo_component_send_complete_with_metadata(struct rayo_component *component, const char *reason, const char *reason_namespace, iks *meta, int child_of_complete)
+{
+ rayo_component_send_complete_event(component, rayo_component_create_complete_event_with_metadata(component, reason, reason_namespace, meta, child_of_complete));
+}
+
+/**
+ * Send rayo complete
+ */
+void rayo_component_send_complete_with_metadata_string(struct rayo_component *component, const char *reason, const char *reason_namespace, const char *meta, int child_of_complete)
+{
+ iks *meta_xml = NULL;
+ iksparser *p = iks_dom_new(&meta_xml);
+ if (iks_parse(p, meta, 0, 1) != IKS_OK) {
+ /* unexpected ... */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "%s Failed to parse metadata for complete event: %s\n",
+ RAYO_JID(component), meta);
+ /* send without... */
+ rayo_component_send_complete(component, reason, reason_namespace);
+ } else {
+ rayo_component_send_complete_with_metadata(component, reason, reason_namespace, meta_xml, child_of_complete);
+ }
+ if (meta_xml) {
+ iks_delete(meta_xml);
+ }
+ iks_parser_delete(p);
+}
+
+/**
+ * Background API data
+ */
+struct component_bg_api_cmd {
+ const char *cmd;
+ const char *args;
+ switch_memory_pool_t *pool;
+ struct rayo_component *component;
+};
+
+/**
+ * Thread that outputs to component
+ * @param thread this thread
+ * @param obj the Rayo mixer context
+ * @return NULL
+ */
+static void *SWITCH_THREAD_FUNC component_bg_api_thread(switch_thread_t *thread, void *obj)
+{
+ struct component_bg_api_cmd *cmd = (struct component_bg_api_cmd *)obj;
+ switch_stream_handle_t stream = { 0 };
+ switch_memory_pool_t *pool = cmd->pool;
+ SWITCH_STANDARD_STREAM(stream);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "BGAPI EXEC: %s %s\n", cmd->cmd, cmd->args);
+ if (switch_api_execute(cmd->cmd, cmd->args, NULL, &stream) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "BGAPI EXEC FAILURE\n");
+ rayo_component_send_complete(cmd->component, COMPONENT_COMPLETE_ERROR);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "BGAPI EXEC RESULT: %s\n", (char *)stream.data);
+ }
+ switch_safe_free(stream.data);
+ switch_core_destroy_memory_pool(&pool);
+ return NULL;
+}
+
+/**
+ * Run a background API command
+ * @param cmd API command
+ * @param args API args
+ */
+void rayo_component_api_execute_async(struct rayo_component *component, const char *cmd, const char *args)
+{
+ switch_thread_t *thread;
+ switch_threadattr_t *thd_attr = NULL;
+ struct component_bg_api_cmd *bg_cmd = NULL;
+ switch_memory_pool_t *pool;
+
+ /* set up command */
+ switch_core_new_memory_pool(&pool);
+ bg_cmd = switch_core_alloc(pool, sizeof(*bg_cmd));
+ bg_cmd->pool = pool;
+ bg_cmd->cmd = switch_core_strdup(pool, cmd);
+ bg_cmd->args = switch_core_strdup(pool, args);
+ bg_cmd->component = component;
+
+ /* create thread */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s BGAPI START\n", RAYO_JID(component));
+ switch_threadattr_create(&thd_attr, pool);
+ switch_threadattr_detach_set(thd_attr, 1);
+ switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+ switch_thread_create(&thread, thd_attr, component_bg_api_thread, bg_cmd, pool);
+}
+
+/**
+ * Handle configuration
+ */
+switch_status_t rayo_components_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file)
+{
+ rayo_input_component_load();
+ rayo_output_component_load(module_interface, pool);
+ rayo_prompt_component_load();
+ rayo_record_component_load(pool, config_file);
+
+ if (rayo_input_component_load() != SWITCH_STATUS_SUCCESS ||
+ rayo_output_component_load(module_interface, pool) != SWITCH_STATUS_SUCCESS ||
+ rayo_prompt_component_load() != SWITCH_STATUS_SUCCESS ||
+ rayo_record_component_load(pool, config_file) != SWITCH_STATUS_SUCCESS) {
+ return SWITCH_STATUS_TERM;
+ }
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Handle shutdown
+ */
+switch_status_t rayo_components_shutdown(void)
+{
+ rayo_input_component_shutdown();
+ rayo_output_component_shutdown();
+ rayo_prompt_component_shutdown();
+ rayo_record_component_shutdown();
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * components.c -- Rayo component implementations
+ *
+ */
+#ifndef RAYO_COMPONENTS_H
+#define RAYO_COMPONENTS_H
+
+#include <switch.h>
+#include <iksemel.h>
+
+#include "mod_rayo.h"
+
+#define RAYO_EXT_NS RAYO_BASE "ext:" RAYO_VERSION
+#define RAYO_EXT_COMPLETE_NS RAYO_BASE "ext:complete:" RAYO_VERSION
+
+#define RAYO_OUTPUT_NS RAYO_BASE "output:" RAYO_VERSION
+#define RAYO_OUTPUT_COMPLETE_NS RAYO_BASE "output:complete:" RAYO_VERSION
+
+#define RAYO_INPUT_NS RAYO_BASE "input:" RAYO_VERSION
+#define RAYO_INPUT_COMPLETE_NS RAYO_BASE "input:complete:" RAYO_VERSION
+
+#define RAYO_RECORD_NS RAYO_BASE "record:" RAYO_VERSION
+#define RAYO_RECORD_COMPLETE_NS RAYO_BASE "record:complete:" RAYO_VERSION
+
+#define RAYO_PROMPT_NS RAYO_BASE "prompt:" RAYO_VERSION
+#define RAYO_PROMPT_COMPLETE_NS RAYO_BASE "prompt:complete:" RAYO_VERSION
+
+#define COMPONENT_COMPLETE_STOP "stop", RAYO_EXT_COMPLETE_NS
+#define COMPONENT_COMPLETE_ERROR "error", RAYO_EXT_COMPLETE_NS
+#define COMPONENT_COMPLETE_HANGUP "hangup", RAYO_EXT_COMPLETE_NS
+
+extern switch_status_t rayo_components_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file);
+extern switch_status_t rayo_input_component_load(void);
+extern switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool);
+extern switch_status_t rayo_prompt_component_load(void);
+extern switch_status_t rayo_record_component_load(switch_memory_pool_t *pool, const char *config_file);
+
+extern switch_status_t rayo_components_shutdown(void);
+extern switch_status_t rayo_input_component_shutdown(void);
+extern switch_status_t rayo_output_component_shutdown(void);
+extern switch_status_t rayo_prompt_component_shutdown(void);
+extern switch_status_t rayo_record_component_shutdown(void);
+
+extern void rayo_component_send_start(struct rayo_component *component, iks *iq);
+extern void rayo_component_send_iq_error(struct rayo_component *component, iks *iq, const char *error_name, const char *error_type);
+extern void rayo_component_send_iq_error_detailed(struct rayo_component *component, iks *iq, const char *error_name, const char *error_type, const char *detail);
+extern void rayo_component_send_complete(struct rayo_component *component, const char *reason, const char *reason_namespace);
+extern void rayo_component_send_complete_event(struct rayo_component *component, iks *response);
+extern void rayo_component_send_complete_with_metadata(struct rayo_component *component, const char *reason, const char *reason_namespace, iks *meta, int child_of_complete);
+extern void rayo_component_send_complete_with_metadata_string(struct rayo_component *component, const char *reason, const char *reason_namespace, const char *meta, int child_of_complete);
+
+extern iks *rayo_component_create_complete_event(struct rayo_component *component, const char *reason, const char *reason_namespace);
+extern iks *rayo_component_create_complete_event_with_metadata(struct rayo_component *component, const char *reason, const char *reason_namespace, iks *meta, int child_of_complete);
+
+extern void rayo_component_api_execute_async(struct rayo_component *component, const char *cmd, const char *args);
+
+#define RAYO_COMPONENT_LOCATE(id) rayo_component_locate(id, __FILE__, __LINE__)
+extern struct rayo_component *rayo_component_locate(const char *id, const char *file, int line);
+
+#endif
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * rayo_elements.h -- Rayo XML element definition
+ *
+ */
+#ifndef RAYO_ELEMENTS_H
+#define RAYO_ELEMENTS_H
+
+#include "iks_helpers.h"
+
+/**
+ * <input> component validation
+ */
+ELEMENT(RAYO_INPUT)
+ STRING_ATTRIB(mode, any, "any,dtmf,speech");
+ ATTRIB(terminator,, any)
+ ATTRIB(recognizer, en-US, any)
+ ATTRIB(initial-timeout, -1, positive_or_neg_one)
+ ATTRIB(inter-digit-timeout, -1, positive_or_neg_one)
+ ATTRIB(sensitivity, 0.5, decimal_between_zero_and_one)
+ ATTRIB(min-confidence, 0, decimal_between_zero_and_one)
+ ATTRIB(max-silence, -1, positive_or_neg_one)
+ /* internal attribs for prompt support */
+ ATTRIB(barge-event, false, bool)
+ ATTRIB(start-timers, true, bool)
+ELEMENT_END
+
+/**
+ * <output> component validation
+ */
+ELEMENT(RAYO_OUTPUT)
+ ATTRIB(start-offset, 0, not_negative)
+ ATTRIB(start-paused, false, bool)
+ ATTRIB(repeat-interval, 0, not_negative)
+ ATTRIB(repeat-times, 1, positive)
+ ATTRIB(max-time, -1, positive_or_neg_one)
+ ATTRIB(renderer,, any)
+ELEMENT_END
+
+/**
+ * <output><seek> validation
+ */
+ELEMENT(RAYO_OUTPUT_SEEK)
+ STRING_ATTRIB(direction,, "forward,back");
+ ATTRIB(amount,-1, positive)
+ELEMENT_END
+
+/**
+ * <prompt> component validation
+ */
+ELEMENT(RAYO_PROMPT)
+ ATTRIB(barge-in, true, bool)
+ELEMENT_END
+
+/**
+ * <record> component validation
+ */
+ELEMENT(RAYO_RECORD)
+ ATTRIB(format, mp3, any)
+ ATTRIB(start-beep, false, bool)
+ ATTRIB(stop-beep, false, bool)
+ ATTRIB(start-paused, false, bool)
+ ATTRIB(max-duration, -1, positive_or_neg_one)
+ ATTRIB(initial-timeout, -1, positive_or_neg_one)
+ ATTRIB(final-timeout, -1, positive_or_neg_one)
+ STRING_ATTRIB(direction, duplex, "duplex,send,recv");
+ ATTRIB(mix, false, bool)
+ELEMENT_END
+
+/**
+ * <join> command validation
+ */
+ELEMENT(RAYO_JOIN)
+ /* for now, only allow duplex
+ STRING_ATTRIB(direction, duplex, "send,recv,duplex"); */
+ STRING_ATTRIB(direction, duplex, "duplex")
+ STRING_ATTRIB(media, bridge, "bridge,direct")
+ ATTRIB(call-id,, any)
+ ATTRIB(mixer-name,, any)
+ELEMENT_END
+
+#endif
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * rayo_input_component.c -- Rayo input component implementation
+ *
+ */
+#include "rayo_components.h"
+#include "rayo_elements.h"
+#include "srgs.h"
+#include "nlsml.h"
+
+#define MAX_DTMF 64
+
+#define INPUT_INITIAL_TIMEOUT "initial-timeout", RAYO_INPUT_COMPLETE_NS
+#define INPUT_INTER_DIGIT_TIMEOUT "inter-digit-timeout", RAYO_INPUT_COMPLETE_NS
+#define INPUT_MAX_SILENCE "max-silence", RAYO_INPUT_COMPLETE_NS
+#define INPUT_MIN_CONFIDENCE "min-confidence", RAYO_INPUT_COMPLETE_NS
+#define INPUT_MATCH "match", RAYO_INPUT_COMPLETE_NS
+#define INPUT_NOMATCH "nomatch", RAYO_INPUT_COMPLETE_NS
+
+#define RAYO_INPUT_COMPONENT_PRIVATE_VAR "__rayo_input_component"
+
+struct input_handler;
+
+static struct {
+ /** grammar parser */
+ struct srgs_parser *parser;
+} globals;
+
+/**
+ * Input component state
+ */
+struct input_component {
+ /** component base class */
+ struct rayo_component base;
+ /** true if speech detection */
+ int speech_mode;
+ /** Number of collected digits */
+ int num_digits;
+ /** Terminating digits */
+ int term_digit_mask;
+ /** The collected digits */
+ char digits[MAX_DTMF + 1];
+ /** grammar to match */
+ struct srgs_grammar *grammar;
+ /** time when last digit was received */
+ switch_time_t last_digit_time;
+ /** timeout before first digit is received */
+ int initial_timeout;
+ /** maximum silence allowed */
+ int max_silence;
+ /** minimum speech detection confidence */
+ int min_confidence;
+ /** timeout after first digit is received */
+ int inter_digit_timeout;
+ /** stop flag */
+ int stop;
+ /** true if input timers started */
+ int start_timers;
+ /** true if event fired for first digit / start of speech */
+ int barge_event;
+ /** global data */
+ struct input_handler *handler;
+};
+
+#define INPUT_COMPONENT(x) ((struct input_component *)x)
+
+/**
+ * Call input state
+ */
+struct input_handler {
+ /** media bug to monitor frames / control input lifecycle */
+ switch_media_bug_t *bug;
+ /** active input component - TODO multiple inputs */
+ struct input_component *component;
+ /** synchronizes media bug and dtmf callbacks */
+ switch_mutex_t *mutex;
+};
+
+/**
+ * @return digit mask
+ */
+static int get_digit_mask(char digit)
+{
+ switch(digit) {
+ case '0': return 1;
+ case '1': return 1 << 1;
+ case '2': return 1 << 2;
+ case '3': return 1 << 3;
+ case '4': return 1 << 4;
+ case '5': return 1 << 5;
+ case '6': return 1 << 6;
+ case '7': return 1 << 7;
+ case '8': return 1 << 8;
+ case '9': return 1 << 9;
+ case 'A':
+ case 'a': return 1 << 10;
+ case 'B':
+ case 'b': return 1 << 11;
+ case 'C':
+ case 'c': return 1 << 12;
+ case 'D':
+ case 'd': return 1 << 13;
+ case '#': return 1 << 14;
+ case '*': return 1 << 15;
+ }
+ return 0;
+}
+
+/**
+ * @param digit_mask to check
+ * @param digit to look for
+ * @return true if set
+ */
+static int digit_mask_test(int digit_mask, char digit)
+{
+ return digit_mask & get_digit_mask(digit);
+}
+
+/**
+ * @param digit_mask to set digit in
+ * @param digit to set
+ * @return the digit mask with the set digit
+ */
+static int digit_mask_set(int digit_mask, char digit)
+{
+ return digit_mask | get_digit_mask(digit);
+}
+
+/**
+ * @param digit_mask to set digits in
+ * @param digits to add to mask
+ * @return the digit mask with the set digits
+ */
+static int digit_mask_set_from_digits(int digit_mask, const char *digits)
+{
+ if (!zstr(digits)) {
+ int digits_len = strlen(digits);
+ int i;
+ for (i = 0; i < digits_len; i++) {
+ digit_mask = digit_mask_set(digit_mask, digits[i]);
+ }
+ }
+ return digit_mask;
+}
+
+/**
+ * Send barge-in event to client
+ */
+static void send_barge_event(struct rayo_component *component)
+{
+ iks *event = iks_new("presence");
+ iks *x;
+ iks_insert_attrib(event, "from", RAYO_JID(component));
+ iks_insert_attrib(event, "to", component->client_jid);
+ x = iks_insert(event, "start-of-input");
+ iks_insert_attrib(x, "xmlns", RAYO_INPUT_NS);
+ RAYO_SEND_BY_JID(component, component->client_jid, rayo_message_create(event));
+}
+
+/**
+ * Process DTMF press
+ */
+static switch_status_t input_component_on_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf, switch_dtmf_direction_t direction)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ struct input_handler *handler = (struct input_handler *)switch_channel_get_private(channel, RAYO_INPUT_COMPONENT_PRIVATE_VAR);
+
+ if (handler) {
+ int is_term_digit = 0;
+ struct input_component *component;
+ enum srgs_match_type match;
+
+ switch_mutex_lock(handler->mutex);
+ component = handler->component;
+
+ is_term_digit = digit_mask_test(component->term_digit_mask, dtmf->digit);
+
+ if (!is_term_digit) {
+ component->digits[component->num_digits] = dtmf->digit;
+ component->num_digits++;
+ component->digits[component->num_digits] = '\0';
+ component->last_digit_time = switch_micro_time_now();
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Collected digits = \"%s\"\n", component->digits);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Collected term digit = \"%c\"\n", dtmf->digit);
+ }
+
+ match = srgs_grammar_match(component->grammar, component->digits);
+
+ /* adjust result if terminating digit was pressed */
+ if (is_term_digit) {
+ if (match == SMT_MATCH_PARTIAL) {
+ match = SMT_NO_MATCH;
+ } else if (match == SMT_MATCH) {
+ match = SMT_MATCH_END;
+ }
+ }
+
+ switch (match) {
+ case SMT_MATCH:
+ case SMT_MATCH_PARTIAL: {
+ /* need more digits */
+ if (component->num_digits == 1) {
+ send_barge_event(RAYO_COMPONENT(component));
+ }
+ break;
+ }
+ case SMT_NO_MATCH: {
+ /* notify of no-match and remove input component */
+ handler->component = NULL;
+ switch_core_media_bug_remove(session, &handler->bug);
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "NO MATCH = %s\n", component->digits);
+ rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_NOMATCH);
+ break;
+ }
+ case SMT_MATCH_END: {
+ iks *result = nlsml_create_dtmf_match(component->digits);
+ /* notify of match and remove input component */
+ handler->component = NULL;
+ switch_core_media_bug_remove(session, &handler->bug);
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "MATCH = %s\n", component->digits);
+ rayo_component_send_complete_with_metadata(RAYO_COMPONENT(component), INPUT_MATCH, result, 0);
+ iks_delete(result);
+ break;
+ }
+ }
+ switch_mutex_unlock(handler->mutex);
+ }
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Monitor for input
+ */
+static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
+{
+ switch_core_session_t *session = switch_core_media_bug_get_session(bug);
+ struct input_handler *handler = (struct input_handler *)user_data;
+ struct input_component *component;
+
+ switch_mutex_lock(handler->mutex);
+ component = handler->component;
+
+ switch(type) {
+ case SWITCH_ABC_TYPE_INIT: {
+ switch_core_event_hook_add_recv_dtmf(session, input_component_on_dtmf);
+ break;
+ }
+ case SWITCH_ABC_TYPE_READ_REPLACE: {
+ switch_frame_t *rframe = switch_core_media_bug_get_read_replace_frame(bug);
+ /* check for timeout */
+ if (component && component->start_timers) {
+ int elapsed_ms = (switch_micro_time_now() - component->last_digit_time) / 1000;
+ if (component->num_digits && component->inter_digit_timeout > 0 && elapsed_ms > component->inter_digit_timeout) {
+ enum srgs_match_type match;
+ handler->component = NULL;
+
+ /* we got some input, check for match */
+ match = srgs_grammar_match(component->grammar, component->digits);
+ if (match == SMT_MATCH || match == SMT_MATCH_END) {
+ iks *result = nlsml_create_dtmf_match(component->digits);
+ /* notify of match */
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "MATCH = %s\n", component->digits);
+ rayo_component_send_complete_with_metadata(RAYO_COMPONENT(component), INPUT_MATCH, result, 0);
+ iks_delete(result);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "inter-digit-timeout\n");
+ rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_INTER_DIGIT_TIMEOUT);
+ }
+ } else if (!component->num_digits && component->initial_timeout > 0 && elapsed_ms > component->initial_timeout) {
+ handler->component = NULL;
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "initial-timeout\n");
+ rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_INITIAL_TIMEOUT);
+ }
+ }
+ switch_core_media_bug_set_read_replace_frame(bug, rframe);
+ break;
+ }
+ case SWITCH_ABC_TYPE_CLOSE:
+ /* check for hangup */
+ if (component) {
+ if (component->stop) {
+ handler->component = NULL;
+ rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP);
+ } else {
+ handler->component = NULL;
+ rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_HANGUP);
+ }
+ }
+ switch_core_event_hook_remove_recv_dtmf(session, input_component_on_dtmf);
+ break;
+ default:
+ break;
+ }
+ switch_mutex_unlock(handler->mutex);
+ return SWITCH_TRUE;
+}
+
+/**
+ * Validate input request
+ * @param input request to validate
+ * @param error message
+ * @return 0 if error, 1 if valid
+ */
+static int validate_call_input(iks *input, const char **error)
+{
+ iks *grammar;
+ const char *content_type;
+
+ /* validate input attributes */
+ if (!VALIDATE_RAYO_INPUT(input)) {
+ *error = "Bad <input> attrib value";
+ return 0;
+ }
+
+ /* missing grammar */
+ grammar = iks_find(input, "grammar");
+ if (!grammar) {
+ *error = "Missing <grammar>";
+ return 0;
+ }
+
+ /* only support srgs */
+ content_type = iks_find_attrib(grammar, "content-type");
+ if (!zstr(content_type) && strcmp("application/srgs+xml", content_type)) {
+ *error = "Unsupported content type";
+ return 0;
+ }
+
+ /* missing grammar body */
+ if (zstr(iks_find_cdata(input, "grammar"))) {
+ *error = "Grammar content is missing";
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Start call input for the given component
+ * @param component the input or prompt component
+ * @param session the session
+ * @param input the input request
+ * @param iq the original input/prompt request
+ */
+static iks *start_call_input(struct input_component *component, switch_core_session_t *session, iks *input, iks *iq, const char *output_file, int barge_in)
+{
+ /* set up input component for new detection */
+ struct input_handler *handler = (struct input_handler *)switch_channel_get_private(switch_core_session_get_channel(session), RAYO_INPUT_COMPONENT_PRIVATE_VAR);
+ if (!handler) {
+ /* create input component */
+ handler = switch_core_session_alloc(session, sizeof(*handler));
+ switch_mutex_init(&handler->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
+ switch_channel_set_private(switch_core_session_get_channel(session), RAYO_INPUT_COMPONENT_PRIVATE_VAR, handler);
+ }
+ handler->component = component;
+ component->num_digits = 0;
+ component->digits[0] = '\0';
+ component->stop = 0;
+ component->speech_mode = 0;
+ component->initial_timeout = iks_find_int_attrib(input, "initial-timeout");
+ component->inter_digit_timeout = iks_find_int_attrib(input, "inter-digit-timeout");
+ component->max_silence = iks_find_int_attrib(input, "max-silence");
+ component->min_confidence = (int)ceil(iks_find_decimal_attrib(input, "min-confidence") * 100.0);
+ component->barge_event = iks_find_bool_attrib(input, "barge-event");
+ component->start_timers = iks_find_bool_attrib(input, "start-timers");
+ component->term_digit_mask = digit_mask_set_from_digits(0, iks_find_attrib_soft(input, "terminator"));
+ component->handler = handler;
+
+ /* parse the grammar */
+ if (!(component->grammar = srgs_parse(globals.parser, iks_find_cdata(input, "grammar")))) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Failed to parse grammar body\n");
+ RAYO_UNLOCK(component);
+ RAYO_DESTROY(component);
+ return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Failed to parse grammar body");
+ }
+
+ /* is this voice or dtmf srgs grammar? */
+ if (!strcasecmp("dtmf", iks_find_attrib_soft(input, "mode"))) {
+ component->last_digit_time = switch_micro_time_now();
+
+ /* acknowledge command */
+ rayo_component_send_start(RAYO_COMPONENT(component), iq);
+
+ /* start dtmf input detection */
+ if (switch_core_media_bug_add(session, "rayo_input_component", NULL, input_component_bug_callback, handler, 0, SMBF_READ_REPLACE, &handler->bug) != SWITCH_STATUS_SUCCESS) {
+ rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_ERROR);
+ }
+ } else {
+ char *grammar = NULL;
+ const char *jsgf_path;
+ component->speech_mode = 1;
+ jsgf_path = srgs_grammar_to_jsgf_file(component->grammar, SWITCH_GLOBAL_dirs.grammar_dir, "gram");
+ if (!jsgf_path) {
+ RAYO_UNLOCK(component);
+ RAYO_DESTROY(component);
+ return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Grammar error");
+ }
+
+ /* acknowledge command */
+ rayo_component_send_start(RAYO_COMPONENT(component), iq);
+
+ /* TODO configurable speech detection - different engines, grammar passthrough, dtmf handled by recognizer */
+ grammar = switch_mprintf("{no-input-timeout=%s,speech-timeout=%s,start-input-timers=%s,confidence-threshold=%d}%s",
+ component->initial_timeout, component->max_silence,
+ component->start_timers ? "true" : "false",
+ component->min_confidence, jsgf_path);
+ /* start speech detection */
+ switch_channel_set_variable(switch_core_session_get_channel(session), "fire_asr_events", "true");
+ if (switch_ivr_detect_speech(session, "pocketsphinx", grammar, "mod_rayo_grammar", "", NULL) != SWITCH_STATUS_SUCCESS) {
+ rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_ERROR);
+ }
+ switch_safe_free(grammar);
+ }
+
+ return NULL;
+}
+
+/**
+ * Start execution of input component
+ */
+static iks *start_call_input_component(struct rayo_actor *client, struct rayo_actor *call, iks *iq, void *session_data)
+{
+ switch_core_session_t *session = (switch_core_session_t *)session_data;
+ char *component_id = switch_mprintf("%s-input", switch_core_session_get_uuid(session));
+ switch_memory_pool_t *pool = NULL;
+ struct input_component *input_component = NULL;
+ iks *input = iks_find(iq, "input");
+ const char *error = NULL;
+
+ if (!validate_call_input(input, &error)) {
+ return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, error);
+ }
+
+ /* create component */
+ switch_core_new_memory_pool(&pool);
+ input_component = switch_core_alloc(pool, sizeof(*input_component));
+ rayo_component_init(RAYO_COMPONENT(input_component), pool, "input", component_id, call, iks_find_attrib(iq, "from"));
+ switch_safe_free(component_id);
+
+ /* start input */
+ return start_call_input(input_component, session, iks_find(iq, "input"), iq, NULL, 0);
+}
+
+/**
+ * Stop execution of input component
+ */
+static iks *stop_call_input_component(struct rayo_actor *client, struct rayo_actor *component, iks *iq, void *data)
+{
+ struct input_component *input_component = INPUT_COMPONENT(component);
+
+ if (input_component && !input_component->stop) {
+ switch_core_session_t *session = switch_core_session_locate(RAYO_COMPONENT(component)->parent->id);
+ if (session) {
+ switch_mutex_lock(input_component->handler->mutex);
+ if (input_component->speech_mode) {
+ input_component->stop = 1;
+ switch_ivr_stop_detect_speech(session);
+ rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP);
+ } else if (input_component->handler->bug) {
+ input_component->stop = 1;
+ switch_core_media_bug_remove(session, &input_component->handler->bug);
+ }
+ switch_mutex_unlock(input_component->handler->mutex);
+ switch_core_session_rwunlock(session);
+ }
+ }
+ return iks_new_iq_result(iq);
+}
+
+/**
+ * Start input component timers
+ */
+static iks *start_timers_call_input_component(struct rayo_actor *client, struct rayo_actor *component, iks *iq, void *data)
+{
+ struct input_component *input_component = INPUT_COMPONENT(component);
+ if (input_component) {
+ switch_core_session_t *session = switch_core_session_locate(RAYO_COMPONENT(component)->parent->id);
+ if (session) {
+ switch_mutex_lock(input_component->handler->mutex);
+ if (input_component->speech_mode) {
+ switch_ivr_detect_speech_start_input_timers(session);
+ rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP);
+ } else {
+ input_component->last_digit_time = switch_micro_time_now();
+ input_component->start_timers = 1;
+ }
+ switch_mutex_unlock(input_component->handler->mutex);
+ switch_core_session_rwunlock(session);
+ }
+ }
+ return iks_new_iq_result(iq);
+}
+
+/**
+ * Handle speech detection event
+ */
+static void on_detected_speech_event(switch_event_t *event)
+{
+ const char *speech_type = switch_event_get_header(event, "Speech-Type");
+ char *event_str = NULL;
+ switch_event_serialize(event, &event_str, SWITCH_FALSE);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s\n", event_str);
+ if (!speech_type) {
+ return;
+ }
+ if (!strcasecmp("detected-speech", speech_type)) {
+ const char *uuid = switch_event_get_header(event, "Unique-ID");
+ char *component_id = switch_mprintf("%s-input", uuid);
+ struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id);
+ switch_safe_free(component_id);
+ if (component) {
+ const char *result = switch_event_get_body(event);
+ switch_mutex_lock(INPUT_COMPONENT(component)->handler->mutex);
+ INPUT_COMPONENT(component)->handler->component = NULL;
+ switch_mutex_unlock(INPUT_COMPONENT(component)->handler->mutex);
+ if (zstr(result)) {
+ rayo_component_send_complete(component, INPUT_NOMATCH);
+ } else {
+ enum nlsml_match_type match_type = nlsml_parse(result, uuid);
+ switch (match_type) {
+ case NMT_NOINPUT:
+ rayo_component_send_complete(component, INPUT_INITIAL_TIMEOUT);
+ break;
+ case NMT_MATCH: {
+ iks *result_xml = nlsml_normalize(result);
+ rayo_component_send_complete_with_metadata(component, INPUT_MATCH, result_xml, 0);
+ iks_delete(result_xml);
+ break;
+ }
+ case NMT_BAD_XML:
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_WARNING, "Failed to parse NLSML result: %s!\n", result);
+ rayo_component_send_complete(component, INPUT_NOMATCH);
+ break;
+ case NMT_NOMATCH:
+ rayo_component_send_complete(component, INPUT_NOMATCH);
+ break;
+ default:
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_CRIT, "Unknown NLSML match type: %i, %s!\n", match_type, result);
+ rayo_component_send_complete(component, INPUT_NOMATCH);
+ break;
+ }
+ }
+ RAYO_UNLOCK(component);
+ }
+ } else if (!strcasecmp("begin-speaking", speech_type)) {
+ const char *uuid = switch_event_get_header(event, "Unique-ID");
+ char *component_id = switch_mprintf("%s-input", uuid);
+ struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id);
+ switch_safe_free(component_id);
+ if (component && INPUT_COMPONENT(component)->barge_event) {
+ send_barge_event(component);
+ }
+ RAYO_UNLOCK(component);
+ } else if (!strcasecmp("closed", speech_type)) {
+ const char *uuid = switch_event_get_header(event, "Unique-ID");
+ char *component_id = switch_mprintf("%s-input", uuid);
+ struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id);
+ switch_safe_free(component_id);
+ if (component) {
+ char *channel_state = switch_event_get_header(event, "Channel-State");
+ switch_mutex_lock(INPUT_COMPONENT(component)->handler->mutex);
+ INPUT_COMPONENT(component)->handler->component = NULL;
+ switch_mutex_unlock(INPUT_COMPONENT(component)->handler->mutex);
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Recognizer closed\n");
+ if (channel_state && !strcmp("CS_HANGUP", channel_state)) {
+ rayo_component_send_complete(component, COMPONENT_COMPLETE_HANGUP);
+ } else {
+ /* shouldn't get here... */
+ rayo_component_send_complete(component, COMPONENT_COMPLETE_ERROR);
+ }
+ RAYO_UNLOCK(component);
+ }
+ }
+ switch_safe_free(event_str);
+}
+
+/**
+ * Initialize input component
+ * @return SWITCH_STATUS_SUCCESS if successful
+ */
+switch_status_t rayo_input_component_load(void)
+{
+ srgs_init();
+ nlsml_init();
+
+ globals.parser = srgs_parser_new(NULL);
+
+ rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_INPUT_NS":input", start_call_input_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "input", "set:"RAYO_EXT_NS":stop", stop_call_input_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "input", "set:"RAYO_INPUT_NS":start-timers", start_timers_call_input_component);
+ switch_event_bind("rayo_input_component", SWITCH_EVENT_DETECTED_SPEECH, SWITCH_EVENT_SUBCLASS_ANY, on_detected_speech_event, NULL);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Shutdown input component
+ * @return SWITCH_STATUS_SUCCESS if successful
+ */
+switch_status_t rayo_input_component_shutdown(void)
+{
+ srgs_parser_destroy(globals.parser);
+ switch_event_unbind_callback(on_detected_speech_event);
+ return SWITCH_STATUS_SUCCESS;
+}
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * output_component.c -- Rayo output component implementation
+ *
+ */
+#include "rayo_components.h"
+#include "rayo_elements.h"
+
+/**
+ * An output component
+ */
+struct output_component {
+ /** component base class */
+ struct rayo_component base;
+ /** document to play */
+ iks *document;
+ /** maximum time to play */
+ int max_time;
+ /** silence between repeats */
+ int repeat_interval;
+ /** number of times to repeat */
+ int repeat_times;
+ /** true if started paused */
+ switch_bool_t start_paused;
+ /** true if stopped */
+ int stop;
+};
+
+#define OUTPUT_FINISH "finish", RAYO_OUTPUT_COMPLETE_NS
+#define OUTPUT_MAX_TIME "max-time", RAYO_OUTPUT_COMPLETE_NS
+
+#define OUTPUT_COMPONENT(x) ((struct output_component *)x)
+
+/**
+ * Create new output component
+ */
+static struct rayo_component *create_output_component(struct rayo_actor *actor, iks *output, const char *client_jid)
+{
+ switch_memory_pool_t *pool;
+ struct output_component *output_component = NULL;
+
+ switch_core_new_memory_pool(&pool);
+ output_component = switch_core_alloc(pool, sizeof(*output_component));
+ rayo_component_init((struct rayo_component *)output_component, pool, "output", NULL, actor, client_jid);
+
+ output_component->document = iks_copy(output);
+ output_component->repeat_interval = iks_find_int_attrib(output, "repeat-interval");
+ output_component->repeat_times = iks_find_int_attrib(output, "repeat-times");
+ output_component->max_time = iks_find_int_attrib(output, "max-time");
+ output_component->start_paused = iks_find_bool_attrib(output, "start-paused");
+
+ return (struct rayo_component *)output_component;
+}
+
+/**
+ * Start execution of call output component
+ * @param component to start
+ * @param session the session to output to
+ * @param output the output request
+ * @param iq the original request
+ */
+static iks *start_call_output(struct rayo_component *component, switch_core_session_t *session, iks *output, iks *iq)
+{
+ switch_stream_handle_t stream = { 0 };
+
+ /* acknowledge command */
+ rayo_component_send_start(component, iq);
+
+ /* build playback command */
+ SWITCH_STANDARD_STREAM(stream);
+ stream.write_function(&stream, "{id=%s,session=%s,pause=%s",
+ RAYO_JID(component), switch_core_session_get_uuid(session),
+ OUTPUT_COMPONENT(component)->start_paused ? "true" : "false");
+ if (OUTPUT_COMPONENT(component)->max_time > 0) {
+ stream.write_function(&stream, ",timeout=%i", OUTPUT_COMPONENT(component)->max_time * 1000);
+ }
+ stream.write_function(&stream, "}fileman://rayo://%s", RAYO_JID(component));
+
+ if (switch_ivr_displace_session(session, stream.data, 0, "m") == SWITCH_STATUS_SUCCESS) {
+ RAYO_UNLOCK(component);
+ } else {
+ if (OUTPUT_COMPONENT(component)->document) {
+ iks_delete(OUTPUT_COMPONENT(component)->document);
+ }
+ if (switch_channel_get_state(switch_core_session_get_channel(session)) >= CS_HANGUP) {
+ rayo_component_send_complete(component, COMPONENT_COMPLETE_HANGUP);
+ component = NULL;
+ } else {
+ rayo_component_send_complete(component, COMPONENT_COMPLETE_ERROR);
+ component = NULL;
+ }
+ }
+ switch_safe_free(stream.data);
+ return NULL;
+}
+
+/**
+ * Start execution of call output component
+ */
+static iks *start_call_output_component(struct rayo_actor *client, struct rayo_actor *call, iks *iq, void *session_data)
+{
+ switch_core_session_t *session = (switch_core_session_t *)session_data;
+ struct rayo_component *output_component = NULL;
+ iks *output = iks_find(iq, "output");
+
+ /* validate output attributes */
+ if (!VALIDATE_RAYO_OUTPUT(output)) {
+ return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
+ }
+
+ output_component = create_output_component(call, output, iks_find_attrib(iq, "from"));
+ return start_call_output(output_component, session, output, iq);
+}
+
+/**
+ * Start execution of mixer output component
+ */
+static iks *start_mixer_output_component(struct rayo_actor *client, struct rayo_actor *mixer, iks *iq, void *data)
+{
+ struct rayo_component *component = NULL;
+ iks *output = iks_find(iq, "output");
+ switch_stream_handle_t stream = { 0 };
+
+ /* validate output attributes */
+ if (!VALIDATE_RAYO_OUTPUT(output)) {
+ return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
+ }
+
+ component = create_output_component(mixer, output, iks_find_attrib(iq, "from"));
+
+ /* build conference command */
+ SWITCH_STANDARD_STREAM(stream);
+ stream.write_function(&stream, "%s play ", rayo_mixer_get_name(RAYO_MIXER(mixer)), RAYO_ID(component));
+
+ stream.write_function(&stream, "{id=%s,pause=%s",
+ RAYO_JID(component),
+ OUTPUT_COMPONENT(component)->start_paused ? "true" : "false");
+ if (OUTPUT_COMPONENT(component)->max_time > 0) {
+ stream.write_function(&stream, ",timeout=%i", OUTPUT_COMPONENT(component)->max_time * 1000);
+ }
+ stream.write_function(&stream, "}fileman://rayo://%s", RAYO_JID(component));
+
+ rayo_component_api_execute_async(component, "conference", stream.data);
+
+ switch_safe_free(stream.data);
+ RAYO_UNLOCK(component);
+
+ return NULL;
+}
+
+/**
+ * Stop execution of output component
+ */
+static iks *stop_output_component(struct rayo_actor *client, struct rayo_actor *component, iks *iq, void *data)
+{
+ switch_stream_handle_t stream = { 0 };
+ char *command = switch_mprintf("%s stop", RAYO_JID(component));
+ SWITCH_STANDARD_STREAM(stream);
+ OUTPUT_COMPONENT(component)->stop = 1;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s stopping\n", RAYO_JID(component));
+ switch_api_execute("fileman", command, NULL, &stream);
+ switch_safe_free(stream.data);
+ switch_safe_free(command);
+ return iks_new_iq_result(iq);
+}
+
+/**
+ * Pause execution of output component
+ */
+static iks *pause_output_component(struct rayo_actor *client, struct rayo_actor *component, iks *iq, void *data)
+{
+ switch_stream_handle_t stream = { 0 };
+ char *command = switch_mprintf("%s pause", RAYO_JID(component));
+ SWITCH_STANDARD_STREAM(stream);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s pausing\n", RAYO_JID(component));
+ switch_api_execute("fileman", command, NULL, &stream);
+ switch_safe_free(stream.data);
+ switch_safe_free(command);
+ return iks_new_iq_result(iq);
+}
+
+/**
+ * Resume execution of output component
+ */
+static iks *resume_output_component(struct rayo_actor *client, struct rayo_actor *component, iks *iq, void *data)
+{
+ switch_stream_handle_t stream = { 0 };
+ char *command = switch_mprintf("%s resume", RAYO_JID(component));
+ SWITCH_STANDARD_STREAM(stream);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s resuming\n", RAYO_JID(component));
+ switch_api_execute("fileman", command, NULL, &stream);
+ switch_safe_free(stream.data);
+ switch_safe_free(command);
+ return iks_new_iq_result(iq);
+}
+
+/**
+ * Speed up execution of output component
+ */
+static iks *speed_up_output_component(struct rayo_actor *client, struct rayo_actor *component, iks *iq, void *data)
+{
+ switch_stream_handle_t stream = { 0 };
+ char *command = switch_mprintf("%s speed:+", RAYO_JID(component));
+ SWITCH_STANDARD_STREAM(stream);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s speeding up\n", RAYO_JID(component));
+ switch_api_execute("fileman", command, NULL, &stream);
+ switch_safe_free(stream.data);
+ switch_safe_free(command);
+ return iks_new_iq_result(iq);
+}
+
+/**
+ * Slow down execution of output component
+ */
+static iks *speed_down_output_component(struct rayo_actor *client, struct rayo_actor *component, iks *iq, void *data)
+{
+ switch_stream_handle_t stream = { 0 };
+ char *command = switch_mprintf("%s speed:-", RAYO_JID(component));
+ SWITCH_STANDARD_STREAM(stream);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s slowing down\n", RAYO_JID(component));
+ switch_api_execute("fileman", command, NULL, &stream);
+ switch_safe_free(stream.data);
+ switch_safe_free(command);
+ return iks_new_iq_result(iq);
+}
+
+/**
+ * Increase volume of output component
+ */
+static iks *volume_up_output_component(struct rayo_actor *client, struct rayo_actor *component, iks *iq, void *data)
+{
+ switch_stream_handle_t stream = { 0 };
+ char *command = switch_mprintf("%s volume:+", RAYO_JID(component));
+ SWITCH_STANDARD_STREAM(stream);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s increasing volume\n", RAYO_JID(component));
+ switch_api_execute("fileman", command, NULL, &stream);
+ switch_safe_free(stream.data);
+ switch_safe_free(command);
+ return iks_new_iq_result(iq);
+}
+
+/**
+ * Lower volume of output component
+ */
+static iks *volume_down_output_component(struct rayo_actor *client, struct rayo_actor *component, iks *iq, void *data)
+{
+ switch_stream_handle_t stream = { 0 };
+ char *command = switch_mprintf("%s volume:-", RAYO_JID(component));
+ SWITCH_STANDARD_STREAM(stream);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s lowering volume\n", RAYO_JID(component));
+ switch_api_execute("fileman", command, NULL, &stream);
+ switch_safe_free(stream.data);
+ switch_safe_free(command);
+ return iks_new_iq_result(iq);
+}
+
+/**
+ * Seek output component
+ */
+static iks *seek_output_component(struct rayo_actor *client, struct rayo_actor *component, iks *iq, void *data)
+{
+ iks *seek = iks_find(iq, "seek");
+
+ if (VALIDATE_RAYO_OUTPUT_SEEK(seek)) {
+ int is_forward = !strcmp("forward", iks_find_attrib(seek, "direction"));
+ int amount_ms = iks_find_int_attrib(seek, "amount");
+ char *command = switch_mprintf("%s seek:%s%i", RAYO_JID(component),
+ is_forward ? "+" : "-", amount_ms);
+ switch_stream_handle_t stream = { 0 };
+ SWITCH_STANDARD_STREAM(stream);
+
+ switch_api_execute("fileman", command, NULL, &stream);
+
+ switch_safe_free(stream.data);
+ switch_safe_free(command);
+
+ return iks_new_iq_result(iq);
+ }
+ return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
+}
+
+/**
+ * Rayo document playback state
+ */
+struct rayo_file_context {
+ /** handle to current file */
+ switch_file_handle_t fh;
+ /** current document being played */
+ iks *cur_doc;
+ /** current file string being played */
+ char *ssml;
+ /** The component */
+ struct rayo_component *component;
+ /** number of times played */
+ int play_count;
+};
+
+/**
+ * open next file for reading
+ * @param handle the file handle
+ */
+static switch_status_t next_file(switch_file_handle_t *handle)
+{
+ struct rayo_file_context *context = handle->private_info;
+ struct output_component *output = context->component ? OUTPUT_COMPONENT(context->component) : NULL;
+
+ top:
+
+ if (switch_test_flag((&context->fh), SWITCH_FILE_OPEN)) {
+ switch_core_file_close(&context->fh);
+ }
+
+ if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
+ /* unsupported */
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (!context->cur_doc) {
+ context->cur_doc = iks_find(output->document, "document");
+ if (!context->cur_doc) {
+ iks_delete(output->document);
+ output->document = NULL;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing <document>\n");
+ return SWITCH_STATUS_FALSE;
+ }
+ } else {
+ context->cur_doc = iks_next_tag(context->cur_doc);
+ }
+
+ /* done? */
+ if (!context->cur_doc) {
+ if (++context->play_count < output->repeat_times) {
+ /* repeat all document(s) */
+ if (!output->repeat_interval) {
+ goto top;
+ }
+ } else {
+ /* no more files to play */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Done playing\n");
+ return SWITCH_STATUS_FALSE;
+ }
+ }
+
+ if (!context->cur_doc) {
+ /* play silence between repeats */
+ switch_safe_free(context->ssml);
+ context->ssml = switch_mprintf("silence_stream://%i", output->repeat_interval);
+ } else {
+ /* play next document */
+ iks *speak = NULL;
+
+ switch_safe_free(context->ssml);
+ context->ssml = NULL;
+ speak = iks_find(context->cur_doc, "speak");
+ if (speak) {
+ /* <speak> is child node */
+ char *ssml_str = iks_string(NULL, speak);
+ context->ssml = switch_mprintf("ssml://%s", ssml_str);
+ iks_free(ssml_str);
+ } else if (iks_has_children(context->cur_doc)) {
+ /* check if <speak> is in CDATA */
+ const char *ssml_str = NULL;
+ iks *ssml = iks_child(context->cur_doc);
+ if (ssml && iks_type(ssml) == IKS_CDATA) {
+ ssml_str = iks_cdata(ssml);
+ }
+ if (zstr(ssml_str)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing <document> CDATA\n");
+ return SWITCH_STATUS_FALSE;
+ }
+ context->ssml = switch_mprintf("ssml://%s", ssml_str);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing <speak>\n");
+ return SWITCH_STATUS_FALSE;
+ }
+ }
+ if (switch_core_file_open(&context->fh, context->ssml, handle->channels, handle->samplerate, handle->flags, NULL) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Failed to open %s\n", context->ssml);
+ goto top;
+ }
+
+ handle->samples = context->fh.samples;
+ handle->format = context->fh.format;
+ handle->sections = context->fh.sections;
+ handle->seekable = context->fh.seekable;
+ handle->speed = context->fh.speed;
+ handle->vol = context->fh.vol;
+ handle->offset_pos = context->fh.offset_pos;
+ handle->interval = context->fh.interval;
+
+ if (switch_test_flag((&context->fh), SWITCH_FILE_NATIVE)) {
+ switch_set_flag(handle, SWITCH_FILE_NATIVE);
+ } else {
+ switch_clear_flag(handle, SWITCH_FILE_NATIVE);
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Transforms Rayo document into sub-format and opens file_string.
+ * @param handle
+ * @param path the inline Rayo document
+ * @return SWITCH_STATUS_SUCCESS if opened
+ */
+static switch_status_t rayo_file_open(switch_file_handle_t *handle, const char *path)
+{
+ switch_status_t status = SWITCH_STATUS_FALSE;
+ struct rayo_file_context *context = switch_core_alloc(handle->memory_pool, sizeof(*context));
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Got path %s\n", path);
+
+ context->component = RAYO_COMPONENT_LOCATE(path);
+
+ if (context->component) {
+ handle->private_info = context;
+ context->cur_doc = NULL;
+ context->play_count = 0;
+ status = next_file(handle);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "File error! %s\n", path);
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (status != SWITCH_STATUS_SUCCESS && context->component) {
+ /* complete error event will be sent by calling thread */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Status = %i\n", status);
+ RAYO_UNLOCK(context->component);
+ }
+
+ return status;
+}
+
+/**
+ * Close SSML document.
+ * @param handle
+ * @return SWITCH_STATUS_SUCCESS
+ */
+static switch_status_t rayo_file_close(switch_file_handle_t *handle)
+{
+ struct rayo_file_context *context = (struct rayo_file_context *)handle->private_info;
+
+ if (context && context->component) {
+ struct output_component *output = OUTPUT_COMPONENT(context->component);
+
+ /* send completion and destroy */
+ if (output->stop) {
+ rayo_component_send_complete(context->component, COMPONENT_COMPLETE_STOP);
+ } else {
+ rayo_component_send_complete(context->component, OUTPUT_FINISH);
+ }
+ /* TODO hangup / timed out */
+
+ /* cleanup internals */
+ switch_safe_free(context->ssml);
+ context->ssml = NULL;
+ if (output->document) {
+ iks_delete(output->document);
+ output->document = NULL;
+ }
+
+ /* close SSML file */
+ if (switch_test_flag((&context->fh), SWITCH_FILE_OPEN)) {
+ return switch_core_file_close(&context->fh);
+ }
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Read from SSML document
+ * @param handle
+ * @param data
+ * @param len
+ * @return
+ */
+static switch_status_t rayo_file_read(switch_file_handle_t *handle, void *data, size_t *len)
+{
+ switch_status_t status;
+ struct rayo_file_context *context = (struct rayo_file_context *)handle->private_info;
+ size_t llen = *len;
+
+ if (OUTPUT_COMPONENT(context->component)->stop) {
+ return SWITCH_STATUS_FALSE;
+ } else {
+ status = switch_core_file_read(&context->fh, data, len);
+ if (status != SWITCH_STATUS_SUCCESS) {
+ if ((status = next_file(handle)) != SWITCH_STATUS_SUCCESS) {
+ return status;
+ }
+ *len = llen;
+ status = switch_core_file_read(&context->fh, data, len);
+ }
+ }
+
+ return status;
+}
+
+/**
+ * Seek file
+ */
+static switch_status_t rayo_file_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence)
+{
+ struct rayo_file_context *context = handle->private_info;
+
+ if (samples == 0 && whence == SWITCH_SEEK_SET) {
+ /* restart from beginning */
+ context->cur_doc = NULL;
+ context->play_count = 0;
+ return next_file(handle);
+ }
+
+ if (!handle->seekable) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "File is not seekable\n");
+ return SWITCH_STATUS_NOTIMPL;
+ }
+
+ return switch_core_file_seek(&context->fh, cur_sample, samples, whence);
+}
+
+/**
+ * Manages access to fileman controls
+ */
+struct {
+ /** synchronizes access to fileman hash */
+ switch_mutex_t *mutex;
+ /** fileman mapped by id */
+ switch_hash_t *hash;
+} fileman_globals;
+
+#define FILE_STARTBYTES 1024 * 32
+#define FILE_BLOCKSIZE 1024 * 8
+#define FILE_BUFSIZE 1024 * 64
+
+/**
+ * Fileman playback state
+ */
+struct fileman_file_context {
+ /** handle to current file */
+ switch_file_handle_t fh;
+ /** file buffer */
+ int16_t *abuf;
+ /** end of file */
+ int eof;
+ /** maximum size of a packet in 2-byte samples */
+ switch_size_t max_frame_len;
+ /** optional session UUID */
+ const char *uuid;
+ /** fileman control ID */
+ const char *id;
+};
+
+/**
+ * Wraps file with interface that can be controlled by fileman flags
+ * @param handle
+ * @param path the file to play
+ * @return SWITCH_STATUS_SUCCESS if opened
+ */
+static switch_status_t fileman_file_open(switch_file_handle_t *handle, const char *path)
+{
+ switch_status_t status = SWITCH_STATUS_FALSE;
+ struct fileman_file_context *context = switch_core_alloc(handle->memory_pool, sizeof(*context));
+ handle->private_info = context;
+
+ if (handle->params) {
+ const char *id = switch_event_get_header(handle->params, "id");
+ const char *uuid = switch_event_get_header(handle->params, "session");
+ if (!zstr(id)) {
+ context->id = switch_core_strdup(handle->memory_pool, id);
+ }
+ if (!zstr(uuid)) {
+ context->uuid = switch_core_strdup(handle->memory_pool, uuid);
+ }
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Got path %s\n", path);
+
+ if ((status = switch_core_file_open(&context->fh, path, handle->channels, handle->samplerate, handle->flags, NULL)) != SWITCH_STATUS_SUCCESS) {
+ return status;
+ }
+
+ /* set up handle for external control */
+ if (!context->id) {
+ /* use filename as ID */
+ context->id = switch_core_strdup(handle->memory_pool, path);
+ }
+ switch_mutex_lock(fileman_globals.mutex);
+ if (!switch_core_hash_find(fileman_globals.hash, context->id)) {
+ switch_core_hash_insert(fileman_globals.hash, context->id, handle);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_WARNING, "Duplicate fileman ID: %s\n", context->id);
+ return SWITCH_STATUS_FALSE;
+ }
+ switch_mutex_unlock(fileman_globals.mutex);
+
+ context->max_frame_len = (handle->samplerate / 1000 * SWITCH_MAX_INTERVAL);
+ switch_zmalloc(context->abuf, FILE_STARTBYTES);
+
+ if (!context->fh.audio_buffer) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Create audio buffer\n");
+ switch_buffer_create_dynamic(&context->fh.audio_buffer, FILE_BLOCKSIZE, FILE_BUFSIZE, 0);
+ switch_assert(context->fh.audio_buffer);
+ }
+
+ handle->samples = context->fh.samples;
+ handle->format = context->fh.format;
+ handle->sections = context->fh.sections;
+ handle->seekable = context->fh.seekable;
+ handle->speed = context->fh.speed;
+ handle->vol = context->fh.vol;
+ handle->offset_pos = context->fh.offset_pos;
+ handle->interval = context->fh.interval;
+
+ if (switch_test_flag((&context->fh), SWITCH_FILE_NATIVE)) {
+ switch_set_flag(handle, SWITCH_FILE_NATIVE);
+ } else {
+ switch_clear_flag(handle, SWITCH_FILE_NATIVE);
+ }
+
+ if (handle->params && switch_true(switch_event_get_header(handle->params, "pause"))) {
+ switch_set_flag(handle, SWITCH_FILE_PAUSE);
+ }
+
+ return status;
+}
+
+/**
+ * Close file.
+ * @param handle
+ * @return SWITCH_STATUS_SUCCESS
+ */
+static switch_status_t fileman_file_close(switch_file_handle_t *handle)
+{
+ struct fileman_file_context *context = (struct fileman_file_context *)handle->private_info;
+ switch_file_handle_t *fh = &context->fh;
+
+ if (context->id) {
+ switch_mutex_lock(fileman_globals.mutex);
+ switch_core_hash_delete(fileman_globals.hash, context->id);
+ switch_mutex_unlock(fileman_globals.mutex);
+ }
+
+ if (switch_test_flag(fh, SWITCH_FILE_OPEN)) {
+ free(context->abuf);
+
+ if (fh->audio_buffer) {
+ switch_buffer_destroy(&fh->audio_buffer);
+ }
+
+ if (fh->sp_audio_buffer) {
+ switch_buffer_destroy(&fh->sp_audio_buffer);
+ }
+ return switch_core_file_close(fh);
+ }
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Write to file
+ * @param handle
+ * @param data
+ * @param len
+ * @return
+ */
+static switch_status_t fileman_file_write(switch_file_handle_t *handle, void *data, size_t *len)
+{
+ struct fileman_file_context *context = (struct fileman_file_context *)handle->private_info;
+ switch_file_handle_t *fh = &context->fh;
+ if (!switch_test_flag(handle, SWITCH_FILE_PAUSE)) {
+ return switch_core_file_write(fh, data, len);
+ }
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Read from file
+ * @param handle
+ * @param data
+ * @param len
+ * @return
+ */
+static switch_status_t fileman_file_read(switch_file_handle_t *handle, void *data, size_t *len)
+{
+ struct fileman_file_context *context = (struct fileman_file_context *)handle->private_info;
+ switch_file_handle_t *fh = &context->fh;
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ switch_size_t o_len = 0;
+
+ /* anything called "_len" is measured in 2-byte samples */
+
+ if (switch_test_flag(fh, SWITCH_FILE_NATIVE)) {
+ return switch_core_file_read(fh, data, len);
+ }
+
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "len = %"SWITCH_SIZE_T_FMT"\n", *len);
+ if (*len > context->max_frame_len) {
+ *len = context->max_frame_len;
+ }
+
+ for (;;) {
+ int do_speed = 1;
+ int last_speed = -1;
+ size_t read_bytes = 0;
+
+ if (switch_test_flag(handle, SWITCH_FILE_PAUSE)) {
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Read pause frame\n");
+ memset(context->abuf, 255, *len * 2);
+ do_speed = 0;
+ o_len = *len;
+ } else if (fh->sp_audio_buffer && (context->eof || (switch_buffer_inuse(fh->sp_audio_buffer) > (switch_size_t) (*len * 2)))) {
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Read speed frame\n");
+ /* get next speed frame */
+ if (!(read_bytes = switch_buffer_read(fh->sp_audio_buffer, context->abuf, *len * 2))) {
+ /* This is the reverse of what happens in switch_ivr_play_file... i think that implementation is wrong */
+ if (context->eof) {
+ /* done with file */
+ status = SWITCH_STATUS_FALSE;
+ goto done;
+ } else {
+ /* try again to fetch frame */
+ continue;
+ }
+ }
+
+ /* pad short frame with silence */
+ if (read_bytes < *len * 2) {
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Padding speed frame %"SWITCH_SIZE_T_FMT" bytes\n", (context->frame_len * 2) - read_bytes);
+ memset(context->abuf + read_bytes, 255, (*len * 2) - read_bytes);
+ }
+ o_len = *len;
+ do_speed = 0;
+ } else if (fh->audio_buffer && (context->eof || (switch_buffer_inuse(fh->audio_buffer) > (switch_size_t) (*len * 2)))) {
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "(2) Read audio frame\n");
+ /* get next file frame */
+ if (!(read_bytes = switch_buffer_read(fh->audio_buffer, context->abuf, *len * 2))) {
+ if (context->eof) {
+ /* done with file */
+ status = SWITCH_STATUS_FALSE;
+ goto done;
+ } else {
+ /* try again to fetch frame */
+ continue;
+ }
+ }
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "(2) Read audio frame %"SWITCH_SIZE_T_FMT" bytes\n", read_bytes);
+ fh->offset_pos += read_bytes / 2;
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "(2) file pos = %i\n", fh->offset_pos);
+
+ /* pad short frame with silence */
+ if (read_bytes < (*len * 2)) {
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Padding audio frame %"SWITCH_SIZE_T_FMT" bytes\n", (context->frame_len * 2) - read_bytes);
+ memset(context->abuf + read_bytes, 255, (*len * 2) - read_bytes);
+ }
+
+ o_len = *len;
+ } else {
+ if (context->eof) {
+ /* done with file */
+ status = SWITCH_STATUS_FALSE;
+ goto done;
+ }
+ o_len = FILE_STARTBYTES / 2;
+ if (switch_core_file_read(fh, context->abuf, &o_len) != SWITCH_STATUS_SUCCESS) {
+ context->eof++;
+ /* at end of file... need to clear buffers before giving up */
+ continue;
+ }
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Read file %"SWITCH_SIZE_T_FMT" bytes\n", o_len * 2);
+
+ /* add file data to audio bufer */
+ read_bytes = switch_buffer_write(fh->audio_buffer, context->abuf, o_len * 2);
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Write audio frame %"SWITCH_SIZE_T_FMT" bytes\n", read_bytes);
+
+ read_bytes = switch_buffer_read(fh->audio_buffer, context->abuf, *len * 2);
+ o_len = read_bytes / 2;
+ fh->offset_pos += o_len;
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Read audio frame %"SWITCH_SIZE_T_FMT" bytes\n", read_bytes);
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "file pos = %i\n", fh->offset_pos);
+ }
+
+ if (o_len <= 0) {
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "o_len <= 0 (%"SWITCH_SIZE_T_FMT")\n", o_len);
+ status = SWITCH_STATUS_FALSE;
+ goto done;
+ }
+
+ /* limit speed... there is a .25 factor change in packet size relative to original packet size for each increment.
+ Too many increments and we cause badness when (factor * speed * o_len) > o_len */
+ if (handle->speed > 2) {
+ handle->speed = 2;
+ } else if (handle->speed < -2) {
+ handle->speed = -2;
+ }
+
+ if (fh->audio_buffer && last_speed > -1 && last_speed != handle->speed) {
+ /* speed has changed, flush the buffer */
+ switch_buffer_zero(fh->sp_audio_buffer);
+ }
+
+ if (switch_test_flag(fh, SWITCH_FILE_SEEK)) {
+ /* file position has changed flush the buffer */
+ switch_buffer_zero(fh->audio_buffer);
+ switch_clear_flag(fh, SWITCH_FILE_SEEK);
+ }
+
+ /* generate speed frames */
+ if (handle->speed && do_speed) {
+ float factor = 0.25f * abs(handle->speed);
+ switch_size_t new_len, supplement_len, step_len;
+ short *bp = context->abuf;
+ switch_size_t wrote_len = 0;
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Generate speed frame (%i)\n", handle->speed);
+
+ supplement_len = (int) (factor * o_len);
+ if (!supplement_len) {
+ supplement_len = 1;
+ }
+ new_len = (handle->speed > 0) ? o_len - supplement_len : o_len + supplement_len;
+
+ step_len = (handle->speed > 0) ? (new_len / supplement_len) : (o_len / supplement_len);
+
+ if (!fh->sp_audio_buffer) {
+ switch_buffer_create_dynamic(&fh->sp_audio_buffer, 1024, 1024, 0);
+ }
+
+ while ((wrote_len + step_len) < new_len) {
+ switch_buffer_write(fh->sp_audio_buffer, bp, step_len * 2);
+ wrote_len += step_len;
+ bp += step_len;
+ if (handle->speed > 0) {
+ bp++;
+ } else {
+ float f;
+ short s;
+ f = (float) (*bp + *(bp + 1) + *(bp - 1));
+ f /= 3;
+ s = (short) f;
+ switch_buffer_write(fh->sp_audio_buffer, &s, 2);
+ wrote_len++;
+ }
+ }
+ if (wrote_len < new_len) {
+ switch_size_t r_len = new_len - wrote_len;
+ switch_buffer_write(fh->sp_audio_buffer, bp, r_len * 2);
+ wrote_len += r_len;
+ }
+ last_speed = handle->speed;
+ continue;
+ }
+
+ /* adjust volume on frame */
+ if (handle->vol) {
+ //switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Adjust volume to = %i\n", handle->vol);
+ switch_change_sln_volume(context->abuf, *len, handle->vol);
+ }
+ break;
+ }
+
+done:
+
+ /* copy frame over to return to caller */
+ memcpy(data, context->abuf, *len * 2);
+ handle->offset_pos = context->fh.offset_pos;
+
+ return status;
+}
+
+/**
+ * Seek file
+ */
+static switch_status_t fileman_file_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence)
+{
+ struct fileman_file_context *context = handle->private_info;
+
+ if (!handle->seekable) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_WARNING, "File is not seekable\n");
+ return SWITCH_STATUS_NOTIMPL;
+ }
+ return switch_core_file_seek(&context->fh, cur_sample, samples, whence);
+}
+
+/**
+ * Process fileman command
+ */
+static switch_status_t fileman_process_cmd(const char *cmd, switch_file_handle_t *fhp)
+{
+ if (zstr(cmd)) {
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ if (fhp) {
+ if (!switch_test_flag(fhp, SWITCH_FILE_OPEN)) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (!strncasecmp(cmd, "speed", 5)) {
+ char *p;
+
+ if ((p = strchr(cmd, ':'))) {
+ p++;
+ if (*p == '+' || *p == '-') {
+ int step;
+ if (!(step = atoi(p))) {
+ if (*p == '+') {
+ step = 1;
+ } else {
+ step = -1;
+ }
+ }
+ fhp->speed += step;
+ } else {
+ int speed = atoi(p);
+ fhp->speed = speed;
+ }
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ return SWITCH_STATUS_FALSE;
+
+ } else if (!strncasecmp(cmd, "volume", 6)) {
+ char *p;
+
+ if ((p = strchr(cmd, ':'))) {
+ p++;
+ if (*p == '+' || *p == '-') {
+ int step;
+ if (!(step = atoi(p))) {
+ if (*p == '+') {
+ step = 1;
+ } else {
+ step = -1;
+ }
+ }
+ fhp->vol += step;
+ } else {
+ int vol = atoi(p);
+ fhp->vol = vol;
+ }
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ if (fhp->vol) {
+ switch_normalize_volume(fhp->vol);
+ }
+
+ return SWITCH_STATUS_FALSE;
+ } else if (!strcasecmp(cmd, "pause")) {
+ switch_set_flag(fhp, SWITCH_FILE_PAUSE);
+ return SWITCH_STATUS_SUCCESS;
+ } else if (!strcasecmp(cmd, "resume")) {
+ switch_clear_flag(fhp, SWITCH_FILE_PAUSE);
+ return SWITCH_STATUS_SUCCESS;
+ } else if (!strcasecmp(cmd, "stop")) {
+ switch_set_flag(fhp, SWITCH_FILE_DONE);
+ return SWITCH_STATUS_FALSE;
+ } else if (!strcasecmp(cmd, "truncate")) {
+ switch_core_file_truncate(fhp, 0);
+ } else if (!strcasecmp(cmd, "restart")) {
+ unsigned int pos = 0;
+ fhp->speed = 0;
+ switch_core_file_seek(fhp, &pos, 0, SEEK_SET);
+ return SWITCH_STATUS_SUCCESS;
+ } else if (!strncasecmp(cmd, "seek", 4)) {
+ unsigned int samps = 0;
+ unsigned int pos = 0;
+ char *p;
+
+ if ((p = strchr(cmd, ':'))) {
+ p++;
+ if (*p == '+' || *p == '-') {
+ int step;
+ int32_t target;
+ if (!(step = atoi(p))) {
+ if (*p == '+') {
+ step = 1000;
+ } else {
+ step = -1000;
+ }
+ }
+
+ samps = step * (fhp->samplerate / 1000);
+ target = (int32_t)fhp->pos + samps;
+
+ if (target < 0) {
+ target = 0;
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "seek to position %d\n", target);
+ switch_core_file_seek(fhp, &pos, target, SEEK_SET);
+
+ } else {
+ samps = switch_atoui(p) * (fhp->samplerate / 1000);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "seek to position %d\n", samps);
+ switch_core_file_seek(fhp, &pos, samps, SEEK_SET);
+ }
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+ }
+ }
+
+ if (!strcmp(cmd, "true") || !strcmp(cmd, "undefined")) {
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ return SWITCH_STATUS_FALSE;
+}
+
+#define FILEMAN_SYNTAX "<id> <cmd>:<val>"
+SWITCH_STANDARD_API(fileman_api)
+{
+ char *mycmd = NULL, *argv[4] = { 0 };
+ int argc = 0;
+
+ if (!zstr(cmd) && (mycmd = strdup(cmd))) {
+ argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
+ if (argc >= 2 && !zstr(argv[0])) {
+ char *id = argv[0];
+ char *cmd = argv[1];
+ switch_file_handle_t *fh = NULL;
+ switch_mutex_lock(fileman_globals.mutex);
+ fh = (switch_file_handle_t *)switch_core_hash_find(fileman_globals.hash, id);
+ if (fh) {
+ fileman_process_cmd(cmd, fh);
+ switch_mutex_unlock(fileman_globals.mutex);
+ stream->write_function(stream, "+OK\n");
+ } else {
+ switch_mutex_unlock(fileman_globals.mutex);
+ stream->write_function(stream, "-ERR No file handle!\n");
+ }
+ goto done;
+ }
+ }
+
+ stream->write_function(stream, "-USAGE: %s\n", FILEMAN_SYNTAX);
+
+ done:
+ switch_safe_free(mycmd);
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static char *rayo_supported_formats[] = { "rayo", NULL };
+static char *fileman_supported_formats[] = { "fileman", NULL };
+
+/**
+ * Initialize output component
+ * @return SWITCH_STATUS_SUCCESS if successful
+ */
+switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool)
+{
+ switch_api_interface_t *api_interface;
+ switch_file_interface_t *file_interface;
+
+ rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_OUTPUT_NS":output", start_call_output_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_EXT_NS":stop", stop_output_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":pause", pause_output_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":resume", resume_output_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":speed-up", speed_up_output_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":speed-down", speed_down_output_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":volume-up", volume_up_output_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":volume-down", volume_down_output_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":seek", seek_output_component);
+
+ rayo_actor_command_handler_add(RAT_MIXER, "", "set:"RAYO_OUTPUT_NS":output", start_mixer_output_component);
+ rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_EXT_NS":stop", stop_output_component);
+ rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":pause", pause_output_component);
+ rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":resume", resume_output_component);
+ rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":speed-up", speed_up_output_component);
+ rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":speed-down", speed_down_output_component);
+ rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":volume-up", volume_up_output_component);
+ rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":volume-down", volume_down_output_component);
+ rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":seek", seek_output_component);
+
+ file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);
+ file_interface->interface_name = "mod_rayo";
+ file_interface->extens = rayo_supported_formats;
+ file_interface->file_open = rayo_file_open;
+ file_interface->file_close = rayo_file_close;
+ file_interface->file_read = rayo_file_read;
+ file_interface->file_seek = rayo_file_seek;
+
+ switch_mutex_init(&fileman_globals.mutex, SWITCH_MUTEX_NESTED, pool);
+ switch_core_hash_init(&fileman_globals.hash, pool);
+
+ file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);
+ file_interface->interface_name = "mod_rayo";
+ file_interface->extens = fileman_supported_formats;
+ file_interface->file_open = fileman_file_open;
+ file_interface->file_close = fileman_file_close;
+ file_interface->file_write = fileman_file_write;
+ file_interface->file_read = fileman_file_read;
+ file_interface->file_seek = fileman_file_seek;
+
+ SWITCH_ADD_API(api_interface, "fileman", "Manage file audio", fileman_api, FILEMAN_SYNTAX);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Shutdown output component
+ * @return SWITCH_STATUS_SUCCESS if successful
+ */
+switch_status_t rayo_output_component_shutdown(void)
+{
+ return SWITCH_STATUS_SUCCESS;
+}
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * rayo_prompt_component.c -- Rayo prompt component implementation
+ *
+ */
+#include "rayo_components.h"
+#include "rayo_elements.h"
+
+enum prompt_component_state {
+ /* initial state - no barge */
+ PCS_START_OUTPUT,
+ /* playing prompt */
+ PCS_OUTPUT,
+ /* start input - no barge */
+ PCS_START_INPUT,
+ /* input starting - start timers needed */
+ PCS_START_INPUT_TIMERS,
+ /* initial state - barge in */
+ PCS_START_OUTPUT_BARGE,
+ /* start input for barge-in */
+ PCS_START_INPUT_OUTPUT,
+ /* playing and detecting */
+ PCS_INPUT_OUTPUT,
+ /* barge in on output */
+ PCS_STOP_OUTPUT,
+ /* detecting */
+ PCS_INPUT,
+ /* finishing, stop output */
+ PCS_DONE_STOP_OUTPUT,
+ /* finished */
+ PCS_DONE
+};
+
+/**
+ * Prompt state
+ */
+struct prompt_component {
+ struct rayo_component base;
+ enum prompt_component_state state;
+ iks *iq;
+ iks *complete;
+ struct rayo_actor *input;
+ struct rayo_actor *output;
+};
+
+#define PROMPT_COMPONENT(x) ((struct prompt_component *)x)
+
+static const char *prompt_component_state_to_string(enum prompt_component_state state)
+{
+ switch(state) {
+ case PCS_START_OUTPUT_BARGE: return "START_OUTPUT_BARGE";
+ case PCS_START_OUTPUT: return "START_OUTPUT";
+ case PCS_START_INPUT_OUTPUT: return "START_INPUT_OUTPUT";
+ case PCS_START_INPUT: return "START_INPUT";
+ case PCS_START_INPUT_TIMERS: return "START_INPUT_TIMERS";
+ case PCS_INPUT_OUTPUT: return "INPUT_OUTPUT";
+ case PCS_STOP_OUTPUT: return "STOP_OUTPUT";
+ case PCS_INPUT: return "INPUT";
+ case PCS_OUTPUT: return "OUTPUT";
+ case PCS_DONE_STOP_OUTPUT: return "DONE_STOP_OUTPUT";
+ case PCS_DONE: return "DONE";
+ }
+ return "UNKNOWN";
+}
+
+/**
+ * Send stop to component
+ */
+static void rayo_component_send_stop(struct rayo_actor *from, struct rayo_actor *to)
+{
+ struct rayo_message *reply;
+ iks *stop = iks_new("iq");
+ iks *x;
+ iks_insert_attrib(stop, "from", RAYO_JID(from));
+ iks_insert_attrib(stop, "to", RAYO_JID(to));
+ iks_insert_attrib(stop, "type", "set");
+ iks_insert_attrib_printf(stop, "id", "mod_rayo-%d", RAYO_SEQ_NEXT(from));
+ x = iks_insert(stop, "stop");
+ iks_insert_attrib(x, "xmlns", RAYO_EXT_NS);
+ reply = RAYO_SEND(from, to, rayo_message_create(stop));
+ if (reply) {
+ /* don't care */
+ rayo_message_destroy(reply);
+ }
+}
+
+/**
+ * Start input component
+ */
+static void start_input(struct prompt_component *prompt, int start_timers, int barge_event)
+{
+ struct rayo_message *reply;
+ iks *iq = iks_new("iq");
+ iks *input = iks_find(PROMPT_COMPONENT(prompt)->iq, "prompt");
+ input = iks_find(input, "input");
+ iks_insert_attrib(iq, "from", RAYO_JID(prompt));
+ iks_insert_attrib(iq, "to", RAYO_JID(RAYO_COMPONENT(prompt)->parent));
+ iks_insert_attrib_printf(iq, "id", "mod_rayo-%d", RAYO_SEQ_NEXT(prompt));
+ iks_insert_attrib(iq, "type", "set");
+ input = iks_copy_within(input, iks_stack(iq));
+ iks_insert_attrib(input, "start-timers", start_timers ? "true" : "false");
+ iks_insert_attrib(input, "barge-event", barge_event ? "true" : "false");
+ iks_insert_node(iq, input);
+ reply = RAYO_SEND(prompt, RAYO_COMPONENT(prompt)->parent, rayo_message_create(iq));
+ if (reply) {
+ /* handle response */
+ RAYO_SEND(RAYO_COMPONENT(prompt)->parent, prompt, reply);
+ }
+}
+
+/**
+ * Start input component timers
+ */
+static void start_input_timers(struct prompt_component *prompt)
+{
+ struct rayo_message *reply;
+ iks *x;
+ iks *iq = iks_new("iq");
+ iks_insert_attrib(iq, "from", RAYO_JID(prompt));
+ iks_insert_attrib(iq, "to", RAYO_JID(prompt->input));
+ iks_insert_attrib(iq, "type", "set");
+ iks_insert_attrib_printf(iq, "id", "mod_rayo-%d", RAYO_SEQ_NEXT(prompt));
+ x = iks_insert(iq, "start-timers");
+ iks_insert_attrib(x, "xmlns", RAYO_INPUT_NS);
+ reply = RAYO_SEND(prompt, prompt->input, rayo_message_create(iq));
+ if (reply) {
+ /* process reply */
+ RAYO_SEND(prompt->input, prompt, reply);
+ }
+}
+
+/**
+ * Handle start of output.
+ */
+static iks *prompt_component_handle_output_start(struct rayo_actor *output, struct rayo_actor *prompt, iks *iq, void *data)
+{
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) output start\n",
+ RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state));
+
+ switch (PROMPT_COMPONENT(prompt)->state) {
+ case PCS_START_OUTPUT:
+ PROMPT_COMPONENT(prompt)->output = output;
+ RAYO_RDLOCK(output);
+ PROMPT_COMPONENT(prompt)->state = PCS_OUTPUT;
+ /* send ref to client */
+ rayo_component_send_start(RAYO_COMPONENT(prompt), PROMPT_COMPONENT(prompt)->iq);
+ break;
+ case PCS_START_OUTPUT_BARGE:
+ PROMPT_COMPONENT(prompt)->output = output;
+ RAYO_RDLOCK(output);
+ PROMPT_COMPONENT(prompt)->state = PCS_START_INPUT_OUTPUT;
+ /* start input without timers and with barge events */
+ start_input(PROMPT_COMPONENT(prompt), 0, 1);
+ break;
+ case PCS_OUTPUT:
+ case PCS_START_INPUT_OUTPUT:
+ case PCS_START_INPUT:
+ case PCS_START_INPUT_TIMERS:
+ case PCS_INPUT_OUTPUT:
+ case PCS_STOP_OUTPUT:
+ case PCS_INPUT:
+ case PCS_DONE_STOP_OUTPUT:
+ case PCS_DONE:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, unexpected start output event\n", RAYO_JID(prompt));
+ break;
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle start of input.
+ */
+static iks *prompt_component_handle_input_start(struct rayo_actor *input, struct rayo_actor *prompt, iks *iq, void *data)
+{
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) input start\n",
+ RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state));
+
+ switch (PROMPT_COMPONENT(prompt)->state) {
+ case PCS_START_INPUT:
+ PROMPT_COMPONENT(prompt)->input = input;
+ RAYO_RDLOCK(input);
+ PROMPT_COMPONENT(prompt)->state = PCS_INPUT;
+ break;
+ case PCS_START_INPUT_OUTPUT:
+ PROMPT_COMPONENT(prompt)->input = input;
+ RAYO_RDLOCK(input);
+ PROMPT_COMPONENT(prompt)->state = PCS_INPUT_OUTPUT;
+ /* send ref to client */
+ rayo_component_send_start(RAYO_COMPONENT(prompt), PROMPT_COMPONENT(prompt)->iq);
+ iks_delete(PROMPT_COMPONENT(prompt)->iq);
+ break;
+ case PCS_START_INPUT_TIMERS:
+ PROMPT_COMPONENT(prompt)->input = input;
+ RAYO_RDLOCK(input);
+ PROMPT_COMPONENT(prompt)->state = PCS_INPUT;
+ /* send ref to client */
+ rayo_component_send_start(RAYO_COMPONENT(prompt), PROMPT_COMPONENT(prompt)->iq);
+ iks_delete(PROMPT_COMPONENT(prompt)->iq);
+ start_input_timers(PROMPT_COMPONENT(prompt));
+ break;
+ case PCS_DONE:
+ /* stopped by client */
+ PROMPT_COMPONENT(prompt)->input = input;
+ RAYO_RDLOCK(input);
+ rayo_component_send_stop(prompt, input);
+ break;
+ case PCS_START_OUTPUT:
+ case PCS_START_OUTPUT_BARGE:
+ case PCS_INPUT_OUTPUT:
+ case PCS_INPUT:
+ case PCS_STOP_OUTPUT:
+ case PCS_OUTPUT:
+ case PCS_DONE_STOP_OUTPUT:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, unexpected start input event\n", RAYO_JID(prompt));
+ break;
+ }
+ return NULL;
+}
+
+/**
+ * Handle start of input/output.
+ */
+static iks *prompt_component_handle_io_start(struct rayo_actor *component, struct rayo_actor *prompt, iks *iq, void *data)
+{
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, got <ref> from %s: %s\n",
+ RAYO_JID(prompt), RAYO_JID(component), iks_string(iks_stack(iq), iq));
+ if (!strcmp("input", component->subtype)) {
+ return prompt_component_handle_input_start(component, prompt, iq, data);
+ } else if (!strcmp("output", component->subtype)) {
+ return prompt_component_handle_output_start(component, prompt, iq, data);
+ }
+ return NULL;
+}
+
+/**
+ * Handle barge event
+ */
+static iks *prompt_component_handle_input_start_timers_error(struct rayo_actor *input, struct rayo_actor *prompt, iks *iq, void *data)
+{
+ /* this is only expected if input component is gone */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) start timers error\n",
+ RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state));
+
+ return NULL;
+}
+
+/**
+ * Handle input failure.
+ */
+static iks *prompt_component_handle_input_error(struct rayo_actor *input, struct rayo_actor *prompt, iks *iq, void *data)
+{
+ iks *error = iks_find(iq, "error");
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) input error\n",
+ RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state));
+
+ switch (PROMPT_COMPONENT(prompt)->state) {
+ case PCS_START_INPUT_TIMERS:
+ case PCS_START_INPUT:
+ /* send error to client */
+ PROMPT_COMPONENT(prompt)-> state = PCS_DONE;
+ iks_delete(PROMPT_COMPONENT(prompt)->iq);
+ rayo_component_send_complete(RAYO_COMPONENT(prompt), COMPONENT_COMPLETE_ERROR);
+ break;
+ break;
+ case PCS_START_INPUT_OUTPUT:
+ PROMPT_COMPONENT(prompt)->state = PCS_DONE_STOP_OUTPUT;
+
+ /* forward error to client */
+ iq = PROMPT_COMPONENT(prompt)->iq;
+ iks_insert_attrib(iq, "from", RAYO_JID(RAYO_COMPONENT(prompt)->parent));
+ iks_insert_attrib(iq, "to", RAYO_COMPONENT(prompt)->client_jid);
+ iks_insert_node(iq, iks_copy_within(error, iks_stack(iq)));
+ PROMPT_COMPONENT(prompt)->complete = iq;
+
+ rayo_component_send_stop(prompt, PROMPT_COMPONENT(prompt)->output);
+ break;
+ case PCS_START_OUTPUT:
+ case PCS_START_OUTPUT_BARGE:
+ case PCS_INPUT_OUTPUT:
+ case PCS_STOP_OUTPUT:
+ case PCS_INPUT:
+ case PCS_OUTPUT:
+ case PCS_DONE_STOP_OUTPUT:
+ case PCS_DONE:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, unexpected start input error event\n", RAYO_JID(prompt));
+ break;
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle output failure.
+ */
+static iks *prompt_component_handle_output_error(struct rayo_actor *output, struct rayo_actor *prompt, iks *iq, void *data)
+{
+ iks *error = iks_find(iq, "error");
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) output error\n",
+ RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state));
+
+ switch (PROMPT_COMPONENT(prompt)->state) {
+ case PCS_START_OUTPUT:
+ case PCS_START_OUTPUT_BARGE:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, <output> error: %s\n", RAYO_JID(prompt), iks_string(iks_stack(iq), iq));
+ PROMPT_COMPONENT(prompt)->state = PCS_DONE;
+
+ /* forward error to client */
+ iq = PROMPT_COMPONENT(prompt)->iq;
+ iks_insert_attrib(iq, "from", RAYO_JID(RAYO_COMPONENT(prompt)->parent));
+ iks_insert_attrib(iq, "to", RAYO_COMPONENT(prompt)->client_jid);
+ iks_insert_node(iq, iks_copy_within(error, iks_stack(iq)));
+ RAYO_SEND_BY_JID(prompt, RAYO_COMPONENT(prompt)->client_jid, rayo_message_create(iq));
+
+ /* done */
+ RAYO_UNLOCK(prompt);
+ RAYO_DESTROY(prompt);
+
+ break;
+ case PCS_START_INPUT_OUTPUT:
+ case PCS_START_INPUT_TIMERS:
+ case PCS_START_INPUT:
+ case PCS_INPUT_OUTPUT:
+ case PCS_STOP_OUTPUT:
+ case PCS_INPUT:
+ case PCS_OUTPUT:
+ case PCS_DONE_STOP_OUTPUT:
+ case PCS_DONE:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, unexpected start output error event\n", RAYO_JID(prompt));
+ break;
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle barge event
+ */
+static iks *prompt_component_handle_input_barge(struct rayo_actor *input, struct rayo_actor *prompt, iks *presence, void *data)
+{
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) input barge\n",
+ RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state));
+
+ switch (PROMPT_COMPONENT(prompt)->state) {
+ case PCS_INPUT_OUTPUT:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, got <start-of-input> from %s: %s\n",
+ RAYO_JID(prompt), RAYO_JID(input), iks_string(iks_stack(presence), presence));
+ PROMPT_COMPONENT(prompt)->state = PCS_STOP_OUTPUT;
+ rayo_component_send_stop(prompt, PROMPT_COMPONENT(prompt)->output);
+ break;
+ case PCS_STOP_OUTPUT:
+ case PCS_INPUT:
+ /* don't care */
+ break;
+ case PCS_OUTPUT:
+ case PCS_START_OUTPUT:
+ case PCS_START_OUTPUT_BARGE:
+ case PCS_START_INPUT:
+ case PCS_START_INPUT_OUTPUT:
+ case PCS_START_INPUT_TIMERS:
+ case PCS_DONE_STOP_OUTPUT:
+ case PCS_DONE:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, unexpected start output error event\n", RAYO_JID(prompt));
+ break;
+ }
+ return NULL;
+}
+
+/**
+ * Handle completion event
+ */
+static iks *prompt_component_handle_input_complete(struct rayo_actor *input, struct rayo_actor *prompt, iks *presence, void *data)
+{
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) input complete\n",
+ RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state));
+
+ switch (PROMPT_COMPONENT(prompt)->state) {
+ case PCS_INPUT_OUTPUT:
+ PROMPT_COMPONENT(prompt)->state = PCS_DONE_STOP_OUTPUT;
+ presence = iks_copy(presence);
+ iks_insert_attrib(presence, "from", RAYO_JID(prompt));
+ iks_insert_attrib(presence, "to", RAYO_COMPONENT(prompt)->client_jid);
+ PROMPT_COMPONENT(prompt)->complete = presence;
+ rayo_component_send_stop(prompt, PROMPT_COMPONENT(prompt)->output);
+ RAYO_UNLOCK(input);
+ break;
+ case PCS_STOP_OUTPUT:
+ PROMPT_COMPONENT(prompt)->state = PCS_DONE_STOP_OUTPUT;
+ presence = iks_copy(presence);
+ iks_insert_attrib(presence, "from", RAYO_JID(prompt));
+ iks_insert_attrib(presence, "to", RAYO_COMPONENT(prompt)->client_jid);
+ PROMPT_COMPONENT(prompt)->complete = presence;
+ RAYO_UNLOCK(input);
+ break;
+ case PCS_INPUT:
+ PROMPT_COMPONENT(prompt)->state = PCS_DONE;
+ /* pass through */
+ case PCS_DONE:
+ presence = iks_copy(presence);
+ iks_insert_attrib(presence, "from", RAYO_JID(prompt));
+ iks_insert_attrib(presence, "to", RAYO_COMPONENT(prompt)->client_jid);
+ rayo_component_send_complete_event(RAYO_COMPONENT(prompt), presence);
+ RAYO_UNLOCK(input);
+ break;
+ case PCS_OUTPUT:
+ case PCS_START_OUTPUT:
+ case PCS_START_OUTPUT_BARGE:
+ case PCS_START_INPUT:
+ case PCS_START_INPUT_OUTPUT:
+ case PCS_START_INPUT_TIMERS:
+ case PCS_DONE_STOP_OUTPUT:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, unexpected start output error event\n", RAYO_JID(prompt));
+ break;
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle completion event
+ */
+static iks *prompt_component_handle_output_complete(struct rayo_actor *output, struct rayo_actor *prompt, iks *presence, void *data)
+{
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) output complete\n",
+ RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state));
+
+ switch (PROMPT_COMPONENT(prompt)->state) {
+ case PCS_OUTPUT:
+ PROMPT_COMPONENT(prompt)->state = PCS_START_INPUT;
+ RAYO_UNLOCK(output);
+ /* start input with timers enabled and barge events disabled */
+ start_input(PROMPT_COMPONENT(prompt), 1, 0);
+ iks_delete(PROMPT_COMPONENT(prompt)->iq);
+ break;
+ case PCS_START_INPUT_OUTPUT:
+ PROMPT_COMPONENT(prompt)->state = PCS_INPUT;
+ RAYO_UNLOCK(output);
+ break;
+ case PCS_INPUT_OUTPUT:
+ PROMPT_COMPONENT(prompt)->state = PCS_INPUT;
+ RAYO_UNLOCK(output);
+ start_input_timers(PROMPT_COMPONENT(prompt));
+ break;
+ case PCS_STOP_OUTPUT:
+ PROMPT_COMPONENT(prompt)->state = PCS_INPUT;
+ RAYO_UNLOCK(output);
+ start_input_timers(PROMPT_COMPONENT(prompt));
+ break;
+ case PCS_DONE_STOP_OUTPUT:
+ if (PROMPT_COMPONENT(prompt)->complete) {
+ rayo_component_send_complete_event(RAYO_COMPONENT(prompt), PROMPT_COMPONENT(prompt)->complete);
+ }
+ RAYO_UNLOCK(output);
+ break;
+ case PCS_INPUT:
+ case PCS_START_OUTPUT:
+ case PCS_START_OUTPUT_BARGE:
+ case PCS_START_INPUT:
+ case PCS_START_INPUT_TIMERS:
+ case PCS_DONE:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, unexpected start output error event\n", RAYO_JID(prompt));
+ break;
+ }
+
+ return NULL;
+}
+
+/**
+ * Start execution of prompt component
+ */
+static iks *start_call_prompt_component(struct rayo_actor *client, struct rayo_actor *call, iks *iq, void *session_data)
+{
+ switch_core_session_t *session = (switch_core_session_t *)session_data;
+ switch_memory_pool_t *pool;
+ struct prompt_component *prompt_component = NULL;
+ iks *prompt = iks_find(iq, "prompt");
+ iks *input;
+ iks *output;
+ struct rayo_message *reply = NULL;
+ iks *cmd;
+
+ if (!VALIDATE_RAYO_PROMPT(prompt)) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Bad <prompt> attrib\n");
+ return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Bad <prompt> attrib value");
+ }
+
+ output = iks_find(prompt, "output");
+ if (!output) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Missing <output>\n");
+ return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Missing <output>");
+ }
+
+ input = iks_find(prompt, "input");
+ if (!input) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Missing <input>\n");
+ return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Missing <input>");
+ }
+
+ /* create prompt component, linked to call */
+ switch_core_new_memory_pool(&pool);
+ prompt_component = switch_core_alloc(pool, sizeof(*prompt_component));
+ rayo_component_init(RAYO_COMPONENT(prompt_component), pool, "prompt", NULL, call, iks_find_attrib(iq, "from"));
+ prompt_component->iq = iks_copy(iq);
+
+ /* start output */
+ if (iks_find_bool_attrib(prompt, "barge-in")) {
+ prompt_component->state = PCS_START_OUTPUT_BARGE;
+ } else {
+ prompt_component->state = PCS_START_OUTPUT;
+ }
+ cmd = iks_new("iq");
+ iks_insert_attrib(cmd, "from", RAYO_JID(prompt_component));
+ iks_insert_attrib(cmd, "to", RAYO_JID(call));
+ iks_insert_attrib(cmd, "id", iks_find_attrib(iq, "id"));
+ iks_insert_attrib(cmd, "type", "set");
+ output = iks_copy_within(output, iks_stack(cmd));
+ iks_insert_node(cmd, output);
+ reply = RAYO_SEND(prompt_component, call, rayo_message_create(cmd));
+ if (reply) {
+ /* handle response */
+ RAYO_SEND(call, prompt_component, reply);
+ }
+
+ return NULL;
+}
+
+/**
+ * Stop execution of prompt component
+ */
+static iks *stop_call_prompt_component(struct rayo_actor *client, struct rayo_actor *prompt, iks *iq, void *data)
+{
+ iks *reply = NULL;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) stop prompt\n",
+ RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state));
+
+ switch (PROMPT_COMPONENT(prompt)->state) {
+ case PCS_OUTPUT:
+ /* input hasn't started yet */
+ PROMPT_COMPONENT(prompt)->state = PCS_DONE_STOP_OUTPUT;
+ PROMPT_COMPONENT(prompt)->complete = rayo_component_create_complete_event(RAYO_COMPONENT(prompt), COMPONENT_COMPLETE_STOP);
+ rayo_component_send_stop(prompt, PROMPT_COMPONENT(prompt)->output);
+ break;
+ case PCS_INPUT_OUTPUT:
+ case PCS_INPUT:
+ case PCS_STOP_OUTPUT:
+ /* stopping input will trigger completion */
+ rayo_component_send_stop(prompt, PROMPT_COMPONENT(prompt)->input);
+ break;
+ case PCS_START_INPUT:
+ /* stop input as soon as it starts */
+ PROMPT_COMPONENT(prompt)->state = PCS_DONE;
+ break;
+ case PCS_DONE_STOP_OUTPUT:
+ case PCS_DONE:
+ /* already done */
+ break;
+ case PCS_START_OUTPUT:
+ case PCS_START_OUTPUT_BARGE:
+ case PCS_START_INPUT_OUTPUT:
+ case PCS_START_INPUT_TIMERS:
+ /* ref hasn't been sent yet */
+ reply = iks_new_error(iq, STANZA_ERROR_UNEXPECTED_REQUEST);
+ break;
+ }
+
+ if (!reply) {
+ reply = iks_new_iq_result(iq);
+ }
+ return reply;
+}
+
+/**
+ * Pass output component command
+ */
+static iks *forward_output_component_request(struct rayo_actor *client, struct rayo_actor *prompt, iks *iq, void *data)
+{
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) %s prompt\n",
+ RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state), iks_name(iks_first_tag(iq)));
+
+ switch (PROMPT_COMPONENT(prompt)->state) {
+ case PCS_OUTPUT:
+ case PCS_START_INPUT_OUTPUT:
+ case PCS_INPUT_OUTPUT: {
+ /* forward request to output component */
+ struct rayo_message *reply;
+ iks_insert_attrib(iq, "from", RAYO_JID(prompt));
+ iks_insert_attrib(iq, "to", RAYO_JID(PROMPT_COMPONENT(prompt)->output));
+ reply = RAYO_SEND(prompt, PROMPT_COMPONENT(prompt)->output, rayo_message_create_dup(iq));
+
+ /* return reply to client */
+ iq = rayo_message_remove_payload(reply);
+ rayo_message_destroy(reply);
+ iks_insert_attrib(iq, "from", RAYO_JID(prompt));
+ iks_insert_attrib(iq, "to", RAYO_JID(client));
+ return iq;
+ }
+ case PCS_START_INPUT_TIMERS:
+ case PCS_START_OUTPUT:
+ case PCS_START_OUTPUT_BARGE:
+ /* ref hasn't been sent yet */
+ return iks_new_error(iq, STANZA_ERROR_UNEXPECTED_REQUEST);
+ break;
+ case PCS_START_INPUT:
+ case PCS_STOP_OUTPUT:
+ case PCS_DONE_STOP_OUTPUT:
+ case PCS_INPUT:
+ case PCS_DONE:
+ return iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "output is finished");
+ }
+
+ return NULL;
+}
+
+/**
+ * Initialize prompt component
+ * @return SWITCH_STATUS_SUCCESS if successful
+ */
+switch_status_t rayo_prompt_component_load(void)
+{
+ /* Prompt is a convenience component that wraps <input> and <output> */
+ rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_PROMPT_NS":prompt", start_call_prompt_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "prompt", "set:"RAYO_EXT_NS":stop", stop_call_prompt_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "prompt", "result:"RAYO_NS":ref", prompt_component_handle_io_start);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "prompt", "error:"RAYO_OUTPUT_NS":output", prompt_component_handle_output_error);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "prompt", "error:"RAYO_INPUT_NS":input", prompt_component_handle_input_error);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "prompt", "error:"RAYO_INPUT_NS":start-timers", prompt_component_handle_input_start_timers_error);
+ rayo_actor_event_handler_add(RAT_CALL_COMPONENT, "input", RAT_CALL_COMPONENT, "prompt", ":"RAYO_INPUT_NS":start-of-input", prompt_component_handle_input_barge);
+ rayo_actor_event_handler_add(RAT_CALL_COMPONENT, "input", RAT_CALL_COMPONENT, "prompt", "unavailable:"RAYO_EXT_NS":complete", prompt_component_handle_input_complete);
+ rayo_actor_event_handler_add(RAT_CALL_COMPONENT, "output", RAT_CALL_COMPONENT, "prompt", "unavailable:"RAYO_EXT_NS":complete", prompt_component_handle_output_complete);
+
+ /* wrap output commands */
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "prompt", "set:"RAYO_OUTPUT_NS":pause", forward_output_component_request);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "prompt", "set:"RAYO_OUTPUT_NS":resume", forward_output_component_request);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "prompt", "set:"RAYO_OUTPUT_NS":speed-up", forward_output_component_request);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "prompt", "set:"RAYO_OUTPUT_NS":speed-down", forward_output_component_request);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "prompt", "set:"RAYO_OUTPUT_NS":volume-up", forward_output_component_request);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "prompt", "set:"RAYO_OUTPUT_NS":volume-down", forward_output_component_request);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "prompt", "set:"RAYO_OUTPUT_NS":seek", forward_output_component_request);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Shutdown prompt component
+ * @return SWITCH_STATUS_SUCCESS if successful
+ */
+switch_status_t rayo_prompt_component_shutdown(void)
+{
+ return SWITCH_STATUS_SUCCESS;
+}
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * record_component.c -- Rayo record component implementation
+ *
+ */
+#include "rayo_components.h"
+#include "rayo_elements.h"
+
+/* TODO timeouts / durations are affected by pause/resume */
+
+/**
+ * settings
+ */
+static struct {
+ const char *record_file_prefix;
+ const char *record_file_format;
+} globals;
+
+/**
+ * A record component
+ */
+struct record_component {
+ /** component base class */
+ struct rayo_component base;
+ /** maximum duration allowed */
+ int max_duration;
+ /** timeout for total silence */
+ int initial_timeout;
+ /** timeout for silence after initial utterance */
+ int final_timeout;
+ /** duplex/send/recv */
+ const char *direction;
+ /** true if mixed (mono) */
+ int mix;
+ /** true if start beep to be played */
+ int start_beep;
+ /** true if stop beep to be played */
+ int stop_beep;
+ /** time recording started */
+ switch_time_t start_time;
+ /** duration of this recording */
+ int duration_ms;
+ /** path on local filesystem */
+ char *local_file_path;
+ /** true if recording was stopped */
+ int stop;
+};
+
+#define RECORD_COMPONENT(x) ((struct record_component *)x)
+
+/* 1000 Hz beep for 250ms */
+#define RECORD_BEEP "tone_stream://%(250,0,1000)"
+
+#define RECORD_COMPLETE_MAX_DURATION "max-duration", RAYO_RECORD_COMPLETE_NS
+#define RECORD_COMPLETE_INITIAL_TIMEOUT "initial-timeout", RAYO_RECORD_COMPLETE_NS
+#define RECORD_COMPLETE_FINAL_TIMEOUT "final-timeout", RAYO_RECORD_COMPLETE_NS
+
+/**
+ * Notify completion of record component
+ */
+static void complete_record(struct rayo_component *component, const char *reason, const char *reason_namespace)
+{
+ switch_core_session_t *session = NULL;
+ const char *uuid = component->parent->id;
+ char *uri = switch_mprintf("file://%s", RECORD_COMPONENT(component)->local_file_path);
+ iks *recording;
+ switch_size_t file_size = 0;
+/* TODO this doesn't work with HTTP */
+#if 0
+ switch_file_t *file;
+
+ if (switch_file_open(&file, RECORD_COMPONENT(component)->local_file_path, SWITCH_FOPEN_READ, SWITCH_FPROT_UREAD, RAYO_POOL(component)) == SWITCH_STATUS_SUCCESS) {
+ file_size = switch_file_get_size(file);
+ switch_file_close(file);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_INFO, "Failed to open %s.\n", RECORD_COMPONENT(component)->local_file_path);
+ }
+#endif
+
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Recording %s done.\n", RECORD_COMPONENT(component)->local_file_path);
+
+ if (RECORD_COMPONENT(component)->stop_beep && (session = switch_core_session_locate(uuid))) {
+ switch_ivr_displace_session(session, RECORD_BEEP, 0, "");
+ switch_core_session_rwunlock(session);
+ }
+
+ /* send complete event to client */
+ recording = iks_new("recording");
+ iks_insert_attrib(recording, "xmlns", RAYO_RECORD_COMPLETE_NS);
+ iks_insert_attrib(recording, "uri", uri);
+ iks_insert_attrib_printf(recording, "duration", "%i", RECORD_COMPONENT(component)->duration_ms);
+ iks_insert_attrib_printf(recording, "size", "%"SWITCH_SIZE_T_FMT, file_size);
+ rayo_component_send_complete_with_metadata(component, reason, reason_namespace, recording, 1);
+ iks_delete(recording);
+
+ RAYO_UNLOCK(component);
+
+ switch_safe_free(uri);
+}
+
+/**
+ * Handle RECORD_STOP event from FreeSWITCH.
+ * @param event received from FreeSWITCH core. It will be destroyed by the core after this function returns.
+ */
+static void on_call_record_stop_event(switch_event_t *event)
+{
+ const char *file_path = switch_event_get_header(event, "Record-File-Path");
+ struct rayo_component *component = RAYO_COMPONENT_LOCATE(file_path);
+
+ if (component) {
+ RECORD_COMPONENT(component)->duration_ms += (switch_micro_time_now() - RECORD_COMPONENT(component)->start_time) / 1000;
+ if (RECORD_COMPONENT(component)->stop) {
+ complete_record(component, COMPONENT_COMPLETE_STOP);
+ } else {
+ /* TODO assume final timeout, for now */
+ complete_record(component, RECORD_COMPLETE_FINAL_TIMEOUT);
+ }
+ }
+}
+
+/**
+ * Create a record component
+ */
+static struct rayo_component *record_component_create(struct rayo_actor *actor, const char *client_jid, iks *record)
+{
+ switch_memory_pool_t *pool;
+ struct record_component *record_component = NULL;
+ char *local_file_path;
+ char *fs_file_path;
+ switch_bool_t start_paused;
+
+ /* validate record attributes */
+ if (!VALIDATE_RAYO_RECORD(record)) {
+ return NULL;
+ }
+
+ start_paused = iks_find_bool_attrib(record, "start-paused");
+
+ /* create record filename from session UUID and ref */
+ /* for example: prefix/1234-1234-1234-1234-30.wav */
+ local_file_path = switch_mprintf("%s%s-%i.%s",
+ globals.record_file_prefix,
+ actor->id, rayo_actor_seq_next(actor), iks_find_attrib(record, "format"));
+
+ fs_file_path = switch_mprintf("{pause=%s}fileman://%s",
+ start_paused ? "true" : "false",
+ local_file_path);
+
+ switch_core_new_memory_pool(&pool);
+ record_component = switch_core_alloc(pool, sizeof(*record_component));
+ rayo_component_init(RAYO_COMPONENT(record_component), pool, "record", fs_file_path, actor, client_jid);
+ record_component->max_duration = iks_find_int_attrib(record, "max-duration");
+ record_component->initial_timeout = iks_find_int_attrib(record, "initial-timeout");
+ record_component->final_timeout = iks_find_int_attrib(record, "final-timeout");
+ record_component->direction = switch_core_strdup(RAYO_POOL(record_component), iks_find_attrib_soft(record, "direction"));
+ record_component->mix = iks_find_bool_attrib(record, "mix");
+ record_component->start_beep = iks_find_bool_attrib(record, "start-beep");
+ record_component->stop_beep = iks_find_bool_attrib(record, "stop-beep");
+ record_component->start_time = start_paused ? 0 : switch_micro_time_now();
+ record_component->local_file_path = switch_core_strdup(RAYO_POOL(record_component), local_file_path);
+
+ switch_safe_free(local_file_path);
+ switch_safe_free(fs_file_path);
+
+ return RAYO_COMPONENT(record_component);
+}
+
+/**
+ * Start recording call
+ * @param session the session to record
+ * @param record the record component
+ */
+static int start_call_record(switch_core_session_t *session, struct rayo_component *component)
+{
+ struct record_component *record_component = RECORD_COMPONENT(component);
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ int max_duration_sec = 0;
+
+ switch_channel_set_variable(channel, "RECORD_HANGUP_ON_ERROR", "false");
+ switch_channel_set_variable(channel, "RECORD_TOGGLE_ON_REPEAT", "");
+ switch_channel_set_variable(channel, "RECORD_CHECK_BRIDGE", "");
+ switch_channel_set_variable(channel, "RECORD_MIN_SEC", "0");
+ switch_channel_set_variable(channel, "RECORD_STEREO", "");
+ switch_channel_set_variable(channel, "RECORD_READ_ONLY", "");
+ switch_channel_set_variable(channel, "RECORD_WRITE_ONLY", "");
+ switch_channel_set_variable(channel, "RECORD_APPEND", "");
+ switch_channel_set_variable(channel, "RECORD_WRITE_OVER", "true");
+ switch_channel_set_variable(channel, "RECORD_ANSWER_REQ", "");
+ switch_channel_set_variable(channel, "RECORD_SILENCE_THRESHOLD", "200");
+ if (record_component->initial_timeout > 0) {
+ switch_channel_set_variable_printf(channel, "RECORD_INITIAL_TIMEOUT_MS", "%i", record_component->initial_timeout);
+ } else {
+ switch_channel_set_variable(channel, "RECORD_INITIAL_TIMEOUT_MS", "");
+ }
+ if (record_component->final_timeout > 0) {
+ switch_channel_set_variable_printf(channel, "RECORD_FINAL_TIMEOUT_MS", "%i", record_component->final_timeout);
+ } else {
+ switch_channel_set_variable(channel, "RECORD_FINAL_TIMEOUT_MS", "");
+ }
+ /* allow dialplan override for these variables */
+ //switch_channel_set_variable(channel, "RECORD_PRE_BUFFER_FRAMES", "");
+ //switch_channel_set_variable(channel, "record_sample_rate", "");
+ //switch_channel_set_variable(channel, "enable_file_write_buffering", "");
+
+ /* max duration attribute is in milliseconds- convert to seconds */
+ if (record_component->max_duration > 0) {
+ max_duration_sec = ceil((double)(record_component->max_duration - record_component->duration_ms) / 1000.0);
+ }
+
+ if (!strcmp(record_component->direction, "duplex")) {
+ if (!record_component->mix) {
+ /* STEREO */
+ switch_channel_set_variable(channel, "RECORD_STEREO", "true");
+ } /* else MONO (default) */
+ } else if (!strcmp(record_component->direction, "send")) {
+ /* record audio sent from the caller */
+ switch_channel_set_variable(channel, "RECORD_READ_ONLY", "true");
+ } else if (!strcmp(record_component->direction, "recv")) {
+ /* record audio received by the caller */
+ switch_channel_set_variable(channel, "RECORD_WRITE_ONLY", "true");
+ };
+
+ if (record_component->start_beep) {
+ switch_ivr_displace_session(session, RECORD_BEEP, 0, "");
+ record_component->start_time = switch_micro_time_now();
+ }
+
+ if (switch_ivr_record_session(session, (char *)RAYO_ID(component), max_duration_sec, NULL) == SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Recording started: file = %s\n", RAYO_ID(component));
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Start execution of call record component
+ */
+static iks *start_call_record_component(struct rayo_actor *client, struct rayo_actor *call, iks *iq, void *session_data)
+{
+ switch_core_session_t *session = (switch_core_session_t *)session_data;
+ struct rayo_component *component = NULL;
+ iks *record = iks_find(iq, "record");
+
+ component = record_component_create(call, iks_find_attrib(iq, "from"), record);
+ if (!component) {
+ return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
+ }
+
+ if (start_call_record(session, component)) {
+ rayo_component_send_start(component, iq);
+ } else {
+ RAYO_UNLOCK(component);
+ RAYO_DESTROY(component);
+ return iks_new_error(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR);
+ }
+
+ return NULL;
+}
+
+/**
+ * Stop execution of record component
+ */
+static iks *stop_call_record_component(struct rayo_actor *client, struct rayo_actor *component, iks *iq, void *data)
+{
+ switch_core_session_t *session = switch_core_session_locate(RAYO_COMPONENT(component)->parent->id);
+ if (session) {
+ RECORD_COMPONENT(component)->stop = 1;
+ switch_ivr_stop_record_session(session, RAYO_ID(component));
+ switch_core_session_rwunlock(session);
+ }
+ return iks_new_iq_result(iq);
+}
+
+/**
+ * Pause execution of record component
+ */
+static iks *pause_record_component(struct rayo_actor *client, struct rayo_actor *component, iks *iq, void *data)
+{
+ struct record_component *record = RECORD_COMPONENT(component);
+ switch_stream_handle_t stream = { 0 };
+ char *command = switch_mprintf("%s pause", record->local_file_path);
+ SWITCH_STANDARD_STREAM(stream);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s pausing\n", RAYO_ID(component));
+ if (record->start_time) {
+ record->duration_ms += (switch_micro_time_now() - record->start_time) / 1000;
+ record->start_time = 0;
+ }
+ switch_api_execute("fileman", command, NULL, &stream);
+ switch_safe_free(stream.data);
+ switch_safe_free(command);
+
+ return iks_new_iq_result(iq);
+}
+
+/**
+ * Resume execution of record component
+ */
+static iks *resume_record_component(struct rayo_actor *client, struct rayo_actor *component, iks *iq, void *data)
+{
+ struct record_component *record = RECORD_COMPONENT(component);
+ switch_stream_handle_t stream = { 0 };
+ char *command = switch_mprintf("%s resume", record->local_file_path);
+ SWITCH_STANDARD_STREAM(stream);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s resuming\n", RAYO_ID(component));
+ if (!record->start_time) {
+ record->start_time = switch_micro_time_now();
+ }
+ switch_api_execute("fileman", command, NULL, &stream);
+ switch_safe_free(stream.data);
+ switch_safe_free(command);
+
+ return iks_new_iq_result(iq);
+}
+
+/**
+ * Handle conference events from FreeSWITCH.
+ * @param event received from FreeSWITCH core. It will be destroyed by the core after this function returns.
+ */
+static void on_mixer_record_event(switch_event_t *event)
+{
+ const char *file_path = switch_event_get_header(event, "Path");
+ const char *action = switch_event_get_header(event, "Action");
+ struct rayo_component *component = RAYO_COMPONENT_LOCATE(file_path);
+
+ if (component) {
+ struct record_component *record = RECORD_COMPONENT(component);
+ if (!strcmp("stop-recording", action)) {
+ record->duration_ms += (switch_micro_time_now() - record->start_time) / 1000;
+ if (record->stop) {
+ complete_record(component, COMPONENT_COMPLETE_STOP);
+ } else {
+ /* TODO assume final timeout, for now */
+ complete_record(component, RECORD_COMPLETE_FINAL_TIMEOUT);
+ }
+ }
+ }
+}
+
+/**
+ * Start recording mixer
+ * @param record the record component
+ */
+static int start_mixer_record(struct rayo_component *component)
+{
+ switch_stream_handle_t stream = { 0 };
+ char *args;
+ SWITCH_STANDARD_STREAM(stream);
+
+ args = switch_mprintf("%s recording start %s", component->parent->id, RAYO_ID(component));
+ switch_api_execute("conference", args, NULL, &stream);
+ switch_safe_free(args);
+ switch_safe_free(stream.data);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Recording started: file = %s\n", RAYO_ID(component));
+ return 1;
+}
+
+/**
+ * Start execution of mixer record component
+ */
+static iks *start_mixer_record_component(struct rayo_actor *client, struct rayo_actor *mixer, iks *iq, void *data)
+{
+ struct rayo_component *component = NULL;
+ iks *record = iks_find(iq, "record");
+
+ component = record_component_create(mixer, iks_find_attrib(iq, "from"), record);
+ if (!component) {
+ return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
+ }
+
+ /* mixer doesn't allow "send" */
+ if (!strcmp("send", iks_find_attrib_soft(record, "direction"))) {
+ RAYO_UNLOCK(component);
+ RAYO_DESTROY(component);
+ return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
+ }
+
+ if (start_mixer_record(component)) {
+ rayo_component_send_start(component, iq);
+ } else {
+ RAYO_UNLOCK(component);
+ RAYO_DESTROY(component);
+ return iks_new_error(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR);
+ }
+
+ return NULL;
+}
+
+/**
+ * Stop execution of record component
+ */
+static iks *stop_mixer_record_component(struct rayo_actor *client, struct rayo_actor *component, iks *iq, void *data)
+{
+ char *args;
+ switch_stream_handle_t stream = { 0 };
+ SWITCH_STANDARD_STREAM(stream);
+
+ RECORD_COMPONENT(component)->stop = 1;
+ args = switch_mprintf("%s recording stop %s", RAYO_COMPONENT(component)->parent->id, RAYO_ID(component));
+ switch_api_execute("conference", args, NULL, &stream);
+ switch_safe_free(args);
+ switch_safe_free(stream.data);
+
+ return iks_new_iq_result(iq);
+}
+
+/**
+ * Process module XML configuration
+ * @param pool memory pool to allocate from
+ * @param config_file to use
+ * @return SWITCH_STATUS_SUCCESS on successful configuration
+ */
+static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_file)
+{
+ switch_xml_t cfg, xml;
+
+ /* set defaults */
+ globals.record_file_prefix = switch_core_sprintf(pool, "%s%s", SWITCH_GLOBAL_dirs.recordings_dir, SWITCH_PATH_SEPARATOR);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Configuring module\n");
+ if (!(xml = switch_xml_open_cfg(config_file, &cfg, NULL))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", config_file);
+ return SWITCH_STATUS_TERM;
+ }
+
+ /* get params */
+ {
+ switch_xml_t settings = switch_xml_child(cfg, "record");
+ if (settings) {
+ switch_xml_t param;
+ for (param = switch_xml_child(settings, "param"); param; param = param->next) {
+ const char *var = switch_xml_attr_soft(param, "name");
+ const char *val = switch_xml_attr_soft(param, "value");
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "param: %s = %s\n", var, val);
+ if (!strcasecmp(var, "record-file-prefix")) {
+ if (!zstr(val)) {
+ globals.record_file_prefix = switch_core_strdup(pool, val);
+ }
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unsupported param: %s\n", var);
+ }
+ }
+ }
+ }
+
+ switch_xml_free(xml);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Initialize record component
+ * @param pool memory pool to allocate from
+ * @param config_file to use
+ * @return SWITCH_STATUS_SUCCESS if successful
+ */
+switch_status_t rayo_record_component_load(switch_memory_pool_t *pool, const char *config_file)
+{
+ if (do_config(pool, config_file) != SWITCH_STATUS_SUCCESS) {
+ return SWITCH_STATUS_TERM;
+ }
+
+ switch_event_bind("rayo_record_component", SWITCH_EVENT_RECORD_STOP, NULL, on_call_record_stop_event, NULL);
+ rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_RECORD_NS":record", start_call_record_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "record", "set:"RAYO_RECORD_NS":pause", pause_record_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "record", "set:"RAYO_RECORD_NS":resume", resume_record_component);
+ rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "record", "set:"RAYO_EXT_NS":stop", stop_call_record_component);
+
+ switch_event_bind("rayo_record_component", SWITCH_EVENT_CUSTOM, "conference::maintenance", on_mixer_record_event, NULL);
+ rayo_actor_command_handler_add(RAT_MIXER, "", "set:"RAYO_RECORD_NS":record", start_mixer_record_component);
+ rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "record", "set:"RAYO_RECORD_NS":pause", pause_record_component);
+ rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "record", "set:"RAYO_RECORD_NS":resume", resume_record_component);
+ rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "record", "set:"RAYO_EXT_NS":stop", stop_mixer_record_component);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Shutdown record component
+ * @return SWITCH_STATUS_SUCCESS if successful
+ */
+switch_status_t rayo_record_component_shutdown(void)
+{
+ switch_event_unbind_callback(on_call_record_stop_event);
+ switch_event_unbind_callback(on_mixer_record_event);
+ return SWITCH_STATUS_SUCCESS;
+}
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * sasl.c -- SASL functions
+ *
+ */
+#include <switch.h>
+#include <iksemel.h>
+#include "sasl.h"
+
+/**
+ * Parse authzid, authcid, and password tokens from base64 PLAIN auth message.
+ * @param message the base-64 encoded authentication message
+ * @param authzid the authorization id in the message - free this string when done with parsed message
+ * @param authcid the authentication id in the message
+ * @param password the password in the message
+ */
+void parse_plain_auth_message(const char *message, char **authzid, char **authcid, char **password)
+{
+ char *decoded = iks_base64_decode(message);
+ int maxlen = strlen(message) * 6 / 8 + 1;
+ int pos = 0;
+ *authzid = NULL;
+ *authcid = NULL;
+ *password = NULL;
+ if (decoded == NULL) {
+ return;
+ }
+ pos = strlen(decoded) + 1;
+ if (pos >= maxlen) {
+ return;
+ }
+ *authcid = strdup(decoded + pos);
+ pos += strlen(*authcid) + 1;
+ if (pos >= maxlen) {
+ return;
+ }
+ *password = strdup(decoded + pos);
+ if (zstr(decoded)) {
+ *authzid = strdup(*authcid);
+ } else {
+ *authzid = strdup(decoded);
+ }
+ free(decoded);
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * sasl.h -- SASL
+ *
+ */
+#ifndef SASL_H
+#define SASL_H
+
+extern void parse_plain_auth_message(const char *message, char **authzid, char **authcid, char **password);
+
+#endif
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * srgs.c -- Parses / converts / matches SRGS grammars
+ *
+ */
+#include <switch.h>
+#include <iksemel.h>
+#include <pcre.h>
+
+#include "srgs.h"
+
+#define MAX_RECURSION 100
+
+/** function to handle tag attributes */
+typedef int (* tag_attribs_fn)(struct srgs_grammar *, char **);
+/** function to handle tag CDATA */
+typedef int (* tag_cdata_fn)(struct srgs_grammar *, char *, size_t);
+
+/**
+ * Tag definition
+ */
+struct tag_def {
+ tag_attribs_fn attribs_fn;
+ tag_cdata_fn cdata_fn;
+ switch_bool_t is_root;
+ switch_hash_t *children_tags;
+};
+
+/**
+ * library configuration
+ */
+static struct {
+ /** true if initialized */
+ switch_bool_t init;
+ /** Mapping of tag name to definition */
+ switch_hash_t *tag_defs;
+ /** library memory pool */
+ switch_memory_pool_t *pool;
+} globals;
+
+/**
+ * SRGS node types
+ */
+enum srgs_node_type {
+ /** anything */
+ SNT_ANY,
+ /** <grammar> */
+ SNT_GRAMMAR,
+ /** <rule> */
+ SNT_RULE,
+ /** <one-of> */
+ SNT_ONE_OF,
+ /** <item> */
+ SNT_ITEM,
+ /** <ruleref> unresolved reference to node */
+ SNT_UNRESOLVED_REF,
+ /** <ruleref> resolved reference to node */
+ SNT_REF,
+ /** <item> string */
+ SNT_STRING,
+ /** <tag> */
+ SNT_TAG,
+ /** <lexicon> */
+ SNT_LEXICON,
+ /** <example> */
+ SNT_EXAMPLE,
+ /** <token> */
+ SNT_TOKEN,
+ /** <meta> */
+ SNT_META,
+ /** <metadata> */
+ SNT_METADATA
+};
+
+/**
+ * <rule> value
+ */
+struct rule_value {
+ char is_public;
+ char *id;
+ char *regex;
+};
+
+/**
+ * <item> value
+ */
+struct item_value {
+ int repeat_min;
+ int repeat_max;
+ const char *weight;
+};
+
+/**
+ * <ruleref> value
+ */
+union ref_value {
+ struct srgs_node *node;
+ char *uri;
+};
+
+/**
+ * A node in the SRGS parse tree
+ */
+struct srgs_node {
+ /** Name of node */
+ const char *name;
+ /** Type of node */
+ enum srgs_node_type type;
+ /** True if node has been inspected for loops */
+ char visited;
+ /** Node value */
+ union {
+ char *root;
+ const char *string;
+ union ref_value ref;
+ struct rule_value rule;
+ struct item_value item;
+ } value;
+ /** parent node */
+ struct srgs_node *parent;
+ /** child node */
+ struct srgs_node *child;
+ /** sibling node */
+ struct srgs_node *next;
+ /** number of child nodes */
+ int num_children;
+ /** tag handling data */
+ struct tag_def *tag_def;
+};
+
+/**
+ * A parsed grammar
+ */
+struct srgs_grammar {
+ /** grammar memory pool */
+ switch_memory_pool_t *pool;
+ /** current node being parsed */
+ struct srgs_node *cur;
+ /** rule names mapped to node */
+ switch_hash_t *rules;
+ /** grammar encoding */
+ char *encoding;
+ /** grammar language */
+ char *language;
+ /** true if digit grammar */
+ int digit_mode;
+ /** grammar parse tree root */
+ struct srgs_node *root;
+ /** root rule */
+ struct srgs_node *root_rule;
+ /** compiled grammar regex */
+ pcre *compiled_regex;
+ /** grammar in regex format */
+ char *regex;
+ /** grammar in JSGF format */
+ char *jsgf;
+ /** grammar as JSGF file */
+ char *jsgf_file_name;
+ /** synchronizes access to this grammar */
+ switch_mutex_t *mutex;
+ /** optional uuid for logging */
+ const char *uuid;
+};
+
+/**
+ * The SRGS SAX parser
+ */
+struct srgs_parser {
+ /** parser memory pool */
+ switch_memory_pool_t *pool;
+ /** grammar cache */
+ switch_hash_t *cache;
+ /** cache mutex */
+ switch_mutex_t *mutex;
+ /** optional uuid for logging */
+ const char *uuid;
+};
+
+/**
+ * Convert entity name to node type
+ * @param name of entity
+ * @return the type or ANY
+ */
+static enum srgs_node_type string_to_node_type(char *name)
+{
+ if (!strcmp("grammar", name)) {
+ return SNT_GRAMMAR;
+ }
+ if (!strcmp("item", name)) {
+ return SNT_ITEM;
+ }
+ if (!strcmp("one-of", name)) {
+ return SNT_ONE_OF;
+ }
+ if (!strcmp("ruleref", name)) {
+ return SNT_UNRESOLVED_REF;
+ }
+ if (!strcmp("rule", name)) {
+ return SNT_RULE;
+ }
+ if (!strcmp("tag", name)) {
+ return SNT_TAG;
+ }
+ if (!strcmp("lexicon", name)) {
+ return SNT_LEXICON;
+ }
+ if (!strcmp("example", name)) {
+ return SNT_EXAMPLE;
+ }
+ if (!strcmp("token", name)) {
+ return SNT_TOKEN;
+ }
+ if (!strcmp("meta", name)) {
+ return SNT_META;
+ }
+ if (!strcmp("metadata", name)) {
+ return SNT_METADATA;
+ }
+ return SNT_ANY;
+}
+
+/**
+ * Log node
+ */
+static void sn_log_node_open(struct srgs_node *node)
+{
+ switch (node->type) {
+ case SNT_ANY:
+ case SNT_METADATA:
+ case SNT_META:
+ case SNT_TOKEN:
+ case SNT_EXAMPLE:
+ case SNT_LEXICON:
+ case SNT_TAG:
+ case SNT_ONE_OF:
+ case SNT_GRAMMAR:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "<%s>\n", node->name);
+ return;
+ case SNT_RULE:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "<rule id='%s' scope='%s'>\n", node->value.rule.id, node->value.rule.is_public ? "public" : "private");
+ return;
+ case SNT_ITEM:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "<item repeat='%i'>\n", node->value.item.repeat_min);
+ return;
+ case SNT_UNRESOLVED_REF:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "<ruleref (unresolved) uri='%s'\n", node->value.ref.uri);
+ return;
+ case SNT_REF:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "<ruleref uri='#%s'>\n", node->value.ref.node->value.rule.id);
+ return;
+ case SNT_STRING:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "%s\n", node->value.string);
+ return;
+ }
+}
+
+/**
+ * Log node
+ */
+static void sn_log_node_close(struct srgs_node *node)
+{
+ switch (node->type) {
+ case SNT_GRAMMAR:
+ case SNT_RULE:
+ case SNT_ONE_OF:
+ case SNT_ITEM:
+ case SNT_REF:
+ case SNT_TAG:
+ case SNT_LEXICON:
+ case SNT_EXAMPLE:
+ case SNT_TOKEN:
+ case SNT_META:
+ case SNT_METADATA:
+ case SNT_ANY:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "</%s>\n", node->name);
+ return;
+ case SNT_UNRESOLVED_REF:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "</ruleref (unresolved)>\n");
+ return;
+ case SNT_STRING:
+ return;
+ }
+}
+
+/**
+ * Create a new node
+ * @param pool to use
+ * @param name of node
+ * @param type of node
+ * @return the node
+ */
+static struct srgs_node *sn_new(switch_memory_pool_t *pool, const char *name, enum srgs_node_type type)
+{
+ struct srgs_node *node = switch_core_alloc(pool, sizeof(*node));
+ node->name = switch_core_strdup(pool, name);
+ node->type = type;
+ return node;
+}
+
+/**
+ * @param node to search
+ * @return the last sibling of node
+ */
+static struct srgs_node *sn_find_last_sibling(struct srgs_node *node)
+{
+ if (node && node->next) {
+ return sn_find_last_sibling(node->next);
+ }
+ return node;
+}
+
+/**
+ * Add child node
+ * @param pool to use
+ * @param parent node to add child to
+ * @param name the child node name
+ * @param type the child node type
+ * @return the child node
+ */
+static struct srgs_node *sn_insert(switch_memory_pool_t *pool, struct srgs_node *parent, const char *name, enum srgs_node_type type)
+{
+ struct srgs_node *sibling = parent ? sn_find_last_sibling(parent->child) : NULL;
+ struct srgs_node *child = sn_new(pool, name, type);
+ if (parent) {
+ parent->num_children++;
+ child->parent = parent;
+ }
+ if (sibling) {
+ sibling->next = child;
+ } else if (parent) {
+ parent->child = child;
+ }
+ return child;
+}
+
+/**
+ * Add string child node
+ * @param pool to use
+ * @param parent node to add string to
+ * @param string to add - this function does not copy the string
+ * @return the string child node
+ */
+static struct srgs_node *sn_insert_string(switch_memory_pool_t *pool, struct srgs_node *parent, char *string)
+{
+ struct srgs_node *child = sn_insert(pool, parent, string, SNT_STRING);
+ child->value.string = string;
+ return child;
+}
+
+/**
+ * Add a definition for a tag
+ * @param tag the name
+ * @param attribs_fn the function to handle the tag attributes
+ * @param cdata_fn the function to handler the tag CDATA
+ * @param children_tags comma-separated list of valid child tag names
+ * @return the definition
+ */
+static struct tag_def *add_tag_def(const char *tag, tag_attribs_fn attribs_fn, tag_cdata_fn cdata_fn, const char *children_tags)
+{
+ struct tag_def *def = switch_core_alloc(globals.pool, sizeof(*def));
+ switch_core_hash_init(&def->children_tags, globals.pool);
+ if (!zstr(children_tags)) {
+ char *children_tags_dup = switch_core_strdup(globals.pool, children_tags);
+ char *tags[32] = { 0 };
+ int tag_count = switch_separate_string(children_tags_dup, ',', tags, sizeof(tags) / sizeof(tags[0]));
+ if (tag_count) {
+ int i;
+ for (i = 0; i < tag_count; i++) {
+ switch_core_hash_insert(def->children_tags, tags[i], tags[i]);
+ }
+ }
+ }
+ def->attribs_fn = attribs_fn;
+ def->cdata_fn = cdata_fn;
+ def->is_root = SWITCH_FALSE;
+ switch_core_hash_insert(globals.tag_defs, tag, def);
+ return def;
+}
+
+/**
+ * Add a definition for a root tag
+ * @param tag the name
+ * @param attribs_fn the function to handle the tag attributes
+ * @param cdata_fn the function to handler the tag CDATA
+ * @param children_tags comma-separated list of valid child tag names
+ * @return the definition
+ */
+static struct tag_def *add_root_tag_def(const char *tag, tag_attribs_fn attribs_fn, tag_cdata_fn cdata_fn, const char *children_tags)
+{
+ struct tag_def *def = add_tag_def(tag, attribs_fn, cdata_fn, children_tags);
+ def->is_root = SWITCH_TRUE;
+ return def;
+}
+
+/**
+ * Handle tag attributes
+ * @param parser the parser
+ * @param name the tag name
+ * @param atts the attributes
+ * @return IKS_OK if OK IKS_BADXML on parse failure
+ */
+static int process_tag(struct srgs_grammar *grammar, const char *name, char **atts)
+{
+ struct srgs_node *cur = grammar->cur;
+ if (cur->tag_def->is_root && cur->parent == NULL) {
+ /* no parent for ROOT tags */
+ return cur->tag_def->attribs_fn(grammar, atts);
+ } else if (!cur->tag_def->is_root && cur->parent) {
+ /* check if this child is allowed by parent node */
+ struct tag_def *parent_def = cur->parent->tag_def;
+ if (switch_core_hash_find(parent_def->children_tags, "ANY") ||
+ switch_core_hash_find(parent_def->children_tags, name)) {
+ return cur->tag_def->attribs_fn(grammar, atts);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "<%s> cannot be a child of <%s>\n", name, cur->parent->name);
+ }
+ } else if (cur->tag_def->is_root && cur->parent != NULL) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "<%s> must be the root element\n", name);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "<%s> cannot be a root element\n", name);
+ }
+ return IKS_BADXML;
+}
+
+/**
+ * Handle tag attributes that are ignored
+ * @param grammar the grammar
+ * @param atts the attributes
+ * @return IKS_OK
+ */
+static int process_attribs_ignore(struct srgs_grammar *grammar, char **atts)
+{
+ return IKS_OK;
+}
+
+/**
+ * Handle CDATA that is ignored
+ * @param grammar the grammar
+ * @param data the CDATA
+ * @param len the CDATA length
+ * @return IKS_OK
+ */
+static int process_cdata_ignore(struct srgs_grammar *grammar, char *data, size_t len)
+{
+ return IKS_OK;
+}
+
+/**
+ * Handle CDATA that is not allowed
+ * @param grammar the grammar
+ * @param data the CDATA
+ * @param len the CDATA length
+ * @return IKS_BADXML if any printable characters
+ */
+static int process_cdata_bad(struct srgs_grammar *grammar, char *data, size_t len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ if (isgraph(data[i])) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "Unexpected CDATA for <%s>\n", grammar->cur->name);
+ return IKS_BADXML;
+ }
+ }
+ return IKS_OK;
+}
+
+/**
+ * Process <rule> attributes
+ * @param grammar the grammar state
+ * @param atts the attributes
+ * @return IKS_OK if ok
+ */
+static int process_rule(struct srgs_grammar *grammar, char **atts)
+{
+ struct srgs_node *rule = grammar->cur;
+ rule->value.rule.is_public = 0;
+ rule->value.rule.id = NULL;
+ if (atts) {
+ int i = 0;
+ while (atts[i]) {
+ if (!strcmp("scope", atts[i])) {
+ rule->value.rule.is_public = !zstr(atts[i + 1]) && !strcmp("public", atts[i + 1]);
+ } else if (!strcmp("id", atts[i])) {
+ if (!zstr(atts[i + 1])) {
+ rule->value.rule.id = switch_core_strdup(grammar->pool, atts[i + 1]);
+ }
+ }
+ i += 2;
+ }
+ }
+
+ if (zstr(rule->value.rule.id)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "Missing rule ID: %s\n", rule->value.rule.id);
+ return IKS_BADXML;
+ }
+
+ if (switch_core_hash_find(grammar->rules, rule->value.rule.id)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "Duplicate rule ID: %s\n", rule->value.rule.id);
+ return IKS_BADXML;
+ }
+ switch_core_hash_insert(grammar->rules, rule->value.rule.id, rule);
+
+ return IKS_OK;
+}
+
+/**
+ * Process <ruleref> attributes
+ * @param grammar the grammar state
+ * @param atts the attributes
+ * @return IKS_OK if ok
+ */
+static int process_ruleref(struct srgs_grammar *grammar, char **atts)
+{
+ struct srgs_node *ruleref = grammar->cur;
+ if (atts) {
+ int i = 0;
+ while (atts[i]) {
+ if (!strcmp("uri", atts[i])) {
+ char *uri = atts[i + 1];
+ if (zstr(uri)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "Empty <ruleref> uri\n");
+ return IKS_BADXML;
+ }
+ /* only allow local reference */
+ if (uri[0] != '#' || strlen(uri) < 2) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "Only local rule refs allowed\n");
+ return IKS_BADXML;
+ }
+ ruleref->value.ref.uri = switch_core_strdup(grammar->pool, uri);
+ return IKS_OK;
+ }
+ i += 2;
+ }
+ }
+ return IKS_OK;
+}
+
+/**
+ * Process <item> attributes
+ * @param grammar the grammar state
+ * @param atts the attributes
+ * @return IKS_OK if ok
+ */
+static int process_item(struct srgs_grammar *grammar, char **atts)
+{
+ struct srgs_node *item = grammar->cur;
+ item->value.item.repeat_min = 1;
+ item->value.item.repeat_max = 1;
+ item->value.item.weight = NULL;
+ if (atts) {
+ int i = 0;
+ while (atts[i]) {
+ if (!strcmp("repeat", atts[i])) {
+ /* repeats of 0 are not supported by this code */
+ char *repeat = atts[i + 1];
+ if (zstr(repeat)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "Empty <item> repeat atribute\n");
+ return IKS_BADXML;
+ }
+ if (switch_is_number(repeat)) {
+ /* single number */
+ int repeat_val = atoi(repeat);
+ if (repeat_val < 1) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "<item> repeat must be >= 0\n");
+ return IKS_BADXML;
+ }
+ item->value.item.repeat_min = repeat_val;
+ item->value.item.repeat_max = repeat_val;
+ } else {
+ /* range */
+ char *min = switch_core_strdup(grammar->pool, repeat);
+ char *max = strchr(min, '-');
+ if (max) {
+ *max = '\0';
+ max++;
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "<item> repeat must be a number or range\n");
+ return IKS_BADXML;
+ }
+ if (switch_is_number(min) && (switch_is_number(max) || zstr(max))) {
+ int min_val = atoi(min);
+ int max_val = zstr(max) ? INT_MAX : atoi(max);
+ /* max must be >= min and > 0
+ min must be >= 0 */
+ if ((max_val <= 0) || (max_val < min_val) || (min_val < 0)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "<item> repeat range invalid\n");
+ return IKS_BADXML;
+ }
+ item->value.item.repeat_min = min_val;
+ item->value.item.repeat_max = max_val;
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "<item> repeat range is not a number\n");
+ return IKS_BADXML;
+ }
+ }
+ } else if (!strcmp("weight", atts[i])) {
+ const char *weight = atts[i + 1];
+ if (zstr(weight) || !switch_is_number(weight) || atof(weight) < 0) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "<item> weight is not a number >= 0\n");
+ return IKS_BADXML;
+ }
+ item->value.item.weight = switch_core_strdup(grammar->pool, weight);
+ }
+ i += 2;
+ }
+ }
+ return IKS_OK;
+}
+
+/**
+ * Process <grammar> attributes
+ * @param grammar the grammar state
+ * @param atts the attributes
+ * @return IKS_OK if ok
+ */
+static int process_grammar(struct srgs_grammar *grammar, char **atts)
+{
+ if (grammar->root) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "Only one <grammar> tag allowed\n");
+ return IKS_BADXML;
+ }
+ grammar->root = grammar->cur;
+ if (atts) {
+ int i = 0;
+ while (atts[i]) {
+ if (!strcmp("mode", atts[i])) {
+ char *mode = atts[i + 1];
+ if (zstr(mode)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "<grammar> mode is missing\n");
+ return IKS_BADXML;
+ }
+ grammar->digit_mode = !strcasecmp(mode, "dtmf");
+ } else if(!strcmp("encoding", atts[i])) {
+ char *encoding = atts[i + 1];
+ if (zstr(encoding)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "<grammar> encoding is empty\n");
+ return IKS_BADXML;
+ }
+ grammar->encoding = switch_core_strdup(grammar->pool, encoding);
+ } else if (!strcmp("language", atts[i])) {
+ char *language = atts[i + 1];
+ if (zstr(language)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "<grammar> language is empty\n");
+ return IKS_BADXML;
+ }
+ grammar->language = switch_core_strdup(grammar->pool, language);
+ } else if (!strcmp("root", atts[i])) {
+ char *root = atts[i + 1];
+ if (zstr(root)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "<grammar> root is empty\n");
+ return IKS_BADXML;
+ }
+ grammar->cur->value.root = switch_core_strdup(grammar->pool, root);
+ }
+ i += 2;
+ }
+ }
+ return IKS_OK;
+}
+
+/**
+ * Process a tag
+ */
+static int tag_hook(void *user_data, char *name, char **atts, int type)
+{
+ int result = IKS_OK;
+ struct srgs_grammar *grammar = (struct srgs_grammar *)user_data;
+
+ if (type == IKS_OPEN || type == IKS_SINGLE) {
+ enum srgs_node_type ntype = string_to_node_type(name);
+ grammar->cur = sn_insert(grammar->pool, grammar->cur, name, ntype);
+ grammar->cur->tag_def = switch_core_hash_find(globals.tag_defs, name);
+ if (!grammar->cur->tag_def) {
+ grammar->cur->tag_def = switch_core_hash_find(globals.tag_defs, "ANY");
+ }
+ result = process_tag(grammar, name, atts);
+ sn_log_node_open(grammar->cur);
+ }
+
+ if (type == IKS_CLOSE || type == IKS_SINGLE) {
+ sn_log_node_close(grammar->cur);
+ grammar->cur = grammar->cur->parent;
+ }
+
+ return result;
+}
+
+/**
+ * Process CDATA grammar tokens
+ * @param grammar the grammar
+ * @param data the CDATA
+ * @param len the CDATA length
+ * @return IKS_OK
+ */
+static int process_cdata_tokens(struct srgs_grammar *grammar, char *data, size_t len)
+{
+ struct srgs_node *string = grammar->cur;
+ int i;
+ if (grammar->digit_mode) {
+ for (i = 0; i < len; i++) {
+ if (isdigit(data[i]) || data[i] == '#' || data[i] == '*') {
+ char *digit = switch_core_alloc(grammar->pool, sizeof(char) * 2);
+ digit[0] = data[i];
+ digit[1] = '\0';
+ string = sn_insert_string(grammar->pool, string, digit);
+ sn_log_node_open(string);
+ }
+ }
+ } else {
+ char *data_dup = switch_core_alloc(grammar->pool, sizeof(char) * (len + 1));
+ char *start = data_dup;
+ char *end = start + len - 1;
+ memcpy(data_dup, data, len);
+ /* remove start whitespace */
+ for (; start && *start && !isgraph(*start); start++) {
+ }
+ if (!zstr(start)) {
+ /* remove end whitespace */
+ for (; end != start && *end && !isgraph(*end); end--) {
+ *end = '\0';
+ }
+ if (!zstr(start)) {
+ string = sn_insert_string(grammar->pool, string, start);
+ }
+ }
+ }
+ return IKS_OK;
+}
+
+/**
+ * Process cdata
+ * @param user_data the grammar
+ * @param data the CDATA
+ * @param len the CDATA length
+ * @return IKS_OK
+ */
+static int cdata_hook(void *user_data, char *data, size_t len)
+{
+ struct srgs_grammar *grammar = (struct srgs_grammar *)user_data;
+ if (!grammar) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing grammar\n");
+ return IKS_BADXML;
+ }
+ if (grammar->cur) {
+ if (grammar->cur->tag_def) {
+ return grammar->cur->tag_def->cdata_fn(grammar, data, len);
+ }
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "Missing definition for <%s>\n", grammar->cur->name);
+ return IKS_BADXML;
+ }
+ return IKS_OK;
+}
+
+/**
+ * Create a new parsed grammar
+ * @param parser
+ * @return the grammar
+ */
+struct srgs_grammar *srgs_grammar_new(struct srgs_parser *parser)
+{
+ switch_memory_pool_t *pool = NULL;
+ struct srgs_grammar *grammar = NULL;
+ switch_core_new_memory_pool(&pool);
+ grammar = switch_core_alloc(pool, sizeof (*grammar));
+ grammar->pool = pool;
+ grammar->root = NULL;
+ grammar->cur = NULL;
+ grammar->uuid = (parser && !zstr(parser->uuid)) ? switch_core_strdup(pool, parser->uuid) : "";
+ switch_core_hash_init(&grammar->rules, pool);
+ switch_mutex_init(&grammar->mutex, SWITCH_MUTEX_NESTED, pool);
+ return grammar;
+}
+
+/**
+ * Destroy a parsed grammar
+ * @param grammar the grammar
+ */
+static void srgs_grammar_destroy(struct srgs_grammar *grammar)
+{
+ switch_memory_pool_t *pool = grammar->pool;
+ if (grammar->compiled_regex) {
+ pcre_free(grammar->compiled_regex);
+ }
+ if (grammar->jsgf_file_name) {
+ switch_file_remove(grammar->jsgf_file_name, pool);
+ }
+ switch_core_destroy_memory_pool(&pool);
+}
+
+/**
+ * Create a new parser.
+ * @param uuid optional uuid for logging
+ * @return the created parser
+ */
+struct srgs_parser *srgs_parser_new(const char *uuid)
+{
+ switch_memory_pool_t *pool = NULL;
+ struct srgs_parser *parser = NULL;
+ switch_core_new_memory_pool(&pool);
+ if (pool) {
+ parser = switch_core_alloc(pool, sizeof(*parser));
+ parser->pool = pool;
+ parser->uuid = zstr(uuid) ? "" : switch_core_strdup(pool, uuid);
+ switch_core_hash_init(&parser->cache, pool);
+ switch_mutex_init(&parser->mutex, SWITCH_MUTEX_NESTED, pool);
+ }
+ return parser;
+}
+
+/**
+ * Destroy the parser.
+ * @param parser to destroy
+ */
+void srgs_parser_destroy(struct srgs_parser *parser)
+{
+ switch_memory_pool_t *pool = parser->pool;
+ switch_hash_index_t *hi = NULL;
+
+ /* clean up all cached grammars */
+ for (hi = switch_core_hash_first(parser->cache); hi; hi = switch_core_hash_next(hi)) {
+ struct srgs_grammar *grammar = NULL;
+ const void *key;
+ void *val;
+ switch_core_hash_this(hi, &key, NULL, &val);
+ grammar = (struct srgs_grammar *)val;
+ switch_assert(grammar);
+ srgs_grammar_destroy(grammar);
+ }
+ switch_core_destroy_memory_pool(&pool);
+}
+
+/**
+ * Create regexes
+ * @param grammar the grammar
+ * @param node root node
+ * @param stream set to NULL
+ * @return 1 if successful
+ */
+static int create_regexes(struct srgs_grammar *grammar, struct srgs_node *node, switch_stream_handle_t *stream)
+{
+ sn_log_node_open(node);
+ switch (node->type) {
+ case SNT_GRAMMAR:
+ if (node->child) {
+ int num_rules = 0;
+ struct srgs_node *child = node->child;
+ if (grammar->root_rule) {
+ if (!create_regexes(grammar, grammar->root_rule, NULL)) {
+ return 0;
+ }
+ grammar->regex = switch_core_sprintf(grammar->pool, "^%s$", grammar->root_rule->value.rule.regex);
+ } else {
+ switch_stream_handle_t new_stream = { 0 };
+ SWITCH_STANDARD_STREAM(new_stream);
+ if (node->num_children > 1) {
+ new_stream.write_function(&new_stream, "%s", "^(?:");
+ } else {
+ new_stream.write_function(&new_stream, "%s", "^");
+ }
+ for (; child; child = child->next) {
+ if (!create_regexes(grammar, child, &new_stream)) {
+ switch_safe_free(new_stream.data);
+ return 0;
+ }
+ if (child->type == SNT_RULE && child->value.rule.is_public) {
+ if (num_rules > 0) {
+ new_stream.write_function(&new_stream, "%s", "|");
+ }
+ new_stream.write_function(&new_stream, "%s", child->value.rule.regex);
+ num_rules++;
+ }
+ }
+ if (node->num_children > 1) {
+ new_stream.write_function(&new_stream, "%s", ")$");
+ } else {
+ new_stream.write_function(&new_stream, "%s", "$");
+ }
+ grammar->regex = switch_core_strdup(grammar->pool, new_stream.data);
+ switch_safe_free(new_stream.data);
+ }
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_DEBUG, "document regex = %s\n", grammar->regex);
+ }
+ break;
+ case SNT_RULE:
+ if (node->value.rule.regex) {
+ return 1;
+ } else if (node->child) {
+ struct srgs_node *item = node->child;
+ switch_stream_handle_t new_stream = { 0 };
+ SWITCH_STANDARD_STREAM(new_stream);
+ for (; item; item = item->next) {
+ if (!create_regexes(grammar, item, &new_stream)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_DEBUG, "%s regex failed = %s\n", node->value.rule.id, node->value.rule.regex);
+ switch_safe_free(new_stream.data);
+ return 0;
+ }
+ }
+ node->value.rule.regex = switch_core_strdup(grammar->pool, new_stream.data);
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_DEBUG, "%s regex = %s\n", node->value.rule.id, node->value.rule.regex);
+ switch_safe_free(new_stream.data);
+ }
+ break;
+ case SNT_STRING: {
+ int i;
+ for (i = 0; i < strlen(node->value.string); i++) {
+ switch (node->value.string[i]) {
+ case '[':
+ case '\\':
+ case '^':
+ case '$':
+ case '.':
+ case '|':
+ case '?':
+ case '*':
+ case '+':
+ case '(':
+ case ')':
+ /* escape special PCRE regex characters */
+ stream->write_function(stream, "\\%c", node->value.string[i]);
+ break;
+ default:
+ stream->write_function(stream, "%c", node->value.string[i]);
+ break;
+ }
+ }
+ if (node->child) {
+ if (!create_regexes(grammar, node->child, stream)) {
+ return 0;
+ }
+ }
+ break;
+ }
+ case SNT_ITEM:
+ if (node->child) {
+ struct srgs_node *item = node->child;
+ if (node->value.item.repeat_min != 1 || node->value.item.repeat_max != 1) {
+ stream->write_function(stream, "%s", "(?:");
+ }
+ for(; item; item = item->next) {
+ if (!create_regexes(grammar, item, stream)) {
+ return 0;
+ }
+ }
+ if (node->value.item.repeat_min != 1 || node->value.item.repeat_max != 1) {
+ if (node->value.item.repeat_min != node->value.item.repeat_max) {
+ if (node->value.item.repeat_min == 0 && node->value.item.repeat_max == INT_MAX) {
+ stream->write_function(stream, ")*");
+ } else if (node->value.item.repeat_min == 0 && node->value.item.repeat_max == 1) {
+ stream->write_function(stream, ")?");
+ } else if (node->value.item.repeat_min == 1 && node->value.item.repeat_max == INT_MAX) {
+ stream->write_function(stream, ")+");
+ } else if (node->value.item.repeat_max == INT_MAX) {
+ stream->write_function(stream, "){%i,1000}", node->value.item.repeat_min);
+ } else {
+ stream->write_function(stream, "){%i,%i}", node->value.item.repeat_min, node->value.item.repeat_max);
+ }
+ } else {
+ stream->write_function(stream, "){%i}", node->value.item.repeat_min);
+ }
+ }
+ }
+ break;
+ case SNT_ONE_OF:
+ if (node->child) {
+ struct srgs_node *item = node->child;
+ if (node->num_children > 1) {
+ stream->write_function(stream, "%s", "(?:");
+ }
+ for (; item; item = item->next) {
+ if (item != node->child) {
+ stream->write_function(stream, "%s", "|");
+ }
+ if (!create_regexes(grammar, item, stream)) {
+ return 0;
+ }
+ }
+ if (node->num_children > 1) {
+ stream->write_function(stream, "%s", ")");
+ }
+ }
+ break;
+ case SNT_REF: {
+ struct srgs_node *rule = node->value.ref.node;
+ if (!rule->value.rule.regex) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_DEBUG, "ruleref: create %s regex\n", rule->value.rule.id);
+ if (!create_regexes(grammar, rule, NULL)) {
+ return 0;
+ }
+ }
+ if (!rule->value.rule.regex) {
+ return 0;
+ }
+ stream->write_function(stream, "%s", rule->value.rule.regex);
+ break;
+ }
+ case SNT_ANY:
+ default:
+ /* ignore */
+ return 1;
+ }
+ sn_log_node_close(node);
+ return 1;
+}
+
+/**
+ * Compile regex
+ */
+static pcre *get_compiled_regex(struct srgs_grammar *grammar)
+{
+ int erroffset = 0;
+ const char *errptr = "";
+ int options = 0;
+ const char *regex;
+
+ if (!grammar) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_CRIT, "grammar is NULL!\n");
+ return NULL;
+ }
+
+ switch_mutex_lock(grammar->mutex);
+ if (!grammar->compiled_regex && (regex = srgs_grammar_to_regex(grammar))) {
+ if (!(grammar->compiled_regex = pcre_compile(regex, options, &errptr, &erroffset, NULL))) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_WARNING, "Failed to compile grammar regex: %s\n", regex);
+ }
+ }
+ switch_mutex_unlock(grammar->mutex);
+ return grammar->compiled_regex;
+}
+
+/**
+ * Resolve all unresolved references and detect loops.
+ * @param grammar the grammar
+ * @param node the current node
+ * @param level the recursion level
+ */
+static int resolve_refs(struct srgs_grammar *grammar, struct srgs_node *node, int level)
+{
+ sn_log_node_open(node);
+ if (node->visited) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "Loop detected.\n");
+ return 0;
+ }
+ node->visited = 1;
+
+ if (level > MAX_RECURSION) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "Recursion too deep.\n");
+ return 0;
+ }
+
+ if (node->type == SNT_GRAMMAR && node->value.root) {
+ struct srgs_node *rule = (struct srgs_node *)switch_core_hash_find(grammar->rules, node->value.root);
+ if (!rule) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "Root rule not found: %s\n", node->value.root);
+ return 0;
+ }
+ grammar->root_rule = rule;
+ }
+
+ if (node->type == SNT_UNRESOLVED_REF) {
+ /* resolve reference to local rule- drop first character # from URI */
+ struct srgs_node *rule = (struct srgs_node *)switch_core_hash_find(grammar->rules, node->value.ref.uri + 1);
+ if (!rule) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_INFO, "Local rule not found: %s\n", node->value.ref.uri);
+ return 0;
+ }
+
+ /* link to rule */
+ node->type = SNT_REF;
+ node->value.ref.node = rule;
+ }
+
+ /* travel through rule to detect loops */
+ if (node->type == SNT_REF) {
+ if (!resolve_refs(grammar, node->value.ref.node, level + 1)) {
+ return 0;
+ }
+ }
+
+ /* resolve children refs */
+ if (node->child) {
+ struct srgs_node *child = node->child;
+ for (; child; child = child->next) {
+ if (!resolve_refs(grammar, child, level + 1)) {
+ return 0;
+ }
+ }
+ }
+
+ node->visited = 0;
+ sn_log_node_close(node);
+ return 1;
+}
+
+/**
+ * Parse the document into rules to match
+ * @param parser the parser
+ * @param document the document to parse
+ * @return the parsed grammar if successful
+ */
+struct srgs_grammar *srgs_parse(struct srgs_parser *parser, const char *document)
+{
+ struct srgs_grammar *grammar = NULL;
+ if (!parser) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "NULL parser!!\n");
+ return NULL;
+ }
+
+ if (zstr(document)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser->uuid), SWITCH_LOG_INFO, "Missing grammar document\n");
+ return NULL;
+ }
+
+ /* check for cached grammar */
+ switch_mutex_lock(parser->mutex);
+ grammar = (struct srgs_grammar *)switch_core_hash_find(parser->cache, document);
+ if (!grammar) {
+ int result = 0;
+ iksparser *p;
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser->uuid), SWITCH_LOG_DEBUG, "Parsing new grammar\n");
+ grammar = srgs_grammar_new(parser);
+ p = iks_sax_new(grammar, tag_hook, cdata_hook);
+ if (iks_parse(p, document, 0, 1) == IKS_OK) {
+ if (grammar->root) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser->uuid), SWITCH_LOG_DEBUG, "Resolving references\n");
+ if (resolve_refs(grammar, grammar->root, 0)) {
+ result = 1;
+ }
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser->uuid), SWITCH_LOG_INFO, "Nothing to parse!\n");
+ }
+ }
+ iks_parser_delete(p);
+ if (result) {
+ switch_core_hash_insert(parser->cache, document, grammar);
+ } else {
+ if (grammar) {
+ srgs_grammar_destroy(grammar);
+ grammar = NULL;
+ }
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser->uuid), SWITCH_LOG_INFO, "Failed to parse grammar\n");
+ }
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(parser->uuid), SWITCH_LOG_DEBUG, "Using cached grammar\n");
+ }
+ switch_mutex_unlock(parser->mutex);
+
+ return grammar;
+}
+
+#define MAX_INPUT_SIZE 128
+#define OVECTOR_SIZE 30
+#define WORKSPACE_SIZE 1024
+
+/**
+ * Check if no more digits can be added to input and match
+ * @param compiled_regex the regex used in the initial match
+ * @param input the input to check
+ * @return true if end of match (no more input can be added)
+ */
+static int is_match_end(pcre *compiled_regex, const char *input)
+{
+ int ovector[OVECTOR_SIZE];
+ int input_size = strlen(input);
+ char search_input[MAX_INPUT_SIZE + 2];
+ const char *search_set = "0123456789#*ABCD";
+ const char *search = strchr(search_set, input[input_size - 1]); /* start with last digit in input */
+ int i = 0;
+
+ /* For each digit in search_set, check if input + search_set digit is a potential match.
+ If so, then this is not a match end.
+ */
+ sprintf(search_input, "%sZ", input);
+ for (i = 0; i < 16; i++) {
+ int result;
+ if (!*search) {
+ search = search_set;
+ }
+ search_input[input_size] = *search++;
+ result = pcre_exec(compiled_regex, NULL, search_input, input_size + 1, 0, 0,
+ ovector, sizeof(ovector) / sizeof(ovector[0]));
+ if (result > 0) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "not match end\n");
+ return 0;
+ }
+ }
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "is match end\n");
+ return 1;
+}
+
+/**
+ * Find a match
+ * @param grammar the grammar to match
+ * @param input the input to compare
+ * @return the match result
+ */
+enum srgs_match_type srgs_grammar_match(struct srgs_grammar *grammar, const char *input)
+{
+ int result = 0;
+ int ovector[OVECTOR_SIZE];
+ int workspace[WORKSPACE_SIZE];
+ pcre *compiled_regex;
+
+ if (zstr(input)) {
+ return SMT_NO_MATCH;
+ }
+ if (strlen(input) > MAX_INPUT_SIZE) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "input too large: %s\n", input);
+ return SMT_NO_MATCH;
+ }
+
+ if (!(compiled_regex = get_compiled_regex(grammar))) {
+ return SMT_NO_MATCH;
+ }
+ result = pcre_dfa_exec(compiled_regex, NULL, input, strlen(input), 0, PCRE_PARTIAL,
+ ovector, sizeof(ovector) / sizeof(ovector[0]),
+ workspace, sizeof(workspace) / sizeof(workspace[0]));
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "match = %i\n", result);
+ if (result > 0) {
+ if (is_match_end(compiled_regex, input)) {
+ return SMT_MATCH_END;
+ }
+ return SMT_MATCH;
+ }
+ if (result == PCRE_ERROR_PARTIAL) {
+ return SMT_MATCH_PARTIAL;
+ }
+
+ return SMT_NO_MATCH;
+}
+
+/**
+ * Generate regex from SRGS document. Call this after parsing SRGS document.
+ * @param parser the parser
+ * @return the regex or NULL
+ */
+const char *srgs_grammar_to_regex(struct srgs_grammar *grammar)
+{
+ if (!grammar) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "grammar is NULL!\n");
+ return NULL;
+ }
+ switch_mutex_lock(grammar->mutex);
+ if (!grammar->regex && !create_regexes(grammar, grammar->root, NULL)) {
+ switch_mutex_unlock(grammar->mutex);
+ return NULL;
+ }
+ switch_mutex_unlock(grammar->mutex);
+ return grammar->regex;
+}
+
+/**
+ * Create JSGF grammar
+ * @param parser the parser
+ * @param node root node
+ * @param stream set to NULL
+ * @return 1 if successful
+ */
+static int create_jsgf(struct srgs_grammar *grammar, struct srgs_node *node, switch_stream_handle_t *stream)
+{
+ sn_log_node_open(node);
+ switch (node->type) {
+ case SNT_GRAMMAR:
+ if (node->child) {
+ struct srgs_node *child;
+ switch_stream_handle_t new_stream = { 0 };
+ SWITCH_STANDARD_STREAM(new_stream);
+
+ new_stream.write_function(&new_stream, "#JSGF V1.0");
+ if (!zstr(grammar->encoding)) {
+ new_stream.write_function(&new_stream, " %s", grammar->encoding);
+ if (!zstr(grammar->language)) {
+ new_stream.write_function(&new_stream, " %s", grammar->language);
+ }
+ }
+
+ new_stream.write_function(&new_stream,
+ ";\ngrammar org.freeswitch.srgs_to_jsgf;\n"
+ "public ");
+
+ /* output root rule */
+ if (grammar->root_rule) {
+ if (!create_jsgf(grammar, grammar->root_rule, &new_stream)) {
+ switch_safe_free(new_stream.data);
+ return 0;
+ }
+ } else {
+ int num_rules = 0;
+ int first = 1;
+
+ for (child = node->child; child; child = child->next) {
+ if (child->type == SNT_RULE && child->value.rule.is_public) {
+ num_rules++;
+ }
+ }
+
+ if (num_rules > 1) {
+ new_stream.write_function(&new_stream, "<root> =");
+ for (child = node->child; child; child = child->next) {
+ if (child->type == SNT_RULE && child->value.rule.is_public) {
+ if (!first) {
+ new_stream.write_function(&new_stream, "%s", " |");
+ }
+ first = 0;
+ new_stream.write_function(&new_stream, " <%s>", child->value.rule.id);
+ }
+ }
+ new_stream.write_function(&new_stream, ";\n");
+ } else {
+ for (child = node->child; child; child = child->next) {
+ if (child->type == SNT_RULE && child->value.rule.is_public) {
+ grammar->root_rule = child;
+ if (!create_jsgf(grammar, child, &new_stream)) {
+ switch_safe_free(new_stream.data);
+ return 0;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* output all rule definitions */
+ for (child = node->child; child; child = child->next) {
+ if (child->type == SNT_RULE && child != grammar->root_rule) {
+ if (!create_jsgf(grammar, child, &new_stream)) {
+ switch_safe_free(new_stream.data);
+ return 0;
+ }
+ }
+ }
+ grammar->jsgf = switch_core_strdup(grammar->pool, new_stream.data);
+ switch_safe_free(new_stream.data);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "document jsgf = %s\n", grammar->jsgf);
+ }
+ break;
+ case SNT_RULE:
+ if (node->child) {
+ struct srgs_node *item = node->child;
+ stream->write_function(stream, "<%s> =", node->value.rule.id);
+ for (; item; item = item->next) {
+ if (!create_jsgf(grammar, item, stream)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s jsgf rule failed\n", node->value.rule.id);
+ return 0;
+ }
+ }
+ stream->write_function(stream, ";\n");
+ }
+ break;
+ case SNT_STRING: {
+ int len = strlen(node->value.string);
+ int i;
+ stream->write_function(stream, " ");
+ for (i = 0; i < len; i++) {
+ switch (node->value.string[i]) {
+ case '\\':
+ case '*':
+ case '+':
+ case '/':
+ case '(':
+ case ')':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case '=':
+ case '<':
+ case '>':
+ case ';':
+ case '|':
+ stream->write_function(stream, "\\");
+ break;
+ default:
+ break;
+ }
+ stream->write_function(stream, "%c", node->value.string[i]);
+ }
+ if (node->child) {
+ if (!create_jsgf(grammar, node->child, stream)) {
+ return 0;
+ }
+ }
+ break;
+ }
+ case SNT_ITEM:
+ if (node->child) {
+ struct srgs_node *item;
+ if (node->value.item.repeat_min == 0 && node->value.item.repeat_max == 1) {
+ /* optional item */
+ stream->write_function(stream, " [");
+ for(item = node->child; item; item = item->next) {
+ if (!create_jsgf(grammar, item, stream)) {
+ return 0;
+ }
+ }
+ stream->write_function(stream, " ]");
+ } else {
+ /* minimum repeats */
+ int i;
+ for (i = 0; i < node->value.item.repeat_min; i++) {
+ if (node->value.item.repeat_min != 1 && node->value.item.repeat_max != 1) {
+ stream->write_function(stream, " (");
+ }
+ for(item = node->child; item; item = item->next) {
+ if (!create_jsgf(grammar, item, stream)) {
+ return 0;
+ }
+ }
+ if (node->value.item.repeat_min != 1 && node->value.item.repeat_max != 1) {
+ stream->write_function(stream, " )");
+ }
+ }
+ if (node->value.item.repeat_max == INT_MAX) {
+ stream->write_function(stream, "*");
+ } else {
+ for (;i < node->value.item.repeat_max; i++) {
+ stream->write_function(stream, " [");
+ for(item = node->child; item; item = item->next) {
+ if (!create_jsgf(grammar, item, stream)) {
+ return 0;
+ }
+ }
+ stream->write_function(stream, " ]");
+ }
+ }
+ }
+ }
+ break;
+ case SNT_ONE_OF:
+ if (node->child) {
+ struct srgs_node *item = node->child;
+ if (node->num_children > 1) {
+ stream->write_function(stream, " (");
+ }
+ for (; item; item = item->next) {
+ if (item != node->child) {
+ stream->write_function(stream, " |");
+ }
+ stream->write_function(stream, " (");
+ if (!create_jsgf(grammar, item, stream)) {
+ return 0;
+ }
+ stream->write_function(stream, " )");
+ }
+ if (node->num_children > 1) {
+ stream->write_function(stream, " )");
+ }
+ }
+ break;
+ case SNT_REF: {
+ struct srgs_node *rule = node->value.ref.node;
+ stream->write_function(stream, " <%s>", rule->value.rule.id);
+ break;
+ }
+ case SNT_ANY:
+ default:
+ /* ignore */
+ return 1;
+ }
+ sn_log_node_close(node);
+ return 1;
+}
+
+/**
+ * Generate JSGF from SRGS document. Call this after parsing SRGS document.
+ * @param grammar the grammar
+ * @return the JSGF document or NULL
+ */
+const char *srgs_grammar_to_jsgf(struct srgs_grammar *grammar)
+{
+ if (!grammar) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "grammar is NULL!\n");
+ return NULL;
+ }
+ switch_mutex_lock(grammar->mutex);
+ if (!grammar->jsgf && !create_jsgf(grammar, grammar->root, NULL)) {
+ switch_mutex_unlock(grammar->mutex);
+ return NULL;
+ }
+ switch_mutex_unlock(grammar->mutex);
+ return grammar->jsgf;
+}
+
+/**
+ * Generate JSGF file from SRGS document. Call this after parsing SRGS document.
+ * @param grammar the grammar
+ * @param basedir the base path to use if file does not already exist
+ * @param ext the extension to use
+ * @return the path or NULL
+ */
+const char *srgs_grammar_to_jsgf_file(struct srgs_grammar *grammar, const char *basedir, const char *ext)
+{
+ if (!grammar) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(grammar->uuid), SWITCH_LOG_CRIT, "grammar is NULL!\n");
+ return NULL;
+ }
+ switch_mutex_lock(grammar->mutex);
+ if (!grammar->jsgf_file_name) {
+ char file_name_buf[SWITCH_UUID_FORMATTED_LENGTH + 1];
+ switch_file_t *file;
+ switch_size_t len;
+ const char *jsgf = srgs_grammar_to_jsgf(grammar);
+ switch_uuid_str(file_name_buf, sizeof(file_name_buf));
+ grammar->jsgf_file_name = switch_core_sprintf(grammar->pool, "%s%s%s.%s", basedir, SWITCH_PATH_SEPARATOR, file_name_buf, ext);
+ if (!jsgf) {
+ switch_mutex_unlock(grammar->mutex);
+ return NULL;
+ }
+
+ /* write grammar to file */
+ if (switch_file_open(&file, grammar->jsgf_file_name, SWITCH_FOPEN_WRITE | SWITCH_FOPEN_TRUNCATE | SWITCH_FOPEN_CREATE, SWITCH_FPROT_OS_DEFAULT, grammar->pool) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to create jsgf file: %s!\n", grammar->jsgf_file_name);
+ grammar->jsgf_file_name = NULL;
+ switch_mutex_unlock(grammar->mutex);
+ return NULL;
+ }
+ len = strlen(jsgf);
+ switch_file_write(file, jsgf, &len);
+ switch_file_close(file);
+ }
+ switch_mutex_unlock(grammar->mutex);
+ return grammar->jsgf_file_name;
+}
+
+/**
+ * Initialize SRGS parser. This function is not thread safe.
+ */
+int srgs_init(void)
+{
+ if (globals.init) {
+ return 1;
+ }
+
+ globals.init = SWITCH_TRUE;
+ switch_core_new_memory_pool(&globals.pool);
+ switch_core_hash_init(&globals.tag_defs, globals.pool);
+
+ add_root_tag_def("grammar", process_grammar, process_cdata_bad, "meta,metadata,lexicon,tag,rule");
+ add_tag_def("ruleref", process_ruleref, process_cdata_bad, "");
+ add_tag_def("token", process_attribs_ignore, process_cdata_ignore, "");
+ add_tag_def("tag", process_attribs_ignore, process_cdata_ignore, "");
+ add_tag_def("one-of", process_attribs_ignore, process_cdata_tokens, "item");
+ add_tag_def("item", process_item, process_cdata_tokens, "token,ruleref,item,one-of,tag");
+ add_tag_def("rule", process_rule, process_cdata_tokens, "token,ruleref,item,one-of,tag,example");
+ add_tag_def("example", process_attribs_ignore, process_cdata_ignore, "");
+ add_tag_def("lexicon", process_attribs_ignore, process_cdata_bad, "");
+ add_tag_def("meta", process_attribs_ignore, process_cdata_bad, "");
+ add_tag_def("metadata", process_attribs_ignore, process_cdata_ignore, "ANY");
+ add_tag_def("ANY", process_attribs_ignore, process_cdata_ignore, "ANY");
+
+ return 1;
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * srgs.h -- Transforms SRGS into regex rules
+ *
+ */
+#ifndef SRGS_H
+#define SRGS_H
+
+#include <switch.h>
+
+struct srgs_parser;
+struct srgs_grammar;
+
+enum srgs_match_type {
+ /** invalid input */
+ SMT_NO_MATCH,
+ /** matches, can accept more input */
+ SMT_MATCH,
+ /** not yet a match, but valid input so far */
+ SMT_MATCH_PARTIAL,
+ /** matches, cannot accept more input */
+ SMT_MATCH_END
+};
+
+extern int srgs_init(void);
+extern struct srgs_parser *srgs_parser_new(const char *uuid);
+extern struct srgs_grammar *srgs_parse(struct srgs_parser *parser, const char *document);
+extern const char *srgs_grammar_to_regex(struct srgs_grammar *grammar);
+extern const char *srgs_grammar_to_jsgf(struct srgs_grammar *grammar);
+extern const char *srgs_grammar_to_jsgf_file(struct srgs_grammar *grammar, const char *basedir, const char *ext);
+extern enum srgs_match_type srgs_grammar_match(struct srgs_grammar *grammar, const char *input);
+extern void srgs_parser_destroy(struct srgs_parser *parser);
+
+#endif
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * test.h -- simple unit testing macros
+ *
+ */
+#ifndef TEST_H
+#define TEST_H
+
+#define assert_equals(test, expected_str, expected, actual, file, line) \
+{ \
+ int actual_val = actual; \
+ if (expected != actual_val) { \
+ printf("TEST\t%s\tFAIL\t%s\t%i\t!=\t%i\t%s:%i\n", test, expected_str, expected, actual_val, file, line); \
+ exit(1); \
+ } else { \
+ printf("TEST\t%s\tPASS\n", test); \
+ } \
+}
+
+#define assert_string_equals(test, expected, actual, file, line) \
+{ \
+ const char *actual_str = actual; \
+ if (!actual_str || strcmp(expected, actual_str)) { \
+ printf("TEST\t%s\tFAIL\t\t%s\t!=\t%s\t%s:%i\n", test, expected, actual_str, file, line); \
+ exit(1); \
+ } else { \
+ printf("TEST\t%s\tPASS\n", test); \
+ } \
+}
+
+#define assert_not_null(test, actual, file, line) \
+{ \
+ const void *actual_val = actual; \
+ if (!actual_val) { \
+ printf("TEST\t%s\tFAIL\t\t\t\t\t%s:%i\n", test, file, line); \
+ exit(1); \
+ } else { \
+ printf("TEST\t%s\tPASS\n", test); \
+ } \
+}
+
+#define assert_null(test, actual, file, line) \
+{ \
+ const void *actual_val = actual; \
+ if (actual_val) { \
+ printf("TEST\t%s\tFAIL\t\t\t\t\t%s:%i\n", test, file, line); \
+ exit(1); \
+ } else { \
+ printf("TEST\t%s\tPASS\n", test); \
+ } \
+}
+
+#define ASSERT_EQUALS(expected, actual) assert_equals(#actual, #expected, expected, actual, __FILE__, __LINE__)
+#define ASSERT_STRING_EQUALS(expected, actual) assert_string_equals(#actual, expected, actual, __FILE__, __LINE__)
+#define ASSERT_NOT_NULL(actual) assert_not_null(#actual " not null", actual, __FILE__, __LINE__)
+#define ASSERT_NULL(actual) assert_null(#actual " is null", actual, __FILE__, __LINE__)
+
+#define SKIP_ASSERT_EQUALS(expected, actual) if (0) { ASSERT_EQUALS(expected, actual); }
+
+#define TEST(name) printf("TEST BEGIN\t" #name "\n"); name(); printf("TEST END\t"#name "\tPASS\n");
+
+#define SKIP_TEST(name) if (0) { TEST(name) };
+
+#define TEST_INIT switch_core_init(0, SWITCH_TRUE, &err);
+
+#endif
--- /dev/null
+BASE=../../../../..
+
+IKS_DIR=$(BASE)/libs/iksemel
+IKS_LA=$(IKS_DIR)/src/libiksemel.la
+LOCAL_CFLAGS += -I../ -I$(BASE)/libs/iksemel/include
+LOCAL_OBJS= $(PCRE_LA) $(IKS_LA) main.o ../iks_helpers.o
+LOCAL_SOURCES= main.c
+include $(BASE)/build/modmake.rules
+
+$(IKS_LA): $(IKS_DIR) $(IKS_DIR)/.update
+ @cd $(IKS_DIR) && $(MAKE)
+ @$(TOUCH_TARGET)
+
+local_all:
+ libtool --mode=link gcc main.o ../iks_helpers.o -o test test_iks.la
+
+local_clean:
+ -rm test
--- /dev/null
+
+
+#include <switch.h>
+#include <iksemel.h>
+#include "test.h"
+#include "iks_helpers.h"
+
+static const char *voxeo_grammar =
+ "<iq id='8847' type='set' from='usera@192.168.1.10/voxeo3' to='e7632f74-8c55-11e2-84b0-e538fa88a1ef@192.168.1.10'><input xmlns='urn:xmpp:rayo:input:1' min-confidence='0.3' mode='DTMF' sensitivity='0.5'><grammar content-type='application/grammar+voxeo'><![CDATA[[1 DIGITS]]]></grammar></input></iq>";
+
+static void test_iks_cdata_bug(void)
+{
+ iks *iq = NULL;
+ iks *input = NULL;
+ iksparser *p = iks_dom_new(&iq);
+ const char *cdata;
+ ASSERT_EQUALS(IKS_OK, iks_parse(p, voxeo_grammar, 0, 1));
+ iks_parser_delete(p);
+ ASSERT_NOT_NULL((input = iks_find(iq, "input")));
+ ASSERT_NOT_NULL((cdata = iks_find_cdata(input, "grammar")));
+ ASSERT_STRING_EQUALS("[1 DIGITS]", cdata);
+ iks_delete(iq);
+}
+
+static const char *repeating_bracket =
+ "<iq id='8847' type='set' from='usera@192.168.1.10/voxeo3' to='e7632f74-8c55-11e2-84b0-e538fa88a1ef@192.168.1.10'><input xmlns='urn:xmpp:rayo:input:1' min-confidence='0.3' mode='DTMF' sensitivity='0.5'><grammar content-type='application/grammar+voxeo'><![CDATA[[1 DIGITS]>]]]]]]]]] ]] ]]></grammar></input></iq>";
+
+static void test_repeating_bracket(void)
+{
+ iks *iq = NULL;
+ iks *input = NULL;
+ iksparser *p = iks_dom_new(&iq);
+ const char *cdata;
+ ASSERT_EQUALS(IKS_OK, iks_parse(p, repeating_bracket, 0, 1));
+ iks_parser_delete(p);
+ ASSERT_NOT_NULL((input = iks_find(iq, "input")));
+ ASSERT_NOT_NULL((cdata = iks_find_cdata(input, "grammar")));
+ ASSERT_STRING_EQUALS("[1 DIGITS]>]]]]]]]]] ]] ", cdata);
+ iks_delete(iq);
+}
+
+static const char *normal_cdata =
+ "<iq id='8847' type='set' from='usera@192.168.1.10/voxeo3' to='e7632f74-8c55-11e2-84b0-e538fa88a1ef@192.168.1.10'><input xmlns='urn:xmpp:rayo:input:1' min-confidence='0.3' mode='DTMF' sensitivity='0.5'><grammar content-type='application/grammar+voxeo'><![CDATA[1 DIGITS]]></grammar></input></iq>";
+
+static void test_normal_cdata(void)
+{
+ iks *iq = NULL;
+ iks *input = NULL;
+ iksparser *p = iks_dom_new(&iq);
+ const char *cdata;
+ ASSERT_EQUALS(IKS_OK, iks_parse(p, normal_cdata, 0, 1));
+ iks_parser_delete(p);
+ ASSERT_NOT_NULL((input = iks_find(iq, "input")));
+ ASSERT_NOT_NULL((cdata = iks_find_cdata(input, "grammar")));
+ ASSERT_STRING_EQUALS("1 DIGITS", cdata);
+ iks_delete(iq);
+}
+
+static const char *empty_cdata =
+ "<iq id='8847' type='set' from='usera@192.168.1.10/voxeo3' to='e7632f74-8c55-11e2-84b0-e538fa88a1ef@192.168.1.10'><input xmlns='urn:xmpp:rayo:input:1' min-confidence='0.3' mode='DTMF' sensitivity='0.5'><grammar content-type='application/grammar+voxeo'><![CDATA[]]></grammar></input></iq>";
+
+static void test_empty_cdata(void)
+{
+ iks *iq = NULL;
+ iks *input = NULL;
+ iksparser *p = iks_dom_new(&iq);
+ const char *cdata;
+ ASSERT_EQUALS(IKS_OK, iks_parse(p, empty_cdata, 0, 1));
+ iks_parser_delete(p);
+ ASSERT_NOT_NULL((input = iks_find(iq, "input")));
+ ASSERT_NULL((cdata = iks_find_cdata(input, "grammar")));
+ iks_delete(iq);
+}
+
+static const char *rayo_test_srgs =
+ "<grammar xmlns=\"http://www.w3.org/2001/06/grammar\" root=\"MAINRULE\">\n"
+ " <rule id=\"MAINRULE\">\n"
+ " <one-of>\n"
+ " <item>\n"
+ " <item repeat=\"0-1\"> need a</item>\n"
+ " <item repeat=\"0-1\"> i need a</item>\n"
+ " <one-of>\n"
+ " <item> clue </item>\n"
+ " </one-of>\n"
+ " <tag> out.concept = \"clue\";</tag>\n"
+ " </item>\n"
+ " <item>\n"
+ " <item repeat=\"0-1\"> have an</item>\n"
+ " <item repeat=\"0-1\"> i have an</item>\n"
+ " <one-of>\n"
+ " <item> answer </item>\n"
+ " </one-of>\n"
+ " <tag> out.concept = \"answer\";</tag>\n"
+ " </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "</grammar>";
+
+static void test_rayo_test_srgs(void)
+{
+ iks *grammar = NULL;
+ iksparser *p = iks_dom_new(&grammar);
+ ASSERT_EQUALS(IKS_OK, iks_parse(p, rayo_test_srgs, 0, 1));
+ iks_parser_delete(p);
+ iks_delete(grammar);
+}
+
+#define MATCH 1
+#define NO_MATCH 0
+
+static void test_iks_helper_value_matches(void)
+{
+ ASSERT_EQUALS(MATCH, value_matches("1", "1,2,3"));
+ ASSERT_EQUALS(MATCH, value_matches("2", "1,2,3"));
+ ASSERT_EQUALS(MATCH, value_matches("3", "1,2,3"));
+ ASSERT_EQUALS(NO_MATCH, value_matches("4", "1,2,3"));
+ ASSERT_EQUALS(NO_MATCH, value_matches("1,2", "1,2,3"));
+ ASSERT_EQUALS(NO_MATCH, value_matches(NULL, "1,2,3"));
+ ASSERT_EQUALS(NO_MATCH, value_matches(NULL, NULL));
+ ASSERT_EQUALS(NO_MATCH, value_matches("1", NULL));
+ ASSERT_EQUALS(NO_MATCH, value_matches("", "1,2,3"));
+ ASSERT_EQUALS(NO_MATCH, value_matches("", ""));
+ ASSERT_EQUALS(NO_MATCH, value_matches("1", ""));
+ ASSERT_EQUALS(MATCH, value_matches("duplex", "duplex,send,recv"));
+ ASSERT_EQUALS(MATCH, value_matches("send", "duplex,send,recv"));
+ ASSERT_EQUALS(MATCH, value_matches("recv", "duplex,send,recv"));
+ ASSERT_EQUALS(NO_MATCH, value_matches("sendrecv", "duplex,send,recv"));
+ ASSERT_EQUALS(MATCH, value_matches("duplex1", "duplex1,duplex2,duplex3"));
+ ASSERT_EQUALS(MATCH, value_matches("duplex2", "duplex1,duplex2,duplex3"));
+ ASSERT_EQUALS(MATCH, value_matches("duplex3", "duplex1,duplex2,duplex3"));
+ ASSERT_EQUALS(NO_MATCH, value_matches("duplex4", "duplex1,duplex2,duplex3"));
+ ASSERT_EQUALS(NO_MATCH, value_matches("duplex", "duplex1,duplex2,duplex3"));
+}
+
+static void test_dialback_key(void)
+{
+ ASSERT_STRING_EQUALS("37c69b1cf07a3f67c04a5ef5902fa5114f2c76fe4a2686482ba5b89323075643", iks_server_dialback_key("s3cr3tf0rd14lb4ck", "xmpp.example.com", "example.org", "D60000229F"));
+ ASSERT_NULL(iks_server_dialback_key("", "xmpp.example.com", "example.org", "D60000229F"));
+ ASSERT_NULL(iks_server_dialback_key("s3cr3tf0rd14lb4ck", "", "example.org", "D60000229F"));
+ ASSERT_NULL(iks_server_dialback_key("s3cr3tf0rd14lb4ck", "xmpp.example.com", "", "D60000229F"));
+ ASSERT_NULL(iks_server_dialback_key("s3cr3tf0rd14lb4ck", "xmpp.example.com", "example.org", ""));
+ ASSERT_NULL(iks_server_dialback_key(NULL, "xmpp.example.com", "example.org", "D60000229F"));
+ ASSERT_NULL(iks_server_dialback_key("s3cr3tf0rd14lb4ck", NULL, "example.org", "D60000229F"));
+ ASSERT_NULL(iks_server_dialback_key("s3cr3tf0rd14lb4ck", "xmpp.example.com", NULL, "D60000229F"));
+ ASSERT_NULL(iks_server_dialback_key("s3cr3tf0rd14lb4ck", "xmpp.example.com", "example.org", NULL));
+}
+
+/**
+ * main program
+ */
+int main(int argc, char **argv)
+{
+ const char *err;
+ TEST_INIT
+ TEST(test_iks_cdata_bug);
+ TEST(test_repeating_bracket);
+ TEST(test_normal_cdata);
+ TEST(test_empty_cdata);
+ TEST(test_rayo_test_srgs);
+ TEST(test_iks_helper_value_matches);
+ TEST(test_dialback_key);
+ return 0;
+}
--- /dev/null
+int dummy(int i)
+{
+ return 0;
+}
+
+
--- /dev/null
+BASE=../../../../..
+
+IKS_DIR=$(BASE)/libs/iksemel
+IKS_LA=$(IKS_DIR)/src/libiksemel.la
+LOCAL_CFLAGS += -I../ -I$(BASE)/libs/iksemel/include
+LOCAL_OBJS= $(PCRE_LA) $(IKS_LA) main.o ../nlsml.o
+LOCAL_SOURCES= main.c
+include $(BASE)/build/modmake.rules
+
+$(IKS_LA): $(IKS_DIR) $(IKS_DIR)/.update
+ @cd $(IKS_DIR) && $(MAKE)
+ @$(TOUCH_TARGET)
+
+local_all:
+ libtool --mode=link gcc main.o ../nlsml.o -o test test_nlsml.la
+
+local_clean:
+ -rm test
--- /dev/null
+
+
+#include <switch.h>
+#include "test.h"
+#include "nlsml.h"
+
+static const char *nlsml_good =
+ "<result x-model=\"http://theYesNoModel\""
+ " xmlns:xf=\"http://www.w3.org/2000/xforms\""
+ " grammar=\"http://theYesNoGrammar\">"
+ "<interpretation>"
+ "<xf:instance>"
+ "<myApp:yes_no>"
+ "<response>yes</response>"
+ "</myApp:yes_no>"
+ "</xf:instance>"
+ "<input>ok</input>"
+ "</interpretation>"
+ "</result>";
+
+static const char *nlsml_bad =
+ "<result grammar=\"http://grammar\" x-model=\"http://dataModel\"\n"
+ "xmlns:xf=\"http://www.w3.org/2000/xforms\"\n"
+ " <interpretation/>\n"
+ "</result>\n";
+
+static const char *nlsml_match_with_model_instance =
+ "<result grammar=\"http://grammar\" x-model=\"http://dataModel\"\n"
+ " xmlns:xf=\"http://www.w3.org/2000/xforms\">\"\n"
+ " <interpretation confidence=\"75\" grammar=\"http://grammar\"\n"
+ " x-model=\"http://dataModel\"\n"
+ " xmlns:xf=\"http://www.w3.org/2000/xforms\">\n"
+ "\n"
+ " <model>\n"
+ " <xf:group name=\"nameAddress\">\n"
+ " <string name=\"name\"/>\n"
+ " <string name=\"street\"/>\n"
+ " <string name=\"city\"/>\n"
+ " <string name=\"state\"/>\n"
+ " <string name=\"zip\">\n"
+ " <mask>ddddd</mask>\n"
+ " </string>\n"
+ " </xf:group>\n"
+ " </model>\n"
+ "\n"
+ " <xf:instance name=\"nameAddress\">\n"
+ " <nameAddress>\n"
+ " <street confidence=\"75\">123 Maple Street</street>\n"
+ " <city>Mill Valley</city>\n"
+ " <state>CA</state>\n"
+ " <zip>90952</zip>\n"
+ " </nameAddress>\n"
+ " </xf:instance>\n"
+ " <input>\n"
+ " My address is 123 Maple Street,\n"
+ " Mill Valley, California, 90952\n"
+ " </input>n"
+ " </interpretation>\n"
+ "</result>\n";
+
+static const char *nlsml_multi_input =
+ "<result grammar=\"http://grammar\" x-model=\"http://dataModel\"\n"
+ " xmlns:xf=\"http://www.w3.org/2000/xforms\">\"\n"
+ " <interpretation confidence=\"75\" grammar=\"http://grammar\"\n"
+ " x-model=\"http://dataModel\"\n"
+ " xmlns:xf=\"http://www.w3.org/2000/xforms\">\n"
+ "\n"
+ " <input>\n"
+ " <input mode=\"speech\" confidence=\"50\"\n"
+ " timestamp-start=\"2000-04-03T0:00:00\"\n"
+ " timestamp-end=\"2000-04-03T0:00:00.2\">fried</input>\n"
+ " <input mode=\"speech\" confidence=\"100\"\n"
+ " timestamp-start=\"2000-04-03T0:00:00.25\"\n"
+ " timestamp-end=\"2000-04-03T0:00:00.6\">onions</input>\n"
+ " </input>\n"
+ " </interpretation>\n"
+ "</result>\n";
+
+static const char *nlsml_no_input =
+ "<result grammar=\"http://grammar\" x-model=\"http://dataModel\"\n"
+ " xmlns:xf=\"http://www.w3.org/2000/xforms\">\"\n"
+ " <interpretation confidence=\"100\" grammar=\"http://grammar\"\n"
+ " x-model=\"http://dataModel\"\n"
+ " xmlns:xf=\"http://www.w3.org/2000/xforms\">\n"
+ "\n"
+ " <input>\n"
+ " <noinput/>\n"
+ " </input>\n"
+ " </interpretation>\n"
+ "</result>\n";
+
+static const char *nlsml_multi_input_dtmf =
+ "<result grammar=\"http://grammar\" x-model=\"http://dataModel\"\n"
+ " xmlns:xf=\"http://www.w3.org/2000/xforms\">\"\n"
+ " <interpretation confidence=\"100\" grammar=\"http://grammar\"\n"
+ " x-model=\"http://dataModel\"\n"
+ " xmlns:xf=\"http://www.w3.org/2000/xforms\">\n"
+ "\n"
+ " <input>\n"
+ " <input mode=\"speech\"><nomatch/></input>\n"
+ " <input mode=\"dtmf\">1 2 3 4</input>\n"
+ " </input>\n"
+ " </interpretation>\n"
+ "</result>\n";
+
+static const char *nlsml_meta =
+ "<result grammar=\"http://grammar\" x-model=\"http://dataModel\"\n"
+ " xmlns:xf=\"http://www.w3.org/2000/xforms\">\n"
+ "<interpretation grammar=\"http://toppings\"\n"
+ " xmlns:xf=\"http://www.w3.org/2000/xforms\">\n"
+ " <input mode=\"speech\">\n"
+ " what toppings do you have?\n"
+ " </input>\n"
+ " <xf:model>\n"
+ " <xf:group xf:name=\"question\">\n"
+ " <xf:string xf:name=\"questioned_item\"/>\n"
+ " <xf:string xf:name=\"questioned_property\"/>\n"
+ " </xf:group>\n"
+ " </xf:model>\n"
+ " <xf:instance>\n"
+ " <xf:question>\n"
+ " <xf:questioned_item>toppings</xf:questioned_item>\n"
+ " <xf:questioned_property>\n"
+ " availability\n"
+ " </xf:questioned_property>\n"
+ " </xf:question>\n"
+ " </xf:instance>\n"
+ "</interpretation>\n"
+ "</result>\n";
+
+static const char *nlsml_simple_ambiguity =
+ "<result xmlns:xf=\"http://www.w3.org/2000/xforms\"\n"
+ " grammar=\"http://flight\">\n"
+ " <interpretation confidence=\"60\">\n"
+ " <input mode=\"speech\">\n"
+ " I want to go to Pittsburgh\n"
+ " </input>\n"
+ " <xf:model>\n"
+ " <group name=\"airline\">\n"
+ " <string name=\"to_city\"/>\n"
+ " </group>\n"
+ " </xf:model>\n"
+ " <xf:instance>\n"
+ " <myApp:airline>\n"
+ " <to_city>Pittsburgh</to_city>\n"
+ " </myApp:airline>\n"
+ " </xf:instance>\n"
+ " </interpretation>\n"
+ " <interpretation confidence=\"40\">\n"
+ " <input>I want to go to Stockholm</input>\n"
+ " <xf:model>\n"
+ " <group name=\"airline\">\n"
+ " <string name=\"to_city\"/>\n"
+ " </group>\n"
+ " </xf:model>\n"
+ " <xf:instance>\n"
+ " <myApp:airline>\n"
+ " <to_city>Stockholm</to_city>\n"
+ " </myApp:airline>\n"
+ " </xf:instance>\n"
+ " </interpretation>\n"
+ "</result>\n";
+
+const char *nlsml_mixed_initiative =
+ "<result xmlns:xf=\"http://www.w3.org/2000/xforms\"\n"
+ " grammar=\"http://foodorder\">\n"
+ " <interpretation confidence=\"100\" >\n"
+ " <xf:model>\n"
+ " <group name=\"order\">\n"
+ " <group name=\"food_item\" maxOccurs=\"*\">\n"
+ " <group name=\"pizza\" >\n"
+ " <string name=\"ingredients\" maxOccurs=\"*\"/>\n"
+ " </group>\n"
+ " <group name=\"burger\">\n"
+ " <string name=\"ingredients\" maxOccurs=\"*/\">\n"
+ " </group>\n"
+ " </group>\n"
+ " <group name=\"drink_item\" maxOccurs=\"*\">\n"
+ " <string name=\"size\">\n"
+ " <string name=\"type\">\n"
+ " </group>\n"
+ " <string name=\"delivery_method\"/>\n"
+ " </group>\n"
+ " </xf:model>\n"
+ " <xf:instance>\n"
+ " <myApp:order>\n"
+ " <food_item confidence=\"100\">\n"
+ " <pizza>\n"
+ " <xf:ingredients confidence=\"100\">\n"
+ " pepperoni\n"
+ " </xf:ingredients>\n"
+ " <xf:ingredients confidence=\"100\">\n"
+ " cheese\n"
+ " </xf:ingredients>\n"
+ " </pizza>\n"
+ " <pizza>\n"
+ " <ingredients>sausage</ingredients>\n"
+ " </pizza>\n"
+ " </food_item>\n"
+ " <drink_item confidence=\"100\">\n"
+ " <size>2-liter</size>\n"
+ " </drink_item>\n"
+ " <delivery_method>to go</delivery_method>\n"
+ " </myApp:order>\n"
+ " </xf:instance>\n"
+ " <input mode=\"speech\">I would like 2 pizzas,\n"
+ " one with pepperoni and cheese, one with sausage\n"
+ " and a bottle of coke, to go.\n"
+ " </input>\n"
+ " </interpretation>\n"
+ "</result>\n";
+
+static const char *nlsml_no_match =
+ "<result grammar=\"http://grammar\" x-model=\"http://dataModel\"\n"
+ " xmlns:xf=\"http://www.w3.org/2000/xforms\">\"\n"
+ " <interpretation confidence=\"100\" grammar=\"http://grammar\"\n"
+ " x-model=\"http://dataModel\"\n"
+ " xmlns:xf=\"http://www.w3.org/2000/xforms\">\n"
+ "\n"
+ " <input>\n"
+ " <input mode=\"speech\"><nomatch/></input>\n"
+ " <input mode=\"dtmf\"><nomatch/></input>\n"
+ " </input>\n"
+ " </interpretation>\n"
+ "</result>\n";
+
+/**
+ * Test parsing NLSML example results
+ */
+static void test_parse_nlsml_examples(void)
+{
+ ASSERT_EQUALS(NMT_MATCH, nlsml_parse(nlsml_good, "1234"));
+ ASSERT_EQUALS(NMT_BAD_XML, nlsml_parse(nlsml_bad, "1234"));
+ ASSERT_EQUALS(NMT_MATCH, nlsml_parse(nlsml_match_with_model_instance, "1234"));
+ ASSERT_EQUALS(NMT_MATCH, nlsml_parse(nlsml_multi_input, "1234"));
+ ASSERT_EQUALS(NMT_NOINPUT, nlsml_parse(nlsml_no_input, "1234"));
+ ASSERT_EQUALS(NMT_MATCH, nlsml_parse(nlsml_multi_input_dtmf, "1234"));
+ ASSERT_EQUALS(NMT_MATCH, nlsml_parse(nlsml_meta, "1234"));
+ ASSERT_EQUALS(NMT_MATCH, nlsml_parse(nlsml_simple_ambiguity, "1234"));
+ ASSERT_EQUALS(NMT_MATCH, nlsml_parse(nlsml_mixed_initiative, "1234"));
+ ASSERT_EQUALS(NMT_NOMATCH, nlsml_parse(nlsml_no_match, "1234"));
+}
+
+static const char *nlsml_dtmf_result =
+ "<result xmlns='http://www.ietf.org/xml/ns/mrcpv2' "
+ "xmlns:xf='http://www.w3.org/2000/xforms'><interpretation>"
+ "<input><input mode='dtmf' confidence='100'>1 2 3 4</input>"
+ "</input></interpretation></result>";
+
+/**
+ * Test parsing NLSML example results
+ */
+static void test_create_dtmf_match(void)
+{
+ iks *result = nlsml_create_dtmf_match("1234");
+ char *result_str;
+ ASSERT_NOT_NULL(result);
+ result_str = iks_string(NULL, result);
+ ASSERT_STRING_EQUALS(nlsml_dtmf_result, result_str);
+ iks_free(result_str);
+}
+
+static const char *nlsml_good_normalized =
+ "<result x-model='http://theYesNoModel'"
+ " xmlns:xf='http://www.w3.org/2000/xforms'"
+ " grammar='http://theYesNoGrammar'"
+ " xmlns='http://www.ietf.org/xml/ns/mrcpv2'>"
+ "<interpretation>"
+ "<xf:instance>"
+ "<myApp:yes_no>"
+ "<response>yes</response>"
+ "</myApp:yes_no>"
+ "</xf:instance>"
+ "<input>ok</input>"
+ "</interpretation>"
+ "</result>";
+
+/**
+ * Test NLSML normalization
+ */
+static void test_normalize(void)
+{
+ iks *result = nlsml_normalize(nlsml_good);
+ ASSERT_NOT_NULL(result);
+ ASSERT_STRING_EQUALS(nlsml_good_normalized, iks_string(NULL, result));
+}
+
+/**
+ * main program
+ */
+int main(int argc, char **argv)
+{
+ const char *err;
+ TEST_INIT
+ nlsml_init();
+ TEST(test_parse_nlsml_examples);
+ TEST(test_create_dtmf_match);
+ TEST(test_normalize);
+ return 0;
+}
+
--- /dev/null
+int dummy(int i)
+{
+ return 0;
+}
+
+
--- /dev/null
+BASE=../../../../..
+
+IKS_DIR=$(BASE)/libs/iksemel
+PCRE_DIR=$(BASE)/libs/pcre
+IKS_LA=$(IKS_DIR)/src/libiksemel.la
+PCRE_LA=$(PCRE_DIR)/libpcre.la
+LOCAL_CFLAGS += -I../ -I$(BASE)/libs/iksemel/include -I$(BASE)/libs/pcre/include
+LOCAL_OBJS= $(PCRE_LA) $(IKS_LA) main.o ../srgs.o
+LOCAL_SOURCES= main.c
+include $(BASE)/build/modmake.rules
+
+$(IKS_LA): $(IKS_DIR) $(IKS_DIR)/.update
+ @cd $(IKS_DIR) && $(MAKE)
+ @$(TOUCH_TARGET)
+
+$(PCRE_LA): $(PCRE_DIR) $(PCRE_DIR)/.update
+ @cd $(PCRE_DIR) && $(MAKE)
+ @$(TOUCH_TARGET)
+
+local_all:
+ libtool --mode=link gcc main.o ../srgs.o -o test test_srgs.la
+
+local_clean:
+ -rm test
--- /dev/null
+
+
+#include <switch.h>
+#include "test.h"
+#include "srgs.h"
+
+
+static const char *adhearsion_ask_grammar =
+ "<grammar xmlns=\"http://www.w3.org/2001/06/grammar\" version=\"1.0\" xml:lang=\"en-US\" mode=\"dtmf\" root=\"inputdigits\">"
+ " <rule id=\"inputdigits\" scope=\"public\">\n"
+ " <one-of>\n"
+ " <item>0</item>\n"
+ " <item>1</item>\n"
+ " <item>2</item>\n"
+ " <item>3</item>\n"
+ " <item>4</item>\n"
+ " <item>5</item>\n"
+ " <item>6</item>\n"
+ " <item>7</item>\n"
+ " <item>8</item>\n"
+ " <item>9</item>\n"
+ " <item>#</item>\n"
+ " <item>*</item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "</grammar>\n";
+
+/**
+ * Test matching against adhearsion ask grammar
+ */
+static void test_match_adhearsion_ask_grammar(void)
+{
+ struct srgs_parser *parser;
+ struct srgs_grammar *grammar;
+
+ parser = srgs_parser_new("1234");
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, adhearsion_ask_grammar)));
+
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "0"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "1"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "2"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "3"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "4"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "5"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "6"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "7"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "8"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "9"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "#"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "*"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "A"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "27"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "223"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "0123456789*#"));
+
+ srgs_parser_destroy(parser);
+}
+
+static const char *multi_digit_grammar =
+ "<grammar xmlns=\"http://www.w3.org/2001/06/grammar\" version=\"1.0\" xml:lang=\"en-US\" mode=\"dtmf\" root=\"misc\">"
+ " <rule id=\"misc\" scope=\"public\">\n"
+ " <one-of>\n"
+ " <item>01</item>\n"
+ " <item>13</item>\n"
+ " <item> 24</item>\n"
+ " <item>36 </item>\n"
+ " <item>223</item>\n"
+ " <item>5 5</item>\n"
+ " <item>63</item>\n"
+ " <item>76</item>\n"
+ " <item>8 8 0</item>\n"
+ " <item>93</item>\n"
+ " <item> # 2 </item>\n"
+ " <item>*3</item>\n"
+ " <item> 27</item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "</grammar>\n";
+
+/**
+ * Test matching against grammar with multiple digits per item
+ */
+static void test_match_multi_digit_grammar(void)
+{
+ struct srgs_parser *parser;
+ struct srgs_grammar *grammar;
+
+ parser = srgs_parser_new("1234");
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, multi_digit_grammar)));
+
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "0"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "1"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "2"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "3"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "4"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "5"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "6"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "7"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "8"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "9"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "*"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "A"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "27"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "223"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "0123456789*#"));
+
+ srgs_parser_destroy(parser);
+}
+
+static const char *multi_rule_grammar =
+ "<grammar xmlns=\"http://www.w3.org/2001/06/grammar\" version=\"1.0\" xml:lang=\"en-US\" mode=\"dtmf\">"
+ " <rule id=\"misc\" scope=\"public\">\n"
+ " <one-of>\n"
+ " <item>01</item>\n"
+ " <item>13</item>\n"
+ " <item> 24</item>\n"
+ " <item>36 </item>\n"
+ " <item>5 5</item>\n"
+ " <item>63</item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ " <rule id=\"misc2\" scope=\"public\">\n"
+ " <one-of>\n"
+ " <item>76</item>\n"
+ " <item>8 8 0</item>\n"
+ " <item>93</item>\n"
+ " <item> # 2 </item>\n"
+ " <item>*3</item>\n"
+ " <item> 27</item>\n"
+ " <item>223</item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "</grammar>\n";
+
+static void test_match_multi_rule_grammar(void)
+{
+ struct srgs_parser *parser;
+ struct srgs_grammar *grammar;
+
+ parser = srgs_parser_new("1234");
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, multi_rule_grammar)));
+
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "0"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "1"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "2"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "3"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "4"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "5"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "6"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "7"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "8"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "9"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "*"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "A"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "27"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "223"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "0123456789*#"));
+
+ srgs_parser_destroy(parser);
+}
+
+static const char *rayo_example_grammar =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"digit\">\n"
+ " <one-of>\n"
+ " <item> 0 </item>\n"
+ " <item> 1 </item>\n"
+ " <item> 2 </item>\n"
+ " <item> 3 </item>\n"
+ " <item> 4 </item>\n"
+ " <item> 5 </item>\n"
+ " <item> 6 </item>\n"
+ " <item> 7 </item>\n"
+ " <item> 8 </item>\n"
+ " <item> 9 </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <one-of>\n"
+ " <item>\n"
+ " <item repeat=\"4\"><ruleref uri=\"#digit\"/></item>\n"
+ " #\n"
+ " </item>"
+ " <item>"
+ " * 9 \n"
+ " </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "</grammar>\n";
+
+static void test_match_rayo_example_grammar(void)
+{
+ struct srgs_parser *parser;
+ struct srgs_grammar *grammar;
+
+ parser = srgs_parser_new("1234");
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, rayo_example_grammar)));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "0"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "1"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "2"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "3"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "4"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "5"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "6"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "7"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "8"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "9"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "*"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "A"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "*9"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "1234#"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "2321#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "27"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "223"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "0123456789*#"));
+
+ srgs_parser_destroy(parser);
+}
+
+static const char *bad_ref_grammar =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"digit\">\n"
+ " <one-of>\n"
+ " <item> 0 </item>\n"
+ " <item> 1 </item>\n"
+ " <item> 2 </item>\n"
+ " <item> 3 </item>\n"
+ " <item> 4 </item>\n"
+ " <item> 5 </item>\n"
+ " <item> 6 </item>\n"
+ " <item> 7 </item>\n"
+ " <item> 8 </item>\n"
+ " <item> 9 </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <one-of>\n"
+ " <item>\n"
+ " <item repeat=\"4\"><ruleref uri=\"#digi\"/></item>\n"
+ " #\n"
+ " </item>"
+ " <item>"
+ " * 9 \n"
+ " </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "</grammar>\n";
+
+static const char *adhearsion_ask_grammar_bad =
+ "<grammar xmlns=\"http://www.w3.org/2001/06/grammar\" version=\"1.0\" xml:lang=\"en-US\" mode=\"dtmf\" root=\"inputdigits\">"
+ " <rule id=\"inputdigits\" scope=\"public\">\n"
+ " <one-of>\n"
+ " <item>0</item>\n"
+ " <item>1</item\n"
+ " <item>2</item>\n"
+ " <item>3</item>\n"
+ " <item>4</item>\n"
+ " <item>5</item>\n"
+ " <item>6</item>\n"
+ " <item>7</item>\n"
+ " <item>8</item>\n"
+ " <item>9</item>\n"
+ " <item>#</item>\n"
+ " <item>*</item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "</grammar>\n";
+
+static void test_parse_grammar(void)
+{
+ struct srgs_parser *parser;
+
+ parser = srgs_parser_new("1234");
+
+ ASSERT_NOT_NULL(srgs_parse(parser, adhearsion_ask_grammar));
+ ASSERT_NULL(srgs_parse(parser, adhearsion_ask_grammar_bad));
+ ASSERT_NULL(srgs_parse(parser, NULL));
+ ASSERT_NULL(srgs_parse(NULL, adhearsion_ask_grammar));
+ ASSERT_NULL(srgs_parse(NULL, adhearsion_ask_grammar_bad));
+ ASSERT_NULL(srgs_parse(parser, bad_ref_grammar));
+
+ srgs_parser_destroy(parser);
+}
+
+static const char *repeat_item_grammar_bad =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <item>\n"
+ " <item repeat=\"3-1\">4</item>\n"
+ " #\n"
+ " </item>"
+ " </rule>\n"
+ "</grammar>\n";
+
+static const char *repeat_item_grammar_bad2 =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <item>\n"
+ " <item repeat=\"-1\">4</item>\n"
+ " #\n"
+ " </item>"
+ " </rule>\n"
+ "</grammar>\n";
+
+static const char *repeat_item_grammar_bad3 =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <item>\n"
+ " <item repeat=\"1--1\">4</item>\n"
+ " #\n"
+ " </item>"
+ " </rule>\n"
+ "</grammar>\n";
+
+static const char *repeat_item_grammar_bad4 =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <item>\n"
+ " <item repeat=\"ABC\">4</item>\n"
+ " #\n"
+ " </item>"
+ " </rule>\n"
+ "</grammar>\n";
+
+static const char *repeat_item_grammar_bad5 =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <item>\n"
+ " <item repeat=\"\">4</item>\n"
+ " #\n"
+ " </item>"
+ " </rule>\n"
+ "</grammar>\n";
+
+static const char *repeat_item_grammar_bad6 =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <item>\n"
+ " <item repeat=\"1-Z\">4</item>\n"
+ " #\n"
+ " </item>"
+ " </rule>\n"
+ "</grammar>\n";
+
+static const char *repeat_item_grammar =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"digit\">\n"
+ " <one-of>\n"
+ " <item> 0 </item>\n"
+ " <item> 1 </item>\n"
+ " <item> 2 </item>\n"
+ " <item> 3 </item>\n"
+ " <item> 4 </item>\n"
+ " <item> 5 </item>\n"
+ " <item> 6 </item>\n"
+ " <item> 7 </item>\n"
+ " <item> 8 </item>\n"
+ " <item> 9 </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <one-of>\n"
+ " <item>\n"
+ " <item repeat=\"4-4\"><ruleref uri=\"#digit\"/></item>\n"
+ " #\n"
+ " </item>"
+ " <item>"
+ " * 9 \n"
+ " </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "</grammar>\n";
+
+static const char *repeat_item_range_grammar =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"digit\">\n"
+ " <one-of>\n"
+ " <item> 0 </item>\n"
+ " <item> 1 </item>\n"
+ " <item> 2 </item>\n"
+ " <item> 3 </item>\n"
+ " <item> 4 </item>\n"
+ " <item> 5 </item>\n"
+ " <item> 6 </item>\n"
+ " <item> 7 </item>\n"
+ " <item> 8 </item>\n"
+ " <item> 9 </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <one-of>\n"
+ " <item>\n"
+ " <item repeat=\"4-6\"><ruleref uri=\"#digit\"/></item>\n"
+ " #\n"
+ " </item>"
+ " <item>"
+ " * 9 \n"
+ " </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "</grammar>\n";
+
+static const char *repeat_item_optional_grammar =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"digit\">\n"
+ " <one-of>\n"
+ " <item> 0 </item>\n"
+ " <item> 1 </item>\n"
+ " <item> 2 </item>\n"
+ " <item> 3 </item>\n"
+ " <item> 4 </item>\n"
+ " <item> 5 </item>\n"
+ " <item> 6 </item>\n"
+ " <item> 7 </item>\n"
+ " <item> 8 </item>\n"
+ " <item> 9 </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <one-of>\n"
+ " <item>\n"
+ " <item repeat=\"0-1\"><ruleref uri=\"#digit\"/></item>\n"
+ " #\n"
+ " </item>"
+ " <item>"
+ " * 9 \n"
+ " </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "</grammar>\n";
+
+static const char *repeat_item_star_grammar =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"digit\">\n"
+ " <one-of>\n"
+ " <item> 0 </item>\n"
+ " <item> 1 </item>\n"
+ " <item> 2 </item>\n"
+ " <item> 3 </item>\n"
+ " <item> 4 </item>\n"
+ " <item> 5 </item>\n"
+ " <item> 6 </item>\n"
+ " <item> 7 </item>\n"
+ " <item> 8 </item>\n"
+ " <item> 9 </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <one-of>\n"
+ " <item>\n"
+ " <item repeat=\"0-\"><ruleref uri=\"#digit\"/></item>\n"
+ " #\n"
+ " </item>"
+ " <item>"
+ " * 9 \n"
+ " </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "</grammar>\n";
+
+static const char *repeat_item_plus_grammar =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"digit\">\n"
+ " <one-of>\n"
+ " <item> 0 </item>\n"
+ " <item> 1 </item>\n"
+ " <item> 2 </item>\n"
+ " <item> 3 </item>\n"
+ " <item> 4 </item>\n"
+ " <item> 5 </item>\n"
+ " <item> 6 </item>\n"
+ " <item> 7 </item>\n"
+ " <item> 8 </item>\n"
+ " <item> 9 </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <one-of>\n"
+ " <item>\n"
+ " <item repeat=\"1-\"><ruleref uri=\"#digit\"/></item>\n"
+ " #\n"
+ " </item>"
+ " <item>"
+ " * 9 \n"
+ " </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "</grammar>\n";
+
+static void test_repeat_item_grammar(void)
+{
+ struct srgs_parser *parser;
+ struct srgs_grammar *grammar;
+
+ parser = srgs_parser_new("1234");
+ ASSERT_NULL(srgs_parse(parser, repeat_item_grammar_bad));
+ ASSERT_NULL(srgs_parse(parser, repeat_item_grammar_bad2));
+ ASSERT_NULL(srgs_parse(parser, repeat_item_grammar_bad3));
+ ASSERT_NULL(srgs_parse(parser, repeat_item_grammar_bad4));
+ ASSERT_NULL(srgs_parse(parser, repeat_item_grammar_bad5));
+ ASSERT_NULL(srgs_parse(parser, repeat_item_grammar_bad6));
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, repeat_item_grammar)));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "1111#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "1111"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "1234#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "1234"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "11115#"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "11115"));
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, repeat_item_range_grammar)));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "1111#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "1111"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "1234#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "1234"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "11115#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "11115"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "111156#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "111156"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "1111567#"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "1111567"));
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, repeat_item_optional_grammar)));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "1111#"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "1111"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "1234#"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "1234"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "11115#"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "11115"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "111156#"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "111156"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "1111567#"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "1111567"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "1#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "1"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "#"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, ""));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "A#"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "A"));
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, repeat_item_plus_grammar)));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "1111#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "1111"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "1234#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "1234"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "11115#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "11115"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "111156#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "111156"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "111157#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "111157"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "1#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "1"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "#"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, ""));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "A#"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "A"));
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, repeat_item_star_grammar)));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "1111#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "1111"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "1234#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "1234"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "11115#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "11115"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "111156#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "111156"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "111157#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "111157"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "1#"));
+ ASSERT_EQUALS(SMT_MATCH_PARTIAL, srgs_grammar_match(grammar, "1"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "#"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, ""));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "A#"));
+ ASSERT_EQUALS(SMT_NO_MATCH, srgs_grammar_match(grammar, "A"));
+
+ srgs_parser_destroy(parser);
+}
+
+static const char *repeat_item_range_ambiguous_grammar =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"digit\">\n"
+ " <one-of>\n"
+ " <item> 0 </item>\n"
+ " <item> 1 </item>\n"
+ " <item> 2 </item>\n"
+ " <item> 3 </item>\n"
+ " <item> 4 </item>\n"
+ " <item> 5 </item>\n"
+ " <item> 6 </item>\n"
+ " <item> 7 </item>\n"
+ " <item> 8 </item>\n"
+ " <item> 9 </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <item repeat=\"1-3\"><ruleref uri=\"#digit\"/></item>\n"
+ " </rule>\n"
+ "</grammar>\n";
+
+static void test_repeat_item_range_ambiguous_grammar(void)
+{
+ struct srgs_parser *parser;
+ struct srgs_grammar *grammar;
+
+ parser = srgs_parser_new("1234");
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, repeat_item_range_ambiguous_grammar)));
+ ASSERT_EQUALS(SMT_MATCH, srgs_grammar_match(grammar, "1"));
+ ASSERT_EQUALS(SMT_MATCH, srgs_grammar_match(grammar, "12"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "123"));
+}
+
+static const char *repeat_item_range_optional_pound_grammar =
+ "<grammar mode=\"dtmf\" version=\"1.0\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\""
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"digit\">\n"
+ " <one-of>\n"
+ " <item> 0 </item>\n"
+ " <item> 1 </item>\n"
+ " <item> 2 </item>\n"
+ " <item> 3 </item>\n"
+ " <item> 4 </item>\n"
+ " <item> 5 </item>\n"
+ " <item> 6 </item>\n"
+ " <item> 7 </item>\n"
+ " <item> 8 </item>\n"
+ " <item> 9 </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "\n"
+ " <rule id=\"pin\" scope=\"public\">\n"
+ " <one-of>\n"
+ " <item>\n"
+ " <item repeat=\"1-2\"><ruleref uri=\"#digit\"/></item>\n"
+ " <item repeat=\"0-1\">#</item>\n"
+ " </item>\n"
+ " <item repeat=\"3\"><ruleref uri=\"#digit\"/></item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "</grammar>\n";
+static void test_repeat_item_range_optional_pound_grammar(void)
+{
+ struct srgs_parser *parser;
+ struct srgs_grammar *grammar;
+
+ parser = srgs_parser_new("1234");
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, repeat_item_range_optional_pound_grammar)));
+ ASSERT_EQUALS(SMT_MATCH, srgs_grammar_match(grammar, "1"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "1#"));
+ ASSERT_EQUALS(SMT_MATCH, srgs_grammar_match(grammar, "12"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "12#"));
+ ASSERT_EQUALS(SMT_MATCH_END, srgs_grammar_match(grammar, "123"));
+}
+
+/*
+<polite> = please | kindly | oh mighty computer;
+public <command> = [ <polite> ] don't crash;
+*/
+static const char *voice_srgs1 =
+ "<grammar mode=\"voice\" version=\"1.0\"\n"
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\"\n"
+ " language\"en-US\"\n"
+ " xmlns=\"http://www.w3.org/2001/06/grammar\">\n"
+ "\n"
+ " <rule id=\"polite\">\n"
+ " <one-of>\n"
+ " <item>please</item>\n"
+ " <item>kindly</item>\n"
+ " <item> oh mighty computer</item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "\n"
+ " <rule id=\"command\" scope=\"public\">\n"
+ " <item repeat=\"0-1\"><ruleref uri=\"#polite\"/></item>\n"
+ " <item>don't crash</item>\n"
+ " </rule>\n"
+ "</grammar>\n";
+
+static const char *voice_jsgf =
+ "#JSGF V1.0;\n"
+ "grammar org.freeswitch.srgs_to_jsgf;\n"
+ "public <command> = [ <polite> ] don't crash;\n"
+ "<polite> = ( ( please ) | ( kindly ) | ( oh mighty computer ) );\n";
+
+static const char *rayo_test_srgs =
+ "<grammar xmlns=\"http://www.w3.org/2001/06/grammar\" root=\"MAINRULE\">\n"
+ " <rule id=\"MAINRULE\">\n"
+ " <one-of>\n"
+ " <item>\n"
+ " <item repeat=\"0-1\"> need a</item>\n"
+ " <item repeat=\"0-1\"> i need a</item>\n"
+ " <one-of>\n"
+ " <item> clue </item>\n"
+ " </one-of>\n"
+ " <tag> out.concept = \"clue\";</tag>\n"
+ " </item>\n"
+ " <item>\n"
+ " <item repeat=\"0-1\"> have an</item>\n"
+ " <item repeat=\"0-1\"> i have an</item>\n"
+ " <one-of>\n"
+ " <item> answer </item>\n"
+ " </one-of>\n"
+ " <tag> out.concept = \"answer\";</tag>\n"
+ " </item>\n"
+ " </one-of>\n"
+ " </rule>\n"
+ "</grammar>";
+
+static void test_jsgf(void)
+{
+ struct srgs_parser *parser;
+ struct srgs_grammar *grammar;
+ const char *jsgf;
+ parser = srgs_parser_new("1234");
+
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, adhearsion_ask_grammar)));
+ ASSERT_NOT_NULL((jsgf = srgs_grammar_to_jsgf(grammar)));
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, voice_srgs1)));
+ ASSERT_NOT_NULL((jsgf = srgs_grammar_to_jsgf(grammar)));
+ ASSERT_STRING_EQUALS(voice_jsgf, jsgf);
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, multi_rule_grammar)));
+ ASSERT_NOT_NULL((jsgf = srgs_grammar_to_jsgf(grammar)));
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, rayo_test_srgs)));
+ ASSERT_NOT_NULL((jsgf = srgs_grammar_to_jsgf(grammar)));
+ ASSERT_NULL(srgs_grammar_to_jsgf(NULL));
+ srgs_parser_destroy(parser);
+}
+
+/* removed the ruleref to URL from example */
+static const char *w3c_example_grammar =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+
+ "<!DOCTYPE grammar PUBLIC \"-//W3C//DTD GRAMMAR 1.0//EN\""
+ " \"http://www.w3.org/TR/speech-grammar/grammar.dtd\">\n"
+ "\n"
+ "<grammar xmlns=\"http://www.w3.org/2001/06/grammar\" xml:lang=\"en\"\n"
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\"\n"
+ " version=\"1.0\" mode=\"voice\" root=\"basicCmd\">\n"
+ "\n"
+ "<meta name=\"author\" content=\"Stephanie Williams\"/>\n"
+ "\n"
+ "<rule id=\"basicCmd\" scope=\"public\">\n"
+ " <example> please move the window </example>\n"
+ " <example> open a file </example>\n"
+ "\n"
+ " <!--ruleref uri=\"http://grammar.example.com/politeness.grxml#startPolite\"/-->\n"
+ "\n"
+ " <ruleref uri=\"#command\"/>\n"
+ " <!--ruleref uri=\"http://grammar.example.com/politeness.grxml#endPolite\"/-->\n"
+ "\n"
+ "</rule>\n"
+ "\n"
+ "<rule id=\"command\">\n"
+ " <ruleref uri=\"#action\"/> <ruleref uri=\"#object\"/>\n"
+ "</rule>\n"
+ "\n"
+ "<rule id=\"action\">\n"
+ " <one-of>\n"
+ " <item weight=\"10\"> open <tag>TAG-CONTENT-1</tag> </item>\n"
+ " <item weight=\"2\"> close <tag>TAG-CONTENT-2</tag> </item>\n"
+ " <item weight=\"1\"> delete <tag>TAG-CONTENT-3</tag> </item>\n"
+ " <item weight=\"1\"> move <tag>TAG-CONTENT-4</tag> </item>\n"
+ " </one-of>\n"
+ "</rule>\n"
+ "\n"
+ "<rule id=\"object\">\n"
+ " <item repeat=\"0-1\">\n"
+ " <one-of>\n"
+ " <item> the </item>\n"
+ " <item> a </item>\n"
+ " </one-of>\n"
+ " </item>\n"
+ "\n"
+ " <one-of>\n"
+ " <item> window </item>\n"
+ " <item> file </item>\n"
+ " <item> menu </item>\n"
+ " </one-of>\n"
+ "</rule>\n"
+ "\n"
+ "</grammar>";
+
+static void test_w3c_example_grammar(void)
+{
+ struct srgs_parser *parser;
+ struct srgs_grammar *grammar;
+ parser = srgs_parser_new("1234");
+
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, w3c_example_grammar)));
+ ASSERT_NOT_NULL(srgs_grammar_to_jsgf(grammar));
+}
+
+static const char *metadata_grammar =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+
+ "<!DOCTYPE grammar PUBLIC \"-//W3C//DTD GRAMMAR 1.0//EN\""
+ " \"http://www.w3.org/TR/speech-grammar/grammar.dtd\">\n"
+ "\n"
+ "<grammar xmlns=\"http://www.w3.org/2001/06/grammar\" xml:lang=\"en\"\n"
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+ " xsi:schemaLocation=\"http://www.w3.org/2001/06/grammar\n"
+ " http://www.w3.org/TR/speech-grammar/grammar.xsd\"\n"
+ " version=\"1.0\" mode=\"voice\" root=\"basicCmd\">\n"
+ "\n"
+ "<meta name=\"author\" content=\"Stephanie Williams\"/>\n"
+ "<metadata>\n"
+ " <foo>\n"
+ " <bar>\n"
+ " <luser/>\n"
+ " </bar>\n"
+ " </foo>\n"
+ "</metadata>\n"
+ "\n"
+ "<rule id=\"basicCmd\" scope=\"public\">\n"
+ " <example> please move the window </example>\n"
+ " <example> open a file </example>\n"
+ "\n"
+ " <!--ruleref uri=\"http://grammar.example.com/politeness.grxml#startPolite\"/-->\n"
+ "\n"
+ " <ruleref uri=\"#command\"/>\n"
+ " <!--ruleref uri=\"http://grammar.example.com/politeness.grxml#endPolite\"/-->\n"
+ "\n"
+ "</rule>\n"
+ "\n"
+ "<rule id=\"command\">\n"
+ " <ruleref uri=\"#action\"/> <ruleref uri=\"#object\"/>\n"
+ "</rule>\n"
+ "\n"
+ "<rule id=\"action\">\n"
+ " <one-of>\n"
+ " <item weight=\"10\"> open <tag>TAG-CONTENT-1</tag> </item>\n"
+ " <item weight=\"2\"> close <tag>TAG-CONTENT-2</tag> </item>\n"
+ " <item weight=\"1\"> delete <tag>TAG-CONTENT-3</tag> </item>\n"
+ " <item weight=\"1\"> move <tag>TAG-CONTENT-4</tag> </item>\n"
+ " </one-of>\n"
+ "</rule>\n"
+ "\n"
+ "<rule id=\"object\">\n"
+ " <item repeat=\"0-1\">\n"
+ " <one-of>\n"
+ " <item> the </item>\n"
+ " <item> a </item>\n"
+ " </one-of>\n"
+ " </item>\n"
+ "\n"
+ " <one-of>\n"
+ " <item> window </item>\n"
+ " <item> file </item>\n"
+ " <item> menu </item>\n"
+ " </one-of>\n"
+ "</rule>\n"
+ "\n"
+ "</grammar>";
+
+static void test_metadata_grammar(void)
+{
+ struct srgs_parser *parser;
+ struct srgs_grammar *grammar;
+ parser = srgs_parser_new("1234");
+
+ ASSERT_NOT_NULL((grammar = srgs_parse(parser, metadata_grammar)));
+ ASSERT_NOT_NULL(srgs_grammar_to_jsgf(grammar));
+}
+
+/**
+ * main program
+ */
+int main(int argc, char **argv)
+{
+ const char *err;
+ TEST_INIT
+ srgs_init();
+ TEST(test_parse_grammar);
+ TEST(test_match_adhearsion_ask_grammar);
+ TEST(test_match_multi_digit_grammar);
+ TEST(test_match_multi_rule_grammar);
+ TEST(test_match_rayo_example_grammar);
+ TEST(test_repeat_item_grammar);
+ TEST(test_jsgf);
+ TEST(test_w3c_example_grammar);
+ TEST(test_metadata_grammar);
+ TEST(test_repeat_item_range_ambiguous_grammar);
+ TEST(test_repeat_item_range_optional_pound_grammar);
+ return 0;
+}
+
--- /dev/null
+int dummy(int i)
+{
+ return 0;
+}
+
+
--- /dev/null
+XMPP_ERROR(STANZA_ERROR_BAD_REQUEST, "bad-request", "modify")
+XMPP_ERROR(STANZA_ERROR_CONFLICT, "conflict", "cancel")
+XMPP_ERROR(STANZA_ERROR_FEATURE_NOT_IMPLEMENTED, "feature-not-implemented", "modify")
+XMPP_ERROR(STANZA_ERROR_FORBIDDEN, "forbidden", "auth")
+XMPP_ERROR(STANZA_ERROR_GONE, "gone", "modify")
+XMPP_ERROR(STANZA_ERROR_INTERNAL_SERVER_ERROR, "internal-server-error", "wait")
+XMPP_ERROR(STANZA_ERROR_ITEM_NOT_FOUND, "item-not-found", "cancel")
+XMPP_ERROR(STANZA_ERROR_JID_MALFORMED, "jid-malformed", "modify")
+XMPP_ERROR(STANZA_ERROR_NOT_ACCEPTABLE, "not-acceptable", "modify")
+XMPP_ERROR(STANZA_ERROR_NOT_ALLOWED, "not-allowed", "cancel")
+XMPP_ERROR(STANZA_ERROR_NOT_AUTHORIZED, "not-authorized", "auth")
+XMPP_ERROR(STANZA_ERROR_RECIPIENT_UNAVAILABLE, "recipient-unavailable", "wait")
+XMPP_ERROR(STANZA_ERROR_REDIRECT, "redirect", "modify")
+XMPP_ERROR(STANZA_ERROR_REGISTRATION_REQUIRED, "registration-required", "auth")
+XMPP_ERROR(STANZA_ERROR_REMOTE_SERVER_NOT_FOUND, "remote-server-not-found", "cancel")
+XMPP_ERROR(STANZA_ERROR_REMOTE_SERVER_TIMEOUT, "remote-server-timeout", "wait")
+XMPP_ERROR(STANZA_ERROR_RESOURCE_CONSTRAINT, "resource-constraint", "wait")
+XMPP_ERROR(STANZA_ERROR_SERVICE_UNAVAILABLE, "service-unavailable", "cancel")
+XMPP_ERROR(STANZA_ERROR_UNDEFINED_CONDITION, "undefined-condition", "wait")
+XMPP_ERROR(STANZA_ERROR_UNEXPECTED_REQUEST, "unexpected-request", "wait")
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * xmpp_streams.c -- XMPP s2s and c2s streams
+ *
+ */
+#include <switch.h>
+#include <iksemel.h>
+
+#include "xmpp_streams.h"
+#include "iks_helpers.h"
+#include "sasl.h"
+
+#define MAX_QUEUE_LEN 25000
+
+/**
+ * Context for all streams
+ */
+struct xmpp_stream_context {
+ /** memory pool to use */
+ switch_memory_pool_t *pool;
+ /** domain for this context */
+ const char *domain;
+ /** synchronizes access to streams and routes hashes */
+ switch_mutex_t *streams_mutex;
+ /** map of stream JID to routable stream */
+ switch_hash_t *routes;
+ /** map of stream ID to stream */
+ switch_hash_t *streams;
+ /** map of user ID to password */
+ switch_hash_t *users;
+ /** shared secret for server dialback */
+ const char *dialback_secret;
+ /** callback when a new stream is ready */
+ xmpp_stream_ready_callback ready_callback;
+ /** callback when a stream is destroyed */
+ xmpp_stream_destroy_callback destroy_callback;
+ /** callback when a stanza is received */
+ xmpp_stream_recv_callback recv_callback;
+ /** context shutdown flag */
+ int shutdown;
+ /** prevents context shutdown until all threads are finished */
+ switch_thread_rwlock_t *shutdown_rwlock;
+};
+
+/**
+ * State of a stream
+ */
+enum xmpp_stream_state {
+ /** new connection */
+ XSS_CONNECT,
+ /** bidirectional comms established */
+ XSS_BIDI,
+ /** remote party authenticated */
+ XSS_AUTHENTICATED,
+ /** client resource bound */
+ XSS_RESOURCE_BOUND,
+ /** ready to accept requests */
+ XSS_READY,
+ /** terminating stream */
+ XSS_SHUTDOWN,
+ /** unrecoverable error */
+ XSS_ERROR,
+ /** destroyed */
+ XSS_DESTROY
+};
+
+/**
+ * A client/server stream connection
+ */
+struct xmpp_stream {
+ /** stream state */
+ enum xmpp_stream_state state;
+ /** true if server-to-server connection */
+ int s2s;
+ /** true if incoming connection */
+ int incoming;
+ /** Jabber ID of remote party */
+ char *jid;
+ /** stream ID */
+ char *id;
+ /** stream pool */
+ switch_memory_pool_t *pool;
+ /** address of this stream */
+ const char *address;
+ /** port of this stream */
+ int port;
+ /** synchronizes access to this stream */
+ switch_mutex_t *mutex;
+ /** socket to remote party */
+ switch_socket_t *socket;
+ /** socket poll descriptor */
+ switch_pollfd_t *pollfd;
+ /** XML stream parser */
+ iksparser *parser;
+ /** outbound message queue */
+ switch_queue_t *msg_queue;
+ /** true if no activity last poll */
+ int idle;
+ /** context for this stream */
+ struct xmpp_stream_context *context;
+ /** user private data */
+ void *user_private;
+};
+
+/**
+ * A socket listening for new connections
+ */
+struct xmpp_listener {
+ /** listener pool */
+ switch_memory_pool_t *pool;
+ /** listen address */
+ char *addr;
+ /** listen port */
+ switch_port_t port;
+ /** access control list */
+ const char *acl;
+ /** listen socket */
+ switch_socket_t *socket;
+ /** pollset for listen socket */
+ switch_pollfd_t *read_pollfd;
+ /** true if server to server connections only */
+ int s2s;
+ /** context for new streams */
+ struct xmpp_stream_context *context;
+};
+
+static void xmpp_stream_new_id(struct xmpp_stream *stream);
+static void xmpp_stream_set_id(struct xmpp_stream *stream, const char *id);
+
+/**
+ * Convert xmpp stream state to string
+ * @param state the xmpp stream state
+ * @return the string value of state or "UNKNOWN"
+ */
+static const char *xmpp_stream_state_to_string(enum xmpp_stream_state state)
+{
+ switch(state) {
+ case XSS_CONNECT: return "CONNECT";
+ case XSS_BIDI: return "BIDI";
+ case XSS_AUTHENTICATED: return "AUTHENTICATED";
+ case XSS_RESOURCE_BOUND: return "RESOURCE_BOUND";
+ case XSS_READY: return "READY";
+ case XSS_SHUTDOWN: return "SHUTDOWN";
+ case XSS_ERROR: return "ERROR";
+ case XSS_DESTROY: return "DESTROY";
+ }
+ return "UNKNOWN";
+}
+
+/**
+ * Handle XMPP stream logging callback
+ * @param user_data the xmpp stream
+ * @param data the log message
+ * @param size of the log message
+ * @param is_incoming true if this is a log for a received message
+ */
+static void on_stream_log(void *user_data, const char *data, size_t size, int is_incoming)
+{
+ if (size > 0) {
+ struct xmpp_stream *stream = (struct xmpp_stream *)user_data;
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_DEBUG, "%s_%s %s %s %s\n", stream->s2s ? "s2s" : "c2s",
+ stream->incoming ? "in" : "out", stream->jid, is_incoming ? "RECV" : "SEND", data);
+ }
+}
+
+/**
+ * Send stanza to stream.
+ */
+static void xmpp_stream_stanza_send(struct xmpp_stream *stream, iks *msg)
+{
+ /* send directly if client or outbound s2s stream */
+ if (!stream->s2s || !stream->incoming) {
+ iks_send(stream->parser, msg);
+ iks_delete(msg);
+ } else {
+ /* route message to outbound server stream */
+ xmpp_stream_context_send(stream->context, stream->jid, msg);
+ iks_delete(msg);
+ }
+}
+
+/**
+ * Attach stream to connected socket
+ * @param stream the stream
+ * @param socket the connected socket
+ */
+static void xmpp_stream_set_socket(struct xmpp_stream *stream, switch_socket_t *socket)
+{
+ stream->socket = socket;
+ switch_socket_create_pollset(&stream->pollfd, stream->socket, SWITCH_POLLIN | SWITCH_POLLERR, stream->pool);
+
+ /* connect XMPP stream parser to socket */
+ {
+ switch_os_socket_t os_socket;
+ switch_os_sock_get(&os_socket, stream->socket);
+ iks_connect_fd(stream->parser, os_socket);
+ /* TODO connect error checking */
+ }
+}
+
+/**
+ * Assign a new ID to the stream
+ * @param stream the stream
+ */
+static void xmpp_stream_new_id(struct xmpp_stream *stream)
+{
+ char id[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 };
+ switch_uuid_str(id, sizeof(id));
+ xmpp_stream_set_id(stream, id);
+}
+
+/**
+ * Send session reply to server <stream> after auth is done
+ * @param stream the xmpp stream
+ */
+static void xmpp_send_server_header_features(struct xmpp_stream *stream)
+{
+ struct xmpp_stream_context *context = stream->context;
+ char *header = switch_mprintf(
+ "<stream:stream xmlns='"IKS_NS_SERVER"' xmlns:db='"IKS_NS_XMPP_DIALBACK"'"
+ " from='%s' id='%s' xml:lang='en' version='1.0'"
+ " xmlns:stream='"IKS_NS_XMPP_STREAMS"'><stream:features>"
+ "</stream:features>", context->domain, stream->id);
+
+ iks_send_raw(stream->parser, header);
+ free(header);
+}
+
+/**
+ * Send bind + session reply to client <stream>
+ * @param stream the xmpp stream
+ */
+static void xmpp_send_client_header_bind(struct xmpp_stream *stream)
+{
+ struct xmpp_stream_context *context = stream->context;
+ char *header = switch_mprintf(
+ "<stream:stream xmlns='"IKS_NS_CLIENT"' xmlns:db='"IKS_NS_XMPP_DIALBACK"'"
+ " from='%s' id='%s' xml:lang='en' version='1.0'"
+ " xmlns:stream='"IKS_NS_XMPP_STREAMS"'><stream:features>"
+ "<bind xmlns='"IKS_NS_XMPP_BIND"'/>"
+ "<session xmlns='"IKS_NS_XMPP_SESSION"'/>"
+ "</stream:features>", context->domain, stream->id);
+
+ iks_send_raw(stream->parser, header);
+ free(header);
+}
+
+/**
+ * Handle <presence> message callback
+ * @param stream the stream
+ * @param node the presence message
+ */
+static void on_stream_presence(struct xmpp_stream *stream, iks *node)
+{
+ struct xmpp_stream_context *context = stream->context;
+ const char *from = iks_find_attrib(node, "from");
+
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_DEBUG, "%s, presence, state = %s\n", stream->jid, xmpp_stream_state_to_string(stream->state));
+
+ if (!from) {
+ if (stream->s2s) {
+ /* from is required in s2s connections */
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_DEBUG, "%s, no presence from JID\n", stream->jid);
+ return;
+ }
+
+ /* use stream JID if a c2s connection */
+ from = stream->jid;
+ if (zstr(from)) {
+ /* error */
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_DEBUG, "%s, no presence from JID\n", stream->jid);
+ return;
+ }
+ iks_insert_attrib(node, "from", from);
+ }
+ if (context->recv_callback) {
+ context->recv_callback(stream, node);
+ }
+}
+
+/**
+ * Send <success> reply to xmpp stream <auth>
+ * @param stream the xmpp stream.
+ */
+static void xmpp_send_auth_success(struct xmpp_stream *stream)
+{
+ iks_send_raw(stream->parser, "<success xmlns='"IKS_NS_XMPP_SASL"'/>");
+}
+
+/**
+ * Send <failure> reply to xmpp client <auth>
+ * @param stream the xmpp stream to use.
+ * @param reason the reason for failure
+ */
+static void xmpp_send_auth_failure(struct xmpp_stream *stream, const char *reason)
+{
+ char *reply = switch_mprintf("<failure xmlns='"IKS_NS_XMPP_SASL"'>"
+ "<%s/></failure>", reason);
+ iks_send_raw(stream->parser, reply);
+ free(reply);
+}
+
+/**
+ * Validate username and password
+ * @param authzid authorization id
+ * @param authcid authentication id
+ * @param password
+ * @return 1 if authenticated
+ */
+static int verify_plain_auth(struct xmpp_stream_context *context, const char *authzid, const char *authcid, const char *password)
+{
+ char *correct_password;
+ if (zstr(authzid) || zstr(authcid) || zstr(password)) {
+ return 0;
+ }
+ correct_password = switch_core_hash_find(context->users, authcid);
+ return !zstr(correct_password) && !strcmp(correct_password, password);
+}
+
+/**
+ * Send sasl reply to xmpp <stream>
+ * @param stream the xmpp stream
+ */
+static void xmpp_send_client_header_auth(struct xmpp_stream *stream)
+{
+ struct xmpp_stream_context *context = stream->context;
+ char *header = switch_mprintf(
+ "<stream:stream xmlns='"IKS_NS_CLIENT"' xmlns:db='"IKS_NS_XMPP_DIALBACK"'"
+ " from='%s' id='%s' xml:lang='en' version='1.0'"
+ " xmlns:stream='"IKS_NS_XMPP_STREAMS"'><stream:features>"
+ "<mechanisms xmlns='"IKS_NS_XMPP_SASL"'>"
+ "<mechanism>PLAIN</mechanism>"
+ "</mechanisms></stream:features>", context->domain, stream->id);
+ iks_send_raw(stream->parser, header);
+ free(header);
+}
+
+/**
+ * Send sasl reply to xmpp <stream>
+ * @param stream the xmpp stream
+ */
+static void xmpp_send_server_header_auth(struct xmpp_stream *stream)
+{
+ struct xmpp_stream_context *context = stream->context;
+ char *header = switch_mprintf(
+ "<stream:stream xmlns='"IKS_NS_SERVER"' xmlns:db='"IKS_NS_XMPP_DIALBACK"'"
+ " from='%s' id='%s' xml:lang='en' version='1.0'"
+ " xmlns:stream='"IKS_NS_XMPP_STREAMS"'>"
+ "<stream:features>"
+#if 0
+ "<bidi xmlns='"IKS_NS_BIDI_FEATURE"'/>"
+ "<mechanisms xmlns='"IKS_NS_XMPP_SASL"'>"
+ "<mechanism>PLAIN</mechanism>"
+ "</mechanisms>"
+#endif
+ "</stream:features>",
+ context->domain, stream->id);
+ iks_send_raw(stream->parser, header);
+ free(header);
+}
+
+/**
+ * Send dialback to receiving server
+ */
+static void xmpp_send_dialback_key(struct xmpp_stream *stream)
+{
+ struct xmpp_stream_context *context = stream->context;
+ char *dialback_key = iks_server_dialback_key(context->dialback_secret, stream->jid, context->domain, stream->id);
+ if (dialback_key) {
+ char *dialback = switch_mprintf(
+ "<db:result from='%s' to='%s'>%s</db:result>",
+ context->domain, stream->jid,
+ dialback_key);
+ iks_send_raw(stream->parser, dialback);
+ free(dialback);
+ free(dialback_key);
+ } else {
+ /* TODO missing shared secret */
+ }
+}
+
+/**
+ * Send initial <stream> header to peer server
+ * @param stream the xmpp stream
+ */
+static void xmpp_send_outbound_server_header(struct xmpp_stream *stream)
+{
+ struct xmpp_stream_context *context = stream->context;
+ char *header = switch_mprintf(
+ "<stream:stream xmlns='"IKS_NS_SERVER"' xmlns:db='"IKS_NS_XMPP_DIALBACK"'"
+ " from='%s' to='%s' xml:lang='en' version='1.0'"
+ " xmlns:stream='"IKS_NS_XMPP_STREAMS"'>", context->domain, stream->jid);
+ iks_send_raw(stream->parser, header);
+ free(header);
+}
+
+/**
+ * Handle <auth> message. Only PLAIN supported.
+ * @param stream the xmpp stream
+ * @param node the <auth> packet
+ */
+static void on_stream_auth(struct xmpp_stream *stream, iks *node)
+{
+ struct xmpp_stream_context *context = stream->context;
+ const char *xmlns, *mechanism;
+ iks *auth_body;
+
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_DEBUG, "%s, auth, state = %s\n", stream->jid, xmpp_stream_state_to_string(stream->state));
+
+ /* wrong state for authentication */
+ if (stream->state != XSS_BIDI) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_WARNING, "%s, auth UNEXPECTED, state = %s\n", stream->jid, xmpp_stream_state_to_string(stream->state));
+ /* on_auth unexpected error */
+ stream->state = XSS_ERROR;
+ return;
+ }
+
+ /* unsupported authentication type */
+ xmlns = iks_find_attrib_soft(node, "xmlns");
+ if (strcmp(IKS_NS_XMPP_SASL, xmlns)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_WARNING, "%s, auth, state = %s, unsupported namespace: %s!\n", stream->jid, xmpp_stream_state_to_string(stream->state), xmlns);
+ /* on_auth namespace error */
+ stream->state = XSS_ERROR;
+ return;
+ }
+
+ /* unsupported SASL authentication mechanism */
+ mechanism = iks_find_attrib_soft(node, "mechanism");
+ if (strcmp("PLAIN", mechanism)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_WARNING, "%s, auth, state = %s, unsupported SASL mechanism: %s!\n", stream->jid, xmpp_stream_state_to_string(stream->state), mechanism);
+ xmpp_send_auth_failure(stream, "invalid-mechanism");
+ stream->state = XSS_ERROR;
+ return;
+ }
+
+ if ((auth_body = iks_child(node)) && iks_type(auth_body) == IKS_CDATA) {
+ /* get user and password from auth */
+ char *message = iks_cdata(auth_body);
+ char *authzid = NULL, *authcid, *password;
+ /* TODO use library for SASL! */
+ parse_plain_auth_message(message, &authzid, &authcid, &password);
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_DEBUG, "%s, auth, state = %s, SASL/PLAIN decoded authzid = \"%s\" authcid = \"%s\"\n", stream->jid, xmpp_stream_state_to_string(stream->state), authzid, authcid);
+ if (verify_plain_auth(context, authzid, authcid, password)) {
+ stream->jid = switch_core_strdup(stream->pool, authzid);
+ if (!stream->s2s && !strchr(stream->jid, '@')) {
+ /* add missing domain on client stream */
+ stream->jid = switch_core_sprintf(stream->pool, "%s@%s", stream->jid, context->domain);
+ }
+
+ xmpp_send_auth_success(stream);
+ stream->state = XSS_AUTHENTICATED;
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_WARNING, "%s, auth, state = %s, invalid user or password!\n", stream->jid, xmpp_stream_state_to_string(stream->state));
+ xmpp_send_auth_failure(stream, "not-authorized");
+ stream->state = XSS_ERROR;
+ }
+ switch_safe_free(authzid);
+ switch_safe_free(authcid);
+ switch_safe_free(password);
+ } else {
+ /* missing message */
+ stream->state = XSS_ERROR;
+ }
+}
+
+/**
+ * Handle <bidi> message.
+ * @param stream the xmpp stream
+ * @param node the <bidi> packet
+ */
+static void on_stream_bidi(struct xmpp_stream *stream, iks *node)
+{
+ /* only allow bidi on s2s connections before auth */
+ if (stream->s2s) {
+ switch(stream->state) {
+ case XSS_CONNECT:
+ stream->state = XSS_BIDI;
+ break;
+ case XSS_BIDI:
+ case XSS_AUTHENTICATED:
+ case XSS_RESOURCE_BOUND:
+ case XSS_READY:
+ case XSS_SHUTDOWN:
+ case XSS_ERROR:
+ case XSS_DESTROY:
+ /* error */
+ stream->state = XSS_ERROR;
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, bad state: %s\n", stream->jid, xmpp_stream_state_to_string(stream->state));
+ break;
+ }
+ } else {
+ /* error */
+ stream->state = XSS_ERROR;
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, bidi not allowed from client\n", stream->jid);
+ }
+}
+
+/**
+ * Handle <iq><session> request
+ * @param stream the xmpp stream
+ * @param node the <iq> node
+ * @return NULL
+ */
+static iks *on_iq_set_xmpp_session(struct xmpp_stream *stream, iks *node)
+{
+ struct xmpp_stream_context *context = stream->context;
+ iks *reply;
+
+ switch(stream->state) {
+ case XSS_RESOURCE_BOUND: {
+ reply = iks_new_iq_result(node);
+ stream->state = XSS_READY;
+
+ /* add to available streams */
+ switch_mutex_lock(context->streams_mutex);
+ switch_core_hash_insert(context->routes, stream->jid, stream);
+ switch_mutex_unlock(context->streams_mutex);
+
+ if (context->ready_callback) {
+ context->ready_callback(stream);
+ }
+ break;
+ }
+ case XSS_AUTHENTICATED:
+ case XSS_READY:
+ default:
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_WARNING, "%s, iq UNEXPECTED <session>, state = %s\n", stream->jid, xmpp_stream_state_to_string(stream->state));
+ reply = iks_new_error(node, STANZA_ERROR_SERVICE_UNAVAILABLE);
+ break;
+ }
+
+ return reply;
+}
+
+/**
+ * Handle <iq><bind> request
+ * @param stream the xmpp stream
+ * @param node the <iq> node
+ */
+static iks *on_iq_set_xmpp_bind(struct xmpp_stream *stream, iks *node)
+{
+ iks *reply = NULL;
+
+ switch(stream->state) {
+ case XSS_AUTHENTICATED: {
+ iks *bind = iks_find(node, "bind");
+ iks *x;
+ /* get optional client resource ID */
+ char *resource_id = iks_find_cdata(bind, "resource");
+
+ /* generate resource ID for client if not already set */
+ if (zstr(resource_id)) {
+ char resource_id_buf[SWITCH_UUID_FORMATTED_LENGTH + 1];
+ switch_uuid_str(resource_id_buf, sizeof(resource_id_buf));
+ resource_id = switch_core_strdup(stream->pool, resource_id_buf);
+ }
+ stream->jid = switch_core_sprintf(stream->pool, "%s/%s", stream->jid, resource_id);
+ stream->state = XSS_RESOURCE_BOUND;
+
+ /* create reply */
+ reply = iks_new_iq_result(node);
+ x = iks_insert(reply, "bind");
+ iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_BIND);
+ iks_insert_cdata(iks_insert(x, "jid"), stream->jid, strlen(stream->jid));
+ break;
+ }
+ default:
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_WARNING, "%s, iq UNEXPECTED <bind>\n", stream->jid);
+ reply = iks_new_error(node, STANZA_ERROR_NOT_ALLOWED);
+ break;
+ }
+
+ return reply;
+}
+
+/**
+ * Handle <iq> message callback
+ * @param stream the stream
+ * @param iq the packet
+ */
+static void on_stream_iq(struct xmpp_stream *stream, iks *iq)
+{
+ struct xmpp_stream_context *context = stream->context;
+ switch(stream->state) {
+ case XSS_CONNECT:
+ case XSS_BIDI: {
+ iks *error = iks_new_error(iq, STANZA_ERROR_NOT_AUTHORIZED);
+ xmpp_stream_stanza_send(stream, error);
+ break;
+ }
+ case XSS_AUTHENTICATED: {
+ iks *cmd = iks_first_tag(iq);
+ if (cmd && !strcmp("bind", iks_name(cmd)) && !strcmp(IKS_NS_XMPP_BIND, iks_find_attrib_soft(cmd, "xmlns"))) {
+ iks *reply = on_iq_set_xmpp_bind(stream, iq);
+ xmpp_stream_stanza_send(stream, reply);
+ } else {
+ iks *error = iks_new_error(iq, STANZA_ERROR_SERVICE_UNAVAILABLE);
+ xmpp_stream_stanza_send(stream, error);
+ }
+ break;
+ }
+ case XSS_RESOURCE_BOUND: {
+ iks *cmd = iks_first_tag(iq);
+ if (cmd && !strcmp("session", iks_name(cmd)) && !strcmp(IKS_NS_XMPP_SESSION, iks_find_attrib_soft(cmd, "xmlns"))) {
+ iks *reply = on_iq_set_xmpp_session(stream, iq);
+ xmpp_stream_stanza_send(stream, reply);
+ } else {
+ iks *error = iks_new_error(iq, STANZA_ERROR_SERVICE_UNAVAILABLE);
+ xmpp_stream_stanza_send(stream, error);
+ }
+ break;
+ }
+ case XSS_READY: {
+ /* client requests */
+ if (context->recv_callback) {
+ context->recv_callback(stream, iq);
+ }
+ break;
+ }
+ case XSS_SHUTDOWN:
+ case XSS_DESTROY:
+ case XSS_ERROR: {
+ iks *error = iks_new_error(iq, STANZA_ERROR_UNEXPECTED_REQUEST);
+ xmpp_stream_stanza_send(stream, error);
+ break;
+ }
+ };
+}
+
+/**
+ * Handle </stream>
+ * @param stream the stream
+ */
+static void on_stream_stop(struct xmpp_stream *stream)
+{
+ if (stream->state != XSS_SHUTDOWN) {
+ iks_send_raw(stream->parser, "</stream:stream>");
+ }
+ stream->state = XSS_DESTROY;
+}
+
+/**
+ * Handle <stream> from a client
+ * @param stream the stream
+ * @param node the stream message
+ */
+static void on_client_stream_start(struct xmpp_stream *stream, iks *node)
+{
+ struct xmpp_stream_context *context = stream->context;
+ const char *to = iks_find_attrib_soft(node, "to");
+ const char *xmlns = iks_find_attrib_soft(node, "xmlns");
+
+ /* to is optional, must be server domain if set */
+ if (!zstr(to) && strcmp(context->domain, to)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, wrong server domain!\n", stream->jid);
+ stream->state = XSS_ERROR;
+ return;
+ }
+
+ /* xmlns = client */
+ if (zstr(xmlns) || strcmp(xmlns, IKS_NS_CLIENT)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, wrong stream namespace!\n", stream->jid);
+ stream->state = XSS_ERROR;
+ return;
+ }
+
+ switch (stream->state) {
+ case XSS_CONNECT:
+ case XSS_BIDI:
+ xmpp_send_client_header_auth(stream);
+ break;
+ case XSS_AUTHENTICATED:
+ /* client bind required */
+ xmpp_stream_new_id(stream);
+ xmpp_send_client_header_bind(stream);
+ break;
+ case XSS_SHUTDOWN:
+ /* strange... I expect IKS_NODE_STOP, this is a workaround. */
+ stream->state = XSS_DESTROY;
+ break;
+ case XSS_RESOURCE_BOUND:
+ case XSS_READY:
+ case XSS_ERROR:
+ case XSS_DESTROY:
+ /* bad state */
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, bad state!\n", stream->jid);
+ stream->state = XSS_ERROR;
+ break;
+ }
+}
+
+/**
+ * Handle <db:result type='valid'>
+ */
+static void on_stream_dialback_result_valid(struct xmpp_stream *stream, iks *node)
+{
+ struct xmpp_stream_context *context = stream->context;
+
+ /* TODO check domain pair and allow access if pending request exists */
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_DEBUG, "%s, valid dialback result\n", stream->jid);
+
+ /* this stream is routable */
+ stream->state = XSS_READY;
+
+ /* add to available streams */
+ switch_mutex_lock(context->streams_mutex);
+ switch_core_hash_insert(context->routes, stream->jid, stream);
+ switch_mutex_unlock(context->streams_mutex);
+
+ if (context->ready_callback) {
+ context->ready_callback(stream);
+ }
+}
+
+/**
+ * Handle <db:result type='valid'>
+ */
+static void on_stream_dialback_result_invalid(struct xmpp_stream *stream, iks *node)
+{
+ /* close stream */
+ stream->state = XSS_ERROR;
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, invalid dialback result!\n", stream->jid);
+}
+
+/**
+ * Handle <db:result type='error'>
+ */
+static void on_stream_dialback_result_error(struct xmpp_stream *stream, iks *node)
+{
+ /* close stream */
+ stream->state = XSS_ERROR;
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, error dialback result!\n", stream->jid);
+}
+
+/**
+ * Handle <db:result>
+ */
+static void on_stream_dialback_result_key(struct xmpp_stream *stream, iks *node)
+{
+ struct xmpp_stream_context *context = stream->context;
+ const char *from = iks_find_attrib_soft(node, "from");
+ const char *to = iks_find_attrib_soft(node, "to");
+ iks *cdata = iks_child(node);
+ iks *reply;
+ const char *dialback_key = NULL;
+
+ if (cdata && iks_type(cdata) == IKS_CDATA) {
+ dialback_key = iks_cdata(cdata);
+ }
+ if (zstr(dialback_key)) {
+ iks *error = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "Missing dialback key");
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, dialback result missing key!\n", stream->jid);
+ iks_send(stream->parser, error);
+ iks_delete(error);
+ stream->state = XSS_ERROR;
+ return;
+ }
+
+ if (zstr(from)) {
+ iks *error = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "Missing from");
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, dialback result missing from!\n", stream->jid);
+ iks_send(stream->parser, error);
+ iks_delete(error);
+ stream->state = XSS_ERROR;
+ return;
+ }
+
+ if (zstr(to)) {
+ iks *error = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "Missing to");
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, dialback result missing to!\n", stream->jid);
+ iks_send(stream->parser, error);
+ iks_delete(error);
+ stream->state = XSS_ERROR;
+ return;
+ }
+
+ if (strcmp(context->domain, to)) {
+ iks *error = iks_new_error(node, STANZA_ERROR_ITEM_NOT_FOUND);
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, invalid domain!\n", stream->jid);
+ iks_send(stream->parser, error);
+ iks_delete(error);
+ stream->state = XSS_ERROR;
+ return;
+ }
+
+ /* TODO validate key */
+ reply = iks_new("db:result");
+ iks_insert_attrib(reply, "from", to);
+ iks_insert_attrib(reply, "to", from);
+ iks_insert_attrib(reply, "type", "valid");
+ iks_send(stream->parser, reply);
+ iks_delete(reply);
+
+ /* this stream is not routable */
+ stream->state = XSS_READY;
+ stream->jid = switch_core_strdup(stream->pool, from);
+
+ if (context->ready_callback) {
+ context->ready_callback(stream);
+ }
+}
+
+/**
+ * Handle <db:result>
+ */
+static void on_stream_dialback_result(struct xmpp_stream *stream, iks *node)
+{
+ const char *type = iks_find_attrib_soft(node, "type");
+
+ if (stream->state == XSS_ERROR || stream->state == XSS_DESTROY) {
+ stream->state = XSS_ERROR;
+ return;
+ }
+
+ if (zstr(type)) {
+ on_stream_dialback_result_key(stream, node);
+ } else if (!strcmp("valid", type)) {
+ on_stream_dialback_result_valid(stream, node);
+ } else if (!strcmp("invalid", type)) {
+ on_stream_dialback_result_invalid(stream, node);
+ } else if (!strcmp("error", type)) {
+ on_stream_dialback_result_error(stream, node);
+ }
+}
+
+/**
+ * Handle <db:verify>
+ */
+static void on_stream_dialback_verify(struct xmpp_stream *stream, iks *node)
+{
+ struct xmpp_stream_context *context = stream->context;
+ const char *from = iks_find_attrib_soft(node, "from");
+ const char *id = iks_find_attrib_soft(node, "id");
+ const char *to = iks_find_attrib_soft(node, "to");
+ iks *cdata = iks_child(node);
+ iks *reply;
+ const char *dialback_key = NULL;
+ char *expected_key = NULL;
+ int valid;
+
+ if (stream->state == XSS_ERROR || stream->state == XSS_DESTROY) {
+ stream->state = XSS_ERROR;
+ return;
+ }
+
+ if (cdata && iks_type(cdata) == IKS_CDATA) {
+ dialback_key = iks_cdata(cdata);
+ }
+ if (zstr(dialback_key)) {
+ iks *error = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "Missing dialback key");
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, dialback verify missing key!\n", stream->jid);
+ iks_send(stream->parser, error);
+ iks_delete(error);
+ return;
+ }
+
+ if (zstr(id)) {
+ iks *error = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "Missing id");
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, dialback verify missing stream ID!\n", stream->jid);
+ iks_send(stream->parser, error);
+ iks_delete(error);
+ return;
+ }
+
+ if (zstr(from)) {
+ iks *error = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "Missing from");
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, dialback verify missing from!\n", stream->jid);
+ iks_send(stream->parser, error);
+ iks_delete(error);
+ return;
+ }
+
+ if (zstr(to)) {
+ iks *error = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "Missing to");
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, dialback verify missing to!\n", stream->jid);
+ iks_send(stream->parser, error);
+ iks_delete(error);
+ return;
+ }
+
+ if (strcmp(context->domain, to)) {
+ iks *error = iks_new_error(node, STANZA_ERROR_ITEM_NOT_FOUND);
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, invalid domain!\n", stream->jid);
+ iks_send(stream->parser, error);
+ iks_delete(error);
+ return;
+ }
+
+ expected_key = iks_server_dialback_key(context->dialback_secret, from, to, id);
+ valid = expected_key && !strcmp(expected_key, dialback_key);
+
+ reply = iks_new("db:verify");
+ iks_insert_attrib(reply, "from", to);
+ iks_insert_attrib(reply, "to", from);
+ iks_insert_attrib(reply, "id", id);
+ iks_insert_attrib(reply, "type", valid ? "valid" : "invalid");
+ iks_send(stream->parser, reply);
+ iks_delete(reply);
+ free(expected_key);
+
+ if (!valid) {
+ /* close the stream */
+ stream->state = XSS_ERROR;
+ }
+}
+
+/**
+ * Handle <stream> from an outbound peer server
+ */
+static void on_outbound_server_stream_start(struct xmpp_stream *stream, iks *node)
+{
+ const char *xmlns = iks_find_attrib_soft(node, "xmlns");
+
+ /* xmlns = server */
+ if (zstr(xmlns) || strcmp(xmlns, IKS_NS_SERVER)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, wrong stream namespace!\n", stream->jid);
+ stream->state = XSS_ERROR;
+ return;
+ }
+
+ switch (stream->state) {
+ case XSS_CONNECT: {
+ /* get stream ID and send dialback */
+ const char *id = iks_find_attrib_soft(node, "id");
+ if (zstr(id)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, missing stream ID!\n", stream->jid);
+ stream->state = XSS_ERROR;
+ return;
+ }
+ xmpp_stream_set_id(stream, id);
+
+ /* send dialback */
+ xmpp_send_dialback_key(stream);
+ break;
+ }
+ case XSS_SHUTDOWN:
+ /* strange... I expect IKS_NODE_STOP, this is a workaround. */
+ stream->state = XSS_DESTROY;
+ break;
+ case XSS_BIDI:
+ case XSS_AUTHENTICATED:
+ case XSS_RESOURCE_BOUND:
+ case XSS_READY:
+ case XSS_ERROR:
+ case XSS_DESTROY:
+ /* bad state */
+ stream->state = XSS_ERROR;
+ break;
+ }
+}
+
+/**
+ * Handle <stream> from an inbound peer server
+ * @param stream the stream
+ * @param node the stream message
+ */
+static void on_inbound_server_stream_start(struct xmpp_stream *stream, iks *node)
+{
+ struct xmpp_stream_context *context = stream->context;
+ const char *to = iks_find_attrib_soft(node, "to");
+ const char *xmlns = iks_find_attrib_soft(node, "xmlns");
+
+ /* to is required, must be server domain */
+ if (zstr(to) || strcmp(context->domain, to)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, wrong server domain!\n", stream->jid);
+ stream->state = XSS_ERROR;
+ return;
+ }
+
+ /* xmlns = server */
+ if (zstr(xmlns) || strcmp(xmlns, IKS_NS_SERVER)) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, wrong stream namespace!\n", stream->jid);
+ stream->state = XSS_ERROR;
+ return;
+ }
+
+ switch (stream->state) {
+ case XSS_CONNECT:
+ xmpp_send_server_header_auth(stream);
+ break;
+ case XSS_BIDI:
+ break;
+ case XSS_AUTHENTICATED: {
+ /* all set */
+ xmpp_send_server_header_features(stream);
+ stream->state = XSS_READY;
+
+ /* add to available streams */
+ switch_mutex_lock(context->streams_mutex);
+ switch_core_hash_insert(context->routes, stream->jid, stream);
+ switch_mutex_unlock(context->streams_mutex);
+
+ if (context->ready_callback) {
+ context->ready_callback(stream);
+ }
+ break;
+ }
+ case XSS_SHUTDOWN:
+ /* strange... I expect IKS_NODE_STOP, this is a workaround. */
+ stream->state = XSS_DESTROY;
+ break;
+ case XSS_RESOURCE_BOUND:
+ case XSS_READY:
+ case XSS_ERROR:
+ case XSS_DESTROY:
+ /* bad state */
+ stream->state = XSS_ERROR;
+ break;
+ }
+}
+
+/**
+ * Handle XML stream callback
+ * @param user_data the xmpp stream
+ * @param type stream type (start/normal/stop/etc)
+ * @param node optional XML node
+ * @return IKS_OK
+ */
+static int on_stream(void *user_data, int type, iks *node)
+{
+ struct xmpp_stream *stream = (struct xmpp_stream *)user_data;
+
+ stream->idle = 0;
+
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_DEBUG, "%s, state = %s, node type = %s\n", stream->jid, xmpp_stream_state_to_string(stream->state), iks_node_type_to_string(type));
+
+ switch(type) {
+ case IKS_NODE_START:
+ /* <stream> */
+ if (node) {
+ if (stream->s2s) {
+ if (stream->incoming) {
+ on_inbound_server_stream_start(stream, node);
+ } else {
+ on_outbound_server_stream_start(stream, node);
+ }
+ } else {
+ on_client_stream_start(stream, node);
+ }
+ } else {
+ stream->state = XSS_ERROR;
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, missing node!\n", stream->jid);
+ }
+ break;
+ case IKS_NODE_NORMAL:
+ /* stanza */
+ if (node) {
+ const char *name = iks_name(node);
+ if (!strcmp("iq", name) || !strcmp("message", name)) {
+ on_stream_iq(stream, node);
+ } else if (!strcmp("presence", name)) {
+ on_stream_presence(stream, node);
+ } else if (!strcmp("auth", name)) {
+ on_stream_auth(stream, node);
+ } else if (!strcmp("bidi", name)) {
+ on_stream_bidi(stream, node);
+ } else if (!strcmp("db:result", name)) {
+ on_stream_dialback_result(stream, node);
+ } else if (!strcmp("db:verify", name)) {
+ on_stream_dialback_verify(stream, node);
+ } else {
+ /* unknown first-level element */
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_DEBUG, "%s, unknown first-level element: %s\n", stream->jid, name);
+ }
+ }
+ break;
+ case IKS_NODE_ERROR:
+ /* <error> */
+ break;
+ case IKS_NODE_STOP:
+ on_stream_stop(stream);
+ break;
+ }
+
+ if (node) {
+ iks_delete(node);
+ }
+
+ return IKS_OK;
+}
+
+/**
+ * Cleanup xmpp stream
+ */
+static void xmpp_stream_destroy(struct xmpp_stream *stream)
+{
+ struct xmpp_stream_context *context = stream->context;
+ switch_memory_pool_t *pool = stream->pool;
+ stream->state = XSS_DESTROY;
+
+ /* remove from available streams */
+ switch_mutex_lock(context->streams_mutex);
+ if (stream->jid && !stream->incoming) {
+ switch_core_hash_delete(context->routes, stream->jid);
+ }
+ if (stream->id) {
+ switch_core_hash_delete(context->streams, stream->id);
+ }
+ switch_mutex_unlock(context->streams_mutex);
+
+ /* close connection */
+ if (stream->parser) {
+ iks_disconnect(stream->parser);
+ iks_parser_delete(stream->parser);
+ }
+
+ if (stream->socket) {
+ switch_socket_shutdown(stream->socket, SWITCH_SHUTDOWN_READWRITE);
+ switch_socket_close(stream->socket);
+ }
+
+ /* flush pending messages */
+ if (stream->msg_queue) {
+ char *msg;
+ while (switch_queue_trypop(stream->msg_queue, (void *)&msg) == SWITCH_STATUS_SUCCESS) {
+ iks_free(msg);
+ }
+ }
+
+ if (context->destroy_callback) {
+ context->destroy_callback(stream);
+ }
+
+ switch_core_destroy_memory_pool(&pool);
+}
+
+/**
+ * @param stream the xmpp stream to check
+ * @return 0 if stream is dead
+ */
+static int xmpp_stream_ready(struct xmpp_stream *stream)
+{
+ return stream->state != XSS_ERROR && stream->state != XSS_DESTROY;
+}
+
+#define KEEP_ALIVE_INTERVAL_NS (60 * 1000 * 1000)
+
+/**
+ * Thread that handles xmpp XML stream
+ * @param thread this thread
+ * @param obj the xmpp stream
+ * @return NULL
+ */
+static void *SWITCH_THREAD_FUNC xmpp_stream_thread(switch_thread_t *thread, void *obj)
+{
+ struct xmpp_stream *stream = (struct xmpp_stream *)obj;
+ struct xmpp_stream_context *context = stream->context;
+ int err_count = 0;
+ switch_time_t last_activity = 0;
+ int ping_id = 1;
+
+ if (stream->incoming) {
+ switch_thread_rwlock_rdlock(context->shutdown_rwlock);
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_DEBUG, "New %s_%s stream\n", stream->s2s ? "s2s" : "c2s", stream->incoming ? "in" : "out");
+
+ if (stream->s2s && !stream->incoming) {
+ xmpp_send_outbound_server_header(stream);
+ }
+
+ while (xmpp_stream_ready(stream)) {
+ char *msg;
+ int result;
+ switch_time_t now = switch_micro_time_now();
+
+ /* read any messages from client */
+ stream->idle = 1;
+ result = iks_recv(stream->parser, 0);
+ switch (result) {
+ case IKS_OK:
+ err_count = 0;
+ break;
+ case IKS_NET_RWERR:
+ case IKS_NET_NOCONN:
+ case IKS_NET_NOSOCK:
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, iks_recv() error = %s, ending session\n", stream->jid, iks_net_error_to_string(result));
+ stream->state = XSS_ERROR;
+ goto done;
+ default:
+ if (err_count++ == 0) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, iks_recv() error = %s\n", stream->jid, iks_net_error_to_string(result));
+ }
+ if (err_count >= 50) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, too many iks_recv() error = %s, ending session\n", stream->jid, iks_net_error_to_string(result));
+ stream->state = XSS_ERROR;
+ goto done;
+ }
+ }
+
+ /* send queued stanzas once stream is authorized for outbound stanzas */
+ if (!stream->s2s || stream->state == XSS_READY) {
+ while (switch_queue_trypop(stream->msg_queue, (void *)&msg) == SWITCH_STATUS_SUCCESS) {
+ if (!stream->s2s || !stream->incoming) {
+ iks_send_raw(stream->parser, msg);
+ } else {
+ /* TODO sent out wrong stream! */
+ }
+ iks_free(msg);
+ stream->idle = 0;
+ }
+ }
+
+ /* check for shutdown */
+ if (stream->state != XSS_DESTROY && context->shutdown && stream->state != XSS_SHUTDOWN) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, detected shutdown\n", stream->jid);
+ iks_send_raw(stream->parser, "</stream:stream>");
+ stream->state = XSS_SHUTDOWN;
+ stream->idle = 0;
+ }
+
+ if (stream->idle) {
+ int fdr = 0;
+
+ /* send keep-alive ping if idle for a long time */
+ if (stream->s2s && !stream->incoming && stream->state == XSS_READY && now - last_activity > KEEP_ALIVE_INTERVAL_NS) {
+ char *ping = switch_mprintf("<iq to=\"%s\" from=\"%s\" type=\"get\" id=\"internal-%d\"><ping xmlns=\""IKS_NS_XMPP_PING"\"/></iq>",
+ stream->jid, stream->context->domain, ping_id++);
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_DEBUG, "%s, keep alive\n", stream->jid);
+ last_activity = now;
+ iks_send_raw(stream->parser, ping);
+ free(ping);
+ }
+
+ switch_poll(stream->pollfd, 1, &fdr, 20000);
+ } else {
+ last_activity = now;
+ switch_os_yield();
+ }
+ }
+
+ done:
+
+ if (stream->incoming) {
+ xmpp_stream_destroy(stream);
+ switch_thread_rwlock_unlock(context->shutdown_rwlock);
+ }
+
+ return NULL;
+}
+
+/**
+ * Initialize the xmpp stream
+ * @param context the stream context
+ * @param stream the stream to initialize
+ * @param pool for this stream
+ * @param address remote address (outbound streams only)
+ * @param port remote port (outbound streams only)
+ * @param s2s true if a server-to-server stream
+ * @param incoming true if incoming stream
+ * @return the stream
+ */
+static struct xmpp_stream *xmpp_stream_init(struct xmpp_stream_context *context, struct xmpp_stream *stream, switch_memory_pool_t *pool, const char *address, int port, int s2s, int incoming)
+{
+ stream->context = context;
+ stream->pool = pool;
+ if (incoming) {
+ xmpp_stream_new_id(stream);
+ }
+ switch_mutex_init(&stream->mutex, SWITCH_MUTEX_NESTED, pool);
+ if (!zstr(address)) {
+ stream->address = switch_core_strdup(pool, address);
+ }
+ if (port > 0) {
+ stream->port = port;
+ }
+ stream->s2s = s2s;
+ stream->incoming = incoming;
+ switch_queue_create(&stream->msg_queue, MAX_QUEUE_LEN, pool);
+
+ if (!stream->s2s) {
+ /* client is already bi-directional */
+ stream->state = XSS_BIDI;
+ }
+
+ /* set up XMPP stream parser */
+ stream->parser = iks_stream_new(stream->s2s ? IKS_NS_SERVER : IKS_NS_CLIENT, stream, on_stream);
+
+ /* enable logging of XMPP stream */
+ iks_set_log_hook(stream->parser, on_stream_log);
+
+ return stream;
+}
+
+/**
+ * Create a new xmpp stream
+ * @param context the stream context
+ * @param pool the memory pool for this stream
+ * @param address remote address (outbound streams only)
+ * @param port remote port (outbound streams only)
+ * @param s2s true if server-to-server stream
+ * @param incoming true if incoming stream
+ * @return the new stream or NULL
+ */
+static struct xmpp_stream *xmpp_stream_create(struct xmpp_stream_context *context, switch_memory_pool_t *pool, const char *address, int port, int s2s, int incoming)
+{
+ struct xmpp_stream *stream = NULL;
+ if (!(stream = switch_core_alloc(pool, sizeof(*stream)))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error\n");
+ return NULL;
+ }
+ return xmpp_stream_init(context, stream, pool, address, port, s2s, incoming);
+}
+
+/**
+ * Thread that handles XMPP XML stream
+ * @param thread this thread
+ * @param obj the XMPP stream
+ * @return NULL
+ */
+static void *SWITCH_THREAD_FUNC xmpp_outbound_stream_thread(switch_thread_t *thread, void *obj)
+{
+ struct xmpp_stream *stream = (struct xmpp_stream *)obj;
+ struct xmpp_stream_context *context = stream->context;
+ switch_socket_t *socket;
+ int warned = 0;
+
+ switch_thread_rwlock_rdlock(context->shutdown_rwlock);
+
+ /* connect to server */
+ while (!context->shutdown) {
+ struct xmpp_stream *new_stream = NULL;
+ switch_memory_pool_t *pool;
+ switch_sockaddr_t *sa;
+
+ if (switch_sockaddr_info_get(&sa, stream->address, SWITCH_UNSPEC, stream->port, 0, stream->pool) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s:%i, failed to get sockaddr info!\n", stream->address, stream->port);
+ goto fail;
+ }
+
+ if (switch_socket_create(&socket, switch_sockaddr_get_family(sa), SOCK_STREAM, SWITCH_PROTO_TCP, stream->pool) != SWITCH_STATUS_SUCCESS) {
+ if (!warned) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_ERROR, "%s:%i, failed to create socket!\n", stream->address, stream->port);
+ }
+ goto sock_fail;
+ }
+
+ switch_socket_opt_set(socket, SWITCH_SO_KEEPALIVE, 1);
+ switch_socket_opt_set(socket, SWITCH_SO_TCP_NODELAY, 1);
+
+ if (switch_socket_connect(socket, sa) != SWITCH_STATUS_SUCCESS) {
+ if (!warned) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_ERROR, "%s:%i, Socket Error!\n", stream->address, stream->port);
+ }
+ goto sock_fail;
+ }
+
+ if (warned) {
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_ERROR, "%s:%i, connected!\n", stream->address, stream->port);
+ warned = 0;
+ }
+
+ /* run the stream thread */
+ xmpp_stream_set_socket(stream, socket);
+ xmpp_stream_thread(thread, stream);
+
+ /* re-establish connection if not shutdown */
+ if (!context->shutdown) {
+ /* create new stream for reconnection */
+ switch_core_new_memory_pool(&pool);
+ new_stream = xmpp_stream_create(stream->context, pool, stream->address, stream->port, 1, 0);
+ new_stream->jid = switch_core_strdup(pool, stream->jid);
+ xmpp_stream_destroy(stream);
+ stream = new_stream;
+
+ switch_yield(1000 * 1000); /* 1000 ms */
+ continue;
+ }
+ break;
+
+ sock_fail:
+ if (socket) {
+ switch_socket_close(socket);
+ socket = NULL;
+ }
+ if (!warned) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Error! Could not connect to %s:%i\n", stream->address, stream->port);
+ warned = 1;
+ }
+ switch_yield(1000 * 1000); /* 1000 ms */
+ }
+
+ fail:
+
+ xmpp_stream_destroy(stream);
+
+ switch_thread_rwlock_unlock(context->shutdown_rwlock);
+ return NULL;
+}
+
+/**
+ * Set the id for this stream
+ * @param stream
+ * @param id
+ */
+static void xmpp_stream_set_id(struct xmpp_stream *stream, const char *id)
+{
+ struct xmpp_stream_context *context = stream->context;
+ if (!zstr(stream->id)) {
+ switch_mutex_lock(context->streams_mutex);
+ switch_core_hash_delete(context->streams, stream->id);
+ switch_mutex_unlock(context->streams_mutex);
+ }
+ if (!zstr(id)) {
+ stream->id = switch_core_strdup(stream->pool, id);
+ switch_mutex_lock(context->streams_mutex);
+ switch_core_hash_insert(context->streams, stream->id, stream);
+ switch_mutex_unlock(context->streams_mutex);
+ } else {
+ stream->id = NULL;
+ }
+}
+
+/**
+ * Destroy the listener
+ * @param server the server
+ */
+static void xmpp_listener_destroy(struct xmpp_listener *listener)
+{
+ switch_memory_pool_t *pool = listener->pool;
+
+ /* shutdown socket */
+ if (listener->socket) {
+ switch_socket_shutdown(listener->socket, SWITCH_SHUTDOWN_READWRITE);
+ switch_socket_close(listener->socket);
+ }
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "xmpp listener %s:%u closed\n", listener->addr, listener->port);
+ switch_core_destroy_memory_pool(&pool);
+}
+
+/**
+ * Open a new XMPP stream with a peer server
+ * @param peer_domain of server - if not set, address is used
+ * @param peer_address of server - if not set, domain is used
+ * @param peer_port of server - if not set default port is used
+ */
+switch_status_t xmpp_stream_context_connect(struct xmpp_stream_context *context, const char *peer_domain, const char *peer_address, int peer_port)
+{
+ struct xmpp_stream *stream;
+ switch_memory_pool_t *pool;
+ switch_thread_t *thread;
+ switch_threadattr_t *thd_attr = NULL;
+
+ if (peer_port <= 0) {
+ peer_port = IKS_JABBER_SERVER_PORT;
+ }
+
+ if (zstr(peer_address)) {
+ peer_address = peer_domain;
+ } else if (zstr(peer_domain)) {
+ peer_domain = peer_address;
+ }
+
+ /* start outbound stream thread */
+ switch_core_new_memory_pool(&pool);
+ stream = xmpp_stream_create(context, pool, peer_address, peer_port, 1, 0);
+ stream->jid = switch_core_strdup(pool, peer_domain);
+ switch_threadattr_create(&thd_attr, pool);
+ switch_threadattr_detach_set(thd_attr, 1);
+ switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+ switch_thread_create(&thread, thd_attr, xmpp_outbound_stream_thread, stream, pool);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Thread that listens for new XMPP connections
+ * @param thread this thread
+ * @param obj the listener
+ * @return NULL
+ */
+static void *SWITCH_THREAD_FUNC xmpp_listener_thread(switch_thread_t *thread, void *obj)
+{
+ struct xmpp_listener *listener = (struct xmpp_listener *)obj;
+ struct xmpp_stream_context *context = listener->context;
+ switch_memory_pool_t *pool = NULL;
+ uint32_t errs = 0;
+ int warned = 0;
+
+ switch_thread_rwlock_rdlock(context->shutdown_rwlock);
+
+ /* bind to XMPP port */
+ while (!context->shutdown) {
+ switch_status_t rv;
+ switch_sockaddr_t *sa;
+ rv = switch_sockaddr_info_get(&sa, listener->addr, SWITCH_UNSPEC, listener->port, 0, listener->pool);
+ if (rv)
+ goto fail;
+ rv = switch_socket_create(&listener->socket, switch_sockaddr_get_family(sa), SOCK_STREAM, SWITCH_PROTO_TCP, listener->pool);
+ if (rv)
+ goto sock_fail;
+ rv = switch_socket_opt_set(listener->socket, SWITCH_SO_REUSEADDR, 1);
+ if (rv)
+ goto sock_fail;
+#ifdef WIN32
+ /* Enable dual-stack listening on Windows (if the listening address is IPv6), it's default on Linux */
+ if (switch_sockaddr_get_family(sa) == AF_INET6) {
+ rv = switch_socket_opt_set(listener->socket, 16384, 0);
+ if (rv) goto sock_fail;
+ }
+#endif
+ rv = switch_socket_bind(listener->socket, sa);
+ if (rv)
+ goto sock_fail;
+ rv = switch_socket_listen(listener->socket, 5);
+ if (rv)
+ goto sock_fail;
+
+ rv = switch_socket_create_pollset(&listener->read_pollfd, listener->socket, SWITCH_POLLIN | SWITCH_POLLERR, listener->pool);
+ if (rv) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Create pollset for %s listener socket %s:%u error!\n", listener->s2s ? "s2s" : "c2s", listener->addr, listener->port);
+ goto sock_fail;
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "xmpp %s listener bound to %s:%u\n", listener->s2s ? "s2s" : "c2s", listener->addr, listener->port);
+
+ break;
+ sock_fail:
+ if (listener->socket) {
+ switch_socket_close(listener->socket);
+ listener->socket = NULL;
+ }
+ if (!warned) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Error! xmpp %s listener could not bind to %s:%u\n", listener->s2s ? "s2s" : "c2s", listener->addr, listener->port);
+ warned = 1;
+ }
+ switch_yield(1000 * 100); /* 100 ms */
+ }
+
+ /* Listen for XMPP client connections */
+ while (!context->shutdown) {
+ switch_socket_t *socket = NULL;
+ switch_status_t rv;
+ int32_t fdr;
+
+ if (pool == NULL && switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create memory pool for new client connection!\n");
+ goto fail;
+ }
+
+ /* is there a new connection? */
+ rv = switch_poll(listener->read_pollfd, 1, &fdr, 1000 * 1000 /* 1000 ms */);
+ if (rv != SWITCH_STATUS_SUCCESS) {
+ continue;
+ }
+
+ /* accept the connection */
+ if ((rv = switch_socket_accept(&socket, listener->socket, pool))) {
+ if (context->shutdown) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Shutting down xmpp listener\n");
+ goto end;
+ } else {
+ /* I wish we could use strerror_r here but its not defined everywhere =/ */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Accept connection error [%s]\n", strerror(errno));
+ if (++errs > 100) {
+ goto end;
+ }
+ }
+ } else { /* got a new connection */
+ switch_thread_t *thread;
+ switch_threadattr_t *thd_attr = NULL;
+ struct xmpp_stream *stream;
+
+ errs = 0;
+
+ /* check if connection is allowed */
+ if (listener->acl) {
+ switch_sockaddr_t *sa = NULL;
+ int allowed = 0;
+ char remote_ip[50] = { 0 };
+ if (switch_socket_addr_get(&sa, SWITCH_TRUE, socket) == SWITCH_STATUS_SUCCESS && sa) {
+ switch_get_addr(remote_ip, sizeof(remote_ip), sa);
+ allowed = switch_check_network_list_ip(remote_ip, listener->acl);
+ }
+ if (!allowed) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "ACL %s denies access to %s.\n", listener->acl, remote_ip);
+ switch_socket_shutdown(socket, SWITCH_SHUTDOWN_READWRITE);
+ switch_socket_close(socket);
+ continue;
+ }
+ }
+
+ /* start connection thread */
+ if (!(stream = xmpp_stream_create(context, pool, NULL, 0, listener->s2s, 1))) {
+ switch_socket_shutdown(socket, SWITCH_SHUTDOWN_READWRITE);
+ switch_socket_close(socket);
+ break;
+ }
+ xmpp_stream_set_socket(stream, socket);
+ pool = NULL; /* connection now owns the pool */
+ switch_threadattr_create(&thd_attr, stream->pool);
+ switch_threadattr_detach_set(thd_attr, 1);
+ switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+ switch_thread_create(&thread, thd_attr, xmpp_stream_thread, stream, stream->pool);
+ }
+ }
+
+ end:
+
+ if (pool) {
+ switch_core_destroy_memory_pool(&pool);
+ }
+
+ fail:
+
+ xmpp_listener_destroy(listener);
+
+ switch_thread_rwlock_unlock(context->shutdown_rwlock);
+ return NULL;
+}
+
+/**
+ * Add a new socket to listen for XMPP client/server connections.
+ * @param context the XMPP context
+ * @param addr the IP address
+ * @param port the port
+ * @param is_s2s true if s2s
+ * @param acl name of optional access control list
+ * @return SWITCH_STATUS_SUCCESS if successful
+ */
+switch_status_t xmpp_stream_context_listen(struct xmpp_stream_context *context, const char *addr, int port, int is_s2s, const char *acl)
+{
+ switch_memory_pool_t *pool;
+ struct xmpp_listener *new_listener = NULL;
+ switch_thread_t *thread;
+ switch_threadattr_t *thd_attr = NULL;
+
+ if (zstr(addr)) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ switch_core_new_memory_pool(&pool);
+ new_listener = switch_core_alloc(pool, sizeof(*new_listener));
+ new_listener->pool = pool;
+ new_listener->addr = switch_core_strdup(pool, addr);
+ if (!zstr(acl)) {
+ new_listener->acl = switch_core_strdup(pool, acl);
+ }
+
+ new_listener->s2s = is_s2s;
+ if (port <= 0) {
+ new_listener->port = is_s2s ? IKS_JABBER_SERVER_PORT : IKS_JABBER_PORT;
+ } else {
+ new_listener->port = port;
+ }
+ new_listener->context = context;
+
+ /* start the server thread */
+ switch_threadattr_create(&thd_attr, pool);
+ switch_threadattr_detach_set(thd_attr, 1);
+ switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+ switch_thread_create(&thread, thd_attr, xmpp_listener_thread, new_listener, pool);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/**
+ * Queue a message for delivery
+ */
+void xmpp_stream_context_send(struct xmpp_stream_context *context, const char *jid, iks *msg)
+{
+ if (!zstr(jid)) {
+ if (msg) {
+ struct xmpp_stream *stream;
+ switch_mutex_lock(context->streams_mutex);
+ stream = switch_core_hash_find(context->routes, jid);
+ if (stream) {
+ char *raw = iks_string(NULL, msg);
+ if (switch_queue_trypush(stream->msg_queue, raw) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "failed to deliver outbound message via %s!\n", jid);
+ iks_free(raw);
+ }
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s stream is gone\n", jid);
+ /* TODO automatically open connection if valid domain JID? */
+ }
+ switch_mutex_unlock(context->streams_mutex);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "missing message\n");
+ }
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "missing stream JID\n");
+ }
+}
+
+/**
+ * Dump xmpp stream stats
+ */
+void xmpp_stream_context_dump(struct xmpp_stream_context *context, switch_stream_handle_t *stream)
+{
+ switch_hash_index_t *hi;
+ switch_mutex_lock(context->streams_mutex);
+ stream->write_function(stream, "\nACTIVE STREAMS\n");
+ for (hi = switch_core_hash_first(context->streams); hi; hi = switch_core_hash_next(hi)) {
+ struct xmpp_stream *s = NULL;
+ const void *key;
+ void *val;
+ switch_core_hash_this(hi, &key, NULL, &val);
+ s = (struct xmpp_stream *)val;
+ switch_assert(s);
+ stream->write_function(stream, " TYPE='%s_%s',ID='%s',JID='%s',STATE='%s'\n", s->s2s ? "s2s" : "c2s", s->incoming ? "in" : "out", s->id, s->jid, xmpp_stream_state_to_string(s->state));
+ }
+ switch_mutex_unlock(context->streams_mutex);
+}
+
+/**
+ * Create a new XMPP stream context
+ * @param domain for new streams
+ * @param domain_secret domain shared secret for server dialback
+ * @param ready callback function when new stream is ready
+ * @param recv callback function when a new stanza is received
+ * @param destroy callback function when a stream is destroyed
+ * @return the context
+ */
+struct xmpp_stream_context *xmpp_stream_context_create(const char *domain, const char *domain_secret, xmpp_stream_ready_callback ready, xmpp_stream_recv_callback recv, xmpp_stream_destroy_callback destroy)
+{
+ switch_memory_pool_t *pool;
+ struct xmpp_stream_context *context;
+
+ switch_core_new_memory_pool(&pool);
+ context = switch_core_alloc(pool, sizeof(*context));
+ context->pool = pool;
+ switch_mutex_init(&context->streams_mutex, SWITCH_MUTEX_NESTED, context->pool);
+ switch_core_hash_init(&context->routes, context->pool);
+ switch_core_hash_init(&context->streams, context->pool);
+ context->dialback_secret = switch_core_strdup(context->pool, domain_secret);
+ context->ready_callback = ready;
+ context->destroy_callback = destroy;
+ context->recv_callback = recv;
+ context->shutdown = 0;
+ context->domain = switch_core_strdup(context->pool, domain);
+ switch_thread_rwlock_create(&context->shutdown_rwlock, context->pool);
+ switch_core_hash_init(&context->users, context->pool);
+
+ return context;
+}
+
+/**
+ * Add an authorized user
+ * @param context the context to add user to
+ * @param user the username
+ * @param password the password
+ */
+void xmpp_stream_context_add_user(struct xmpp_stream_context *context, const char *user, const char *password)
+{
+ switch_core_hash_insert(context->users, user, switch_core_strdup(context->pool, password));
+}
+
+/**
+ * Destroy an XMPP stream context. All open streams are closed.
+ * @param context to destroy
+ */
+void xmpp_stream_context_destroy(struct xmpp_stream_context *context)
+{
+ switch_memory_pool_t *pool;
+ context->shutdown = 1;
+ /* wait for threads to finish */
+ switch_thread_rwlock_wrlock(context->shutdown_rwlock);
+ pool = context->pool;
+ switch_core_destroy_memory_pool(&pool);
+}
+
+/**
+ * @param stream
+ * @return true if server-to-server stream
+ */
+int xmpp_stream_is_s2s(struct xmpp_stream *stream)
+{
+ return stream->s2s;
+}
+
+/**
+ * @param stream
+ * @return true if incoming stream
+ */
+int xmpp_stream_is_incoming(struct xmpp_stream *stream)
+{
+ return stream->incoming;
+}
+
+/**
+ * @param stream
+ * @return the stream JID
+ */
+const char *xmpp_stream_get_jid(struct xmpp_stream *stream)
+{
+ return stream->jid;
+}
+
+/**
+ * Set private data for this stream
+ */
+void xmpp_stream_set_private(struct xmpp_stream *stream, void *user_private)
+{
+ stream->user_private = user_private;
+}
+
+/**
+ * Get private data for this stream
+ */
+void *xmpp_stream_get_private(struct xmpp_stream *stream)
+{
+ return stream->user_private;
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
--- /dev/null
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * xmpp_streams.h -- XMPP in/out s2s and in c2s streams
+ *
+ */
+#ifndef XMPP_STREAMS_H
+#define XMPP_STREAMS_H
+
+struct xmpp_stream;
+struct xmpp_stream_context;
+
+typedef void (* xmpp_stream_ready_callback)(struct xmpp_stream *stream);
+typedef void (* xmpp_stream_recv_callback)(struct xmpp_stream *stream, iks *stanza);
+typedef void (* xmpp_stream_destroy_callback)(struct xmpp_stream *stream);
+
+extern struct xmpp_stream_context *xmpp_stream_context_create(const char *domain, const char *domain_secret, xmpp_stream_ready_callback ready, xmpp_stream_recv_callback recv, xmpp_stream_destroy_callback destroy);
+extern void xmpp_stream_context_add_user(struct xmpp_stream_context *context, const char *user, const char *password);
+extern void xmpp_stream_context_dump(struct xmpp_stream_context *context, switch_stream_handle_t *stream);
+extern void xmpp_stream_context_destroy(struct xmpp_stream_context *context);
+extern void xmpp_stream_context_send(struct xmpp_stream_context *context, const char *jid, iks *stanza);
+
+extern switch_status_t xmpp_stream_context_listen(struct xmpp_stream_context *context, const char *addr, int port, int is_s2s, const char *acl);
+extern switch_status_t xmpp_stream_context_connect(struct xmpp_stream_context *context, const char *peer_domain, const char *peer_address, int peer_port);
+
+extern int xmpp_stream_is_s2s(struct xmpp_stream *stream);
+extern int xmpp_stream_is_incoming(struct xmpp_stream *stream);
+extern const char *xmpp_stream_get_jid(struct xmpp_stream *stream);
+extern void xmpp_stream_set_private(struct xmpp_stream *stream, void *user_private);
+extern void *xmpp_stream_get_private(struct xmpp_stream *stream);
+
+#endif
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */