diff --git a/build/config/ax_compiler_vendor.m4 b/build/config/ax_compiler_vendor.m4 index a24a58da0f..b506b78aae 100644 --- a/build/config/ax_compiler_vendor.m4 +++ b/build/config/ax_compiler_vendor.m4 @@ -3,7 +3,7 @@ AC_DEFUN([AX_COMPILER_VENDOR], AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, [ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=unknown # note: don't check for gcc first since some other compilers define __GNUC__ - for ventest in intel:__ICC,__ECC,__INTEL_COMPILER ibm:__xlc__,__xlC__,__IBMC__,__IBMCPP__ gnu:__GNUC__ sun:__SUNPRO_C,__SUNPRO_CC hp:__HP_cc,__HP_aCC dec:__DECC,__DECCXX,__DECC_VER,__DECCXX_VER borland:__BORLANDC__,__TURBOC__ comeau:__COMO__ cray:_CRAYC kai:__KCC lcc:__LCC__ metrowerks:__MWERKS__ sgi:__sgi,sgi microsoft:_MSC_VER watcom:__WATCOMC__ portland:__PGI; do + for ventest in intel:__ICC,__ECC,__INTEL_COMPILER ibm:__xlc__,__xlC__,__IBMC__,__IBMCPP__ clang:__clang__ gnu:__GNUC__ sun:__SUNPRO_C,__SUNPRO_CC hp:__HP_cc,__HP_aCC dec:__DECC,__DECCXX,__DECC_VER,__DECCXX_VER borland:__BORLANDC__,__TURBOC__ comeau:__COMO__ cray:_CRAYC kai:__KCC lcc:__LCC__ metrowerks:__MWERKS__ sgi:__sgi,sgi microsoft:_MSC_VER watcom:__WATCOMC__ portland:__PGI; do vencpp="defined("`echo $ventest | cut -d: -f2 | sed 's/,/) || defined(/g'`")" AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[ #if !($vencpp) diff --git a/build/modules.conf.in b/build/modules.conf.in index d60b1211ef..3f77b125b5 100644 --- a/build/modules.conf.in +++ b/build/modules.conf.in @@ -82,7 +82,6 @@ dialplans/mod_dialplan_xml #endpoints/mod_dingaling #endpoints/mod_gsmopen #endpoints/mod_h323 -#endpoints/mod_html5 #endpoints/mod_khomp endpoints/mod_loopback #endpoints/mod_opal @@ -152,4 +151,4 @@ xml_int/mod_xml_scgi #../../libs/openzap/mod_openzap ## Experimental Modules (don't cry if they're broken) -#../../contrib/mod/xml_int/mod_xml_odbc \ No newline at end of file +#../../contrib/mod/xml_int/mod_xml_odbc diff --git a/build/next-release.txt b/build/next-release.txt index c11470058f..fd9d1a5aca 100644 --- a/build/next-release.txt +++ b/build/next-release.txt @@ -1 +1 @@ -1.2.11 +1.2.14 diff --git a/configure.in b/configure.in index b1e7e5f9cb..b3d3cc6509 100644 --- a/configure.in +++ b/configure.in @@ -3,10 +3,10 @@ # Must change all of the below together # For a release, set revision for that tagged release as well and uncomment -AC_INIT([freeswitch], [1.2.13], bugs@freeswitch.org) +AC_INIT([freeswitch], [1.2.14], bugs@freeswitch.org) AC_SUBST(SWITCH_VERSION_MAJOR, [1]) AC_SUBST(SWITCH_VERSION_MINOR, [2]) -AC_SUBST(SWITCH_VERSION_MICRO, [13]) +AC_SUBST(SWITCH_VERSION_MICRO, [14]) AC_SUBST(SWITCH_VERSION_REVISION, []) AC_SUBST(SWITCH_VERSION_REVISION_HUMAN, []) @@ -167,11 +167,19 @@ SWITCH_AM_LDFLAGS="-lm" #set SOLINK variable based on compiler and host if test "x${ax_cv_c_compiler_vendor}" = "xsun" ; then SOLINK="-Bdynamic -dy -G" -elif test "x${ax_cv_c_compiler_vendor}" = "xgnu" ; then +elif test "x${ax_cv_c_compiler_vendor}" = "xclang" ; then case "$host" in - *darwin12.*|*darwin11.*|*darwin10.*) + *darwin*) SOLINK="-dynamic -force-flat-namespace" ;; + *) + AC_ERROR([Please update configure.in with SOLINK values for your compiler]) + ;; + + esac +elif test "x${ax_cv_c_compiler_vendor}" = "xgnu" ; then + case "$host" in +# older Xcode test for darwin, Xcode 4/5 use clang above *darwin*) SOLINK="-dynamic -bundle -force-flat-namespace" ;; @@ -185,7 +193,6 @@ elif test "x${ax_cv_c_compiler_vendor}" = "xgnu" ; then else AC_ERROR([Please update configure.in with SOLINK values for your compiler]) fi - # set DYNAMIC_LIB_EXTEN # we should really be using libtool so we don't need to do this case "$host" in @@ -716,6 +723,17 @@ if test x"$ac_cv_gcc_declaration_after_statement" = xyes; then fi CFLAGS="$saved_CFLAGS" +if test "x${ax_cv_c_compiler_vendor}" = "xclang" ; then + # Next check added for Xcode 5 and systems with clang 5 llvm 3.3 or above, extended offset must be off + AC_CACHE_CHECK([whether compiler supports -Wextended-offsetof], [ac_cv_clang_extended_offsetof], [ + AC_TRY_COMPILE([],[return 0;],[ac_cv_clang_extended_offsetof=yes],[ac_cv_clang_extended_offsetof=no]) + ]) + AC_MSG_RESULT($ac_cv_clang_extended_offsetof) + if test x"$ac_cv_clang_extended_offsetof" = xyes; then + APR_ADDTO(CFLAGS, -Wno-extended-offsetof) + fi +fi + # Tested and fixed lot of modules, but some are untested. Will be added back when the core team decide it ready # Untested modules : mod_osp mod_soundtouch mod_sangoma_codec mod_dingaling mod_opal mod_skypopen mod_h323 mod_khomp # mod_unimrcp mod_cepstral mod_erlang_event mod_snmp mod_perl mod_java mod_managed diff --git a/debian/.gitignore b/debian/.gitignore index 81fc750bf9..896d998162 100644 --- a/debian/.gitignore +++ b/debian/.gitignore @@ -5,6 +5,7 @@ /files /modules.conf /modules_.conf +/freeswitch-all.* /freeswitch-autotools.install /freeswitch-mod-*.install /freeswitch-conf-*.install diff --git a/debian/bootstrap.sh b/debian/bootstrap.sh index 5843872904..b9776f14ac 100755 --- a/debian/bootstrap.sh +++ b/debian/bootstrap.sh @@ -6,7 +6,7 @@ mod_dir="../src/mod" conf_dir="../conf" lang_dir="../conf/vanilla/lang" fs_description="FreeSWITCH is a scalable open source cross-platform telephony platform designed to route and interconnect popular communication protocols using audio, video, text or any other form of media." -mod_build_depends="." +mod_build_depends="." mod_depends="." mod_recommends="." mod_suggests="." supported_distros="squeeze wheezy jessie sid" avoid_mods=( applications/mod_limit @@ -204,6 +204,32 @@ EOF print_core_control () { cat <= 1.0.8), + freeswitch-sounds-en-us-callie (>= 1.0.25) | freeswitch-sounds, + $(debian_wrap "${mod_depends}") +Recommends: + $(debian_wrap "${mod_recommends}") +Suggests: freeswitch-all-dbg, + $(debian_wrap "${mod_suggests}") +Description: Cross-Platform Scalable Multi-Protocol Soft Switch + $(debian_wrap "${fs_description}") + . + This package contains FreeSWITCH and all modules and extras. + Package: freeswitch Architecture: any Depends: \${shlibs:Depends}, \${perl:Depends}, \${misc:Depends}, @@ -616,6 +642,16 @@ Description: Cross-Platform Scalable Multi-Protocol Soft Switch This is a metapackage which depends on all mod_say languages for FreeSWITCH. +Package: freeswitch-all-dbg +Section: debug +Priority: extra +Architecture: any +Depends: \${misc:Depends}, freeswitch (= \${binary:Version}) +Description: debugging symbols for FreeSWITCH + $(debian_wrap "${fs_description}") + . + This package contains debugging symbols for FreeSWITCH. + Package: freeswitch-dbg Section: debug Priority: extra @@ -890,6 +926,7 @@ gencontrol_per_cat () { geninstall_per_mod () { local f=freeswitch-${module_name//_/-}.install (print_edit_warning; print_mod_install "$module_name") > $f + print_mod_install "$module_name" >> freeswitch-all.install test -f $f.tmpl && cat $f.tmpl >> $f } @@ -912,6 +949,7 @@ genconf () { local p=freeswitch-conf-${conf//_/-} local f=$p.install (print_edit_warning; print_conf_install) > $f + print_conf_install >> freeswitch-all.install test -f $f.tmpl && cat $f.tmpl >> $f local f=$p.lintian-overrides (print_edit_warning; print_conf_overrides "$p") > $f @@ -923,26 +961,50 @@ genlang () { local p=freeswitch-lang-${lang//_/-} local f=$p.install (print_edit_warning; print_lang_install) > $f + print_lang_install >> freeswitch-all.install test -f $f.tmpl && cat $f.tmpl >> $f local f=$p.lintian-overrides (print_edit_warning; print_lang_overrides "$p") > $f test -f $f.tmpl && cat $f.tmpl >> $f } -accumulate_build_depends () { +accumulate_mod_deps () { local x="" + # build-depends if [ -n "$(eval echo \$build_depends_$codename)" ]; then x="$(eval echo \$build_depends_$codename)" - else - x="${build_depends}" - fi + else x="${build_depends}"; fi if [ -n "$x" ]; then if [ ! "$mod_build_depends" = "." ]; then mod_build_depends="${mod_build_depends}, ${x}" - else - mod_build_depends="${x}" - fi - fi + else mod_build_depends="${x}"; fi; fi + # depends + if [ -n "$(eval echo \$depends_$codename)" ]; then + x="$(eval echo \$depends_$codename)" + else x="${depends}"; fi + x="$(echo "$x" | sed 's/, \?/\n/g' | grep -v '^freeswitch' | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" + if [ -n "$x" ]; then + if [ ! "$mod_depends" = "." ]; then + mod_depends="${mod_depends}, ${x}" + else mod_depends="${x}"; fi; fi + # recommends + if [ -n "$(eval echo \$recommends_$codename)" ]; then + x="$(eval echo \$recommends_$codename)" + else x="${recommends}"; fi + x="$(echo "$x" | sed 's/, \?/\n/g' | grep -v '^freeswitch' | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" + if [ -n "$x" ]; then + if [ ! "$mod_recommends" = "." ]; then + mod_recommends="${mod_recommends}, ${x}" + else mod_recommends="${x}"; fi; fi + # suggests + if [ -n "$(eval echo \$suggests_$codename)" ]; then + x="$(eval echo \$suggests_$codename)" + else x="${suggests}"; fi + x="$(echo "$x" | sed 's/, \?/\n/g' | grep -v '^freeswitch' | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" + if [ -n "$x" ]; then + if [ ! "$mod_suggests" = "." ]; then + mod_suggests="${mod_suggests}, ${x}" + else mod_suggests="${x}"; fi; fi } genmodctl_new_mod () { @@ -1108,10 +1170,13 @@ echo "Generating control-modules.gen as sanity check..." >&2 map_modules ':' 'genmodctl_cat' 'genmodctl_mod' \ ) > control-modules.gen -echo "Accumulating build dependencies from modules..." >&2 -map_modules 'mod_filter' '' 'accumulate_build_depends' +echo "Accumulating dependencies from modules..." >&2 +map_modules 'mod_filter' '' 'accumulate_mod_deps' echo "Generating debian/..." >&2 > control +> freeswitch-all.install +(print_edit_warning; print_mod_overrides "freeswitch-all") \ + > freeswitch-all.lintian-overrides (print_edit_warning; print_source_control; print_core_control) >> control echo "Generating debian/ (conf)..." >&2 (echo "### conf"; echo) >> control @@ -1125,6 +1190,25 @@ print_edit_warning > modules_.conf map_modules "mod_filter" \ "gencontrol_per_cat genmodules_per_cat" \ "gencontrol_per_mod geninstall_per_mod genoverrides_per_mod genmodules_per_mod" +echo "Generating debian/ (-all package)..." >&2 +grep -e '^Package:' control | grep -v '^freeswitch-all$' | while xread l; do + m="${l#*: }" + f=$m.install + if [ -s $f ]; then + grep -v '^##\|^$' $f | while xread x; do + if ! grep -e "$x" freeswitch-all.install >/dev/null; then + printf '%s\n' "$x" >> freeswitch-all.install + fi + done + fi +done +for x in postinst postrm preinst prerm; do + cp -a freeswitch.$x freeswitch-all.$x +done +cp -a freeswitch-doc.docs freeswitch-all.docs +#cp -a freeswitch-systemd.freeswitch.service freeswitch-all.freeswitch.service +cp -a freeswitch-sysvinit.freeswitch.default freeswitch-all.freeswitch.default +cp -a freeswitch-sysvinit.freeswitch.init freeswitch-all.freeswitch.init echo "Generating additional lintian overrides..." >&2 grep -e '^Package:' control | while xread l; do diff --git a/debian/control-modules b/debian/control-modules index 919382ce93..61f0b33a80 100644 --- a/debian/control-modules +++ b/debian/control-modules @@ -229,7 +229,7 @@ Description: Voicemail detection Module: applications/mod_voicemail Description: Voicemail This module provides a voicemail system. -Depends: mail-transport-agent +Depends: ssmtp | mail-transport-agent Module: applications/mod_voicemail_ivr Description: Voicemail IVR @@ -395,10 +395,6 @@ Description: mod_h323 Adds mod_h323. Build-Depends: libopenh323-dev | libh323plus-dev, libpt-dev -Module: endpoints/mod_html5 -Description: HTML5 endpoint module - This module adds support for HTML5 technologies such as WebRTC. - Module: endpoints/mod_khomp Description: mod_khomp Adds mod_khomp. diff --git a/debian/copyright b/debian/copyright index fcea146b7a..d7ea35053f 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1765,7 +1765,6 @@ License: GPL-2+ Files: src/mod/endpoints/mod_rtmp/rtmp.c src/mod/endpoints/mod_rtmp/mod_rtmp.[ch] src/mod/endpoints/mod_rtmp/rtmp_sig.c - src/mod/endpoints/mod_html5/mod_html5.c src/mod/endpoints/mod_rtmp/rtmp_tcp.c Copyright: 2011-2012, Barracuda Networks Inc. License: MPL-1.1 diff --git a/debian/freeswitch-mod-perl.install.tmpl b/debian/freeswitch-mod-perl.install.tmpl new file mode 100644 index 0000000000..5b2fe712f3 --- /dev/null +++ b/debian/freeswitch-mod-perl.install.tmpl @@ -0,0 +1,2 @@ +debian/tmp/usr/perl/freeswitch.pm /usr/lib/perl5 +debian/tmp/usr/perl/freeswitch.so /usr/lib/perl5/auto/freeswitch diff --git a/debian/rules b/debian/rules index 7da66a8eaa..7d7d50fd71 100755 --- a/debian/rules +++ b/debian/rules @@ -104,6 +104,7 @@ override_dh_auto_install: override_dh_installinit: dh_installinit -pfreeswitch-sysvinit --name=freeswitch + dh_installinit -pfreeswitch-all --name=freeswitch debian-bootstrap: debian/.stamp-bootstrap debian/.stamp-bootstrap: diff --git a/libs/apr-util/xml/expat/buildconf.sh b/libs/apr-util/xml/expat/buildconf.sh index 1f5789dc07..9050a0282a 100755 --- a/libs/apr-util/xml/expat/buildconf.sh +++ b/libs/apr-util/xml/expat/buildconf.sh @@ -28,26 +28,34 @@ rm -rf aclocal.m4 libtool.m4 ltsugar.m4 autom4te*.cache $libtoolize --copy --automake # -# Build aclocal.m4 from libtool's libtool.m4 +# find libtool.m4 # -if [ -f libtool.m4 ]; then - ltfile=libtool.m4 -else +if [ ! -f libtool.m4 ]; then ltpath=`dirname $libtoolize` ltfile=${LIBTOOL_M4-`cd $ltpath/../share/aclocal ; pwd`/libtool.m4} + if [ -f $ltfile ]; then + echo "libtool.m4 found at $ltfile" + cp $ltfile libtool.m4 + else + echo "libtool.m4 not found - aborting!" + exit 1 + fi fi -echo "Incorporating $ltfile into aclocal.m4 ..." + +# +# Build aclocal.m4 from libtool's m4 files +# echo "dnl THIS FILE IS AUTOMATICALLY GENERATED BY buildconf.sh" > aclocal.m4 echo "dnl edits here will be lost" >> aclocal.m4 -cat $ltfile >> aclocal.m4 -if [ -f ltsugar.m4 ]; then - echo "Incorporating ltsugar.m4 into aclocal.m4 ..." - cat ltsugar.m4 >> aclocal.m4 -fi - -# Clean up again -rm -f libtool.m4 ltsugar.m4 +for m4file in libtool.m4 ltsugar.m4 ltoptions.m4 ltversion.m4 lt~obsolete.m4 +do + if [ -f $m4file ]; then + echo "Incorporating $m4file into aclocal.m4 ..." + cat $m4file >> aclocal.m4 + rm -f $m4file + fi +done cross_compile_warning="warning: AC_TRY_RUN called without default to allow cross compiling" diff --git a/libs/apr-util/xml/expat/configure.in b/libs/apr-util/xml/expat/configure.in index 478d8d5313..f808df4cce 100644 --- a/libs/apr-util/xml/expat/configure.in +++ b/libs/apr-util/xml/expat/configure.in @@ -12,6 +12,7 @@ dnl AC_INIT(Makefile.in) AC_CONFIG_AUX_DIR(conftools) +AC_CONFIG_MACRO_DIR(.) dnl dnl Follow the GNU/Linux convention of odd number minor version for diff --git a/libs/openzap/configure.ac b/libs/openzap/configure.ac index d2c23c1091..9b41d26953 100644 --- a/libs/openzap/configure.ac +++ b/libs/openzap/configure.ac @@ -80,6 +80,15 @@ esac #set SOLINK variable based on compiler and host if test "x${ax_cv_c_compiler_vendor}" = "xsun" ; then SOLINK="-Bdynamic -dy -G" +elif test "x${ax_cv_c_compiler_vendor}" = "xclang" ; then + case "$host" in + *darwin*) + SOLINK="-dynamic -bundle -force-flat-namespace" + ;; + *) + AC_ERROR([Please update configure.in with SOLINK values for your compiler]) + ;; + esac elif test "x${ax_cv_c_compiler_vendor}" = "xgnu" ; then case "$host" in *darwin*) diff --git a/libs/sofia-sip/.update b/libs/sofia-sip/.update index 275b8aceb5..b07a4f4762 100644 --- a/libs/sofia-sip/.update +++ b/libs/sofia-sip/.update @@ -1 +1 @@ -Fri Sep 20 00:39:20 CDT 2013 +Tue Oct 8 23:05:34 CDT 2013 diff --git a/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c b/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c index 02b9a5c5a7..e20a9d19a6 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c +++ b/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c @@ -640,14 +640,16 @@ static void print_media(sdp_printer_t *p, print_key(p, m->m_key); for (rm = m->m_rtpmaps; rm; rm = rm->rm_next) { - if (!rm->rm_predef || p->pr_all_rtpmaps) - sdp_printf(p, "a=rtpmap:%u %s/%lu%s%s" CRLF, - rm->rm_pt, rm->rm_encoding, rm->rm_rate, - rm->rm_params ? "/" : "", - rm->rm_params ? rm->rm_params : ""); - if (rm->rm_fmtp) - sdp_printf(p, "a=fmtp:%u %s" CRLF, - rm->rm_pt, rm->rm_fmtp); + if (rm->rm_encoding && *rm->rm_encoding && (!rm->rm_predef || p->pr_all_rtpmaps)) { + sdp_printf(p, "a=rtpmap:%u %s/%lu%s%s" CRLF, + rm->rm_pt, rm->rm_encoding, rm->rm_rate, + rm->rm_params ? "/" : "", + rm->rm_params ? rm->rm_params : ""); + } + if (rm->rm_fmtp) { + sdp_printf(p, "a=fmtp:%u %s" CRLF, + rm->rm_pt, rm->rm_fmtp); + } } if (!p->pr_mode_manual && !m->m_rejected && diff --git a/src/mod/.gitignore b/src/mod/.gitignore index bfcc02ab70..78158b6fd1 100644 --- a/src/mod/.gitignore +++ b/src/mod/.gitignore @@ -36,7 +36,6 @@ /codecs/mod_vp8/Makefile /dialplans/mod_dialplan_asterisk/Makefile /dialplans/mod_dialplan_xml/Makefile -/endpoints/mod_html5/mod_html5.log /endpoints/mod_portaudio/Makefile /endpoints/mod_portaudio/Makefile.in /endpoints/mod_portaudio/mod_portaudio.log @@ -49,8 +48,6 @@ /endpoints/mod_sofia/Makefile /endpoints/mod_sofia/Makefile.in /endpoints/mod_sofia/mod_sofia.log -/endpoints/mod_html5/Makefile -/endpoints/mod_html5/Makefile.in /event_handlers/mod_erlang_event/Makefile /event_handlers/mod_event_socket/Makefile /formats/mod_native_file/Makefile diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 97ec23f9c9..3f561ba386 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -1321,11 +1321,11 @@ SWITCH_STANDARD_API(echo_function) SWITCH_STANDARD_API(stun_function) { char *stun_ip = NULL; + char *src_ip = NULL; switch_port_t stun_port = (switch_port_t) SWITCH_STUN_DEFAULT_PORT; char *p; char ip_buf[256] = ""; char *ip = NULL; - char *pip = NULL; switch_port_t port = 0; switch_memory_pool_t *pool = NULL; char *error = ""; @@ -1346,7 +1346,7 @@ SWITCH_STANDARD_API(stun_function) switch_assert(stun_ip); - port = argv[1] ? atoi(argv[1]) : 0; + src_ip = argv[1]; if ((p = strchr(stun_ip, ':'))) { int iport; @@ -1359,12 +1359,19 @@ SWITCH_STANDARD_API(stun_function) p = stun_ip; } - if (p && (pip = strchr(p, ' '))) { - *pip++ = '\0'; + if (!zstr(src_ip) && (p = strchr(src_ip, ':'))) { + int iport; + *p++ = '\0'; + iport = atoi(p); + if (iport > 0 && iport < 0xFFFF) { + port = (switch_port_t) iport; + } + } else if (!zstr(src_ip)) { + ip = src_ip; } - if (pip) { - switch_copy_string(ip_buf, pip, sizeof(ip_buf)); + if ( !zstr(src_ip) ) { + switch_copy_string(ip_buf, src_ip, sizeof(ip_buf)); } else { switch_find_local_ip(ip_buf, sizeof(ip_buf), NULL, AF_INET); } @@ -6150,7 +6157,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) SWITCH_ADD_API(commands_api_interface, "sql_escape", "Escape a string to prevent sql injection", sql_escape, SQL_ESCAPE_SYNTAX); SWITCH_ADD_API(commands_api_interface, "status", "Show current status", status_function, ""); SWITCH_ADD_API(commands_api_interface, "strftime_tz", "Display formatted time of timezone", strftime_tz_api_function, " [|][format string]"); - SWITCH_ADD_API(commands_api_interface, "stun", "Execute STUN lookup", stun_function, "[:port]"); + SWITCH_ADD_API(commands_api_interface, "stun", "Execute STUN lookup", stun_function, "[:port] [[: [count]"); SWITCH_ADD_API(commands_api_interface, "timer_test", "Exercise FS timer", timer_test_function, TIMER_TEST_SYNTAX); diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 8e06012b7d..265effdb2e 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -283,6 +283,17 @@ struct vid_helper { int up; }; +struct conference_obj; + +/* Record Node */ +typedef struct conference_record { + struct conference_obj *conference; + char *path; + switch_memory_pool_t *pool; + switch_bool_t autorec; + struct conference_record *next; +} conference_record_t; + /* Conference Object */ typedef struct conference_obj { char *name; @@ -348,7 +359,7 @@ typedef struct conference_obj { int pin_retries; int broadcast_chat_messages; int comfort_noise_level; - int is_recording; + int auto_recording; int record_count; int video_running; int ivr_dtmf_timeout; @@ -381,6 +392,7 @@ typedef struct conference_obj { cdr_event_mode_t cdr_event_mode; struct vid_helper vh[2]; struct vid_helper mh; + conference_record_t *rec_node_head; } conference_obj_t; /* Relationship with another member */ @@ -416,6 +428,7 @@ struct conference_member { switch_codec_t write_codec; char *rec_path; switch_time_t rec_time; + conference_record_t *rec; uint8_t *frame; uint8_t *last_frame; uint32_t frame_size; @@ -450,13 +463,6 @@ struct conference_member { switch_thread_t *input_thread; }; -/* Record Node */ -typedef struct conference_record { - conference_obj_t *conference; - char *path; - switch_memory_pool_t *pool; -} conference_record_t; - typedef enum { CONF_API_SUB_ARGS_SPLIT, CONF_API_SUB_MEMBER_TARGET, @@ -524,7 +530,7 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c static switch_status_t chat_send(switch_event_t *message_event); -static void launch_conference_record_thread(conference_obj_t *conference, char *path); +static void launch_conference_record_thread(conference_obj_t *conference, char *path, switch_bool_t autorec); static int launch_conference_video_bridge_thread(conference_member_t *member_a, conference_member_t *member_b); static void launch_conference_video_mirror_thread(conference_member_t *member_a); @@ -1211,7 +1217,7 @@ static conference_member_t *conference_member_get(conference_obj_t *conference, } /* stop the specified recording */ -static switch_status_t conference_record_stop(conference_obj_t *conference, char *path) +static switch_status_t conference_record_stop(conference_obj_t *conference, switch_stream_handle_t *stream, char *path) { conference_member_t *member = NULL; int count = 0; @@ -1220,10 +1226,21 @@ static switch_status_t conference_record_stop(conference_obj_t *conference, char switch_mutex_lock(conference->member_mutex); for (member = conference->members; member; member = member->next) { if (switch_test_flag(member, MFLAG_NOCHANNEL) && (!path || !strcmp(path, member->rec_path))) { + if (member->rec && member->rec->autorec) { + stream->write_function(stream, "Stopped AUTO recording file %s (Auto Recording Now Disabled)\n", member->rec_path); + conference->auto_record = 0; + } else { + stream->write_function(stream, "Stopped recording file %s\n", member->rec_path); + } + switch_clear_flag_locked(member, MFLAG_RUNNING); count++; + } } + + conference->record_count -= count; + switch_mutex_unlock(conference->member_mutex); return count; } @@ -1965,7 +1982,7 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v globals.threads++; switch_mutex_unlock(globals.hash_mutex); - conference->is_recording = 0; + conference->auto_recording = 0; conference->record_count = 0; @@ -2105,15 +2122,15 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v } /* Start recording if there's more than one participant. */ - if (conference->auto_record && !conference->is_recording && conference->count > 1) { - conference->is_recording = 1; + if (conference->auto_record && !conference->auto_recording && conference->count > 1) { + conference->auto_recording++; conference->record_count++; imember = conference->members; if (imember) { switch_channel_t *channel = switch_core_session_get_channel(imember->session); char *rfile = switch_channel_expand_variables(channel, conference->auto_record); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Auto recording file: %s\n", rfile); - launch_conference_record_thread(conference, rfile); + launch_conference_record_thread(conference, rfile, SWITCH_TRUE); if (rfile != conference->auto_record) { conference->record_filename = switch_core_strdup(conference->pool, rfile); switch_safe_free(rfile); @@ -3877,7 +3894,7 @@ static void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *th int16_t *data_buf; switch_file_handle_t fh = { 0 }; conference_member_t smember = { 0 }, *member; - conference_record_t *rec = (conference_record_t *) obj; + conference_record_t *rp, *last = NULL, *rec = (conference_record_t *) obj; conference_obj_t *conference = rec->conference; uint32_t samples = switch_samples_per_packet(conference->rate, conference->interval); uint32_t mux_used; @@ -3915,7 +3932,7 @@ static void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *th fh.samplerate = conference->rate; member->id = next_member_id(); member->pool = rec->pool; - + member->rec = rec; member->frame_size = SWITCH_RECOMMENDED_BUFFER_SIZE; member->frame = switch_core_alloc(member->pool, member->frame_size); member->mux_frame = switch_core_alloc(member->pool, member->frame_size); @@ -4058,8 +4075,6 @@ static void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *th switch_mutex_unlock(member->audio_out_mutex); } - conference->is_recording = 0; - switch_safe_free(data_buf); switch_core_timer_destroy(&timer); conference_del_member(conference, member); @@ -4078,6 +4093,23 @@ static void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *th switch_event_fire(&event); } + if (rec->autorec && conference->auto_recording) { + conference->auto_recording--; + } + + switch_mutex_lock(conference->flag_mutex); + for (rp = conference->rec_node_head; rp; rp = rp->next) { + if (rec == rp) { + if (last) { + last->next = rp->next; + } else { + conference->rec_node_head = rp->next; + } + } + } + switch_mutex_unlock(conference->flag_mutex); + + if (rec->pool) { switch_memory_pool_t *pool = rec->pool; rec = NULL; @@ -6177,11 +6209,20 @@ static switch_status_t conf_api_sub_transfer(conference_obj_t *conference, switc static switch_status_t conf_api_sub_check_record(conference_obj_t *conference, switch_stream_handle_t *stream, int arc, char **argv) { - if (conference->is_recording) { - stream->write_function(stream, "Record file %s\n", conference->record_filename); - } else { + conference_record_t *rec; + int x = 0; + + switch_mutex_lock(conference->flag_mutex); + for (rec = conference->rec_node_head; rec; rec = rec->next) { + stream->write_function(stream, "Record file %s%s%s\n", rec->path, rec->autorec ? " " : "", rec->autorec ? "(Auto)" : ""); + x++; + } + + if (!x) { stream->write_function(stream, "Conference is not being recorded.\n"); } + switch_mutex_unlock(conference->flag_mutex); + return SWITCH_STATUS_SUCCESS; } @@ -6190,19 +6231,20 @@ static switch_status_t conf_api_sub_record(conference_obj_t *conference, switch_ switch_assert(conference != NULL); switch_assert(stream != NULL); - if (argc <= 2) + if (argc <= 2) { return SWITCH_STATUS_GENERR; + } stream->write_function(stream, "Record file %s\n", argv[2]); conference->record_filename = switch_core_strdup(conference->pool, argv[2]); conference->record_count++; - launch_conference_record_thread(conference, argv[2]); + launch_conference_record_thread(conference, argv[2], SWITCH_FALSE); return SWITCH_STATUS_SUCCESS; } static switch_status_t conf_api_sub_norecord(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv) { - int all; + int all, before = conference->record_count, ttl = 0; switch_event_t *event; switch_assert(conference != NULL); @@ -6212,15 +6254,10 @@ static switch_status_t conf_api_sub_norecord(conference_obj_t *conference, switc return SWITCH_STATUS_GENERR; all = (strcasecmp(argv[2], "all") == 0); - stream->write_function(stream, "Stop recording file %s\n", argv[2]); - if (!conference_record_stop(conference, all ? NULL : argv[2]) && !all) { + + if (!conference_record_stop(conference, stream, all ? NULL : argv[2]) && !all) { stream->write_function(stream, "non-existant recording '%s'\n", argv[2]); } else { - if (all) { - conference->record_count = 0; - } else { - conference->record_count--; - } if (test_eflag(conference, EFLAG_RECORD) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) { conference_add_event_data(conference, event); @@ -6231,6 +6268,9 @@ static switch_status_t conf_api_sub_norecord(conference_obj_t *conference, switc } } + ttl = before - conference->record_count; + stream->write_function(stream, "Stopped recording %d file%s\n", ttl, ttl == 1 ? "" : "s"); + return SWITCH_STATUS_SUCCESS; } @@ -6282,6 +6322,13 @@ static switch_status_t conf_api_sub_recording(conference_obj_t *conference, swit switch_assert(conference != NULL); switch_assert(stream != NULL); + if (argc > 2 && argc <= 3) { + if (strcasecmp(argv[2], "stop") == 0 || strcasecmp(argv[2], "check") == 0) { + argv[3] = "all"; + argc++; + } + } + if (argc <= 3) { /* It means that old syntax is used */ return conf_api_sub_record(conference,stream,argc,argv); @@ -8023,7 +8070,7 @@ static void launch_conference_video_mirror_thread(conference_member_t *member_a) launch_thread_detached(conference_video_mirror_thread_run, pool, &conference->mh); } -static void launch_conference_record_thread(conference_obj_t *conference, char *path) +static void launch_conference_record_thread(conference_obj_t *conference, char *path, switch_bool_t autorec) { switch_thread_t *thread; switch_threadattr_t *thd_attr = NULL; @@ -8042,11 +8089,15 @@ static void launch_conference_record_thread(conference_obj_t *conference, char * return; } - conference->is_recording = 1; - rec->conference = conference; rec->path = switch_core_strdup(pool, path); rec->pool = pool; + rec->autorec = autorec; + + switch_mutex_lock(conference->flag_mutex); + rec->next = conference->rec_node_head; + conference->rec_node_head = rec; + switch_mutex_unlock(conference->flag_mutex); switch_threadattr_create(&thd_attr, rec->pool); switch_threadattr_detach_set(thd_attr, 1); diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index b49e1a02ee..8d7b5c6f37 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -283,10 +283,17 @@ SWITCH_STANDARD_APP(clear_digit_action_function) { //switch_channel_t *channel = switch_core_session_get_channel(session); switch_ivr_dmachine_t *dmachine; - char *realm = switch_core_session_strdup(session, data); + char *realm = NULL; char *target_str; switch_digit_action_target_t target = DIGIT_TARGET_SELF; + if (zstr((char *)data)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "clear_digit_action called with no args"); + return; + } + + realm = switch_core_session_strdup(session, data); + if ((target_str = strchr(realm, ','))) { *target_str++ = '\0'; target = str2target(target_str); @@ -4615,6 +4622,7 @@ static switch_status_t file_string_file_read(switch_file_handle_t *handle, void return status; } + static switch_status_t file_string_file_write(switch_file_handle_t *handle, void *data, size_t *len) { file_string_context_t *context = handle->private_info; @@ -4633,10 +4641,101 @@ static switch_status_t file_string_file_write(switch_file_handle_t *handle, void return status; } +static switch_status_t file_url_file_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence) +{ + switch_file_handle_t *fh = handle->private_info; + return switch_core_file_seek(fh, cur_sample, samples, whence); +} + +static switch_status_t file_url_file_close(switch_file_handle_t *handle) +{ + switch_file_handle_t *fh = handle->private_info; + if (switch_test_flag(fh, SWITCH_FILE_OPEN)) { + switch_core_file_close(fh); + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t file_url_file_read(switch_file_handle_t *handle, void *data, size_t *len) +{ + switch_file_handle_t *fh = handle->private_info; + return switch_core_file_read(fh, data, len); +} + +static switch_status_t file_url_file_open(switch_file_handle_t *handle, const char *path) +{ + switch_file_handle_t *fh = switch_core_alloc(handle->memory_pool, sizeof(*fh)); + switch_status_t status; + char *url_host; + char *url_path; + + if (zstr(path)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NULL path\n"); + return SWITCH_STATUS_FALSE; + } + + /* parse and check host */ + url_host = switch_core_strdup(handle->memory_pool, path); + if (!(url_path = strchr(url_host, '/'))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "missing path\n"); + return SWITCH_STATUS_FALSE; + } + *url_path = '\0'; + /* TODO allow this host */ + if (!zstr(url_host) && strcasecmp(url_host, "localhost")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "not localhost\n"); + return SWITCH_STATUS_FALSE; + } + + /* decode and check path */ + url_path++; + if (zstr(url_path)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "empty path\n"); + return SWITCH_STATUS_FALSE; + } + if (strstr(url_path, "%2f") || strstr(url_path, "%2F")) { + /* don't allow %2f or %2F encoding (/) */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "encoded slash is not allowed\n"); + return SWITCH_STATUS_FALSE; + } + url_path = switch_core_sprintf(handle->memory_pool, "/%s", url_path); + switch_url_decode(url_path); + + /* TODO convert to native file separators? */ + + handle->private_info = fh; + status = switch_core_file_open(fh, url_path, handle->channels, handle->samplerate, handle->flags, NULL); + if (status == SWITCH_STATUS_SUCCESS) { + handle->samples = fh->samples; + handle->cur_samplerate = fh->samplerate; + handle->cur_channels = fh->channels; + handle->format = fh->format; + handle->sections = fh->sections; + handle->seekable = fh->seekable; + handle->speed = fh->speed; + handle->interval = fh->interval; + handle->max_samples = 0; + + if (switch_test_flag(fh, SWITCH_FILE_NATIVE)) { + switch_set_flag(handle, SWITCH_FILE_NATIVE); + } else { + switch_clear_flag(handle, SWITCH_FILE_NATIVE); + } + } + return status; +} + +static switch_status_t file_url_file_write(switch_file_handle_t *handle, void *data, size_t *len) +{ + switch_file_handle_t *fh = handle->private_info; + return switch_core_file_write(fh, data, len); +} /* Registration */ static char *file_string_supported_formats[SWITCH_MAX_CODECS] = { 0 }; +static char *file_url_supported_formats[SWITCH_MAX_CODECS] = { 0 }; /* /FILE STRING INTERFACE */ @@ -5504,6 +5603,17 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load) file_interface->file_write = file_string_file_write; file_interface->file_seek = file_string_file_seek; + file_url_supported_formats[0] = "file"; + + file_interface = (switch_file_interface_t *) switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE); + file_interface->interface_name = modname; + file_interface->extens = file_url_supported_formats; + file_interface->file_open = file_url_file_open; + file_interface->file_close = file_url_file_close; + file_interface->file_read = file_url_file_read; + file_interface->file_write = file_url_file_write; + file_interface->file_seek = file_url_file_seek; + error_endpoint_interface = (switch_endpoint_interface_t *) switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE); error_endpoint_interface->interface_name = "error"; diff --git a/src/mod/applications/mod_voicemail/mod_voicemail.c b/src/mod/applications/mod_voicemail/mod_voicemail.c index cb54af30bf..07d4d2fe95 100644 --- a/src/mod/applications/mod_voicemail/mod_voicemail.c +++ b/src/mod/applications/mod_voicemail/mod_voicemail.c @@ -3078,10 +3078,17 @@ static switch_status_t deliver_vm(vm_profile_t *profile, vm_cc_num = switch_separate_string(vm_cc_dup, ',', vm_cc_list, (sizeof(vm_cc_list) / sizeof(vm_cc_list[0]))); for (vm_cc_i=0; vm_cc_iwrite_frame = (switch_frame_t *) pop; + + switch_clear_flag(tech_pvt->write_frame, SFF_RAW_RTP); + tech_pvt->write_frame->timestamp = 0; + tech_pvt->write_frame->codec = &tech_pvt->read_codec; *frame = tech_pvt->write_frame; tech_pvt->packet_count++; diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index cfd14aba4d..567ee00ae5 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -718,6 +718,8 @@ struct private_object { sofia_private_t *sofia_private; uint8_t flags[TFLAG_MAX]; switch_payload_t agreed_pt; + int channels; + int adv_channels; switch_payload_t audio_recv_pt; switch_payload_t video_recv_pt; switch_core_session_t *session; diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index 74b27f3676..e54beabd3d 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -39,6 +39,15 @@ switch_cache_db_handle_t *_sofia_glue_get_db_handle(sofia_profile_t *profile, const char *file, const char *func, int line); #define sofia_glue_get_db_handle(_p) _sofia_glue_get_db_handle(_p, __FILE__, __SWITCH_FUNC__, __LINE__) +static int get_channels(const switch_codec_implementation_t *imp) +{ + if (!strcasecmp(imp->iananame, "opus")) { + return 2; /* IKR???*/ + } + + return imp->number_of_channels; +} + void sofia_glue_set_udptl_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *t38_options, int insist) { char buf[2048] = ""; @@ -308,7 +317,14 @@ static void generate_m(private_object_t *tech_pvt, char *buf, size_t buflen, } if (tech_pvt->ianacodes[i] > 95 || verbose_sdp) { - switch_snprintf(buf + strlen(buf), buflen - strlen(buf), "a=rtpmap:%d %s/%d\n", tech_pvt->ianacodes[i], imp->iananame, rate); + int channels = get_channels(imp); + + if (channels > 1) { + switch_snprintf(buf + strlen(buf), buflen - strlen(buf), "a=rtpmap:%d %s/%d/%d\n", tech_pvt->ianacodes[i], imp->iananame, rate, channels); + + } else { + switch_snprintf(buf + strlen(buf), buflen - strlen(buf), "a=rtpmap:%d %s/%d\n", tech_pvt->ianacodes[i], imp->iananame, rate); + } } if (fmtp) { @@ -530,7 +546,15 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, switch switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "\n"); rate = tech_pvt->rm_rate; - switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtpmap:%d %s/%d\n", tech_pvt->agreed_pt, tech_pvt->rm_encoding, rate); + + if (tech_pvt->adv_channels > 1) { + switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtpmap:%d %s/%d/%d\n", + tech_pvt->agreed_pt, tech_pvt->rm_encoding, rate, tech_pvt->adv_channels); + } else { + switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtpmap:%d %s/%d\n", + tech_pvt->agreed_pt, tech_pvt->rm_encoding, rate); + } + if (fmtp_out) { switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=fmtp:%d %s\n", tech_pvt->agreed_pt, fmtp_out); } @@ -735,6 +759,7 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, switch const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; char *fmtp = NULL; uint32_t ianacode = tech_pvt->ianacodes[i]; + int channels = 1; if (imp->codec_type != SWITCH_CODEC_TYPE_VIDEO) { continue; @@ -751,9 +776,15 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, switch rate = imp->samples_per_second; } - - switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtpmap:%d %s/%d\n", ianacode, imp->iananame, - imp->samples_per_second); + channels = get_channels(imp); + + if (channels > 1) { + switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtpmap:%d %s/%d/%d\n", ianacode, imp->iananame, + imp->samples_per_second, channels); + } else { + switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtpmap:%d %s/%d\n", ianacode, imp->iananame, + imp->samples_per_second); + } if (!zstr(ov_fmtp)) { fmtp = (char *) ov_fmtp; @@ -4414,7 +4445,6 @@ int sofia_glue_toggle_hold(private_object_t *tech_pvt, int sendonly) switch_channel_stop_broadcast(b_channel); switch_channel_wait_for_flag(b_channel, CF_BROADCAST, SWITCH_FALSE, 5000, NULL); } - switch_core_session_rwunlock(b_session); } sofia_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD); @@ -5315,6 +5345,23 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s switch_channel_set_variable(tech_pvt->channel, SWITCH_REMOTE_MEDIA_IP_VARIABLE, tech_pvt->remote_sdp_audio_ip); switch_channel_set_variable(tech_pvt->channel, SWITCH_REMOTE_MEDIA_PORT_VARIABLE, tmp); tech_pvt->audio_recv_pt = (switch_payload_t)map->rm_pt; + + if (!strcasecmp((char *) map->rm_encoding, "opus")) { + if (tech_pvt->channels == 1) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Invalid SDP for opus. Don't ask.. but it needs a /2\n"); + tech_pvt->adv_channels = 1; + } else { + tech_pvt->adv_channels = 2; /* IKR ???*/ + } + if (!zstr((char *) map->rm_fmtp) && switch_stristr("stereo=1", (char *) map->rm_fmtp)) { + tech_pvt->channels = 2; + } else { + tech_pvt->channels = 1; + } + } else { + tech_pvt->adv_channels = tech_pvt->channels; + } + if (!switch_true(mirror) && switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND && diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index e232ac8d38..6dd58d29c6 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -128,6 +128,8 @@ struct rayo_call { switch_time_t idle_start_time; /** 1 if joined to call, 2 if joined to mixer */ int joined; + /** pending join */ + iks *pending_join_request; /** ID of joined party TODO this will be many mixers / calls */ const char *joined_id; /** set if response needs to be sent to IQ request */ @@ -937,6 +939,15 @@ static void rayo_call_cleanup(struct rayo_actor *actor) RAYO_SEND_MESSAGE_DUP(actor, rayo_call_get_dcp_jid(call), revent); } + /* lost the race: pending join failed... send IQ result to client now. */ + if (call->pending_join_request) { + iks *request = call->pending_join_request; + iks *result = iks_new_error_detailed(request, STANZA_ERROR_ITEM_NOT_FOUND, "call ended"); + call->pending_join_request = NULL; + RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); + iks_delete(call->pending_join_request); + } + iks_delete(revent); switch_event_destroy(&event); } @@ -1068,6 +1079,7 @@ static struct rayo_call *rayo_call_init(struct rayo_call *call, switch_memory_po call->joined = 0; call->joined_id = NULL; call->ringing_sent = 0; + call->pending_join_request = NULL; switch_core_hash_init(&call->pcps, pool); switch_safe_free(call_jid); @@ -1292,6 +1304,17 @@ static struct rayo_peer_server *rayo_peer_server_create(const char *jid) return rserver; } +/** + * Check if message sender has control of offered call. + * @param call the Rayo call + * @param msg the message + * @return 1 if sender has call control, 0 if sender does not have control + */ +static int has_call_control(struct rayo_call *call, struct rayo_message *msg) +{ + return (!strcmp(rayo_call_get_dcp_jid(call), msg->from_jid) || is_internal_message(msg) || is_admin_client_message(msg)); +} + /** * Check if message sender has control of offered call. Take control if nobody else does. * @param call the Rayo call @@ -1299,7 +1322,7 @@ static struct rayo_peer_server *rayo_peer_server_create(const char *jid) * @param msg the message * @return 1 if sender has call control */ -static int has_call_control(struct rayo_call *call, switch_core_session_t *session, struct rayo_message *msg) +static int take_call_control(struct rayo_call *call, switch_core_session_t *session, struct rayo_message *msg) { int control = 0; @@ -1313,7 +1336,7 @@ static int has_call_control(struct rayo_call *call, switch_core_session_t *sessi 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 (!strcmp(rayo_call_get_dcp_jid(call), msg->from_jid) || is_internal_message(msg) || is_admin_client_message(msg)) { + } else if (has_call_control(call, msg)) { control = 1; } @@ -1358,7 +1381,7 @@ static iks *rayo_call_command_ok(struct rayo_call *call, switch_core_session_t * if (bad) { response = iks_new_error(node, STANZA_ERROR_BAD_REQUEST); - } else if (!has_call_control(call, session, msg)) { + } else if (!take_call_control(call, session, msg)) { response = iks_new_error(node, STANZA_ERROR_CONFLICT); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, %s conflict\n", msg->from_jid, RAYO_JID(call)); } @@ -1711,13 +1734,14 @@ static iks *on_rayo_hangup(struct rayo_actor *call, struct rayo_message *msg, vo * Join calls together * @param call the call that joins * @param session the session - * @param node the join request + * @param msg the rayo join message * @param call_uri 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_uri, const char *media) +static iks *join_call(struct rayo_call *call, switch_core_session_t *session, struct rayo_message *msg, const char *call_uri, const char *media) { + iks *node = msg->payload; iks *response = NULL; /* take call out of media path if media = "direct" */ const char *bypass = !strcmp("direct", media) ? "true" : "false"; @@ -1727,6 +1751,9 @@ static iks *join_call(struct rayo_call *call, switch_core_session_t *session, ik 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 (!has_call_control(b_call, msg)) { + /* not allowed to join to this call */ + response = iks_new_error(node, STANZA_ERROR_NOT_ALLOWED); } 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"); @@ -1737,10 +1764,13 @@ static iks *join_call(struct rayo_call *call, switch_core_session_t *session, ik if (switch_false(bypass)) { switch_channel_pre_answer(switch_core_session_get_channel(session)); } - if (switch_ivr_uuid_bridge(rayo_call_get_uuid(call), rayo_call_get_uuid(b_call)) == 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"); + call->pending_join_request = iks_copy(node); + if (switch_ivr_uuid_bridge(rayo_call_get_uuid(call), rayo_call_get_uuid(b_call)) != SWITCH_STATUS_SUCCESS) { + iks *request = call->pending_join_request; + iks *result = iks_new_error_detailed(request, STANZA_ERROR_ITEM_NOT_FOUND, "failed to bridge call"); + call->pending_join_request = NULL; + RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); + iks_delete(call->pending_join_request); } RAYO_UNLOCK(b_call); } @@ -1749,40 +1779,99 @@ static iks *join_call(struct rayo_call *call, switch_core_session_t *session, ik /** * Execute command on session's conference + * @param session to execute conference API on + * @param conf_name of conference + * @param command to send to conference + * @param node IQ request + * @return response on failure */ -static void exec_conference_api(switch_core_session_t *session, const char *conf_name, const char *command) +static iks *exec_conference_api(switch_core_session_t *session, const char *conf_name, const char *command, iks *node) { + iks *response = NULL; switch_stream_handle_t stream = { 0 }; const char *conf_member_id = switch_channel_get_variable(switch_core_session_get_channel(session), "conference_member_id"); SWITCH_STANDARD_STREAM(stream); switch_api_execute("conference", switch_core_session_sprintf(session, "%s %s %s", conf_name, command, conf_member_id), NULL, &stream); + if (!zstr(stream.data) && strncmp("OK", stream.data, 2)) { + response = iks_new_error_detailed_printf(node, STANZA_ERROR_SERVICE_UNAVAILABLE, "%s", stream.data); + } switch_safe_free(stream.data); + return response; +} + +/** + * Execute conference app on session + * @param session to execute conference API on + * @param command to send to conference (conference name, member flags, etc) + * @param node IQ request + * @return response on failure + */ +static iks *exec_conference_app(switch_core_session_t *session, const char *command, iks *node) +{ + iks *response = NULL; + switch_event_t *execute_event = NULL; + switch_channel_t *channel = switch_core_session_get_channel(session); + + /* conference requires local media on channel */ + if (!switch_channel_media_ready(channel) && switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { + /* shit */ + response = iks_new_error_detailed(node, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to start media"); + return response; + } + + /* send execute conference event to session */ + if (switch_event_create(&execute_event, SWITCH_EVENT_COMMAND) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "call-command", "execute"); + switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "execute-app-name", "conference"); + switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "execute-app-arg", command); + //switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "event_uuid", uuid); + switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "event-lock", "true"); + if (!switch_channel_test_flag(channel, CF_PROXY_MODE)) { + switch_channel_set_flag(channel, CF_BLOCK_BROADCAST_UNTIL_MEDIA); + } + + if (switch_core_session_queue_private_event(session, &execute_event, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) { + response = iks_new_error_detailed(node, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to join mixer (queue event failed)"); + if (execute_event) { + switch_event_destroy(&execute_event); + } + return response; + } + } + return response; } /** * Join call to a mixer * @param call the call that joins * @param session the session - * @param node the join request + * @param msg the join request * @param mixer_name the mixer to join * @param direction the media direction * @return the response */ -static iks *join_mixer(struct rayo_call *call, switch_core_session_t *session, iks *node, const char *mixer_name, const char *direction) +static iks *join_mixer(struct rayo_call *call, switch_core_session_t *session, struct rayo_message *msg, const char *mixer_name, const char *direction) { + iks *node = msg->payload; iks *response = NULL; if (call->joined_id) { /* adjust join conference params */ if (!strcmp("duplex", direction)) { - exec_conference_api(session, mixer_name, "unmute"); - exec_conference_api(session, mixer_name, "undeaf"); + if ((response = exec_conference_api(session, mixer_name, "unmute", node)) || + (response = exec_conference_api(session, mixer_name, "undeaf", node))) { + return response; + } } else if (!strcmp("recv", direction)) { - exec_conference_api(session, mixer_name, "mute"); - exec_conference_api(session, mixer_name, "undeaf"); + if ((response = exec_conference_api(session, mixer_name, "mute", node)) || + (response = exec_conference_api(session, mixer_name, "undeaf", node))) { + return response; + } } else { - exec_conference_api(session, mixer_name, "unmute"); - exec_conference_api(session, mixer_name, "deaf"); + if ((response = exec_conference_api(session, mixer_name, "unmute", node)) || + (response = exec_conference_api(session, mixer_name, "deaf", node))) { + return response; + } } response = iks_new_iq_result(node); } else { @@ -1793,10 +1882,12 @@ static iks *join_mixer(struct rayo_call *call, switch_core_session_t *session, i } else if (!strcmp("recv", direction)) { conf_args = switch_core_session_sprintf(session, "%s+flags{mute}", conf_args); } - 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"); + + call->pending_join_request = iks_copy(node); + response = exec_conference_app(session, conf_args, node); + if (response) { + iks_delete(call->pending_join_request); + call->pending_join_request = NULL; } } return response; @@ -1806,14 +1897,13 @@ static iks *join_mixer(struct rayo_call *call, switch_core_session_t *session, i * Handle request * @param call the Rayo call * @param session the session - * @param node the node + * @param msg the rayo join message */ static iks *on_rayo_join(struct rayo_actor *call, struct rayo_message *msg, void *session_data) { - iks *node = msg->payload; switch_core_session_t *session = (switch_core_session_t *)session_data; iks *response = NULL; - iks *join = iks_find(node, "join"); + iks *join = iks_find(msg->payload, "join"); const char *join_id; const char *mixer_name; const char *call_uri; @@ -1821,7 +1911,7 @@ static iks *on_rayo_join(struct rayo_actor *call, struct rayo_message *msg, void /* 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); + response = iks_new_error(msg->payload, STANZA_ERROR_BAD_REQUEST); goto done; } mixer_name = iks_find_attrib(join, "mixer-name"); @@ -1835,29 +1925,35 @@ static iks *on_rayo_join(struct rayo_actor *call, struct rayo_message *msg, void /* can't join both mixer and call */ if (!zstr(mixer_name) && !zstr(call_uri)) { - response = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "mixer-name and call-uri are mutually exclusive"); + response = iks_new_error_detailed(msg->payload, STANZA_ERROR_BAD_REQUEST, "mixer-name and call-uri are mutually exclusive"); goto done; } /* need to join *something* */ if (zstr(mixer_name) && zstr(call_uri)) { - response = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "mixer-name or call-uri is required"); + response = iks_new_error_detailed(msg->payload, STANZA_ERROR_BAD_REQUEST, "mixer-name or call-uri is required"); goto done; } if ((RAYO_CALL(call)->joined == JOINED_CALL) || (RAYO_CALL(call)->joined == JOINED_MIXER && strcmp(RAYO_CALL(call)->joined_id, join_id))) { /* already joined */ - response = iks_new_error_detailed(node, STANZA_ERROR_CONFLICT, "call is already joined"); + response = iks_new_error_detailed(msg->payload, STANZA_ERROR_CONFLICT, "call is already joined"); + goto done; + } + + if (RAYO_CALL(call)->pending_join_request) { + /* don't allow concurrent join requests */ + response = iks_new_error_detailed(msg->payload, STANZA_ERROR_UNEXPECTED_REQUEST, "(un)join request is pending"); goto done; } if (!zstr(mixer_name)) { /* join conference */ - response = join_mixer(RAYO_CALL(call), session, node, mixer_name, iks_find_attrib(join, "direction")); + response = join_mixer(RAYO_CALL(call), session, msg, mixer_name, iks_find_attrib(join, "direction")); } else { /* bridge calls */ - response = join_call(RAYO_CALL(call), session, node, call_uri, iks_find_attrib(join, "media")); + response = join_call(RAYO_CALL(call), session, msg, call_uri, iks_find_attrib(join, "media")); } done: @@ -1868,20 +1964,18 @@ done: * unjoin call to a bridge * @param call the call that unjoined * @param session the session - * @param node the unjoin request + * @param msg the unjoin request * @param call_uri the b-leg xmpp URI * @return the response */ -static iks *unjoin_call(struct rayo_call *call, switch_core_session_t *session, iks *node, const char *call_uri) +static iks *unjoin_call(struct rayo_call *call, switch_core_session_t *session, struct rayo_message *msg, const char *call_uri) { + iks *node = msg->payload; iks *response = NULL; - const char *bleg_uuid = switch_channel_get_variable(switch_core_session_get_channel(session), SWITCH_BRIDGE_UUID_VARIABLE); - const char *bleg_uri = switch_core_session_sprintf(session, "xmpp:%s@%s", bleg_uuid ? bleg_uuid : "", RAYO_JID(globals.server)); - /* bleg must match call_uri */ - if (!zstr(bleg_uri) && !strcmp(bleg_uri, call_uri)) { + if (!strcmp(call_uri, call->joined_id)) { /* unbridge call */ - response = iks_new_iq_result(node); + call->pending_join_request = iks_copy(node); switch_ivr_park_session(session); } else { /* not bridged or wrong b-leg URI */ @@ -1895,15 +1989,16 @@ static iks *unjoin_call(struct rayo_call *call, switch_core_session_t *session, * unjoin call to a mixer * @param call the call that unjoined * @param session the session - * @param node the unjoin request + * @param msg the unjoin request * @param mixer_name the mixer name * @return the response */ -static iks *unjoin_mixer(struct rayo_call *call, switch_core_session_t *session, iks *node, const char *mixer_name) +static iks *unjoin_mixer(struct rayo_call *call, switch_core_session_t *session, struct rayo_message *msg, 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"); + iks *node = msg->payload; iks *response = NULL; /* not conferenced, or wrong conference */ @@ -1916,11 +2011,12 @@ static iks *unjoin_mixer(struct rayo_call *call, switch_core_session_t *session, goto done; } - /* ack command */ - response = iks_new_iq_result(node); - /* kick the member */ - exec_conference_api(session, mixer_name, "hup"); + response = exec_conference_api(session, mixer_name, "hup", node); + if (!response) { + /* ack command */ + response = iks_new_iq_result(node); + } done: @@ -1935,31 +2031,39 @@ done: */ static iks *on_rayo_unjoin(struct rayo_actor *call, struct rayo_message *msg, void *session_data) { - iks *node = msg->payload; switch_core_session_t *session = (switch_core_session_t *)session_data; iks *response = NULL; - iks *unjoin = iks_find(node, "unjoin"); + iks *unjoin = iks_find(msg->payload, "unjoin"); const char *call_uri = iks_find_attrib(unjoin, "call-uri"); const char *mixer_name = iks_find_attrib(unjoin, "mixer-name"); if (!zstr(call_uri) && !zstr(mixer_name)) { - response = iks_new_error(node, STANZA_ERROR_BAD_REQUEST); + response = iks_new_error(msg->payload, STANZA_ERROR_BAD_REQUEST); + } else if (RAYO_CALL(call)->pending_join_request) { + /* need to let pending request finish first */ + response = iks_new_error_detailed(msg->payload, STANZA_ERROR_UNEXPECTED_REQUEST, "(un)join request is pending"); } else if (!RAYO_CALL(call)->joined) { /* not joined to anything */ - response = iks_new_error(node, STANZA_ERROR_SERVICE_UNAVAILABLE); + response = iks_new_error_detailed(msg->payload, STANZA_ERROR_SERVICE_UNAVAILABLE, "not joined to anything"); + } else if (RAYO_CALL(call)->joined == JOINED_MIXER && !zstr(call_uri)) { + /* joined to mixer, not call */ + response = iks_new_error_detailed(msg->payload, STANZA_ERROR_SERVICE_UNAVAILABLE, "not joined to call"); + } else if (RAYO_CALL(call)->joined == JOINED_CALL && !zstr(mixer_name)) { + /* joined to call, not mixer */ + response = iks_new_error_detailed(msg->payload, STANZA_ERROR_SERVICE_UNAVAILABLE, "not joined to mixer"); } else if (!zstr(call_uri)) { - response = unjoin_call(RAYO_CALL(call), session, node, call_uri); + response = unjoin_call(RAYO_CALL(call), session, msg, call_uri); } else if (!zstr(mixer_name)) { - response = unjoin_mixer(RAYO_CALL(call), session, node, mixer_name); + response = unjoin_mixer(RAYO_CALL(call), session, msg, mixer_name); } else { /* unjoin everything */ if (RAYO_CALL(call)->joined == JOINED_MIXER) { - response = unjoin_mixer(RAYO_CALL(call), session, node, RAYO_CALL(call)->joined_id); + response = unjoin_mixer(RAYO_CALL(call), session, msg, RAYO_CALL(call)->joined_id); } else if (RAYO_CALL(call)->joined == JOINED_CALL) { - response = unjoin_call(RAYO_CALL(call), session, node, RAYO_CALL(call)->joined_id); + response = unjoin_call(RAYO_CALL(call), session, msg, RAYO_CALL(call)->joined_id); } else { /* shouldn't happen */ - response = iks_new_error(node, STANZA_ERROR_INTERNAL_SERVER_ERROR); + response = iks_new_error(msg->payload, STANZA_ERROR_INTERNAL_SERVER_ERROR); } } @@ -2501,6 +2605,15 @@ static void on_mixer_add_member_event(struct rayo_mixer *mixer, switch_event_t * call->joined = JOINED_MIXER; call->joined_id = switch_core_strdup(RAYO_POOL(call), rayo_mixer_get_name(mixer)); + /* send IQ result to client now. */ + if (call->pending_join_request) { + iks *request = call->pending_join_request; + iks *result = iks_new_iq_result(request); + call->pending_join_request = NULL; + RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); + iks_delete(request); + } + /* 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"); @@ -2661,31 +2774,53 @@ static void on_call_bridge_event(struct rayo_client *rclient, switch_event_t *ev 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_printf(joined, "call-uri", "xmpp:%s@%s", b_uuid, RAYO_JID(globals.server)); + iks *revent; + iks *joined; call->joined = JOINED_CALL; - call->joined_id = switch_core_strdup(RAYO_POOL(call), b_uuid); + call->joined_id = switch_core_sprintf(RAYO_POOL(call), "xmpp:%s@%s", b_uuid, RAYO_JID(globals.server)); - RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), revent); + /* send IQ result to client now. */ + if (call->pending_join_request) { + iks *request = call->pending_join_request; + iks *result = iks_new_iq_result(request); + call->pending_join_request = NULL; + RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); + iks_delete(request); + } - /* send B-leg event */ b_call = RAYO_CALL_LOCATE_BY_ID(b_uuid); if (b_call) { + b_call->joined = JOINED_CALL; + b_call->joined_id = switch_core_sprintf(RAYO_POOL(b_call), "xmpp:%s@s", a_uuid, RAYO_JID(globals.server)); + + /* send IQ result to client now. */ + if (b_call->pending_join_request) { + iks *request = b_call->pending_join_request; + iks *result = iks_new_iq_result(request); + b_call->pending_join_request = NULL; + RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); + iks_delete(request); + } + + /* send B-leg event */ 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_printf(joined, "call-uri", "xmpp:%s@%s", a_uuid, RAYO_JID(globals.server)); - b_call->joined = JOINED_CALL; - b_call->joined_id = switch_core_strdup(RAYO_POOL(b_call), a_uuid); - RAYO_SEND_MESSAGE(b_call, rayo_call_get_dcp_jid(b_call), revent); RAYO_UNLOCK(b_call); } + + /* send A-leg event */ + 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")); + joined = iks_find(revent, "joined"); + iks_insert_attrib_printf(joined, "call-uri", "xmpp:%s@%s", b_uuid, RAYO_JID(globals.server)); + + RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), revent); + RAYO_UNLOCK(call); } } @@ -2703,33 +2838,85 @@ static void on_call_unbridge_event(struct rayo_client *rclient, switch_event_t * 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_printf(joined, "call-uri", "xmpp:%s@%s", b_uuid, RAYO_JID(globals.server)); - RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), revent); + iks *revent; + iks *joined; call->joined = 0; call->joined_id = NULL; - /* send B-leg event */ + /* send IQ result to client now. */ + if (call->pending_join_request) { + iks *request = call->pending_join_request; + iks *result = iks_new_iq_result(request); + call->pending_join_request = NULL; + RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); + iks_delete(request); + } + b_call = RAYO_CALL_LOCATE_BY_ID(b_uuid); if (b_call) { + b_call->joined = 0; + b_call->joined_id = NULL; + + /* send IQ result to client now. */ + if (b_call->pending_join_request) { + iks *request = b_call->pending_join_request; + iks *result = iks_new_iq_result(request); + b_call->pending_join_request = NULL; + RAYO_SEND_REPLY(b_call, iks_find_attrib_soft(request, "from"), result); + iks_delete(request); + } + + /* send B-leg event */ 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_printf(joined, "call-uri", "xmpp:%s@%s", a_uuid, RAYO_JID(globals.server)); RAYO_SEND_MESSAGE(b_call, rayo_call_get_dcp_jid(b_call), revent); - - b_call->joined = 0; - b_call->joined_id = NULL; RAYO_UNLOCK(b_call); } + + /* send A-leg event */ + 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")); + joined = iks_find(revent, "unjoined"); + iks_insert_attrib_printf(joined, "call-uri", "xmpp:%s@%s", b_uuid, RAYO_JID(globals.server)); + RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), revent); + RAYO_UNLOCK(call); } } +/** + * Handle call execute application event + * @param rclient the Rayo client + * @param event the execute event + */ +static void on_call_execute_event(struct rayo_client *rclient, switch_event_t *event) +{ + struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(switch_event_get_header(event, "Unique-ID")); + if (call) { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(RAYO_ID(call)), SWITCH_LOG_DEBUG, "Application %s execute\n", switch_event_get_header(event, "Application")); + RAYO_UNLOCK(call); + } +} + +/** + * Handle call execute application complete event + * @param rclient the Rayo client + * @param event the execute complete event + */ +static void on_call_execute_complete_event(struct rayo_client *rclient, switch_event_t *event) +{ + struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(switch_event_get_header(event, "Unique-ID")); + if (call) { + const char *app = switch_event_get_header(event, "Application"); + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(RAYO_ID(call)), SWITCH_LOG_DEBUG, "Application %s execute complete: %s \n", + app, + switch_event_get_header(event, "Application-Response")); + RAYO_UNLOCK(call); + } +} /** * Handle events to deliver to client connection @@ -2756,6 +2943,12 @@ static void rayo_client_handle_event(struct rayo_client *rclient, switch_event_t case SWITCH_EVENT_CHANNEL_UNBRIDGE: on_call_unbridge_event(rclient, event); break; + case SWITCH_EVENT_CHANNEL_EXECUTE: + on_call_execute_event(rclient, event); + break; + case SWITCH_EVENT_CHANNEL_EXECUTE_COMPLETE: + on_call_execute_complete_event(rclient, event); + break; default: /* don't care */ break; @@ -2881,7 +3074,9 @@ static switch_status_t rayo_call_on_read_frame(switch_core_session_t *session, s 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) { + if (rayo_call_is_joined(call)) { + call->idle_start_time = now; + } else if (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); } @@ -2974,8 +3169,9 @@ 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, "transfer_after_bridge", ""); switch_channel_set_variable(channel, "park_after_bridge", "true"); + switch_channel_set_variable(channel, "hold_hangup_xfer_exten", "foo"); /* Icky hack to prevent unjoin of call on hold from hanging up b-leg. park_after_bridge will take precedence over the transfer_after_bridge variable that gets set by this var */ 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) { @@ -3828,6 +4024,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load) 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_EXECUTE, NULL, route_call_event, NULL); + switch_event_bind(modname, SWITCH_EVENT_CHANNEL_EXECUTE_COMPLETE, NULL, route_call_event, NULL); switch_event_bind(modname, SWITCH_EVENT_CHANNEL_DESTROY, NULL, on_call_end_event, NULL); diff --git a/src/mod/formats/mod_ssml/mod_ssml.c b/src/mod/formats/mod_ssml/mod_ssml.c index cb29c5e79d..8b172721c7 100644 --- a/src/mod/formats/mod_ssml/mod_ssml.c +++ b/src/mod/formats/mod_ssml/mod_ssml.c @@ -692,11 +692,14 @@ static int get_file_from_macro(struct ssml_parser *parsed_data, char *to_say) static int get_file_from_voice(struct ssml_parser *parsed_data, char *to_say) { struct ssml_node *cur_node = parsed_data->cur_node; - char *file = switch_core_sprintf(parsed_data->pool, "%s%s", cur_node->tts_voice->prefix, to_say); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding <%s>: \"%s\"\n", cur_node->tag_name, file); - parsed_data->files[parsed_data->num_files].name = file; - parsed_data->files[parsed_data->num_files++].prefix = NULL; - return 1; + if (cur_node->tts_voice) { + char *file = switch_core_sprintf(parsed_data->pool, "%s%s", cur_node->tts_voice->prefix, to_say); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding <%s>: \"%s\"\n", cur_node->tag_name, file); + parsed_data->files[parsed_data->num_files].name = file; + parsed_data->files[parsed_data->num_files++].prefix = NULL; + return 1; + } + return 0; } /** @@ -708,7 +711,7 @@ static int process_cdata_tts(struct ssml_parser *parsed_data, char *data, size_t if (!len) { return IKS_OK; } - if (cur_node && cur_node->tts_voice && parsed_data->num_files < parsed_data->max_files) { + if (cur_node && parsed_data->num_files < parsed_data->max_files) { int i = 0; int empty = 1; char *to_say; @@ -728,7 +731,9 @@ static int process_cdata_tts(struct ssml_parser *parsed_data, char *data, size_t to_say[len] = '\0'; if (!cur_node->say_macro || !get_file_from_macro(parsed_data, to_say)) { /* use voice instead */ - get_file_from_voice(parsed_data, to_say); + if (!get_file_from_voice(parsed_data, to_say)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "No TTS voices available to render text!\n"); + } } free(to_say); return IKS_OK; diff --git a/src/switch_core.c b/src/switch_core.c index 5fafa3de8a..b2a07f0c5f 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -2600,7 +2600,6 @@ SWITCH_DECLARE(switch_status_t) switch_core_destroy(void) switch_nat_shutdown(); } switch_xml_destroy(); - switch_core_session_uninit(); switch_console_shutdown(); switch_channel_global_uninit(); @@ -2610,6 +2609,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_destroy(void) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Finalizing Shutdown.\n"); switch_log_shutdown(); + switch_core_session_uninit(); switch_core_unset_variables(); switch_core_memory_stop(); diff --git a/src/switch_core_session.c b/src/switch_core_session.c index f77ef60559..60358ca0dd 100644 --- a/src/switch_core_session.c +++ b/src/switch_core_session.c @@ -2480,8 +2480,8 @@ void switch_core_session_init(switch_memory_pool_t *pool) switch_queue_create(&session_manager.thread_queue, 100000, session_manager.memory_pool); switch_threadattr_create(&thd_attr, session_manager.memory_pool); switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); - switch_thread_create(&session_manager.manager_thread, thd_attr, switch_core_session_thread_pool_manager, NULL, session_manager.memory_pool); session_manager.ready = 1; + switch_thread_create(&session_manager.manager_thread, thd_attr, switch_core_session_thread_pool_manager, NULL, session_manager.memory_pool); } } @@ -2491,15 +2491,16 @@ void switch_core_session_uninit(void) int sanity = 100; switch_status_t st = SWITCH_STATUS_FALSE; - switch_core_hash_destroy(&session_manager.session_table); session_manager.ready = 0; - - switch_thread_join(&st, session_manager.manager_thread); + wake_queue(); while(session_manager.running && --sanity > 0) { switch_queue_interrupt_all(session_manager.thread_queue); switch_yield(100000); } + + switch_thread_join(&st, session_manager.manager_thread); + switch_core_hash_destroy(&session_manager.session_table); } diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 39d1dd5303..f765ecf973 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -2777,7 +2777,7 @@ static void do_2833(switch_rtp_t *rtp_session, switch_core_session_t *session) rtp_session->dtmf_data.out_digit_dur = rdigit->duration; rtp_session->dtmf_data.out_digit = rdigit->digit; rtp_session->dtmf_data.out_digit_packet[0] = (unsigned char) switch_char_to_rfc2833(rdigit->digit); - rtp_session->dtmf_data.out_digit_packet[1] = 7; + rtp_session->dtmf_data.out_digit_packet[1] = 13; rtp_session->dtmf_data.out_digit_packet[2] = (unsigned char) (rtp_session->dtmf_data.out_digit_sub_sofar >> 8); rtp_session->dtmf_data.out_digit_packet[3] = (unsigned char) rtp_session->dtmf_data.out_digit_sub_sofar; diff --git a/support-d/gl b/support-d/gl new file mode 100755 index 0000000000..c4e878ba98 --- /dev/null +++ b/support-d/gl @@ -0,0 +1,82 @@ +#!/usr/bin/perl + +my $pager = `which less` || `which more`; +my $tmpdir = "/tmp/FSJIRA"; + +system("mkdir -p $tmpdir"); + +my $cmd = "git log " . join(" ", @ARGV); + +open(CMD, "$cmd |"); +open(PAGER, "|$pager"); +select PAGER; + +while(my $line = ) { + + print $line; + + if ($line =~ /([A-Z]+\-[0-9]+)/) { + my $bug = $1; + my $txt = bugtxt($bug); + if ($txt) { + print "=" x 80 . "\n"; + print $txt; + print "=" x 80 . "\n"; + } + } +} + +close(CMD); +close(PAGER); + +sub catfile($) { + my $file = shift; + open(I, $file) or return; + $/ = undef; + my $txt = ; + $/ = "\n"; + close(I); + return $txt; +} + + +sub bugtxt($) +{ + my $bug = shift or return ""; + my $now = time; + my $tmp; + + $bug =~ s/\.\.//g; + $bug =~ s/^\///g; + $bug =~ s/~//g; + $bug =~ s/[^a-zA-Z0-9\-]//g; + + $tmp = "$tmpdir/$bug.txt"; + + if(-f $tmp) { + return catfile($tmp); + } + + my $cmd = "wget -q http://jira.freeswitch.org/si/jira.issueviews:issue-xml/$bug/$bug.xml -O $tmp"; + + system($cmd); + + my $txt = catfile($tmp); + + my ($a,$title) = $txt =~ /\(.*?)\<\/title\>/smg; + my ($status) = $txt =~ /\(.*?)\<\/status\>/smg; + my ($a,$des) = $txt =~ /\(.*?)\<\/description\>/smg; + my ($alogin, $aname) = $txt =~ /\(.*?)\<\/assignee\>/smg; + my ($rlogin, $rname) = $txt =~ /\(.*?)\<\/reporter\>/smg; + + + if ($rname && $aname) { + my $data = "$title\nReporter: $rname [$rlogin]\nAssignee: $aname [$alogin]\nStatus: $status\nhttp://jira.freeswitch.org/browse/$bug\n"; + open(O, ">$tmp"); + print O $data; + close(O); + return $data; + } else { + unlink($tmp); + } +}