diff --git a/.gitignore b/.gitignore index f6eb3374de..57a923062a 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ sipXconfig/packages/logging sipXconfig/packages/path sipXconfig/packages/unmodifiable_collection pubspec.lock +/sipXyealink/nbproject/private/ +/sipXyealink/build/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 2430e30614..caacc748cb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "freeswitch"] path = freeswitch - url = git://github.com/SIPfoundry/freeswitch.git + url = git://github.com/sipXcom/freeswitch.git [submodule "sipXcustomCallerId"] path = sipXcustomCallerId url = git://github.com/ezuce/sipXcustomCallerId.git @@ -9,61 +9,61 @@ url = http://code.google.com/p/homer/ [submodule "sipXhomer"] path = sipXhomer - url = git://github.com/SIPfoundry/sipXhomer.git + url = git://github.com/sipXcom/sipXhomer.git [submodule "sipXlang-abitibi-fr_CA"] path = sipXlang-abitibi-fr_CA - url = git://github.com/SIPfoundry/sipXlang-abitibi-fr_CA.git + url = git://github.com/sipXcom/sipXlang-abitibi-fr_CA.git [submodule "sipXlang-ch"] path = sipXlang-ch - url = git://github.com/SIPfoundry/sipXlang-ch.git + url = git://github.com/sipXcom/sipXlang-ch.git [submodule "sipXlang-cs"] path = sipXlang-cs - url = git://github.com/SIPfoundry/sipXlang-cs.git + url = git://github.com/sipXcom/sipXlang-cs.git [submodule "sipXlang-de"] path = sipXlang-de - url = git://github.com/SIPfoundry/sipXlang-de.git + url = git://github.com/sipXcom/sipXlang-de.git [submodule "sipXlang-en_GB"] path = sipXlang-en_GB - url = git://github.com/SIPfoundry/sipXlang-en_GB.git + url = git://github.com/sipXcom/sipXlang-en_GB.git [submodule "sipXlang-es"] path = sipXlang-es - url = git://github.com/SIPfoundry/sipXlang-es.git + url = git://github.com/sipXcom/sipXlang-es.git [submodule "sipXlang-fr_CA"] path = sipXlang-fr_CA - url = git://github.com/SIPfoundry/sipXlang-fr_CA.git + url = git://github.com/sipXcom/sipXlang-fr_CA.git [submodule "sipXlang-fr"] path = sipXlang-fr - url = git://github.com/SIPfoundry/sipXlang-fr.git + url = git://github.com/sipXcom/sipXlang-fr.git [submodule "sipXlang-it"] path = sipXlang-it - url = git://github.com/SIPfoundry/sipXlang-it.git + url = git://github.com/sipXcom/sipXlang-it.git [submodule "sipXlang-ja"] path = sipXlang-ja - url = git://github.com/SIPfoundry/sipXlang-ja.git + url = git://github.com/sipXcom/sipXlang-ja.git [submodule "sipXlang-es_MX"] path = sipXlang-es_MX - url = git://github.com/SIPfoundry/sipXlang-es_MX.git + url = git://github.com/sipXcom/sipXlang-es_MX.git [submodule "sipXlang-nl"] path = sipXlang-nl - url = git://github.com/SIPfoundry/sipXlang-nl.git + url = git://github.com/sipXcom/sipXlang-nl.git [submodule "sipXlang-pl"] path = sipXlang-pl - url = git://github.com/SIPfoundry/sipXlang-pl.git + url = git://github.com/sipXcom/sipXlang-pl.git [submodule "sipXlang-pt_BR"] path = sipXlang-pt_BR - url = git://github.com/SIPfoundry/sipXlang-pt_BR.git + url = git://github.com/sipXcom/sipXlang-pt_BR.git [submodule "sipXlang-zh"] path = sipXlang-zh - url = git://github.com/SIPfoundry/sipXlang-zh.git + url = git://github.com/sipXcom/sipXlang-zh.git [submodule "oss_core"] path = oss_core - url = git://github.com/ezuce/oss_core.git + url = git://github.com/joegen/oss_core.git [submodule "sipXsbc"] path = sipXsbc url = git://github.com/ezuce/sipXsbc.git [submodule "openfire"] path = openfire - url = git://github.com/SIPfoundry/openfire.git + url = git://github.com/sipXcom/openfire.git [submodule "oacd_freeswitch"] path = oacd_freeswitch url = git://github.com/sipxopenacd/oacd_freeswitch.git @@ -81,10 +81,19 @@ url = git://github.com/sipxopenacd/openacd.git [submodule "sipXcallQueue"] path = sipXcallQueue - url = git://github.com/SIPfoundry/sipXcallQueue.git + url = git://github.com/sipXcom/sipXcallQueue.git [submodule "sipXresiprocate"] path = sipXresiprocate - url = git://github.com/SIPfoundry/resiprocate.git + url = git://github.com/sipXcom/resiprocate.git [submodule "freeswitch_edge"] path = freeswitch_edge - url = git://github.com/SIPfoundry/freeswitch.git + url = git://github.com/sipXcom/freeswitch.git +[submodule "sipXAocBilling"] + path = sipXAocBilling + url = https://github.com/sipXcom/sipXAocBilling.git +[submodule "sipXdashboard"] + path = sipXdashboard + url = https://github.com/sipXcom/sipXdashboard.git +[submodule "sipXlang-pt"] + path = sipXlang-pt + url = https://github.com/sipXcom/sipXlang-pt.git diff --git a/Makefile.in b/Makefile.in old mode 100644 new mode 100755 diff --git a/build_docker_rpm.sh b/build_docker_rpm.sh new file mode 100755 index 0000000000..1f31dfb150 --- /dev/null +++ b/build_docker_rpm.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +for i in "$@" +do +case $i in + -s=*|--source-dir=*) + SOURCE_DIR="${i#*=}" + shift + ;; + -v=*|--version=*) + VERSION="${i#*=}" + shift + ;; + -p=*|--project=*) + PROJECT="${i#*=}" + shift + ;; + -u=*|--repo=*) + UPSTREAM_REPO="${i#*=}" + shift + ;; + *) + echo "Usage: -s|--source-dir: absolute path to source directory, e.g. /home/sipx/sipxecs + -v|--version: version of sipxcom RPM to build, e.g. 15.10 + -p|--project: project to build (or sipx for building all RPMs) or init for the first run, e.g. init, sipXconfig, sipx + +Sample: + ./build-rpm.sh --source-dir=/home/sipx/sipxecs --version=15.08 --project=init + ./build-rpm.sh --source-dir=/home/sipx/sipxecs --version=15.08 --project=sipx + ./build-rpm.sh --source-dir=/home/sipx/sipxecs --version=15.08 --project=sipXconfig" + exit + ;; +esac +done +if [ -z "${UPSTREAM_REPO}" ] +then +UPSTREAM_REPO="http://download.sipxcom.org/pub/${VERSION}-unstable/" +fi +sudo docker run -e "SIPXCOM_VERSION=${VERSION}" --rm -t --privileged -v ${SOURCE_DIR}:/home/sipx/sipxcom dizzy/docker-dev-rpm:15.10 ${PROJECT} ${UPSTREAM_REPO} diff --git a/config/revision-gen b/config/revision-gen index f71f04395f..60e082067b 100755 --- a/config/revision-gen +++ b/config/revision-gen @@ -24,6 +24,30 @@ if test -d ../.git; then fi VERSION=$1 +if [ $VERSION == "16.12" ]; then + VERSION="4.6.0" +fi +if [ $VERSION == "16.08" ]; then + VERSION="4.6.0" +fi +if [ $VERSION == "16.04" ]; then + VERSION="4.6.0" +fi +if [ $VERSION == "16.02" ]; then + VERSION="4.6.0" +fi +if [ $VERSION == "15.12" ]; then + VERSION="4.6.0" +fi +if [ $VERSION == "15.10" ]; then + VERSION="4.6.0" +fi +if [ $VERSION == "15.06" ]; then + VERSION="4.6.0" +fi +if [ $VERSION == "15.05" ]; then + VERSION="4.6.0" +fi if [ $VERSION == "15.04" ]; then VERSION="4.6.0" fi diff --git a/config/sipXlib.m4 b/config/sipXlib.m4 index bc9259da54..4a323433dc 100644 --- a/config/sipXlib.m4 +++ b/config/sipXlib.m4 @@ -797,31 +797,38 @@ AC_DEFUN([CHECK_SERVICEDIR], AC_SUBST([SERVICEDIR]) ]) -AC_DEFUN([OSSCORE_LIB], + +AC_DEFUN([SFAC_LIB_OSS_CORE], [ - AC_ARG_WITH(osscore, [--with-osscore={yes,no,osscore-location} Default is yes and will search - for osscore in standard locations], -osscore_prefix="$withval", -osscore_prefix="yes") + SFAC_ARG_WITH_INCLUDE([OSS/OSS.h], + [oss_coreinc], + [ --with-oss_coreinc= oss core include path ], + [oss_core]) - if test "$osscore_prefix" == "yes" ; then - osscore_prefix="${PREFIX}/lib /usr/lib /usr/lib64" - fi + if test x_$foundpath != x_; then + AC_MSG_RESULT($foundpath) + else + AC_MSG_WARN([ assuming it will be in '${prefix}/include']) + foundpath=${prefix}/include + fi + OSSCOREINC=$foundpath + AC_SUBST(OSSCOREINC) - if test "$osscore_prefix" != "no"; then - AC_MSG_CHECKING([for OssCore]) - for d in $osscore_prefix ; do - if test -f $d/liboss_core.la ; then - AC_MSG_RESULT([yes - $d]) - osscore_found=yes - break; - fi - done - fi + SFAC_ARG_WITH_LIB([liboss_core.la], + [oss_corelib], + [ --with-oss_corelib= oss core library path ], + [oss_core]) - if test -z "$osscore_found" ; then - AC_MSG_RESULT([no]) - fi + if test x_$foundpath != x_; then + AC_MSG_RESULT($foundpath) + else + AC_MSG_WARN([ assuming it will be in '${prefix}/lib']) + foundpath=${prefix}/lib + fi - AC_SUBST(OSSCORE_LIBS, "$d/liboss_core.la") -]) + OSSCORELIB=$foundpath + LIBRE="${prefix}/opt/ossapp/librem/lib/librem.a ${prefix}/opt/ossapp/libre/lib/libre.a" + + AC_SUBST(OSSCORE_LIBS,["$OSSCORELIB/liboss_core.la $OSSCORELIB/liboss_carp.la $LIBRE"]) + +]) # SFAC_LIB_OSS_CORE diff --git a/config/sipxconfig-integration-testing.am b/config/sipxconfig-integration-testing.am index 9c06c3e837..faaad97200 100644 --- a/config/sipxconfig-integration-testing.am +++ b/config/sipxconfig-integration-testing.am @@ -80,7 +80,7 @@ plugin-reset-integration-test : javac-test : $(SIPXCONFIG_TEST_CONFIG_FILES) -SIPXCONFIG_TEST_CONFIG_FILES = test.properties sipxconfig.properties +SIPXCONFIG_TEST_CONFIG_FILES = test.properties sipxconfig.properties postgres-pwd.properties # tip: to append to CLEANFILES, use CLEANFILES += A B C CLEANFILES = $(SIPXCONFIG_TEST_CONFIG_FILES) @@ -99,6 +99,10 @@ test.properties : DOMAIN=$(shell hostname -f) .PHONY: test.properties test.properties : @echo -e "$(subst $(space),\n,$(foreach V,local.etc.dir DOMAIN $(CommonVariablesNames),$(V)=$($(V))))" > $@ + + +postgres-pwd.properties : + @echo -e "" >$@ # customize spring beans for unit test environment, must include tab as first char otherwise # automake treats it differently diff --git a/config/sipxconfig.am b/config/sipxconfig.am index 15eba09d18..78cd1c98e4 100644 --- a/config/sipxconfig.am +++ b/config/sipxconfig.am @@ -40,12 +40,16 @@ sipxconfig_neoconf_PKGS = \ js-1.7R4 \ ldapbp \ lucene-core \ + lucene-analyzers-common \ + lucene-queryparser \ + lucene-suggest \ mime-dir-j-vcard4j \ mongo \ mysql-connector-java \ org.restlet \ org.restlet.ext.json \ org.restlet.ext.spring \ + org.restlet.ext.fileupload \ spring-aop \ spring-beans \ spring-context \ @@ -104,10 +108,13 @@ sipxconfig_neoconf_PKGS = \ jcl-over-slf4j \ spring-messaging \ jaudiotagger \ - spring-websocket \ - hazelcast-all \ - reflections \ - guava + spring-websocket \ + hazelcast-all \ + reflections \ + guava \ + elasticsearch \ + gson \ + commons-cli sipxconfig_web_PKGS = \ $(sipxconfig_neoconf_PKGS) \ diff --git a/configure.ac b/configure.ac index 802fa8c21c..0347969e6b 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ # AC_PREREQ(2.57) -AC_INIT(sipX, 15.04, sipx-dev@list.sipfoundry.org) +AC_INIT(sipX, 16.12, sipx-dev@list.sipfoundry.org) # Pass standard and sipxecs common params to ./configure switches on all sub-projects. # Doesn't have to handle ./configure vars like SIPX_PBXUSER because those diff --git a/epel/.sipxecs.mk b/epel/.sipxecs.mk index 2aa8a3780a..8efdd68925 100644 --- a/epel/.sipxecs.mk +++ b/epel/.sipxecs.mk @@ -34,24 +34,16 @@ BUILD_EPEL = \ Canna-libs-* \ ccache-* \ compface-1.5*\ - erlang-erlydtl-* \ - erlang-getopt-* \ - erlang-gettext-* \ - erlang-lfe-* \ - erlang-mustache-* \ - erlang-neotoma-* \ - erlang-protobuffs-* \ - erlang-meck-* \ - erlang-rebar-* \ + erlang-* \ gperftools-devel-* \ gtest-devel-* \ gyp-* \ http-parser-* \ http-parser-devel-* \ + libmcrypt-devel-* \ libdnet-* \ libdnet-devel-* \ libev-* \ - libmongodb-devel-2.4* \ libuv-* \ libuv-devel-* \ neXtaw-* \ @@ -74,6 +66,7 @@ BUILD_EPEL = \ # Technincally these could be build and runtime requirements RUNTIME_EPEL = \ + elasticsearch-* \ erlang-lager-* \ erlang-gen_leader-* \ erlang-gproc-* \ @@ -87,11 +80,8 @@ RUNTIME_EPEL = \ libiodbc-3* \ libev-4.* \ libmcrypt-* \ - libmongodb-2.4* \ libunwind-* \ monit-* \ - mongodb-2.4* \ - mongodb-server-2.4* \ openpgm-5* \ php-pecl-mongo-* \ poco-crypto-* \ diff --git a/freeswitch b/freeswitch index 7c5c602d65..00a87f46cf 160000 --- a/freeswitch +++ b/freeswitch @@ -1 +1 @@ -Subproject commit 7c5c602d657cda189b00b2cc1d26c13f0f424e0c +Subproject commit 00a87f46cf26bbc1b78943101754e01c991698db diff --git a/freeswitch-sounds-en-us-callie/.sipxecs.mk b/freeswitch-sounds-en-us-callie/.sipxecs.mk new file mode 100644 index 0000000000..32de4707a5 --- /dev/null +++ b/freeswitch-sounds-en-us-callie/.sipxecs.mk @@ -0,0 +1,11 @@ +freeswitch-sounds-en-us-callie_VER = 1.0.51 +freeswitch-sounds-en-us-callie_REL = 1 +freeswitch-sounds-en-us-callie_SRPM = freeswitch-sounds-en-us-callie-$(freeswitch-sounds-en-us-callie_VER)-$(freeswitch-sounds-en-us-callie_REL)$(RPM_DIST).src.rpm +freeswitch-sounds-en-us-callie_SPEC = $(SRC)/$(PROJ)/freeswitch-sounds-en-us-callie.spec +freeswitch-sounds-en-us-callie_SOURCES = \ + freeswitch-sounds-en-us-callie-8000-1.0.51.tar.gz \ + freeswitch-sounds-en-us-callie-16000-1.0.51.tar.gz \ + freeswitch-sounds-en-us-callie-32000-1.0.51.tar.gz \ + freeswitch-sounds-en-us-callie-48000-1.0.51.tar.gz + +freeswitch-sounds-en-us-callie.dist:; diff --git a/freeswitch-sounds-en-us-callie/freeswitch-sounds-en-us-callie.spec b/freeswitch-sounds-en-us-callie/freeswitch-sounds-en-us-callie.spec new file mode 100644 index 0000000000..cb0153a6a2 --- /dev/null +++ b/freeswitch-sounds-en-us-callie/freeswitch-sounds-en-us-callie.spec @@ -0,0 +1,361 @@ +############################################################################## +# Copyright and license +############################################################################## +# +# Spec file for package freeswitch-sounds-en-us-callie (version 1.0.18-1) +# +# Copyright (c) 2009 Patrick Laimbock +# Some fixes and additions (c) 2011 Michal Bielicki +# This file and all modifications and additions to the pristine +# package are under the same license as the package itself. +# + +############################################################################## +# Determine distribution +############################################################################## + +# %define is_rhel5 %(test -f /etc/redhat-release && egrep -q 'release 5' /etc/redhat-release && echo 1 || echo 0) + +############################################################################## +# Set variables +############################################################################## + +%define version 1.0.51 +%define release 1 + +%define fsname freeswitch +# you could add a version number to be more strict + +%define PREFIX %{_prefix} +%define EXECPREFIX %{_exec_prefix} +%define BINDIR %{_bindir} +%define SBINDIR %{_sbindir} +%define LIBEXECDIR %{_libexecdir}/%{fsname} +%define SYSCONFDIR %{_sysconfdir}/%{fsname} +%define SHARESTATEDIR %{_sharedstatedir}/%{fsname} +%define LOCALSTATEDIR %{_localstatedir}/lib/%{fsname} +%define LIBDIR %{_libdir} +%define INCLUDEDIR %{_includedir} +%define _datarootdir %{_prefix}/share +%define DATAROOTDIR %{_datarootdir} +%define DATADIR %{_datadir} +%define INFODIR %{_infodir} +%define LOCALEDIR %{_datarootdir}/locale +%define MANDIR %{_mandir} +%define DOCDIR %{_defaultdocdir}/%{fsname} +%define HTMLDIR %{_defaultdocdir}/%{fsname}/html +%define DVIDIR %{_defaultdocdir}/%{fsname}/dvi +%define PDFDIR %{_defaultdocdir}/%{fsname}/pdf +%define PSDIR %{_defaultdocdir}/%{fsname}/ps +%define LOGFILEDIR /var/log/%{fsname} +%define MODINSTDIR %{_libdir}/%{fsname}/mod +%define RUNDIR %{_localstatedir}/run/%{fsname} +%define DBDIR %{LOCALSTATEDIR}/db +%define HTDOCSDIR %{_datarootdir}/%{fsname}/htdocs +%define SOUNDSDIR %{_datarootdir}/%{fsname}/sounds +%define GRAMMARDIR %{_datarootdir}/%{fsname}/grammar +%define SCRIPTDIR %{_datarootdir}/%{fsname}/scripts +%define RECORDINGSDIR %{LOCALSTATEDIR}/recordings +%define PKGCONFIGDIR %{_datarootdir}/%{fsname}/pkgconfig +%define HOMEDIR %{LOCALSTATEDIR} + + +############################################################################## +# General +############################################################################## + +Summary: FreeSWITCH en-us Callie prompts +Name: freeswitch-sounds-en-us-callie +Version: %{version} +Release: %{release}%{?dist} +License: MPL +Group: Applications/Communications +Packager: Patrick Laimbock +URL: http://www.freeswitch.org +Source0:http://files.freeswitch.org/releases/sounds/%{name}-48000-%{version}.tar.gz +Source1:http://files.freeswitch.org/releases/sounds/%{name}-32000-%{version}.tar.gz +Source2:http://files.freeswitch.org/releases/sounds/%{name}-16000-%{version}.tar.gz +Source3:http://files.freeswitch.org/releases/sounds/%{name}-8000-%{version}.tar.gz +BuildArch: noarch +BuildRequires: sox +Requires: freeswitch +Requires: freeswitch-sounds-en-us-callie-48000 +Requires: sox +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +%description +FreeSWITCH 48kHz en-us Callie prompts plus, during the installation, +it will also install locally generated 8KHz, 16KHz and 32KHz prompts + +%package -n freeswitch-sounds-en-us-callie-8000 +Summary: FreeSWITCH 8kHz en-us Callie prompts +Group: Applications/Communications +BuildArch: noarch +Requires: %{fsname} + +%description -n freeswitch-sounds-en-us-callie-8000 +FreeSWITCH 8kHz en-us Callie prompts + +%package -n freeswitch-sounds-en-us-callie-16000 +Summary: FreeSWITCH 16kHz en-us Callie prompts +Group: Applications/Communications +BuildArch: noarch +Requires: %{fsname} + +%description -n freeswitch-sounds-en-us-callie-16000 +FreeSWITCH 16kHz en-us Callie prompts + +%package -n freeswitch-sounds-en-us-callie-32000 +Summary: FreeSWITCH 32kHz en-us Callie prompts +Group: Applications/Communications +BuildArch: noarch +Requires: %{fsname} + +%description -n freeswitch-sounds-en-us-callie-32000 +FreeSWITCH 32kHz en-us Callie prompts + +%package -n freeswitch-sounds-en-us-callie-48000 +Summary: FreeSWITCH 48kHz en-us Callie prompts +Group: Applications/Communications +BuildArch: noarch +Requires: %{fsname} + +%description -n freeswitch-sounds-en-us-callie-48000 +FreeSWITCH 48kHz en-us Callie prompts + +%package -n freeswitch-sounds-en-us-callie-all +Summary: FreeSWITCH en-us Callie prompts +Group: Applications/Communications +BuildArch: noarch +Requires: %{fsname} +Requires: freeswitch-sounds-en-us-callie-8000 = %{version} +Requires: freeswitch-sounds-en-us-callie-16000 = %{version} +Requires: freeswitch-sounds-en-us-callie-32000 = %{version} +Requires: freeswitch-sounds-en-us-callie-48000 = %{version} + +%description -n freeswitch-sounds-en-us-callie-all +FreeSWITCH Callie prompts package that pulls in the 8KHz, 16KHz, +32KHz and 48KHz RPMs + +############################################################################## +# Prep +############################################################################## + +%prep +%setup -n en +%setup -T -D -b 0 -n en +%setup -T -D -b 1 -n en +%setup -T -D -b 2 -n en +%setup -T -D -b 3 -n en + +############################################################################## +# Build +############################################################################## + +%build +# nothing to do here + +############################################################################## +# Install +############################################################################## + +%install +[ "%{buildroot}" != '/' ] && rm -rf %{buildroot} + +# create the sounds directories +%{__install} -d -m 0777 %{buildroot}%{SOUNDSDIR}/en/us/callie + +pushd us/callie +# first install the 48KHz sounds +%{__cp} -prv ./* %{buildroot}%{SOUNDSDIR}/en/us/callie +# now resample the 48KHz ones to 8KHz, 16KHz and 32KHz +popd + +############################################################################## +# Clean +############################################################################## + +%clean +[ "%{buildroot}" != '/' ] && rm -rf %{buildroot} + +############################################################################## +# Post +############################################################################## + +%post +# generate the 8KHz, 16KHz and 32KHz prompts from the 48KHz ones + +############################################################################## +# Postun +############################################################################## + +%postun +# you could check if there are sound files in 8000/ or +# 16000/ or 32000/ and remove them *only* if the files +# do not belong to an rpm + +############################################################################## +# Files +############################################################################## + +%files +%defattr(-,root,root) + +%files -n freeswitch-sounds-en-us-callie-8000 +%defattr(-,root,root,-) +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/ascii/8000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/base256/8000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/conference/8000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/currency/8000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/digits/8000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/directory/8000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/ivr/8000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/misc/8000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/phonetic-ascii/8000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/time/8000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/voicemail/8000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/zrtp/8000 +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/ascii/8000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/base256/8000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/conference/8000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/currency/8000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/digits/8000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/directory/8000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/ivr/8000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/misc/8000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/phonetic-ascii/8000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/time/8000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/voicemail/8000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/zrtp/8000/*.wav + +%files -n freeswitch-sounds-en-us-callie-16000 +%defattr(-,root,root,-) +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/ascii/16000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/base256/16000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/conference/16000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/currency/16000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/digits/16000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/directory/16000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/ivr/16000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/misc/16000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/phonetic-ascii/16000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/time/16000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/voicemail/16000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/zrtp/16000 +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/ascii/16000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/base256/16000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/conference/16000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/currency/16000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/digits/16000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/directory/16000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/ivr/16000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/misc/16000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/phonetic-ascii/16000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/time/16000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/voicemail/16000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/zrtp/16000/*.wav + +%files -n freeswitch-sounds-en-us-callie-32000 +%defattr(-,root,root,-) +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/ascii/32000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/base256/32000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/conference/32000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/currency/32000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/digits/32000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/directory/32000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/ivr/32000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/misc/32000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/phonetic-ascii/32000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/time/32000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/voicemail/32000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/zrtp/32000 +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/ascii/32000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/base256/32000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/conference/32000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/currency/32000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/digits/32000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/directory/32000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/ivr/32000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/misc/32000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/phonetic-ascii/32000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/time/32000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/voicemail/32000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/zrtp/32000/*.wav + +%files -n freeswitch-sounds-en-us-callie-48000 +%defattr(-,root,root,-) +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/ascii/48000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/base256/48000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/conference/48000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/currency/48000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/digits/48000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/directory/48000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/ivr/48000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/misc/48000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/phonetic-ascii/48000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/time/48000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/voicemail/48000 +%attr(0777,freeswitch,daemon) %dir %{SOUNDSDIR}/en/us/callie/zrtp/48000 +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/ascii/48000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/base256/48000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/conference/48000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/currency/48000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/digits/48000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/directory/48000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/ivr/48000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/misc/48000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/phonetic-ascii/48000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/time/48000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/voicemail/48000/*.wav +%attr(0777,freeswitch,daemon) %{SOUNDSDIR}/en/us/callie/zrtp/48000/*.wav + +%files -n freeswitch-sounds-en-us-callie-all + +############################################################################## +# Changelog +############################################################################## + +%changelog +* Sun Mar 05 2012 Ken Rice - 1.0.18-1 +- update to FHS Layout for FreeSWITCH +- bump up version +* Sun May 22 2011 Michal Bielicki - 1.0.16-1 +- bump up version +* Tue Jan 18 2011 Michal Bielicki - 1.0.14-1 +- bump up version +- include script into freeswitch core +- include specfile into freeswitch core +- runtime does not require sox, only building + +* Thu Dec 17 2009 Patrick Laimbock - 1.0.12-8 +- update perms and user/group to sync with the old situation + +* Wed Dec 16 2009 Patrick Laimbock - 1.0.12-7 +- make main package require freeswitch-sounds-en-us-callie-48000 and +- generate the 8KHz, 16KHz and 32KHz sounds from there +- add license to spec file + +* Wed Dec 16 2009 Patrick Laimbock - 1.0.12-5 +- put 48KHz in a separate package and let the main package Require 48KHz +- and then use the script to generate the 8KHz, 16KHz and 32KHz sounds + +* Wed Dec 16 2009 Patrick Laimbock - 1.0.12-4 +- add freeswitch-sounds-en-us-callie-all package that pulls in the 8KHz, +- 16KHz, 32KHz and 48KHz RPM packages + +* Tue Dec 15 2009 Patrick Laimbock - 1.0.12-3 +- override subpackage name with -n so it no longer builds an empty main RPM +- rework spec file +- add sox as a requirement +- run buildsounds-callie.sh in post to generate 8KHz, 16KHz and 32KHz prompts + +* Tue Dec 15 2009 Patrick Laimbock - 1.0.12-2 +- can't override Name in subpackage so put all versions in RPM subpackages +- with an empty main RPM package + +* Tue Dec 15 2009 Patrick Laimbock - 1.0.12-1 +- create spec file with the following requirement: +- source only contains the 48KHz sound prompts +- during build the 48KHz sound prompts are resampled to 8KHz, 16KHz and 32KHz +- the 8KHz, 16KHz, 32KHz and 48KHz sound prompts are packaged separately + diff --git a/jasperserver/.sipxecs.mk b/jasperserver/.sipxecs.mk index 0833ee7190..2e3b1b4e99 100644 --- a/jasperserver/.sipxecs.mk +++ b/jasperserver/.sipxecs.mk @@ -1,6 +1,6 @@ -jasperserver_SRPM = jasperserver-5.2.0-1.src.rpm +jasperserver_SRPM = jasperserver-6.2.0-1.src.rpm jasperserver_SPEC = $(SRC)/$(PROJ)/jasperserver.spec -jasperserver_SOURCES = $(SRC)/$(PROJ)/jasperserver-5.2.0.tar.gz +jasperserver_SOURCES = $(SRC)/$(PROJ)/jasperserver-6.2.0.tar.gz jasperserver.dist:; diff --git a/jasperserver/jasperserver.spec b/jasperserver/jasperserver.spec index 6e646290b7..e080927802 100644 --- a/jasperserver/jasperserver.spec +++ b/jasperserver/jasperserver.spec @@ -1,5 +1,5 @@ Name: jasperserver -Version: 5.2.0 +Version: 6.2.0 Release: 1 Summary: Jasperserver reports war file diff --git a/mak/build.mk.in b/mak/build.mk.in index 154ed12f1d..ac3da99d1e 100644 --- a/mak/build.mk.in +++ b/mak/build.mk.in @@ -68,7 +68,8 @@ $(foreach P,$(custommodules),$(SRC)/$(P)) : $(SRC)/% : $(foreach P,$(git_submodules),$(SRC)/$(P)/.git) : $(SRC)/%/.git : cd $(SRC); \ git submodule init $*; \ - git submodule update $* + git submodule update $*; \ + git submodule foreach 'git submodule init; git submodule update;' .PHONY:$(sipx) help.* = Compile and install from source this a specific project. Assumes all dependencies are already compiled and installed. diff --git a/mak/centos-iso.mk.in b/mak/centos-iso.mk.in index 990e3649ea..47be99c25e 100644 --- a/mak/centos-iso.mk.in +++ b/mak/centos-iso.mk.in @@ -4,16 +4,16 @@ ## Common make variables you may want to override in another makefile ISO_SRC ?= $(SRC)/mak/centos-iso -ISO_LABEL ?= sipxecs +ISO_LABEL ?= sipxcom ISO_REV ?= $(PACKAGE_REVISION) DIST_DIR = @DIST_DIR@ SPLASH_FILE ?= $(ISO_SRC)/splash.jpg SPLASH_MENU ?= $(ISO_SRC)/isolinux.cfg VOLUME_LABEL ?= "CentOS sipX $(PACKAGE_VERSION)" APPLICATION_LABEL ?= "CentOS sipX $(PACKAGE_VERSION)" -PUBLISHER_ID ?= sipx-dev@list.sipfoundry.org -PREPARERER_ID ?= sipx-dev@list.sipfoundry.org -CENTOS_VER = 6.5 +PUBLISHER_ID ?= sipxcom-dev@googlegroups.com +PREPARERER_ID ?= sipxcom-dev@googlegroups.com +CENTOS_VER = 6.7 ISO_RPM_DOWNLOAD_URL ?= @CENTOS_BASE_URL@/$(CENTOS_VER) ISO_M4_OPTS = $(CUSTOM_ISO_M4_OPTS) -D PACKAGE_VERSION=$(PACKAGE_VERSION) ISO_PACKAGES_OS=$(ARCH)-os-Packages @@ -119,7 +119,7 @@ iso-download-os-% : mak/*-centos-iso.mk iso-download-epel-% : mak/*-centos-iso.mk rsync -av --delete \ - $(addprefix @CENTOS_RSYNC_URL@/epel/6/$(ARCH)/,$(ISO_ADD_EPEL)) \ + $(addprefix rsync://download.sipxcom.org/pub/epel/6/$(ARCH)/,$(ISO_ADD_EPEL)) \ $(ISO_PACKAGES_EPEL)/ iso-packages-% : iso-download-os-% iso-download-epel-% @@ -128,9 +128,24 @@ iso-packages-% : iso-download-os-% iso-download-epel-% rsync -av $(realpath $(RPM_DIST_DIR))/CentOS_6/$(ARCH)/ $(ISO_DISC)/Packages/ find $(ISO_DISC)/Packages \( \ -not -name 'java-1.6.0-openjdk-devel-*.rpm' -a \ + -not -name 'nodejs-devel-*.rpm' -a \ + -not -name 'v8-devel-*.rpm' -a \ + -not -name 'libuv-devel-*.rpm' -a \ + -not -name 'libstdc++-devel-*.rpm' -a \ + -not -name 'libgnat-devel-*.rpm' -a \ + -not -name 'libgcj-devel-*.rpm' -a \ + -not -name 'zlib-devel-*.rpm' -a \ + -not -name 'c-ares19-devel-*.rpm' -a \ + -not -name 'openssl-devel-*.rpm' -a \ + -not -name 'http-parser-devel-*.rpm' -a \ -not -name 'db4-devel-*.rpm' -a \ -not -name 'gdbm-devel-*.rpm' -a \ -not -name 'glibc-devel-*.rpm' -a \ + -not -name 'keyutils-libs-devel-*.rpm' -a \ + -not -name 'krb5-devel-*.rpm' -a \ + -not -name 'libcom_err-devel-*.rpm' -a \ + -not -name 'libselinux-devel-*.rpm' -a \ + -not -name 'libsepol-devel-*.rpm' -a \ -not -name 'perl-devel-*.rpm' \) \ | grep $(foreach REGEX,$(ISO_RM_LIST),-e '.*/$(REGEX)') \ | xargs rm @@ -144,15 +159,28 @@ ISO_RM_LIST = \ .*-static-.* \ bakefile-.* \ emacs-erlang-.* \ + erlang-bitcask-* \ + erlang-erlsyslog-* \ + erlang-esasl-* \ + erlang-inviso-* \ freeswitch-freetdm-.* \ freeswitch-java-.* \ - freeswitch-lang-.* \ freeswitch-perl-.* \ freeswitch-python-.* \ freeswitch-skypopen-.* \ freeswitch-spidermonkey-.* \ - oss_core-.* \ + homer-* \ + net-snmp-perl-* \ + net-snmp-gui-* \ + nodejs-burrito-.* \ + nodejs-bunker-.* \ + nodejs-jscoverage-.* \ + nodejs-muffin-.* \ + nodejs-tap-.* \ + nodejs-snockets-.* \ + nodejs-runforcover-.* \ sipxcustomcallerid.* \ + sipxhomer-* \ sipxsbc.* \ sipxtest.* \ xemacs-.* \ @@ -172,18 +200,24 @@ ISO_RM_LIST = \ # JV believes erlang list can be dramatically reduced but we haven't investigated ISO_ADD_EPEL = \ + c-ares19-devel-1* \ + cloc-1.58-* \ + coffee-script-1.* \ + coffee-script-common-1.* \ fail2ban-0.8.10* \ js-* \ gperftools-libs-2.0-11* \ gtest-1* \ + http-parser-devel-* \ libiodbc-3* \ libev-4.* \ libmcrypt-* \ - libmongodb-2.4* \ + libuv-devel-* \ monit-5.1* \ - mongodb-2.4* \ - mongodb-server-2.4* \ + nodejs-devel-0.* \ + npm-1.3.* \ openpgm-5* \ + perl-Regexp-Common-* \ perl-Tk-804.* \ php-pecl-mongo-1.4* \ poco-crypto-* \ @@ -204,10 +238,14 @@ ISO_ADD_EPEL = \ shorewall-4* \ shorewall-core-* \ snappy-* \ + uglify-js-* \ + uglify-js-common-* \ v8-3.14.5.10* \ + v8-devel-3.14.5.10-* \ wxBase-* \ wxGTK-2.* \ wxGTK-gl-* \ + ycssmin-1.* \ zeromq-* ISO_ADD_OS = \ @@ -267,11 +305,13 @@ ISO_ADD_OS = \ chkconfig-* \ classpathx-jaf-1* \ classpathx-mail-1* \ + cloog-ppl-0* \ compat-expat1-* \ compat-readline5-* \ coreutils-* \ coreutils-libs-* \ cpio-* \ + cpp-* \ cracklib-2.* \ cracklib-dicts-* \ cronie-1.* \ @@ -306,7 +346,9 @@ ISO_ADD_OS = \ ed-* \ efibootmgr-* \ eggdbus-* \ + elfutils-0* \ elfutils-libelf-* \ + elfutils-libs-* \ ethtool-* \ expat-* \ file-libs-* \ @@ -322,7 +364,10 @@ ISO_ADD_OS = \ gamin-python-* \ gawk-* \ GConf2-2* \ + gcc-* \ + gd-2.* \ gdb-7.* \ + gdk-pixbuf2-* \ gdbm-* \ gettext-0* \ giflib-* \ @@ -333,10 +378,12 @@ ISO_ADD_OS = \ gmp-* \ gnutls-2.* \ gpgme-* \ + graphviz-2.* \ grep-* \ groff-1.* \ grub-* \ grubby-* \ + gsm-1.0.* \ gstreamer-0* \ gstreamer-plugins-base-* \ gstreamer-tools-* \ @@ -379,20 +426,29 @@ ISO_ADD_OS = \ java-1.6.0-openjdk-1.* \ java-1.6.0-openjdk-devel-* \ java_cup-0.* \ + java-1.7.0-openjdk-1.* \ jline-* \ jpackage-utils-* \ kbd-1.* \ kbd-misc-* \ + keyutils-* \ kernel-2.* \ kernel-firmware-* \ kernel-headers-* \ keyutils-libs-* \ + krb5-devel-* \ krb5-libs-* \ less-* \ libart_lgpl-2* \ + libcanberra-0.* \ + libcanberra-gtk2-* \ + libgnat-devel-4.4.7-* \ + libmemcached-0.* \ + libwnck-2.* \ libevent-1.* \ libgomp-4.* \ libgudev1-* \ + libgssglue-* \ libICE-* \ libSM-* \ libX11-1.* \ @@ -404,19 +460,23 @@ ISO_ADD_OS = \ libXext-* \ libXfixes-* \ libXft-* \ + libXfont-1.* \ libXi-* \ libXinerama-* \ libXmu-1.* \ libXpm-* \ libXrandr-* \ libXrender-* \ + libXres-* \ libXtst-* \ libXxf86vm-* \ libacl-* \ libasyncns-* \ libattr-* \ + libao-0.8.* \ libblkid-* \ libcap-2.* \ + libcgroup-0.* \ libcap-ng-0.* \ libcom_err-* \ libconfig-* \ @@ -425,31 +485,44 @@ ISO_ADD_OS = \ libedit-* \ libertas-usb8388-firmware-* \ libffi-* \ + libfontenc-1.* \ libgcc-* \ - libgcj-4* \ + libgcj-4.* \ + libgcj-devel-4.4.7-* \ libgcrypt-* \ + libgfortran-* \ + libgnat-* \ libgpg-error-* \ libicu-* \ libidn-* \ libIDL-* \ libjpeg-* \ + libglade2-2.* \ libnih-* \ libnl-* \ + libnotify-* \ libogg-* \ + libobjc-4.* \ liboil-* \ libpcap-* \ libpng-* \ libselinux-2.* \ + libselinux-devel-* \ libselinux-utils-* \ libsemanage-2.* \ libsepol-2.* \ + libsepol-devel-* \ libsndfile-* \ libss-* \ - libstdc++-* \ + libsamplerate-0.1.* \ + libstdc++-4.4.7-* \ + libstdc++-devel-* \ libtasn1-2.* \ + libtdb-1.* \ libthai-* \ libtheora-* \ libtiff-3.* \ + libtirpc-* \ libtool-ltdl-* \ libudev-* \ libusb-0.* \ @@ -459,8 +532,10 @@ ISO_ADD_OS = \ libvisual-* \ libvorbis-* \ libxcb-1.* \ + libXaw-1.* \ libXt-1.* \ libXv-1.* \ + lksctp-tools-1.* \ lm_sensors-libs-* \ log4j-1.* \ logrotate-* \ @@ -470,13 +545,14 @@ ISO_ADD_OS = \ mailx-12.* \ make-* \ man-1* \ - mesa-dri-drivers-9.* \ + mesa-dri-drivers-10.* \ mesa-dri-filesystem-* \ mesa-dri1-drivers-* \ mesa-libGL-* \ mesa-libGLU-* \ mesa-private-llvm-* \ mingetty-* \ + mpfr-2.* \ mod_ssl-* \ module-init-tools-* \ mysql-5.*\ @@ -491,18 +567,22 @@ ISO_ADD_OS = \ net-tools-* \ newt-0.* \ newt-python-* \ + nfs-utils-* \ nspr-* \ nss-3.* \ nss-softokn-* \ nss-softokn-freebl-* \ nss-sysinit-* \ nss-util-* \ + nss-tools-* \ ntp-4.* \ ntpdate-* \ + notification-daemon-* \ openldap-2.* \ openssh-5.* \ openssh-server-* \ openssl-1.* \ + openssl-devel-1* \ ORBit2-* \ pam-* \ pango-* \ @@ -513,6 +593,7 @@ ISO_ADD_OS = \ pciutils-libs-* \ pcre-7.* \ perl-5.* \ + perl-Algorithm-Diff-1* \ perl-devel-5.* \ perl-CGI-3.* \ perl-DBD-MySQL-* \ @@ -525,6 +606,7 @@ ISO_ADD_OS = \ perl-Pod-Simple-* \ perl-Test-Harness-3.* \ perl-Test-Simple-0.* \ + perl-TimeDate-1.* \ perl-libs-5.* \ perl-version-* \ pkgconfig-* \ @@ -549,6 +631,7 @@ ISO_ADD_OS = \ postgresql-odbc-* \ postgresql-server-* \ portreserve-* \ + ppl-0.* \ procmail-* \ procps-* \ psmisc-* \ @@ -579,10 +662,13 @@ ISO_ADD_OS = \ readline-6.* \ redhat-logos-* \ redhat-lsb-core-4* \ + redhat-rpm-config* \ regexp-1* \ rhino-1.* \ rootfiles-* \ + rpcbind-* \ rpm-4.* \ + rpm-build-4.8* \ rpm-libs-* \ rpm-python-* \ rsyslog-5.* \ @@ -608,7 +694,11 @@ ISO_ADD_OS = \ sqlite-3.* \ strace-* \ stunnel-* \ + startup-notification-0.* \ sudo-* \ + sound-theme-freedesktop-* \ + sox-14.2.* \ + speex-1.* \ system-config-firewall-base-* \ system-config-network-tui-* \ sysvinit-tools-* \ @@ -616,6 +706,7 @@ ISO_ADD_OS = \ tcl-8.* \ tcp_wrappers-7* \ tcp_wrappers-libs-* \ + tcpdump-* \ tftp-server-* \ time-1.* \ tk-* \ @@ -627,26 +718,37 @@ ISO_ADD_OS = \ tomcat6-lib-* \ tomcat6-webapps-* \ tuned-0.* \ + ttmkfdir-* \ tzdata-* \ udev-* \ + unzip-* \ unixODBC-2.* \ upstart-* \ + urw-fonts-* \ usermode-1.* \ ustr-1.* \ util-linux-ng-* \ vim-minimal-* \ vsftpd-* \ + wavpack-4.60-* \ wget-* \ which-* \ wireless-tools-* \ wsdl4j-1.* \ xalan-j2-2.* \ + xcb-util-0.* \ xerces-c-3.* \ xinetd-* \ xml-common-0* \ xml-commons-apis-1* \ xml-commons-resolver-1* \ xorg-x11-drv-ati-firmware-* \ + xorg-x11-fonts-Type1-* \ + xorg-x11-font-utils-* \ + xmlrpc-c-1.* \ + xmlrpc-c-client-1.* \ + xmlrpc-c-c++-1.* \ + xmlrpc-c-client++-1.* \ xz-4.* \ xz-libs-* \ xz-lzma-compat-4.* \ @@ -655,5 +757,6 @@ ISO_ADD_OS = \ yum-plugin-fastestmirror-* \ zd1211-firmware-* \ zip-3* \ - zlib-1.* + zlib-1.* \ + zlib-devel-1.* diff --git a/mak/centos-iso/isolinux.cfg b/mak/centos-iso/isolinux.cfg index 3efa1d9a2c..24b1e87ed5 100644 --- a/mak/centos-iso/isolinux.cfg +++ b/mak/centos-iso/isolinux.cfg @@ -5,7 +5,7 @@ timeout 0 display boot.msg menu background splash.jpg -menu title Welcome to sipXecs Unified Communications System +menu title Welcome to sipXcom Unified Communications System menu color border 0 #ffffffff #00000000 menu color sel 7 #ffffffff #ff000000 menu color title 0 #ffffffff #00000000 @@ -15,12 +15,12 @@ menu color hotsel 0 #ff000000 #ffffffff menu color hotkey 7 #ffffffff #ff000000 menu color scrollbar 0 #ffffffff #00000000 label sipx - menu label ^Install sipXecs + menu label ^Install sipXcom menu default kernel vmlinuz append initrd=initrd.img ramdisk_size=8192 ks=cdrom:/ks.cfg asknetwork noipv6 label sipx-manual-partition - menu label ^Install sipXecs with custom disk partitioning + menu label ^Install sipXcom with custom disk partitioning kernel vmlinuz append initrd=initrd.img ramdisk_size=8192 ks=cdrom:/ks-manual-partition.cfg asknetwork noipv6 label rescue diff --git a/mak/centos-iso/ks.m4 b/mak/centos-iso/ks.m4 index 31e468c0cc..e548acf5aa 100644 --- a/mak/centos-iso/ks.m4 +++ b/mak/centos-iso/ks.m4 @@ -4,9 +4,9 @@ sipxecs define(`repo_filename',`sipxecs.repo') define(`repo_contents', -[sipXecs] -name=sipXecs for CentOS - \$basearch -baseurl=http://download.sipfoundry.org/pub/sipXecs/PACKAGE_VERSION()/CentOS_6/\$basearch +[sipXcom] +name=sipXcom for CentOS - \$basearch +baseurl=http://download.sipxcom.org/pub/sipXecs/PACKAGE_VERSION()/CentOS_6/\$basearch enabled=1 gpgcheck=0 ) @@ -15,7 +15,7 @@ dnl NOTE: You should start message with '====... if you want message to be remov dnl successul setup. See shell code in /root/.bashrc for details define(`welcome_message',` ========================== -Welcome to SIPfoundry sipXecs. +Welcome to sipXcom sipXecs. After logging in as root you will automatically be taken through a setup procedure. diff --git a/mak/centos-iso/splash.jpg b/mak/centos-iso/splash.jpg index a046c5b83a..334f809e6c 100644 Binary files a/mak/centos-iso/splash.jpg and b/mak/centos-iso/splash.jpg differ diff --git a/mak/freeswitch.sipxecs.mk b/mak/freeswitch.sipxecs.mk index 4098608646..6cd9e41da3 100644 --- a/mak/freeswitch.sipxecs.mk +++ b/mak/freeswitch.sipxecs.mk @@ -1,4 +1,4 @@ -freeswitch_VER = 1.4.15 +freeswitch_VER = 1.4.20.1 freeswitch_TAG = 1.4.4 freeswitch_PACKAGE_REVISION = $(shell cd $(SRC)/$(PROJ); ../config/revision-gen $(freeswitch_TAG)) freeswitch_SRPM = freeswitch-$(freeswitch_VER)-$(freeswitch_PACKAGE_REVISION).src.rpm diff --git a/mak/list-dependencies.mk.in b/mak/list-dependencies.mk.in index 3761029e39..935380280b 100644 --- a/mak/list-dependencies.mk.in +++ b/mak/list-dependencies.mk.in @@ -100,7 +100,7 @@ $(sipx:=.deps): %.deps : -e 's/Requires://' \ -e 's/,/\n/g' \ -e '/SELF/d' \ - $(foreach P,$(sipx),-e '/$(P)/Id') \ + $(foreach P,$(sipx_all),-e '/$(P)/Id') \ $(REWRITE_DEPS_$(DISTRO_OS)_$(DISTRO_VER)) | \ awk '{print $$1}' | \ sort -u diff --git a/mak/mock/centos-6-i386.cfg.in b/mak/mock/centos-6-i386.cfg.in index 82f426584b..9c27670d1e 100644 --- a/mak/mock/centos-6-i386.cfg.in +++ b/mak/mock/centos-6-i386.cfg.in @@ -35,7 +35,7 @@ failovermethod=priority # Normally from EPEL, just a list of rpms to create a basic build system [buildsys-build] name=buildsys-build -baseurl=http://download.sipfoundry.org/pub/sipXecs/buildsys-build +baseurl=http://download.sipxcom.org/pub/sipXecs/buildsys-build enabled=1 [upstream] diff --git a/mak/mock/centos-6-x86_64.cfg.in b/mak/mock/centos-6-x86_64.cfg.in index 9e4d05e8d2..329bd7a280 100644 --- a/mak/mock/centos-6-x86_64.cfg.in +++ b/mak/mock/centos-6-x86_64.cfg.in @@ -36,7 +36,7 @@ failovermethod=priority # Normally from EPEL, just a list of rpms to create a basic build system [buildsys-build] name=buildsys-build -baseurl=http://download.sipfoundry.org/pub/sipXecs/buildsys-build/ +baseurl=http://download.sipxcom.org/pub/sipXecs/buildsys-build/ enabled=1 [upstream] diff --git a/mak/modules.mk b/mak/modules.mk index 958a58ab61..5569db3bf8 100644 --- a/mak/modules.mk +++ b/mak/modules.mk @@ -9,6 +9,7 @@ sipx_core = \ sipXportLib \ sipXtackLib \ sipXresiprocate \ + oss_core \ sipXmediaLib \ sipXmediaAdapterLib \ sipXcallLib \ @@ -21,6 +22,7 @@ sipx_core = \ sipXbridge \ sipXcdr \ sipXconfig \ + sipXcom \ sipXopenfire \ sipXcounterpath \ sipXprompts \ @@ -34,6 +36,7 @@ sipx_core = \ sipXsaa \ sipXyard \ sipXrelease \ + sipXjitsi \ sipXecs #additional configure options for sipXresiprocate package @@ -52,7 +55,11 @@ sipx_extra = \ sipXrecording \ sipXhomer \ sipXcallQueue \ - sipXtools + sipXAocBilling \ + sipXtools \ + sipXcallback \ + sipXtcpdumplog \ + sipXdashboard # sipxecs projects that are NOT essential for a running communication system # and are related to configuration system. Many are phone plugins @@ -89,6 +96,7 @@ sipx_lang = \ sipXlang-es_MX \ sipXlang-nl \ sipXlang-pl \ + sipXlang-pt \ sipXlang-pt_BR \ sipXlang-zh @@ -105,6 +113,7 @@ lib_all = \ rubygem-file-tail \ erlang \ freeswitch \ + freeswitch-sounds-en-us-callie \ hiredis \ net-snmp \ homer \ @@ -158,7 +167,7 @@ sipXopenfire_DEPS = sipXconfig sipXsqa sipXcounterpath_DEPS = sipXconfig sipXaudiocodes_DEPS = sipXconfig sipXivr_DEPS = sipXconfig -sipXproxy_DEPS = sipXcommserverLib +sipXproxy_DEPS = sipXcommserverLib oss_core sipXpublisher_DEPS = sipXcommserverLib sipXregistry_DEPS = sipXcommserverLib sipXpage_DEPS = sipXcommons @@ -168,6 +177,7 @@ sipXsaa_DEPS = sipXsqa sipXcallLib sipXcommserverLib sipXhomer_DEPS = sipXsqa sipXresiprocate sipXsbc_DEPS = sipXconfig sipXsqa sipXregistry sipXcallQueue_DEPS = sipXconfig +sipXAocBilling_DEPS = sipXconfig sipXexample_DEPS = sipXcommserverLib sipXconfig sipXsss_DEPS = sipXsqa sipXcommserverLib sipXresiprocate sipXyard = sipXcommserverLib diff --git a/mak/oss_core.sipxecs.mk b/mak/oss_core.sipxecs.mk deleted file mode 100644 index 1a6e6f1721..0000000000 --- a/mak/oss_core.sipxecs.mk +++ /dev/null @@ -1,10 +0,0 @@ -oss_core_VER = 2.0.0 -oss_core_REV = $(shell cd $(SRC)/$(PROJ); ./config/revision-gen $(oss_core_VER)) -oss_core_TAR = $(PROJ)/oss_core-$(oss_core_VER).tar.gz -oss_core_SRPM = oss_core-$(oss_core_VER)-$(oss_core_REV).src.rpm - -oss_core.dist : oss_core.autoreconf oss_core.configure - cd $(SRC)/$(PROJ); \ - git submodule init; \ - git submodule update - $(MAKE) -C $(PROJ) dist diff --git a/mak/rpm.m4 b/mak/rpm.m4 index 84fb5de2b0..fb6d51ebf2 100644 --- a/mak/rpm.m4 +++ b/mak/rpm.m4 @@ -18,10 +18,10 @@ AC_ARG_ENABLE(upstream, [--disable-upstream Do not use upstream. Appropriate for ]) AC_SUBST(UPSTREAM) -AC_ARG_VAR(UPSTREAM_URL, [Where to find sipXecs distribution. Default: http://download.sipfoundry.org/pub/sipXecs]) +AC_ARG_VAR(UPSTREAM_URL, [Where to find sipXecs distribution. Default: http://download.sipxcom.org/pub/sipXecs]) if test -z "$UPSTREAM_URL"; then # This repo matches the release branch code slightly more closely then last release - UPSTREAM_URL=http://download.sipfoundry.org/pub/${PACKAGE_VERSION}-stable + UPSTREAM_URL=http://download.sipxcom.org/pub/${PACKAGE_VERSION}-stable else UPSTREAM_URL=`echo $UPSTREAM_URL | sed 's|/$||g'` fi @@ -114,8 +114,8 @@ AC_ARG_ENABLE(rpm, [--enable-rpm Using mock package to build rpms], AC_ARG_VAR(DOWNLOAD_LIB_CACHE, [When to cache source files that are downloaded, default ~/libsrc]) test -n "${DOWNLOAD_LIB_CACHE}" || DOWNLOAD_LIB_CACHE=~/libsrc - AC_ARG_VAR(DOWNLOAD_LIB_URL, [When to cache source files that are downloaded, default download.sipfoundry.org]) - test -n "${DOWNLOAD_LIB_URL}" || DOWNLOAD_LIB_URL=http://download.sipfoundry.org/pub/sipXecs/libs + AC_ARG_VAR(DOWNLOAD_LIB_URL, [When to cache source files that are downloaded, default download.sipxcom.org]) + test -n "${DOWNLOAD_LIB_URL}" || DOWNLOAD_LIB_URL=http://download.sipxcom.org/pub/sipXecs/libs AC_ARG_VAR(RPM_DIST_DIR, [Where to assemble final set of RPMs and SRPMs in preparation for publishing to a download server.]) test -n "${RPM_DIST_DIR}" || RPM_DIST_DIR=repo diff --git a/mak/setup.mk.in b/mak/setup.mk.in index 9e778155cd..7aa664accb 100644 --- a/mak/setup.mk.in +++ b/mak/setup.mk.in @@ -110,7 +110,7 @@ export rpm_setup_header define download_sipfoundry_repo_contents [sipxecs] name= sipXecs -baseurl=http://download.sipfoundry.org/pub/$(PACKAGE_VERSION)-unstable/$(CURRENT_DISTRO)_$$releasever/$$basearch +baseurl=http://download.sipxcom.org/pub/$(PACKAGE_VERSION)-unstable/$(CURRENT_DISTRO)_$$releasever/$$basearch enabled=1 gpgcheck=0 endef diff --git a/mongo-cxx-driver/.sipxecs.mk b/mongo-cxx-driver/.sipxecs.mk index 38fe78ab66..fda31b7e4f 100644 --- a/mongo-cxx-driver/.sipxecs.mk +++ b/mongo-cxx-driver/.sipxecs.mk @@ -1,10 +1,12 @@ mongo-cxx-driver_VER = 2.6.7 -mongo-cxx-driver_REL = 2 +mongo-cxx-driver_REL = 4 mongo-cxx-driver_SRPM = mongo-cxx-driver-$(mongo-cxx-driver_VER)-$(mongo-cxx-driver_REL)$(RPM_DIST).src.rpm mongo-cxx-driver_SPEC = $(SRC)/$(PROJ)/mongo-cxx-driver.spec mongo-cxx-driver_SOURCES = \ mongo-cxx-driver-legacy-0.0-26compat-2.6.7.tar.gz \ - $(SRC)/$(PROJ)/mongo-cxx-driver-2.6.7-logger.patch + $(SRC)/$(PROJ)/mongo-cxx-driver-2.6.7-logger.patch \ + $(SRC)/$(PROJ)/mongo-cxx-driver-2.6.7-maxTimeMS.patch \ + $(SRC)/$(PROJ)/mongo-cxx-driver-2.6.7-UC-4104.patch mongo-cxx-driver.dist:; diff --git a/mongo-cxx-driver/mongo-cxx-driver-2.6.7-UC-4104.patch b/mongo-cxx-driver/mongo-cxx-driver-2.6.7-UC-4104.patch new file mode 100644 index 0000000000..43eaaaa4d6 --- /dev/null +++ b/mongo-cxx-driver/mongo-cxx-driver-2.6.7-UC-4104.patch @@ -0,0 +1,72 @@ +From bfb47e9dab9c7d8c477f47f26a48aa27a061c7d4 Mon Sep 17 00:00:00 2001 +From: dizzy +Date: Wed, 1 Jun 2016 16:29:58 +0300 +Subject: [PATCH] UC-4104: refresh nodes + +--- + src/mongo/client/dbclient_rs.cpp | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/src/mongo/client/dbclient_rs.cpp b/src/mongo/client/dbclient_rs.cpp +index 9311bf3..47e88f1 100644 +--- a/src/mongo/client/dbclient_rs.cpp ++++ b/src/mongo/client/dbclient_rs.cpp +@@ -514,6 +514,10 @@ namespace { + _lastSlaveOkConn->getServerAddress() : "[not cached]" ) + << ")" << endl; + ++ if (_lastSlaveOkConn.get() == NULL) { ++ checkMaster(); ++ } ++ + string lastNodeErrMsg; + for (size_t retry = 0; retry < MAX_RETRY; retry++) { + try { +@@ -573,6 +577,10 @@ namespace { + _lastSlaveOkConn->getServerAddress() : "[not cached]" ) + << ")" << endl; + ++ if (_lastSlaveOkConn.get() == NULL) { ++ checkMaster(); ++ } ++ + string lastNodeErrMsg; + + for (size_t retry = 0; retry < MAX_RETRY; retry++) { +@@ -619,7 +627,7 @@ namespace { + verify(0); + } + +- void DBClientReplicaSet::isntMaster() { ++ void DBClientReplicaSet::isntMaster() { + log() << "got not master for: " << _masterHost << endl; + // Can't use _getMonitor because that will create a new monitor from the cached seed if + // the monitor doesn't exist. +@@ -627,7 +635,7 @@ namespace { + if ( monitor ) { + monitor->failedHost( _masterHost ); + } +- _master.reset(); ++ _master.reset(); + } + + auto_ptr DBClientReplicaSet::checkSlaveQueryResult( auto_ptr result ){ +@@ -948,13 +956,13 @@ namespace { + return false; + } + } +- ++ + LOG( 3 ) << "dbclient_rs call to primary node in " << _getMonitor()->getName() << endl; + + DBClientConnection* m = checkMaster(); + if ( actualServer ) + *actualServer = m->getServerAddress(); +- ++ + if ( ! m->call( toSend , response , assertOk ) ) + return false; + +-- +2.4.3 + diff --git a/mongo-cxx-driver/mongo-cxx-driver-2.6.7-maxTimeMS.patch b/mongo-cxx-driver/mongo-cxx-driver-2.6.7-maxTimeMS.patch new file mode 100644 index 0000000000..a6e69f4ae3 --- /dev/null +++ b/mongo-cxx-driver/mongo-cxx-driver-2.6.7-maxTimeMS.patch @@ -0,0 +1,76 @@ +diff -ur mongo-cxx-driver-legacy-0.0-26compat-2.6.7.orig/src/mongo/client/dbclient.cpp mongo-cxx-driver-legacy-0.0-26compat-2.6.7/src/mongo/client/dbclient.cpp +--- mongo-cxx-driver-legacy-0.0-26compat-2.6.7.orig/src/mongo/client/dbclient.cpp 2015-01-29 20:41:04.000000000 +0200 ++++ mongo-cxx-driver-legacy-0.0-26compat-2.6.7/src/mongo/client/dbclient.cpp 2015-02-18 18:10:30.342949382 +0200 +@@ -239,6 +239,7 @@ + const BSONField Query::ReadPrefField("$readPreference"); + const BSONField Query::ReadPrefModeField("mode"); + const BSONField Query::ReadPrefTagsField("tags"); ++ static const char* maxTimeMsField = "$maxTimeMS"; + + Query::Query( const string &json ) : obj( fromjson( json ) ) {} + +@@ -274,6 +275,11 @@ + return *this; + } + ++ Query& Query::maxTimeMs(int millis) { ++ appendComplex( maxTimeMsField, millis ); ++ return *this; ++ } ++ + Query& Query::explain() { + appendComplex( "$explain", true ); + return *this; +@@ -356,6 +362,10 @@ + hasReadPrefOption; + } + ++ bool Query::hasMaxTimeMs() const { ++ return obj.hasField( maxTimeMsField ); ++ } ++ + BSONObj Query::getFilter() const { + bool hasDollar; + if ( ! isComplex( &hasDollar ) ) +@@ -376,6 +386,11 @@ + return BSONObj(); + return obj.getObjectField( "$hint" ); + } ++ ++ int Query::getMaxTimeMs() const { ++ return obj.getIntField(maxTimeMsField); ++ } ++ + bool Query::isExplain() const { + return isComplex() && obj.getBoolField( "$explain" ); + } +diff -ur mongo-cxx-driver-legacy-0.0-26compat-2.6.7.orig/src/mongo/client/dbclientinterface.h mongo-cxx-driver-legacy-0.0-26compat-2.6.7/src/mongo/client/dbclientinterface.h +--- mongo-cxx-driver-legacy-0.0-26compat-2.6.7.orig/src/mongo/client/dbclientinterface.h 2015-01-29 20:41:04.000000000 +0200 ++++ mongo-cxx-driver-legacy-0.0-26compat-2.6.7/src/mongo/client/dbclientinterface.h 2015-02-18 18:10:30.342949382 +0200 +@@ -389,6 +389,12 @@ + Query& hint(BSONObj keyPattern); + Query& hint(const string &jsonKeyPatt); + ++ /** ++ * Specifies a cumulative time limit in milliseconds for processing an operation. ++ * MongoDB will interrupt the operation at the earliest following interrupt point. ++ */ ++ Query& maxTimeMs(int millis); ++ + /** Provide min and/or max index limits for the query. + min <= x < max + */ +@@ -450,11 +456,13 @@ + BSONObj getSort() const; + BSONObj getHint() const; + bool isExplain() const; ++ int getMaxTimeMs() const; + + /** + * @return true if the query object contains a read preference specification object. + */ + static bool hasReadPreference(const BSONObj& queryObj); ++ bool hasMaxTimeMs() const; + + string toString() const; + operator string() const { return toString(); } diff --git a/mongo-cxx-driver/mongo-cxx-driver.spec b/mongo-cxx-driver/mongo-cxx-driver.spec index 5301affaa7..10081f5e45 100644 --- a/mongo-cxx-driver/mongo-cxx-driver.spec +++ b/mongo-cxx-driver/mongo-cxx-driver.spec @@ -3,7 +3,7 @@ Name: mongo-cxx-driver Version: 2.6.7 -Release: 2%{?dist} +Release: 4%{?dist} Summary: The mongoDb C++ client driver library and its include files Group: Development/Libraries License: AGPLv3 and zlib and ASL 2.0 @@ -13,6 +13,11 @@ Source0: https://github.com/mongodb/%{pkg_name}/archive/%{pkg_name}-legac ## Patch 1 - http://track.sipfoundry.org/browse/XX-11578 Patch1: mongo-cxx-driver-2.6.7-logger.patch +## Patch 2 - http://track.sipfoundry.org/browse/XX-11590 +Patch2: mongo-cxx-driver-2.6.7-maxTimeMS.patch + +Patch3: mongo-cxx-driver-2.6.7-UC-4104.patch + BuildRequires: scons BuildRequires: openssl-devel BuildRequires: boost-devel @@ -40,6 +45,8 @@ This package provides the header files for MongoDB legacy C++ driver. %prep %setup -q -n %{name}-legacy-0.0-26compat-%{version} %patch1 -p1 +%patch2 -p1 +%patch3 -p1 # CRLF -> LF sed -i 's/\r//' README.md @@ -82,6 +89,11 @@ rm -f %{buildroot}%{_libdir}/../lib/libmongoclient.a %{_libdir}/libmongoclient.so %changelog -* Tue Feb 03 2015 Ionut Oancea - 2.6.7-1 +* Wed Feb 18 2015 Ionut Oancea - 2.6.7-3 +- Added + + for maxTimeMS: http://track.sipfoundry.org/browse/XX-11590 + +* Tue Feb 03 2015 Ionut Oancea - 2.6.7-2 - Added patch for logging: http://track.sipfoundry.org/browse/XX-11578 diff --git a/mongodb/mongod.init b/mongodb/mongod.init index 488e7f6724..6ac13f92eb 100644 --- a/mongodb/mongod.init +++ b/mongodb/mongod.init @@ -14,6 +14,8 @@ prog="mongod" pidfile=${PIDFILE-/var/run/mongodb/mongodb.pid} logfile="/var/log/mongodb/mongodb.log" options="$MONGODB_OPTIONS -f /etc/mongodb.conf" +starttime=10 +stoptime=10 [ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog @@ -97,7 +99,9 @@ start() { [ -x $exec ] || exit 5 echo -n $"Starting $prog: " daemon --pidfile=${pidfile} --user mongodb "$exec --quiet $options run >> $logfile 2>&1 &" + [ -n "$starttime" ] && sleep $starttime # Wait some time retval=$? + [ -n "$starttime" ] && sleep $starttime # Wait some time echo [ $retval -eq 0 ] && touch $lockfile return $retval @@ -105,8 +109,10 @@ start() { stop() { echo -n $"Stopping $prog: " + [ -n "$stoptime" ] && sleep $stoptime killproc_nice -p ${pidfile} -d 300 $prog retval=$? + [ -n "$stoptime" ] && sleep $stoptime echo [ $retval -eq 0 ] && rm -f $lockfile return $retval diff --git a/mongodb/mongodb.spec b/mongodb/mongodb.spec index 6c0060697f..495e560da6 100644 --- a/mongodb/mongodb.spec +++ b/mongodb/mongodb.spec @@ -1,4 +1,5 @@ %global daemon mongod +%define debug_package %{nil} Name: mongodb Version: 2.6.7 @@ -26,6 +27,7 @@ BuildRequires: scons BuildRequires: openssl-devel BuildRequires: boost-devel BuildRequires: pcre-devel +BuildRequires: libpcap-devel %if 0%{?fedora} >= 15 || 0%{?rhel} >= 7 Requires(post): systemd-units diff --git a/oss_core b/oss_core index 629e2c0140..142c347562 160000 --- a/oss_core +++ b/oss_core @@ -1 +1 @@ -Subproject commit 629e2c0140e9701673d9ec993d8a9a6295642468 +Subproject commit 142c347562e976a202ae2889c2cbd742bbcbe4dd diff --git a/sipXAocBilling b/sipXAocBilling new file mode 160000 index 0000000000..a40f11cfa6 --- /dev/null +++ b/sipXAocBilling @@ -0,0 +1 @@ +Subproject commit a40f11cfa67e9702689056058651d6b3a62a156a diff --git a/sipXaastra/configure.ac b/sipXaastra/configure.ac index ed29a88fe3..86e98c0c01 100644 --- a/sipXaastra/configure.ac +++ b/sipXaastra/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.57) -AC_INIT(sipXaastra, 15.04, sipx-dev@list.sipfoundry.org) +AC_INIT(sipXaastra, 16.12, sipx-dev@list.sipfoundry.org) AC_CONFIG_AUX_DIR(config) m4_include([config/general.m4]) m4_include([config/sipXlib.m4]) diff --git a/sipXacccode/configure.ac b/sipXacccode/configure.ac index c436dc4561..6e1da25541 100644 --- a/sipXacccode/configure.ac +++ b/sipXacccode/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.57) -AC_INIT(sipXacccode, 15.04, sipx-dev@list.sipfoundry.org) +AC_INIT(sipXacccode, 16.12, sipx-dev@list.sipfoundry.org) AC_CONFIG_AUX_DIR(config) m4_include([config/general.m4]) m4_include([config/sipXlib.m4]) diff --git a/sipXacccode/src/org/sipfoundry/authcode/AuthCode.java b/sipXacccode/src/org/sipfoundry/authcode/AuthCode.java index f591aab7f1..eba4f9a6b9 100644 --- a/sipXacccode/src/org/sipfoundry/authcode/AuthCode.java +++ b/sipXacccode/src/org/sipfoundry/authcode/AuthCode.java @@ -158,7 +158,7 @@ public void transfer(String uri, String user, String passwd) { // for call permissions that will be used. new Set(m_fses, "sip_auth_username", user).go(); new Set(m_fses, "sip_auth_password", passwd).go(); - Transfer xfer = new Transfer(m_fses, uri); + Transfer xfer = new Transfer(m_fses, uri, false); xfer.go(); throw new DisconnectException(); } diff --git a/sipXaudiocodes/README b/sipXaudiocodes/README index e69de29bb2..932c39bce2 100644 --- a/sipXaudiocodes/README +++ b/sipXaudiocodes/README @@ -0,0 +1 @@ +# Audiocodes support diff --git a/sipXaudiocodes/configure.ac b/sipXaudiocodes/configure.ac index 2dd210a110..4b9d6305cc 100644 --- a/sipXaudiocodes/configure.ac +++ b/sipXaudiocodes/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.57) -AC_INIT(sipXaudiocodes, 15.04, sipx-dev@list.sipfoundry.org) +AC_INIT(sipXaudiocodes, 16.12, sipx-dev@list.sipfoundry.org) AC_CONFIG_AUX_DIR(config) AC_CONFIG_SRCDIR([src/org/sipfoundry/sipxconfig/phone/audiocodesphone/AudioCodesPhone.java]) m4_include([config/sipXlib2.m4]) diff --git a/sipXaudiocodes/etc/audiocodes/gateway-5.2.ini.vm b/sipXaudiocodes/etc/audiocodes/gateway-5.2.ini.vm index e9f951f0df..c347980415 100644 --- a/sipXaudiocodes/etc/audiocodes/gateway-5.2.ini.vm +++ b/sipXaudiocodes/etc/audiocodes/gateway-5.2.ini.vm @@ -114,6 +114,19 @@ FORMAT Authentication_Index = Authentication_UserId, Authentication_UserPassword Authentication $line_id = $username, ${password}; #end [\Authentication] + +[CallerDisplayInfo] +FORMAT CallerDisplayInfo_Index = CallerDisplayInfo_DisplayString, CallerDisplayInfo_IsCidRestricted; +#foreach ($line in $phone.lines) +#set($line_id = $velocityCount - 1) +#set($display = $line.getSettingValue('CALLER_ID/Display')) +#set($cidRestricted = $line.getSettingValue('CALLER_ID/CIDRestricted')) +#if(!($display) || $!display == "") +#set($display = $line.getSettingValue('SIP/Authentication/Username')) +#end +CallerDisplayInfo $line_id = "${display}", $cidRestricted; +#end +[\CallerDisplayInfo] #end #if(${gateway.ModelDefinitions.contains("digital")}) diff --git a/sipXaudiocodes/etc/audiocodes/gateway-5.4.ini.vm b/sipXaudiocodes/etc/audiocodes/gateway-5.4.ini.vm index e9f951f0df..d01111ad51 100644 --- a/sipXaudiocodes/etc/audiocodes/gateway-5.4.ini.vm +++ b/sipXaudiocodes/etc/audiocodes/gateway-5.4.ini.vm @@ -114,6 +114,19 @@ FORMAT Authentication_Index = Authentication_UserId, Authentication_UserPassword Authentication $line_id = $username, ${password}; #end [\Authentication] + +[CallerDisplayInfo] +FORMAT CallerDisplayInfo_Index = CallerDisplayInfo_DisplayString, CallerDisplayInfo_IsCidRestricted; +#foreach ($line in $phone.lines) +#set($line_id = $velocityCount - 1) +#set($display = $line.getSettingValue('CALLER_ID/Display')) +#set($cidRestricted = $line.getSettingValue('CALLER_ID/CIDRestricted')) +#if(!($display) || $!display == "") +#set($display = $line.getSettingValue('SIP/Authentication/Username')) +#end +CallerDisplayInfo $line_id = ${display}, $cidRestricted; +#end +[\CallerDisplayInfo] #end #if(${gateway.ModelDefinitions.contains("digital")}) diff --git a/sipXaudiocodes/etc/audiocodes/gateway-5.6.ini.vm b/sipXaudiocodes/etc/audiocodes/gateway-5.6.ini.vm index 5369afb01e..c02e714417 100644 --- a/sipXaudiocodes/etc/audiocodes/gateway-5.6.ini.vm +++ b/sipXaudiocodes/etc/audiocodes/gateway-5.6.ini.vm @@ -115,6 +115,19 @@ FORMAT Authentication_Index = Authentication_UserId, Authentication_UserPassword Authentication $line_id = $username, ${password}; #end [\Authentication] + +[CallerDisplayInfo] +FORMAT CallerDisplayInfo_Index = CallerDisplayInfo_DisplayString, CallerDisplayInfo_IsCidRestricted; +#foreach ($line in $phone.lines) +#set($line_id = $velocityCount - 1) +#set($display = $line.getSettingValue('CALLER_ID/Display')) +#set($cidRestricted = $line.getSettingValue('CALLER_ID/CIDRestricted')) +#if(!($display) || $!display == "") +#set($display = $line.getSettingValue('SIP/Authentication/Username')) +#end +CallerDisplayInfo $line_id = "${display}", $cidRestricted; +#end +[\CallerDisplayInfo] #end #if(${gateway.ModelDefinitions.contains("digital")}) diff --git a/sipXaudiocodes/etc/audiocodes/gateway-5.8.ini.vm b/sipXaudiocodes/etc/audiocodes/gateway-5.8.ini.vm index c792d6de59..fd94f1da63 100644 --- a/sipXaudiocodes/etc/audiocodes/gateway-5.8.ini.vm +++ b/sipXaudiocodes/etc/audiocodes/gateway-5.8.ini.vm @@ -115,7 +115,21 @@ FORMAT Authentication_Index = Authentication_UserId, Authentication_UserPassword Authentication $line_id = $username, ${password}; #end [\Authentication] + +[CallerDisplayInfo] +FORMAT CallerDisplayInfo_Index = CallerDisplayInfo_DisplayString, CallerDisplayInfo_IsCidRestricted; +#foreach ($line in $phone.lines) +#set($line_id = $velocityCount - 1) +#set($display = $line.getSettingValue('CALLER_ID/Display')) +#set($cidRestricted = $line.getSettingValue('CALLER_ID/CIDRestricted')) +#if(!($display) || $!display == "") +#set($display = $line.getSettingValue('SIP/Authentication/Username')) #end +CallerDisplayInfo $line_id = "${display}", $cidRestricted; +#end +[\CallerDisplayInfo] +#end + #if(${gateway.ModelDefinitions.contains("digital")}) #foreach ($portSettings in $portFlatSettings) diff --git a/sipXaudiocodes/etc/audiocodes/gateway-6.0.ini.vm b/sipXaudiocodes/etc/audiocodes/gateway-6.0.ini.vm index 87903e1e67..55ea8ae26c 100644 --- a/sipXaudiocodes/etc/audiocodes/gateway-6.0.ini.vm +++ b/sipXaudiocodes/etc/audiocodes/gateway-6.0.ini.vm @@ -115,6 +115,20 @@ FORMAT Authentication_Index = Authentication_UserId, Authentication_UserPassword Authentication $line_id = $username, ${password}; #end [\Authentication] + +[CallerDisplayInfo] +FORMAT CallerDisplayInfo_Index = CallerDisplayInfo_DisplayString, CallerDisplayInfo_IsCidRestricted; +#foreach ($line in $phone.lines) +#set($line_id = $velocityCount - 1) +#set($display = $line.getSettingValue('CALLER_ID/Display')) +#set($cidRestricted = $line.getSettingValue('CALLER_ID/CIDRestricted')) +#if(!($display) || $!display == "") +#set($display = $line.getSettingValue('SIP/Authentication/Username')) +#end +CallerDisplayInfo $line_id = "${display}", $cidRestricted; +#end +[\CallerDisplayInfo] + #end #if(${gateway.ModelDefinitions.contains("digital")}) diff --git a/sipXaudiocodes/etc/audiocodes/gateway.properties b/sipXaudiocodes/etc/audiocodes/gateway.properties index 72e55b0526..096fac089d 100644 --- a/sipXaudiocodes/etc/audiocodes/gateway.properties +++ b/sipXaudiocodes/etc/audiocodes/gateway.properties @@ -171,7 +171,7 @@ SIP_general.EnableReasonHeader.label=Reason Header #SIP_general.3xxBehavior.label= SIP_general.3xxBehavior.description=Determines the gateway's behavior when a 3xx response is received for an outgoing INVITE request. The gateway \ - can either use the same call identifiers (CallID, branch, to and from tags) or change them in the new initiated \ + can either use the same call identifiers (CallID, location, to and from tags) or change them in the new initiated \ INVITE. When enabled gateway will use the same call identifiers. SIP_general.SIPReroutingMode.label=SIP Rerouting Mode diff --git a/sipXaudiocodes/etc/audiocodes/line.properties b/sipXaudiocodes/etc/audiocodes/line.properties index 008732bfa2..04cff49b8a 100644 --- a/sipXaudiocodes/etc/audiocodes/line.properties +++ b/sipXaudiocodes/etc/audiocodes/line.properties @@ -11,3 +11,12 @@ #SIP.Authentication.Password.label= #SIP.Authentication.Password.description= + +CALLER_ID.label=Caller Id +CALLER_ID.description= + +CALLER_ID.Display.label=Display +CALLER_ID.Display.description= + +CALLER_ID.CIDRestricted.label=CID Restricted +CALLER_ID.CIDRestricted.description= \ No newline at end of file diff --git a/sipXaudiocodes/etc/audiocodes/line.xml b/sipXaudiocodes/etc/audiocodes/line.xml index 843b576099..09aade553f 100644 --- a/sipXaudiocodes/etc/audiocodes/line.xml +++ b/sipXaudiocodes/etc/audiocodes/line.xml @@ -9,4 +9,14 @@ + + + + + + + + 0 + + diff --git a/sipXbridge/configure.ac b/sipXbridge/configure.ac index 1351b55a92..4cb6228216 100644 --- a/sipXbridge/configure.ac +++ b/sipXbridge/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.57) -AC_INIT(sipXbridge, 15.04, sipx-dev@list.sipfoundry.org) +AC_INIT(sipXbridge, 16.12, sipx-dev@list.sipfoundry.org) AC_CONFIG_AUX_DIR(config) m4_include([config/general.m4]) m4_include([config/sipXlib.m4]) diff --git a/sipXbridge/src/main/java/org/sipfoundry/sipxbridge/RtpReceiverEndpoint.java b/sipXbridge/src/main/java/org/sipfoundry/sipxbridge/RtpReceiverEndpoint.java index 82621c10c7..82dfdf16e4 100644 --- a/sipXbridge/src/main/java/org/sipfoundry/sipxbridge/RtpReceiverEndpoint.java +++ b/sipXbridge/src/main/java/org/sipfoundry/sipxbridge/RtpReceiverEndpoint.java @@ -101,16 +101,16 @@ void setSessionDescription(SessionDescription sessionDescription) { * Filter the codecs to the allow set. This filter is applied only for * audio codecs. */ - this.sessionDescription = SipUtilities.cleanSessionDescription(sessionDescription); + sessionDescription = SipUtilities.cleanSessionDescription( sessionDescription ); /* * draft-ietf-sipping-sip-offeranswer-08 section 5.2.5 makes it clear that a UA cannot * change the session id field of the o-line when making a subsequent offer/answer. * We use the same Origin field for all interactions. */ - this.sessionDescription.setOrigin(origin); + sessionDescription.setOrigin(origin); SipUtilities.fixupSdpMediaAddresses(sessionDescription, address, this.getPort()); - + this.sessionDescription = SipUtilities.cloneSessionDescription(sessionDescription); if ( logger.isDebugEnabled() ) { logger.debug("sessionDescription after fixup : " + sessionDescription); diff --git a/sipXbridge/src/main/java/org/sipfoundry/sipxbridge/SipUtilities.java b/sipXbridge/src/main/java/org/sipfoundry/sipxbridge/SipUtilities.java index b120b11919..5f59730ba5 100644 --- a/sipXbridge/src/main/java/org/sipfoundry/sipxbridge/SipUtilities.java +++ b/sipXbridge/src/main/java/org/sipfoundry/sipxbridge/SipUtilities.java @@ -1048,7 +1048,7 @@ static Set getNonTelephoneEventMediaFormats( for (Iterator it1 = formats.iterator(); it1.hasNext();) { Object format = it1.next(); int fmt = new Integer(format.toString()); - if (fmt != 100 && fmt != 101) { + if (fmt != 100 && fmt != 101 && fmt != 19) { retval.add(fmt); } } @@ -1135,10 +1135,10 @@ static SessionDescription cleanSessionDescription( Integer fmt = new Integer(format.toString()); if (!codecs.contains(fmt)) { /* - * Preserve the telephone event lines -- this upsets + * Preserve the telephone event lines and reserved payload -- this upsets * some ITSPs otherwise. AT&T Hack. */ - if (fmt != 100 && fmt != 101) { + if (fmt != 100 && fmt != 101 && fmt != 19) { it1.remove(); } } diff --git a/sipXcallController/configure.ac b/sipXcallController/configure.ac index dc83925885..c342ab4c0a 100644 --- a/sipXcallController/configure.ac +++ b/sipXcallController/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.57) -AC_INIT(sipXcallController, 15.04, sipx-dev@list.sipfoundry.org) +AC_INIT(sipXcallController, 16.12, sipx-dev@list.sipfoundry.org) AC_CONFIG_AUX_DIR(config) m4_include([config/general.m4]) m4_include([config/sipXlib.m4]) diff --git a/sipXcallController/src/org/sipfoundry/callcontroller/DialogContext.java b/sipXcallController/src/org/sipfoundry/callcontroller/DialogContext.java index 1a15a2765b..436912cc8d 100644 --- a/sipXcallController/src/org/sipfoundry/callcontroller/DialogContext.java +++ b/sipXcallController/src/org/sipfoundry/callcontroller/DialogContext.java @@ -359,6 +359,7 @@ public void processNotify(RequestEvent requestEvent) { SubscriptionStateHeader subscriptionState = (SubscriptionStateHeader) request .getHeader(SubscriptionStateHeader.NAME); boolean ringing = false; + boolean sessionProgress = false; if (request.getContentLength().getContentLength() != 0) { String statusLine = new String(request.getRawContent()); logger.debug("dialog = " + dialog); @@ -366,6 +367,7 @@ public void processNotify(RequestEvent requestEvent) { if (!StringUtils.isEmpty(statusLine)) { ringing = containsIgnoreCase(statusLine, SipHelper.RINGING_MESSAGE); + sessionProgress = containsIgnoreCase(statusLine, SipHelper.SESSION_PROGRESS); this.setStatus(SipHelper.getCallId(request), request.getMethod(), statusLine); logger.debug("Ringing received " + ringing); @@ -373,7 +375,7 @@ public void processNotify(RequestEvent requestEvent) { } boolean terminated = subscriptionState.getState().equalsIgnoreCase(SubscriptionStateHeader.TERMINATED); logger.debug("Is subscription state terminated: " + terminated); - if (ringing || terminated) { + if (ringing || terminated || sessionProgress) { String content = new String(request.getRawContent()); ReasonHeader busyHeader = null; if (containsIgnoreCase(content, SipHelper.BUSY_MESSAGE)) { @@ -385,7 +387,7 @@ public void processNotify(RequestEvent requestEvent) { byeUri = context.getRequest(dialog).getRequestURI(); logger.debug("BYE URI is: " + byeUri); } - logger.debug("Send BYE - Ringing " + ringing + " Subscription state terminated " + terminated); + logger.debug("Send BYE - Ringing " + ringing + " Subscription state terminated " + terminated + " Session in progress " + sessionProgress); SipListenerImpl.getInstance().getHelper().tearDownDialog(dialog, busyHeader, byeUri); } } else { diff --git a/sipXcallLib/configure.ac b/sipXcallLib/configure.ac index 7b9346a443..a07a16a015 100644 --- a/sipXcallLib/configure.ac +++ b/sipXcallLib/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.57) -AC_INIT(sipXcallLib, 15.04, sipx-dev@list.sipfoundry.org) +AC_INIT(sipXcallLib, 16.12, sipx-dev@list.sipfoundry.org) AC_CONFIG_AUX_DIR(config) m4_include([config/general.m4]) m4_include([config/sipXlib.m4]) diff --git a/sipXcallQueue b/sipXcallQueue index bb323e4809..0f7cd32058 160000 --- a/sipXcallQueue +++ b/sipXcallQueue @@ -1 +1 @@ -Subproject commit bb323e48091003fa1cb341a02d3e7ff624c495e2 +Subproject commit 0f7cd320585e634a668efb2f9da2dc546b1bcd6f diff --git a/sipXcallback/.classpath b/sipXcallback/.classpath new file mode 100644 index 0000000000..69c442f3bc --- /dev/null +++ b/sipXcallback/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/sipXcallback/.project b/sipXcallback/.project new file mode 100644 index 0000000000..c8815bffab --- /dev/null +++ b/sipXcallback/.project @@ -0,0 +1,17 @@ + + + sipXcallback + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/sipXcallback/LICENSE b/sipXcallback/LICENSE new file mode 100644 index 0000000000..618b3af720 --- /dev/null +++ b/sipXcallback/LICENSE @@ -0,0 +1,10 @@ +Copyright © 2015 sipXcom Inc. + +Certain elements licensed under a Contributor Agreement. Contributors retain copyright to elements licensed under a Contributor Agreement. + +Licensed to the user under the Affero General Public License (AGPL) version 3 or newer. + +The SIPfoundry and sipXecs names and logos are the property of SIPfoundry Inc. an may not be used without permission. + +This license and the SIPfoundry copyright may not be removed. +All user facing (Web) interfaces that provide access to functionality or services provided in full or in part by this software must display the license and SIPfoundry copyright in a clearly visible way. By using this software you grant the right to SIPfoundry to use your name and logo to make such representation. \ No newline at end of file diff --git a/sipXcallback/Makefile.am b/sipXcallback/Makefile.am new file mode 100644 index 0000000000..d7a59ce7df --- /dev/null +++ b/sipXcallback/Makefile.am @@ -0,0 +1,8 @@ +include config/utility.am +include config/project.am + +SUBDIRS = \ + src \ + test \ + bin \ + etc diff --git a/sipXcallback/bin/.project b/sipXcallback/bin/.project new file mode 100644 index 0000000000..c8815bffab --- /dev/null +++ b/sipXcallback/bin/.project @@ -0,0 +1,17 @@ + + + sipXcallback + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/sipXcallback/bin/Makefile.am b/sipXcallback/bin/Makefile.am new file mode 100644 index 0000000000..7de8c2f3a2 --- /dev/null +++ b/sipXcallback/bin/Makefile.am @@ -0,0 +1,13 @@ +include $(top_srcdir)/config/utility.am +include $(top_srcdir)/common.am + +EXTRA_DIST = $(initd_SCRIPTS:=.in) + +initddir = @SIPX_SERVICEDIR@ +initd_SCRIPTS = \ + sipxcallback + +$(initd_SCRIPTS) : % : %.in Makefile + @$(call SearchAndReplace,$<,$@,sipxcall_PKGS) + +CLEANFILES = $(initd_SCRIPTS) diff --git a/sipXcallback/bin/sipxcallback.in b/sipXcallback/bin/sipxcallback.in new file mode 100755 index 0000000000..a3b5c5a63d --- /dev/null +++ b/sipXcallback/bin/sipxcallback.in @@ -0,0 +1,116 @@ +#!/bin/bash + +# sipxcallback - Startup script for sipxcallback + +# chkconfig: 35 97 15 +# description: sipxcallback is Call Back on Busy using FreeSwitch +# processname: sipxcallback + +# +# Copyright (C) 2015 sipXcom., certain elements licensed under a Contributor Agreement. +# Contributors retain copyright to elements licensed under a Contributor Agreement. +# Licensed to the User under the LGPL license. + +. /etc/rc.d/init.d/functions || exit 1 +. @SIPX_LIBEXECDIR@/sipx-utils.sh || exit 1 + +prog=sipxcallback +pidfile="@SIPX_RUNDIR@/sipxcallback.pid" + +[ -e @SIPX_CONFDIR@/sysconfig/$prog ] && . @SIPX_CONFDIR@/sysconfig/$prog + +checkRunningInstance() { + if [ -f "${pidfile}" ]; then + pid=`cat ${pidfile}` + if checkpid $pid 2>&1; then + echo "Process $prog is already running with pid $pid" + exit 1 + fi + fi +} + +start() { + checkRunningInstance + + JavaCmd=`@SIPX_BINDIR@/sipx-config --java` + Dependencies=`@SIPX_BINDIR@/java-dep -d @SIPX_JAVADIR@/sipXcommons @sipxcall_PKGS@` + export CLASSPATH=`echo @SIPX_CONFDIR@/sipxcallback @SIPX_JAVADIR@/sipXcallback/*.jar ${Dependencies} | sed -e 's/ /:/g'` + + setJavaSslOpts + + # procname is there so cfengine can find it + Command="$JavaCmd \ + -Dprocname=sipxcallback \ + -Dconf.dir=@SIPX_CONFDIR@ \ + -Dlog.dir=@SIPX_LOGDIR@ \ + -Dvar.dir=@SIPX_VARDIR@ \ + -Dstdprompts.dir=@wwwdir@/doc/stdprompts \ + $JavaSslOpts \ + ${SIPXCALL_OPTS} \ + org.sipfoundry.sipxcallback.SipXcallbackServer \ + $Args" + if [ -n "${NoFork}" ] ; then + runuser -s /bin/bash @SIPXPBXUSER@ -c "${Command}" + else + echo -n $"Starting sipxcallback: " + runuser -s /bin/bash @SIPXPBXUSER@ -c "${Command}" >/dev/null 2>&1 & + echo $! > ${pidfile} + echo_success + fi +} + +stop() { + echo -n $"Stopping sipxcallback: " + killproc -p "${pidfile}" "sipxcallback" + Status=$? + echo + [ $Status -eq 0 ] && rm -f "${pidfile}" +} + +restart() { + stop + start +} + +configtest() { + sipx_config_exists @SIPX_CONFDIR@/sipxcallback.properties + Status=$(($Status+$?)) + + # Check that the log file is writable. + logfile="@SIPX_LOGDIR@/sipxcallback.log" + if [ -e $logfile -a ! -w $logfile ] + then + echo "Log file '$logfile' exists but is not writable." >&2 + Status=1 + fi +} + +case "$1" in + nofork) + NoFork=1 + start + ;; + start) + start + ;; + stop) + stop + ;; + restart|reload|force-reload) + restart + ;; + condrestart) + [ -f ${pidfile} ] && restart || : + ;; + status) + status -p ${pidfile} sipxcallback + ;; + configtest) + configtest + ;; + *) + echo "Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart|nofork|configtest}" + Status=1 +esac + +exit $Status diff --git a/sipXcallback/common.am b/sipXcallback/common.am new file mode 100644 index 0000000000..f48775b876 --- /dev/null +++ b/sipXcallback/common.am @@ -0,0 +1,29 @@ +# NOTE: List is alphabetical +sipxcall_PKGS = \ + commons-codec \ + commons-lang \ + commons-io \ + commons-logging \ + dom4j \ + jaxen \ + log4j \ + mongo \ + sipxcommons \ + slf4j-api \ + slf4j-log4j12 \ + spring-beans \ + org.springframework.web \ + org.springframework.web.servlet \ + spring-context \ + spring-context-support \ + spring-core \ + org.springframework.jdbc \ + org.springframework.transaction \ + spring-expression \ + spring-aop \ + spring-data-commons \ + spring-data-mongodb \ + aopalliance \ + hazelcast-all + +sipxcall_JAVAROOT = $(abspath $(top_builddir)/src/classes) \ No newline at end of file diff --git a/sipXcallback/config b/sipXcallback/config new file mode 120000 index 0000000000..3ca249e06b --- /dev/null +++ b/sipXcallback/config @@ -0,0 +1 @@ +../config \ No newline at end of file diff --git a/sipXcallback/configure.ac b/sipXcallback/configure.ac new file mode 100644 index 0000000000..bd93736eeb --- /dev/null +++ b/sipXcallback/configure.ac @@ -0,0 +1,20 @@ +AC_PREREQ(2.57) +AC_INIT(sipXcallback, 16.12, sipx-dev@list.sipfoundry.org) +AC_CONFIG_AUX_DIR(config) +m4_include([config/general.m4]) +m4_include([config/sipXlib.m4]) +m4_include([config/java2.m4]) +AM_INIT_AUTOMAKE(foreign tar-ustar) +SFAC_AUTOMAKE_VERSION([1.6]) +SFAC_INIT_FLAGS +AC_PROG_JAVA_CC([javac]) +AC_PROG_JAVA([java]) +PROG_JAVA_DEP +AC_CONFIG_FILES([ + Makefile + src/Makefile + test/Makefile + bin/Makefile + etc/Makefile +]) +AC_OUTPUT diff --git a/sipXcallback/etc/Makefile.am b/sipXcallback/etc/Makefile.am new file mode 100644 index 0000000000..7b35574958 --- /dev/null +++ b/sipXcallback/etc/Makefile.am @@ -0,0 +1,17 @@ +include $(top_srcdir)/config/utility.am + +cfinputsdir = $(SIPX_CFINPUTS)/plugin.d +dist_cfinputs_DATA = \ + sipxcallback.cf + +schemadir = @SIPX_DATADIR@/schema + +EXTRA_DIST = \ + $(conf_DATA:=.in) + +confdir = @SIPX_CONFDIR@/sipxcallback +conf_DATA = \ + log4j.properties + +$(conf_DATA) : % : %.in Makefile + @$(call SearchAndReplace,$<,$@) diff --git a/sipXcallback/etc/log4j.properties.in b/sipXcallback/etc/log4j.properties.in new file mode 100644 index 0000000000..99742b2898 --- /dev/null +++ b/sipXcallback/etc/log4j.properties.in @@ -0,0 +1,6 @@ +log4j.logger.org.sipfoundry.sipxcallback=INFO +log4j.rootLogger=info, file +log4j.appender.file=org.sipfoundry.commons.log4j.SipFoundryAppender +log4j.appender.file.File=@SIPX_LOGDIR@/sipxcallback.log +log4j.appender.file.layout=org.sipfoundry.commons.log4j.SipFoundryLayout +log4j.appender.file.layout.facility=sipXcallback diff --git a/sipXcallback/etc/sipxcallback.cf b/sipXcallback/etc/sipxcallback.cf new file mode 100644 index 0000000000..9a968137c3 --- /dev/null +++ b/sipXcallback/etc/sipxcallback.cf @@ -0,0 +1,103 @@ +# Copyright (c) 2015 sipXcom, Inc. All rights reserved. +# Contributed to SIPfoundry under a Contributor Agreement + +# This software is free software; you can redistribute it and/or modify it under +# the terms of the Affero General Public License (AGPL) as published by the +# Free Software Foundation; either version 3 of the License, or (at your option) +# any later version. + +# This software is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. + +# +# sipxcallback configuration and process management +# +bundle agent sipxcallback { + methods: + sipxcallback:: + "any" usebundle => "sipxcallback_config"; + any:: + "any" usebundle => "sipxcallback_setup"; + "any" usebundle => "sipxcallback_running"; +} + +bundle agent sipxcallback_config { + + files: + any:: + "$(sipx.SIPX_CONFDIR)/sipxcallback.properties" + comment => "install call back on busy config $(this.promiser)", + create => "true", + perms => m("644"), + edit_line => sipxcallback_properties_contents, + classes => if_repaired("restart_sipxcallback"); + + "$(sipx.SIPX_CONFDIR)/sipxcallback/log4j.properties" + comment => "configure callback log4j $(this.promiser)", + create => "true", + perms => m("644"), + edit_line => sipxcallback_log4j_properties_contents; +} + +bundle edit_line sipxcallback_properties_contents { + insert_lines: + any:: + "$(sipx.SIPX_CFDATA)/$(sipx.location_id)/sipxcallback.properties.part" + insert_type => "file"; + "callback.sipxchangeDomainName=$(sipx.domain)"; + "log.file=$(sipx.SIPX_LOGDIR)/sipxcallback.log"; + delete_lines: + any:: + ".*"; +} + +bundle edit_line sipxcallback_log4j_properties_contents { + insert_lines: + any:: + "$(sipx.SIPX_CFDATA)/$(sipx.location_id)/log4j-callback.properties.part" + insert_type => "file"; + "log4j.rootLogger=warn, file"; + "log4j.appender.file=org.sipfoundry.commons.log4j.SipFoundryAppender"; + "log4j.appender.file.File=$(sipx.SIPX_LOGDIR)/sipxcallback.log"; + "log4j.appender.file.layout=org.sipfoundry.commons.log4j.SipFoundryLayout"; + "log4j.appender.file.layout.facility=sipXcallback"; + delete_lines: + any:: + ".*"; +} + +bundle agent sipxcallback_setup { + methods: + !src.sipxcallback:: + "any" usebundle => rh_chkconfig_status("sipxcallback on"); + !src.!sipxcallback:: + "any" usebundle => rh_chkconfig_status("sipxcallback off"); +} + +bundle agent sipxcallback_running { + vars: + any:: + "service_command" string => ".*\s-Dprocname=sipxcallback\s.*"; + + methods: + any:: + "any" usebundle => find_sipxservice_by_command_regex("$(service_command)","sipxcallback_running"); + + commands: + (!sipxcallback|stop_sipxecs).sipxcallback_running:: + "$(sipx.SIPX_SERVICEDIR)/sipxcallback" + comment => "stop sipxcallback", + args => "stop"; + + sipxcallback.!sipxcallback_running.!stop_sipxecs:: + "$(sipx.SIPX_SERVICEDIR)/sipxcallback" + comment => "start sipxcallback", + args => "start"; + + sipxcallback.sipxcallback_running.(restart_sipxecs|restart_sipxcallback):: + "$(sipx.SIPX_SERVICEDIR)/sipxcallback" + comment => "restart sipxcallback", + args => "restart"; +} \ No newline at end of file diff --git a/sipXcallback/sipxcallback.spec.in b/sipXcallback/sipxcallback.spec.in new file mode 100644 index 0000000000..7e5eef8052 --- /dev/null +++ b/sipXcallback/sipxcallback.spec.in @@ -0,0 +1,61 @@ +Name: @PACKAGE@ +Version: @VERSION@ +Release: @PACKAGE_REVISION@ + +Summary: Call Back on Busy/DISA/Account using FreeSwitch for sipX +License: LGPL +Group: Productivity/Telephony/SIP/Servers +Vendor: sipxcom +Packager: sipxcom +Url: http://www.sipxcom.org + +BuildRequires: automake +BuildRequires: java-1.7.0-openjdk-devel +BuildRequires: zip +BuildRequires: sipxcommons >= %version + +Source: %name-%version.tar.gz + +Requires: sipxcommserverlib >= %version +Requires: sipxcommons >= %version +Requires: chkconfig + +%if %{?use_ibm_jvm}0 +Requires: java-ibm >= 1.6 +Requires: java-ibm-unrestricted +%else +Requires: java-1.7.0-openjdk +%endif + +Prefix: %_prefix +BuildRoot: %{_tmppath}/%name-%version-root + +%description +Authorization Code/DISA/Account Code subsystem that uses FreeSWITCH as a media server. + +%prep +%setup -q + +%build +%configure @SIPX_RPM_CONFIGURE_OPTIONS@ +cp config.log %name.configlog +make all + +%install +rm -rf $RPM_BUILD_ROOT +make DESTDIR=$RPM_BUILD_ROOT install + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(644,root,root,755) +%attr(755,root,root) %{_sysconfdir}/init.d/sipxcallback +%{_datadir}/sipxecs/cfinputs/plugin.d/sipxcallback.cf +%attr(755,sipx,sipx) %{_datadir}/java/sipXecs/sipXcallback +%config(noreplace) %{_sysconfdir}/sipxpbx/sipxcallback/log4j.properties + +%post +if grep -q :on <<<`/sbin/chkconfig sipxcallback --list 2>&1`; then + /sbin/chkconfig sipxcallback reset +fi diff --git a/sipXcallback/src/Makefile.am b/sipXcallback/src/Makefile.am new file mode 100644 index 0000000000..b06a46b403 --- /dev/null +++ b/sipXcallback/src/Makefile.am @@ -0,0 +1,26 @@ +include $(top_srcdir)/config/utility.am +include $(top_srcdir)/config/java.am +include $(top_srcdir)/common.am + +EXTRA_DIST = \ + $(sipxcall_SRC) \ + $(sipxcall_RESOURCES) + +noinst_DATA = javac-sipxcall + +sipxcall_SRC = \ + $(shell cd $(srcdir); find org -type f -name '*.java') +sipxcall_RESOURCES = $(shell cd $(srcdir); find . \( -name '*.xml' \) ) + +sipxcall_DEPS = \ + $(JAVAROOT) \ + $(call JavaDep, @SIPX_JAVADIR@/sipXcommons, $(sipxcall_PKGS)) + +jardir = @SIPX_JAVADIR@/sipXcallback +JAR_FILE = sipXcallback.jar +jar_DATA = $(JAR_FILE) + +$(JAR_FILE) : javac-sipxcall $(sipxcall_RESOURCES) Manifest.txt Makefile + jar cfm $@ Manifest.txt \ + $(call JarInclude,$(sipxcall_JAVAROOT),.) \ + $(call JarInclude,$(srcdir),$(sipxcall_RESOURCES)) diff --git a/sipXcallback/src/org/sipfoundry/sipxcallback/CallbackCallHandler.java b/sipXcallback/src/org/sipfoundry/sipxcallback/CallbackCallHandler.java new file mode 100644 index 0000000000..2e980e27ee --- /dev/null +++ b/sipXcallback/src/org/sipfoundry/sipxcallback/CallbackCallHandler.java @@ -0,0 +1,124 @@ +/* + * + * + * Copyright (C) 2015 sipXcom, certain elements licensed under a Contributor Agreement. + * Contributors retain copyright to elements licensed under a Contributor Agreement. + * Licensed to the User under the LGPL license. + * + */ +package org.sipfoundry.sipxcallback; + +import java.io.IOException; +import java.net.Socket; +import java.util.Date; +import java.util.HashMap; + +import org.apache.log4j.Logger; +import org.sipfoundry.commons.freeswitch.Answer; +import org.sipfoundry.commons.freeswitch.Broadcast; +import org.sipfoundry.commons.freeswitch.DisconnectException; +import org.sipfoundry.commons.freeswitch.FreeSwitchEventSocketInterface; +import org.sipfoundry.commons.freeswitch.Hangup; +import org.sipfoundry.commons.freeswitch.eslrequest.EslRequestScopeRunnable; +import org.sipfoundry.sipxcallback.common.CallbackException; +import org.sipfoundry.sipxcallback.common.CallbackLegs; +import org.sipfoundry.sipxcallback.common.CallbackService; +import org.springframework.beans.factory.annotation.Required; + +/** + * Class used to register callback requests in the system + */ +public abstract class CallbackCallHandler extends EslRequestScopeRunnable { + private static final Logger LOG = Logger.getLogger("org.sipfoundry.sipxcallback"); + + private Socket m_clientSocket; + private String m_prefix; + private CallbackService m_callbackService; + private String m_welcomePrompt; + private String m_errorPrompt; + + protected abstract FreeSwitchEventSocketInterface getFsEventSocket(); + + @Override + public void runEslRequest() { + FreeSwitchEventSocketInterface fses = getFsEventSocket(); + try { + if (fses.connect(m_clientSocket, null)) { + LOG.info(String.format( + "SipXcallback::run Accepting call-id %s from %s to %s", + fses.getVariable("variable_sip_call_id"), + fses.getVariable("variable_sip_from_uri"), + fses.getVariable("variable_sip_req_uri"))); + new Answer(fses).go(); + run(fses); + } + } catch (DisconnectException e) { + LOG.info("sipXcallback::run Far end hungup."); + } catch (Throwable t) { + LOG.error("sipXcallback::run", t); + } finally { + try { + new Hangup(fses).go(); + fses.close(); + } catch (IOException e) { + // Nothing to do, no where to go home... + } + } + } + + /** + * Marks the callee user for callback on busy + */ + public final void run(FreeSwitchEventSocketInterface fses) { + HashMap variables = fses.getVariables(); + String callerUserName = variables.get("caller-username"); + String callerChannelName = variables.get("caller-channel-name"); + String callerURL = callerChannelName.split("/")[2].replace(".", ";"); + String toUri = fses.getToUri(); + toUri = toUri.replace(m_prefix, ""); + String[] splittedToUri = toUri.split("@"); + String calleeUserName = splittedToUri[0]; + if (calleeUserName == null || calleeUserName.isEmpty() + || calleeUserName.equals(callerUserName)) { + // callback user not found + LOG.warn("Callback user " + calleeUserName + " was not found."); + new Broadcast(fses, fses.getVariable("variable_sip_call_id"), m_errorPrompt, false).startResponse(); + return; + } + try { + CallbackLegs callbackLegs = new CallbackLegs(calleeUserName,callerURL, new Date().getTime()); + m_callbackService.updateCallbackInfoToMongo(callbackLegs, true); + } catch (CallbackException e) { + // callback user not found + LOG.warn("Callback user " + calleeUserName + " was not found."); + new Broadcast(fses, fses.getVariable("variable_sip_call_id"), m_errorPrompt, false).startResponse(); + return; + } + new Broadcast(fses, fses.getVariable("variable_sip_call_id"), m_welcomePrompt, false).startResponse(); + } + + @Required + public void setClient(Socket clientSocket) { + m_clientSocket = clientSocket; + } + + @Required + public void setPrefix(String prefix) { + m_prefix = prefix; + } + + @Required + public void setCallbackService(CallbackService callbackService) { + m_callbackService = callbackService; + } + + @Required + public void setWelcomePrompt(String welcomePrompt) { + this.m_welcomePrompt = welcomePrompt; + } + + @Required + public void setErrorPrompt(String errorPrompt) { + this.m_errorPrompt = errorPrompt; + } +} diff --git a/sipXcallback/src/org/sipfoundry/sipxcallback/CallbackExecutor.java b/sipXcallback/src/org/sipfoundry/sipxcallback/CallbackExecutor.java new file mode 100644 index 0000000000..1b9f1edcdd --- /dev/null +++ b/sipXcallback/src/org/sipfoundry/sipxcallback/CallbackExecutor.java @@ -0,0 +1,155 @@ +/** + * + * + * Copyright (c) 2015 sipXcom inc, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxcallback; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.sipfoundry.commons.freeswitch.BridgeCommand; +import org.sipfoundry.commons.freeswitch.Broadcast; +import org.sipfoundry.commons.freeswitch.FreeSwitchEvent; +import org.sipfoundry.commons.freeswitch.FreeSwitchEventSocketInterface; +import org.sipfoundry.commons.freeswitch.OriginateCommand; +import org.sipfoundry.commons.freeswitch.Set; +import org.sipfoundry.sipxcallback.common.CallbackException; +import org.sipfoundry.sipxcallback.common.CallbackLegs; +import org.sipfoundry.sipxcallback.common.CallbackService; +import org.springframework.beans.factory.annotation.Required; + +import com.hazelcast.core.IAtomicReference; + +public class CallbackExecutor { + + private static final Logger LOG = Logger.getLogger("org.sipfoundry.sipxcallback"); + private static final String ORIGINATE_RESPONSE_OK = "+OK "; + private static final String ORIGINATE_PROPERTIES = "{ignore_early_media=true,originate_timeout=20,fail_on_single_reject=USER_BUSY,hangup_after_bridge=true,origination_caller_id_number=00000000}"; + public static final String AROUND = "@"; + public static final String SOFIA = "sofia/"; + + private CallbackLegs m_callbackLegs; + private String m_callerUID; + private String m_calleeUID; + private String m_callerName; + private FreeSwitchEventSocketInterface m_fsCmdSocket; + private CallbackService m_callbackService; + private String sipxchangeDomainName; + + private String m_callerPrompt; + private String m_requestedCallbackPrompt; + + public void initiate(CallbackLegs callbackLegs, FreeSwitchEventSocketInterface fsCmdSocket) { + m_callbackLegs = callbackLegs; + String callerName = callbackLegs.getCallerUID().replace(";", "."); + String[] callerNameSplit = callerName.split(AROUND); + m_callerName = callerNameSplit[0]; + m_callerUID = StringUtils.join(new String[] { SOFIA, sipxchangeDomainName, "/", callerName }); + m_calleeUID = StringUtils.join(new String[] { SOFIA, sipxchangeDomainName, "/", callbackLegs.getCalleeName(), + AROUND, callerNameSplit[1] }); + m_callerUID = m_callerUID.split("/")[2]; + m_fsCmdSocket = fsCmdSocket; + } + + /** + * Returns true if the processing was successful. + */ + public boolean execute(CallbackLegs callbackLegs, FreeSwitchEventSocketInterface fsCmdSocket) { + initiate(callbackLegs, fsCmdSocket); + LOG.debug("Originating call to " + m_calleeUID); + // mark callee and caller as processing (so as not to receive other callbacks) + IAtomicReference calleeReference = m_callbackService.getAtomicReference(m_callbackLegs.getCalleeName()); + calleeReference.set(new Boolean(true)); + IAtomicReference callerReference = m_callbackService.getAtomicReference(m_callbackLegs.getCallerName()); + callerReference.set(new Boolean(true)); + boolean callbackSuccessful = false; + + try { + String originateProperties = ORIGINATE_PROPERTIES.replace("00000000", m_callerName); + OriginateCommand originateCalleeCmd = new OriginateCommand(m_fsCmdSocket, + originateProperties + m_calleeUID); + FreeSwitchEvent responseCallee = originateCalleeCmd.originate(); + String responseContent = responseCallee.getContent(); + if ((responseContent != null) && (responseContent.startsWith(ORIGINATE_RESPONSE_OK))) { + LOG.debug(m_calleeUID + " answered the call"); + handleCalleeResponse(responseContent); + callbackSuccessful = true; + } + return callbackSuccessful; + } catch (Exception e) { + LOG.error(e); + return callbackSuccessful; + } finally { + // remove mark for callee and caller as beeing in use + calleeReference.destroy(); + callerReference.destroy(); + } + } + + /** + * Action to be taken after the B user answered the call:
+ * - remove the callback flag from B user
+ * - originate a call to A user
+ * - if user A responds: bridge A and B
+ * - if user A busy: goes to his voicemail + */ + private void handleCalleeResponse(String responseContent) throws InterruptedException { + try { + // remove the callback flag from B user + m_callbackService.updateCallbackInfoToMongo(m_callbackLegs, false); + } catch (CallbackException e) { + LOG.error(e); + return; + } + LOG.debug("Originating call to " + m_callerUID); + String calleeUUID = getUUIDFromResponseContent(responseContent); + + // play "Caller requested a callback" to B users + new Broadcast(m_fsCmdSocket, calleeUUID, m_callerPrompt, false).startResponse(); + new Broadcast(m_fsCmdSocket, calleeUUID, m_callerName, true).startResponse(); + new Broadcast(m_fsCmdSocket, calleeUUID, m_requestedCallbackPrompt, false).startResponse(); + Thread.sleep(CallbackTimer.THREAD_WAIT_TIME); + + // bridge B and A legs + Set set = new Set(m_fsCmdSocket, calleeUUID, "ringback", "${us-ring}"); + set.start(); + BridgeCommand bridge = new BridgeCommand(m_fsCmdSocket, calleeUUID, m_callerUID, sipxchangeDomainName); + bridge.start(); + } + + private String getUUIDFromResponseContent(String responseContent){ + return responseContent.split(" ")[1].replace("\n",""); + } + + @Required + public void setCallbackService(CallbackService callbackService) { + m_callbackService = callbackService; + } + + @Required + public void setCallerPrompt(String callerPrompt) { + this.m_callerPrompt = callerPrompt; + } + + @Required + public void setRequestedCallbackPrompt(String requestedCallbackPrompt) { + this.m_requestedCallbackPrompt = requestedCallbackPrompt; + } + + @Required + public void setSipxchangeDomainName(String sipxchangeDomainName) { + this.sipxchangeDomainName = sipxchangeDomainName; + } + +} diff --git a/sipXcallback/src/org/sipfoundry/sipxcallback/CallbackTimer.java b/sipXcallback/src/org/sipfoundry/sipxcallback/CallbackTimer.java new file mode 100644 index 0000000000..b817b1f11d --- /dev/null +++ b/sipXcallback/src/org/sipfoundry/sipxcallback/CallbackTimer.java @@ -0,0 +1,138 @@ +/** + * + * + * Copyright (c) 2015 sipXcom, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxcallback; + +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.HashSet; +import java.util.Queue; +import java.util.Set; + +import org.apache.log4j.Logger; +import org.sipfoundry.commons.freeswitch.ConfBasicThread; +import org.sipfoundry.commons.freeswitch.FreeSwitchEventSocket; +import org.sipfoundry.sipxcallback.common.CallbackLegs; +import org.sipfoundry.sipxcallback.common.CallbackService; +import org.sipfoundry.sipxcallback.common.CallbackServiceImpl; +import org.sipfoundry.sipxcallback.common.FreeSwitchConfigurationImpl; +import org.springframework.beans.factory.annotation.Required; + +/** + * Daemon task class that handles callback requests registered in the system + */ +public class CallbackTimer { + private static final Logger LOG = Logger.getLogger("org.sipfoundry.sipxcallback"); + public static final long THREAD_WAIT_TIME = 4000; + + private CallbackService m_callbackService; + private CallbackExecutor m_callbackExecutor; + private int m_expires; + + private FreeSwitchConfigurationImpl fsConfig; + private FreeSwitchEventSocket m_fsCmdSocket; + + public void run() { + initiateSocket(); + // make sure callback_queue is initiated + m_callbackService.initiateCallbackQueue(); + + Queue hazelcastQueue = m_callbackService.getCallbackQueue(); + LOG.debug("Retrieving data from callback request queue."); + Set failedCallbackLegs = new HashSet(); + while (!hazelcastQueue.isEmpty()) { + CallbackLegs callbackLegs = hazelcastQueue.poll(); + boolean processingFailed = true; + try { + // process this request ONLY if this callee is not currently being processed by another callback thread + if (m_callbackService.isCallbackLegsFreeToProcess(callbackLegs)) { + LOG.debug("Processing callback request from " + callbackLegs.getCallerName() + + " to " + callbackLegs.getCalleeName()); + long currentDate = CallbackServiceImpl.getCurrentTimestamp(); + long timeDiff = currentDate - callbackLegs.getDate(); + if (timeDiff < m_expires * 60000) { + processingFailed = !m_callbackExecutor.execute(callbackLegs, m_fsCmdSocket); + Thread.sleep(THREAD_WAIT_TIME); + } else { + //callback request expired, remove it from mongo + LOG.debug("Callback request from " + callbackLegs.getCallerName() + + " to " + callbackLegs.getCalleeName() + " expired."); + m_callbackService.updateCallbackInfoToMongo(callbackLegs, false); + } + } + } catch (Exception e) { + LOG.error(e); + } finally { + if (processingFailed) { + failedCallbackLegs.add(callbackLegs); + } + } + } + hazelcastQueue.addAll(failedCallbackLegs); + } + + private void initiateSocket() { + if (m_fsCmdSocket == null) { + try { + if (m_fsCmdSocket == null) { + fsConfig = new FreeSwitchConfigurationImpl(); + } + m_fsCmdSocket = new FreeSwitchEventSocket(fsConfig); + m_fsCmdSocket.connect(getSocket(), ConfBasicThread.fsPassword); + } catch (IOException e) { + LOG.error(e); + return; + } + } + } + + private Socket getSocket() { + Socket socket = null; + while(socket == null) { + // freeswitch may be slow to start especially on a different machine + try { + socket = new Socket("localhost", Integer.parseInt(ConfBasicThread.fsListenPort)); + } catch (UnknownHostException e) { + LOG.error("Can't create connection to freeswitch " + e.getMessage()); + } catch (IOException e) { + // freeswitch likely is not up yet + try { + Thread.sleep(THREAD_WAIT_TIME); + } catch (InterruptedException e1) { + LOG.error(e1); + } + } + } + return socket; + } + + @Required + public void setCallbackService(CallbackService callbackService) { + m_callbackService = callbackService; + } + + @Required + public void setCallbackExecutor(CallbackExecutor callbackExecutor) { + m_callbackExecutor = callbackExecutor; + } + + @Required + public void setExpires(int expires) { + m_expires = expires; + } + +} diff --git a/sipXcallback/src/org/sipfoundry/sipxcallback/SipXcallbackServer.java b/sipXcallback/src/org/sipfoundry/sipxcallback/SipXcallbackServer.java new file mode 100644 index 0000000000..83d8137418 --- /dev/null +++ b/sipXcallback/src/org/sipfoundry/sipxcallback/SipXcallbackServer.java @@ -0,0 +1,108 @@ +/** + * + * + * Copyright (c) 2015 sipXcom, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxcallback; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; +import org.sipfoundry.commons.log4j.SipFoundryLayout; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public abstract class SipXcallbackServer { + static final Logger LOG = Logger.getLogger("org.sipfoundry.sipxcallback"); + + protected abstract CallbackCallHandler getCallbackCallHandler(); + private int m_eventSocketPort; + + public void runServer() { + LOG.info("Starting SipXcallback listening on port " + m_eventSocketPort); + try { + ServerSocket serverSocket = new ServerSocket(m_eventSocketPort); + LOG.info("SipXcallback::run Starting SipXcallback thread with client " + serverSocket); + for (;;) { + Socket client = serverSocket.accept(); + CallbackCallHandler sipxCalback = getCallbackCallHandler(); + sipxCalback.setClient(client); + Thread thread = new Thread(sipxCalback); + thread.start(); + } + } catch (IOException e) { + System.out.println("FAILED TO START Callback server" + e); + e.printStackTrace(); + System.exit(1); + } + } + + public void setEventSocketPort(int port) { + m_eventSocketPort = port; + } + + /** + * Main entry point for sipXcallback + * + * @param args + */ + public static void main(String[] args) { + try { + initSystemProperties(); + ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { + "classpath:/org/sipfoundry/sipxcallback/system.beans.xml", + "classpath:/org/sipfoundry/sipxcallback/imdb.beans.xml", + }); + SipXcallbackServer socket = (SipXcallbackServer) context.getBean("sipxCallbackServer"); + socket.runServer(); + } catch (BeansException ex) { + System.out.println("FAILED TO CREATE SPRING CONTAINER" + ex); + ex.printStackTrace(); + System.exit(1); + } + } + + private static void initSystemProperties() { + String path = System.getProperty("conf.dir"); + // Configure log4j + PropertyConfigurator.configureAndWatch(path+"/sipxcallback/log4j.properties", + SipFoundryLayout.LOG4J_MONITOR_FILE_DELAY); + if (path == null) { + System.err.println("Cannot get System Property conf.dir! Check jvm argument -Dconf.dir=") ; + System.exit(1); + } + + // Setup SSL properties so we can talk to HTTPS servers + String keyStore = System.getProperty("javax.net.ssl.keyStore"); + if (keyStore == null) { + // Take an educated guess as to where it should be + keyStore = path+"/ssl/ssl.keystore"; + System.setProperty("javax.net.ssl.keyStore", keyStore); + System.setProperty("javax.net.ssl.keyStorePassword", "changeit"); // Real security! + } + String trustStore = System.getProperty("javax.net.ssl.trustStore"); + if (trustStore == null) { + // Take an educated guess as to where it should be + trustStore = path+"/ssl/authorities.jks"; + System.setProperty("javax.net.ssl.trustStore", trustStore); + System.setProperty("javax.net.ssl.trustStoreType", "JKS"); + System.setProperty("javax.net.ssl.trustStorePassword", "changeit"); // Real security! + } + } + +} diff --git a/sipXconfig/web/unitelite/scripts/modules/notify.js b/sipXcallback/src/org/sipfoundry/sipxcallback/common/CallbackException.java similarity index 60% rename from sipXconfig/web/unitelite/scripts/modules/notify.js rename to sipXcallback/src/org/sipfoundry/sipxcallback/common/CallbackException.java index 8e9aa2f2ac..1bebcbbd81 100644 --- a/sipXconfig/web/unitelite/scripts/modules/notify.js +++ b/sipXcallback/src/org/sipfoundry/sipxcallback/common/CallbackException.java @@ -1,6 +1,8 @@ -/* - * Copyright (c) eZuce, Inc. All rights reserved. - * Contributed to SIPfoundry under a Contributor Agreement +/** + * + * + * Copyright (c) 2015 eZuce Corp. All rights reserved. + * Contributed to sipXcom under a Contributor Agreement * * This software is free software; you can redistribute it and/or modify it under * the terms of the Affero General Public License (AGPL) as published by the @@ -12,16 +14,16 @@ * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. */ +package org.sipfoundry.sipxcallback.common; + +public class CallbackException extends Exception{ + + public CallbackException(String cause) { + super(cause); + } -(function() { - 'use strict'; - var notify = angular.module('notify', []); + public CallbackException() { + super(); + } - /** - * angular DI for Strophe.js - * @return {Object} Strophe.js object - */ - notify.factory('notify', function() { - return window.Notify; - }); -})(); +} diff --git a/sipXcallback/src/org/sipfoundry/sipxcallback/common/CallbackLegs.java b/sipXcallback/src/org/sipfoundry/sipxcallback/common/CallbackLegs.java new file mode 100644 index 0000000000..c6cc060eb1 --- /dev/null +++ b/sipXcallback/src/org/sipfoundry/sipxcallback/common/CallbackLegs.java @@ -0,0 +1,98 @@ +/** + * + * + * Copyright (c) 2015 eZuce Corp. All rights reserved. + * Contributed to sipXcom under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxcallback.common; + +import java.io.Serializable; + +/** + * Bean to hold callee and caller information. + */ +public class CallbackLegs implements Serializable{ + + private String m_calleeName; + private String m_callerUID; + private long m_date; + + public CallbackLegs(String calleeName, String callerName, long date) { + super(); + this.m_calleeName = calleeName; + this.m_callerUID = callerName; + this.m_date = date; + } + + public String getCalleeName() { + return m_calleeName; + } + + public void setCalleeName(String calleeName) { + this.m_calleeName = calleeName; + } + + public String getCallerUID() { + return m_callerUID; + } + + public void setCallerUID(String callerUID) { + this.m_callerUID = callerUID; + } + + public String getCallerName() { + return m_callerUID.split("@")[0]; + } + + public long getDate() { + return m_date; + } + + public void setDate(long date) { + this.m_date = date; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((m_calleeName == null) ? 0 : m_calleeName.hashCode()); + result = prime * result + + ((m_callerUID == null) ? 0 : m_callerUID.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CallbackLegs other = (CallbackLegs) obj; + if (m_calleeName == null) { + if (other.m_calleeName != null) + return false; + } else if (!m_calleeName.equals(other.m_calleeName)) + return false; + if (m_callerUID == null) { + if (other.m_callerUID != null) + return false; + } else if (!m_callerUID.equals(other.m_callerUID)) + return false; + return true; + } + +} diff --git a/sipXcallback/src/org/sipfoundry/sipxcallback/common/CallbackService.java b/sipXcallback/src/org/sipfoundry/sipxcallback/common/CallbackService.java new file mode 100644 index 0000000000..54d8f0b49f --- /dev/null +++ b/sipXcallback/src/org/sipfoundry/sipxcallback/common/CallbackService.java @@ -0,0 +1,51 @@ +/** + * + * + * Copyright (c) 2015 eZuce Corp. All rights reserved. + * Contributed to sipXcom under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxcallback.common; + +import java.util.Queue; + +import com.hazelcast.core.IAtomicReference; + +public interface CallbackService { + + /** + * Persists callback info into mongo db + */ + public void updateCallbackInfoToMongo(CallbackLegs callbackLegs, boolean insertNewRequest) + throws CallbackException; + + /** + * Returns the queue used to store callback requests + */ + public Queue getCallbackQueue(); + + /** + * Method used to initiate the callback queue + */ + public void initiateCallbackQueue(); + + /** + * Retrieve an atomic reference + */ + public IAtomicReference getAtomicReference(String key); + + /** + * Check if the caller or callee of this request is in use + */ + public boolean isCallbackLegsFreeToProcess(CallbackLegs callbackLegs); + +} diff --git a/sipXcallback/src/org/sipfoundry/sipxcallback/common/CallbackServiceImpl.java b/sipXcallback/src/org/sipfoundry/sipxcallback/common/CallbackServiceImpl.java new file mode 100644 index 0000000000..5d9dd4cfc2 --- /dev/null +++ b/sipXcallback/src/org/sipfoundry/sipxcallback/common/CallbackServiceImpl.java @@ -0,0 +1,241 @@ +/** + * + * + * Copyright (c) 2015 sipXcom, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxcallback.common; + +import static org.sipfoundry.commons.mongo.MongoConstants.CALLBACK_LIST; +import static org.sipfoundry.commons.mongo.MongoConstants.ENTITY_NAME; +import static org.sipfoundry.commons.mongo.MongoConstants.UID; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.TimeZone; + +import org.apache.log4j.Logger; +import org.sipfoundry.commons.mongo.MongoConstants; +import org.sipfoundry.commons.userdb.ValidUsers; +import org.springframework.beans.factory.annotation.Required; +import org.springframework.data.mongodb.core.MongoTemplate; + +import com.hazelcast.core.Cluster; +import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.core.IAtomicReference; +import com.hazelcast.core.IQueue; +import com.hazelcast.core.Member; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBCursor; +import com.mongodb.DBObject; +import com.mongodb.QueryBuilder; + +public class CallbackServiceImpl implements CallbackService { + + private static final String HAZELCAST_CALLBACK_QUEUE = "hazelcast_callback_queue"; + private static final String HAZELCAST_CALLBACK_QUEUE_INITIATED = "callback_queue_initiated"; + private static final Logger LOG = Logger.getLogger("org.sipfoundry.sipxcallback"); + + private MongoTemplate m_imdbTemplate; + private int m_expires; + private HazelcastInstance m_hazelcastInstance; + + @Override + public void updateCallbackInfoToMongo(CallbackLegs callbackLegs, boolean insertNewRequest) + throws CallbackException { + String callerChannelName = callbackLegs.getCallerUID(); + if (callerChannelName.contains(".")) { + callerChannelName = callerChannelName.replace(".", ";"); + } + DBCollection entityCollection = m_imdbTemplate.getCollection("entity"); + DBObject user = findUserByName(callbackLegs.getCalleeName(), entityCollection); + if (user == null) { + // callback user was not found + throw new CallbackException("Callback user: " + callbackLegs.getCalleeName() + " not found !"); + } + BasicDBList callbackList = null; + if (user.keySet().contains(MongoConstants.CALLBACK_LIST)) { + callbackList = (BasicDBList) user.get(MongoConstants.CALLBACK_LIST); + } else { + callbackList = new BasicDBList(); + } + for (Object callerObject : callbackList.toArray()) { + DBObject callerDbObject = (DBObject) callerObject; + if (((DBObject) callerDbObject).containsField(callerChannelName)) { + callbackList.remove(callerDbObject); + } + } + if (insertNewRequest) { + insertNewObject(callbackLegs, callbackList); + } + updateCallbackList(entityCollection, callbackList, user, null); + } + + private void insertNewObject(CallbackLegs callbackLegs, BasicDBList callbackList) { + DBObject callback = new BasicDBObject(callbackLegs.getCallerUID(), getCurrentTimestamp()); + callbackList.add(callback); + if (callbackLegs != null) { + // add the request also in the hazelcast call queue + IQueue callbackQueue = getCallbackQueue(); + boolean isUpdated = false; + for (CallbackLegs oldCallbackLegs : callbackQueue) { + if (oldCallbackLegs.equals(callbackLegs)) { + oldCallbackLegs.setDate(callbackLegs.getDate()); + isUpdated = true; + break; + } + } + if (!isUpdated) { + getCallbackQueue().add(callbackLegs); + } + } + } + + private Set setupCallbackRequest() { + DBCollection entityCollection = m_imdbTemplate.getCollection("entity"); + // get all users which have callback on busy set + DBCursor users = getCallbackUsers(entityCollection); + + // iterate over users + Set callsMap = new HashSet(); + for (DBObject user : users) { + BasicDBList callbackList = (BasicDBList) user.get(CALLBACK_LIST); + String calleeName = ValidUsers.getStringValue(user, UID); + // iterate over callback requests for each user + // keep tabs if any callback flag has expired and then update the callback list + List objectsToBeRemoved = new ArrayList(); + for (Object callerDbObject : callbackList) { + Set callbackRequests = handleCallbackAction(calleeName, (DBObject) callerDbObject, + callbackList, objectsToBeRemoved); + callsMap.addAll(callbackRequests); + } + updateCallbackList(entityCollection, callbackList, user, objectsToBeRemoved); + } + return callsMap; + } + + private DBObject findUserByName(String userName, DBCollection entityCollection) { + DBObject query = QueryBuilder.start(ENTITY_NAME).is("user").and(UID).is(userName).get(); + return entityCollection.findOne(query); + } + + /** + * Retrieves the current date in UTC timezone + */ + public static long getCurrentTimestamp() { + Date date = Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTime(); + return date.getTime(); + } + + private void updateCallbackList(DBCollection entityCollection, + BasicDBList callbackList, DBObject user, List objectsToBeRemoved) { + if (objectsToBeRemoved != null && !objectsToBeRemoved.isEmpty()) { + callbackList.removeAll(objectsToBeRemoved); + } + if (callbackList.isEmpty()) { + user.removeField(MongoConstants.CALLBACK_LIST); + } else { + user.put(MongoConstants.CALLBACK_LIST, callbackList); + } + entityCollection.save(user); + } + + /** + * Returns a list with objects to be removed because their callback duration has expired + */ + private Set handleCallbackAction(String calleeName,DBObject callbackObject, BasicDBList callbackList, + List objectsToBeRemoved) { + Set callSet = new HashSet(); + for (String callerName : callbackObject.keySet()) { + long callerDate = (long) callbackObject.get(callerName); + long currentDate = getCurrentTimestamp(); + long timeDiff = currentDate - callerDate; + // check if the flag for callback has expired + if (timeDiff < m_expires * 60000) { + CallbackLegs callbackLegs = new CallbackLegs(calleeName, callerName , callerDate); + callSet.add(callbackLegs); + } else { + objectsToBeRemoved.add(callbackObject); + } + } + return callSet; + } + + private DBCursor getCallbackUsers(DBCollection entityCollection) { + DBObject query = QueryBuilder.start(ENTITY_NAME).is("user").and(CALLBACK_LIST).exists(true).get(); + return entityCollection.find(query); + } + + @Required + public void setImdbTemplate(MongoTemplate imdbTemplate) { + m_imdbTemplate = imdbTemplate; + } + + @Required + public void setExpires(int expires) { + m_expires = expires; + } + + @Required + public void setHazelcastInstance(HazelcastInstance hazelcastInstance) { + m_hazelcastInstance = hazelcastInstance; + } + + @Override + public IQueue getCallbackQueue() { + return m_hazelcastInstance.getQueue(HAZELCAST_CALLBACK_QUEUE); + } + + @Override + public void initiateCallbackQueue() { + Cluster hazelcastCluster = m_hazelcastInstance.getCluster(); + Set hazelcastMembers = hazelcastCluster.getMembers(); + + // initiate on "primary" hazelcast instance only + if ((hazelcastCluster.getLocalMember().equals(hazelcastMembers.iterator().next()))) { + IAtomicReference initiated = m_hazelcastInstance + .getAtomicReference(HAZELCAST_CALLBACK_QUEUE_INITIATED); + // initiate the queue if needed + if (initiated.get() == null) { + LOG.debug("Setting up Hazelcast callback queue."); + initiated.set(new Boolean(true)); + Set calls = setupCallbackRequest(); + getCallbackQueue().clear(); + getCallbackQueue().addAll(calls); + } + } + } + + @Override + public IAtomicReference getAtomicReference(String key) { + return m_hazelcastInstance.getAtomicReference(key); + } + + @Override + public boolean isCallbackLegsFreeToProcess(CallbackLegs callbackLegs) { + IAtomicReference calleeReference = getAtomicReference(callbackLegs.getCalleeName()); + Boolean calleeIsProcessing = calleeReference.get(); + IAtomicReference callerReference = getAtomicReference(callbackLegs.getCallerName()); + Boolean callerIsProcessing = callerReference.get(); + // process this request ONLY if this callee is not currently beeing processed by another callback thread + return ((calleeReference.isNull() || calleeIsProcessing.equals(false)) + && (callerReference.isNull() || callerIsProcessing.equals(false))); + } + +} diff --git a/sipXcallback/src/org/sipfoundry/sipxcallback/common/FreeSwitchConfigurationImpl.java b/sipXcallback/src/org/sipfoundry/sipxcallback/common/FreeSwitchConfigurationImpl.java new file mode 100644 index 0000000000..69a40cc58f --- /dev/null +++ b/sipXcallback/src/org/sipfoundry/sipxcallback/common/FreeSwitchConfigurationImpl.java @@ -0,0 +1,78 @@ +/* + * + * + * Copyright (C) 2015 sipXcom., certain elements licensed under a Contributor Agreement. + * Contributors retain copyright to elements licensed under a Contributor Agreement. + * Licensed to the User under the LGPL license. + * + */ +package org.sipfoundry.sipxcallback.common; + +import org.apache.log4j.Logger; +import org.sipfoundry.commons.freeswitch.FreeSwitchConfigurationInterface; +import org.sipfoundry.commons.log4j.SipFoundryLayout; + +public class FreeSwitchConfigurationImpl implements FreeSwitchConfigurationInterface { + private static final Logger LOG = Logger.getLogger("org.sipfoundry.sipxcallback"); + private String m_logFile; + private int m_eventSocketPort; + private String m_docDirectory; + private String m_sipxChangeDomainName; + private String m_realm; + + @Override + public Logger getLogger() { + return LOG; + } + + @Override + public String getLogLevel() { + return SipFoundryLayout.getSipFoundryLogLevel(this.getClass()).toString(); + } + + public void setLogFile(String logFile) { + m_logFile = logFile; + } + + @Override + public String getLogFile() { + return m_logFile; + } + + public void setEventSocketPort(int port) { + m_eventSocketPort = port; + } + + @Override + public int getEventSocketPort() { + return m_eventSocketPort; + } + + public void setDocDirectory(String docDirectory) { + m_docDirectory = docDirectory; + } + + @Override + public String getDocDirectory() { + return m_docDirectory; + } + + public void setSipxchangeDomainName(String domain) { + m_sipxChangeDomainName = domain; + } + + @Override + public String getSipxchangeDomainName() { + return m_sipxChangeDomainName; + } + + @Override + public String getRealm() { + return m_realm; + } + + @Override + public void setRealm(String realm) { + m_realm = realm; + } +} diff --git a/sipXcallback/src/org/sipfoundry/sipxcallback/imdb.beans.xml b/sipXcallback/src/org/sipfoundry/sipxcallback/imdb.beans.xml new file mode 100644 index 0000000000..d4391cf5da --- /dev/null +++ b/sipXcallback/src/org/sipfoundry/sipxcallback/imdb.beans.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/sipXcallback/src/org/sipfoundry/sipxcallback/system.beans.xml b/sipXcallback/src/org/sipfoundry/sipxcallback/system.beans.xml new file mode 100644 index 0000000000..88d4ffd52b --- /dev/null +++ b/sipXcallback/src/org/sipfoundry/sipxcallback/system.beans.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + file:${conf.dir}/sipxcallback.properties + file:${conf.dir}/domain-config + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sipXcallback/test/Makefile.am b/sipXcallback/test/Makefile.am new file mode 100644 index 0000000000..06137cff51 --- /dev/null +++ b/sipXcallback/test/Makefile.am @@ -0,0 +1,17 @@ +include $(top_srcdir)/config/utility.am +include $(top_srcdir)/config/java.am +include $(top_srcdir)/common.am + +EXTRA_DIST = \ + $(test_SRC) \ + $(test_RESOURCES) + +noinst_DATA = javac-test + +test_SRC = $(shell cd $(srcdir); find org -name '*.java') +test_RESOURCES = $(shell find $(srcdir) -type f -not -name '*.java') + +test_DEPS = \ + $(JAVAROOT) \ + $(call JavaDep, @SIPX_JAVADIR@/sipXcommons, junit $(sipxcall_PKGS)) \ + $(sipxcall_JAVAROOT) diff --git a/sipXcallback/test/org/sipfoundry/callback/CallbackTest.java b/sipXcallback/test/org/sipfoundry/callback/CallbackTest.java new file mode 100644 index 0000000000..2b2548271b --- /dev/null +++ b/sipXcallback/test/org/sipfoundry/callback/CallbackTest.java @@ -0,0 +1,20 @@ +/* + * + * + * Copyright (C) 2015 sipXcom, certain elements licensed under a Contributor Agreement. + * Contributors retain copyright to elements licensed under a Contributor Agreement. + * Licensed to the User under the LGPL license. + * + */ + +package org.sipfoundry.callback; + +import junit.framework.TestCase; + +public class CallbackTest extends TestCase { + + public void testGoodbye() throws Exception { + assert(true); + } + +} diff --git a/sipXcdr/bin/sipxcdr-archive.in b/sipXcdr/bin/sipxcdr-archive.in index 99bc51e5cd..9b4b286925 100644 --- a/sipXcdr/bin/sipxcdr-archive.in +++ b/sipXcdr/bin/sipxcdr-archive.in @@ -24,7 +24,7 @@ class ArchiveBase def initialize @pguser = "postgres" @db = "SIPXCDR" - @tmpdir = "/tmp" + @tmpdir = "/var/sipxdata/tmp" @initd = "@SIPX_SERVICEDIR@" end @@ -41,6 +41,10 @@ end class Backup < ArchiveBase def run(params) + if params.has_key?(:tmp_dir) + @tmpdir=params[:tmp_dir] + end + cmd "echo CDR Backup uses #{@tmpdir} as temporary dir." cmd "pg_dump -U #{@pguser} -F t #{@db} | gzip > #{params[:file]}" or raise "Could not backup database" end @@ -53,6 +57,11 @@ class Restore < ArchiveBase end def run(params) + if params.has_key?(:tmp_dir) + @tmpdir=params[:tmp_dir] + end + cmd "echo CDR Restore uses #{@tmpdir} as temporary dir." + legacy_format = false unless cmd "tar -tzf #{params[:file]} toc.dat 2>/dev/null" cmd "tar -tzf #{params[:file]} db.tar" or @@ -113,6 +122,11 @@ EOF params[:file] = v } + opts.on("--tmp-dir ", + "Temporary backup file location"){ |v| + params[:tmp_dir] = v + } + opts.on("--verbose", "Restore the specified Configuration archive."){ verbose = true diff --git a/sipXcdr/bin/sipxcdr.in b/sipXcdr/bin/sipxcdr.in index c4e1c1812b..c02f5d0c62 100644 --- a/sipXcdr/bin/sipxcdr.in +++ b/sipXcdr/bin/sipxcdr.in @@ -139,8 +139,8 @@ EOF # special patch that upgrades DB version to the latest version - only use once VersionPatch="upgrade_version" -UpgradePatches7="" -UpgradePatches6="cdrremote $VersionPatch" +UpgradePatches7="gateway $VersionPatch" +UpgradePatches6="cdrremote $UpgradePatches7" UpgradePatches5="transaction $UpgradePatches6" UpgradePatches4="reference contact calldirection transaction $UpgradePatches5" UpgradePatches3="index_on_time $UpgradePatches4" diff --git a/sipXcdr/configure.ac b/sipXcdr/configure.ac index fc1cdb2994..41dc49d601 100644 --- a/sipXcdr/configure.ac +++ b/sipXcdr/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.57) -AC_INIT(sipXcdr, 15.04, sipx-dev@list.sipfoundry.org) +AC_INIT(sipXcdr, 16.12, sipx-dev@list.sipfoundry.org) AC_CONFIG_AUX_DIR(config) m4_include([config/general.m4]) m4_include([config/sipXlib.m4]) diff --git a/sipXcdr/etc/Makefile.am b/sipXcdr/etc/Makefile.am index 500523a151..229ddb942b 100644 --- a/sipXcdr/etc/Makefile.am +++ b/sipXcdr/etc/Makefile.am @@ -10,7 +10,8 @@ dist_sql_DATA = \ database/contact.sql \ database/calldirection.sql \ database/cdrremote.sql \ - database/transaction.sql + database/transaction.sql \ + database/gateway.sql cfinputsdir = @SIPX_CFINPUTS@/plugin.d dist_cfinputs_DATA = \ diff --git a/sipXcdr/etc/database/gateway.sql b/sipXcdr/etc/database/gateway.sql new file mode 100644 index 0000000000..116226a6e5 --- /dev/null +++ b/sipXcdr/etc/database/gateway.sql @@ -0,0 +1,2 @@ +alter table cdrs add column called_number text; +alter table cdrs add column gateway int; diff --git a/sipXcdr/etc/sipxcdr.cf b/sipXcdr/etc/sipxcdr.cf index 1c3a755e2a..eb04dd1d5a 100644 --- a/sipXcdr/etc/sipxcdr.cf +++ b/sipXcdr/etc/sipxcdr.cf @@ -37,10 +37,21 @@ bundle agent sipxcdr_config { comment => "install CDR config $(this.promiser)", create => "true", perms => m("644"), - copy_from => copy_from_cfdata("$(sipx.location_id)/$(conf_file)"), + edit_line => sipxcdr_callresolver_config, classes => if_repaired("restart_sipxcdr"); } +bundle edit_line sipxcdr_callresolver_config { + insert_lines: + any:: + "$(sipx.SIPX_CFDATA)/$(sipx.location_id)/callresolver-config.part" + insert_type => "file"; + 'SIP_CALLRESOLVER_DB_PASSWORD : $(sipx.NEW_POSTGRESQL_PASSWORD)'; + delete_lines: + any:: + ".*"; +} + bundle agent sipxcdr_setup { methods: !src.sipxcdr:: diff --git a/sipXcdr/lib/call_resolver.rb b/sipXcdr/lib/call_resolver.rb index 1be959db11..d982c8c0e4 100644 --- a/sipXcdr/lib/call_resolver.rb +++ b/sipXcdr/lib/call_resolver.rb @@ -45,7 +45,7 @@ def initialize(config) def check_connections @writer.test_connection rescue - log.error("Problems with CDR DB connection.") + log.error("call_resolver.rb:: Problems with CDR DB connection.") raise end @@ -58,7 +58,7 @@ def resolve(start_time, end_time) start_run = Time.now # limit the size of the queues: it's better to get starved than too eat all available memory - @log.debug("CSE Queue Size = #{@config.cse_queue_size} CDR Queue Size = #{@config.cdr_queue_size}") + @log.debug("call_resolver.rb:: CSE Queue Size = #{@config.cse_queue_size} CDR Queue Size = #{@config.cdr_queue_size}") cdr_queue = SizedQueue.new(@config.cdr_queue_size) cse_queue = SizedQueue.new(@config.cse_queue_size) @@ -109,8 +109,8 @@ def resolve(start_time, end_time) # re-raise the exception if any of the reader threads terminated abnormally raise t[:exception] if t.status.nil? end - log.debug("CSE Readers threads stopped.") - log.debug("CSEs in Queue = #{cse_queue.size()}") + log.debug("call_resolver.rb:: CSE Readers threads stopped.") + log.debug("call_resolver.rb:: CSEs in Queue = #{cse_queue.size()}") # stop running housekeeping jobs @long_calls_cleaner.stop @@ -118,7 +118,7 @@ def resolve(start_time, end_time) @long_ringing_calls_cleaner.stop ThreadsWait.all_waits(long_calls_cleaner_thread, failed_calls_cleaner_thread, long_ringing_calls_cleaner_thread) - log.debug("Clean-up threads stopped.") + log.debug("call_resolver.rb:: Clean-up threads stopped.") ensure @server.shutdown if @server @@ -128,21 +128,21 @@ def resolve(start_time, end_time) # wait for writer before exiting - but do not wait forever (all readers might be dead) writer_thread.join(10) if writer_thread - log.debug("CDR writer thread stopped.") + log.debug("call_resolver.rb:: CDR writer thread stopped.") - log.info("resolve: Done. Analysis took #{Time.now - start_run} seconds.") + log.info("call_resolver.rb:: resolve: Done. Analysis took #{Time.now - start_run} seconds.") end # install handler for INT (2) and TERM (9) signals to cleanly terminate readers threads def install_signal_handler(readers) %w( TERM INT ).each do | s | Signal.trap(s) do - log.info("#{s} intercepted. Terminating reader threads.") + log.info("call_resolver.rb:: #{s} intercepted. Terminating reader threads.") readers.each { |r| r.stop() } end end Signal.trap("USR1") do - log.debug(@state.to_s) + log.debug("call_resolver.rb:: " + @state.to_s) end Signal.trap("ABRT") do # A signal abort has been received. Handle it by simply exiting. diff --git a/sipXcdr/lib/cdr.rb b/sipXcdr/lib/cdr.rb index 197f8f7e5b..cd2e527ca6 100644 --- a/sipXcdr/lib/cdr.rb +++ b/sipXcdr/lib/cdr.rb @@ -153,6 +153,7 @@ def established? private def get_leg(cse) id = CallLeg.leg_id(cse) + puts cse.request_uri,"->", id @legs[id] ||= CallLeg.new(id, @log) end end @@ -207,7 +208,10 @@ def initialize(call_id, log=nil) @failure_status = nil @failure_reason = nil @call_direction = nil - + @called_number = nil + @gateway = nil + @contact_info = {} + @got_original = false @log = log @legs = CallLegs.new(@log) @@ -223,7 +227,8 @@ def initialize(call_id, log=nil) FIELDS = [:call_id, :from_tag, :to_tag, :caller_aor, :callee_aor, :start_time, :connect_time, :end_time, :termination, :failure_status, :failure_reason, - :call_direction, :reference, :caller_contact, :callee_contact, :caller_internal, :callee_route] + :call_direction, :reference, :caller_contact, :callee_contact, :caller_internal, :callee_route, + :called_number, :gateway ] attr_accessor(*FIELDS) @@ -301,8 +306,15 @@ def accept_call_request(cse) original = !cse.to_tag @caller_internal = cse.caller_internal @via_count = cse.via_count if (!@via_count || cse.via_count < @via_count) - @branch_id = cse.branch_id if (!@branch_id || cse.via_count <= @via_count) + @branch_id = cse.branch_id if (!@branch_id || cse.via_count <= @via_count) # bailout if we already have original request and this one is not + + if cse.request_uri.include? "sipxecs-lineid" + @called_number = Utils.contact_without_params(cse.request_uri) + @gateway = Integer(/.*sipxecs-lineid=(\d+).*/.match(cse.request_uri)[1]) + @contact_info[cse.branch_id] = [@called_number, @gateway] + end + return if(@got_original && !original) # continue if we are original or if we are older @@ -370,7 +382,7 @@ def apply_leg(leg) end @failure_reason = leg.failure_reason @failure_status = leg.failure_status - @callee_contact = leg.callee_contact + @callee_contact = leg.callee_contact end # Map termination codes to human-readable strings diff --git a/sipXcdr/lib/db/cdr_writer.rb b/sipXcdr/lib/db/cdr_writer.rb index 7ed9129291..b745611134 100644 --- a/sipXcdr/lib/db/cdr_writer.rb +++ b/sipXcdr/lib/db/cdr_writer.rb @@ -26,6 +26,7 @@ def run(queue) log.warn("Database error occurred for 5 times successively, skipping to the next CDR") end row = CdrWriter.row_from_cdr(cdr) + @last_row = row sth.execute(*row) check_purge(dbh) CdrWriter.cleanRetries() @@ -33,7 +34,7 @@ def run(queue) end end rescue DBI::DatabaseError => e - log.error("#{e.err}, #{e.errstr}") + log.error("cdr_writer.rb:: values = #{@last_row.join(', ')} ---- Error = #{e.err}, #{e.errstr}") CdrWriter.countRetries() retry end @@ -46,7 +47,7 @@ def last_cdr_start_time end def purge_now(dbh, start_time_cdr) - log.debug("Purging CDRs older than #{start_time_cdr}") + log.debug("cdr_writer.rb:: Purging CDRs older than #{start_time_cdr}") sql = CdrWriter.delete_sql dbh.prepare(sql) do | sth | sth.execute(start_time_cdr) @@ -58,7 +59,7 @@ def row_from_cdr(cdr) Cdr::FIELDS.collect { | f | cdr.send(f) } end - def insert_sql() + def insert_sql field_names = Cdr::FIELDS.collect { | f | f.to_s } field_str = field_names.join(', ') value_str = (['?'] * field_names.size).join(', ') diff --git a/sipXcdr/lib/db/cse_reader.rb b/sipXcdr/lib/db/cse_reader.rb index 3658329d0e..d6e898f7f9 100644 --- a/sipXcdr/lib/db/cse_reader.rb +++ b/sipXcdr/lib/db/cse_reader.rb @@ -11,7 +11,7 @@ require 'db/dao' require 'utils/terminator' -# Obtains CSEs from and puts them into CSE queue +# Obtains CSEs from DB and puts them into CSE queue class CseReader < Dao # Limit the maximum number of CSEs that get processed at once to avoid @@ -24,7 +24,7 @@ def initialize(database_url, purge_age, polling_interval, log = nil) @last_read_id = nil @last_read_time = nil @last_cse_event_time = nil - log.debug("Polling CSE DB every #{polling_interval} seconds.") if log + log.debug("cse_reader.rb:: Polling CSE DB every #{polling_interval} seconds.") if log @poll_time = polling_interval @synch_time = 120 if @synch_time < @poll_time @@ -42,7 +42,7 @@ def initialize(database_url, purge_age, polling_interval, log = nil) # end # If stop time is nil reader will keep on reading the connection forever def run(cse_queue, start_id, start_time, stop_time = nil) - @log.debug("Connecting CSE database. #{@database_url}") if @log + @log.debug("cse_reader.rb:: Connecting CSE database. #{@database_url}") if @log connect do | dbh | if stop_time check_purge(dbh) @@ -55,35 +55,35 @@ def run(cse_queue, start_id, start_time, stop_time = nil) check_purge(dbh) first_id = @last_read_id first_time = @last_read_time unless first_id - @log.debug("Read CSEs from ID:#{first_id} Time:#{first_time}") if @log + @log.debug("cse_reader.rb:: Read CSEs from ID:#{first_id} Time:#{first_time}") if @log read_cses(dbh, cse_queue, first_id, first_time, nil) # If we read less than MAX_CSES then we're done reading for now. if ((first_id.to_i + MAX_CSES) > @last_read_id.to_i) - @log.debug("Going to sleep. Connection #{@database_url.host}:#{@database_url.port}") if @log + @log.debug("cse_reader.rb:: Going to sleep. Connection #{@database_url.host}:#{@database_url.port}") if @log break if @stop.wait - @log.debug("Waking up. Connection #{@database_url.host}:#{@database_url.port}") if @log + @log.debug("cse_reader.rb:: Waking up. Connection #{@database_url.host}:#{@database_url.port}") if @log end end end end rescue DBI::DatabaseError, DBI::OperationalError, DBI::ProgrammingError => excp - @log.error("Loss of connection to database #{@database_url} - retrying to connect after sleep") if @log + @log.error("cse_reader.rb:: Loss of connection to database #{@database_url} - retrying to connect after sleep") if @log if !@stop.wait dbh.disconnect retry end rescue - @log.error("Exception in reader thread: <#{$!}> - Database = #{@database_url}") if @log + @log.error("cse_reader.rb:: Exception in reader thread: <#{$!}> - Database = #{@database_url}") if @log # save current exception so that we can re-raise it Thread.current[:exception] = $! raise ensure - @log.debug("Stopping CSE reader - #{@database_url.host}:#{@database_url.port}.") if @log + @log.debug("cse_reader.rb:: Stopping CSE reader - #{@database_url.host}:#{@database_url.port}.") if @log end def stop - @log.debug("CSE Reader - #{@database_url.host}:#{@database_url.port} - calling stop") if @log + @log.debug("cse_reader.rb:: CSE Reader - #{@database_url.host}:#{@database_url.port} - calling stop") if @log @stop.stop end @@ -113,7 +113,7 @@ def read_cses(dbh, cse_queue, start_id, start_time, stop_time) # A CSE with a nil cse_id is the trigger that indicates that the cse is a time synch event. cse = CallStateEvent.new @last_cse_event_time = cse.event_time = DBI::Timestamp.new(@last_cse_event_time.to_time() + @synch_time ) - @log.debug("Injecting synchronize event: new time #{cse.event_time}") if @log + @log.debug("cse_reader.rb:: Injecting synchronize event: new time #{cse.event_time}") if @log cse_queue << cse # re-init the no cse's read loop counter because we've sent a time synch event. @@ -126,7 +126,7 @@ def read_cses(dbh, cse_queue, start_id, start_time, stop_time) end def purge_now(dbh, start_time_cse) - @log.debug("Purging CSEs older than #{start_time_cse}") if @log + @log.debug("cse_reader.rb:: Purging CSEs older than #{start_time_cse}") if @log sql = CseReader.delete_sql(nil, start_time_cse) dbh.prepare(sql) do | sth | sth.execute(start_time_cse) diff --git a/sipXcdr/lib/db/dao.rb b/sipXcdr/lib/db/dao.rb index d68345fdc7..40f7b1a959 100644 --- a/sipXcdr/lib/db/dao.rb +++ b/sipXcdr/lib/db/dao.rb @@ -33,6 +33,7 @@ class Dao def initialize(database_url, purge_age, table_name, log) @connection = database_url.to_dbi @username = database_url.username + @password = database_url.password @purge_age = purge_age @table_name = table_name @last_purge_time = nil @@ -40,7 +41,7 @@ def initialize(database_url, purge_age, table_name, log) end def connect(&block) - DBI.connect(@connection, @username, &block) + DBI.connect(@connection, @username, @password, &block) end # this is run to test if DB connection is working diff --git a/sipXcdr/lib/db/database_url.rb b/sipXcdr/lib/db/database_url.rb index aead7ecce8..9d800596ea 100644 --- a/sipXcdr/lib/db/database_url.rb +++ b/sipXcdr/lib/db/database_url.rb @@ -6,7 +6,7 @@ ############################################################################## # Holds the params needed to connect to a database -class DatabaseUrl < Struct.new(:database, :port, :host, :adapter, :username) +class DatabaseUrl < Struct.new(:database, :port, :host, :adapter, :username, :password) def initialize(args) super() @@ -20,6 +20,7 @@ def initialize(args) if self[:username].nil? raise ArgumentError, 'postgres username is required' end + self[:password] ||= '' end def to_dbi diff --git a/sipXcdr/lib/main.rb b/sipXcdr/lib/main.rb index 8b87f4f6d8..39313242c0 100644 --- a/sipXcdr/lib/main.rb +++ b/sipXcdr/lib/main.rb @@ -107,7 +107,7 @@ def main() resolver.resolve(start_time, end_time) end - config.log.error("Exiting because of error: <#{$!}>") + config.log.error("main.rb:: Exiting because of error: <#{$!}>") config.log.error do start_line = "\n" $!.backtrace.inject("") {|trace, line| "#{trace}\n#{line}" } diff --git a/sipXcdr/lib/rest/active_cdrs.rb b/sipXcdr/lib/rest/active_cdrs.rb index 646d79dc70..bf972e05d5 100644 --- a/sipXcdr/lib/rest/active_cdrs.rb +++ b/sipXcdr/lib/rest/active_cdrs.rb @@ -19,7 +19,7 @@ def get_instance(config, *options) def do_GET(req, resp) active_calls = @cdrService.getActiveCalls - @log.debug("getActiveCalls by REST #{active_calls.size}") if @log + @log.debug("active_cdrs.rb:: getActiveCalls by REST #{active_calls.size}") if @log doc = REXML::Document.new '' name = req.query['name'] diff --git a/sipXcdr/lib/soap/server.rb b/sipXcdr/lib/soap/server.rb index e07976579e..fff99f2576 100644 --- a/sipXcdr/lib/soap/server.rb +++ b/sipXcdr/lib/soap/server.rb @@ -47,7 +47,7 @@ def getActiveCalls @state.active_cdrs.each do | cdr | active_calls << ActiveCall.new(cdr, now) if cdr.termination == Cdr::CALL_IN_PROGRESS_TERM && cdr.start_time end - @log.debug("getActiveCalls #{active_calls.size}") if @log + @log.debug("server.rb:: getActiveCalls #{active_calls.size}") if @log return active_calls end end diff --git a/sipXcdr/lib/state.rb b/sipXcdr/lib/state.rb index 79bb48da2a..6906f67453 100644 --- a/sipXcdr/lib/state.rb +++ b/sipXcdr/lib/state.rb @@ -49,14 +49,15 @@ def run # that feed the cse_queue. myThread = Thread.current myThread.priority = 2 - @log.debug("State Thread priority = #{myThread.priority}") if @log + @log.debug("state.rb:: State Thread priority = #{myThread.priority}") if @log while item = @cse_queue.shift if item.kind_of?(Array) - @log.debug("Start #{item[0]}") if @log + @log.debug("state.rb:: Start #{item[0]}") if @log housekeeping(*item) - @log.debug("Finished #{item[0]}") if @log + @log.debug("state.rb:: Finished #{item[0]}") if @log else accept(item) + #@log.debug("state.rb:: Accepted #{item}") if @log end end flush_failed() @@ -82,7 +83,7 @@ def dump_state end def to_s - "Generation #{@generation}, Retired: #{@retired_calls.size}, Failed: #{@failed_calls.size}, Waiting: #{@cdrs.size}, CSE Queue: #{@cse_queue.length}" + "state.rb:: Generation #{@generation}, Retired: #{@retired_calls.size}, Failed: #{@failed_calls.size}, Waiting: #{@cdrs.size}, CSE Queue: #{@cse_queue.length}" end # Analyze CSE, add CDR to queue if completed @@ -95,7 +96,7 @@ def accept(cse) # Synchronize event in order to trigger advancing last_event_time if ( @last_event_time.to_time() < cse.event_time.to_time() ) @last_event_time = cse.event_time - @log.debug("CSE last event time updated to #{@last_event_time}") if @log + @log.debug("state.rb:: CSE last event time updated to #{@last_event_time}") if @log end end return @@ -103,7 +104,7 @@ def accept(cse) @last_event_time = cse.event_time end - @log.debug("CSE #{cse.event_type}") if @log + #@log.debug("CSE #{cse.event_type}") if @log call_id = cse.call_id @@ -231,7 +232,10 @@ def flush_failed_calls(max_wait_time) @failed_calls.delete_if do |key, value| cdr = value.cdr start_time = cdr.start_time - notify(cdr) if start_time && (@last_event_time.to_time() - start_time.to_time() > max_wait_time) + if start_time && (@last_event_time.to_time() - start_time.to_time() > max_wait_time) + notify(cdr) + true + end end end diff --git a/sipXcdr/lib/utils/call_resolver_configure.rb b/sipXcdr/lib/utils/call_resolver_configure.rb index 67dc62722c..e488b64e82 100644 --- a/sipXcdr/lib/utils/call_resolver_configure.rb +++ b/sipXcdr/lib/utils/call_resolver_configure.rb @@ -89,13 +89,14 @@ def initialize(config, confdir = DEFAULT_CONF_DIR, logdir = DEFAULT_LOG_DIR) # so we can log messages in the methods that are called here. @db_user = config.fetch('SIP_CALLRESOLVER_DB_USER', 'postgres') + @db_password = config.fetch('SIP_CALLRESOLVER_DB_PASSWORD', '') # default values for db connection info - @local_db_url = DatabaseUrl.new(:username => @db_user) + @local_db_url = DatabaseUrl.new(:username => @db_user, :password => @db_password) # :TODO: read CDR database URL params from the Call Resolver config file # rather than just hardwiring default values. - @cdr_database_url = DatabaseUrl.new(:username => @db_user) + @cdr_database_url = DatabaseUrl.new(:username => @db_user, :password => @db_password) # These two methods must get called in this order @cse_hosts, @ha = get_cse_hosts_config @@ -261,7 +262,7 @@ def get_cse_database_urls_config(cse_hosts) return [ @cdr_database_url ] if cse_hosts.empty? # Build the list of CSE DB URLs. cse_hosts.collect do |cse_host| - DatabaseUrl.new(:host =>cse_host.host, :port => cse_host.port, :username => @db_user) + DatabaseUrl.new(:host =>cse_host.host, :port => cse_host.port, :username => @db_user, :password => @db_password) end end @@ -292,11 +293,12 @@ def get_cse_hosts_config host_port = port.to_i raise ConfigException, "Port for #{host} is invalid." if host_port == 0 cse_hosts << CseHost.new(host, host_port) - log.debug("cse_hosts: host name #{host}, host port: #{port}") + log.debug("call_resolver_configure.rb:: cse_hosts: host name #{host}, host port: #{port}") # If at least one of the hosts != 'localhost' we are HA enabled end ha = cse_hosts.length > 1 - log.debug("Found host other than localhost - enable HA") if ha + log.info("call_resolver_configure,rb:: Did not find other host than localhost - keeping HA disabled") unless ha + log.info("call_resolver_configure.rb:: Found host other than localhost - enable HA") if ha return cse_hosts, ha end diff --git a/sipXcdrLog/configure.ac b/sipXcdrLog/configure.ac index 643759c670..c208b03256 100644 --- a/sipXcdrLog/configure.ac +++ b/sipXcdrLog/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.57) -AC_INIT(sipXcdrLog, 15.04, sipx-dev@list.sipfoundry.org) +AC_INIT(sipXcdrLog, 16.12, sipx-dev@list.sipfoundry.org) AC_CONFIG_AUX_DIR(config) m4_include([config/general.m4]) m4_include([config/sipXlib.m4]) diff --git a/sipXcdrLog/src/org/sipfoundry/sipxrest/cdrlog/CdrLogRestlet.java b/sipXcdrLog/src/org/sipfoundry/sipxrest/cdrlog/CdrLogRestlet.java index e8c88bad2d..a20cc4ed69 100644 --- a/sipXcdrLog/src/org/sipfoundry/sipxrest/cdrlog/CdrLogRestlet.java +++ b/sipXcdrLog/src/org/sipfoundry/sipxrest/cdrlog/CdrLogRestlet.java @@ -52,6 +52,7 @@ public void handle(Request request, Response response) { RestServerConfig config = RestServer.getRestServerConfig(); String cdrDBUrl = config.getSipxcdrAddress(); String dbUser = config.getDbUser(); + String dbPassword = config.getDbPassword(); try { Method httpMethod = request.getMethod(); @@ -105,7 +106,7 @@ public void handle(Request request, Response response) { System.setProperty("jdbc.drivers", "org.postgresql.Driver"); // Establish a connection to the CDR database. - cdrConnection = DriverManager.getConnection(cdrDBUrl, dbUser, ""); + cdrConnection = DriverManager.getConnection(cdrDBUrl, dbUser, dbPassword); String sqlPrepareString; String userLike = "%:" + userId + "@%"; diff --git a/sipXcisco/configure.ac b/sipXcisco/configure.ac index c6ea54123d..8c58223117 100644 --- a/sipXcisco/configure.ac +++ b/sipXcisco/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.57) -AC_INIT(sipXcisco, 15.04, sipx-dev@list.sipfoundry.org) +AC_INIT(sipXcisco, 16.12, sipx-dev@list.sipfoundry.org) AC_CONFIG_AUX_DIR(config) m4_include([config/general.m4]) m4_include([config/sipXlib.m4]) diff --git a/sipXcisco/src/org/sipfoundry/sipxconfig/phone/Ciscospa/CiscospaPhone.java b/sipXcisco/src/org/sipfoundry/sipxconfig/phone/Ciscospa/CiscospaPhone.java index 3951ae9008..cb5b1c3f48 100755 --- a/sipXcisco/src/org/sipfoundry/sipxconfig/phone/Ciscospa/CiscospaPhone.java +++ b/sipXcisco/src/org/sipfoundry/sipxconfig/phone/Ciscospa/CiscospaPhone.java @@ -22,8 +22,6 @@ import org.sipfoundry.sipxconfig.setting.Setting; import org.sipfoundry.sipxconfig.setting.SettingEntry; -import static org.sipfoundry.sipxconfig.common.SipUri.stripSipPrefix; - /** * Ciscospa phone. */ @@ -139,13 +137,13 @@ public String getMohUrl() { return SipUri.stripSipPrefix(mohUri); } - @SettingEntry(path = "Call_Feature_Settings/Voice_Mail_Server") - public String getVoicemailServer() { + @SettingEntry(path = "Call_Feature_Settings/Mailbox_ID") + public String getMailboxId() { User user = m_line.getUser(); if (user != null) { - return stripSipPrefix(user.getAddrSpec(m_defaults.getDomainName())); + return user.getUserName(); } - return null; + return ""; } } diff --git a/sipXcisco/src/org/sipfoundry/sipxconfig/phone/linksys/LinksysPhone.java b/sipXcisco/src/org/sipfoundry/sipxconfig/phone/linksys/LinksysPhone.java index 22c092a4ba..8ab14c0216 100755 --- a/sipXcisco/src/org/sipfoundry/sipxconfig/phone/linksys/LinksysPhone.java +++ b/sipXcisco/src/org/sipfoundry/sipxconfig/phone/linksys/LinksysPhone.java @@ -22,8 +22,6 @@ import org.sipfoundry.sipxconfig.setting.Setting; import org.sipfoundry.sipxconfig.setting.SettingEntry; -import static org.sipfoundry.sipxconfig.common.SipUri.stripSipPrefix; - /** * Linksys942 phone. */ @@ -139,13 +137,13 @@ public String getMohUrl() { return SipUri.stripSipPrefix(mohUri); } - @SettingEntry(path = "Call_Feature_Settings/Voice_Mail_Server") - public String getVoicemailServer() { + @SettingEntry(path = "Call_Feature_Settings/Mailbox_ID") + public String getMailboxId() { User user = m_line.getUser(); if (user != null) { - return stripSipPrefix(user.getAddrSpec(m_defaults.getDomainName())); + return user.getUserName(); } - return null; + return ""; } } diff --git a/sipXcisco/test/org/sipfoundry/sipxconfig/phone/linksys/spa942.cfg b/sipXcisco/test/org/sipfoundry/sipxconfig/phone/linksys/spa942.cfg index e1d9ae2356..4cb63519d1 100755 --- a/sipXcisco/test/org/sipfoundry/sipxconfig/phone/linksys/spa942.cfg +++ b/sipXcisco/test/org/sipfoundry/sipxconfig/phone/linksys/spa942.cfg @@ -340,8 +340,8 @@ - -juser@sipfoundry.org +juser + no diff --git a/sipXclearone/configure.ac b/sipXclearone/configure.ac index 684020db7f..221c6bb4a0 100644 --- a/sipXclearone/configure.ac +++ b/sipXclearone/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.57) -AC_INIT(sipXclearone, 15.04, sipx-dev@list.sipfoundry.org) +AC_INIT(sipXclearone, 16.12, sipx-dev@list.sipfoundry.org) AC_CONFIG_AUX_DIR(config) m4_include([config/general.m4]) m4_include([config/sipXlib.m4]) diff --git a/sipXcom/.classpath b/sipXcom/.classpath new file mode 100644 index 0000000000..dec88061e3 --- /dev/null +++ b/sipXcom/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/sipXcom/.project b/sipXcom/.project new file mode 100644 index 0000000000..78c4acaaa6 --- /dev/null +++ b/sipXcom/.project @@ -0,0 +1,17 @@ + + + sipXcom + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/sipXcom/Makefile.am b/sipXcom/Makefile.am new file mode 100644 index 0000000000..4218c42cb9 --- /dev/null +++ b/sipXcom/Makefile.am @@ -0,0 +1,6 @@ +include config/utility.am +include config/project.am + +SUBDIRS = \ + src \ + . diff --git a/sipXcom/config b/sipXcom/config new file mode 120000 index 0000000000..3ca249e06b --- /dev/null +++ b/sipXcom/config @@ -0,0 +1 @@ +../config \ No newline at end of file diff --git a/sipXcom/configure.ac b/sipXcom/configure.ac new file mode 100644 index 0000000000..8e992b5e3e --- /dev/null +++ b/sipXcom/configure.ac @@ -0,0 +1,20 @@ +AC_PREREQ(2.57) +AC_INIT(sipXcom, 16.12, sipx-dev@list.sipxcom.org) +AC_CONFIG_AUX_DIR(config) +AM_INIT_AUTOMAKE(foreign tar-ustar) +m4_include([config/general.m4]) +m4_include([config/sipXlib.m4]) +m4_include([config/sipXlib2.m4]) +m4_include([config/java.m4]) +m4_include([config/java2.m4]) + +AC_PROG_JAVA([java]) +PROG_JAVA_DEP +# Psotgres operations +CHECK_POSTGRES + +AC_CONFIG_FILES([ + Makefile + src/Makefile +]) +AC_OUTPUT diff --git a/sipXcom/sipxcom.spec.in b/sipXcom/sipxcom.spec.in new file mode 100644 index 0000000000..ba84b5bebd --- /dev/null +++ b/sipXcom/sipxcom.spec.in @@ -0,0 +1,170 @@ +Name: @PACKAGE@ +Version: @VERSION@ +Release: @PACKAGE_REVISION@ + +Summary: sipXcom ui plugin +License: LGPL +Group: Telcommunications +Vendor: sipXcom +Packager: Douglas Hubler +Url: http://www.sipxcom.org + +%define __jar_repack %{nil} + +BuildRequires: automake +BuildRequires: libxml2 +BuildRequires: sipxconfig >= %version + +Requires: libcgroup +Requires: freeswitch >= 1.0.7 +Requires: freeswitch-application-abstraction +Requires: freeswitch-application-avmd +Requires: freeswitch-application-blacklist +Requires: freeswitch-application-callcenter +Requires: freeswitch-application-cidlookup +Requires: freeswitch-application-conference +Requires: freeswitch-application-curl +Requires: freeswitch-application-db +Requires: freeswitch-application-directory +Requires: freeswitch-application-distributor +Requires: freeswitch-application-easyroute +Requires: freeswitch-application-esf +Requires: freeswitch-application-expr +Requires: freeswitch-application-fifo +Requires: freeswitch-application-fsk +Requires: freeswitch-application-fsv +Requires: freeswitch-application-hash +Requires: freeswitch-application-httapi +Requires: freeswitch-application-http-cache +Requires: freeswitch-application-lcr +Requires: freeswitch-application-limit +Requires: freeswitch-application-memcache +Requires: freeswitch-application-nibblebill +Requires: freeswitch-application-redis +Requires: freeswitch-application-rss +Requires: freeswitch-application-sms +Requires: freeswitch-application-snapshot +Requires: freeswitch-application-snom +Requires: freeswitch-application-soundtouch +Requires: freeswitch-application-spy +Requires: freeswitch-application-stress +Requires: freeswitch-application-valet_parking +Requires: freeswitch-application-voicemail +Requires: freeswitch-application-voicemail-ivr +Requires: freeswitch-asrtts-flite +Requires: freeswitch-asrtts-pocketsphinx +Requires: freeswitch-asrtts-tts-commandline +Requires: freeswitch-asrtts-unimrcp +Requires: freeswitch-codec-bv +Requires: freeswitch-codec-celt +Requires: freeswitch-codec-codec2 +Requires: freeswitch-codec-h26x +Requires: freeswitch-codec-ilbc +Requires: freeswitch-codec-isac +Requires: freeswitch-codec-mp4v +Requires: freeswitch-codec-opus +Requires: freeswitch-codec-passthru-amr +Requires: freeswitch-codec-passthru-amrwb +Requires: freeswitch-codec-passthru-g723_1 +Requires: freeswitch-codec-passthru-g729 +Requires: freeswitch-codec-silk +Requires: freeswitch-codec-siren +Requires: freeswitch-codec-theora +Requires: freeswitch-codec-vp8 +Requires: freeswitch-config-vanilla +Requires: freeswitch-endpoint-dingaling +Requires: freeswitch-endpoint-portaudio +Requires: freeswitch-endpoint-rtmp +Requires: freeswitch-endpoint-skinny +Requires: freeswitch-endpoint-skypopen +Requires: freeswitch-event-cdr-sqlite +Requires: freeswitch-event-erlang-event +Requires: freeswitch-event-json-cdr +Requires: freeswitch-event-multicast +Requires: freeswitch-event-rayo +Requires: freeswitch-format-local-stream +Requires: freeswitch-format-mod-shout +Requires: freeswitch-format-native-file +Requires: freeswitch-format-portaudio-stream +Requires: freeswitch-format-shell-stream +Requires: freeswitch-format-ssml +Requires: freeswitch-format-tone-stream +Requires: freeswitch-lua +Requires: freeswitch-timer-posix +Requires: freeswitch-xml-cdr +Requires: freeswitch-xml-curl +Requires: sipxaastra >= %version +Requires: sipxacccode >= %version +Requires: sipxaudiocodes >= %version +Requires: sipxbridge >= %version +Requires: sipxcallcontroller >= %version +Requires: sipxcallback >= %version +Requires: sipxcdr >= %version +Requires: sipxcdrlog >= %version +Requires: sipxcisco >= %version +Requires: sipxclearone >= %version +Requires: sipxconfig >= %version +Requires: sipxconfig-ftp >= %version +Requires: sipxconfig-tftp >= %version +Requires: sipxcounterpath >= %version +Requires: sipxgrandstream >= %version +Requires: sipxgtek >= %version +Requires: sipxhitachi >= %version +Requires: sipxtcpdumplog >= %version +Requires: sipximbot >= %version +Requires: sipxipdialog >= %version +Requires: sipxisphone >= %version +Requires: sipxivr >= %version +Requires: sipxlg-nortel >= %version +Requires: sipxmitel >= %version +Requires: sipxnortel >= %version +Requires: sipxopenfire >= %version +Requires: sipxpage >= %version +Requires: sipxpolycom >= %version +Requires: sipxyealink >= %version +Requires: sipxprovision >= %version +Requires: sipxproxy >= %version +Requires: sipxpublisher >= %version +Requires: sipxrecording >= %version +Requires: sipxregistry >= %version +Requires: sipxrelease >= %version +Requires: sipxrelay >= %version +Requires: sipxrest >= %version +Requires: sipxrls >= %version +Requires: sipxsaa >= %version +Requires: sipxsnom >= %version +Requires: sipxsupervisor >= %version +Requires: sipxsqa-server >= %version +Requires: sipxunidata >= %version +Requires: sipxcallqueue +Requires: sipxjitsi >= %version + +Obsoletes: %{name}-doc +Obsoletes: epel-release + +Source: %name-%version.tar.gz +Prefix: %_prefix +BuildRoot: %{_tmppath}/%name-%version-root + +%description +sipXcom config plugin + +%prep +%setup -q + +%build +%configure --enable-rpmbuild @SIPX_RPM_CONFIGURE_OPTIONS@ +make + +%install +rm -rf $RPM_BUILD_ROOT +make DESTDIR=$RPM_BUILD_ROOT install + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(644,root,root,755) +%{_datadir}/java/sipXecs/sipXconfig/plugins/sipXcom.jar + +%post diff --git a/sipXcom/src/Makefile.am b/sipXcom/src/Makefile.am new file mode 100644 index 0000000000..9f23821700 --- /dev/null +++ b/sipXcom/src/Makefile.am @@ -0,0 +1,21 @@ +include $(top_srcdir)/config/utility.am +include $(top_srcdir)/config/java.am + +EXTRA_DIST = \ + $(sipxcom_RESOURCES) + +sipxcom_RESOURCES = $(shell cd $(srcdir); find . -type f \(\ + -name '*.txt' \ + -o -name '*.png' \ + -o -name '*.xml' \ + -o -name '*.properties' \ + \)) + +JAR_FILE = sipXcom.jar +jardir = @SIPX_JAVADIR@/sipXconfig/plugins +jar_DATA = $(JAR_FILE) + +$(JAR_FILE) : $(sipxcom_RESOURCES) + jar -cf $@.tmp . + cd $(srcdir); jar -uf $(abspath .)/$@.tmp $(sipxcom_RESOURCES) + mv $@.tmp $@ diff --git a/sipXcom/src/sipXcomAdminLogo.png b/sipXcom/src/sipXcomAdminLogo.png new file mode 100644 index 0000000000..5a28281dfa Binary files /dev/null and b/sipXcom/src/sipXcomAdminLogo.png differ diff --git a/sipXcom/src/sipXcomLoginScreen.png b/sipXcom/src/sipXcomLoginScreen.png new file mode 100644 index 0000000000..3c2c53e02e Binary files /dev/null and b/sipXcom/src/sipXcomLoginScreen.png differ diff --git a/sipXcom/src/sipxcom.properties b/sipXcom/src/sipxcom.properties new file mode 100755 index 0000000000..3dff678583 --- /dev/null +++ b/sipXcom/src/sipxcom.properties @@ -0,0 +1,9 @@ +product.name=sipXcom +product.copyright=Builds made available to sipXcom Community by eZuce Incorporated. \ +Portions Copyright (C) 2014 SIPfoundry Licensed under AGPL v3 + +help.link=http://wiki.sipxcom.org/ +about.release=Release information: +about.helpLink=http://www.sipxcom.org/ +about.help=Help +product.license=

Licensed to the user under the Affero General Public License(AGPL) version 3 or newer.

diff --git a/sipXcom/src/sipxplugin2.beans.xml b/sipXcom/src/sipxplugin2.beans.xml new file mode 100644 index 0000000000..42775ad9c5 --- /dev/null +++ b/sipXcom/src/sipxplugin2.beans.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + sipXcomAdminLogo.png + sipXcomLoginScreen.png + + + + diff --git a/sipXcommons/.classpath b/sipXcommons/.classpath index eb07526cd3..1c33129c47 100644 --- a/sipXcommons/.classpath +++ b/sipXcommons/.classpath @@ -74,7 +74,6 @@ - @@ -110,7 +109,6 @@ - @@ -194,5 +192,13 @@ + + + + + + + + diff --git a/sipXcommons/configure.ac b/sipXcommons/configure.ac index 23b90a5e9a..8c368bd8a4 100644 --- a/sipXcommons/configure.ac +++ b/sipXcommons/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.57) -AC_INIT(sipXcommons, 15.04, sipx-dev@list.sipfoundry.org) +AC_INIT(sipXcommons, 16.12, sipx-dev@list.sipfoundry.org) AC_CONFIG_AUX_DIR(config) m4_include([config/java2.m4]) m4_include([config/general.m4]) diff --git a/sipXcommons/etc/Makefile.am b/sipXcommons/etc/Makefile.am index a694c58169..da8a4fa305 100644 --- a/sipXcommons/etc/Makefile.am +++ b/sipXcommons/etc/Makefile.am @@ -5,7 +5,7 @@ confdir = $(SIPX_DATADIR)/schema dist_conf_DATA = \ spring-beans-2.0.xsd \ spring-util-2.0.xsd \ - spring-task-3.1.xsd \ + spring-task-4.0.xsd \ spring-security-3.1.xsd \ spring-all.xsd \ jaxrs.xsd \ diff --git a/sipXcommons/etc/spring-all.xsd b/sipXcommons/etc/spring-all.xsd index eb6ea203ab..3b48b39317 100644 --- a/sipXcommons/etc/spring-all.xsd +++ b/sipXcommons/etc/spring-all.xsd @@ -3,7 +3,7 @@ xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.example.com"> - + diff --git a/sipXcommons/etc/spring-task-3.1.xsd b/sipXcommons/etc/spring-task-3.1.xsd deleted file mode 100644 index 56680707c2..0000000000 --- a/sipXcommons/etc/spring-task-3.1.xsd +++ /dev/null @@ -1,289 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sipXcommons/etc/spring-task-4.0.xsd b/sipXcommons/etc/spring-task-4.0.xsd new file mode 100644 index 0000000000..2ae24e0d2f Binary files /dev/null and b/sipXcommons/etc/spring-task-4.0.xsd differ diff --git a/sipXcommons/lib/elasticsearch-1.7.2.jar b/sipXcommons/lib/elasticsearch-1.7.2.jar new file mode 100644 index 0000000000..11abb22cab Binary files /dev/null and b/sipXcommons/lib/elasticsearch-1.7.2.jar differ diff --git a/sipXcommons/lib/gson-2.3.1.jar b/sipXcommons/lib/gson-2.3.1.jar new file mode 100644 index 0000000000..250132c197 Binary files /dev/null and b/sipXcommons/lib/gson-2.3.1.jar differ diff --git a/sipXcommons/lib/lucene-analyzers-common-4.10.4.jar b/sipXcommons/lib/lucene-analyzers-common-4.10.4.jar new file mode 100644 index 0000000000..701caeb79e Binary files /dev/null and b/sipXcommons/lib/lucene-analyzers-common-4.10.4.jar differ diff --git a/sipXcommons/lib/lucene-core-3.2.0.jar b/sipXcommons/lib/lucene-core-3.2.0.jar deleted file mode 100644 index ec436dc319..0000000000 Binary files a/sipXcommons/lib/lucene-core-3.2.0.jar and /dev/null differ diff --git a/sipXcommons/lib/lucene-core-4.10.4.jar b/sipXcommons/lib/lucene-core-4.10.4.jar new file mode 100644 index 0000000000..823664c487 Binary files /dev/null and b/sipXcommons/lib/lucene-core-4.10.4.jar differ diff --git a/sipXcommons/lib/lucene-queryparser-4.10.4.jar b/sipXcommons/lib/lucene-queryparser-4.10.4.jar new file mode 100644 index 0000000000..f9ac0e44b0 Binary files /dev/null and b/sipXcommons/lib/lucene-queryparser-4.10.4.jar differ diff --git a/sipXcommons/lib/lucene-suggest-4.10.4.jar b/sipXcommons/lib/lucene-suggest-4.10.4.jar new file mode 100644 index 0000000000..6eb7a8bdec Binary files /dev/null and b/sipXcommons/lib/lucene-suggest-4.10.4.jar differ diff --git a/sipXcommons/lib/org.restlet.ext.fileupload-1.2.jar b/sipXcommons/lib/org.restlet.ext.fileupload-1.2.jar new file mode 100644 index 0000000000..7caebe367f Binary files /dev/null and b/sipXcommons/lib/org.restlet.ext.fileupload-1.2.jar differ diff --git a/sipXcommons/lib/xstream-1.3.1.jar b/sipXcommons/lib/xstream-1.3.1.jar deleted file mode 100644 index 4ef4219c6f..0000000000 Binary files a/sipXcommons/lib/xstream-1.3.1.jar and /dev/null differ diff --git a/sipXcommons/lib/xstream-1.4.8.jar b/sipXcommons/lib/xstream-1.4.8.jar new file mode 100644 index 0000000000..a493c3b99d Binary files /dev/null and b/sipXcommons/lib/xstream-1.4.8.jar differ diff --git a/sipXcommons/sipxcommons.spec.in b/sipXcommons/sipxcommons.spec.in index 0f77d7966f..a66279dd08 100644 --- a/sipXcommons/sipxcommons.spec.in +++ b/sipXcommons/sipxcommons.spec.in @@ -95,7 +95,7 @@ fi %{_datadir}/sipxecs/schema/spring-beans-2.0.xsd %{_datadir}/sipxecs/schema/spring-util-2.0.xsd %{_datadir}/sipxecs/schema/spring-security-3.1.xsd -%{_datadir}/sipxecs/schema/spring-task-3.1.xsd +%{_datadir}/sipxecs/schema/spring-task-4.0.xsd %{_datadir}/sipxecs/schema/jaxrs.xsd %{_datadir}/sipxecs/schema/spring-context.xsd %{_datadir}/sipxecs/schema/spring-mvc.xsd diff --git a/sipXcommons/src/common.am b/sipXcommons/src/common.am index 6412ed3a46..13de4f63ec 100644 --- a/sipXcommons/src/common.am +++ b/sipXcommons/src/common.am @@ -51,4 +51,7 @@ commons_PKGS = \ joda-time \ hazelcast-all \ reflections \ - guava + guava \ + jackson-core-asl \ + jackson-mapper-asl \ + jackson-annotations diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/confdb/ConfReadConverter.java b/sipXcommons/src/main/java/org/sipfoundry/commons/confdb/ConfReadConverter.java index 46bd0b2551..66ad5d484e 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/confdb/ConfReadConverter.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/confdb/ConfReadConverter.java @@ -19,15 +19,11 @@ import static org.sipfoundry.commons.mongo.MongoConstants.CONF_AUTORECORD; import static org.sipfoundry.commons.mongo.MongoConstants.CONF_DESCRIPTION; import static org.sipfoundry.commons.mongo.MongoConstants.CONF_EXT; -import static org.sipfoundry.commons.mongo.MongoConstants.CONF_MEMBERS_ONLY; -import static org.sipfoundry.commons.mongo.MongoConstants.CONF_MODERATED; import static org.sipfoundry.commons.mongo.MongoConstants.CONF_NAME; import static org.sipfoundry.commons.mongo.MongoConstants.CONF_OWNER; import static org.sipfoundry.commons.mongo.MongoConstants.CONF_PIN; -import static org.sipfoundry.commons.mongo.MongoConstants.CONF_PUBLIC; import static org.sipfoundry.commons.mongo.MongoConstants.CONF_URI; -import org.apache.commons.lang.StringUtils; import org.sipfoundry.commons.mongo.MongoConstants; import org.springframework.core.convert.converter.Converter; @@ -41,9 +37,6 @@ public Conference convert(DBObject source) { conf.setConfName((String) source.get(CONF_NAME)); conf.setConfDescription((String) source.get(CONF_DESCRIPTION)); conf.setConfOwner((String) source.get(CONF_OWNER)); - conf.setModerated(Boolean.valueOf(!StringUtils.equals((String)source.get(CONF_MODERATED), "0"))); - conf.setPublic(Boolean.valueOf(!StringUtils.equals((String)source.get(CONF_PUBLIC), "0"))); - conf.setMembersOnly(Boolean.valueOf(!StringUtils.equals((String)source.get(CONF_MEMBERS_ONLY), "0"))); conf.setExtension((String) source.get(CONF_EXT)); conf.setPin((String) source.get(CONF_PIN)); conf.setAutoRecord((Boolean)source.get(CONF_AUTORECORD)); diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/BridgeCommand.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/BridgeCommand.java index 4cbacff1b2..6a4fcd53ca 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/BridgeCommand.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/BridgeCommand.java @@ -24,7 +24,8 @@ public BridgeCommand(FreeSwitchEventSocketInterface fses, String uuid, String si super(fses); // Send a REFER m_uuid = uuid; - m_command = "bridge\nexecute-app-arg: {hangup_after_bridge=true}sofia/"; + m_command = "bridge\nexecute-app-arg: {sip_from_uri=${sip_from_uri},origination_caller_id_number=" + + fses.getVariable("Channel-Caller-ID-Number") + ",hangup_after_bridge=true}sofia/"; m_command += sipContext; m_command += "/"; // sipURI MUST have sip: in there (Can be display URI) @@ -37,6 +38,7 @@ public BridgeCommand(FreeSwitchEventSocketInterface fses, String uuid, String si m_command += "event-lock: true"; } + public boolean start() { if(m_uuid == null) { return super.start(); diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Broadcast.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Broadcast.java new file mode 100644 index 0000000000..bb8784bd9d --- /dev/null +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Broadcast.java @@ -0,0 +1,34 @@ +/** + * + * + * Copyright (c) 2015 eZuce Corp. All rights reserved. + * Contributed to sipXcom under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.commons.freeswitch; + +public class Broadcast extends CallCommand { + + private String m_uuid; + + public Broadcast(FreeSwitchEventSocketInterface fses, String uuid, String params, boolean isSay) { + super(fses); + m_uuid = uuid; + m_sendAsApi = true; + if (!isSay) { + m_command = "uuid_broadcast " + m_uuid + " " + params + " aleg"; + } else { + m_command = "uuid_broadcast " + m_uuid + " say::en\\stelephone_number\\siterated\\s" + params + " aleg"; + } + } + +} diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/CallCommand.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/CallCommand.java index 5149225b44..e001e0bcf3 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/CallCommand.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/CallCommand.java @@ -32,6 +32,19 @@ public boolean start() { return false; } + /** + * This method is identical to "start()" method. + * The only difference is that it will also consume the freeswitch response. + */ + public FreeSwitchEvent startResponse() { + m_finished = false; + // Send the command to the socket + if (!m_sendAsApi) + return m_fses.cmdResponse("sendmsg " + m_fses.getSessionUUID() + "\ncall-command: execute\nexecute-app-name: " + m_command); + else + return m_fses.apiCmdResponse(m_command); + } + public void go() { m_fses.invoke(this); } diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/ChannelExists.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/ChannelExists.java new file mode 100644 index 0000000000..8ab0812364 --- /dev/null +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/ChannelExists.java @@ -0,0 +1,32 @@ +/** + * + * + * Copyright (c) 2015 eZuce Corp. All rights reserved. + * Contributed to sipXcom under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.commons.freeswitch; + +public class ChannelExists extends CallCommand { + + public ChannelExists(FreeSwitchEventSocketInterface fses, String uuid) { + super(fses); + m_command = "uuid_exists " + uuid; + } + + public boolean isUUIDActive() { + m_finished = false; + FreeSwitchEvent event = m_fses.apiCmdResponse(m_command); + return event.getContent().contains("true"); + } + +} \ No newline at end of file diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/ConfBasicThread.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/ConfBasicThread.java index cbf963f880..2e90674f03 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/ConfBasicThread.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/ConfBasicThread.java @@ -21,8 +21,8 @@ public class ConfBasicThread extends Thread { // Default freeswitch socket client strings, should be read from: // /usr/local/freeswitch/conf/autoload_configs/event_socket.conf.xml - static String fsListenPort = "8021"; // parameter "listen-port" - static String fsPassword = "ClueCon"; // parameter "password" + public static String fsListenPort = "8021"; // parameter "listen-port" + public static String fsPassword = "ClueCon"; // parameter "password" // a thread that listens on a freeswitch socket for conference related events // based on events received, maintains relavant data about conferences and diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/FreeSwitchEventSocketEmulator.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/FreeSwitchEventSocketEmulator.java index 992410c513..31f5ac8791 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/FreeSwitchEventSocketEmulator.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/FreeSwitchEventSocketEmulator.java @@ -54,4 +54,9 @@ public boolean connect(Socket socket, String authPassword) throws IOException { return true; } + @Override + public FreeSwitchEvent apiCmdResponse(String cmd) { + return null; + } + } diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/FreeSwitchEventSocketInterface.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/FreeSwitchEventSocketInterface.java index e6da5b3713..c28a108c55 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/FreeSwitchEventSocketInterface.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/FreeSwitchEventSocketInterface.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.net.Socket; import java.util.HashMap; +import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import java.util.concurrent.LinkedBlockingQueue; @@ -47,6 +48,8 @@ public FreeSwitchEventSocketInterface(FreeSwitchConfigurationInterface config) { public abstract FreeSwitchEvent cmdResponse(String cmd); + public abstract FreeSwitchEvent apiCmdResponse(String cmd); + public abstract FreeSwitchEvent awaitLiveEvent(); public abstract void close() throws IOException; @@ -242,6 +245,10 @@ public String getFromUri() { return getVariable("variable_sip_from_uri"); } + public String getToUri() { + return getVariable("variable_sip_to_uri"); + } + public String getFromUser() { return getVariable("variable_sip_from_user"); } @@ -268,4 +275,25 @@ public String redact(String digits) { return digits ; } } + + public Hashtable extractCallParameters() { + String sipReqParams = this.getVariable("variable_sip_req_params"); + // Create a table of parameters to pass in + Hashtable parameters = new Hashtable(); + + if (sipReqParams != null) { + // Split parameter fields (separated by semicolons) + String[] params = sipReqParams.split(";"); + for (String param : params) { + // Split key value pairs (separated by optional equal sign) + String[] kvs = param.split("=", 2); + if (kvs.length == 2) { + parameters.put(kvs[0], kvs[1]); + } else { + parameters.put(kvs[0], ""); + } + } + } + return parameters; + } } diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Hangup.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Hangup.java index 12f283d427..14544162b0 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Hangup.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Hangup.java @@ -14,4 +14,10 @@ public Hangup(FreeSwitchEventSocketInterface fses) { super(fses); m_command = "hangup"; } + + public Hangup(FreeSwitchEventSocketInterface fses, String uuid) { + super(fses); + m_command = "uuid_kill " + uuid; + m_sendAsApi = true; + } } diff --git a/sipXconfig/web/unitelite/scripts/directives/csearch.js b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/OriginateCommand.java similarity index 54% rename from sipXconfig/web/unitelite/scripts/directives/csearch.js rename to sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/OriginateCommand.java index d4f9955b12..ec4bea7da8 100644 --- a/sipXconfig/web/unitelite/scripts/directives/csearch.js +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/OriginateCommand.java @@ -1,5 +1,7 @@ -/* - * Copyright (c) eZuce, Inc. All rights reserved. +/** + * + * + * Copyright (c) 2015 sipXcom, Inc. All rights reserved. * Contributed to SIPfoundry under a Contributor Agreement * * This software is free software; you can redistribute it and/or modify it under @@ -12,25 +14,18 @@ * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. */ +package org.sipfoundry.commons.freeswitch; -(function() { - - 'use strict'; +public class OriginateCommand extends CallCommand { - uw.filter('csearch', [ - function () { - - /* - searches for either name or phone number - */ - return function (searchArr, keyword) { - return _.filter(searchArr, function (el) { - return el.name.toLowerCase().indexOf(keyword) > -1 || - (el.number && el.number.toString().indexOf(keyword) > -1); - }) + public OriginateCommand(FreeSwitchEventSocketInterface fses, String calledURI) { + super(fses); + m_command = "originate " + calledURI + ";sipx-noroute=VoiceMail;sipx-userforward=false &park"; + } - } + public FreeSwitchEvent originate() { + m_finished = false; + return m_fses.apiCmdResponse(m_command); } - ]) -})(); +} diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Set.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Set.java index dc567842d5..c16d5428b3 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Set.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Set.java @@ -42,6 +42,6 @@ public Set(FreeSwitchEventSocketInterface fses, String variable, String value) { public Set(FreeSwitchEventSocketInterface fses, String chan_uuid, String variable, String value) { super(fses); m_uuid = chan_uuid; - m_command = "event\nexecute-app-arg: " + variable + "=" + value; - } + m_command = "set\nexecute-app-arg: " + variable + "=" + value; + } } diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Transfer.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Transfer.java index 1c6d4448c4..91ae1d28f3 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Transfer.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/Transfer.java @@ -8,23 +8,33 @@ */ package org.sipfoundry.commons.freeswitch; +import org.sipfoundry.commons.userdb.ValidUsers; + public class Transfer extends CallCommand { private String m_uuid; - public Transfer(FreeSwitchEventSocketInterface fses, String sipURI) { + public Transfer(FreeSwitchEventSocketInterface fses, String sipURI, boolean bridge) { super(fses); - // Send a REFER - createCommand(sipURI); + if (bridge) { + createBridgeCommand(sipURI); + } else { + // Send a REFER + createDeflectCommand(sipURI); + } } - public Transfer(FreeSwitchEventSocketInterface fses, String uuid, String sipURI) { + public Transfer(FreeSwitchEventSocketInterface fses, String uuid, String sipURI, boolean bridge) { super(fses); m_uuid = uuid; - createCommand(sipURI); + if (bridge) { + createBridgeCommand(sipURI); + } else { + createDeflectCommand(sipURI); + } } - private void createCommand(String sipURI) { + private void createDeflectCommand(String sipURI) { m_command = "deflect\nexecute-app-arg: "; // sipURI MUST have sip: in there (Can be display URI) if (sipURI.toLowerCase().contains("sip:")) { @@ -34,6 +44,10 @@ private void createCommand(String sipURI) { } } + private void createBridgeCommand(String sipURI) { + m_command = "transfer\nexecute-app-arg: transferBridged"+ ValidUsers.getUserPart(sipURI) + " XML default"; + } + @Override public void go() { if(m_uuid == null) { diff --git a/sipXivr/src/org/sipfoundry/sipxivr/eslrequest/AbstractEslRequestController.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/AbstractEslRequestController.java similarity index 95% rename from sipXivr/src/org/sipfoundry/sipxivr/eslrequest/AbstractEslRequestController.java rename to sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/AbstractEslRequestController.java index e2d3c1f6a5..152f32347b 100644 --- a/sipXivr/src/org/sipfoundry/sipxivr/eslrequest/AbstractEslRequestController.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/AbstractEslRequestController.java @@ -14,7 +14,7 @@ * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. */ -package org.sipfoundry.sipxivr.eslrequest; +package org.sipfoundry.commons.freeswitch.eslrequest; import java.util.Hashtable; import java.util.Locale; @@ -38,6 +38,7 @@ public abstract class AbstractEslRequestController implements EslRequestControll private String m_uuid; private String m_localeString; private String m_sipxChangeDomainName; + private boolean m_bridgedTransfer; public abstract void extractParameters(Hashtable parameters); @@ -102,9 +103,9 @@ public void transfer(String dest, boolean playGreeting, boolean disconnect) { } Transfer xfer; if (m_uuid != null) { - xfer = new Transfer(m_fses, m_uuid, dest); + xfer = new Transfer(m_fses, m_uuid, dest, m_bridgedTransfer); } else { - xfer = new Transfer(m_fses, dest); + xfer = new Transfer(m_fses, dest, m_bridgedTransfer); } xfer.go(); if (disconnect) { @@ -288,7 +289,11 @@ public String getSipxchangeDomainName() { } public String getFreeswitchIpAndPort() { - return getVariable("FreeSWITCH-IPv4") + ":15060"; + return getVariable("variable_sip_local_network_addr") + ":15060"; + } + + public void setBridgedTransfer(boolean bridge) { + m_bridgedTransfer = bridge; } } diff --git a/sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestApp.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestApp.java similarity index 93% rename from sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestApp.java rename to sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestApp.java index 89871e67a4..e54ebe6608 100644 --- a/sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestApp.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestApp.java @@ -14,7 +14,7 @@ * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. */ -package org.sipfoundry.sipxivr.eslrequest; +package org.sipfoundry.commons.freeswitch.eslrequest; import java.util.Hashtable; diff --git a/sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestController.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestController.java similarity index 93% rename from sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestController.java rename to sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestController.java index 1cea4e7b05..70c70fa6ea 100644 --- a/sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestController.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestController.java @@ -14,7 +14,7 @@ * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. */ -package org.sipfoundry.sipxivr.eslrequest; +package org.sipfoundry.commons.freeswitch.eslrequest; import java.util.Hashtable; diff --git a/sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestScope.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestScope.java similarity index 97% rename from sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestScope.java rename to sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestScope.java index f4c85e264d..a6fc07fdc0 100644 --- a/sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestScope.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestScope.java @@ -14,7 +14,7 @@ * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. */ -package org.sipfoundry.sipxivr.eslrequest; +package org.sipfoundry.commons.freeswitch.eslrequest; import java.util.Map; diff --git a/sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestScopeAttributes.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestScopeAttributes.java similarity index 97% rename from sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestScopeAttributes.java rename to sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestScopeAttributes.java index b49040ab17..6fa37551b2 100644 --- a/sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestScopeAttributes.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestScopeAttributes.java @@ -14,7 +14,7 @@ * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. */ -package org.sipfoundry.sipxivr.eslrequest; +package org.sipfoundry.commons.freeswitch.eslrequest; import java.util.HashMap; import java.util.LinkedHashMap; diff --git a/sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestScopeContextHolder.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestScopeContextHolder.java similarity index 96% rename from sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestScopeContextHolder.java rename to sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestScopeContextHolder.java index fa3e1da8ae..c853e60891 100644 --- a/sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestScopeContextHolder.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestScopeContextHolder.java @@ -14,7 +14,7 @@ * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. */ -package org.sipfoundry.sipxivr.eslrequest; +package org.sipfoundry.commons.freeswitch.eslrequest; public class EslRequestScopeContextHolder { diff --git a/sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestScopeRunnable.java b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestScopeRunnable.java similarity index 94% rename from sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestScopeRunnable.java rename to sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestScopeRunnable.java index 1b6901ba27..d8162b6b74 100644 --- a/sipXivr/src/org/sipfoundry/sipxivr/eslrequest/EslRequestScopeRunnable.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/freeswitch/eslrequest/EslRequestScopeRunnable.java @@ -14,7 +14,7 @@ * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. */ -package org.sipfoundry.sipxivr.eslrequest; +package org.sipfoundry.commons.freeswitch.eslrequest; public abstract class EslRequestScopeRunnable implements Runnable { diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/mongo/MongoConstants.java b/sipXcommons/src/main/java/org/sipfoundry/commons/mongo/MongoConstants.java index 3769f02b64..19df2d6150 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/mongo/MongoConstants.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/mongo/MongoConstants.java @@ -31,18 +31,13 @@ public interface MongoConstants { static final String USERBUSYPROMPT = "bsyprmpt"; static final String MOH = "moh"; static final String VOICEMAILTUI = "vcmltui"; + static final String FORWARD_DELETE_VOICEMAIL = "fwddelvm"; static final String EMAIL = "email"; static final String NOTIFICATION = "notif"; static final String ATTACH_AUDIO = "attaudio"; static final String ALT_EMAIL = "altemail"; static final String ALT_NOTIFICATION = "altnotif"; static final String ALT_ATTACH_AUDIO = "altattaudio"; - static final String SYNC = "synch"; - static final String HOST = "host"; - static final String PORT = "port"; - static final String TLS = "tls"; - static final String ACCOUNT = "acnt"; - static final String PASSWD = "pswd"; static final String DISPLAY_NAME = "dspl"; static final String HASHED_PASSTOKEN = "hshpstk"; static final String IM_ENABLED = "imenbld"; @@ -60,6 +55,7 @@ public interface MongoConstants { static final String FAX_NUMBER = "fax"; static final String LOCATION = "loctn"; // this is different than loc field in user // location db + static final String LOCATIONS = "locations"; static final String HOME_PHONE_NUMBER = "hmph"; static final String CELL_PHONE_NUMBER = "cell"; static final String AVATAR = "avt"; @@ -103,9 +99,6 @@ public interface MongoConstants { static final String CONF_DESCRIPTION = "cnfdescr"; static final String CONF_OWNER = "cnfown"; static final String CONF_PIN = "cnfpin"; - static final String CONF_MODERATED = "cnfmod"; - static final String CONF_PUBLIC = "cnfpbl"; - static final String CONF_MEMBERS_ONLY = "cnfmonly"; static final String CONF_URI = "cnfuri"; static final String PERMISSIONS = "prm"; static final String CFWDTIME = "cfwdtm"; @@ -116,6 +109,7 @@ public interface MongoConstants { static final String TO_URI = "to"; static final String CALLID = "cid"; static final String VMONDND = "vmondnd"; + static final String CALLBACK_LIST = "cbu"; // speed dials static final String SPEEDDIAL = "spdl"; static final String USER = "usr"; @@ -139,6 +133,9 @@ public interface MongoConstants { static final String ITEM = "itm"; static final String DISTRIB_LISTS = "dlst"; static final String FORCE_PIN_CHANGE= "fpchg"; + static final String AUTO_ENTER_PIN_EXTENSION= "aepe"; + static final String AUTO_ENTER_PIN_EXTERNAL= "aepen"; + static final String DAYS_TO_KEEP_VM= "dtkvm"; // AUTH CODE static final String AUTH_CODE = "authc"; // GROUP @@ -174,4 +171,15 @@ public interface MongoConstants { //phones static final String SERIAL_NUMBER = "mac"; + static final String LINES = "phLines"; + static final String PHONE_MODEL = "model"; + + //location + static final String LOCATION_NAME="loc"; + static final String LOCATION_RESTRICTIONS_DOMAINS="loc_restr_dom"; + static final String LOCATION_RESTRICTIONS_SUBNETS="loc_restr_sbnet"; + static final String LOCATION_ASSOCIATIONS="loc_assoc"; + static final String LOCATION_ASSOCIATIONS_INBOUND="loc_assoc_inbound"; + static final String LOCATION_ASSOCIATIONS_FALLBACK="loc_assoc_fallback"; + } diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/mongo/MongoFactory.java b/sipXcommons/src/main/java/org/sipfoundry/commons/mongo/MongoFactory.java index e143e64ad0..4b5a8dd0ec 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/mongo/MongoFactory.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/mongo/MongoFactory.java @@ -26,7 +26,11 @@ public class MongoFactory { public static final Mongo fromConnectionFile() throws UnknownHostException { if (connectionURL == null) { synchronized (FILE_LOCK) { - String configurationPath = System.getProperty("conf.dir", "/etc/sipxpbx"); + String configurationPathDef = "/etc/sipxpbx"; + if (MongoUtil.isFedora()) { + configurationPathDef = "/usr/local/sipx/" + configurationPathDef; + } + String configurationPath = System.getProperty("conf.dir", configurationPathDef); @SuppressWarnings("resource") InputStream is = null; String config = null; @@ -53,6 +57,8 @@ public static final Mongo fromConnectionFile() throws UnknownHostException { return fromConnectionString(connectionURL); } + + public static final Mongo fromConnectionString(String connectionUrl) throws UnknownHostException { MongoURI uri = new MongoURI(connectionUrl); Mongo m = uri.connect(); diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/mongo/MongoUtil.java b/sipXcommons/src/main/java/org/sipfoundry/commons/mongo/MongoUtil.java index 8521fd13ca..a600730c97 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/mongo/MongoUtil.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/mongo/MongoUtil.java @@ -87,4 +87,13 @@ public static BasicBSONObject getObject(BasicBSONObject o, String... keys) { } return s; } + + public static boolean isFedora() { + if (System.getProperty("os.name").equals("Linux") + && System.getProperty("os.version").contains("fc")) { + return true; + } else { + return false; + } + } } diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/restconfig/RestServerConfig.java b/sipXcommons/src/main/java/org/sipfoundry/commons/restconfig/RestServerConfig.java index 1f87da47af..ec524bbc99 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/restconfig/RestServerConfig.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/restconfig/RestServerConfig.java @@ -19,6 +19,7 @@ public class RestServerConfig { private String sipxProxyDomain; private String sipxcdrAddress; private String dbUser; + private String dbPassword; public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; @@ -92,6 +93,14 @@ public void setDbUser(String dbUser) { this.dbUser = dbUser; } + public String getDbPassword() { + return dbPassword; + } + + public void setDbPassword(String dbPassword) { + this.dbPassword = dbPassword; + } + public int getCacheTimeout() { return 30; } diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/restconfig/RestServerConfigFileParser.java b/sipXcommons/src/main/java/org/sipfoundry/commons/restconfig/RestServerConfigFileParser.java index 3b43a9e5a0..24029ccfb5 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/restconfig/RestServerConfigFileParser.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/restconfig/RestServerConfigFileParser.java @@ -31,6 +31,7 @@ private static void addRules(Digester digester) { digester.addCallMethod( String.format("%s/%s", REST_CONFIG,"log-level"), "setLogLevel",0); digester.addCallMethod( String.format("%s/%s", REST_CONFIG,"sipxcdr-address"), "setSipxcdrAddress", 0); digester.addCallMethod( String.format("%s/%s", REST_CONFIG,"db-user"), "setDbUser", 0); + digester.addCallMethod( String.format("%s/%s", REST_CONFIG,"db-password"), "setDbPassword", 0); } public RestServerConfig parse(String url) { diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/ImapInfo.java b/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/ImapInfo.java deleted file mode 100644 index 4b1115567c..0000000000 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/ImapInfo.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * - * - * Copyright (C) 2009 Pingtel Corp., certain elements licensed under a Contributor Agreement. - * Contributors retain copyright to elements licensed under a Contributor Agreement. - * Licensed to the User under the LGPL license. - * - */ -package org.sipfoundry.commons.userdb; - -import org.apache.commons.codec.binary.Base64; - -public class ImapInfo { - private String m_host; - private String m_port; - private boolean useTLS; - private String m_account; - private String m_password; - private boolean m_synchronize; - - public String getHost() { - return m_host; - } - - public void setHost(String host) { - m_host = host; - } - - public String getPort() { - return m_port; - } - - public void setPort(String port) { - m_port = port; - } - - public boolean isUseTLS() { - return useTLS; - } - - public void setUseTLS(boolean useTLS) { - this.useTLS = useTLS; - } - - public String getAccount() { - return m_account; - } - - public void setAccount(String account) { - m_account = account; - } - - public String getPassword() { - return m_password; - } - - public String getDecodedPassword() { - byte[] decoded = Base64.decodeBase64(m_password.getBytes()); - String decodedStr = new String(decoded); - - return decodedStr; - } - - public void setPassword(String password) { - m_password = password; - } - - public boolean isSynchronize() { - return m_synchronize; - } - - public void setSynchronize(boolean synchronize) { - m_synchronize = synchronize; - } -} diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/User.java b/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/User.java index 9ecdebc3ba..64c4a0c496 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/User.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/User.java @@ -35,11 +35,11 @@ public class User { private Vector m_aliases; private HashMap m_distributionLists; private Locale m_locale; // The locale for the UI to present to this user + private boolean m_forwardDeleteVoicemail; private String m_emailAddress; private String m_altEmailAddress; private boolean m_attachAudioToEmail; private boolean m_altAttachAudioToEmail; - private ImapInfo m_imapInfo; private String m_cellNumber; private String m_homeNumber; private boolean m_imEnabled; @@ -84,10 +84,14 @@ public class User { private boolean m_admin; private boolean m_hotelingEnabled; private boolean m_forcePinChange; + private boolean m_autoEnterPinExtension; + private boolean m_autoEnterPinExternal; + private int m_daysToKeepVM; private String m_vmLanguage; + private Vector m_callbackUsers; public enum EmailFormats { - FORMAT_NONE("NONE"), FORMAT_FULL("FULL"), FORMAT_MEDIUM("MEDIUM"), FORMAT_BRIEF("BRIEF"), FORMAT_IMAP("IMAP"); + FORMAT_NONE("NONE"), FORMAT_FULL("FULL"), FORMAT_MEDIUM("MEDIUM"), FORMAT_BRIEF("BRIEF"); private final String m_id; EmailFormats(String id) { @@ -258,6 +262,14 @@ public void setLocale(Locale locale) { m_locale = locale; } + public boolean isForwardDeleteVoicemail() { + return m_forwardDeleteVoicemail; + } + + public void setForwardDeleteVoicemail(String value) { + m_forwardDeleteVoicemail = value.equals("1") || value.equals("true"); + } + public String getEmailAddress() { return m_emailAddress; } @@ -314,14 +326,6 @@ public void setAltEmailFormat(String emailFormat) { m_altEmailFormat = EmailFormats.valueOfById(emailFormat); } - public ImapInfo getImapInfo() { - return m_imapInfo; - } - - public void setImapInfo(ImapInfo imapInfo) { - m_imapInfo = imapInfo; - } - public void setCellNum(String cellNum) { m_cellNumber = cellNum; } @@ -719,6 +723,30 @@ public void setForcePinChange(String value) { m_forcePinChange = value.equals("1") || value.equals("true"); } + public boolean isAutoEnterPinExtension() { + return m_autoEnterPinExtension; + } + + public void setAutoEnterPinExtension(String value) { + m_autoEnterPinExtension = value.equals("1") || value.equals("true"); + } + + public boolean isAutoEnterPinExternal() { + return m_autoEnterPinExternal; + } + + public void setAutoEnterPinExternal(String value) { + m_autoEnterPinExternal = value.equals("1") || value.equals("true"); + } + + public int getDaysToKeepVM() { + return m_daysToKeepVM; + } + + public void setDaysToKeepVM(int value) { + m_daysToKeepVM = value; + } + public String getSipPassword() { return m_sipPassword; } @@ -734,4 +762,20 @@ public String getVmLanguage() { public void setVmLanguage(String vmLanguage) { m_vmLanguage = vmLanguage; } + + public Vector getCallbackUsers() { + return m_callbackUsers; + } + + public void addCallbackUser(String callbackUser) { + this.m_callbackUsers.add(callbackUser); + } + + public void setCallbackUsers(Vector callbackUsers) { + this.m_callbackUsers = callbackUsers; + } + + public boolean isMarkedForCallback() { + return !m_callbackUsers.isEmpty(); + } } diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/ValidUsers.java b/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/ValidUsers.java index c684c9dd35..2e10584bc3 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/ValidUsers.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/ValidUsers.java @@ -16,7 +16,6 @@ */ package org.sipfoundry.commons.userdb; -import static org.sipfoundry.commons.mongo.MongoConstants.ACCOUNT; import static org.sipfoundry.commons.mongo.MongoConstants.ACTIVEGREETING; import static org.sipfoundry.commons.mongo.MongoConstants.ALIAS; import static org.sipfoundry.commons.mongo.MongoConstants.ALIASES; @@ -26,6 +25,8 @@ import static org.sipfoundry.commons.mongo.MongoConstants.ALT_IM_ID; import static org.sipfoundry.commons.mongo.MongoConstants.ALT_NOTIFICATION; import static org.sipfoundry.commons.mongo.MongoConstants.ATTACH_AUDIO; +import static org.sipfoundry.commons.mongo.MongoConstants.AUTO_ENTER_PIN_EXTENSION; +import static org.sipfoundry.commons.mongo.MongoConstants.AUTO_ENTER_PIN_EXTERNAL; import static org.sipfoundry.commons.mongo.MongoConstants.AVATAR; import static org.sipfoundry.commons.mongo.MongoConstants.BUTTONS; import static org.sipfoundry.commons.mongo.MongoConstants.CALL_FROM_ANY_IM; @@ -39,6 +40,7 @@ import static org.sipfoundry.commons.mongo.MongoConstants.CONF_OWNER; import static org.sipfoundry.commons.mongo.MongoConstants.CONF_PIN; import static org.sipfoundry.commons.mongo.MongoConstants.CONTACT; +import static org.sipfoundry.commons.mongo.MongoConstants.DAYS_TO_KEEP_VM; import static org.sipfoundry.commons.mongo.MongoConstants.DESCR; import static org.sipfoundry.commons.mongo.MongoConstants.DIALPAD; import static org.sipfoundry.commons.mongo.MongoConstants.DISPLAY_NAME; @@ -47,6 +49,7 @@ import static org.sipfoundry.commons.mongo.MongoConstants.ENTITY_NAME; import static org.sipfoundry.commons.mongo.MongoConstants.FAX_NUMBER; import static org.sipfoundry.commons.mongo.MongoConstants.FORCE_PIN_CHANGE; +import static org.sipfoundry.commons.mongo.MongoConstants.FORWARD_DELETE_VOICEMAIL; import static org.sipfoundry.commons.mongo.MongoConstants.GROUPS; import static org.sipfoundry.commons.mongo.MongoConstants.HASHED_PASSTOKEN; import static org.sipfoundry.commons.mongo.MongoConstants.HOME_CITY; @@ -55,7 +58,6 @@ import static org.sipfoundry.commons.mongo.MongoConstants.HOME_STATE; import static org.sipfoundry.commons.mongo.MongoConstants.HOME_STREET; import static org.sipfoundry.commons.mongo.MongoConstants.HOME_ZIP; -import static org.sipfoundry.commons.mongo.MongoConstants.HOST; import static org.sipfoundry.commons.mongo.MongoConstants.HOTELING; import static org.sipfoundry.commons.mongo.MongoConstants.ID; import static org.sipfoundry.commons.mongo.MongoConstants.IDENTITY; @@ -82,25 +84,21 @@ import static org.sipfoundry.commons.mongo.MongoConstants.OFFICE_ZIP; import static org.sipfoundry.commons.mongo.MongoConstants.OPERATOR; import static org.sipfoundry.commons.mongo.MongoConstants.PASSTOKEN; -import static org.sipfoundry.commons.mongo.MongoConstants.PASSWD; import static org.sipfoundry.commons.mongo.MongoConstants.PERMISSIONS; import static org.sipfoundry.commons.mongo.MongoConstants.PERSONAL_ATT; import static org.sipfoundry.commons.mongo.MongoConstants.PINTOKEN; import static org.sipfoundry.commons.mongo.MongoConstants.PLAY_DEFAULT_VM; -import static org.sipfoundry.commons.mongo.MongoConstants.PORT; import static org.sipfoundry.commons.mongo.MongoConstants.RELATION; import static org.sipfoundry.commons.mongo.MongoConstants.SPEEDDIAL; -import static org.sipfoundry.commons.mongo.MongoConstants.SYNC; import static org.sipfoundry.commons.mongo.MongoConstants.TIMEZONE; -import static org.sipfoundry.commons.mongo.MongoConstants.TLS; import static org.sipfoundry.commons.mongo.MongoConstants.UID; +import static org.sipfoundry.commons.mongo.MongoConstants.UNIFIED_MESSAGING_LANGUAGE; import static org.sipfoundry.commons.mongo.MongoConstants.USERBUSYPROMPT; import static org.sipfoundry.commons.mongo.MongoConstants.USER_LOCATION; import static org.sipfoundry.commons.mongo.MongoConstants.VALID_USER; import static org.sipfoundry.commons.mongo.MongoConstants.VOICEMAILTUI; import static org.sipfoundry.commons.mongo.MongoConstants.VOICEMAIL_ENABLED; import static org.sipfoundry.commons.mongo.MongoConstants.VOICEMAIL_PINTOKEN; -import static org.sipfoundry.commons.mongo.MongoConstants.UNIFIED_MESSAGING_LANGUAGE; import java.util.ArrayList; import java.util.Collection; @@ -117,7 +115,6 @@ import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import org.sipfoundry.commons.mongo.MongoConstants; -import org.sipfoundry.commons.userdb.User.EmailFormats; import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; @@ -303,6 +300,40 @@ public User getUser(String userName) { return extractValidUser(aliasResult); } + /** + * Retrieve a specific user based on their Cell Phone or Home Phone (as defined on user's contact information) + * which has the Auto Enter Pin from External Number permission set to true.
+ * If the method finds more than 1 user who share the same external number it will return null; + */ + public User getUserWithAutoEnterPinByExternalNumber(String externalNumber, int matchLastDigits) { + if (externalNumber == null) { + return null; + } + QueryBuilder query = QueryBuilder.start(VALID_USER).is(Boolean.TRUE); + externalNumber = getExternalNumberLastDigits(externalNumber, matchLastDigits); + Pattern cellPattern = Pattern.compile(".*" + externalNumber); + BasicDBObject cell = new BasicDBObject(CELL_PHONE_NUMBER, cellPattern); + BasicDBObject home = new BasicDBObject(HOME_PHONE_NUMBER, cellPattern); + query.or(cell, home); + query.and(AUTO_ENTER_PIN_EXTERNAL).is("1"); + DBObject queryUserName = query.get(); + DBCursor result = getEntityCollection().find(queryUserName); + if (result != null && result.size() == 1) { + return extractValidUser(result.one()); + } + return null; + } + + private String getExternalNumberLastDigits(String externalNumber, int matchLastDigits) { + String externalNumberToMatch; + if (matchLastDigits == 0 || externalNumber == null || externalNumber.length() < matchLastDigits) { + externalNumberToMatch = externalNumber; + } else { + externalNumberToMatch = externalNumber.substring(externalNumber.length() - matchLastDigits); + } + return externalNumberToMatch; + } + public User getUserByConferenceName(String conferenceName) { DBObject queryConference = QueryBuilder.start(CONF_NAME).is(conferenceName).get(); DBObject conferenceResult = getEntityCollection().findOne(queryConference); @@ -706,6 +737,11 @@ private static User extractUser(DBObject obj) { } user.setVoicemailTui(getStringValue(obj, VOICEMAILTUI)); + + if (getStringValue(obj, FORWARD_DELETE_VOICEMAIL) != null) { + user.setForwardDeleteVoicemail(getStringValue(obj, FORWARD_DELETE_VOICEMAIL)); + } + user.setEmailAddress(getStringValue(obj, EMAIL)); if (obj.keySet().contains(NOTIFICATION)) { user.setEmailFormat(getStringValue(obj, NOTIFICATION)); @@ -718,8 +754,24 @@ private static User extractUser(DBObject obj) { } user.setAltAttachAudioToEmail(Boolean.valueOf(getStringValue(obj, ALT_ATTACH_AUDIO))); - if (getStringValue(obj, FORCE_PIN_CHANGE) != null) { - user.setForcePinChange(getStringValue(obj, FORCE_PIN_CHANGE)); + String forcePinChange = getStringValue(obj, FORCE_PIN_CHANGE); + if (forcePinChange != null) { + user.setForcePinChange(forcePinChange); + } + + String autoEnterPinExtension = getStringValue(obj, AUTO_ENTER_PIN_EXTENSION); + if (autoEnterPinExtension != null) { + user.setAutoEnterPinExtension(autoEnterPinExtension); + } + + String autoEnterPinExternal = getStringValue(obj, AUTO_ENTER_PIN_EXTERNAL); + if (autoEnterPinExternal != null) { + user.setAutoEnterPinExternal(autoEnterPinExternal); + } + + Integer daysToKeepVM = getIntegerValue(obj, DAYS_TO_KEEP_VM); + if (daysToKeepVM != null) { + user.setDaysToKeepVM(daysToKeepVM); } BasicDBList aliasesObj = (BasicDBList) obj.get(ALIASES); @@ -734,27 +786,6 @@ private static User extractUser(DBObject obj) { user.setAliases(aliases); } - if (obj.keySet().contains(SYNC)) { - ImapInfo imapInfo = new ImapInfo(); - imapInfo.setSynchronize(Boolean.valueOf(getStringValue(obj, SYNC))); - imapInfo.setHost(getStringValue(obj, HOST)); - imapInfo.setPort(getStringValue(obj, PORT)); - imapInfo.setUseTLS(Boolean.valueOf(getStringValue(obj, TLS))); - imapInfo.setAccount(getStringValue(obj, ACCOUNT)); - imapInfo.setPassword(getStringValue(obj, PASSWD)); - user.setImapInfo(imapInfo); - if (imapInfo.isSynchronize()) { - user.setEmailFormat(EmailFormats.FORMAT_IMAP); - user.setAttachAudioToEmail(true); - } - // // If account isn't set, use the e-mail username - if (imapInfo.getAccount() == null || imapInfo.getAccount().length() == 0) { - if (user.getEmailAddress() != null) { - imapInfo.setAccount(user.getEmailAddress().split("@")[0]); - } - } - } - // contact info related data user.setCellNum(getStringValue(obj, CELL_PHONE_NUMBER)); user.setHomeNum(getStringValue(obj, HOME_PHONE_NUMBER)); @@ -844,7 +875,7 @@ private static User extractUser(DBObject obj) { return user; } - private static String getStringValue(DBObject obj, String key) { + public static String getStringValue(DBObject obj, String key) { if (obj.keySet().contains(key)) { if (obj.get(key) != null) { return obj.get(key).toString(); @@ -853,6 +884,15 @@ private static String getStringValue(DBObject obj, String key) { return null; } + public static Integer getIntegerValue(DBObject obj, String key) { + if (obj.keySet().contains(key)) { + if (obj.get(key) != null) { + return Integer.parseInt(obj.get(key).toString()); + } + } + return null; + } + /** * Remove all non-letter characters, convert to upper case Remove diacritical marks if * possible @@ -1015,6 +1055,15 @@ protected static void buildDialPatterns(User u) { } } + public boolean isValidIdentity(String uri) { + if (uri == null) { + return false; + } + DBObject queryIdent = QueryBuilder.start(IDENTITY).is(uri).get(); + long result = getEntityCollection().count(queryIdent); + return result > 0; + } + public DB getImdb() { return m_imdb; } diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/Address.java b/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/Address.java index 25f1b85c19..67ad0c4598 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/Address.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/Address.java @@ -16,6 +16,9 @@ */ package org.sipfoundry.commons.userdb.profile; +import org.codehaus.jackson.annotate.JsonPropertyOrder; + +@JsonPropertyOrder({"country", "street", "city", "zip", "officeDesignation", "state"}) public class Address { private String m_street; diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/UserProfile.java b/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/UserProfile.java index a4a6a26243..8517d674c1 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/UserProfile.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/UserProfile.java @@ -94,6 +94,8 @@ public class UserProfile { private boolean m_ldapManaged; private Date m_lastImportedDate; private Date m_disabledDate; + + private Date m_extAvatarSyncDate; @Indexed private String m_custom1; @@ -419,6 +421,14 @@ public Date getDisabledDate() { public void setDisabledDate(Date disabledDate) { m_disabledDate = disabledDate; } + + public Date getExtAvatarSyncDate() { + return m_extAvatarSyncDate; + } + + public void setExtAvatarSyncDate(Date extAvatarSyncDate) { + m_extAvatarSyncDate = extAvatarSyncDate; + } public void update(UserProfile object) { try { diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/UserProfileService.java b/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/UserProfileService.java index 7036368c19..be34c56949 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/UserProfileService.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/UserProfileService.java @@ -28,6 +28,8 @@ public interface UserProfileService { static final String PHANTOM = "PHANTOM"; UserProfile getUserProfile(String userId); + + UserProfile getUserProfileByUsername(String userName); void saveUserProfile(UserProfile profile); @@ -62,6 +64,10 @@ public interface UserProfileService { void saveAvatar(String userName, InputStream is, boolean overwrite) throws AvatarUploadException; void deleteAvatar(String userName); + + void deleteExtAvatar(String userName); + + void deleteIntAvatar(String userName); List getAllUserProfiles(); @@ -74,6 +80,8 @@ public interface UserProfileService { List getUserProfilesToDisable(long age); List getUserProfilesToDelete(long age); + + public List getUserProfilesByExtAvatarUrl(); int getEnabledUsersCount(); @@ -82,4 +90,6 @@ public interface UserProfileService { ObjectId getAvatarId(String userName); String getAvatarDBFileMD5(String userName); + + void saveExtAvatar(String userName, String url) throws AvatarUploadException; } diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/UserProfileServiceImpl.java b/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/UserProfileServiceImpl.java index eefcda5a8a..b5eb56233c 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/UserProfileServiceImpl.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/userdb/profile/UserProfileServiceImpl.java @@ -19,7 +19,10 @@ import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.InputStream; +import java.net.URL; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -29,9 +32,12 @@ import net.coobird.thumbnailator.Thumbnails; import net.coobird.thumbnailator.geometry.Positions; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.bson.types.ObjectId; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; @@ -53,8 +59,10 @@ public class UserProfileServiceImpl implements UserProfileService { private static final String BRANCH_NAME = "m_branchName"; private static final String BRANCH_ADDRESS = "m_branchAddress"; private static final String AVATAR_NAME = "avatar_%s.png"; + private static final String AVATAR_EXT_NAME = "avatar_ext_%s.png"; private static final int LIMIT_DISABLE = 500; private static final int LIMIT_DELETE = 50; + private static final int LIMIT_EXT_AVATAR_SYNC = 500; private static String m_defaultId = ""; private MongoTemplate m_template; @@ -63,6 +71,12 @@ public UserProfile getUserProfile(String userId) { return m_template.findOne(new Query(Criteria.where(USER_ID).is(userId.toString())), UserProfile.class, USER_PROFILE_COLLECTION); } + + @Override + public UserProfile getUserProfileByUsername(String userName) { + return m_template.findOne(new Query(Criteria.where(USERNAME).is(userName)), UserProfile.class, + USER_PROFILE_COLLECTION); + } @Override public void saveUserProfile(UserProfile profile) { @@ -201,7 +215,15 @@ public ObjectId getAvatarId(String userName) { private GridFSDBFile getAvatarDBFile(String userName) { GridFS avatarFS = new GridFS(m_template.getDb()); - GridFSDBFile imageForOutput = avatarFS.findOne(String.format(AVATAR_NAME, userName)); + UserProfile userProfile = getUserProfileByUsername(userName); + GridFSDBFile imageForOutput = null; + if (userProfile != null) { + if (!userProfile.getUseExtAvatar()) { + imageForOutput = avatarFS.findOne(String.format(AVATAR_NAME, userName)); + } else { + imageForOutput =avatarFS.findOne(String.format(AVATAR_EXT_NAME, userName)); + } + } if (imageForOutput != null) { return imageForOutput; } @@ -218,14 +240,31 @@ public String getAvatarDBFileMD5(String userName) { GridFSDBFile avatar = getAvatarDBFile(userName); return avatar != null ? avatar.getMD5() : null; } + + private void deleteAvatar(String userName, String avatarName) { + GridFS avatarFS = new GridFS(m_template.getDb()); + avatarFS.remove(String.format(avatarName, userName)); + } + @Override public void deleteAvatar(String userName) { - GridFS avatarFS = new GridFS(m_template.getDb()); - avatarFS.remove(String.format(AVATAR_NAME, userName)); + deleteIntAvatar(userName); + deleteExtAvatar(userName); } - + @Override - public void saveAvatar(String userName, InputStream originalIs) throws AvatarUploadException { + public void deleteExtAvatar(String userName) { + deleteAvatar(userName, AVATAR_EXT_NAME); + + } + + @Override + public void deleteIntAvatar(String userName) { + deleteAvatar(userName, AVATAR_NAME); + + } + + private void saveAvatar(String userName, InputStream originalIs, String avatarName) throws AvatarUploadException { ByteArrayOutputStream os = null; InputStream is = null; try { @@ -235,7 +274,7 @@ public void saveAvatar(String userName, InputStream originalIs) throws AvatarUpl os = new ByteArrayOutputStream(); ImageIO.write(thumbnail, "png", os); is = new ByteArrayInputStream(os.toByteArray()); - String fileName = String.format(AVATAR_NAME, userName); + String fileName = String.format(avatarName, userName); GridFS avatarFS = new GridFS(m_template.getDb()); avatarFS.remove(fileName); GridFSInputFile gfsFile = avatarFS.createFile(is); @@ -249,6 +288,29 @@ public void saveAvatar(String userName, InputStream originalIs) throws AvatarUpl IOUtils.closeQuietly(os); } } + + @Override + public void saveAvatar(String userName, InputStream originalIs) throws AvatarUploadException { + saveAvatar(userName, originalIs, AVATAR_NAME); + } + + @Override + public void saveExtAvatar(String userName, String url) throws AvatarUploadException { + File temp = null; + FileInputStream tempIs = null; + try { + try { + temp = File.createTempFile(String.format(AVATAR_EXT_NAME, userName), ".tmp"); + FileUtils.copyURLToFile(new URL(url), temp); + tempIs = new FileInputStream(temp); + } catch (Exception ex) { + throw new AvatarUploadException(ex); + } + saveAvatar(userName, tempIs, AVATAR_EXT_NAME); + } finally { + IOUtils.closeQuietly(tempIs); + } + } @Override public void saveAvatar(String userName, InputStream originalIs, boolean overwriteIfExists) @@ -305,6 +367,15 @@ public List getUserProfilesByEnabledProperty(String search, int fir } return getUserProfiles(firstRow, pageSize, property, enabled); } + + @Override + public List getUserProfilesByExtAvatarUrl() { + return m_template.find( + new Query(Criteria.where("m_useExtAvatar").is(true). + and("m_extAvatar").exists(true)). + limit(LIMIT_EXT_AVATAR_SYNC).with(new Sort(Sort.Direction.ASC, "m_extAvatarSyncDate")), + UserProfile.class, USER_PROFILE_COLLECTION); + } private List getUserProfiles(int firstRow, int pageSize, String property, boolean enabled) { return m_template.find(new Query(Criteria.where(property).is(enabled)).skip(firstRow).limit(pageSize), diff --git a/sipXcommons/src/main/java/org/sipfoundry/commons/util/TimeZoneUtils.java b/sipXcommons/src/main/java/org/sipfoundry/commons/util/TimeZoneUtils.java index f532bf81c1..56c8c7f455 100644 --- a/sipXcommons/src/main/java/org/sipfoundry/commons/util/TimeZoneUtils.java +++ b/sipXcommons/src/main/java/org/sipfoundry/commons/util/TimeZoneUtils.java @@ -1,7 +1,10 @@ package org.sipfoundry.commons.util; +import java.util.Calendar; import java.util.Date; +import java.util.TimeZone; +import org.apache.commons.lang.time.DateUtils; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.LocalDateTime; @@ -13,4 +16,65 @@ public static Date convertJodaTimezone(LocalDateTime date, String srcTz, String DateTime dstDateTime = srcDateTime.withZone(DateTimeZone.forID(destTz)); return dstDateTime.toLocalDateTime().toDateTime().toDate(); } + + /** + * Returns the currentDate minus x daysAgo + */ + public static Date getDateXDaysAgo(int daysAgo) { + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DAY_OF_MONTH, -daysAgo); + return calendar.getTime(); + } + + /** + * By default set start at next midnight + */ + public static Date getDefaultEndTime(String timezone) { + Calendar now = Calendar.getInstance(); + if (timezone != null) { + now.setTimeZone(TimeZone.getTimeZone(timezone)); + } + now.add(Calendar.DAY_OF_MONTH, 1); + Calendar end = DateUtils.truncate(now, Calendar.DAY_OF_MONTH); + return end.getTime(); + } + + /** + * start a day before end time + */ + public static Date getDefaultStartTime(Date endTime, String timezone) { + Calendar then = Calendar.getInstance(); + if (timezone != null) { + then.setTimeZone(TimeZone.getTimeZone(timezone)); + } + then.setTime(endTime); + then.add(Calendar.DAY_OF_MONTH, -1); + return then.getTime(); + } + + /** + * Return a date with the same values as the input date but a different time zone + */ + public static Date getSameDateWithNewTimezone(Date initialDate, TimeZone timezone) { + Calendar calTime = Calendar.getInstance(); + calTime.setTime(initialDate); + + Calendar cal = Calendar.getInstance(timezone); + cal.set(Calendar.YEAR, calTime.get(Calendar.YEAR)); + cal.set(Calendar.MONTH, calTime.get(Calendar.MONTH)); + cal.set(Calendar.DAY_OF_MONTH, calTime.get(Calendar.DAY_OF_MONTH)); + cal.set(Calendar.HOUR_OF_DAY, calTime.get(Calendar.HOUR_OF_DAY)); + cal.set(Calendar.MINUTE, calTime.get(Calendar.MINUTE)); + cal.set(Calendar.SECOND, calTime.get(Calendar.SECOND)); + return cal.getTime(); + } + + /** + * Return a date with converted to the new timezone + */ + public static Date convertDateToNewTimezone(Date initialDate, TimeZone timezone) { + return new DateTime(initialDate).withZone(DateTimeZone.forTimeZone(timezone)) + .toLocalDateTime().toDate(); + } + } diff --git a/sipXcommserverLib/configure.ac b/sipXcommserverLib/configure.ac index fa5faa0806..7a369f816f 100644 --- a/sipXcommserverLib/configure.ac +++ b/sipXcommserverLib/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.57) -AC_INIT(sipXcommserverLib, 15.04, sipx-dev@list.sipfoundry.org) +AC_INIT(sipXcommserverLib, 16.12, sipx-dev@list.sipfoundry.org) AC_CONFIG_AUX_DIR(config) m4_include([config/general.m4]) m4_include([config/sipXlib.m4]) diff --git a/sipXcommserverLib/etc/sipxcommserverlib.cf b/sipXcommserverLib/etc/sipxcommserverlib.cf index e829a2db5c..dc7f32b93f 100644 --- a/sipXcommserverLib/etc/sipxcommserverlib.cf +++ b/sipXcommserverLib/etc/sipxcommserverlib.cf @@ -37,28 +37,28 @@ bundle agent sipxcommserverlib_config { "$(sipx.SIPX_CONFDIR)/ssl/$(ssl)" comment => "install SSL SIP info $(this.promiser)", create => "true", - copy_from => copy_from_cfdata("$(ssl)"), + copy_from => copy_from_cfdata("$(sipx.location_id)/$(ssl)"), perms => mog("600","$(sipx.SIPXPBXUSER)","$(sipx.SIPXPBXGROUP)"), classes => if_repaired("ssl_repaired"); "$(sipx.SIPX_CONFDIR)/ssl/ssl.keystore" comment => "install Java SSL info $(this.promiser)", create => "true", - copy_from => copy_from_cfdata("ssl.keystore"), + copy_from => copy_from_cfdata("$(sipx.location_id)/ssl.keystore"), perms => mog("600","$(sipx.SIPXPBXUSER)","$(sipx.SIPXPBXGROUP)"), classes => if_repaired("java_ssl_repaired"); "$(sipx.SIPX_CONFDIR)/ssl/ssl-web.keystore" comment => "install Java SSL info $(this.promiser)", create => "true", - copy_from => copy_from_cfdata("ssl-web.keystore"), + copy_from => copy_from_cfdata("$(sipx.location_id)/ssl-web.keystore"), perms => mog("600","$(sipx.SIPXPBXUSER)","$(sipx.SIPXPBXGROUP)"), classes => if_repaired("java_ssl_web_repaired"); "$(sipx.SIPX_CONFDIR)/ssl/authorities.jks" comment => "install Java WEB SSL $(this.promiser)", create => "true", - copy_from => copy_from_cfdata("authorities.jks"), + copy_from => copy_from_cfdata("$(sipx.location_id)/authorities.jks"), perms => mog("600","$(sipx.SIPXPBXUSER)","$(sipx.SIPXPBXGROUP)"), classes => if_repaired("java_authorities_repaired"); @@ -70,7 +70,7 @@ bundle agent sipxcommserverlib_config { "$(sipx.SIPX_CONFDIR)/ssl/authorities" comment => "install SSL info $(this.promiser)", create => "true", - copy_from => copy_from_cfdata("authorities"), + copy_from => copy_from_sync_cfdata("$(sipx.location_id)/authorities"), depth_search => recurse("1"), file_select => authority_files, perms => mog("644","$(sipx.SIPXPBXUSER)","$(sipx.SIPXPBXGROUP)"), diff --git a/sipXcommserverLib/include/persist/SipPersistentSubscriptionMgr.h b/sipXcommserverLib/include/persist/SipPersistentSubscriptionMgr.h index cf022c412f..310376ad89 100755 --- a/sipXcommserverLib/include/persist/SipPersistentSubscriptionMgr.h +++ b/sipXcommserverLib/include/persist/SipPersistentSubscriptionMgr.h @@ -42,7 +42,7 @@ class SipPersistentSubscriptionMgr : public SipSubscriptionMgr //! Asks the SipPersistentSubscriptionMgr to initialize itself and sets the address // of the queue to which to send resend messages. The SipPersistentSubscriptionMgr // may not be used until initialize() returns. - virtual void initialize(OsMsgQ* pMsgQ); + virtual UtlBoolean initialize(OsMsgQ* pMsgQ); //! Add/Update subscription for the given SUBSCRIBE request virtual UtlBoolean updateDialogInfo(const SipMessage& subscribeRequest, @@ -129,6 +129,9 @@ class SipPersistentSubscriptionMgr : public SipSubscriptionMgr SubscribeDB& mDB; + //! Marks whether the base class was initialized or not (i.e. SipSubscriptionMgr::initialize() was called) + UtlBoolean mBaseInitialized; + //! Copy constructor NOT ALLOWED SipPersistentSubscriptionMgr(const SipPersistentSubscriptionMgr& rSipPersistentSubscriptionMgr); diff --git a/sipXcommserverLib/include/sipdb/EntityDB.h b/sipXcommserverLib/include/sipdb/EntityDB.h index 50fd700073..49a54fafbf 100755 --- a/sipXcommserverLib/include/sipdb/EntityDB.h +++ b/sipXcommserverLib/include/sipdb/EntityDB.h @@ -35,6 +35,11 @@ class EntityDB: public MongoDB::BaseDB typedef std::set Permissions; typedef Poco::ExpireCache ExpireCache; typedef Poco::SharedPtr ExpireCacheable; + + typedef Poco::ExpireCache EntityTypeCache; + typedef Poco::SharedPtr EntityTypeCacheable; + typedef std::vector BSONObjects; + typedef std::set CallerLocations; void init() { @@ -42,14 +47,14 @@ class EntityDB: public MongoDB::BaseDB } EntityDB(const MongoDB::ConnectionInfo& info) : - BaseDB(info, NS), _cache(1000 * ENTITYDB_CACHE_EXPIRE) + BaseDB(info, NS), _cache(1000 * ENTITYDB_CACHE_EXPIRE), _typeCache(1000 * ENTITYDB_CACHE_EXPIRE) { init(); } EntityDB(const MongoDB::ConnectionInfo& info, const std::string& ns) : - BaseDB(info, ns), _cache(1000 * ENTITYDB_CACHE_EXPIRE) + BaseDB(info, ns), _cache(1000 * ENTITYDB_CACHE_EXPIRE), _typeCache(1000 * ENTITYDB_CACHE_EXPIRE) { init(); } @@ -67,6 +72,8 @@ class EntityDB: public MongoDB::BaseDB bool findByAliasUserId(const std::string& alias, EntityRecord& entity) const; bool findByAliasIdentity(const std::string& identity, EntityRecord& entity) const; + void getCallerLocation(CallerLocations& locations, std::string& fallbackLocation, const std::string& identity, const std::string& host, const std::string& address); + /// Retrieve the SIP credential check values for a given identity and realm bool getCredential(const Url& uri, const UtlString& realm, UtlString& userid, UtlString& passtoken, UtlString& authType) const; @@ -78,8 +85,14 @@ class EntityDB: public MongoDB::BaseDB // Query interface to return a set of mapped full URI // contacts associated with the alias void getAliasContacts(const Url& aliasIdentity, Aliases& aliases, bool& isUserIdentity) const; + + void getAliasContacts(const Url& aliasIdentity, Aliases& aliases, bool& isUserIdentity, UtlString& identity) const; bool tail(std::vector& opLogs); + + void getEntitiesByType(const std::string& entityType, Entities& entities, bool nocache = false); + // Return a vector of entity records matching entityType. + // The result is cached std::string& ns() { return _ns; @@ -88,6 +101,7 @@ class EntityDB: public MongoDB::BaseDB private: mongo::BSONElement _lastTailId; ExpireCache _cache; + EntityTypeCache _typeCache; }; #endif /* ENTITYDB_H */ diff --git a/sipXcommserverLib/include/sipdb/EntityRecord.h b/sipXcommserverLib/include/sipdb/EntityRecord.h index 3b44a04e85..07b3ac8239 100755 --- a/sipXcommserverLib/include/sipdb/EntityRecord.h +++ b/sipXcommserverLib/include/sipdb/EntityRecord.h @@ -82,6 +82,9 @@ class EntityRecord extensionLength = 0; } }; + + typedef std::vector LocationSubnet; + typedef std::vector LocationDomain; EntityRecord(); @@ -142,9 +145,9 @@ class EntityRecord // std::string& location(); static const char* location_fld(); - - // - // User Location + + // + // Call Forward Time // int& callForwardTime(); static const char* callForwardTime_fld(); @@ -154,12 +157,43 @@ class EntityRecord // std::set& permissions(); static const char* permission_fld(); - + + std::set& allowedLocations(); + static const char* allowed_locations_fld(); + + std::set& associatedLocations(); + static const char* associated_locations_fld(); + + const std::string& associatedLocationFallback(); + static const char* associated_location_fallback_fld(); + + std::set& inboundAssociatedLocations(); + static const char* inbound_associated_locations_fld(); // // Permission array to which the user has access to // std::string& entity(); static const char* entity_fld(); + + // + // Authentication Code if present + // + std::string& authc(); + static const char* authc_fld(); + + // + // Inbound location filter by domain + // + LocationDomain& loc_restr_dom(); + static const char* loc_restr_dom_fld(); + + // + // Inbound location filter by IP + // + LocationSubnet& loc_restr_sbnet(); + static const char* loc_restr_sbnet_fld(); + + static const char* entity_branch_str(); // // Caller alias to be sent to certain target domains @@ -209,11 +243,18 @@ class EntityRecord std::string _pin; std::string _authType; std::string _location; + std::string _authc; CallerId _callerId; + LocationDomain _locRestrDom; + LocationSubnet _locRestrSbnet; //bool _ignoreUserCallerId; //bool _transformCallerExtension; int _callForwardTime; std::set _permissions; + std::set _allowedLocations; + std::set _associatedLocations; + std::set _inboundAssociatedLocations; + std::string _associatedLocationFallback; std::string _entity; std::vector _aliases; std::vector _staticUserLoc; @@ -275,6 +316,26 @@ inline std::set& EntityRecord::permissions() return _permissions; } +inline std::set& EntityRecord::allowedLocations() +{ + return _allowedLocations; +} + +inline std::set& EntityRecord::associatedLocations() +{ + return _associatedLocations; +} + +inline const std::string& EntityRecord::associatedLocationFallback() +{ + return _associatedLocationFallback; +} + +inline std::set& EntityRecord::inboundAssociatedLocations() +{ + return _inboundAssociatedLocations; +} + inline std::string& EntityRecord::entity() { return _entity; @@ -285,6 +346,11 @@ inline EntityRecord::CallerId& EntityRecord::callerId() return _callerId; } +inline std::string& EntityRecord::authc() +{ + return _authc; +} + inline std::vector& EntityRecord::aliases() { return _aliases; @@ -300,5 +366,15 @@ inline bool& EntityRecord::vmOnDnd() return _vmOnDnd; } +inline EntityRecord::LocationDomain& EntityRecord::loc_restr_dom() +{ + return _locRestrDom; +} + +inline EntityRecord::LocationSubnet& EntityRecord::loc_restr_sbnet() +{ + return _locRestrSbnet; +} + #endif /* ENTITYRECORD_H */ diff --git a/sipXcommserverLib/include/sipdb/MongoDB.h b/sipXcommserverLib/include/sipdb/MongoDB.h index ba5e36da5a..5de130fe74 100755 --- a/sipXcommserverLib/include/sipdb/MongoDB.h +++ b/sipXcommserverLib/include/sipdb/MongoDB.h @@ -244,6 +244,29 @@ class BaseDB const double getWriteQueryTimeout() const { double writeQueryTimeout = _info.getWriteQueryTimeoutMs(); return writeQueryTimeout/1000; } + // + // Construct final read and write queries by setting the maximum + // time (in milliseconds) the queries will be available in the server part + // + static mongo::Query queryMaxTimeMS(const mongo::BSONObj& obj, unsigned int maxTimeMS); + mongo::Query readQueryMaxTimeMS(const mongo::BSONObj& obj) const; + mongo::Query writeQueryMaxTimeMS(const mongo::BSONObj& obj) const; + + // + // Gets a mongo::Date_t object from the given epoch time in seconds + // + static mongo::Date_t dateFromSecsSinceEpoch(unsigned long timestamp); + + // + // Drops/Removes the specified index keys in a safe way (i.e. catching mongo's possible exceptions) + // + bool safeDropIndex(mongo::DBClientBase* client, const std::string& key) const; + + // + // Ensures the creation of a TTL index in a safe way (i.e. catching mongo's possible exceptions) + // + bool safeEnsureTTLIndex(mongo::DBClientBase* client, const std::string& key, int ttl) const; + protected: std::string _ns; mutable ConnectionInfo _info; diff --git a/sipXcommserverLib/include/sipdb/MongoMod.h b/sipXcommserverLib/include/sipdb/MongoMod.h index 8d42a0ec45..4c158f3856 100644 --- a/sipXcommserverLib/include/sipdb/MongoMod.h +++ b/sipXcommserverLib/include/sipdb/MongoMod.h @@ -32,6 +32,16 @@ namespace mongoMod // extern mongo::BSONObj minKey; + // + // Defines the minimum number of seconds used by the mongo TTL thread, as + // 'expireAfterSeconds' value, to expire documents + // !Note!: This time should be 0 (as specified in + // http://docs.mongodb.org/manual/tutorial/expire-data/#expire-documents-at-a-specific-clock-time + // but due to the limitation in the C++ driver + // (see https://github.com/mongodb/mongo/commit/85b1a93def9416ce3fb00faa077dd871183ef39a#diff-7cd4c4d808fb366aeb57036d60ed1806R1110) + // we'll have to use the minimum possible value + extern const int EXPIRES_AFTER_SECONDS_MINIMUM_SECS; + namespace ScopedDbConnection { // diff --git a/sipXcommserverLib/include/sipdb/MongoOpLog.h b/sipXcommserverLib/include/sipdb/MongoOpLog.h index 4c21c3542d..fe1df139f4 100644 --- a/sipXcommserverLib/include/sipdb/MongoOpLog.h +++ b/sipXcommserverLib/include/sipdb/MongoOpLog.h @@ -87,6 +87,9 @@ class MongoOpLog : public MongoDB::BaseDB // Starts monitor thread and sets _isRunning variable true bool run(); + // Sets _isRunning variable false + void requestStop(); + // Sets _isRunning variable false and then waits for the monitor thread to finish void stop(); diff --git a/sipXcommserverLib/include/sipdb/RegBinding.h b/sipXcommserverLib/include/sipdb/RegBinding.h index 7d5120b247..847eb6a8a6 100755 --- a/sipXcommserverLib/include/sipdb/RegBinding.h +++ b/sipXcommserverLib/include/sipdb/RegBinding.h @@ -33,6 +33,7 @@ class RegBinding void swap(RegBinding& binding); RegBinding& operator=(const RegBinding& binding); RegBinding& operator=(const mongo::BSONObj& bson); + mongo::BSONObj toBSONObj() const; const std::string& getIdentity() const; const std::string& getUri() const; const std::string& getCallId() const; @@ -85,6 +86,7 @@ class RegBinding static const char* shardId_fld(); private: + void fromBSONObj(const mongo::BSONObj& bson); std::string _identity; std::string _uri; std::string _callId; diff --git a/sipXcommserverLib/include/sipdb/RegDB.h b/sipXcommserverLib/include/sipdb/RegDB.h index 07c7648d7e..afec33c9ef 100755 --- a/sipXcommserverLib/include/sipdb/RegDB.h +++ b/sipXcommserverLib/include/sipdb/RegDB.h @@ -39,19 +39,19 @@ class RegDB : public MongoDB::BaseDB typedef std::vector Bindings; RegDB(const MongoDB::ConnectionInfo& info) : - BaseDB(info, NS), _local(NULL), _expireGracePeriod(0) + BaseDB(info, NS), _local(NULL), _expireGracePeriod(0), _expirationTimeIndexTTL(0) { } ; RegDB(const MongoDB::ConnectionInfo& info, RegDB* local) : - BaseDB(info, NS), _local(local), _expireGracePeriod(0) + BaseDB(info, NS), _local(local), _expireGracePeriod(0), _expirationTimeIndexTTL(0) { } ; RegDB(const MongoDB::ConnectionInfo& info, RegDB* local, const std::string& ns) : - BaseDB(info, ns), _local(local), _expireGracePeriod(0) + BaseDB(info, ns), _local(local), _expireGracePeriod(0), _expirationTimeIndexTTL(0) { } ; @@ -65,7 +65,7 @@ class RegDB : public MongoDB::BaseDB } ; - static RegDB* CreateInstance(); + static RegDB* CreateInstance(bool ensureIndexes = false); void updateBinding(const RegBinding::Ptr& pBinding); @@ -125,6 +125,11 @@ class RegDB : public MongoDB::BaseDB bool isRegisteredBinding( const Url& binding, bool preferPrimary = false); + + bool getUnexpiredRegisteredBinding( + const Url& registeredBinding, + Bindings& bindings, + bool preferPrimary = false); void cleanAndPersist(int currentExpireTime); @@ -150,9 +155,11 @@ class RegDB : public MongoDB::BaseDB protected: private: + void ensureIndexes(mongo::DBClientBase* client = NULL); std::string _localAddress; RegDB* _local; unsigned long _expireGracePeriod; + int _expirationTimeIndexTTL; }; // diff --git a/sipXcommserverLib/include/sipdb/SubscribeDB.h b/sipXcommserverLib/include/sipdb/SubscribeDB.h index 2ca25af1d7..1fd9b5d0e9 100755 --- a/sipXcommserverLib/include/sipdb/SubscribeDB.h +++ b/sipXcommserverLib/include/sipdb/SubscribeDB.h @@ -52,19 +52,19 @@ class SubscribeDB : public MongoDB::BaseDB static const std::string NS; typedef std::vector Subscriptions; SubscribeDB(const MongoDB::ConnectionInfo& info) : - BaseDB(info, NS), _local(NULL) + BaseDB(info, NS), _local(NULL), _isFirstEnsureIndexes(true) { } ; SubscribeDB(const MongoDB::ConnectionInfo& info, SubscribeDB* local) : - BaseDB(info, NS) , _local(local) + BaseDB(info, NS) , _local(local), _isFirstEnsureIndexes(true) { } ; SubscribeDB(const MongoDB::ConnectionInfo& info, SubscribeDB* local, std::string ns) : - BaseDB(info, ns), _local(local) + BaseDB(info, ns), _local(local), _isFirstEnsureIndexes(true) { } ; @@ -148,7 +148,7 @@ class SubscribeDB : public MongoDB::BaseDB const UtlString& id, unsigned long timeNow, int updatedNotifyCseq, - int version) const; + int version); // void updateSubscribeUnexpiredSubscription ( // const UtlString& component, @@ -164,7 +164,7 @@ class SubscribeDB : public MongoDB::BaseDB void updateToTag( const UtlString& callid, const UtlString& fromtag, - const UtlString& totag) const; + const UtlString& totag); bool findFromAndTo( const UtlString& callid, @@ -179,12 +179,14 @@ class SubscribeDB : public MongoDB::BaseDB void removeAllExpired(); - static SubscribeDB* CreateInstance(); + static SubscribeDB* CreateInstance(bool ensureIndexes = false); private: - void ensureIndex(mongo::DBClientBase* client) const; + void ensureIndexes(mongo::DBClientBase* client = NULL); SubscribeDB* _local; + bool _isFirstEnsureIndexes; + }; #endif /* SUBSCRIBEDB_H */ diff --git a/sipXcommserverLib/sipxcommserverlib.spec.in b/sipXcommserverLib/sipxcommserverlib.spec.in index 6412b4e34f..bcf137fac6 100755 --- a/sipXcommserverLib/sipxcommserverlib.spec.in +++ b/sipXcommserverLib/sipxcommserverlib.spec.in @@ -144,7 +144,7 @@ rm -rf $RPM_BUILD_ROOT %dir %attr(755,sipx,sipx) %{_localstatedir}/log/sipxpbx %dir %attr(755,sipx,sipx) %{_localstatedir}/run/sipxpbx %dir %attr(755,sipx,sipx) %{_localstatedir}/sipxdata -%dir %attr(755,sipx,sipx) %{_localstatedir}/sipxdata/tmp +%dir %attr(777,sipx,sipx) %{_localstatedir}/sipxdata/tmp %dir %attr(755,sipx,sipx) %{_localstatedir}/sipxdata/sipdb %dir %attr(755,sipx,sipx) %{_libexecdir}/sipXecs %dir %attr(755,sipx,sipx) %{_libexecdir}/sipXecs/setup.d diff --git a/sipXcommserverLib/src/persist/SipPersistentSubscriptionMgr.cpp b/sipXcommserverLib/src/persist/SipPersistentSubscriptionMgr.cpp index 4c75db72b7..fa6b44e45b 100755 --- a/sipXcommserverLib/src/persist/SipPersistentSubscriptionMgr.cpp +++ b/sipXcommserverLib/src/persist/SipPersistentSubscriptionMgr.cpp @@ -33,7 +33,8 @@ SipPersistentSubscriptionMgr::SipPersistentSubscriptionMgr( SubscribeDB& db) : mComponent(component), mDomain(domain), - mDB(db) + mDB(db), + mBaseInitialized(FALSE) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipPersistentSubscriptionMgr:: " @@ -49,16 +50,37 @@ SipPersistentSubscriptionMgr::~SipPersistentSubscriptionMgr() } /* ============================ MANIPULATORS ============================== */ -void SipPersistentSubscriptionMgr::initialize(OsMsgQ* pMsgQ) +UtlBoolean SipPersistentSubscriptionMgr::initialize(OsMsgQ* pMsgQ) { - // initialize the base class - SipSubscriptionMgr::initialize(pMsgQ); + // initialize the base class, if needed + if (!mBaseInitialized) + { + mBaseInitialized = SipSubscriptionMgr::initialize(pMsgQ); + } - // Read the subscription table and initialize the SipSubscriptionMgr. + // exit in case the base class wasn't successfully initialized + if (!mBaseInitialized) + { + return FALSE; + } + // Read the subscription table and initialize the SipSubscriptionMgr. unsigned long now = OsDateTime::getSecsSinceEpoch(); SubscribeDB::Subscriptions subscriptions; - mDB.getAll(subscriptions); + try + { + mDB.getAll(subscriptions); + } + catch (std::exception& e) + { + Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipPersistentSubscriptionMgr::initialize - exception %s", e.what()); + return FALSE; + } + catch (...) + { + Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipPersistentSubscriptionMgr::initialize - unknown exception"); + return FALSE; + } for (SubscribeDB::Subscriptions::iterator iter = subscriptions.begin(); iter != subscriptions.end(); iter++) @@ -137,6 +159,8 @@ void SipPersistentSubscriptionMgr::initialize(OsMsgQ* pMsgQ) row.version()); } } + + return TRUE; } UtlBoolean SipPersistentSubscriptionMgr::updateDialogInfo( @@ -289,7 +313,25 @@ UtlBoolean SipPersistentSubscriptionMgr::getNotifyDialogInfo( // because updateFromAndTo uses these words in relationship to // the SUBSCRIBE, whereas getNotifyDialogInfo uses these words // in relationship to the NOTIFY, which is the reverse order. - mDB.updateToTag(callId, localTag, remoteTag); + try + { + mDB.updateToTag(callId, localTag, remoteTag); + } +#ifdef MONGO_assert + catch (mongo::DBException& e) + { + + OS_LOG_ERROR( FAC_SIP, "SipSubscriptionMgr::getNotifyDialogInfo::mDB.updateToTag() Exception: " + << e.what() ); + ret = FALSE; + } +#endif + catch(...) + { + OS_LOG_ERROR( FAC_SIP, "SipSubscriptionMgr::getNotifyDialogInfo::mDB.updateToTag() Exception: " + << " UNKNOWN" ); + ret = FALSE; + } } // The NOTIFY CSeq and XML version will be saved when our caller diff --git a/sipXcommserverLib/src/sipdb/EntityDB.cpp b/sipXcommserverLib/src/sipdb/EntityDB.cpp index 515cf9e9c1..ebf586deb5 100755 --- a/sipXcommserverLib/src/sipdb/EntityDB.cpp +++ b/sipXcommserverLib/src/sipdb/EntityDB.cpp @@ -20,6 +20,7 @@ #include "sipdb/MongoDB.h" #include "sipdb/MongoMod.h" #include +#include #include using namespace std; @@ -60,6 +61,78 @@ static std::string validate_identity_string(const std::string& identity) return identity; } +static bool wildcard_compare(const char* wild, const std::string& str) +{ + const char* string = str.c_str(); + + const char *cp = NULL, *mp = NULL; + + while ((*string) && (*wild != '*')) { + if ((*wild != *string) && (*wild != '?')) { + return 0; + } + wild++; + string++; + } + + while (*string) { + if (*wild == '*') { + if (!*++wild) { + return 1; + } + mp = wild; + cp = string+1; + } else if ((*wild == *string) || (*wild == '?')) { + wild++; + string++; + } else { + wild = mp; + string = cp++; + } + } + + while (*wild == '*') { + wild++; + } + return !*wild; +} + +static bool cidr_compare(const std::string& cidr, const std::string& ip) +{ + std::vector cidr_tokens; + boost::split(cidr_tokens, cidr, boost::is_any_of("/-"), boost::token_compress_on); + + unsigned bits = 24; + std::string start_ip; + if (cidr_tokens.size() == 2) + { + bits = (unsigned)::atoi(cidr_tokens[1].c_str()); + start_ip = cidr_tokens[0]; + } + else + { + start_ip = cidr; + } + + boost::system::error_code ec; + boost::asio::ip::address_v4 ipv4; + ipv4 = boost::asio::ip::address_v4::from_string(ip, ec); + if (!ec) + { + unsigned long ipv4ul = ipv4.to_ulong(); + boost::asio::ip::address_v4 start_ip_ipv4; + start_ip_ipv4 = boost::asio::ip::address_v4::from_string(start_ip, ec); + if (!ec) + { + long double numHosts = pow((long double)2, (int)(32-bits)) - 1; + unsigned long start_ip_ipv4ul = start_ip_ipv4.to_ulong(); + unsigned long ceiling = start_ip_ipv4ul + numHosts; + return ipv4ul >= start_ip_ipv4ul && ipv4ul <= ceiling; + } + } + return false; +} + bool EntityDB::findByIdentity(const string& ident, EntityRecord& entity) const { MongoDB::ReadTimer readTimer(const_cast(*this)); @@ -84,15 +157,11 @@ bool EntityDB::findByIdentity(const string& ident, EntityRecord& entity) const mongo::BSONObjBuilder builder; BaseDB::nearest(builder, query); - auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); - if (!pCursor.get()) - { - throw mongo::DBException("mongo query returned null cursor", 0); - } - else if (pCursor->more()) + mongo::BSONObj entityObj = conn->get()->findOne(_ns, readQueryMaxTimeMS(builder.obj()), 0, mongo::QueryOption_SlaveOk); + if (!entityObj.isEmpty()) { OS_LOG_DEBUG(FAC_ODBC, identity << " is present in namespace " << _ns); - entity = pCursor->next(); + entity = entityObj; conn->done(); // // Cache the entity @@ -107,6 +176,146 @@ bool EntityDB::findByIdentity(const string& ident, EntityRecord& entity) const return false; } +void EntityDB::getEntitiesByType(const std::string& entityType, Entities& entities, bool nocache) +{ + MongoDB::ReadTimer readTimer(const_cast(*this)); + OS_LOG_INFO(FAC_ODBC, "EntityDB::getEntitiesByType - Finding entity records for type " << entityType << " from namespace " << _ns); + + // + // Check if we have it in cache + // + if (!nocache) + { + EntityTypeCacheable pCacheObj = const_cast(_typeCache).get(entityType); + if (pCacheObj) + { + OS_LOG_DEBUG(FAC_ODBC, "EntityDB::getEntitiesByType - " << entityType << " is present in namespace " << _ns << " (CACHED)"); + entities = *pCacheObj; + return; + } + } + + mongo::BSONObj query = BSON(EntityRecord::entity_fld() << entityType); + + MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); + + mongo::BSONObjBuilder builder; + BaseDB::nearest(builder, query); + + /** query N objects from the database into an array. makes sense mostly when you want a small number of results. if a huge number, use + query() and iterate the cursor. + void findN(vector& out, const string&ns, Query query, int nToReturn, int nToSkip = 0, const BSONObj *fieldsToReturn = 0, int queryOptions = 0); + */ + + BSONObjects objects; + conn->get()->findN( + objects, // out + _ns, // ns + readQueryMaxTimeMS(builder.obj()), // query + 1024, // nToReturn + 0, // nToSkip, + 0, // fieldsToReturn + mongo::QueryOption_SlaveOk // queryOptions + ); + + entities.clear(); + for (BSONObjects::iterator iter = objects.begin(); iter != objects.end(); iter++) + { + if (!iter->isEmpty()) + { + EntityRecord entity; + entity = (*iter); + entities.push_back(entity); + } + } + + if (entities.empty()) + { + OS_LOG_DEBUG(FAC_ODBC, entityType << " is NOT present in namespace " << _ns); + OS_LOG_INFO(FAC_ODBC, "EntityDB::getEntitiesByType - Unable to find entity record for type " << entityType << " from namespace " << _ns); + } + else + { + const_cast(_typeCache).add(entityType, EntityTypeCacheable(new Entities(entities))); + OS_LOG_DEBUG(FAC_ODBC, "EntityDB::getEntitiesByType - " << entityType << " is present in namespace " << _ns); + } + + conn->done(); +} + +void EntityDB::getCallerLocation(CallerLocations& locations, std::string& fallbackLocation, const std::string& identity, const std::string& host, const std::string& address) +{ + Entities branches; + getEntitiesByType(EntityRecord::entity_branch_str(), branches); + + OS_LOG_INFO(FAC_ODBC, "EntityDB::getCallerLocation( identity=" << identity << ", host=" << host << ", address=" << address << ")"); + + EntityRecord userEntity; + if (findByIdentity(identity, userEntity) && !userEntity.allowedLocations().empty()) + { + // + // Now that we have the locations for this user, check for branch associated locations + // + for (std::set::iterator locations_iter = userEntity.allowedLocations().begin(); locations_iter != userEntity.allowedLocations().end(); locations_iter++) + { + for (Entities::iterator branch_iter = branches.begin(); branch_iter != branches.end(); branch_iter++) + { + if (branch_iter->location() == *locations_iter) + { + OS_LOG_INFO(FAC_ODBC, "EntityDB::getCallerLocation - Setting associated locations based on identity " << identity << " branch " << branch_iter->location()); + locations = branch_iter->associatedLocations(); + fallbackLocation = branch_iter->associatedLocationFallback(); + return; + } + } + } + + return; + } + + // + // Get locations by domain or subnet + // + for (Entities::iterator host_iter = branches.begin(); host_iter != branches.end(); host_iter++) // this loop iterates the branch record we have queried + { + if (!host.empty() && !host_iter->loc_restr_dom().empty() && !host_iter->inboundAssociatedLocations().empty()) // only if locations and wild card matching is specified by the branch + { + for (EntityRecord::LocationDomain::iterator domainIter = host_iter->loc_restr_dom().begin(); domainIter != host_iter->loc_restr_dom().end(); domainIter++) + { + if (wildcard_compare(domainIter->c_str(), host)) + { + OS_LOG_INFO(FAC_ODBC, "EntityDB::getCallerLocation - Inserting location based on " << *domainIter << " wildcard match for domain " << host); + locations = host_iter->inboundAssociatedLocations(); + fallbackLocation = host_iter->associatedLocationFallback(); + return; + } + else + { + OS_LOG_DEBUG(FAC_ODBC,"EntityDB::getCallerLocation - " << host_iter->location() << "/" << *domainIter << " does not own domain " << host); + } + } + } + + if (!address.empty() && !host_iter->loc_restr_sbnet().empty() && !host_iter->inboundAssociatedLocations().empty()) + { + for (EntityRecord::LocationSubnet::iterator subnetIter = host_iter->loc_restr_sbnet().begin(); subnetIter != host_iter->loc_restr_sbnet().end(); subnetIter++) + { + if (cidr_compare(*subnetIter, address)) + { + OS_LOG_INFO(FAC_ODBC, "EntityDB::getCallerLocation - Inserting location based on " << *subnetIter << " CIDR match for address " << address); + locations = host_iter->inboundAssociatedLocations(); + fallbackLocation = host_iter->associatedLocationFallback(); + return; + } + else + { + OS_LOG_DEBUG(FAC_ODBC,"EntityDB::getCallerLocation - " << host_iter->location() << "/" << *subnetIter << " does not own address " << address); + } + } + } + } +} + bool EntityDB::findByUserId(const string& uid, EntityRecord& entity) const { MongoDB::ReadTimer readTimer(const_cast(*this)); @@ -126,15 +335,11 @@ bool EntityDB::findByUserId(const string& uid, EntityRecord& entity) const mongo::BSONObjBuilder builder; BaseDB::nearest(builder, query); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); - auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); - if (!pCursor.get()) + mongo::BSONObj entityObj = conn->get()->findOne(_ns, readQueryMaxTimeMS(builder.obj()), 0, mongo::QueryOption_SlaveOk); + if (!entityObj.isEmpty()) { - throw mongo::DBException("mongo query returned null cursor", 0); - } - else if (pCursor->more()) - { - entity = pCursor->next(); + entity = entityObj; conn->done(); // // Cache the entity @@ -190,15 +395,11 @@ bool EntityDB::findByAliasUserId(const string& alias, EntityRecord& entity) cons mongo::BSONObjBuilder builder; BaseDB::nearest(builder, query); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); - auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); - if (!pCursor.get()) - { - throw mongo::DBException("mongo query returned null cursor", 0); - } - else if (pCursor->more()) + mongo::BSONObj entityObj = conn->get()->findOne(_ns, readQueryMaxTimeMS(builder.obj()), 0, mongo::QueryOption_SlaveOk); + if (!entityObj.isEmpty()) { - entity = pCursor->next(); + entity = entityObj; conn->done(); // // Cache the entity @@ -264,9 +465,9 @@ bool EntityDB::getCredential(const UtlString& userid, const UtlString& realm, Ur return true; } -void EntityDB::getAliasContacts(const Url& aliasIdentity, Aliases& aliases, bool& isUserIdentity) const +void EntityDB::getAliasContacts(const Url& aliasIdentity, Aliases& aliases, bool& isUserIdentity, UtlString& userIdentity) const { - UtlString alias; + UtlString alias; aliasIdentity.getUserId(alias); if (alias.isNull()) return; @@ -286,9 +487,16 @@ void EntityDB::getAliasContacts(const Url& aliasIdentity, Aliases& aliases, bool aliases.push_back(*iter); } isUserIdentity = !entity.realm().empty() && !entity.password().empty(); + userIdentity = entity.identity().c_str(); } } +void EntityDB::getAliasContacts(const Url& aliasIdentity, Aliases& aliases, bool& isUserIdentity) const +{ + UtlString identity; + getAliasContacts(aliasIdentity, aliases, isUserIdentity, identity); +} + bool EntityDB::findByIdentity(const Url& uri, EntityRecord& entity) const { UtlString identity; diff --git a/sipXcommserverLib/src/sipdb/EntityRecord.cpp b/sipXcommserverLib/src/sipdb/EntityRecord.cpp index 26c5deeea4..27e165e512 100755 --- a/sipXcommserverLib/src/sipdb/EntityRecord.cpp +++ b/sipXcommserverLib/src/sipdb/EntityRecord.cpp @@ -29,7 +29,12 @@ const char* EntityRecord::pin_fld(){ static std::string fld = "pntk"; return fld const char* EntityRecord::authType_fld(){ static std::string fld = "authtp"; return fld.c_str(); } const char* EntityRecord::location_fld(){ static std::string fld = "loc"; return fld.c_str(); } const char* EntityRecord::permission_fld(){ static std::string fld = "prm"; return fld.c_str(); } +const char* EntityRecord::allowed_locations_fld(){ static std::string fld = "locations"; return fld.c_str(); } +const char* EntityRecord::associated_locations_fld(){ static std::string fld = "loc_assoc"; return fld.c_str(); } +const char* EntityRecord::associated_location_fallback_fld(){ static std::string fld = "loc_assoc_fallback"; return fld.c_str(); } +const char* EntityRecord::inbound_associated_locations_fld(){ static std::string fld = "loc_assoc_inbound"; return fld.c_str(); } const char* EntityRecord::entity_fld(){ static std::string fld = "ent"; return fld.c_str(); } +const char* EntityRecord::authc_fld(){ static std::string fld = "authc"; return fld.c_str(); } const char* EntityRecord::callerId_fld(){ static std::string fld = "clrid"; return fld.c_str(); } const char* EntityRecord::callerIdEnforcePrivacy_fld(){ static std::string fld = "blkcid"; return fld.c_str(); } @@ -49,6 +54,9 @@ const char* EntityRecord::staticUserLocContact_fld(){ static std::string fld = " const char* EntityRecord::staticUserLocFromUri_fld(){ static std::string fld = "from"; return fld.c_str(); } const char* EntityRecord::staticUserLocToUri_fld(){ static std::string fld = "to"; return fld.c_str(); } const char* EntityRecord::staticUserLocCallId_fld(){ static std::string fld = "cid"; return fld.c_str(); } +const char* EntityRecord::loc_restr_dom_fld(){ static std::string fld = "loc_restr_dom"; return fld.c_str(); } +const char* EntityRecord::loc_restr_sbnet_fld(){ static std::string fld = "loc_restr_sbnet"; return fld.c_str(); } +const char* EntityRecord::entity_branch_str(){ static std::string fld = "branch"; return fld.c_str(); } const char* EntityRecord::vmOnDnd_fld(){ static std::string fld = "vmondnd"; return fld.c_str(); }; @@ -69,12 +77,19 @@ EntityRecord::EntityRecord(const EntityRecord& entity) _authType = entity._authType; _location = entity._location; _permissions = entity._permissions; + _allowedLocations = entity._allowedLocations; + _associatedLocations = entity._associatedLocations; + _associatedLocationFallback = entity._associatedLocationFallback; + _inboundAssociatedLocations = entity._inboundAssociatedLocations; _entity = entity._entity; + _authc = entity._authc; _callerId = entity._callerId; _aliases = entity._aliases; _callForwardTime = entity._callForwardTime; _staticUserLoc = entity._staticUserLoc; _vmOnDnd = entity._vmOnDnd; + _locRestrDom = entity._locRestrDom; + _locRestrSbnet = entity._locRestrSbnet; } EntityRecord::~EntityRecord() @@ -99,12 +114,19 @@ void EntityRecord::swap(EntityRecord& entity) std::swap(_authType, entity._authType); std::swap(_location, entity._location); std::swap(_permissions, entity._permissions); + std::swap(_allowedLocations, entity._allowedLocations); + std::swap(_associatedLocations, entity._associatedLocations); + std::swap(_associatedLocationFallback, entity._associatedLocationFallback); + std::swap(_inboundAssociatedLocations, entity._inboundAssociatedLocations); std::swap(_entity, entity._entity); + std::swap(_authc, entity._authc); std::swap(_callerId, entity._callerId); std::swap(_aliases, entity._aliases); std::swap(_callForwardTime, entity._callForwardTime); std::swap(_staticUserLoc, entity._staticUserLoc); std::swap(_vmOnDnd, entity._vmOnDnd); + std::swap(_locRestrDom, entity._locRestrDom); + std::swap(_locRestrSbnet, entity._locRestrSbnet); } void EntityRecord::fillStaticUserLoc(StaticUserLoc& userLoc, const mongo::BSONObj& innerObj) @@ -169,6 +191,36 @@ EntityRecord& EntityRecord::operator = (const mongo::BSONObj& bsonObj) { _vmOnDnd = bsonObj.getBoolField(EntityRecord::vmOnDnd_fld()); } + + if (bsonObj.hasField(EntityRecord::loc_restr_dom_fld())) + { + mongo::BSONElement obj = bsonObj[EntityRecord::loc_restr_dom_fld()]; + if ( obj.isABSONObj() && obj.type() == mongo::Array) + { + std::vector locations = obj.Array(); + _locRestrDom.clear(); + for (std::vector::iterator iter = locations.begin(); + iter != locations.end(); iter++) + { + _locRestrDom.push_back(iter->String()); + } + } + } + + if (bsonObj.hasField(EntityRecord::loc_restr_sbnet_fld())) + { + mongo::BSONElement obj = bsonObj[EntityRecord::loc_restr_sbnet_fld()]; + if ( obj.isABSONObj() && obj.type() == mongo::Array) + { + std::vector locations = obj.Array(); + _locRestrSbnet.clear(); + for (std::vector::iterator iter = locations.begin(); + iter != locations.end(); iter++) + { + _locRestrSbnet.push_back(iter->String()); + } + } + } if (bsonObj.hasField(EntityRecord::callerId_fld())) { @@ -198,11 +250,66 @@ EntityRecord& EntityRecord::operator = (const mongo::BSONObj& bsonObj) } } } + + if (bsonObj.hasField(EntityRecord::allowed_locations_fld())) + { + mongo::BSONElement obj = bsonObj[EntityRecord::allowed_locations_fld()]; + if ( obj.isABSONObj() && obj.type() == mongo::Array) + { + std::vector locations = obj.Array(); + _allowedLocations.clear(); + for (std::vector::iterator iter = locations.begin(); + iter != locations.end(); iter++) + { + _allowedLocations.insert(iter->String()); + } + } + } + + if (bsonObj.hasField(EntityRecord::associated_locations_fld())) + { + mongo::BSONElement obj = bsonObj[EntityRecord::associated_locations_fld()]; + if ( obj.isABSONObj() && obj.type() == mongo::Array) + { + std::vector locations = obj.Array(); + _associatedLocations.clear(); + for (std::vector::iterator iter = locations.begin(); + iter != locations.end(); iter++) + { + _associatedLocations.insert(iter->String()); + } + } + } + + if (bsonObj.hasField(EntityRecord::inbound_associated_locations_fld())) + { + mongo::BSONElement obj = bsonObj[EntityRecord::inbound_associated_locations_fld()]; + if ( obj.isABSONObj() && obj.type() == mongo::Array) + { + std::vector locations = obj.Array(); + _inboundAssociatedLocations.clear(); + for (std::vector::iterator iter = locations.begin(); + iter != locations.end(); iter++) + { + _inboundAssociatedLocations.insert(iter->String()); + } + } + } if (bsonObj.hasField(EntityRecord::entity_fld())) { _entity = bsonObj.getStringField(EntityRecord::entity_fld()); } + + if (bsonObj.hasField(EntityRecord::authc_fld())) + { + _authc = bsonObj.getStringField(EntityRecord::authc_fld()); + } + + if (bsonObj.hasField(EntityRecord::associated_location_fallback_fld())) + { + _associatedLocationFallback = bsonObj.getStringField(EntityRecord::associated_location_fallback_fld()); + } if (bsonObj.hasField(EntityRecord::aliases_fld())) { diff --git a/sipXcommserverLib/src/sipdb/GatewayDestDB.cpp b/sipXcommserverLib/src/sipdb/GatewayDestDB.cpp index 2ed374f752..80a5c3ba3c 100755 --- a/sipXcommserverLib/src/sipdb/GatewayDestDB.cpp +++ b/sipXcommserverLib/src/sipdb/GatewayDestDB.cpp @@ -140,12 +140,8 @@ bool GatewayDestDB::getUnexpiredRecord(GatewayDestRecord& record) const mongo::BSONObjBuilder builder; BaseDB::nearest(builder, query); - auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); - if (!pCursor.get()) - { - throw mongo::DBException("mongo query returned null cursor", 0); - } - else if (pCursor->more()) + mongo::BSONObj recordObj = conn->get()->findOne(_ns, readQueryMaxTimeMS(builder.obj()), 0, mongo::QueryOption_SlaveOk); + if (!recordObj.isEmpty()) { OS_LOG_DEBUG(FAC_ODBC, "GatewayDestDB::getUnexpiredRecord - found record " " callid " << record.getCallId() << @@ -155,7 +151,7 @@ bool GatewayDestDB::getUnexpiredRecord(GatewayDestRecord& record) const " lineId " << record.getLineId() << " expirationTime " << record.getExpirationTime()); - record = pCursor->next(); + record = recordObj; conn->done(); return true; } diff --git a/sipXcommserverLib/src/sipdb/MongoDB.cpp b/sipXcommserverLib/src/sipdb/MongoDB.cpp index 675b4425b8..d6d6916940 100755 --- a/sipXcommserverLib/src/sipdb/MongoDB.cpp +++ b/sipXcommserverLib/src/sipdb/MongoDB.cpp @@ -329,7 +329,87 @@ namespace MongoDB return _lastReadSpeed; } + mongo::Query BaseDB::queryMaxTimeMS(const mongo::BSONObj& obj, unsigned int maxTimeMs) + { + mongo::Query query(obj); + + return query.maxTimeMs(maxTimeMs); + } + + mongo::Query BaseDB::readQueryMaxTimeMS(const mongo::BSONObj& obj) const + { + return queryMaxTimeMS(obj, _info.getReadQueryTimeoutMs()); + } + + mongo::Query BaseDB::writeQueryMaxTimeMS(const mongo::BSONObj& obj) const + { + return queryMaxTimeMS(obj, _info.getWriteQueryTimeoutMs()); + } + + mongo::Date_t BaseDB::dateFromSecsSinceEpoch(unsigned long timestamp) + { + // Note: the constructor of mongo::Date_t expects the timestamp in millis + return mongo::Date_t(1000 * timestamp); + } + + bool BaseDB::safeDropIndex(mongo::DBClientBase* client, const std::string& key) const + { + int ret = true; + + try + { + client->dropIndex(_ns, BSON(key << 1)); + } + // NOTE: we're logging with WARNING level because this function will return + // an exception in case the key does not exist in the database, which + // looks like a pretty common setup + catch (std::exception& e) + { + OS_LOG_WARNING(FAC_SIP, "BaseDB::safeDropIndex failed for index: " << key + << ". " << e.what()); + ret = false; + } + catch (...) + { + OS_LOG_WARNING(FAC_SIP, "BaseDB::safeDropIndex failed for index: " << key + << ". Unknown Exception"); + ret = false; + } + + return ret; + } + + bool BaseDB::safeEnsureTTLIndex(mongo::DBClientBase* client, const std::string& key, int ttl) const + { + int ret = true; + + try + { + // Note: the parameters from 3 to 7 are just the defaults of the function + client->ensureIndex(_ns, BSON(key << 1), + false, "", true, false, -1, /* just the defaults */ + ttl); + } + catch (std::exception& e) + { + OS_LOG_ERROR(FAC_SIP, "BaseDB::safeEnsureTTLIndex failed for index: " << key + << ", ttl: " << ttl + << ". " << e.what()); + ret = false; + } + catch (...) + { + OS_LOG_ERROR(FAC_SIP, "BaseDB::safeEnsureTTLIndex failed for index: " << key + << ", ttl: " << ttl + << ". Unknown Exception"); + ret = false; + } + + return ret; + } + UpdateTimer::UpdateTimer(BaseDB& db) : + _end(0), _db(db) { struct timeval sTimeVal; @@ -346,6 +426,7 @@ namespace MongoDB } ReadTimer::ReadTimer(BaseDB& db) : + _end(0), _db(db) { struct timeval sTimeVal; diff --git a/sipXcommserverLib/src/sipdb/MongoMod.cpp b/sipXcommserverLib/src/sipdb/MongoMod.cpp index 0afb66d0df..1788153c5b 100644 --- a/sipXcommserverLib/src/sipdb/MongoMod.cpp +++ b/sipXcommserverLib/src/sipdb/MongoMod.cpp @@ -15,8 +15,14 @@ #include +#include "os/OsTime.h" +#include "os/OsDateTime.h" +#include "os/OsLogger.h" #include "sipdb/MongoMod.h" +// Defines the maximum delay accepted for mongo connection +#define MAX_CONNECTION_DELAY_MSEC 100 + // // Rewrites the mongo::minKey external variable defined in Mongo c++ driver // implementation - mongo/db/jsonobj.cpp file @@ -37,7 +43,35 @@ static struct MinKeyData } minkeydata; mongo::BSONObj mongoMod::minKey((const char *) &minkeydata); +const int mongoMod::EXPIRES_AFTER_SECONDS_MINIMUM_SECS = 1; + mongo::ScopedDbConnection* mongoMod::ScopedDbConnection::getScopedDbConnection(const std::string& host, double socketTimeout) { - return new mongo::ScopedDbConnection(host, socketTimeout); + OS_LOG_DEBUG(FAC_ODBC, "ScopedDbConnection::getScopedDbConnection() - host '" << host << "', socketTimeout '" << socketTimeout << "' seconds"); + + OsTime start; + OsDateTime::getCurTime(start); + + mongo::ScopedDbConnection* connection = new mongo::ScopedDbConnection(host, socketTimeout); + if (connection->ok()) + { + OsTime stop; + OsDateTime::getCurTime(stop); + int connectionDelayMsec = (stop - start).cvtToMsecs(); + + OsSysLogPriority priority = PRI_DEBUG; + if (MAX_CONNECTION_DELAY_MSEC < connectionDelayMsec) + { + priority = PRI_ALERT; + } + Os::Logger::instance().log(FAC_ODBC, priority, "ScopedDbConnection::getScopedDbConnection() - returned connection %p in %d milliseconds", + connection->get(), + connectionDelayMsec); + } + else + { + OS_LOG_ERROR(FAC_ODBC, "ScopedDbConnection::getScopedDbConnection() - failed for host " << host); + } + + return connection; } diff --git a/sipXcommserverLib/src/sipdb/MongoOpLog.cpp b/sipXcommserverLib/src/sipdb/MongoOpLog.cpp index 46fa417329..7c2f5ef27f 100644 --- a/sipXcommserverLib/src/sipdb/MongoOpLog.cpp +++ b/sipXcommserverLib/src/sipdb/MongoOpLog.cpp @@ -97,18 +97,19 @@ bool MongoOpLog::run() return true; } +void MongoOpLog::requestStop() +{ + OS_LOG_INFO(FAC_SIP, "MongoOpLog::requestStop notify MongoOpLog thread"); + _isRunning = false; +} + void MongoOpLog::stop() { - if (false == _isRunning) - { - return; - } + requestStop(); OS_LOG_INFO(FAC_SIP, "MongoOpLog::stop:" << " stopping MongoOpLog thread"); - _isRunning = false; - if (_pThread) { _pThread->join(); diff --git a/sipXcommserverLib/src/sipdb/RegBinding.cpp b/sipXcommserverLib/src/sipdb/RegBinding.cpp index bb1f54d6aa..6be4c0868c 100755 --- a/sipXcommserverLib/src/sipdb/RegBinding.cpp +++ b/sipXcommserverLib/src/sipdb/RegBinding.cpp @@ -13,6 +13,8 @@ * details. */ +#include + #include "sipdb/RegBinding.h" const char* RegBinding::identity_fld(){ static std::string fld = "identity"; return fld.c_str(); } @@ -90,6 +92,104 @@ void RegBinding::swap(RegBinding& binding) std::swap(_expired, binding._expired); } +void RegBinding::fromBSONObj(const mongo::BSONObj& bson) +{ + if (bson.hasField(RegBinding::identity_fld())) + _identity = bson.getStringField(RegBinding::identity_fld()); + + if (bson.hasField(RegBinding::uri_fld())) + _uri = bson.getStringField(RegBinding::uri_fld()); + + if (bson.hasField(RegBinding::callId_fld())) + _callId = bson.getStringField(RegBinding::callId_fld()); + + if (bson.hasField(RegBinding::contact_fld())) + _contact = bson.getStringField(RegBinding::contact_fld()); + + if (bson.hasField(RegBinding::binding_fld())) + _binding = bson.getStringField(RegBinding::binding_fld()); + + if (bson.hasField(RegBinding::qvalue_fld())) + _qvalue = bson.getStringField(RegBinding::qvalue_fld()); + + if (bson.hasField(RegBinding::instanceId_fld())) + _instanceId = bson.getStringField(RegBinding::instanceId_fld()); + + if (bson.hasField(RegBinding::gruu_fld())) + _gruu = bson.getStringField(RegBinding::gruu_fld()); + + if (bson.hasField(RegBinding::shardId_fld())) + _shardId = bson.getIntField(RegBinding::shardId_fld()); + + if (bson.hasField(RegBinding::path_fld())) + _path = bson.getStringField(RegBinding::path_fld()); + + if (bson.hasField(RegBinding::cseq_fld())) + _cseq = bson.getIntField(RegBinding::cseq_fld()); + + if (bson.hasField(RegBinding::expirationTime_fld())) + { + mongo::BSONElement expirationTimeElement = bson.getField(RegBinding::expirationTime_fld()); + + // save the time depending on its type + if (mongo::Date == expirationTimeElement.type()) + { + _expirationTime = static_cast(expirationTimeElement.date().toTimeT()); + } + else if (expirationTimeElement.isNumber()) + { + _expirationTime = static_cast(expirationTimeElement.Number()); + OS_LOG_WARNING(FAC_SIP, "RegBinding::fromBSONObj found old-style registration" + << " Identity: " << _identity + << " Contact: " << _contact + << " Call-Id: " << _callId); + } + else + { + OS_LOG_ERROR(FAC_SIP, "RegBinding::fromBSONObj " + "unsupported " << expirationTimeElement.toString() << " element for registration" + << " Identity: " << _identity + << " Contact: " << _contact + << " Call-Id: " << _callId); + } + } + + if (bson.hasField(RegBinding::instrument_fld())) + _instrument = bson.getStringField(RegBinding::instrument_fld()); + + if (bson.hasField(RegBinding::localAddress_fld())) + _localAddress = bson.getStringField(RegBinding::localAddress_fld()); + + if (bson.hasField(RegBinding::timestamp_fld())) + _timestamp = bson.getIntField(RegBinding::timestamp_fld()); + + if (bson.hasField(RegBinding::expired_fld())) + _expired = bson.getBoolField(RegBinding::expired_fld()); + + +} + +mongo::BSONObj RegBinding::toBSONObj() const +{ + return BSON( + timestamp_fld() << static_cast(_timestamp) << + localAddress_fld() << _localAddress << + identity_fld() << _identity << + uri_fld() << _uri << + callId_fld() << _callId << + contact_fld() << _contact << + binding_fld() << _binding << + qvalue_fld() << _qvalue << + instanceId_fld() << _instanceId << + gruu_fld() << _gruu << + shardId_fld() << _shardId << + path_fld() << _path << + cseq_fld() << _cseq << + expirationTime_fld() << MongoDB::BaseDB::dateFromSecsSinceEpoch(_expirationTime) << + instrument_fld() << _instrument << + expired_fld() << _expired); +} + RegBinding::RegBinding(const mongo::BSONObj& bson) : _shardId(0), _cseq(0), @@ -97,109 +197,14 @@ RegBinding::RegBinding(const mongo::BSONObj& bson) : _timestamp(0), _expired(false) { - if (bson.hasField(RegBinding::identity_fld())) - _identity = bson.getStringField(RegBinding::identity_fld()); - - if (bson.hasField(RegBinding::uri_fld())) - _uri = bson.getStringField(RegBinding::uri_fld()); - - if (bson.hasField(RegBinding::callId_fld())) - _callId = bson.getStringField(RegBinding::callId_fld()); - - if (bson.hasField(RegBinding::contact_fld())) - _contact = bson.getStringField(RegBinding::contact_fld()); - - if (bson.hasField(RegBinding::binding_fld())) - _binding = bson.getStringField(RegBinding::binding_fld()); - - if (bson.hasField(RegBinding::qvalue_fld())) - _qvalue = bson.getStringField(RegBinding::qvalue_fld()); - - if (bson.hasField(RegBinding::instanceId_fld())) - _instanceId = bson.getStringField(RegBinding::instanceId_fld()); - - if (bson.hasField(RegBinding::gruu_fld())) - _gruu = bson.getStringField(RegBinding::gruu_fld()); - - if (bson.hasField(RegBinding::shardId_fld())) - _shardId = bson.getIntField(RegBinding::shardId_fld()); - - if (bson.hasField(RegBinding::path_fld())) - _path = bson.getStringField(RegBinding::path_fld()); - - if (bson.hasField(RegBinding::cseq_fld())) - _cseq = bson.getIntField(RegBinding::cseq_fld()); - - if (bson.hasField(RegBinding::expirationTime_fld())) - _expirationTime = bson.getIntField(RegBinding::expirationTime_fld()); - - if (bson.hasField(RegBinding::instrument_fld())) - _instrument = bson.getStringField(RegBinding::instrument_fld()); - - if (bson.hasField(RegBinding::localAddress_fld())) - _localAddress = bson.getStringField(RegBinding::localAddress_fld()); - - if (bson.hasField(RegBinding::timestamp_fld())) - _timestamp = bson.getIntField(RegBinding::timestamp_fld()); - - if (bson.hasField(RegBinding::expired_fld())) - _expired = bson.getBoolField(RegBinding::expired_fld()); + fromBSONObj(bson); } RegBinding& RegBinding::operator=(const mongo::BSONObj& bson) { - if (bson.hasField(RegBinding::identity_fld())) - _identity = bson.getStringField(RegBinding::identity_fld()); - - if (bson.hasField(RegBinding::uri_fld())) - _uri = bson.getStringField(RegBinding::uri_fld()); - - if (bson.hasField(RegBinding::callId_fld())) - _callId = bson.getStringField(RegBinding::callId_fld()); - - if (bson.hasField(RegBinding::contact_fld())) - _contact = bson.getStringField(RegBinding::contact_fld()); - - if (bson.hasField(RegBinding::binding_fld())) - _binding = bson.getStringField(RegBinding::binding_fld()); - - if (bson.hasField(RegBinding::qvalue_fld())) - _qvalue = bson.getStringField(RegBinding::qvalue_fld()); - - if (bson.hasField(RegBinding::instanceId_fld())) - _instanceId = bson.getStringField(RegBinding::instanceId_fld()); - - if (bson.hasField(RegBinding::gruu_fld())) - _gruu = bson.getStringField(RegBinding::gruu_fld()); + fromBSONObj(bson); - if (bson.hasField(RegBinding::shardId_fld())) - _shardId = bson.getIntField(RegBinding::shardId_fld()); - - if (bson.hasField(RegBinding::shardId_fld())) - _shardId = bson.getIntField(RegBinding::shardId_fld()); - - if (bson.hasField(RegBinding::path_fld())) - _path = bson.getStringField(RegBinding::path_fld()); - - if (bson.hasField(RegBinding::cseq_fld())) - _cseq = bson.getIntField(RegBinding::cseq_fld()); - - if (bson.hasField(RegBinding::expirationTime_fld())) - _expirationTime = bson.getIntField(RegBinding::expirationTime_fld()); - - if (bson.hasField(RegBinding::instrument_fld())) - _instrument = bson.getStringField(RegBinding::instrument_fld()); - - if (bson.hasField(RegBinding::localAddress_fld())) - _localAddress = bson.getStringField(RegBinding::localAddress_fld()); - - if (bson.hasField(RegBinding::timestamp_fld())) - _timestamp = bson.getIntField(RegBinding::timestamp_fld()); - - if (bson.hasField(RegBinding::expired_fld())) - _expired = bson.getBoolField(RegBinding::expired_fld()); - - return *this; + return *this; } RegBinding& RegBinding::operator=(const RegBinding& binding) diff --git a/sipXcommserverLib/src/sipdb/RegDB.cpp b/sipXcommserverLib/src/sipdb/RegDB.cpp index e36a893cfd..90af898028 100755 --- a/sipXcommserverLib/src/sipdb/RegDB.cpp +++ b/sipXcommserverLib/src/sipdb/RegDB.cpp @@ -26,9 +26,7 @@ using namespace std; const string RegDB::NS("node.registrar"); -extern mongo::DBConnectionPool pool; - -RegDB* RegDB::CreateInstance() { +RegDB* RegDB::CreateInstance(bool ensureIndexes) { RegDB* lRegDb = NULL; MongoDB::ConnectionInfo local = MongoDB::ConnectionInfo::localInfo(); @@ -36,6 +34,12 @@ RegDB* RegDB::CreateInstance() { Os::Logger::instance().log(FAC_SIP, PRI_INFO, "Regional database defined"); Os::Logger::instance().log(FAC_SIP, PRI_INFO, local.getConnectionString().toString().c_str()); lRegDb = new RegDB(local); + + // ensure indexes, if requested + if (ensureIndexes) + { + lRegDb->ensureIndexes(); + } } else { Os::Logger::instance().log(FAC_SIP, PRI_INFO, "No regional database found"); } @@ -43,9 +47,58 @@ RegDB* RegDB::CreateInstance() { MongoDB::ConnectionInfo global = MongoDB::ConnectionInfo::globalInfo(); RegDB* regDb = new RegDB(global, lRegDb); + // ensure indexes, if requested + if (ensureIndexes) + { + regDb->ensureIndexes(); + } return regDb; } +// +// Creates/updates the indexes needed for RegDB +// +void RegDB::ensureIndexes(mongo::DBClientBase* client) +{ + MongoDB::ScopedDbConnectionPtr conn(0); + mongo::DBClientBase* clientPtr = client; + + OS_LOG_INFO(FAC_SIP, "RegDB::ensureIndexes " + << "existing client: " << (client ? "yes" : "no") + << ", expireGracePeriod: " << _expireGracePeriod + << ", expirationTimeIndexTTL: " << _expirationTimeIndexTTL); + + // in case no client was provided, create a new connection + if (!clientPtr) + { + conn.reset(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); + clientPtr = conn->get(); + } + + clientPtr->ensureIndex(_ns, BSON(RegBinding::identity_fld() << 1 )); + clientPtr->ensureIndex(_ns, BSON(RegBinding::instrument_fld() << 1 )); + + // shape the new expirationtime index TTL + int newExpirationTimeIndexTTL = (mongoMod::EXPIRES_AFTER_SECONDS_MINIMUM_SECS > static_cast(_expireGracePeriod)) ? mongoMod::EXPIRES_AFTER_SECONDS_MINIMUM_SECS : static_cast(_expireGracePeriod); + + // Note: Since we're not allowed to create the same index using different parameters, + // we'll have to drop the existing index in case we're setting it for the first + // time or in case the expireGracePeriod was changed + if (newExpirationTimeIndexTTL != _expirationTimeIndexTTL) + { + _expirationTimeIndexTTL = newExpirationTimeIndexTTL; + safeDropIndex(clientPtr, RegBinding::expirationTime_fld()); + } + + safeEnsureTTLIndex(clientPtr, RegBinding::expirationTime_fld(), _expirationTimeIndexTTL); + + // close the connection, if it was created + if (conn) + { + conn->done(); + } +} + void RegDB::updateBinding(const RegBinding::Ptr& pBinding) { updateBinding(*(pBinding.get())); @@ -61,7 +114,9 @@ void RegDB::updateBinding(RegBinding& binding) MongoDB::UpdateTimer updateTimer(const_cast(*this)); if (binding.getTimestamp() == 0) + { binding.setTimestamp(OsDateTime::getSecsSinceEpoch()); + } if (binding.getLocalAddress().empty()) { @@ -86,45 +141,34 @@ void RegDB::updateBinding(RegBinding& binding) binding.setBinding(strm.str()); } - mongo::BSONObj query = BSON( - "identity" << binding.getIdentity() << - "contact" << binding.getContact() << - "shardId" << getShardId()); - - bool isExpired = binding.getExpirationTime() <= 0; - mongo::BSONObj update; - update = BSON( - "timestamp" << static_cast(binding.getTimestamp()) << - "localAddress" << binding.getLocalAddress() << - "identity" << binding.getIdentity() << - "uri" << binding.getUri() << - "callId" << binding.getCallId() << - "contact" << binding.getContact() << - "binding" << binding.getBinding() << - "qvalue" << binding.getQvalue() << - "instanceId" << binding.getInstanceId() << - "gruu" << binding.getGruu() << - "shardId" << getShardId() << - "path" << binding.getPath() << - "cseq" << binding.getCseq() << - "expirationTime" << static_cast(binding.getExpirationTime()) << - "instrument" << binding.getInstrument() << - "expired" << isExpired ); + binding.setShardId(getShardId()); - MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); - mongo::DBClientBase* client = conn->get(); + mongo::BSONObj query = BSON( + RegBinding::identity_fld() << binding.getIdentity() << + RegBinding::contact_fld() << binding.getContact() << + RegBinding::shardId_fld() << binding.getShardId()); - client->remove(_ns, query); - client->insert(_ns, update); - client->ensureIndex("node.registrar", BSON( "identity" << 1 )); - client->ensureIndex("node.registrar", BSON( "expirationTime" << 1 )); + bool isExpired = (binding.getExpirationTime() == 0); + binding.setExpired(isExpired); - string e = client->getLastError(); - if( !e.empty() ) { - Os::Logger::instance().log(FAC_SIP, PRI_ERR, e.c_str()); - } else { - Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "Save reg ok"); - } + mongo::BSONObj update = binding.toBSONObj(); + + MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); + mongo::DBClientBase* client = conn->get(); + + client->remove(_ns, query); + client->insert(_ns, update); + ensureIndexes(client); + + string e = client->getLastError(); + if ( !e.empty() ) + { + Os::Logger::instance().log(FAC_SIP, PRI_ERR, e.c_str()); + } + else + { + Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "Save reg ok"); + } conn->done(); } @@ -139,17 +183,16 @@ void RegDB::expireOldBindings(const string& identity, const string& callId, unsi MongoDB::UpdateTimer updateTimer(const_cast(*this)); mongo::BSONObj query = BSON( - "identity" << identity << - "callId"<< callId << - "cseq" << BSON_LESS_THAN(cseq) << - "shardId" << getShardId()); + RegBinding::identity_fld() << identity << + RegBinding::callId_fld() << callId << + RegBinding::cseq_fld() << BSON_LESS_THAN(cseq) << + RegBinding::shardId_fld() << getShardId()); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); mongo::DBClientBase* client = conn->get(); client->remove(_ns, query); - client->ensureIndex("node.registrar", BSON( "identity" << 1 )); - client->ensureIndex("node.registrar", BSON( "expirationTime" << 1 )); + ensureIndexes(client); conn->done(); } @@ -164,15 +207,14 @@ void RegDB::expireAllBindings(const string& identity, const string& callId, unsi MongoDB::UpdateTimer updateTimer(const_cast(*this)); mongo::BSONObj query = BSON( - "shardId" << getShardId() << - "identity" << identity); + RegBinding::shardId_fld() << getShardId() << + RegBinding::identity_fld() << identity); - MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); - mongo::DBClientBase* client = conn->get(); + MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); + mongo::DBClientBase* client = conn->get(); - client->remove(_ns, query); - client->ensureIndex("node.registrar", BSON( "identity" << 1 )); - client->ensureIndex("node.registrar", BSON( "expirationTime" << 1 )); + client->remove(_ns, query); + ensureIndexes(client); conn->done(); } @@ -193,15 +235,14 @@ void RegDB::removeAllExpired() MongoDB::UpdateTimer updateTimer(const_cast(*this)); mongo::BSONObj query = BSON( - "shardId" << getShardId() << - "expirationTime" << BSON_LESS_THAN_EQUAL((long long)timeNow)); + RegBinding::shardId_fld() << getShardId() << + RegBinding::expirationTime_fld() << BSON_LESS_THAN_EQUAL(BaseDB::dateFromSecsSinceEpoch(timeNow))); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); mongo::DBClientBase* client = conn->get(); client->remove(_ns, query); - client->ensureIndex("node.registrar", BSON( "identity" << 1 )); - client->ensureIndex("node.registrar", BSON( "expirationTime" << 1 )); + ensureIndexes(client); conn->done(); } @@ -227,13 +268,13 @@ bool RegDB::isRegisteredBinding(const Url& curl, bool preferPrimary) binding << hostPort.data(); mongo::BSONObjBuilder query; - query.append("binding", binding.str()); + query.append(RegBinding::binding_fld(), binding.str()); if (_local) { preferPrimary = false; _local->isRegisteredBinding(curl, preferPrimary); - query.append("shardId", BSON("$ne" << _local->getShardId())); + query.append(RegBinding::shardId_fld(), BSON_NOT_EQUAL(_local->getShardId())); } MongoDB::ReadTimer readTimer(const_cast(*this)); @@ -245,11 +286,11 @@ bool RegDB::isRegisteredBinding(const Url& curl, bool preferPrimary) BaseDB::primaryPreferred(builder, query.obj()); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); - auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); + auto_ptr pCursor = conn->get()->query(_ns, readQueryMaxTimeMS(builder.obj()), 0, 0, 0, mongo::QueryOption_SlaveOk); if (!pCursor.get()) { - throw mongo::DBException("mongo query returned null cursor", 0); + throw mongo::DBException("mongo query returned null cursor", 0); } isRegistered = pCursor->more(); @@ -300,6 +341,84 @@ static void push_or_replace_binding(RegDB::Bindings& bindings, const RegBinding& bindings.push_back(binding); } +bool RegDB::getUnexpiredRegisteredBinding( + const Url& registeredBinding, + Bindings& bindings, + bool preferPrimary) +{ + bool isRegistered = false; + UtlString hostPort; + UtlString user; + registeredBinding.getHostWithPort(hostPort); + registeredBinding.getUserId(user); + + std::ostringstream binding; + binding << "sip:"; + if (!user.isNull()) + binding << user.data() << "@"; + binding << hostPort.data(); + + unsigned long timeNow = OsDateTime::getSecsSinceEpoch(); + + mongo::BSONObjBuilder query; + query.append(RegBinding::binding_fld(), binding.str()); + query.append(RegBinding::expirationTime_fld(), BSON_GREATER_THAN(BaseDB::dateFromSecsSinceEpoch(timeNow))); + + if (_local) + { + preferPrimary = false; + _local->getUnexpiredRegisteredBinding(registeredBinding, bindings, preferPrimary); + query.append(RegBinding::shardId_fld(), BSON_NOT_EQUAL(_local->getShardId())); + } + + MongoDB::ReadTimer readTimer(const_cast(*this)); + + mongo::BSONObjBuilder builder; + if (!preferPrimary) + BaseDB::nearest(builder, query.obj()); + else + BaseDB::primaryPreferred(builder, query.obj()); + + MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); + auto_ptr pCursor = conn->get()->query(_ns, readQueryMaxTimeMS(builder.obj()), 0, 0, 0, mongo::QueryOption_SlaveOk); + + if (!pCursor.get()) + { + throw mongo::DBException("mongo query returned null cursor", 0); + } + + if ((isRegistered = pCursor->more())) + { + RegBinding binding(pCursor->next()); + + if (binding.getExpirationTime() > timeNow) + { + OS_LOG_INFO(FAC_SIP, "RegDB::getUnexpiredRegisteredBinding " + << " Identity: " << binding.getIdentity() + << " Contact: " << binding.getContact() + << " Expires: " << binding.getExpirationTime() - OsDateTime::getSecsSinceEpoch() << " sec" + << " Call-Id: " << binding.getCallId()); + + push_or_replace_binding(bindings, binding); + } + else + { + OS_LOG_WARNING(FAC_SIP, "RegDB::getUnexpiredRegisteredBinding returned an expired record?!?!" + << " Identity: " << binding.getIdentity() + << " Contact: " << binding.getContact() + << " Call-Id: " << binding.getCallId() + << " Expires: " << binding.getExpirationTime() << " epoch" + << " TimeNow: " << timeNow << " epoch"); + } + } + + conn->done(); + + OS_LOG_INFO(FAC_SIP, "RegDB::getUnexpiredRegisteredBinding returning " << (isRegistered ? "TRUE" : "FALSE") << " for binding " << binding.str()); + + return isRegistered; +} + bool RegDB::getUnexpiredContactsUser(const string& identity, unsigned long timeNow, Bindings& bindings, bool preferPrimary) const { static string gruuPrefix = GRUU_PREFIX; @@ -307,13 +426,13 @@ bool RegDB::getUnexpiredContactsUser(const string& identity, unsigned long timeN bool isGruu = identity.substr(0, gruuPrefix.size()) == gruuPrefix; mongo::BSONObjBuilder query; - query.append("expirationTime", BSON_GREATER_THAN((long long)timeNow)); + query.append(RegBinding::expirationTime_fld(), BSON_GREATER_THAN(BaseDB::dateFromSecsSinceEpoch(timeNow))); if (_local) { preferPrimary = false; _local->getUnexpiredContactsUser(identity, timeNow, bindings, preferPrimary); - query.append("shardId", BSON("$ne" << _local->getShardId())); + query.append(RegBinding::shardId_fld(), BSON_NOT_EQUAL(_local->getShardId())); } MongoDB::ReadTimer readTimer(const_cast(*this)); @@ -322,10 +441,10 @@ bool RegDB::getUnexpiredContactsUser(const string& identity, unsigned long timeN string searchString(identity); searchString += ";"; searchString += SIP_GRUU_URI_PARAM; - query.append("gruu", searchString); + query.append(RegBinding::gruu_fld(), searchString); } else { - query.append("identity", identity); + query.append(RegBinding::identity_fld(), identity); } mongo::BSONObjBuilder builder; @@ -335,7 +454,7 @@ bool RegDB::getUnexpiredContactsUser(const string& identity, unsigned long timeN BaseDB::primaryPreferred(builder, query.obj()); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); - auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); + auto_ptr pCursor = conn->get()->query(_ns, readQueryMaxTimeMS(builder.obj()), 0, 0, 0, mongo::QueryOption_SlaveOk); if (!pCursor.get()) { throw mongo::DBException("mongo query returned null cursor", 0); @@ -381,13 +500,13 @@ bool RegDB::getUnexpiredContactsUser(const string& identity, unsigned long timeN bool RegDB::getUnexpiredContactsUserContaining(const string& matchIdentity, unsigned long timeNow, Bindings& bindings, bool preferPrimary) const { mongo::BSONObjBuilder query; - query.append("expirationTime", BSON_GREATER_THAN((long long)timeNow)); + query.append(RegBinding::expirationTime_fld(), BSON_GREATER_THAN(BaseDB::dateFromSecsSinceEpoch(timeNow))); if (_local) { preferPrimary = false; _local->getUnexpiredContactsUserContaining(matchIdentity, timeNow, bindings, preferPrimary); - query.append("shardId", BSON("$ne" << _local->getShardId())); + query.append(RegBinding::shardId_fld(), BSON_NOT_EQUAL(_local->getShardId())); } MongoDB::ReadTimer readTimer(const_cast(*this)); @@ -399,7 +518,7 @@ bool RegDB::getUnexpiredContactsUserContaining(const string& matchIdentity, unsi BaseDB::primaryPreferred(builder, query.obj()); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); - auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); + auto_ptr pCursor = conn->get()->query(_ns, readQueryMaxTimeMS(builder.obj()), 0, 0, 0, mongo::QueryOption_SlaveOk); if (!pCursor.get()) { @@ -453,15 +572,15 @@ bool RegDB::getUnexpiredContactsUserInstrument(const string& identity, const str Bindings& bindings, bool preferPrimary) const { mongo::BSONObjBuilder query; - query.append("identity", identity); - query.append("instrument", instrument); - query.append("expirationTime", BSON_GREATER_THAN((long long)timeNow)); + query.append(RegBinding::identity_fld(), identity); + query.append(RegBinding::instrument_fld(), instrument); + query.append(RegBinding::expirationTime_fld(), BSON_GREATER_THAN(BaseDB::dateFromSecsSinceEpoch(timeNow))); if (_local) { preferPrimary = false; _local->getUnexpiredContactsUserInstrument(identity, instrument, timeNow, bindings, preferPrimary); - query.append("shardId", BSON("$ne" << _local->getShardId())); + query.append(RegBinding::shardId_fld(), BSON_NOT_EQUAL(_local->getShardId())); } MongoDB::ReadTimer readTimer(const_cast(*this)); @@ -473,7 +592,7 @@ bool RegDB::getUnexpiredContactsUserInstrument(const string& identity, const str BaseDB::primaryPreferred(builder, query.obj()); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); - auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); + auto_ptr pCursor = conn->get()->query(_ns, readQueryMaxTimeMS(builder.obj()), 0, 0, 0, mongo::QueryOption_SlaveOk); if (!pCursor.get()) { throw mongo::DBException("mongo query returned null cursor", 0); @@ -497,14 +616,14 @@ bool RegDB::getUnexpiredContactsUserInstrument(const string& identity, const str bool RegDB::getUnexpiredContactsInstrument(const string& instrument, unsigned long timeNow, Bindings& bindings, bool preferPrimary) const { mongo::BSONObjBuilder query; - query.append("instrument", instrument); - query.append("expirationTime", BSON_GREATER_THAN((long long)timeNow)); + query.append(RegBinding::instrument_fld(), instrument); + query.append(RegBinding::expirationTime_fld(), BSON_GREATER_THAN(BaseDB::dateFromSecsSinceEpoch(timeNow))); if (_local) { preferPrimary = false; _local->getUnexpiredContactsInstrument(instrument, timeNow, bindings, preferPrimary); - query.append("shardId", BSON("$ne" << _local->getShardId())); + query.append(RegBinding::shardId_fld(), BSON_NOT_EQUAL(_local->getShardId())); } MongoDB::ReadTimer readTimer(const_cast(*this)); @@ -516,7 +635,7 @@ bool RegDB::getUnexpiredContactsInstrument(const string& instrument, unsigned lo BaseDB::primaryPreferred(builder, query.obj()); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); - auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); + auto_ptr pCursor = conn->get()->query(_ns, readQueryMaxTimeMS(builder.obj()), 0, 0, 0, mongo::QueryOption_SlaveOk); if (!pCursor.get()) { throw mongo::DBException("mongo query returned null cursor", 0); @@ -543,11 +662,12 @@ void RegDB::cleanAndPersist(int currentExpireTime) return; } - MongoDB::UpdateTimer updateTimer(const_cast(*this)); - mongo::BSONObj query = BSON( - "expirationTime" << BSON_LESS_THAN(currentExpireTime)); - MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); - conn->get()->remove(_ns, query); + MongoDB::UpdateTimer updateTimer(const_cast(*this)); + + mongo::BSONObj query = BSON(RegBinding::expirationTime_fld() << BSON_LESS_THAN(BaseDB::dateFromSecsSinceEpoch(currentExpireTime))); + MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); + + conn->get()->remove(_ns, query); conn->done(); } @@ -560,8 +680,8 @@ void RegDB::clearAllBindings() MongoDB::UpdateTimer updateTimer(const_cast(*this)); - mongo::BSONObj all; - MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); - conn->get()->remove(_ns, all); - conn->done(); + mongo::BSONObj all; + MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); + conn->get()->remove(_ns, all); + conn->done(); } diff --git a/sipXcommserverLib/src/sipdb/SubscribeDB.cpp b/sipXcommserverLib/src/sipdb/SubscribeDB.cpp index c6ecf14f25..5458e51fdf 100755 --- a/sipXcommserverLib/src/sipdb/SubscribeDB.cpp +++ b/sipXcommserverLib/src/sipdb/SubscribeDB.cpp @@ -27,30 +27,44 @@ using namespace std; const string SubscribeDB::NS("node.subscription"); -SubscribeDB* SubscribeDB::CreateInstance() { - SubscribeDB* ldb = NULL; +SubscribeDB* SubscribeDB::CreateInstance(bool ensureIndexes) +{ + SubscribeDB* ldb = NULL; + + MongoDB::ConnectionInfo local = MongoDB::ConnectionInfo::localInfo(); + if (!local.isEmpty()) + { + ldb = new SubscribeDB(local); + + // ensure the indexes + if (ensureIndexes) + { + ldb->ensureIndexes(); + } + } - MongoDB::ConnectionInfo local = MongoDB::ConnectionInfo::localInfo(); - if (!local.isEmpty()) { - ldb = new SubscribeDB(local); - } + MongoDB::ConnectionInfo global = MongoDB::ConnectionInfo::globalInfo(); + SubscribeDB* db = new SubscribeDB(global, ldb); - MongoDB::ConnectionInfo global = MongoDB::ConnectionInfo::globalInfo(); - SubscribeDB* db = new SubscribeDB(global, ldb); + // ensure the indexes + if (ensureIndexes) + { + db->ensureIndexes(); + } - return db; + return db; } void SubscribeDB::getAll(Subscriptions& subscriptions, bool preferPrimary) { - mongo::BSONObjBuilder query; - MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); - if (_local) { - preferPrimary = false; - _local->getAll(subscriptions, preferPrimary); - query.append(Subscription::shardId_fld(), BSON("$ne" << getShardId())); - } + mongo::BSONObjBuilder query; + if (_local) { + preferPrimary = false; + _local->getAll(subscriptions, preferPrimary); + query.append(Subscription::shardId_fld(), BSON_NOT_EQUAL(getShardId())); + } + MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); MongoDB::ReadTimer readTimer(const_cast(*this)); mongo::BSONObjBuilder builder; @@ -60,17 +74,17 @@ void SubscribeDB::getAll(Subscriptions& subscriptions, bool preferPrimary) BaseDB::nearest(builder, query.obj()); - auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); + auto_ptr pCursor = conn->get()->query(_ns, readQueryMaxTimeMS(builder.obj()), 0, 0, 0, mongo::QueryOption_SlaveOk); if (!pCursor.get()) { - throw mongo::DBException("mongo query returned null cursor", 0); + throw mongo::DBException("mongo query returned null cursor", 0); } while (pCursor->more()) - { - subscriptions.push_back(Subscription(pCursor->next())); - } - conn->done(); + { + subscriptions.push_back(Subscription(pCursor->next())); + } + conn->done(); } void SubscribeDB::upsert ( @@ -115,17 +129,17 @@ void SubscribeDB::upsert ( MongoDB::UpdateTimer updateTimer(const_cast(*this)); mongo::BSONObj query = BSON( - "toUri" << toUri.str() << - "fromUri" << fromUri.str() << - "callId" << callId.str() << - "eventTypeKey" << eventTypeKey.str()); + Subscription::toUri_fld() << toUri.str() << + Subscription::fromUri_fld() << fromUri.str() << + Subscription::callId_fld() << callId.str() << + Subscription::eventTypeKey_fld() << eventTypeKey.str()); mongo::BSONObjBuilder objBuilder; objBuilder.append(Subscription::component_fld(), component.str()); objBuilder.append(Subscription::uri_fld(), uri.str()); objBuilder.append(Subscription::callId_fld(), callId.str()); objBuilder.append(Subscription::contact_fld(), contact.str()); - objBuilder.append(Subscription::expires_fld(), expires); + objBuilder.append(Subscription::expires_fld(), BaseDB::dateFromSecsSinceEpoch(expires)); objBuilder.append(Subscription::subscribeCseq_fld(), subscribeCseq); objBuilder.append(Subscription::eventTypeKey_fld(), eventTypeKey.str()); objBuilder.append(Subscription::eventType_fld(), eventType.str()); @@ -158,17 +172,45 @@ void SubscribeDB::upsert ( MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); mongo::DBClientBase* client = conn->get(); client->update(_ns, query, update, true, false); - ensureIndex(client); + ensureIndexes(client); conn->done(); - - removeAllExpired(); } -void SubscribeDB::ensureIndex(mongo::DBClientBase* client) const { - client->ensureIndex("node.subscription", BSON( "expires" << 1 )); - client->ensureIndex("node.subscription", BSON( "key" << 1 )); - client->ensureIndex("node.subscription", BSON( "toUri" << 1 )); - client->ensureIndex("node.subscription", BSON( "shardId" << 1 )); +void SubscribeDB::ensureIndexes(mongo::DBClientBase* client) +{ + MongoDB::ScopedDbConnectionPtr conn(0); + mongo::DBClientBase* clientPtr = client; + + OS_LOG_INFO(FAC_SIP, "SubscribeDB::ensureIndexes " + << "existing client: " << (client ? "yes" : "no") + << ", isFirstEnsureIndexes: " << _isFirstEnsureIndexes); + + // in case no client was provided, create a new connection + if (!clientPtr) + { + conn.reset(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); + clientPtr = conn->get(); + } + + clientPtr->ensureIndex(_ns, BSON(Subscription::key_fld() << 1)); + clientPtr->ensureIndex(_ns, BSON(Subscription::toUri_fld() << 1)); + clientPtr->ensureIndex(_ns, BSON(Subscription::shardId_fld() << 1)); + + // drop the 'expires' index in case this is the 1st call + if (_isFirstEnsureIndexes) + { + safeDropIndex(clientPtr, Subscription::expires_fld()); + _isFirstEnsureIndexes = false; + } + + // recreate the 'expires' index by also specifying TTL + safeEnsureTTLIndex(clientPtr, Subscription::expires_fld(), mongoMod::EXPIRES_AFTER_SECONDS_MINIMUM_SECS); + + // close the connection, if it was created + if (conn) + { + conn->done(); + } } //delete methods - delete a subscription session @@ -232,11 +274,11 @@ bool SubscribeDB::subscriptionExists ( query.append(Subscription::toUri_fld(), toUri.str()); query.append(Subscription::fromUri_fld(), fromUri.str()); query.append(Subscription::callId_fld(), callId.str()); - query.append(Subscription::expires_fld(), BSON_GREATER_THAN_EQUAL((long long)timeNow)); + query.append(Subscription::expires_fld(), BSON_GREATER_THAN_EQUAL(BaseDB::dateFromSecsSinceEpoch(timeNow))); if (_local) { - preferPrimary = false; + preferPrimary = false; if (_local->subscriptionExists(component, toUri, fromUri, callId, timeNow, preferPrimary)) { return true; @@ -247,24 +289,22 @@ bool SubscribeDB::subscriptionExists ( MongoDB::ReadTimer readTimer(const_cast(*this)); mongo::BSONObjBuilder builder; - if (preferPrimary) + if (preferPrimary) BaseDB::primaryPreferred(builder, query.obj()); else BaseDB::nearest(builder, query.obj()); - MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); -auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); - if (!pCursor.get()) - { - throw mongo::DBException("mongo query returned null cursor", 0); - } - else if (pCursor->more()) { - conn->done(); - return pCursor->itcount() > 0; - } + MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); + mongo::BSONObj subscriptionObj = conn->get()->findOne(_ns, readQueryMaxTimeMS(builder.obj()), 0, mongo::QueryOption_SlaveOk); + if (!subscriptionObj.isEmpty()) + { conn->done(); - return false; + return true; + } + + conn->done(); + return false; } //void SubscribeDB::removeRows(const UtlString& key) @@ -285,7 +325,7 @@ void SubscribeDB::removeExpired( const UtlString& component, const unsigned long mongo::BSONObj query = BSON( Subscription::component_fld() << component.str() << - Subscription::expires_fld() << BSON_LESS_THAN((long long)timeNow)); + Subscription::expires_fld() << BSON_LESS_THAN(BaseDB::dateFromSecsSinceEpoch(timeNow))); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); conn->get()->remove(_ns, query); conn->done(); @@ -299,7 +339,6 @@ void SubscribeDB::getUnexpiredSubscriptions ( Subscriptions& subscriptions, bool preferPrimary) { - removeAllExpired(); //query="key=",key,"and eventtypekey=",eventTypeKey; if (_local) { preferPrimary = false; @@ -312,6 +351,7 @@ void SubscribeDB::getUnexpiredSubscriptions ( query.append(Subscription::key_fld(), key.str()); query.append(Subscription::eventTypeKey_fld(), eventTypeKey.str()); query.append(Subscription::shardId_fld(), getShardId()); + query.append(Subscription::expires_fld(), BSON_GREATER_THAN(BaseDB::dateFromSecsSinceEpoch(timeNow))); mongo::BSONObjBuilder builder; if (preferPrimary) @@ -320,7 +360,7 @@ void SubscribeDB::getUnexpiredSubscriptions ( BaseDB::nearest(builder, query.obj()); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); - auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); + auto_ptr pCursor = conn->get()->query(_ns, readQueryMaxTimeMS(builder.obj()), 0, 0, 0, mongo::QueryOption_SlaveOk); if (!pCursor.get()) { throw mongo::DBException("mongo query returned null cursor", 0); @@ -339,15 +379,13 @@ void SubscribeDB::getUnexpiredContactsFieldsContaining( std::vector& matchingContactFields, bool preferPrimary ) const { - const_cast(this)->removeAllExpired(); - mongo::BSONObjBuilder query; - query.append(Subscription::expires_fld(), BSON_GREATER_THAN((long long)timeNow)); + query.append(Subscription::expires_fld(), BSON_GREATER_THAN(BaseDB::dateFromSecsSinceEpoch(timeNow))); if (_local) { preferPrimary = false; _local->getUnexpiredContactsFieldsContaining(substringToMatch, timeNow, matchingContactFields, preferPrimary); - query.append(Subscription::shardId_fld(), BSON("$ne" << getShardId())); + query.append(Subscription::shardId_fld(), BSON_NOT_EQUAL(getShardId())); } MongoDB::ReadTimer readTimer(const_cast(*this)); @@ -359,7 +397,7 @@ void SubscribeDB::getUnexpiredContactsFieldsContaining( BaseDB::nearest(builder, query.obj()); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); - auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); + auto_ptr pCursor = conn->get()->query(_ns, readQueryMaxTimeMS(builder.obj()), 0, 0, 0, mongo::QueryOption_SlaveOk); if (!pCursor.get()) { throw mongo::DBException("mongo query returned null cursor", 0); @@ -386,7 +424,7 @@ void SubscribeDB::updateNotifyUnexpiredSubscription( const UtlString& id, unsigned long timeNow, int updatedNotifyCseq, - int version) const + int version) { if (_local) { _local->updateNotifyUnexpiredSubscription( @@ -417,7 +455,7 @@ void SubscribeDB::updateNotifyUnexpiredSubscription( MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); mongo::DBClientBase* client = conn->get(); client->update(_ns, query, update); - ensureIndex(client); + ensureIndexes(client); conn->done(); } @@ -475,7 +513,7 @@ void SubscribeDB::updateNotifyUnexpiredSubscription( void SubscribeDB::updateToTag( const UtlString& callid, const UtlString& fromtag, - const UtlString& totag) const + const UtlString& totag) { if (_local) { _local->updateToTag(callid, fromtag, totag); @@ -487,7 +525,7 @@ void SubscribeDB::updateToTag( mongo::BSONObj query = BSON(Subscription::callId_fld() << callid.str()); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); mongo::DBClientBase* client = conn->get(); - auto_ptr pCursor = client->query(_ns, query); + auto_ptr pCursor = client->query(_ns, writeQueryMaxTimeMS(query)); if (!pCursor.get()) { throw mongo::DBException("mongo query returned null cursor", 0); @@ -523,7 +561,7 @@ void SubscribeDB::updateToTag( mongo::BSONObj update = BSON("$set" << BSON(Subscription::toUri_fld() << dummy.data())); client->update(_ns, query, update); - ensureIndex(client); + ensureIndexes(client); } else { @@ -553,7 +591,7 @@ bool SubscribeDB::findFromAndTo( if (_local->findFromAndTo(callid, fromtag, totag, from, to, preferPrimary)) { return true; } - query.append(Subscription::shardId_fld(), BSON("$ne" << getShardId())); + query.append(Subscription::shardId_fld(), BSON_NOT_EQUAL(getShardId())); } MongoDB::ReadTimer readTimer(const_cast(*this)); @@ -565,7 +603,7 @@ bool SubscribeDB::findFromAndTo( BaseDB::nearest(builder, query.obj()); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); - auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); + auto_ptr pCursor = conn->get()->query(_ns, readQueryMaxTimeMS(builder.obj()), 0, 0, 0, mongo::QueryOption_SlaveOk); if (!pCursor.get()) { throw mongo::DBException("mongo query returned null cursor", 0); @@ -611,7 +649,7 @@ int SubscribeDB::getMaxVersion(const UtlString& uri, bool preferPrimary) const if (_local) { preferPrimary = false; value = _local->getMaxVersion(uri, preferPrimary); - query.append(Subscription::shardId_fld(), BSON("$ne" << getShardId())); + query.append(Subscription::shardId_fld(), BSON_NOT_EQUAL(getShardId())); } MongoDB::ReadTimer readTimer(const_cast(*this)); @@ -623,7 +661,7 @@ int SubscribeDB::getMaxVersion(const UtlString& uri, bool preferPrimary) const BaseDB::nearest(builder, query.obj()); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getReadQueryTimeout())); - auto_ptr pCursor = conn->get()->query(_ns, builder.obj(), 0, 0, 0, mongo::QueryOption_SlaveOk); + auto_ptr pCursor = conn->get()->query(_ns, readQueryMaxTimeMS(builder.obj()), 0, 0, 0, mongo::QueryOption_SlaveOk); if (!pCursor.get()) { throw mongo::DBException("mongo query returned null cursor", 0); @@ -653,7 +691,7 @@ void SubscribeDB::removeAllExpired() mongo::BSONObj query = BSON( Subscription::shardId_fld() << getShardId() << - Subscription::expires_fld() << BSON_LESS_THAN_EQUAL((long long)timeNow)); + Subscription::expires_fld() << BSON_LESS_THAN_EQUAL(BaseDB::dateFromSecsSinceEpoch(timeNow))); MongoDB::ScopedDbConnectionPtr conn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString(), getWriteQueryTimeout())); conn->get()->remove(_ns, query); diff --git a/sipXcommserverLib/src/sipdb/Subscription.cpp b/sipXcommserverLib/src/sipdb/Subscription.cpp index 7ec0e37f32..eae8d12f6a 100755 --- a/sipXcommserverLib/src/sipdb/Subscription.cpp +++ b/sipXcommserverLib/src/sipdb/Subscription.cpp @@ -13,6 +13,8 @@ * details. */ +#include + #include "sipdb/Subscription.h" using namespace std; @@ -178,10 +180,33 @@ Subscription& Subscription::operator=(const mongo::BSONObj& bsonObj) if (bsonObj.hasField(Subscription::version_fld())) _version = bsonObj.getIntField(Subscription::version_fld()); - if (bsonObj.hasField(Subscription::expires_fld())) - _expires = bsonObj.getIntField(Subscription::expires_fld()); + if (bsonObj.hasField(Subscription::expires_fld())) + { + mongo::BSONElement expiresElement = bsonObj.getField(Subscription::expires_fld()); + + // save the time depending on its type + if (mongo::Date == expiresElement.type()) + { + _expires = static_cast(expiresElement.date().toTimeT()); + } + else if (expiresElement.isNumber()) + { + _expires = static_cast(expiresElement.Number()); + OS_LOG_WARNING(FAC_SIP, "Found old-style subscription" + << " Uri: " << _uri + << " Contact: " << _contact + << " Call-Id: " << _callId); + } + else + { + OS_LOG_ERROR(FAC_SIP, "unsupported " << expiresElement.toString() << " element for subscription" + << " Uri: " << _uri + << " Contact: " << _contact + << " Call-Id: " << _callId); + } + } - return *this; + return *this; } void Subscription::swap(Subscription& subscription) diff --git a/sipXcommserverLib/src/test/RegDBTest.cpp b/sipXcommserverLib/src/test/RegDBTest.cpp index ec9bee0713..73497dc0f4 100644 --- a/sipXcommserverLib/src/test/RegDBTest.cpp +++ b/sipXcommserverLib/src/test/RegDBTest.cpp @@ -268,7 +268,7 @@ class RegDBTest: public CppUnit::TestCase bool getAllOldBindings(int timeNow, RegDB::Bindings& bindings) { - mongo::BSONObj query = BSON( "expirationTime" << BSON_LESS_THAN(timeNow)); + mongo::BSONObj query = BSON( "expirationTime" << BSON_LESS_THAN(MongoDB::BaseDB::dateFromSecsSinceEpoch(timeNow))); MongoDB::ScopedDbConnectionPtr pConn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString())); auto_ptr pCursor = pConn->get()->query(_info.getNS(), query); if (pCursor.get() && pCursor->more()) diff --git a/sipXcommserverLib/src/test/sipdb/RegBindingTest.cpp b/sipXcommserverLib/src/test/sipdb/RegBindingTest.cpp index 683fd62af3..9dde5edd35 100644 --- a/sipXcommserverLib/src/test/sipdb/RegBindingTest.cpp +++ b/sipXcommserverLib/src/test/sipdb/RegBindingTest.cpp @@ -97,7 +97,7 @@ class RegBindingTest: public CppUnit::TestCase regBinding.gruu_fld() << regBinding.getGruu() << // "gruu" regBinding.path_fld() << regBinding.getPath() << // "path" regBinding.cseq_fld() << regBinding.getCseq() << // "cseq" - regBinding.expirationTime_fld() << (unsigned int)regBinding.getExpirationTime() << // "expirationTime" + regBinding.expirationTime_fld() << MongoDB::BaseDB::dateFromSecsSinceEpoch((unsigned int)regBinding.getExpirationTime()) << // "expirationTime" regBinding.instrument_fld() << regBinding.getInstrument() << // "instrument" regBinding.localAddress_fld() << regBinding.getLocalAddress() << // "localAddress" regBinding.timestamp_fld() << (unsigned int)regBinding.getTimestamp() << // "timestamp" diff --git a/sipXcommserverLib/src/test/sipdb/RegDBTest.cpp b/sipXcommserverLib/src/test/sipdb/RegDBTest.cpp index 9d92dff876..4199c58b09 100644 --- a/sipXcommserverLib/src/test/sipdb/RegDBTest.cpp +++ b/sipXcommserverLib/src/test/sipdb/RegDBTest.cpp @@ -375,7 +375,7 @@ class RegDBTest: public CppUnit::TestCase bool getAllOldBindings(int timeNow, RegDB::Bindings& bindings) { - mongo::BSONObj query = BSON( RegBinding::expirationTime_fld() << BSON_LESS_THAN((long long)timeNow)); + mongo::BSONObj query = BSON( RegBinding::expirationTime_fld() << BSON_LESS_THAN(MongoDB::BaseDB::dateFromSecsSinceEpoch((long long)timeNow))); MongoDB::ScopedDbConnectionPtr pConn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString())); auto_ptr pCursor = pConn->get()->query(_databaseName, query); if (pCursor.get() && pCursor->more()) @@ -386,7 +386,7 @@ class RegDBTest: public CppUnit::TestCase bindings.push_back(binding); } pConn->done(); - return bindings.size() > 0;; + return bindings.size() > 0; } pConn->done(); diff --git a/sipXcommserverLib/src/test/sipdb/RegExpireThreadTest.cpp b/sipXcommserverLib/src/test/sipdb/RegExpireThreadTest.cpp index 4d2a663aba..e9d6a07265 100644 --- a/sipXcommserverLib/src/test/sipdb/RegExpireThreadTest.cpp +++ b/sipXcommserverLib/src/test/sipdb/RegExpireThreadTest.cpp @@ -126,7 +126,7 @@ class RegExpireThreadTest: public CppUnit::TestCase bool getAllOldBindings(int timeNow, RegDB::Bindings& bindings) { - mongo::BSONObj query = BSON(RegBinding::expirationTime_fld() << BSON_LESS_THAN((long long)timeNow)); + mongo::BSONObj query = BSON(RegBinding::expirationTime_fld() << BSON_LESS_THAN(MongoDB::BaseDB::dateFromSecsSinceEpoch((long long)timeNow))); MongoDB::ScopedDbConnectionPtr pConn(mongoMod::ScopedDbConnection::getScopedDbConnection(_info.getConnectionString().toString())); auto_ptr pCursor = pConn->get()->query(_databaseName, query); if (pCursor.get() && pCursor->more()) diff --git a/sipXcommserverLib/src/test/sipdb/SubscriptionTest.cpp b/sipXcommserverLib/src/test/sipdb/SubscriptionTest.cpp index ef5ec3aced..85d0cff319 100644 --- a/sipXcommserverLib/src/test/sipdb/SubscriptionTest.cpp +++ b/sipXcommserverLib/src/test/sipdb/SubscriptionTest.cpp @@ -121,7 +121,7 @@ class SubscriptionTest: public CppUnit::TestCase subscription.notifyCseq_fld() << subscription.notifyCseq() << // "notifyCseq" subscription.subscribeCseq_fld() << subscription.subscribeCseq() << // "subscribeCseq" subscription.version_fld() << subscription.version() << // "version" - subscription.expires_fld() << subscription.expires(); // "expires" + subscription.expires_fld() << BaseDB::dateFromSecsSinceEpoch(subscription.expires()); // "expires" bsonObj = bsonObjBuilder.obj(); } diff --git a/sipXconfig/Makefile.am b/sipXconfig/Makefile.am index 53cb57e868..b8119fa2ec 100644 --- a/sipXconfig/Makefile.am +++ b/sipXconfig/Makefile.am @@ -53,6 +53,7 @@ pub-get $(SRC_PUB_PACKAGES) : pubspec.yaml remove-extraneous-packages-symlinks : find $(srcdir)/web \ -name packages -type l \ + -not -path '$(srcdir)/web/packages' -prune \ -not -path '$(srcdir)/web/context/WEB-INF/*' \ -exec rm {} \; diff --git a/sipXconfig/bin/Makefile.am b/sipXconfig/bin/Makefile.am index 802b625a46..2675936153 100644 --- a/sipXconfig/bin/Makefile.am +++ b/sipXconfig/bin/Makefile.am @@ -24,9 +24,8 @@ bin_SCRIPTS = \ mongodb-analyzer \ mongodb-admin \ sipxtrap \ - system-audit-cleanup \ - system-audit-cleanup.rb \ - sipx-dns-validator-regions + sipx-dns-validator-regions \ + postgres_change_pwd libexecdir = $(SIPX_LIBEXECDIR) libexec_SCRIPTS = \ diff --git a/sipXconfig/bin/postgres_change_pwd.in b/sipXconfig/bin/postgres_change_pwd.in new file mode 100644 index 0000000000..fd80eaf231 --- /dev/null +++ b/sipXconfig/bin/postgres_change_pwd.in @@ -0,0 +1,16 @@ +#!@BASH@ + +# Copyright (c) 2015 eZuce, Inc. All rights reserved. +# Contributed to SIPfoundry under a Contributor Agreement +# +# This software is free software; you can redistribute it and/or modify it under +# the terms of the Affero General Public License (AGPL) as published by the +# Free Software Foundation; either version 3 of the License, or (at your option) +# any later version. +# +# This software is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. + +su - postgres -c "psql -U postgres -d postgres -c \"alter user postgres with password '$1';\"" diff --git a/sipXconfig/bin/sipxconfig.in b/sipXconfig/bin/sipxconfig.in index bc1efc28a9..a3787a4d21 100644 --- a/sipXconfig/bin/sipxconfig.in +++ b/sipXconfig/bin/sipxconfig.in @@ -79,6 +79,12 @@ IndexDir=${TmpDir}/index Dependencies=`@SIPX_BINDIR@/java-dep -d ${CommonsLibDir} -d ${LibDir} sipxconfig sipxconfig-web @web_PKGS@ cdr-binding` export CLASSPATH=`echo @SIPX_CONFDIR@ ${Dependencies} ${PluginDir}/*.jar | sed -e 's/ /:/g'` +#source @SIPX_CONFDIR@/postgres-pwd.properties + +value=`cat @SIPX_CONFDIR@/postgres-pwd.properties` +prefix="password=" +password=${value#$prefix} + # Incase system failed suddenly, clear previous locks clearIndexLocks() { rm ${IndexDir}/*.lock 2>/dev/null @@ -102,6 +108,7 @@ databaseCommand() { -Dlog.dir=${LogDir} \ -Dtmp.dir=${TmpDir} \ -Dsipxconfig.db.user=@POSTGRESQL_USER@ \ + -Dsipxconfig.db.password='${password}' \ ${SIPXCONFIG_ANT_OPTS} \ -f @SIPX_CONFDIR@/database/database.xml \ $@" @@ -141,6 +148,7 @@ buildCommand() { -Djava.io.tmpdir=${TmpDir} \ -Djetty.lib.dir=@SIPX_JAVADIR@/sipXconfig \ -Djetty.conf.dir=@SIPX_CONFDIR@ \ + -Djetty.tmp.dir=@SIPX_TMPDIR@ \ -Djetty.log.dir=${LogDir} \ -Dorg.apache.lucene.lockdir=${IndexDir} \ -Dorg.apache.commons.loging.Log=org.apache.commons.logging.impl.Log4JLogger \ diff --git a/sipXconfig/bin/sipxconfig_archive.rb.in b/sipXconfig/bin/sipxconfig_archive.rb.in index 4057ee4426..da0f4bc390 100644 --- a/sipXconfig/bin/sipxconfig_archive.rb.in +++ b/sipXconfig/bin/sipxconfig_archive.rb.in @@ -8,7 +8,7 @@ class ArchiveBase def initialize @pguser = "postgres" @db = "SIPXCONFIG" - @tmpdir = "/tmp" + @tmpdir = "/var/sipxdata/tmp" @libexec = "@SIPX_LIBEXECDIR@" @initd = "@SIPX_SERVICEDIR@" @bindir = "@SIPX_BINDIR@" @@ -40,20 +40,38 @@ class ArchiveBase end class Backup < ArchiveBase - def add_additional_databases() + def add_additional_databases(args) end def run(params) - cmd "pg_dump -U #{@pguser} -F t #{@db} > db.tar" or + if params.has_key?(:tmp_dir) + @tmpdir=params[:tmp_dir] + end + cmd "echo Backup uses #{@tmpdir} as temporary dir." + cmd "pg_dump -U #{@pguser} -F t #{@db} --file=#{@tmpdir}/db.tar" or raise "Could not backup postgres database" - cmd "mongodump --db profiles --collection userProfile" or + cmd "mongodump --out #{@tmpdir}/dump --db profiles --collection userProfile" or raise "Could not backup user profiles database" - cmd "mongodump --db profiles --collection fs.files" or + cmd "mongodump --out #{@tmpdir}/dump --db profiles --collection fs.files" or raise "Could not backup avatar database" - cmd "mongodump --db profiles --collection fs.chunks" or + cmd "mongodump --out #{@tmpdir}/dump --db profiles --collection fs.chunks" or raise "Could not backup avatar database" - add_additional_databases() - cmd "echo @PACKAGE_VERSION@ > version" or + add_additional_databases(params) + + elasticsearch_started = cmd "service elasticsearch status" + elasticsearch_folder = "" + + if elasticsearch_started + cmd "curl -XPUT 'http://localhost:9200/_snapshot/sipXcom_backup?wait_for_completion=true' -d '{ \"type\": \"fs\", \"settings\": { \"location\": \"#{@tmpdir}/elasticsearch_backup/\", \"compress\": true }}'" or + raise "Could not create elasticsearch snapshot repository" + cmd "curl -XDELETE 'http://localhost:9200/_snapshot/sipXcom_backup/elasticsearch_backup?wait_for_completion=true' -d '{ \"indices\": \"audit\" }'" or + raise "Could delete old elasticsearch snapshot" + cmd "curl -XPUT 'http://localhost:9200/_snapshot/sipXcom_backup/elasticsearch_backup?wait_for_completion=true' -d '{ \"indices\": \"audit\" }'" or + raise "Could not create elasticsearch snapshot" + elasticsearch_folder="elasticsearch_backup/" + end + + cmd "echo @PACKAGE_VERSION@ > #{@tmpdir}/version" or raise "Could not generate version file" if params.has_key?(:no_device_files) @attachments = [ @@ -72,9 +90,12 @@ class Backup < ArchiveBase # by stripping @localstatedir@ we can backup/restore from/to source/production machines localstatedir = '@localstatedir@'[1..-1] - cmd "tar --transform='s,^#{localstatedir}/,var/,' -cvzf #{params[:file]} db.tar version dump/ #{attachments.join('/ ')}" or + cmd "tar --directory #{@tmpdir} --transform='s,^#{localstatedir}/,var/,' -cvzf #{params[:file]} db.tar version dump/ #{elasticsearch_folder} #{attachments.join('/ ')}" or raise "Could not archive databases" - FileUtils.rm_rf(['db.tar', 'version', 'dump']) + if elasticsearch_started + cmd "curl -XDELETE 'http://localhost:9200/_snapshot/sipXcom_backup?wait_for_completion=true'" + end + FileUtils.rm_rf(["#{@tmpdir}/db.tar", "#{@tmpdir}/version", "#{@tmpdir}/dump", "#{@tmpdir}/elasticsearch_backup"]) end end @@ -91,7 +112,12 @@ class Restore < ArchiveBase end def run(params) + if params.has_key?(:tmp_dir) + @tmpdir=params[:tmp_dir] + end + cmd "echo Restore uses #{@tmpdir} as temporary dir." pre_mongo = false + pre_elasticsearch = false backup_domain = nil backup_sip_domain = nil backup_host = nil @@ -105,6 +131,7 @@ class Restore < ArchiveBase v = File.read("#{@tmpdir}/version").split(".") dev = (v[0].to_i == '0'.to_i and v[1].to_i == '0'.to_i) pre_mongo = !(dev or (v[0].to_i == '4'.to_i and v[1].to_i > '4'.to_i) or v[0].to_i > '4'.to_i or v[0].to_i >= '14'.to_i) + pre_elasticsearch = !(dev or (v[0].to_i >= '15'.to_i and v[1].to_i >= '10'.to_i) or (v[0].to_i >= '16'.to_i)) else raise "Not a known archive, version file is missing." end @@ -128,21 +155,39 @@ class Restore < ArchiveBase cmd "SIPXCONFIG_ANT_OPTS=\"#{ant}\" #{@initd}/sipxconfig db upgrade" or raise "Failed to migrate database." - if !pre_mongo + if !pre_mongo cmd "tar -zxvf #{params[:file]} -C #{@tmpdir} dump" or raise "Failed to extract user profiles database from archive" + cmd "mongo profiles --eval \"db.userProfile.remove({})\"" cmd "mongorestore --db profiles --collection userProfile #{@tmpdir}/dump/profiles/userProfile.bson" or raise "Failed to restore user profiles database" + + cmd "mongo profiles --eval \"db.fs.files.remove({})\"" cmd "mongorestore --db profiles --collection fs.files #{@tmpdir}/dump/profiles/fs.files.bson" or cmd "echo Failed to restore avatar database" + + cmd "mongo profiles --eval \"db.fs.chunks.remove({})\"" cmd "mongorestore --db profiles --collection fs.chunks #{@tmpdir}/dump/profiles/fs.chunks.bson" or cmd "echo Failed to restore avatar database" - restore_additional_databases() + + restore_additional_databases() else sql_exec(@restoredb, "insert into setup(setup_id) values ('migrate_profiles');") or raise "Failed to trigger user profile migration." end + elasticsearch_started = cmd "service elasticsearch status" + if (!pre_elasticsearch and elasticsearch_started) + cmd "tar -zxvf #{params[:file]} -C / elasticsearch_backup" or + cmd "echo Failed to extract elasticsearch backup from archive" + cmd "curl -XPUT 'http://localhost:9200/_snapshot/sipXcom_backup?wait_for_completion=true' -d '{ \"type\": \"fs\", \"settings\": { \"location\": \"#{@tmpdir}/elasticsearch_backup/\", \"compress\": true }}'" or + cmd "echo Could not create elasticsearch snapshot repository" + cmd "curl -XDELETE 'http://localhost:9200/audit?wait_for_completion=true'" or + cmd "echo Failed to remove old audit index" + cmd "curl -XPOST 'http://localhost:9200/_snapshot/sipXcom_backup/elasticsearch_backup/_restore?wait_for_completion=true'" or + cmd "echo Failed to restore elasticsearch snapshot" + end + if params.has_key?(:reset_password) sql_exec(@restoredb, "update users set pintoken = '#{params[:reset_password]}'") or raise "Failed to reset password." @@ -176,6 +221,9 @@ class Restore < ArchiveBase sql_exec(@restoredb, "select change_primary_ip_on_restore('#{params[:ipaddress]}');") or raise "Failed to update ip address." + sql_exec(@restoredb, "update park_orbit set location_id=(select location_id from location where primary_location=true) WHERE location_id IS NULL;") or + raise "Failed to update park orbit locations." + cmd "#{@initd}/sipxconfig stop" if params[:restart] cmd "dropdb -U #{@pguser} #{@db}" # alter database command need double quotes if db name is capitalized. Also, first set of double @@ -215,6 +263,7 @@ class Restore < ArchiveBase puts "Reconfig replica set: mongo --eval \"var oldHost=\\\"#{current_fqdn}\\\", newHost=\\\"#{new_fqdn}\\\"\" #{@bindir}/master-reconfig.js" cmd "mongo --eval \"var oldHost=\\\"#{current_fqdn}\\\", newHost=\\\"#{new_fqdn}\\\"\" #{@bindir}/master-reconfig.js" if params[:restart] end + FileUtils.rm_rf(["#{@tmpdir}/db.tar", "#{@tmpdir}/version", "#{@tmpdir}/dump", "#{@tmpdir}/elasticsearch_backup"]) end end @@ -246,6 +295,11 @@ class Fetcher @params[:no_device_files] = true } + opts.on_tail("--tmp-dir ", + "Temporary backup file location"){ |v| + @params[:tmp_dir] = v + } + opts.on("--restore ", "Restore the specified Configuration archive."){ |v| @operation = Restore.new diff --git a/sipXconfig/bin/sipxfreeswitch.in b/sipXconfig/bin/sipxfreeswitch.in index a4e9b1c7ea..de7cca66b3 100644 --- a/sipXconfig/bin/sipxfreeswitch.in +++ b/sipXconfig/bin/sipxfreeswitch.in @@ -22,6 +22,7 @@ DAEMON_ARGS="\ -conf @SIPX_CONFDIR@/freeswitch/conf \ -db @SIPX_VARDIR@/tmp/freeswitch \ -log @SIPX_LOGDIR@ \ + -run @SIPX_RUNDIR@ \ -htdocs @SIPX_CONFDIR@/freeswitch/conf/htdoc" DAEMON_START_ARGS="-nc -nonat \ -u @SIPXPBXUSER@ \ @@ -29,7 +30,7 @@ DAEMON_START_ARGS="-nc -nonat \ $DAEMON_ARGS" DAEMON_STOP_ARGS="-stop \ $DAEMON_ARGS" -PIDFILE=@SIPX_LOGDIR@/freeswitch.pid +PIDFILE=@SIPX_RUNDIR@/freeswitch.pid do_setlimits() { ulimit -c unlimited diff --git a/sipXconfig/bin/system-audit-cleanup.in b/sipXconfig/bin/system-audit-cleanup.in deleted file mode 100644 index 30f8fc911c..0000000000 --- a/sipXconfig/bin/system-audit-cleanup.in +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/env ruby - -# Copyright (c) 2013 Karel Electronics Corp. All rights reserved. -# Contributed to SIPfoundry under a Contributor Agreement -# -# This software is free software; you can redistribute it and/or modify it under -# the terms of the Affero General Public License (AGPL) as published by the -# Free Software Foundation; either version 3 of the License, or (at your option) -# any later version. -# -# This software is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -# details. - -# -# cleanup sipXconfig's system audit. Can be used as standalone -# script or within the larger sipx-backup context. -# -$LOAD_PATH.unshift File.expand_path(File.dirname($PROGRAM_NAME)) -require 'system-audit-cleanup' - -if __FILE__ == $0 - cleanup = SystemAuditCleanup.new - begin - cleanup.run() - exit 0 - rescue => e - puts e.message.capitalize - exit 1 - end -end diff --git a/sipXconfig/bin/system-audit-cleanup.rb.in b/sipXconfig/bin/system-audit-cleanup.rb.in deleted file mode 100644 index fb03e9b0dd..0000000000 --- a/sipXconfig/bin/system-audit-cleanup.rb.in +++ /dev/null @@ -1,18 +0,0 @@ -class SystemAuditCleanup - - def initialize - @pguser = "postgres" - @db = "SIPXCONFIG" - @sql = "select system_audit_cleanup();" - end - - def run() - cmd "psql -U #{@pguser} #{@db} -c \"#{@sql}\"" - end - - def cmd(cmd) - puts cmd if @verbose - rc = system(cmd) - return rc - end -end diff --git a/sipXconfig/configure.ac b/sipXconfig/configure.ac index 0f7d345d47..6ceeb4d41f 100644 --- a/sipXconfig/configure.ac +++ b/sipXconfig/configure.ac @@ -1,12 +1,12 @@ AC_PREREQ(2.57) -AC_INIT(sipXconfig, 15.04, sipx-dev@list.sipfoundry.org) +AC_INIT(sipXconfig, 16.12, sipx-dev@list.sipfoundry.org) AC_CONFIG_AUX_DIR(config) m4_include([config/general.m4]) m4_include([config/sipXlib.m4]) m4_include([config/java.m4]) m4_include([config/java2.m4]) m4_include([config/dart.m4]) -AM_INIT_AUTOMAKE(foreign tar-ustar) +AM_INIT_AUTOMAKE(foreign tar-pax) SFAC_AUTOMAKE_VERSION([1.6]) AC_CONFIG_SRCDIR([neoconf/src/org/sipfoundry/sipxconfig/common/User.java]) SFAC_INIT_FLAGS diff --git a/sipXconfig/etc/00_sipxconfig.cf b/sipXconfig/etc/00_sipxconfig.cf index 07e5cad65c..38cd4a2f38 100644 --- a/sipXconfig/etc/00_sipxconfig.cf +++ b/sipXconfig/etc/00_sipxconfig.cf @@ -16,6 +16,8 @@ # bundle agent 00_sipxconfig { + classes: + "postgres_pwd_conf" expression => fileexists("$(sipx.SIPX_CONFDIR)/postgres-pwd.properties"); methods: primary:: # normally not nec but mongodb need 00_ssh to run first @@ -45,7 +47,20 @@ bundle agent 00_sipxconfig { create => "true", perms => mog("644", "root", "root"), edit_defaults => empty, - edit_line => sipxconfig_config; + edit_line => sipxconfig_config; + postgres.!postgres_pwd_conf:: + "$(sipx.SIPX_CONFDIR)/postgres-pwd.properties" + comment => "initialize postgres pwd file config", + create => "true", + perms => mog("644", "$(sipx.SIPXPBXUSER)", "$(sipx.SIPXPBXGROUP)"), + edit_defaults => empty, + edit_line => empty_postgres_pwd; + postgres:: + "$(sipx.SIPX_CONFDIR)/postgres-pwd.properties" + comment => "change postgresql password", + copy_from => copy_from_cfdata("$(sipx.location_id)/postgres-pwd.properties"), + perms => mog("644", "$(sipx.SIPXPBXUSER)", "$(sipx.SIPXPBXGROUP)"), + classes => if_repaired("change_password"); commands: primary.setup:: @@ -97,3 +112,11 @@ mongoReplicaSetManager.primaryFqdn=$(sipx.host).$(sipx.net_domain) expand_scalars => "true", insert_type => "file"; } + +bundle edit_line empty_postgres_pwd { + insert_lines: + "password="; + delete_lines: + any:: + ".*"; +} diff --git a/sipXconfig/etc/Makefile.am b/sipXconfig/etc/Makefile.am index 15d92d62cd..bc4f259b72 100644 --- a/sipXconfig/etc/Makefile.am +++ b/sipXconfig/etc/Makefile.am @@ -36,7 +36,8 @@ dist_cfinputs_DATA = \ snmp.cf \ snmptrap.cf \ tftp.cf \ - zz_apache.cf + zz_apache.cf \ + elasticsearch.cf cfdefaultsdir = $(SIPX_CFDATA)/defaults cfdefaults_DATA = \ diff --git a/sipXconfig/etc/elasticsearch.cf b/sipXconfig/etc/elasticsearch.cf new file mode 100644 index 0000000000..27bc5252e6 --- /dev/null +++ b/sipXconfig/etc/elasticsearch.cf @@ -0,0 +1,112 @@ +# Copyright (c) 2015 eZuce, Inc. All rights reserved. +# Contributed to SIPfoundry under a Contributor Agreement + +# This software is free software; you can redistribute it and/or modify it under +# the terms of the Affero General Public License (AGPL) as published by the +# Free Software Foundation; either version 3 of the License, or (at your option) +# any later version. + +# This software is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. + +# +# Elasticsearch configuration for working with sipxecs +# + +bundle agent elasticsearch { + methods: + primary:: + "any" usebundle => elasticsearch_config; + fedora16_or_greater:: + "any" usebundle => "elasticsearch_systemctl_running"; + !fedora16_or_greater:: + "any" usebundle => "elasticsearch_sysv_running"; +} + +bundle agent elasticsearch_config { + files: + any:: + "/etc/elasticsearch/elasticsearch.yml", + comment => "configure elasticsearch", + create => "true", + perms => mog("644", "root", "root"), + edit_line => elasticsearch_config_file, + classes => if_repaired("restart_elasticsearch"); +} + +bundle edit_line elasticsearch_config_file { + vars: + "tmpDir" slist => readstringlist("$(sipx.SIPX_CFDATA)/$(sipx.location_id)/elasticsearch.yml.part", "tmpDir=", "\n", 1, 4000); + + delete_lines: + any:: + "path.repo.*"; + + insert_lines: + any:: + "path.repo: [\"$(tmpDir)\", \"$(tmpDir)/elasticsearch_backup\"]"; +} + +bundle agent elasticsearch_sysv_running { + vars: + any:: + "service" string => "/etc/init.d/elasticsearch"; + + methods: + !elasticsearch:: + "any" usebundle => rh_chkconfig_status("elasticsearch off"); + elasticsearch:: + "any" usebundle => rh_chkconfig_status("elasticsearch on"); + + commands: + (!elasticsearch|stop_sipxecs):: + "$(service)" + comment => "stop elasticsearch", + args => "stop"; + + elasticsearch:: + "$(service)" + comment => "start elasticsearch", + args => "start"; + + elasticsearch.restart_elasticsearch:: + "$(service)" + comment => "restart elasticsearch", + args => "restart"; +} + +bundle agent elasticsearch_systemctl_running { + vars: + any:: + "service" string => "elasticsearch.service"; + + classes: + fedora16_or_greater:: + "elasticsearch_running" expression => returnszero("$(sipx.systemctl) is-active $(service)","noshell"); + + commands: + (!elasticsearch|stop_sipxecs).elasticsearch_running:: + "$(sipx.systemctl)" + comment => "stop elasticsearch", + args => "stop $(service)"; + + "$(sipx.systemctl)" + comment => "disable elasticsearch", + args => "disable $(service)"; + + elasticsearch.!elasticsearch_running:: + "$(sipx.systemctl)" + comment => "start elasticsearch", + args => "start $(service)"; + + "$(sipx.systemctl)" + comment => "enable elasticsearch", + args => "enable $(service)"; + + elasticsearch.elasticsearch_running.restart_elasticsearch:: + "$(sipx.systemctl)" + comment => "restart elasticsearch", + args => "restart $(service)"; +} \ No newline at end of file diff --git a/sipXconfig/etc/mongodb.cf b/sipXconfig/etc/mongodb.cf index 8a221ce06d..8b9ad0f7de 100644 --- a/sipXconfig/etc/mongodb.cf +++ b/sipXconfig/etc/mongodb.cf @@ -112,7 +112,8 @@ bundle agent mongodb_arbiter_config { mongod_arbiter:: "dirs" slist => { "/var/lib/mongodb-arbiter", - "/var/run/$(mongodb.id)" + "/var/run/$(mongodb.id)", + "/var/log/mongodb/arbiter" }; files: @@ -129,6 +130,13 @@ bundle agent mongodb_arbiter_config { edit_defaults => empty, edit_line => mongodb_arbiter_config_contents, classes => if_repaired("restart_mongo_arbiter"); + + "/etc/logrotate.d/mongodb-arbiter" + comment => "Log rotation config for mongodb arbiter", + perms => mog("644", "root", "root"), + create => "true", + edit_defaults => empty, + edit_line => mongo_arbiter_logrotate_contents; } bundle edit_line mongodb_arbiter_config_contents { @@ -137,7 +145,7 @@ bundle edit_line mongodb_arbiter_config_contents { insert_lines: "replSet=sipxecs port=27018 -logpath=/var/log/$(mongodb.id)/mongod-arbiter.log +logpath=/var/log/$(mongodb.id)/arbiter/mongodb.log fork=true pidfilepath=/var/run/$(mongodb.id)/mongodb-arbiter.pid smallfiles=true @@ -148,12 +156,31 @@ bind_ip=0.0.0.0 "; } +bundle edit_line mongo_arbiter_logrotate_contents { + insert_lines: +"/var/log/$(mongodb.id)/arbiter/mongodb.log { + weekly + rotate 10 + copytruncate + delaycompress + compress + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/$(mongodb.id)/mongodb-arbiter.pid 2>/dev/null` 2> /dev/null|| true + endscript +} +"; +} + + bundle agent mongo_local_config { vars: any:: "dirs" slist => { "/var/lib/mongo-local", - "/var/run/$(mongodb.id)" + "/var/run/$(mongodb.id)", + "/var/log/mongodb/local" }; files: @@ -170,6 +197,13 @@ bundle agent mongo_local_config { edit_defaults => empty, edit_line => mongo_local_config_contents, classes => if_repaired("restart_mongo_local"); + + "/etc/logrotate.d/mongodb-local" + comment => "Log rotation config for local mongodb", + perms => mog("644", "root", "root"), + create => "true", + edit_defaults => empty, + edit_line => mongo_local_logrotate_contents; !mongo_local:: "/var/lib/mongo-local" @@ -201,7 +235,7 @@ bundle edit_line mongo_local_config_contents { "replSet=sipxlocal port=27019 logappend=true -logpath=/var/log/$(mongodb.id)/local.log +logpath=/var/log/$(mongodb.id)/local/mongodb.log fork=true smallfiles=true pidfilepath=/var/run/$(mongodb.id)/local.pid @@ -245,6 +279,23 @@ bundle agent mongo_local_sync { args => "restart"; } +bundle edit_line mongo_local_logrotate_contents { + insert_lines: +"/var/log/$(mongodb.id)/local/mongodb.log { + weekly + rotate 10 + copytruncate + delaycompress + compress + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/$(mongodb.id)/local.pid 2>/dev/null` 2> /dev/null|| true + endscript +} +"; +} + bundle agent mongo_local_arbiter_config { vars: any:: diff --git a/sipXconfig/etc/postgres.cf b/sipXconfig/etc/postgres.cf index 468c024bd0..c4eab14248 100644 --- a/sipXconfig/etc/postgres.cf +++ b/sipXconfig/etc/postgres.cf @@ -24,6 +24,8 @@ bundle agent postgres { "any" usebundle => postgres_sysctl_running; !fedora16_or_greater:: "any" usebundle => postgres_sysv_running; + postgres.change_password:: + "any" usebundle => postgres_activate_new_password; } bundle agent postgres_init { @@ -52,6 +54,7 @@ bundle agent postgres_init { # starting and stopping server. Although you could call # initdb directly there is various other housekeeping that # the service script handles so leverage it here. + fedora16_or_greater.initdb:: "/usr/bin/postgresql-setup" comment => "initialize postgres", @@ -89,6 +92,7 @@ bundle agent postgres_config { edit_line => replace_contents("-i"); } + bundle edit_line postgresql_conf { insert_lines: any:: @@ -120,12 +124,23 @@ bundle edit_line postgresql_conf { } bundle edit_line pg_hba_conf { + classes: + "trust" expression => strcmp("",'$(sipx.NEW_POSTGRESQL_PASSWORD)'); + "pwdfile_exists" expression => fileexists("$(sipx.SIPX_CFDATA)/$(sipx.location_id)/postgres-pwd.cfdat"); insert_lines: - "local all all trust"; - "host all all 127.0.0.1/32 trust"; - "host all all ::1/128 trust"; - # any node in the cluster can connect to postgresql - "host all all 0.0.0.0/0 trust"; + pwdfile_exists.!trust:: + "local all all md5"; + "host all all 127.0.0.1/32 md5"; + "host all all ::1/128 md5"; + # any node in the cluster can connect to postgresql + "host all all 0.0.0.0/0 md5"; + !pwdfile_exists|trust:: + "local all all trust"; + "host all all 127.0.0.1/32 trust"; + "host all all ::1/128 trust"; + # any node in the cluster can connect to postgresql + "host all all 0.0.0.0/0 trust"; + delete_lines: "(host|local).*"; } @@ -153,8 +168,13 @@ bundle agent postgres_sysv_running { "$(sipx.PG_DAEMON)" comment => "start postgres", args => "start"; + + postgres.change_password:: + "$(sipx.SIPX_BINDIR)/postgres_change_pwd" + comment => "change postgres password ", + args => '$(sipx.NEW_POSTGRESQL_PASSWORD)'; - postgres.postgres_running.restart_postgres:: + postgres.postgres_running.(restart_postgres|change_password):: "$(sipx.PG_DAEMON)" comment => "restart postgres", args => "restart"; @@ -187,9 +207,38 @@ bundle agent postgres_sysctl_running { "$(sipx.systemctl)" comment => "enable $(service)", args => "enable $(service)"; - - postgres.postgres_running.restart_postgres:: + + postgres.change_password:: + "$(sipx.SIPX_BINDIR)/postgres_change_pwd" + comment => "change postgres password", + args => '$(sipx.NEW_POSTGRESQL_PASSWORD)'; + + postgres.postgres_running.(restart_postgres|change_password):: "$(sipx.systemctl)" comment => "restart $(service)", - args => "restart $(service)"; -} \ No newline at end of file + args => "restart $(service)"; +} + +bundle agent postgres_activate_new_password { + files: + "/root/.pgpass" + comment => "create postgress pgpass file", + create => "true", + perms => mog("600", root, root), + edit_line => postgres_pgpass; + + "$(sipx.PGDATA)/../.pgpass" + comment => "create postgress pgpass file", + create => "true", + perms => mog("600", "$(sipx.POSTGRESQL_USER)","$(sipx.POSTGRESQL_GROUP)"), + edit_line => postgres_pgpass; +} + +bundle edit_line postgres_pgpass { + insert_lines: + any:: + 'localhost:5432:*:postgres:$(sipx.NEW_POSTGRESQL_PASSWORD)'; + delete_lines: + any:: + ".*"; +} diff --git a/sipXconfig/etc/sipxconfig.cf b/sipXconfig/etc/sipxconfig.cf index d82b73dd7c..9ba4e22eb3 100644 --- a/sipXconfig/etc/sipxconfig.cf +++ b/sipXconfig/etc/sipxconfig.cf @@ -64,7 +64,7 @@ bundle agent sipxconfig_running { comment => "start sipxconfig", args => "start"; - primary.sipxconfig_running.(restart_sipxconfig|restart_sipxecs|java_authorities_repaired):: + primary.sipxconfig_running.(restart_sipxconfig|restart_sipxecs|java_authorities_repaired|change_password):: "$(sipx.SIPX_SERVICEDIR)/sipxconfig" comment => "restart sipxconfig", args => "asyncrestart"; @@ -135,13 +135,9 @@ bundle edit_line sipx_log4j_properties_contents { bundle agent system_audit_cleanup { vars: # execute it every day after midnight - "cron" string => "20 0 * * * $(sipx.SIPX_BINDIR)/system-audit-cleanup &>> $(sipx.SIPX_LOGDIR)/system-audit-cleanup.log"; "cron_id" string => ".*/system-audit-cleanup .*"; #regex methods: - primary:: - "any" usebundle => add_crontab("$(cron_id)", "$(sipx.SIPXPBXUSER)", "$(cron)"); - - !primary:: + any:: "any" usebundle => remove_crontab("$(cron_id)", "$(sipx.SIPXPBXUSER)"); } diff --git a/sipXconfig/etc/sipxdns.cf b/sipXconfig/etc/sipxdns.cf index ae226af2d8..a6a313ddd3 100644 --- a/sipXconfig/etc/sipxdns.cf +++ b/sipXconfig/etc/sipxdns.cf @@ -25,7 +25,7 @@ bundle agent sipxdns { methods: !sipxdns_unmanaged:: - "any" usebundle => sipxecs_network_config; + "any" usebundle => sipxecs_network_config; "any" usebundle => network_scripts; sipxdns:: @@ -45,32 +45,35 @@ bundle agent sipxdns { } bundle agent sipxecs_network_config { + classes: + "sipx_interface" expression => fileexists("$(sipx.SIPX_CONFDIR)/sipxecs.cfg"); files: - "/etc/sipxecs.cfg" - comment => "create sipxecs network configuration file if not found", - perms => mog("644","root","root"), - create => "true"; + !sipx_interface:: + "$(sipx.SIPX_CONFDIR)/sipxecs.cfg" + comment => "create sipxecs network configuration file if not found", + perms => mog("644","root","root"), + create => "true", + edit_defaults => empty, + edit_line => empty_interface; } bundle agent network_scripts { vars: any:: - "entry" string => readfile("/etc/sipxecs.cfg", "400"); "default_entry" string => "/etc/sysconfig/network-scripts/ifcfg-eth0"; classes: - "network_interface_entry" not => strcmp("","$(entry)"); - "peerdns_added" and => {network_interface_entry, regline("PEERDNS=NO", "$(entry)")}; - "nmcontrolled_added" and => {network_interface_entry, regline("NM_CONTROLLED=NO", "$(entry)")}; - + "network_interface_entry" not => strcmp("","$(sipx.SIPX_INTERFACE)"); + "peerdns_added" and => {network_interface_entry, regline("PEERDNS=NO", "$(sipx.SIPX_INTERFACE)")}; + "nmcontrolled_added" and => {network_interface_entry, regline("NM_CONTROLLED=NO", "$(sipx.SIPX_INTERFACE)")}; "default_network_interface_file_exists" expression => fileexists("$(default_entry)"); "default_peerdns_added" and => {default_network_interface_file_exists, regline("PEERDNS=NO", "$(default_entry)")}; "default_nmcontrolled_added" and => {default_network_interface_file_exists, regline("NM_CONTROLLED=NO", "$(default_entry)")}; files: network_interface_entry.(!peerdns_added|!nm_controlled_added):: - "$(entry)" - comment => "add peerdns and nmcontrolled network settings in custom: $(entry)", + "$(sipx.SIPX_INTERFACE)" + comment => "add peerdns and nmcontrolled network settings in custom: $(sipx.SIPX_INTERFACE)", create => "false", edit_line => edit_network_scripts; @@ -91,9 +94,15 @@ bundle edit_line edit_network_scripts { "PEERDNS(.*)=(.*)"; "peerdns(.*)=(.*)"; "NM_CONTROLLED(.*)=(.*)"; - "nm_controlled(.*)=(.*)"; - - + "nm_controlled(.*)=(.*)"; +} + +bundle edit_line empty_interface { + insert_lines: + "=SIPX_INTERFACE="; + delete_lines: + any:: + ".*"; } bundle agent sipxdns_resolv { diff --git a/sipXconfig/etc/sipxfreeswitch.cf b/sipXconfig/etc/sipxfreeswitch.cf index 3d55ca8a65..5e1b97cc9e 100644 --- a/sipXconfig/etc/sipxfreeswitch.cf +++ b/sipXconfig/etc/sipxfreeswitch.cf @@ -29,7 +29,8 @@ bundle agent sipxfreeswitch_config { "conference_file" string => "$(sipx.SIPX_CONFDIR)/freeswitch/conf/autoload_configs/conference.conf.xml"; "xml_file_reload_only" slist => { - "dialplan/sipX_context.xml" + "dialplan/sipX_context.xml", + "autoload_configs/local_stream.conf.xml" }; "xml_file" slist => { @@ -39,9 +40,9 @@ bundle agent sipxfreeswitch_config { "autoload_configs/xml_rpc.conf.xml", "autoload_configs/switch.conf.xml", "autoload_configs/logfile.conf.xml", - "autoload_configs/local_stream.conf.xml", "autoload_configs/ivr.conf.xml", - "autoload_configs/erlang_event.conf.xml" + "autoload_configs/erlang_event.conf.xml", + "autoload_configs/shout.conf.xml" }; files: @@ -82,6 +83,47 @@ bundle agent sipxfreeswitch_config { perms => m("644"), edit_line => sipxfreeswitch_modules, classes => if_repaired("restart_sipxfreeswitch"); + + any:: + "$(sipx.SIPX_CONFDIR)/freeswitch/conf/" + comment => "copy mod_en configuration files in sipxecs config directory", + perms => m("644"), + copy_from => local_cp("/etc/freeswitch/lang/."), + depth_search => recurse("inf"); + + fs_core:: + "/etc/sysctl.conf" + comment => "Adding kernel network parameters $(this.promoiser)", + perms => mog("0644","root","root"), + edit_line => add_sysctl_conf_core(), + classes => if_repaired("update_sysctl_core"); + + !fs_core:: + "/etc/sysctl.conf" + comment => "Deleting kernel network parameters $(this.promoiser)", + perms => mog("0644","root","root"), + edit_line => delete_sysctl_conf_core(), + classes => if_repaired("update_sysctl_core"); + + commands: + update_sysctl_core:: + "/sbin/sysctl" + comment => "Apply kernel FS core parameters", + args => "-p"; +} + +bundle edit_line add_sysctl_conf_core() { + insert_lines: + "# sipxecs generated - core dumps"; + "fs.suid_dumpable=2"; + "kernel.core_pattern=/tmp/core"; +} + +bundle edit_line delete_sysctl_conf_core() { + delete_lines: + "# sipxecs generated - core dumps"; + "fs.suid_dumpable=2"; + "kernel.core_pattern=/tmp/core"; } bundle edit_line sipxfreeswitch_modules { @@ -89,7 +131,7 @@ bundle edit_line sipxfreeswitch_modules { "mods_part" string => "$(sipx.SIPX_CFDATA)/$(sipx.location_id)/modules.conf.xml.part"; classes: - "fs_g729" expression => fileexists("/usr/lib64/mod/mod_com_g729.so"); + "fs_g729" expression => fileexists("/usr/lib64/freeswitch/mod/mod_com_g729.so"); "has_mods_part" expression => fileexists("$(mods_part)"); insert_lines: @@ -151,9 +193,11 @@ any:: + + "; has_mods_part:: @@ -207,7 +251,7 @@ bundle agent sipxfreeswitch_running { comment => "reload freeswitch", args => "reload"; - sipxfreeswitch.sipxfreeswitch_running.(restart_sipxecs|restart_sipxfreeswitch):: + sipxfreeswitch.sipxfreeswitch_running.(restart_sipxecs|restart_sipxfreeswitch|restart_mediaservice|update_sysctl_core):: "$(sipx.SIPX_SERVICEDIR)/sipxfreeswitch" comment => "restart freeswitch", args => "asyncrestart"; diff --git a/sipXconfig/etc/sipxpbx/backup/backup.properties b/sipXconfig/etc/sipxpbx/backup/backup.properties index f19a0e2807..34f44df8a5 100644 --- a/sipXconfig/etc/sipxpbx/backup/backup.properties +++ b/sipXconfig/etc/sipxpbx/backup/backup.properties @@ -23,3 +23,10 @@ restore.resetPin.label=Reset Voicemail PINs restore.resetPin.description=Enable this if you want to restore and reset all voice mail PINs at the same time. PIN must be 4 characters or longer and most useful as numbers 0-9. restore.resetPassword.label=Reset passwords restore.resetPassword.description=Enable this if you want to restore and reset all user portal/IM/call center passwords at the same time. Password must be 8 characters or longer. +general.label=General Backup/Restore settings +general.tmpDir.label=Temporary files +general.tmpDir.description=Please make sure that this directory has all the required permissions (if you are unsure of what these permissions are, you can check out the default directory permissions).\ +Please wait for "Configuration deployment" to finish in Diagnostics/Job Status in order for the changes to take effect. \ +Note that this temporary directory will also be used in Restore operations. +general.mem.label=RAM Memory (MB) +general.mem.description=CGROUP option configuration for maximum RAM memory allocation for the backup and mongodump process diff --git a/sipXconfig/etc/sipxpbx/backup/backup.xml b/sipXconfig/etc/sipxpbx/backup/backup.xml index d3b155a5ed..534383ebc7 100644 --- a/sipXconfig/etc/sipxpbx/backup/backup.xml +++ b/sipXconfig/etc/sipxpbx/backup/backup.xml @@ -10,6 +10,16 @@ + + + + /var/sipxdata/tmp + + + + 250 + + diff --git a/sipXconfig/etc/sipxpbx/commserver/forwardingrules.vm b/sipXconfig/etc/sipxpbx/commserver/forwardingrules.vm index 618ffa247c..c7024d03fa 100644 --- a/sipXconfig/etc/sipxpbx/commserver/forwardingrules.vm +++ b/sipXconfig/etc/sipxpbx/commserver/forwardingrules.vm @@ -39,11 +39,36 @@ <${regEventAddress.stripProtocol()};transport=tcp> +#foreach($match in $subscribeFieldMatches) +#set ($routeTo = $match.getRouteTo($location)) + + ${match.fieldPattern} + <$routeTo> + + +#end <${regAddress.stripProtocol()};transport=tcp;x-sipx-routetoreg> +#if( ${addNotify} ) + + NOTIFY + +#foreach($match in $notifyFieldMatches) +#set ($routeTo = $match.getRouteTo($location)) + + ${match.fieldPattern} + <$routeTo> + + +#end + + +#end INVITE <${regAddress.stripProtocol()};transport=tcp;x-sipx-routetoreg> diff --git a/sipXconfig/etc/sipxpbx/commserver/user-settings.properties b/sipXconfig/etc/sipxpbx/commserver/user-settings.properties index 360b4c1395..aa1c6c7f9f 100644 --- a/sipXconfig/etc/sipxpbx/commserver/user-settings.properties +++ b/sipXconfig/etc/sipxpbx/commserver/user-settings.properties @@ -93,20 +93,16 @@ voicemail.mailbox.voicemail-tui.description=Select the voicemail Telephony User voicemail.mailbox.voicemail-tui.label.stdui=Standard voicemail.mailbox.voicemail-tui.label.cpui=CallPilot -voicemail.imap.label=IMAP -voicemail.imap.description=Configuring IMAP parameters allows users to synchronize their voicemail and E-mail accounts. - -voicemail.imap.host.label=IMAP server host -voicemail.imap.host.description= - -voicemail.imap.port.label=IMAP server port -voicemail.imap.port.description= - -voicemail.imap.tls.label=TLS -voicemail.imap.tls.description=Use secure (TLS) connection for IMAP server - -voicemail.imap.password.label=IMAP Server Password -voicemail.imap.password.description= +voicemail.security.auto-enter-pin-extension.label=Auto Enter PIN from user Extension +voicemail.security.auto-enter-pin-extension.description=When a user dials voicemail pilot number from their extension this option will \ +not require user to enter PIN but instead directly access their voicemail. +voicemail.security.auto-enter-pin-external.label=Auto Enter PIN from External # +voicemail.security.auto-enter-pin-external.description=When a user dials voicemail pilot number from their Cell Phone or Home Phone \ +(as defined on user's contact information) this option will not require user to enter PIN but instead directly access their voicemail. +voicemail.security.days-to-keep-vm.label=Days to keep voicemails and conference recordings +voicemail.security.days-to-keep-vm.description=The amount of days a voicemail will be stored before automatically removed. \ +This expiration limit would be imposed on any voicemail within any folder and would also be applied to conference recordings. \ +If set to default(0) the voicemails will be kept indefinitely. voicemail.fax.label=Fax voicemail.fax.description=Faxes cannot be received until a primary or an alternate email address is defined. @@ -181,10 +177,125 @@ moh.audio-source.label.SYSTEM_DEFAULT=Use System Configuration moh.audio-source.label.NONE=None timezone.timezone.label=Time zone -timezone.useBranchTimezone.label=Use branch time zone -timezone.useBranchTimezone.description=Check this if the user is in the same time zone of the branch +timezone.useBranchTimezone.label=Use location time zone +timezone.useBranchTimezone.description=Check this if the user is in the same time zone of the location hotelling.label=Hoteling hotelling.enable.label=Enable hotelling.enable.description=When enabling, a user profile will be created in tftproot folder. \ User will be able to log in on any Polycom phone in the system that has the hoteling feature enabled. + +label.inheritLocation=Auto Assign Location +description.inheritLocation=Automatically assign the Location for the Conference bridge to the Location specified by this User Group. + +user-portal.label=User Portal +user-portal.description= + +user-portal.dial-pad-search.label=Disable Dial Pad and Search Icons +user-portal.dial-pad-search.description= + +user-portal.dial-pad-search.dial-pad.label=Enable Dial Pad Icon +user-portal.dial-pad-search.dial-pad.description=Enable or disable dial pad icon +user-portal.dial-pad-search.search.label=Enable Search Icon +user-portal.dial-pad-search.search.description=Enable or disable search icon + +user-portal.clickToCall-chat.label=Disable Click to Call and Chat Icons +user-portal.clickToCall-chat.description= + +user-portal.clickToCall-chat.clickToCall.label=Enable Contact Click to Call +user-portal.clickToCall-chat.clickToCall.description=Enable or disable contact click to call icon +user-portal.clickToCall-chat.chat.label=Enable Contact Click to Chat Icon +user-portal.clickToCall-chat.chat.description=Enable or disable contact click to chat icon + +user-portal.clickToCall-conf.label=Disable Click to Call and Conference Bridge +user-portal.clickToCall-conf.description= +user-portal.clickToCall-conf.conf.label=Enable Conference Bridge Click to Call +user-portal.clickToCall-conf.conf.description=Enable or disable conference bridge click to call + +user-portal.menu.label=Disable UniteWeb menu items +user-portal.menu.description= + +user-portal.menu.activityList.label=Enable Activity List +user-portal.menu.activityList.description=Enable or disable Activity List +user-portal.menu.contacts.label=Enable Contacts +user-portal.menu.contacts.description=Enable or disable Contacts +user-portal.menu.groupChats.label=Enable Group Chats +user-portal.menu.groupChats.description=Enable or disable Group Chats +user-portal.menu.confBridge.label=Enable Conference Bridge +user-portal.menu.confBridge.description=Enable or disable Conference Bridge +user-portal.menu.voicemails.label=Enable Voicemails +user-portal.menu.voicemails.description=Enable or disable Voicemails +user-portal.menu.myProfile.label=Enable My Profile +user-portal.menu.myProfile.description=Enable or disable My Profile +user-portal.menu.callHistory.label=Enable Call History +user-portal.menu.callHistory.description=Enable or disable Call History +user-portal.menu.settings.label=Enable Settings +user-portal.menu.settings.description=Enable or disable Settings +user-portal.menu.settingsPersonalAttendant.label=Enable Settings Personal Attendant +user-portal.menu.settingsPersonalAttendant.description=Enable or disable Settings Personal Attendant +user-portal.menu.settingsCallForwarding.label=Enable Settings Call Forwarding +user-portal.menu.settingsCallForwarding.description=Enable or disable Settings Call Forwarding +user-portal.menu.settingsSpeedDials.label=Enable Settings Speed Dials +user-portal.menu.settingsSpeedDials.description=Enable or disable Settings Speed Dials +user-portal.menu.userSettings.label=Enable User Settings +user-portal.menu.userSettings.description=Enable or disable User Settings +user-portal.menu.userSettingsChangePassword.label=Enable User Settings Change Password +user-portal.menu.userSettingsChangePassword.description=Enable or disable User Settings Change Password +user-portal.menu.userSettingsVoicemailPin.label=Enable User Settings Voicemail Pin +user-portal.menu.userSettingsVoicemailPin.description=Enable or disable User Settings Voicemail Pin +user-portal.menu.userSettingsAnnouncement.label=Enable User Settings Announcement +user-portal.menu.userSettingsAnnouncement.description=Enable or disable User Settings Announcement +user-portal.menu.userSettingsEmail.label=Enable User Settings Email +user-portal.menu.userSettingsEmail.description=Enable or disable User Settings Email +user-portal.menu.userSettingsAttachAudio.label=Enable User Settings Attach Audio +user-portal.menu.userSettingsAttachAudio.description=Enable or disable User Settings Attach Audio +user-portal.menu.userSettingsAlternateEmail.label=Enable User Settings Alternate Email +user-portal.menu.userSettingsAlternateEmail.description=Enable or disable User Settings Alternate Email +user-portal.menu.userSettingsAlternateAttachAudio.label=Enable User Settings Alternate Attach Audio +user-portal.menu.userSettingsAlternateAttachAudio.description=Enable or disable User Settings Alternate Attach Audio +user-portal.menu.userSettingsConfBridgeRoom.label=Enable User Settings Conference Bridge Room +user-portal.menu.userSettingsConfBridgeRoom.description=Enable or disable User Settings Conference Bridge Room +user-portal.menu.userSettingsConfBridgeEnabled.label=Enable User Settings Conference Bridge Enabled +user-portal.menu.userSettingsConfBridgeEnabled.description=Enable or disable User Settings Conference Bridge Enabled +user-portal.menu.userSettingsConfBridgeName.label=Enable User Settings Conference Bridge Name +user-portal.menu.userSettingsConfBridgeName.description=Enable or disable User Settings Conference Bridge Name +user-portal.menu.userSettingsConfBridgeModeratorPin.label=Enable User Settings Conference Bridge Moderator Pin +user-portal.menu.userSettingsConfBridgeModeratorPin.description=Enable or disable User Settings Conference Bridge Moderator Pin +user-portal.menu.userSettingsConfBridgeParticipantPin.label=Enable User Settings Conference Bridge Participant Pin +user-portal.menu.userSettingsConfBridgeParticipantPin.description=Enable or disable User Settings Conference Bridge Participant Pin +user-portal.menu.userSettingsConfBridgeMaxMembers.label=Enable User Settings Conference Bridge Max Members +user-portal.menu.userSettingsConfBridgeMaxMembers.description=Enable or disable User Settings Conference Bridge Max Members +user-portal.menu.userSettingsConfBridgeQuickStart.label=Enable User Settings Conference Bridge Quick Start +user-portal.menu.userSettingsConfBridgeQuickStart.description=Enable or disable User Settings Conference Bridge Quick Start +user-portal.menu.userSettingsConfBridgeAutoRecord.label=Enable User Settings Conference Bridge Auto Record +user-portal.menu.userSettingsConfBridgeAutoRecord.description=Enable or disable User Settings Conference Bridge Auto Record +user-portal.menu.userSettingsConfBridgeModerated.label=Enable User Settings Conference Bridge Moderated +user-portal.menu.userSettingsConfBridgeModerated.description=Enable or disable User Settings Conference Bridge Moderated +user-portal.menu.userSettingsConfBridgePublic.label=Enable User Settings Conference Bridge Public +user-portal.menu.userSettingsConfBridgePublic.description=Enable or disable User Settings Conference Bridge Public +user-portal.menu.userSettingsConfBridgeEntryTone.label=Enable User Settings Conference Bridge Entry Tone +user-portal.menu.userSettingsConfBridgeEntryTone.description=Enable or disable User Settings Conference Bridge Entry Tone +user-portal.menu.userSettingsConfBridgeExitTone.label=Enable User Settings Conference Bridge Exit Tone +user-portal.menu.userSettingsConfBridgeExitTone.description=Enable or disable User Settings Conference Bridge Exit Tone +user-portal.menu.userSettingsConfBridgeVoiceEntry.label=Enable User Settings Conference Bridge Voice Announce Entry +user-portal.menu.userSettingsConfBridgeVoiceEntry.description=Enable or disable User Settings Conference Bridge Voice Entry +user-portal.menu.userSettingsConfBridgeVoiceExit.label=Enable User Settings Conference Bridge Voice Announce Exit +user-portal.menu.userSettingsConfBridgeVoiceExit.description=Enable or disable User Settings Conference Bridge Voice Exit +user-portal.menu.userSettingsMohAudioSource.label=Enable User Settings Moh Audio Source +user-portal.menu.userSettingsMohAudioSource.description=Enable or disable User Settings Mod Audio Source +user-portal.menu.userSettingsMohPersonal.label=Enable User Settings Moh Personal +user-portal.menu.userSettingsMohPersonal.description=Enable or disable User Settings Moh Personal +user-portal.menu.userSettingsMohFiles.label=Enable User Settings Moh Files +user-portal.menu.userSettingsMohFiles.description=Enable or disable User Settings Moh Files +user-portal.menu.userSettingsMohAudio.label=Enable User Settings Moh Audio +user-portal.menu.userSettingsMohAudio.description=Enable or disable User Settings Moh Audio +user-portal.menu.userSettingsMyBuddyConfEntry.label=Enable User Settings My Buddy Conference Entry +user-portal.menu.userSettingsMyBuddyConfEntry.description=Enable or disable User Settings My Buddy Conference Entry +user-portal.menu.userSettingsMyBuddyConfExit.label=Enable User Settings My Buddy Conference Exit +user-portal.menu.userSettingsMyBuddyConfExit.description=Enable or disablUser Settings My Buddy Conference Exit +user-portal.menu.userSettingsMyBuddyVoicemailBegin.label=Enable User Settings My Buddy Voicemail Begin +user-portal.menu.userSettingsMyBuddyVoicemailBegin.description=Enable or disable User Settings My Buddy Voicemail Begin +user-portal.menu.userSettingsMyBuddyVoicemailEnd.label=Enable User Settings My Buddy Voicemail End +user-portal.menu.userSettingsMyBuddyVoicemailEnd.description=Enable or disable User Settings My Buddy Voicemail End +user-portal.menu.soundNotifications.label=Enable Sound Notifications +user-portal.menu.soundNotifications.description=Enable or disable Sound Notifications diff --git a/sipXconfig/etc/sipxpbx/commserver/user-settings.xml b/sipXconfig/etc/sipxpbx/commserver/user-settings.xml index 959f6e8c76..ef3b425fc0 100644 --- a/sipXconfig/etc/sipxpbx/commserver/user-settings.xml +++ b/sipXconfig/etc/sipxpbx/commserver/user-settings.xml @@ -153,6 +153,19 @@ + + + + + + + + false + - @@ -552,4 +560,308 @@ 0 + + + + + + + 1 + + + + + + 1 + + + + + + + + 1 + + + + + + 1 + + + + + + + + 1 + + + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + diff --git a/sipXconfig/etc/sipxpbx/database/add-4.4-orbit-location.sql b/sipXconfig/etc/sipxpbx/database/add-4.4-orbit-location.sql new file mode 100644 index 0000000000..ed7ce95291 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/add-4.4-orbit-location.sql @@ -0,0 +1,2 @@ +update park_orbit set location_id=(select location_id from location where primary_location=true) WHERE location_id IS NULL; +alter table park_orbit alter column location_id set default 1; diff --git a/sipXconfig/etc/sipxpbx/database/add-release-on-ring-failure.sql b/sipXconfig/etc/sipxpbx/database/add-release-on-ring-failure.sql new file mode 100644 index 0000000000..0ba8d54aa4 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/add-release-on-ring-failure.sql @@ -0,0 +1,2 @@ +ALTER TABLE openacd_agent_group ADD COLUMN release_on_ring_failure INTEGER; +UPDATE openacd_agent_group SET release_on_ring_failure = 3; diff --git a/sipXconfig/etc/sipxpbx/database/add_fallback_branch.sql b/sipXconfig/etc/sipxpbx/database/add_fallback_branch.sql new file mode 100644 index 0000000000..7c9ed3fd72 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/add_fallback_branch.sql @@ -0,0 +1,5 @@ +alter table branch add column fallback_branch_id int4; +alter table branch add constraint fk_fallback_branch + foreign key (fallback_branch_id) + references branch(branch_id) match full + on delete set null; \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/database/add_location_call_restrictions.sql b/sipXconfig/etc/sipxpbx/database/add_location_call_restrictions.sql new file mode 100644 index 0000000000..037f26db8d --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/add_location_call_restrictions.sql @@ -0,0 +1,23 @@ +create table branch_route_domain ( + branch_id int4 not null, + domain varchar(255) not null, + index int4 not null, + primary key (branch_id, index) +); + +create table branch_route_subnet ( + branch_id int4 not null, + subnet varchar(255) not null, + index int4 not null, + primary key (branch_id, index) +); + +alter table branch_route_domain + add constraint fk_branch_route_domain + foreign key (branch_id) + references branch; + +alter table branch_route_subnet + add constraint fk_branch_route_subnet + foreign key (branch_id) + references branch; \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/database/add_location_location.sql b/sipXconfig/etc/sipxpbx/database/add_location_location.sql new file mode 100644 index 0000000000..e6d27ae641 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/add_location_location.sql @@ -0,0 +1,12 @@ +create table branch_branch +( + branch_id integer not null, + associated_branch_id integer not null, + constraint branch_branch_pkey primary key (branch_id, associated_branch_id), + constraint branch_branch_fk1 foreign key (branch_id) + references branch (branch_id) match simple + on update no action on delete no action, + constraint branch_branch_fk2 foreign key (associated_branch_id) + references branch (branch_id) match simple + on update no action on delete no action +); \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/database/add_location_location_inbound.sql b/sipXconfig/etc/sipxpbx/database/add_location_location_inbound.sql new file mode 100644 index 0000000000..0ff4b15673 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/add_location_location_inbound.sql @@ -0,0 +1,12 @@ +create table branch_branch_inbound +( + branch_id integer not null, + associated_branch_id integer not null, + constraint branch_branch_inbound_pkey primary key (branch_id, associated_branch_id), + constraint branch_branch_inbound_fk1 foreign key (branch_id) + references branch (branch_id) match simple + on update no action on delete no action, + constraint branch_branch_inbound_fk2 foreign key (associated_branch_id) + references branch (branch_id) match simple + on update no action on delete no action +); \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/database/add_vm_authorization.sql b/sipXconfig/etc/sipxpbx/database/add_vm_authorization.sql new file mode 100644 index 0000000000..eeb5196a0e --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/add_vm_authorization.sql @@ -0,0 +1 @@ +alter table internal_dialing_rule add column external_authorization_checked boolean; \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/database/add_vm_server_port.sql b/sipXconfig/etc/sipxpbx/database/add_vm_server_port.sql new file mode 100644 index 0000000000..edf12420c7 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/add_vm_server_port.sql @@ -0,0 +1 @@ +alter table internal_dialing_rule add column media_server_port integer; \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/database/call-restrictions.sql b/sipXconfig/etc/sipxpbx/database/call-restrictions.sql new file mode 100644 index 0000000000..85f0bfb12a --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/call-restrictions.sql @@ -0,0 +1,103 @@ +create table branch_auth_code +( + auth_code_id integer not null, + branch_id integer not null, + constraint branch_auth_code_pkey primary key (auth_code_id, branch_id), + constraint branch_auth_code_fk1 foreign key (auth_code_id) + references auth_code (auth_code_id) match simple + on update no action on delete no action, + constraint branch_auth_code_fk2 foreign key (branch_id) + references branch (branch_id) match simple + on update no action on delete no action +); + +create table branch_auto_attendant +( + auto_attendant_id integer not null, + branch_id integer not null, + constraint branch_auto_attendant_pkey primary key (auto_attendant_id, branch_id), + constraint branch_auto_attendant_fk1 foreign key (auto_attendant_id) + references auto_attendant (auto_attendant_id) match simple + on update no action on delete no action, + constraint branch_auto_attendant_fk2 foreign key (branch_id) + references branch (branch_id) match simple + on update no action on delete no action +); + +create table branch_aa_dialing_rule +( + attendant_dialing_rule_id integer not null, + branch_id integer not null, + constraint branch_aa_dialing_rule_pkey primary key (attendant_dialing_rule_id, branch_id), + constraint branch_aa_dialing_rule_fk1 foreign key (attendant_dialing_rule_id) + references attendant_dialing_rule (attendant_dialing_rule_id) match simple + on update no action on delete no action, + constraint branch_aa_dialing_rule_fk2 foreign key (branch_id) + references branch (branch_id) match simple + on update no action on delete no action +); + +create table branch_park_orbit +( + park_orbit_id integer not null, + branch_id integer not null, + constraint branch_park_orbit_pkey primary key (park_orbit_id, branch_id), + constraint branch_park_orbit_fk1 foreign key (park_orbit_id) + references park_orbit (park_orbit_id) match simple + on update no action on delete no action, + constraint branch_park_orbit_fk2 foreign key (branch_id) + references branch (branch_id) match simple + on update no action on delete no action +); + +create table branch_call_group +( + call_group_id integer not null, + branch_id integer not null, + constraint branch_call_group_pkey primary key (call_group_id, branch_id), + constraint branch_call_group_fk1 foreign key (call_group_id) + references call_group (call_group_id) match simple + on update no action on delete no action, + constraint branch_call_group_fk2 foreign key (branch_id) + references branch (branch_id) match simple + on update no action on delete no action +); + +create table branch_paging_group +( + paging_group_id integer not null, + branch_id integer not null, + constraint branch_paging_group_pkey primary key (paging_group_id, branch_id), + constraint branch_paging_group_fk1 foreign key (paging_group_id) + references paging_group (paging_group_id) match simple + on update no action on delete no action, + constraint branch_paging_group_fk2 foreign key (branch_id) + references branch (branch_id) match simple + on update no action on delete no action +); + +create table branch_call_queue +( + freeswitch_ext_id integer not null, + branch_id integer not null, + constraint branch_call_queue_pkey primary key (freeswitch_ext_id, branch_id), + constraint branch_call_queue_fk1 foreign key (freeswitch_ext_id) + references freeswitch_extension (freeswitch_ext_id) match simple + on update no action on delete no action, + constraint branch_call_queue_fk2 foreign key (branch_id) + references branch (branch_id) match simple + on update no action on delete no action +); + +create table branch_conference +( + meetme_conference_id integer not null, + branch_id integer not null, + constraint branch_conference_pkey primary key (meetme_conference_id, branch_id), + constraint branch_conference_fk1 foreign key (meetme_conference_id) + references meetme_conference (meetme_conference_id) match simple + on update no action on delete no action, + constraint branch_conference_fk2 foreign key (branch_id) + references branch (branch_id) match simple + on update no action on delete no action +); \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/database/database.xml b/sipXconfig/etc/sipxpbx/database/database.xml index 23c4c67e9a..4d6458ca2e 100644 --- a/sipXconfig/etc/sipxpbx/database/database.xml +++ b/sipXconfig/etc/sipxpbx/database/database.xml @@ -471,6 +471,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1430,8 +1461,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UPDATE "public"."phone" SET "bean_id"='yealink' WHERE ("bean_id"='yealinkPhone'); + + + + + UPDATE "public"."phone" SET "model_id"='yealinkPhoneSIPT49G' WHERE ("model_id"='yealinkPhoneVP49G'); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -1445,6 +1576,16 @@ + + + + + + + + + + diff --git a/sipXconfig/etc/sipxpbx/database/delete-verisign-crt-crt.sql b/sipXconfig/etc/sipxpbx/database/delete-verisign-crt-crt.sql new file mode 100644 index 0000000000..6c715b8175 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/delete-verisign-crt-crt.sql @@ -0,0 +1 @@ +delete from authority where name='verisignclass3ca.crt'; \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/database/modify-setting-value-table.sql b/sipXconfig/etc/sipxpbx/database/modify-setting-value-table.sql new file mode 100644 index 0000000000..d181eeec14 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/modify-setting-value-table.sql @@ -0,0 +1 @@ +ALTER table setting_value ALTER COLUMN value TYPE varchar(7500); diff --git a/sipXconfig/etc/sipxpbx/database/openacd-add-pstn-agent.sql b/sipXconfig/etc/sipxpbx/database/openacd-add-pstn-agent.sql new file mode 100644 index 0000000000..1f2ef6eac4 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/openacd-add-pstn-agent.sql @@ -0,0 +1,9 @@ +create table openacd_agent_pstn ( + openacd_agent_id integer not null, + pstn_number varchar(255) NOT NULL unique, + index integer NOT NULL, + constraint pstn_number_pkey primary key (openacd_agent_id, index), + constraint fk_openacd_agent foreign key (openacd_agent_id) + references openacd_agent (openacd_agent_id) match simple + on update no action on delete cascade +); diff --git a/sipXconfig/etc/sipxpbx/database/openacd-client-update.sql b/sipXconfig/etc/sipxpbx/database/openacd-client-update.sql new file mode 100644 index 0000000000..56aae83dea --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/openacd-client-update.sql @@ -0,0 +1,2 @@ +ALTER table openacd_client ADD COLUMN archive_recordings CHARACTER VARYING(255); +ALTER table openacd_client ADD COLUMN retain_recordings_in_archive INTEGER NOT NULL DEFAULT 180; diff --git a/sipXconfig/etc/sipxpbx/database/openacd-custom-header.sql b/sipXconfig/etc/sipxpbx/database/openacd-custom-header.sql new file mode 100644 index 0000000000..758ae4b1f7 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/openacd-custom-header.sql @@ -0,0 +1,8 @@ +create table openacd_custom_header ( + openacd_custom_header_id integer not null, + name character varying(255) not null unique, + active boolean default false, + description character varying(255) not null, + primary key (openacd_custom_header_id) +); +create sequence openacd_custom_header_seq; diff --git a/sipXconfig/etc/sipxpbx/database/openacd-permission-constraint.sql b/sipXconfig/etc/sipxpbx/database/openacd-permission-constraint.sql new file mode 100644 index 0000000000..2c7edc8013 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/openacd-permission-constraint.sql @@ -0,0 +1,17 @@ +alter table openacd_permission_agent_group drop constraint permission_agent_group_fk1; +alter table openacd_permission_agent_group add constraint permission_agent_group_fk1 foreign key (openacd_agent_group_id) + references openacd_agent_group (openacd_agent_group_id) match simple + on update no action on delete cascade; +alter table openacd_permission_agent_group drop constraint permission_agent_group_fk2; +alter table openacd_permission_agent_group add constraint permission_agent_group_fk2 foreign key (openacd_permission_profile_id) + references openacd_permission_profile (openacd_permission_profile_id) match simple + on update no action on delete cascade; + +alter table openacd_permission_queue drop constraint permission_queue_fk1; +alter table openacd_permission_queue add constraint permission_queue_fk1 foreign key (openacd_queue_id) + references openacd_queue (openacd_queue_id) match simple + on update no action on delete cascade; +alter table openacd_permission_queue drop constraint permission_queue_fk2; +alter table openacd_permission_queue add constraint permission_queue_fk2 foreign key (openacd_permission_profile_id) + references openacd_permission_profile (openacd_permission_profile_id) match simple + on update no action on delete cascade; diff --git a/sipXconfig/etc/sipxpbx/database/openacd-permission-profile.sql b/sipXconfig/etc/sipxpbx/database/openacd-permission-profile.sql new file mode 100644 index 0000000000..86672b0c4f --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/openacd-permission-profile.sql @@ -0,0 +1,59 @@ +create table openacd_permission_profile ( + openacd_permission_profile_id integer not null, + name character varying(255) not null unique, + monitor boolean NOT NULL, + barge boolean NOT NULL, + agent_manager boolean NOT NULL, + queue_manager boolean NOT NULL, + primary key (openacd_permission_profile_id) +); +create sequence openacd_permission_profile_seq; + +insert into openacd_permission_profile (openacd_permission_profile_id, name, monitor, barge, agent_manager, queue_manager) + values (nextval('openacd_permission_profile_seq'), 'supervisor', true, true, true, true); +insert into openacd_permission_profile (openacd_permission_profile_id, name, monitor, barge, agent_manager, queue_manager) + values (nextval('openacd_permission_profile_seq'), 'agent', false, false, false, false); + +create table openacd_permission_agent_group ( + openacd_agent_group_id integer not null, + openacd_permission_profile_id integer not null, + constraint openacd_permission_agent_group_pkey primary key (openacd_agent_group_id, openacd_permission_profile_id), + constraint permission_agent_group_fk1 foreign key (openacd_agent_group_id) + references openacd_agent_group (openacd_agent_group_id) match simple + on update no action on delete no action, + constraint permission_agent_group_fk2 foreign key (openacd_permission_profile_id) + references openacd_permission_profile (openacd_permission_profile_id) match simple + on update no action on delete no action +); + +-- insert all existing agent groups to "supervisor" permission +insert into openacd_permission_agent_group (openacd_agent_group_id, openacd_permission_profile_id) +select a.openacd_agent_group_id, p.openacd_permission_profile_id from openacd_agent_group as a, openacd_permission_profile as p where p.name='supervisor'; + +create table openacd_permission_queue ( + openacd_queue_id integer not null, + openacd_permission_profile_id integer not null, + constraint openacd_permission_queue_pkey primary key (openacd_queue_id, openacd_permission_profile_id), + constraint permission_queue_fk1 foreign key (openacd_queue_id) + references openacd_queue (openacd_queue_id) match simple + on update no action on delete no action, + constraint permission_queue_fk2 foreign key (openacd_permission_profile_id) + references openacd_permission_profile (openacd_permission_profile_id) match simple + on update no action on delete no action +); + +-- insert all existing queues to "supervisor" permission +insert into openacd_permission_queue (openacd_queue_id, openacd_permission_profile_id) +select a.openacd_queue_id, p.openacd_permission_profile_id from openacd_queue as a, openacd_permission_profile as p where p.name='supervisor'; + +-- add permission profile column to AGENT table +alter table openacd_agent add column openacd_permission_profile_id integer; + +-- all existing agents will have default "agent" permission profile +update openacd_agent set openacd_permission_profile_id = (SELECT openacd_permission_profile_id FROM openacd_permission_profile where name='agent'); + +alter table openacd_agent alter column openacd_permission_profile_id set NOT NULL; + +alter table openacd_agent add constraint fk_openacd_permission_profile foreign key (openacd_permission_profile_id) + references openacd_permission_profile (openacd_permission_profile_id) match simple + on update no action on delete no action; diff --git a/sipXconfig/etc/sipxpbx/database/openacd-permission-update.sql b/sipXconfig/etc/sipxpbx/database/openacd-permission-update.sql new file mode 100644 index 0000000000..417d8029c7 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/openacd-permission-update.sql @@ -0,0 +1,66 @@ +create table openacd_permission_widget +( + openacd_permission_profile_id integer not null, + widget character varying(255) not null, + constraint openacd_permission_widget_pkey primary key (openacd_permission_profile_id, widget), + constraint openacd_permission_widget_fk1 FOREIGN KEY (openacd_permission_profile_id) + references openacd_permission_profile (openacd_permission_profile_id) match simple + on update no action on delete cascade +); + +insert into openacd_permission_widget (openacd_permission_profile_id, widget) + select p.openacd_permission_profile_id, 'AgentManager' from openacd_permission_profile as p where p.name='supervisor'; +insert into openacd_permission_widget (openacd_permission_profile_id, widget) + select p.openacd_permission_profile_id, 'CallRecording' from openacd_permission_profile as p where p.name='supervisor'; +insert into openacd_permission_widget (openacd_permission_profile_id, widget) + select p.openacd_permission_profile_id, 'MyStatistics' from openacd_permission_profile as p where p.name='supervisor'; +insert into openacd_permission_widget (openacd_permission_profile_id, widget) + select p.openacd_permission_profile_id, 'OutboundCall' from openacd_permission_profile as p where p.name='supervisor'; +insert into openacd_permission_widget (openacd_permission_profile_id, widget) + select p.openacd_permission_profile_id, 'QueueManager' from openacd_permission_profile as p where p.name='supervisor'; + +insert into openacd_permission_widget (openacd_permission_profile_id, widget) + select p.openacd_permission_profile_id, 'MyStatistics' from openacd_permission_profile as p where p.name='agent'; + +ALTER table openacd_permission_profile ADD COLUMN customize_desktop boolean NOT NULL DEFAULT FALSE; +ALTER table openacd_permission_profile ADD COLUMN use_advanced_login boolean NOT NULL DEFAULT FALSE; +ALTER table openacd_permission_profile ADD COLUMN transfer_to_agent boolean NOT NULL DEFAULT TRUE; +ALTER table openacd_permission_profile ADD COLUMN transfer_to_queue boolean NOT NULL DEFAULT TRUE; +ALTER table openacd_permission_profile ADD COLUMN transfer_to_number boolean NOT NULL DEFAULT TRUE; +ALTER table openacd_permission_profile ADD COLUMN conference_to_agent boolean NOT NULL DEFAULT TRUE; +ALTER table openacd_permission_profile ADD COLUMN conference_to_queue boolean NOT NULL DEFAULT TRUE; +ALTER table openacd_permission_profile ADD COLUMN conference_to_number boolean NOT NULL DEFAULT TRUE; +ALTER table openacd_permission_profile ADD COLUMN change_skills_on_tran_conf boolean NOT NULL DEFAULT TRUE; +ALTER table openacd_permission_profile ADD COLUMN control_agent_state boolean NOT NULL DEFAULT TRUE; +ALTER table openacd_permission_profile ADD COLUMN reports_tab boolean NOT NULL DEFAULT FALSE; +ALTER table openacd_permission_profile ADD COLUMN supervisor_tab boolean NOT NULL DEFAULT FALSE; + +UPDATE openacd_permission_profile SET + customize_desktop = true, + use_advanced_login = true, + transfer_to_agent = true, + transfer_to_queue = true, + transfer_to_number = true, + conference_to_agent = true, + conference_to_queue = true, + conference_to_number = true, + change_skills_on_tran_conf = true, + control_agent_state = true, + reports_tab = true, + supervisor_tab = true + WHERE name='supervisor'; + +UPDATE openacd_permission_profile SET + customize_desktop = true, + use_advanced_login = false, + transfer_to_agent = true, + transfer_to_queue = true, + transfer_to_number = true, + conference_to_agent = true, + conference_to_queue = true, + conference_to_number = true, + change_skills_on_tran_conf = true, + control_agent_state = true, + reports_tab = false, + supervisor_tab = false + WHERE name='agent'; diff --git a/sipXconfig/etc/sipxpbx/database/openacd-permission-update2.sql b/sipXconfig/etc/sipxpbx/database/openacd-permission-update2.sql new file mode 100644 index 0000000000..2591b36801 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/openacd-permission-update2.sql @@ -0,0 +1,2 @@ +ALTER table openacd_permission_profile drop COLUMN agent_manager; +ALTER table openacd_permission_profile drop COLUMN queue_manager; diff --git a/sipXconfig/etc/sipxpbx/database/openacd-permission-update3.sql b/sipXconfig/etc/sipxpbx/database/openacd-permission-update3.sql new file mode 100644 index 0000000000..e2728a5dba --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/openacd-permission-update3.sql @@ -0,0 +1,11 @@ +-- add permission profile column to AGENT GROUP table +alter table openacd_agent_group add column openacd_permission_profile_id integer; + +-- all existing agents will have default "agent" permission profile +update openacd_agent_group set openacd_permission_profile_id = (SELECT openacd_permission_profile_id FROM openacd_permission_profile where name='agent'); + +alter table openacd_agent_group alter column openacd_permission_profile_id set NOT NULL; + +alter table openacd_agent_group add constraint fk_openacd_permission_profile foreign key (openacd_permission_profile_id) + references openacd_permission_profile (openacd_permission_profile_id) match simple + on update no action on delete no action; diff --git a/sipXconfig/etc/sipxpbx/database/openacd-permission-update4.sql b/sipXconfig/etc/sipxpbx/database/openacd-permission-update4.sql new file mode 100644 index 0000000000..81c8ade022 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/openacd-permission-update4.sql @@ -0,0 +1,8 @@ +-- ensure that existing users are converted appropriately +update openacd_agent set openacd_permission_profile_id = (SELECT openacd_permission_profile_id FROM openacd_permission_profile where name='agent') +where security='AGENT'; +update openacd_agent set openacd_permission_profile_id = +(SELECT openacd_permission_profile_id FROM openacd_permission_profile where name='supervisor') where security in ('ADMIN','SUPERVISOR'); + +-- drop security column from openacd_agent +alter table openacd_agent drop column security; diff --git a/sipXconfig/etc/sipxpbx/database/openacd-permission-update5.sql b/sipXconfig/etc/sipxpbx/database/openacd-permission-update5.sql new file mode 100644 index 0000000000..fbe24cde31 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/openacd-permission-update5.sql @@ -0,0 +1,4 @@ +ALTER table openacd_permission_profile ADD COLUMN allow_outbound boolean NOT NULL DEFAULT FALSE; + +update openacd_permission_profile set allow_outbound='true' where openacd_permission_profile_id in +(select openacd_permission_profile_id from openacd_permission_widget where widget='OutboundCall') diff --git a/sipXconfig/etc/sipxpbx/database/openacd_autologout_agent_group.sql b/sipXconfig/etc/sipxpbx/database/openacd_autologout_agent_group.sql new file mode 100644 index 0000000000..4a6f372531 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/openacd_autologout_agent_group.sql @@ -0,0 +1,2 @@ +alter table openacd_agent_group add column auto_logout boolean default false; +alter table openacd_agent_group add column release_duration character varying(255) default '0'; diff --git a/sipXconfig/etc/sipxpbx/database/openacd_block_transfer_queue.sql b/sipXconfig/etc/sipxpbx/database/openacd_block_transfer_queue.sql new file mode 100644 index 0000000000..c8af84de8e --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/openacd_block_transfer_queue.sql @@ -0,0 +1 @@ +alter table openacd_queue add column block_transfer boolean default false; diff --git a/sipXconfig/etc/sipxpbx/database/remove-community.sql b/sipXconfig/etc/sipxpbx/database/remove-community.sql new file mode 100644 index 0000000000..f95d40422e --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/remove-community.sql @@ -0,0 +1 @@ +ALTER TABLE alarm_receiver DROP COLUMN community_string; \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/database/remove-firewall-rules.sql b/sipXconfig/etc/sipxpbx/database/remove-firewall-rules.sql new file mode 100644 index 0000000000..0b1c7612eb --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/remove-firewall-rules.sql @@ -0,0 +1 @@ +DELETE FROM firewall_rule where address_type IN ('adminApiAuth', 'imbotRestApi'); \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/database/remove-voicemail-imap-settings.sql b/sipXconfig/etc/sipxpbx/database/remove-voicemail-imap-settings.sql new file mode 100644 index 0000000000..cab9fd8fb6 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/remove-voicemail-imap-settings.sql @@ -0,0 +1 @@ +delete from setting_value where path in ('voicemail/imap/host','voicemail/imap/port','voicemail/imap/tls','voicemail/imap/account','voicemail/imap/password'); \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/database/remove_chat_meeting_setting.sql b/sipXconfig/etc/sipxpbx/database/remove_chat_meeting_setting.sql new file mode 100644 index 0000000000..9c9cf3b0a9 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/remove_chat_meeting_setting.sql @@ -0,0 +1 @@ +delete from setting_value where path in ('chat-meeting/moderated', 'chat-meeting/public', 'chat-meeting/members-only'); diff --git a/sipXconfig/etc/sipxpbx/database/system_audit_remove.sql b/sipXconfig/etc/sipxpbx/database/system_audit_remove.sql new file mode 100644 index 0000000000..85d828fb79 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/system_audit_remove.sql @@ -0,0 +1,16 @@ +insert into feature_local (feature_id, location_id) select 'systemaudit',l.location_id from +location l where not exists(select 1 from setting_value where path='configserver-config/systemAudit') and l.primary_location=true; +insert into setup (setup_id) select 'systemaudit' from +location l, setting_value s where l.primary_location='t' and s.path='configserver-config/systemAudit'; + +insert into feature_local (feature_id, location_id) select 'elasticsearch',l.location_id from +location l where not exists(select 1 from setting_value where path='configserver-config/systemAudit') and l.primary_location=true; + +delete from setting_value where path='configserver-config/systemAudit'; + +DROP SEQUENCE IF EXISTS config_change_seq; +DROP SEQUENCE IF EXISTS config_change_value_seq; +DROP TABLE IF EXISTS config_change_value; +DROP TABLE IF EXISTS config_change; + +DROP FUNCTION IF EXISTS system_audit_cleanup(); diff --git a/sipXconfig/etc/sipxpbx/database/update-expiration-time.js b/sipXconfig/etc/sipxpbx/database/update-expiration-time.js new file mode 100644 index 0000000000..784be1d755 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/database/update-expiration-time.js @@ -0,0 +1,17 @@ +db = new Mongo().getDB("node"); +// update registrations +db.registrar.find().forEach(function(doc) { + db.registrar.update({"_id":doc._id},{$set:{"expirationTime":getDateFromEpoch(doc.expirationTime + "")}} ); +}); +// update subscriptions +db.subscription.find().forEach(function(doc) { + db.subscription.update({"_id":doc._id},{$set:{"expires":getDateFromEpoch(doc.expires)}} ); +}); + +function getDateFromEpoch(mEpoch) { + var mEpochAsInt = parseInt(mEpoch); + var expDate = new Date(); + if (mEpochAsInt<10000000000) mEpochAsInt *= 1000; + expDate.setTime(mEpochAsInt); + return expDate; +} \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/dhcp/dhcp.properties b/sipXconfig/etc/sipxpbx/dhcp/dhcp.properties index 9f81538ac2..27eab9acbf 100644 --- a/sipXconfig/etc/sipxpbx/dhcp/dhcp.properties +++ b/sipXconfig/etc/sipxpbx/dhcp/dhcp.properties @@ -17,7 +17,7 @@ dhcpd-config.default_lease_time.description=Length in seconds that will be assig dhcpd-config.max_lease_time.label=Maximum lease time dhcpd-config.max_lease_time.description=Maximum Length in seconds that will be assigned to a lease if the client requesting the lease asks for a specific expiration time. dhcpd-unmanaged.label=Unmanaged Service -dhcpd-unmanaged.unmanaged.label=Unmanaged DNS service +dhcpd-unmanaged.unmanaged.label=Unmanaged DHCP service dhcpd-unmanaged.unmanaged.description=Enable this option if you don't want DHCP service to be managed by unified communication system \ (for e.g. generating configuration, automatic restart of services). dhcpd-unmanaged.dhcpd-server.label=DHCP server \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/freeswitch/conf/autoload_configs/modules.conf.xml b/sipXconfig/etc/sipxpbx/freeswitch/conf/autoload_configs/modules.conf.xml index 763ecd7086..c3f6f1d0ba 100644 --- a/sipXconfig/etc/sipxpbx/freeswitch/conf/autoload_configs/modules.conf.xml +++ b/sipXconfig/etc/sipxpbx/freeswitch/conf/autoload_configs/modules.conf.xml @@ -96,7 +96,7 @@ - + @@ -104,7 +104,7 @@ - + diff --git a/sipXconfig/etc/sipxpbx/freeswitch/conf/autoload_configs/shout.conf.xml b/sipXconfig/etc/sipxpbx/freeswitch/conf/autoload_configs/shout.conf.xml deleted file mode 100644 index 3f381e6278..0000000000 --- a/sipXconfig/etc/sipxpbx/freeswitch/conf/autoload_configs/shout.conf.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/sipXconfig/etc/sipxpbx/freeswitch/conferenceExit.lua b/sipXconfig/etc/sipxpbx/freeswitch/conferenceExit.lua new file mode 100644 index 0000000000..c5894867a3 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/freeswitch/conferenceExit.lua @@ -0,0 +1,8 @@ +fileToPlay = argv[1] +conference = argv[2] +playPrompt = argv[3] +api = freeswitch.API() +if playPrompt == "true" then + api:executeString("conference "..conference.." play file_string://"..fileToPlay.."!conference/conf-has_left.wav") +end +os.remove(fileToPlay) \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/freeswitch/default_context.xml.vm b/sipXconfig/etc/sipxpbx/freeswitch/default_context.xml.vm index 847ef72662..c9a53415d5 100644 --- a/sipXconfig/etc/sipxpbx/freeswitch/default_context.xml.vm +++ b/sipXconfig/etc/sipxpbx/freeswitch/default_context.xml.vm @@ -3,15 +3,44 @@ #if(${conference.enabled}) +#if ($maxForwards) + +#end +#if(${conference.recordAndPlayNameOnEntryEnabled}) + +#end +#if(${conference.recordAndPlayNameOnEntryEnabled}) + + + + + + + + +#end #end +#end +#if ($callback) + + + +#foreach($other in $locations) + +#end + + #end +#if ($maxForwards) + +#end #foreach($other in $locations) @@ -21,6 +50,9 @@ #if ($acccode) +#if ($maxForwards) + +#end @@ -29,19 +61,30 @@ #foreach($orbit in $orbits) #if(${orbit.enabled}) - + +#if ($maxForwards) + +#end #if(!${orbit.isMultipleCalls()}) - + #end #set( $transfer_expression_referror = 'transf_ext=${regex(${sip_h_X-sipX-referror}|(.*)@|%1)}' ) #set( $transfer_expression_auth = 'transf_ext=${regex(${sip_h_X-Sipx-Authidentity}|sip:(.*)@|%1)}' ) - + + - + + + + + + + + @@ -55,6 +98,9 @@ +#if ($maxForwards) + +#end @@ -62,8 +108,12 @@ #end +#if ($maxForwards) + +#end - + #end @@ -71,6 +121,9 @@ #foreach($condition in $extension.conditions) +#if ($maxForwards) + +#end #foreach($action in $condition.actions) #end @@ -78,11 +131,18 @@ #end #end + + + + + + #if ($blindTransfer) - + #end diff --git a/sipXconfig/etc/sipxpbx/freeswitch/freeswitch.properties b/sipXconfig/etc/sipxpbx/freeswitch/freeswitch.properties index bd3b108dc6..be4d1adb3c 100644 --- a/sipXconfig/etc/sipxpbx/freeswitch/freeswitch.properties +++ b/sipXconfig/etc/sipxpbx/freeswitch/freeswitch.properties @@ -1,10 +1,18 @@ freeswitch-config.FREESWITCH_CODECS.label=Codecs freeswitch-config.FREESWITCH_CODECS.description=All available codecs are selected by default. G729 codec may not be selected because it may not be \ -installed. Useful URLS: http://wiki.sipfoundry.org/display/xecsuser/G729+License and http://www.freeswitch.org +installed. Useful URLS: http://wiki.sipxcom.org and http://www.freeswitch.org freeswitch-config.FREESWITCH_SESSION_PER_SECOND.label=Sessions Per Second freeswitch-config.FREESWITCH_SESSION_PER_SECOND.description=Max Sessions Per Second allowed by this Media Server instance +freeswitch-config.FREESWITCH_RTP_TIMEOUT.label=RTP timeout +freeswitch-config.FREESWITCH_RTP_TIMEOUT.description=The number of seconds of RTP inactivity (media silence) before FreeSWITCH considers the call disconnected, and hangs up. \ +It is recommended that you use session timers instead. Set it to "0" in order to disable RTP timeout. + +freeswitch-config.FREESWITCH_MAX_FORWARDS.label=Max Forwards +freeswitch-config.FREESWITCH_MAX_FORWARDS.description=Set the Max Forwards number for this call. If empty, the original Max Forwards value of call will be preserved. \ +This value applies to all calls handled by Media Services. + freeswitch-config.FREESWITCH_MAX_SESSIONS.label=Max Sessions freeswitch-config.FREESWITCH_MAX_SESSIONS.description=Max Sessions allowed by once by this Media Server instance @@ -24,3 +32,17 @@ freeswitch-config.MOH_SOURCE.label.SOUNDCARD_SRC=Sound Card freeswitch-config.MOH_SOURCE.label.NONE=None freeswitch-config.FREESWITCH_BLIND_TRANSFER.label=Allow Blind Transfer freeswitch-config.FREESWITCH_BLIND_TRANSFER.description=Enables inline Blind Transfer from within media services dial plan +freeswitch-config.FREESWITCH_SIMPLIFY.label=Simplify Call After Transfer +freeswitch-config.FREESWITCH_SIMPLIFY.description=Remove server from SIP signaling path after transfer. When enabled server will be removed from SIP \ +signaling path after transfer on key pressed or timeout. You may want to turn this off for SBCs that does not implement REFER with replaces properly \ +and results in failed calls after transfer. +freeswitch-config.FREESWITCH_USE_CALL_ID_AS_UUID_INBOUND.label=Preserve inbound call id +freeswitch-config.FREESWITCH_USE_CALL_ID_AS_UUID_INBOUND.description=If set on true then Media Services will use same inbound call id as internal call id. \ +Useful for debugging call flows towards Media Services (as IVR, AA, conferences) +freeswitch-config.FREESWITCH_USE_CALL_ID_AS_UUID_OUTBOUND.label=Preserve call id on outbound calls +freeswitch-config.FREESWITCH_USE_CALL_ID_AS_UUID_OUTBOUND.description=If set on true then on outbound calls Media Services will set the callid to match the internal uuid of the session. \ +Useful for debugging call flows towards Media Services (as IVR, AA, conferences) +freeswitch-config.FREESWITCH_CORE.label=Enable core dumps +freeswitch-config.FREESWITCH_CORE.description=When enabled FS will generate core files (prefixed with core.) in /tmp directory when a crash occurs. \ +When disabling this option a system reboot is required in order to become effective. + diff --git a/sipXconfig/etc/sipxpbx/freeswitch/freeswitch.xml b/sipXconfig/etc/sipxpbx/freeswitch/freeswitch.xml index f793bef0c7..8be3ef026e 100644 --- a/sipXconfig/etc/sipxpbx/freeswitch/freeswitch.xml +++ b/sipXconfig/etc/sipxpbx/freeswitch/freeswitch.xml @@ -36,9 +36,22 @@ + + + + + + true + + + false + + + @@ -68,12 +81,38 @@ 30 + + + + + + + + + + + 300 + + + + + 1 + + 0 + + + true + + + + true + @@ -87,5 +126,18 @@ 0 + + + + + 1 + + + 0 + + + + 0 + diff --git a/sipXconfig/etc/sipxpbx/freeswitch/freeswitch.xml.vm b/sipXconfig/etc/sipxpbx/freeswitch/freeswitch.xml.vm index 83bb689e79..e07c055f4e 100644 --- a/sipXconfig/etc/sipxpbx/freeswitch/freeswitch.xml.vm +++ b/sipXconfig/etc/sipxpbx/freeswitch/freeswitch.xml.vm @@ -58,14 +58,22 @@
- +
- -
+
+ + - + + + + + + + +
\ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/freeswitch/recordings.properties b/sipXconfig/etc/sipxpbx/freeswitch/recordings.properties new file mode 100644 index 0000000000..f48b0834a4 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/freeswitch/recordings.properties @@ -0,0 +1,10 @@ +fs-recording.label=Recording Settings + +fs-recording.brate.label=Bit rate +fs-recording.brate.description=Bit rate (in kbit/s) to be used for recordings. Applies to MP3. Select 32 or 64 kbit/s bit rate for a sample rate of 44100Hz. + +fs-recording.resample.label=Sample rate +fs-recording.resample.description=Sample rate (in Hz) to be used for recordings. Applies to WAV and MP3. + +fs-recording.quality.label=Encoder quality +fs-recording.quality.description=Quality to be used for recordings. Applies to MP3. diff --git a/sipXconfig/etc/sipxpbx/freeswitch/recordings.xml b/sipXconfig/etc/sipxpbx/freeswitch/recordings.xml new file mode 100644 index 0000000000..cc0adda144 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/freeswitch/recordings.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + 44100 + + + + 32 + + + + high + + + diff --git a/sipXconfig/etc/sipxpbx/freeswitch/shout.conf.xml.vm b/sipXconfig/etc/sipxpbx/freeswitch/shout.conf.xml.vm new file mode 100644 index 0000000000..ec6528b26a --- /dev/null +++ b/sipXconfig/etc/sipxpbx/freeswitch/shout.conf.xml.vm @@ -0,0 +1,7 @@ + + + + + + + diff --git a/sipXconfig/etc/sipxpbx/freeswitch/sofia.conf.xml.vm b/sipXconfig/etc/sipxpbx/freeswitch/sofia.conf.xml.vm index c7025ff91d..a4b4203492 100644 --- a/sipXconfig/etc/sipxpbx/freeswitch/sofia.conf.xml.vm +++ b/sipXconfig/etc/sipxpbx/freeswitch/sofia.conf.xml.vm @@ -48,13 +48,15 @@ + - + + @@ -75,6 +77,7 @@ + diff --git a/sipXconfig/etc/sipxpbx/sipxbridge/bridge-sbc.properties b/sipXconfig/etc/sipxpbx/sipxbridge/bridge-sbc.properties index 8a088a943c..f12a40eb84 100644 --- a/sipXconfig/etc/sipxpbx/sipxbridge/bridge-sbc.properties +++ b/sipXconfig/etc/sipxpbx/sipxbridge/bridge-sbc.properties @@ -67,3 +67,6 @@ bridge-configuration.log-level.description=Log configuration. Defaults to INFO l #bridge-configuration.log-directory.label= bridge-configuration.log-directory.description=Log directory. Defaults to /var/log/sipxbpx/. The log file name is hard coded to "sipxbridge.log". +bridge-configuration.sipx-proxy-transport.label=Bridge-proxy transport +bridge-configuration.sipx-proxy-transport.description=Transport for bridge-proxy communication. + diff --git a/sipXconfig/etc/sipxpbx/sipxbridge/bridge-sbc.xml b/sipXconfig/etc/sipxpbx/sipxbridge/bridge-sbc.xml index db7f76f88e..5b88d37bd7 100644 --- a/sipXconfig/etc/sipxpbx/sipxbridge/bridge-sbc.xml +++ b/sipXconfig/etc/sipxpbx/sipxbridge/bridge-sbc.xml @@ -12,6 +12,12 @@ + + + + + + + + + udp + diff --git a/sipXconfig/etc/sipxpbx/sipxcallback/prompts/caller.wav b/sipXconfig/etc/sipxpbx/sipxcallback/prompts/caller.wav new file mode 100644 index 0000000000..c113ea9ebe Binary files /dev/null and b/sipXconfig/etc/sipxpbx/sipxcallback/prompts/caller.wav differ diff --git a/sipXconfig/etc/sipxpbx/sipxcallback/prompts/requested_a_callback.wav b/sipXconfig/etc/sipxpbx/sipxcallback/prompts/requested_a_callback.wav new file mode 100644 index 0000000000..3d89acfcab Binary files /dev/null and b/sipXconfig/etc/sipxpbx/sipxcallback/prompts/requested_a_callback.wav differ diff --git a/sipXconfig/etc/sipxpbx/sipxcallback/sipxcallback.properties b/sipXconfig/etc/sipxpbx/sipxcallback/sipxcallback.properties new file mode 100644 index 0000000000..7d68a3982f --- /dev/null +++ b/sipXconfig/etc/sipxpbx/sipxcallback/sipxcallback.properties @@ -0,0 +1,11 @@ +callback.label=Callback on Busy + +callback.freeswitch.eventSocketPort.label=FREESWITCH Event Socket Port + +callback-config.label=Callback on Busy Parameters + +callback-config.log.level.label=Logging Level +callback-config.callback-prefix.label=Callback Prefix +callback-config.callback-prefix.description=The prefix used for callback on busy requests. +callback-config.callback-duration.label=Callback Expiration +callback-config.callback-duration.description=Expiration interval for the callback request (minutes). diff --git a/sipXconfig/etc/sipxpbx/sipxcallback/sipxcallback.properties.vm b/sipXconfig/etc/sipxpbx/sipxcallback/sipxcallback.properties.vm new file mode 100644 index 0000000000..a53d45f1e4 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/sipxcallback/sipxcallback.properties.vm @@ -0,0 +1,7 @@ +#set($callback=${service.getSettings().getSetting('callback')}) +log.level=$callback.getSetting('log.level').Value +log.file=${service.logDir}/sipxcallback.log + +callback.sipxchangeDomainName=${service.domainName} + +freeswitch.eventSocketPort=$callback.getSetting('freeswitch.eventSocketPort').Value diff --git a/sipXconfig/etc/sipxpbx/sipxcallback/sipxcallback.xml b/sipXconfig/etc/sipxpbx/sipxcallback/sipxcallback.xml new file mode 100644 index 0000000000..6c55b240fd --- /dev/null +++ b/sipXconfig/etc/sipxpbx/sipxcallback/sipxcallback.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + NOTICE + + + + + + *92 + + + + + + 30 + + + diff --git a/sipXconfig/etc/sipxpbx/sipxcallresolver/sipxcallresolver.properties b/sipXconfig/etc/sipxpbx/sipxcallresolver/sipxcallresolver.properties index 6f22106e99..13ca708a57 100644 --- a/sipXconfig/etc/sipxpbx/sipxcallresolver/sipxcallresolver.properties +++ b/sipXconfig/etc/sipxpbx/sipxcallresolver/sipxcallresolver.properties @@ -34,4 +34,11 @@ callresolver.SIP_CALLRESOLVER_CSE_QUEUE_SIZE.label=CSE Queue size callresolver.SIP_CALLRESOLVER_CDR_QUEUE_SIZE.label=CDR Queue size callresolver.SIP_CALLRESOLVER_MAX_CALL_LEN.label=Maximum call length callresolver.SIP_CALLRESOLVER_MAX_RINGING_CALL_LEN.label=Maximum Ringing call length -callresolver.SIP_CALLRESOLVER_MIN_CLEANUP_INTERVAL.label=Minimum cleanup interval \ No newline at end of file +callresolver.SIP_CALLRESOLVER_MIN_CLEANUP_INTERVAL.label=Minimum cleanup interval + +callresolver.SIP_CALLRESOLVER_PRIVACY.label=Enable Masquerading +callresolver.SIP_CALLRESOLVER_PRIVACY.description=When enabled hides last digits of phone numbers into cdrs to enhance privacy +callresolver.SIP_CALLRESOLVER_PRIVACY_LENGTH.label=Minimum length of extension that must be masqueraded +callresolver.SIP_CALLRESOLVER_PRIVACY_LENGTH.description=This value indicate the minimum length of extension that should be covered with * +callresolver.SIP_CALLRESOLVER_PRIVACY_EXCLUDE.label=Prefixes excluded from masquerading +callresolver.SIP_CALLRESOLVER_PRIVACY_EXCLUDE.description=This will exclude numbers with the above prefixes from being masqueraded (separate multiple prefixes with spaces). \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/sipxcallresolver/sipxcallresolver.xml b/sipXconfig/etc/sipxpbx/sipxcallresolver/sipxcallresolver.xml index 65016ce603..568ea39639 100644 --- a/sipXconfig/etc/sipxpbx/sipxcallresolver/sipxcallresolver.xml +++ b/sipXconfig/etc/sipxpbx/sipxcallresolver/sipxcallresolver.xml @@ -89,7 +89,7 @@ - + 1000 @@ -116,5 +116,21 @@ + ++ ++ DISABLE ++ ++ ++ ++ ++ ++ 5 ++ ++ ++ ++ ++ ++ ++ diff --git a/sipXconfig/etc/sipxpbx/sipxconference/bridge.properties b/sipXconfig/etc/sipxpbx/sipxconference/bridge.properties index 2dc7e01330..3057c78f01 100644 --- a/sipXconfig/etc/sipxpbx/sipxconference/bridge.properties +++ b/sipXconfig/etc/sipxpbx/sipxconference/bridge.properties @@ -40,28 +40,28 @@ fs-conf-bridge.dtmf-commands.hung-up.hungup.description = Code to leave the \ conference. fs-conf-bridge.dtmf-commands.hung-up.hungup.label = Hang up code fs-conf-bridge.dtmf-commands.audio-volume.label = Conference Audio -fs-conf-bridge.dtmf-commands.audio-volume.down.description = Decrease the audio \ - volume coming out from the conference. -fs-conf-bridge.dtmf-commands.audio-volume.down.label = Volume DOWN -fs-conf-bridge.dtmf-commands.audio-volume.reset.description = Reset the audio \ - volume coming out from the conference to default. -fs-conf-bridge.dtmf-commands.audio-volume.reset.label = Volume RESET -fs-conf-bridge.dtmf-commands.audio-volume.up.description = Increase the audio \ - volume coming out from the conference. -#fs-conf-bridge.dtmf-commands.audio-volume.label= -#fs-conf-bridge.dtmf-commands.audio-volume.description= -fs-conf-bridge.dtmf-commands.audio-volume.up.label = Volume UP -fs-conf-bridge.dtmf-commands.audio-volume.mic-down.description = Decrease the volume \ +fs-conf-bridge.dtmf-commands.audio-volume.down.description = Decrease the volume \ sent to the conference. -fs-conf-bridge.dtmf-commands.audio-volume.mic-down.label = Microphone Gain DOWN -fs-conf-bridge.dtmf-commands.audio-volume.mic-reset.description = Reset the volume \ +fs-conf-bridge.dtmf-commands.audio-volume.down.label = Microphone Gain DOWN +fs-conf-bridge.dtmf-commands.audio-volume.reset.description = Reset the volume \ sent to the conference to default. -fs-conf-bridge.dtmf-commands.audio-volume.mic-reset.label = Microphone Gain RESET -fs-conf-bridge.dtmf-commands.audio-volume.mic-up.description = Increase the volume \ +fs-conf-bridge.dtmf-commands.audio-volume.reset.label = Microphone Gain RESET +fs-conf-bridge.dtmf-commands.audio-volume.up.description = Increase the volume \ sent to the conference. #fs-conf-bridge.dtmf-commands.audio-volume.label= #fs-conf-bridge.dtmf-commands.audio-volume.description= -fs-conf-bridge.dtmf-commands.audio-volume.mic-up.label = Microphone Gain UP +fs-conf-bridge.dtmf-commands.audio-volume.up.label = Microphone Gain UP +fs-conf-bridge.dtmf-commands.audio-volume.mic-down.description = Decrease the audio \ + volume coming out from the conference. +fs-conf-bridge.dtmf-commands.audio-volume.mic-down.label = Volume DOWN +fs-conf-bridge.dtmf-commands.audio-volume.mic-reset.description = Reset the audio \ + volume coming out from the conference to default. +fs-conf-bridge.dtmf-commands.audio-volume.mic-reset.label = Volume RESET +fs-conf-bridge.dtmf-commands.audio-volume.mic-up.description = Increase the audio \ + volume coming out from the conference. +#fs-conf-bridge.dtmf-commands.audio-volume.label= +#fs-conf-bridge.dtmf-commands.audio-volume.description= +fs-conf-bridge.dtmf-commands.audio-volume.mic-up.label = Volume UP fs-conf-bridge.log-level.description = Maximum log severity \ level to be recorded. fs-conf-bridge.log-level.label = Bridge log level diff --git a/sipXconfig/etc/sipxpbx/sipxconference/conference.conf.xml.vm b/sipXconfig/etc/sipxpbx/sipxconference/conference.conf.xml.vm index 9ad84472d6..7fd0bb0a1c 100644 --- a/sipXconfig/etc/sipxpbx/sipxconference/conference.conf.xml.vm +++ b/sipXconfig/etc/sipxpbx/sipxconference/conference.conf.xml.vm @@ -44,7 +44,7 @@ - + @@ -72,7 +72,7 @@ - + #if(${conference.isVideoConference()}) @@ -94,8 +94,12 @@ #if(${conference.isAloneSound()}) #end +#if(${conference.isPlayEntryToneEnabled()}) +#end +#if(${conference.isPlayExitToneEnabled()}) +#end diff --git a/sipXconfig/etc/sipxpbx/sipxconference/conference.properties b/sipXconfig/etc/sipxpbx/sipxconference/conference.properties old mode 100644 new mode 100755 index 4cf77d840f..81fd5b669c --- a/sipXconfig/etc/sipxpbx/sipxconference/conference.properties +++ b/sipXconfig/etc/sipxpbx/sipxconference/conference.properties @@ -27,8 +27,8 @@ fs-conf-conference.ACCESS.label.OPEN=Open fs-conf-conference.ACCESS.label.REMOTE_ADMIT=Use access codes fs-conf-conference.MAX_LEGS.label=Maximum legs -fs-conf-conference.MAX_LEGS.description=The maximum number of call legs to be allowed by this bridge. 0 means \ - unlimited. +fs-conf-conference.MAX_LEGS.description=The maximum number of call legs to be allowed by this bridge. Setting a value of 0 or 1 \ +means unlimited users can join. fs-conf-conference.MOH.label=Music On Hold source fs-conf-conference.MOH.description=Selects the source of the on hold music for this conference. \ @@ -42,22 +42,21 @@ fs-conf-conference.MOH.label.NONE=None #fs-conf-conference.FS_BRIDGE_CONFERENCE.REMOTE_ADMIT.SECRET.label= fs-conf-conference.FS_BRIDGE_CONFERENCE.REMOTE_ADMIT.SECRET.description=Automatically generated value that is used by remote admittance control. -chat-meeting.moderated.label=Moderated -chat-meeting.moderated.description=If checked, only the owner of the chat room will be able to send messages; \ - external users can be given chat privileges from the owner. The owner can \ - grant other users chat privileges by issuing the following command on the \ - client "/role participant nickname". - -chat-meeting.public.label=Public -chat-meeting.public.description=A public room can be found by any user through normal means such as searching and service discovery. - -chat-meeting.members-only.label=Members Only fs-conf-conference.quickstart.label=Quickstart fs-conf-conference.quickstart.description=A non-quickstart conference begins when the chair person arrives. Enable this if you want not to wait for a moderator. Only a quickstart conference can be setup with no PIN. fs-conf-conference.moderator-code.label=Moderator code fs-conf-conference.moderator-code.description=Moderator PIN. If set, a participant PIN must also be set. Users logged in using this pin are automatically recognised as moderators. -#chat-meeting.members-only.description= fs-conf-conference.video.label=Enable Video fs-conf-conference.video.description=Enable video conference. EXPERIMENTAL. fs-conf-conference.video-toogle-floor.label=Send active video only fs-conf-conference.video-toogle-floor.description=Send video from active talking participant only. EXPERIMENTAL. + +fs-conf-conference.play-entry-tone.label=Play Entry Tone +fs-conf-conference.play-entry-tone.description=When enabled, entry tone will be played each time a participant joins this conference. +fs-conf-conference.play-exit-tone.label=Play Exit Tone +fs-conf-conference.play-exit-tone.description=When enabled, exit tone will be played each time a participant leaves this conference. +fs-conf-conference.prompt-name-on-entry.label=Play User Name on Entry +fs-conf-conference.prompt-name-on-entry.description=When enabled, user joining this conference will be prompted to speak their name. The name is played and heard by all the participants upon user joining the meeting. +fs-conf-conference.prompt-name-on-exit.label=Play User Name on Exit +fs-conf-conference.prompt-name-on-exit.description=When enabled, recorded name of user leaving this meeting is played and heard by all participants. + diff --git a/sipXconfig/etc/sipxpbx/sipxconference/conference.xml b/sipXconfig/etc/sipxpbx/sipxconference/conference.xml index bcb44f9c0a..40c4f90ec0 100644 --- a/sipXconfig/etc/sipxpbx/sipxconference/conference.xml +++ b/sipXconfig/etc/sipxpbx/sipxconference/conference.xml @@ -48,6 +48,22 @@ yes + + + yes + + + + yes + + + + no + + + + no + no @@ -96,26 +112,6 @@ + + + + + + + + + + + + + + + + + + + + @@ -112,6 +136,10 @@ false + + + false + false @@ -125,6 +153,10 @@ + + + false + @@ -151,6 +183,18 @@ false + + + + + New_LDAP + + + + + + 0 + diff --git a/sipXconfig/etc/sipxpbx/sipxconfig/sipxconfig-apache.conf b/sipXconfig/etc/sipxpbx/sipxconfig/sipxconfig-apache.conf index 625a1e0237..10c28cac11 100644 --- a/sipXconfig/etc/sipxpbx/sipxconfig/sipxconfig-apache.conf +++ b/sipXconfig/etc/sipxpbx/sipxconfig/sipxconfig-apache.conf @@ -1,11 +1,15 @@ +RewriteEngine On +RewriteCond %{HTTPS} !=on +RewriteRule ^/?sipxconfig/(.*) https://%{SERVER_NAME}/sipxconfig/$1 [NE,R,L] + RewriteEngine on - RewriteRule ^.*$ https://%{HTTP_HOST}/sipxconfig/app [L,R] + RewriteRule ^.*$ https://%{HTTP_HOST}/sipxconfig/app [NE,L,R] RewriteEngine on - RewriteRule ^.*$ https://%{HTTP_HOST}/sipxconfig/app [L,R] + RewriteRule ^.*$ https://%{HTTP_HOST}/sipxconfig/app [NE,L,R] AliasMatch /([a-f\d]{12}).cfg "$(sipx.SIPX_VARDIR)/configserver/phone/profile/tftproot/$1.cfg" @@ -43,6 +47,11 @@ ProxyTimeout 240 ProxyPassReverse http://127.0.0.1:12000/cmcprov + + ProxyPass http://127.0.0.1:12000/jitsiprov + ProxyPassReverse http://127.0.0.1:12000/jitsiprov + + Listen 8090 CoreDumpDirectory $(sipx.SIPX_LOGDIR) Alias /phone/profile/docroot/ "$(sipx.SIPX_VARDIR)/configserver/phone/profile/docroot/" diff --git a/sipXconfig/etc/sipxpbx/sipxivr/sipxivr.properties b/sipXconfig/etc/sipxpbx/sipxivr/sipxivr.properties index e30b34c74d..384f4819ac 100644 --- a/sipXconfig/etc/sipxpbx/sipxivr/sipxivr.properties +++ b/sipXconfig/etc/sipxpbx/sipxivr/sipxivr.properties @@ -14,6 +14,9 @@ ivr.record.time.description=Maximum duration (in seconds) of the voicemail recor ivr.fax.format.label=Fax Format ivr.fax.format.description=The file format for receiving faxes +ivr.fax.reinvite.label=Enable T.38 ReINVITE +ivr.fax.reinvite.description=Send T.38-ReINVITE when fax detected by tone detection + ivr.ivr.freeswitch.eventSocketPort.label=FREESWITCH Event Socket Port ivr.ivr.publicHttpPort.label=Public HTTP port @@ -38,4 +41,23 @@ ivr.ivr.updaterConnectTimeoutMS.label=Database Connection timeout ivr.ivr.updaterConnectTimeoutMS.description=Timeout (in milliseconds) for global database connection. ivr.ivr.updaterSocketTimeoutMS.label=Database Socket timeout -ivr.ivr.updaterSocketTimeoutMS.description=Timeout (in milliseconds) for global database socket. \ No newline at end of file +ivr.ivr.updaterSocketTimeoutMS.description=Timeout (in milliseconds) for global database socket. + +ivr.ivr.transferByBridge.label=Transfer By Bridging the call +ivr.ivr.transferByBridge.description=Bridge call for transfer through IVR / AutoAttendant (default transfer method use SIP REFER method) \ +Enable this option to fix integration with ITSPs / SBCs that does not support SIP REFER and if you want to have ringback played \ +while transferring call. + +ivr.ivr.backup_host.label=Backup Host +ivr.ivr.backup_host.description=The host where the voicemail backup will be executed. \ +If you use a custom location for temporary files in backup, please make sure it is available also on this Backup Host. + +ivr.security.autoEnterPinExternalDigits.label=Last digits to match for Auto Enter PIN from External # +ivr.security.autoEnterPinExternalDigits.description=Number of last digits to match the external number \ +when analyzing Auto Enter PIN from External # permission: 0-15 (if 0 it will match the whole number). \ +Ex: if you call from 15555555555 and in the Cell Phone field you have 5555555555, you need to match the last 10 digits. \ +Be aware that the lower the number of digits to match, the higher the chance to find duplicate users. + +ivr.security.cleanupVoicemailHour.label=Voicemail cleanup hour +ivr.security.cleanupVoicemailHour.description=Choose the hour of the day in which to run the voicemail cleanup task: 0-23. \ +It is recommended to select an hour in which the system load is minimal. diff --git a/sipXconfig/etc/sipxpbx/sipxivr/sipxivr.xml b/sipXconfig/etc/sipxpbx/sipxivr/sipxivr.xml index 22c2c2d10c..6537a66c08 100644 --- a/sipXconfig/etc/sipxpbx/sipxivr/sipxivr.xml +++ b/sipXconfig/etc/sipxpbx/sipxivr/sipxivr.xml @@ -41,16 +41,6 @@ - - - - - - - - - 8000 - 300 @@ -82,6 +68,19 @@ pdf + + + + + + + + false + @@ -130,5 +129,36 @@ 5000 + + + + + + + + false + + + + + + + + + + + + 0 + + + + + + 0 + diff --git a/sipXconfig/etc/sipxpbx/sipxproxy/sipXproxy-config.vm b/sipXconfig/etc/sipxpbx/sipxproxy/sipXproxy-config.vm index d20a6fa0c0..9a3fa96de0 100644 --- a/sipXconfig/etc/sipxpbx/sipxproxy/sipXproxy-config.vm +++ b/sipXconfig/etc/sipxpbx/sipxproxy/sipXproxy-config.vm @@ -60,6 +60,8 @@ SIPX_PROXY_WHITE_LIST : $!{proxyService.whiteList} SIPX_PROXY_BLACK_LIST : $!{proxyService.blackList} SIPX_TRAN_HOOK_LIBRARY.100_sipxhomer : @sipxpbx.lib.dir@/transactionplugins/libsipXhomerProxyPlugin.so -SIPX_TRAN_HOOK_LIBRARY.905_gatewaydest: @sipxpbx.lib.dir@/transactionplugins/libGatewayDestPlugin.so +#if( $!{settings.getSetting('proxy-configuration/SIPX_CONSULTATIVE_TRANSFER_GATEWAY_INITIAL_INVITE').Value} ) + SIPX_TRAN_HOOK_LIBRARY.905_gatewaydest: @sipxpbx.lib.dir@/transactionplugins/libGatewayDestPlugin.so +#end SIPX_TRAN_HOOK_LIBRARY.910_sssmessagefilter: @sipxpbx.lib.dir@/transactionplugins/libSSSMessageFilterProxyPlugin.so diff --git a/sipXconfig/etc/sipxpbx/sipxproxy/sipxproxy.properties b/sipXconfig/etc/sipxpbx/sipxproxy/sipxproxy.properties index 975ca6d3a8..5f92eefc5b 100644 --- a/sipXconfig/etc/sipxpbx/sipxproxy/sipxproxy.properties +++ b/sipXconfig/etc/sipxpbx/sipxproxy/sipxproxy.properties @@ -52,6 +52,10 @@ proxy-configuration.SIPX_PROXY_RELAY_ALLOWED.label=Allow Non-Local Domain Relay proxy-configuration.SIPX_PROXY_RELAY_ALLOWED.description=If checked, proxy can act as a relay for non-local domain transactions. If you are using external phone features for phone lines, you must allow relaying. proxy-configuration.SIPX_PROXY_ENABLE_TCP_RESEND.label=Enable TCP Retransmission proxy-configuration.SIPX_PROXY_ENABLE_TCP_RESEND.description=Enables retransmission of SIP messages when using TCP +proxy-configuration.SIPX_PROXY_DEFAULT_RTT.label=SIP Round Trip Time (T1 Timer) +proxy-configuration.SIPX_PROXY_DEFAULT_RTT.description=The value of the T1 timer used for retransmission interval computation. This is expressed in milliseconds (100ms - 500ms). +proxy-configuration.SIPX_PROXY_RETRANSMIT_TIMES.label=SIP Retransmission Count +proxy-configuration.SIPX_PROXY_RETRANSMIT_TIMES.description=The number of times SIP requests will be retransmitted before a timeout occurs (2 - 7). proxy-configuration.SIPX_PROXY_CALL_STATE.label=Call State proxy-configuration.SIPX_PROXY_CALL_STATE.description= proxy-configuration.SIPX_PROXY_LOG_CONSOLE.label=Log Console @@ -68,8 +72,16 @@ proxy-configuration.SIPX_PROXY_MAX_TRANSACTION_COUNT.label=Maximum active transa proxy-configuration.SIPX_PROXY_MAX_TRANSACTION_COUNT.description=Maximum limit for the number of active transactions before the proxy starts rejecting incoming calls. proxy-configuration.SIPX_PROXY_HOP_BY_HOP_CANCEL.label=Enable Hop By Hop Cancel Processing proxy-configuration.SIPX_PROXY_HOP_BY_HOP_CANCEL.description=If enabled, each canceled transaction will be responded with a 487 error locally. +proxy-configuration.SIPX_PROXY_LOG_AUTH_CODES.label=Log Auth Codes +proxy-configuration.SIPX_PROXY_LOG_AUTH_CODES.description=If enabled, the auth code used to make the call will be inserted as a uri parameter of the caller_aor cdr field proxy-configuration.SIPX_TRUST_SBC_REGISTERED_CALLS.label=Trust SBC registered calls proxy-configuration.SIPX_TRUST_SBC_REGISTERED_CALLS.description= Check this option if there is an unmanaged gateway dial-plan that may conflict with users registered through an SBC. (Experimental Only) +proxy-configuration.SIPX_SUPPRESS_ALERT_INDICATOR_IN_TRANSFERS.label=Suppress alerting indicator +proxy-configuration.SIPX_SUPPRESS_ALERT_INDICATOR_IN_TRANSFERS.description= Suppress alerting notification events for transfers. +proxy-configuration.SIPX_CONSULTATIVE_TRANSFER_GATEWAY_INITIAL_INVITE.label=Consultative Transfer to Use Gateway of Initial Invite +proxy-configuration.SIPX_CONSULTATIVE_TRANSFER_GATEWAY_INITIAL_INVITE.description= When a call is consultative transferred the resulting invite for the final destination of the transfer needs to use the same gateway as the consultative part of the transfer (warning - restarts proxy). +proxy-configuration.SIPX_PASS_P_ASSERTED_IDENTITY.label=Pass P-Asserted-Identity +proxy-configuration.SIPX_PASS_P_ASSERTED_IDENTITY.description=The P-Asserted-Identity contains the caller id information for the call on the INVITE SIP packet. subscriptionauth.label=Subscription Authentication subscriptionauth.PACKAGES_REQUIRING_AUTHENTICATION.label=Packages Requiring Authentication diff --git a/sipXconfig/etc/sipxpbx/sipxproxy/sipxproxy.xml b/sipXconfig/etc/sipxpbx/sipxproxy/sipxproxy.xml index c78a160fa6..b9ea2ff654 100644 --- a/sipXconfig/etc/sipxpbx/sipxproxy/sipxproxy.xml +++ b/sipXconfig/etc/sipxpbx/sipxproxy/sipxproxy.xml @@ -103,6 +103,18 @@ 0 + + + + + 100 + + + + + + 4 + 0 @@ -155,6 +167,22 @@ 20000 + + + 0 + + + + false + + + + true + + + + false + + + + + + 100 + diff --git a/sipXconfig/etc/sipxpbx/sipxtcpdumplog/sipxtcpdumplog.properties b/sipXconfig/etc/sipxpbx/sipxtcpdumplog/sipxtcpdumplog.properties new file mode 100644 index 0000000000..926bd0d624 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/sipxtcpdumplog/sipxtcpdumplog.properties @@ -0,0 +1,4 @@ + +tcpdumpParameters.SIP_TCPDUMP_FILE_COUNT.label = Log File Count +tcpdumpParameters.SIP_TCPDUMP_FILE_SIZE.label = Log File Size (MB) +tcpdumpParameters.label = Network Packet Capture Service diff --git a/sipXconfig/etc/sipxpbx/sipxtcpdumplog/sipxtcpdumplog.properties.vm b/sipXconfig/etc/sipxpbx/sipxtcpdumplog/sipxtcpdumplog.properties.vm new file mode 100644 index 0000000000..ad6a1e8ad9 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/sipxtcpdumplog/sipxtcpdumplog.properties.vm @@ -0,0 +1,3 @@ +SIP_TCPDUMP_FILE_SIZE=$settings.getSetting('SIP_TCPDUMP_FILE_SIZE').Value +SIP_TCPDUMP_FILE_COUNT=$settings.getSetting('SIP_TCPDUMP_FILE_COUNT').Value +SIP_TCPDUMP_LOG_FILE=${service.logDir}/sipxtcpdumplog.log \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/sipxtcpdumplog/sipxtcpdumplog.xml b/sipXconfig/etc/sipxpbx/sipxtcpdumplog/sipxtcpdumplog.xml new file mode 100644 index 0000000000..e32164d017 --- /dev/null +++ b/sipXconfig/etc/sipxpbx/sipxtcpdumplog/sipxtcpdumplog.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + 50 + + + + + + 100 + + + diff --git a/sipXconfig/etc/sipxpbx/sipxvxml/global_aa.properties b/sipXconfig/etc/sipxpbx/sipxvxml/global_aa.properties index 9d98e678d3..d81806f3c0 100644 --- a/sipXconfig/etc/sipxpbx/sipxvxml/global_aa.properties +++ b/sipXconfig/etc/sipxpbx/sipxvxml/global_aa.properties @@ -17,3 +17,17 @@ enter Live Auto Attendant Enable / Disable PIN to disable Live Auto Attendant mo liveAttendant.expireTime.label=Expiration Time liveAttendant.expireTime.description=A disabled Live Auto Attendant will be automatically re enabled by system after expiration time (defined in hours). If no time specified \ the disabled Live Auto Attendant will be automatically re enabled at schedule change + +liveAttendant.dtmf.label=DTMF Handling + +liveAttendant.dtmf.maxDigits.label=Max Digits +liveAttendant.dtmf.maxDigits.description=Maximum number of dialpad keys to accept. + +liveAttendant.dtmf.firstDigitTimeout.label=First Digit Timeout +liveAttendant.dtmf.firstDigitTimeout.description=Time to wait (seconds) for the first digit to be inserted. + +liveAttendant.dtmf.interDigitTimeout.label=Inter Digit Timeout +liveAttendant.dtmf.interDigitTimeout.description=Time to wait (seconds) for each of the next digits to be inserted. + +liveAttendant.dtmf.extraDigitTimeout.label=Extra Digit Timeout +liveAttendant.dtmf.extraDigitTimeout.description=Time to wait (seconds) for each of the extra digits to be inserted. \ No newline at end of file diff --git a/sipXconfig/etc/sipxpbx/sipxvxml/global_aa.xml b/sipXconfig/etc/sipxpbx/sipxvxml/global_aa.xml index 0bffb4a913..bc91c33546 100644 --- a/sipXconfig/etc/sipxpbx/sipxvxml/global_aa.xml +++ b/sipXconfig/etc/sipxpbx/sipxvxml/global_aa.xml @@ -19,5 +19,31 @@ + + + + + + 20 + + + + + + 10 + + + + + + 1 + + + + + + 1 + + diff --git a/sipXconfig/etc/sipxpbx/snmp/snmp.properties b/sipXconfig/etc/sipxpbx/snmp/snmp.properties old mode 100644 new mode 100755 index 3ad8a9fc00..0e604ac875 --- a/sipXconfig/etc/sipxpbx/snmp/snmp.properties +++ b/sipXconfig/etc/sipxpbx/snmp/snmp.properties @@ -3,3 +3,6 @@ cfdat.fix_dead_processes.label=Restart dead processes cfdat.fix_dead_processes.description=Local SNMP server will automatically restart services that are supposed to be running but currently are not running. cfdat.community_string.label=Community string cfdat.community_string.description=User id or password that allows access to device statistics +cfdat.community_string_confirm.label=Retype community string +cfdat.community_string_confirm.description=Confirm the above entered community string + diff --git a/sipXconfig/etc/sipxpbx/snmp/snmp.xml b/sipXconfig/etc/sipxpbx/snmp/snmp.xml old mode 100644 new mode 100755 index 160ea410be..e67452d00f --- a/sipXconfig/etc/sipxpbx/snmp/snmp.xml +++ b/sipXconfig/etc/sipxpbx/snmp/snmp.xml @@ -13,5 +13,10 @@ public + + + + + diff --git a/sipXconfig/etc/snmptrap.cf b/sipXconfig/etc/snmptrap.cf index 446d2c7dad..56fa44891c 100644 --- a/sipXconfig/etc/snmptrap.cf +++ b/sipXconfig/etc/snmptrap.cf @@ -63,7 +63,7 @@ bundle edit_line snmptrapd_conf { insert_lines: any:: - "authCommunity log,execute,net public"; + "authCommunity log,execute,net $(sipx.snmp_community_string)"; "traphandle $(alarm_types) $(sipx.SIPX_LIBEXECDIR)/snmptrap-email-handler --send \ --in $(sipx.SIPX_CFDATA)/$(sipx.location_id)/snmptrap-emails.yaml"; diff --git a/sipXconfig/etc/zz_apache.cf b/sipXconfig/etc/zz_apache.cf index 7f0b10b53f..ec32b02f1a 100644 --- a/sipXconfig/etc/zz_apache.cf +++ b/sipXconfig/etc/zz_apache.cf @@ -66,7 +66,7 @@ bundle agent apache_config { comment => "add apache ssl conf $(this.promiser)", create => "true", edit_defaults => empty, - edit_line => expand_template("$(sipx.SIPX_CFDATA)/ssl.conf"), + edit_line => expand_template("$(sipx.SIPX_CFDATA)/$(sipx.location_id)/ssl.conf"), classes => if_repaired("restart_apache"); "$(sipx.APACHE2_CONFDIR)/unitelite.conf" @@ -83,7 +83,7 @@ bundle agent apache_config { "$(sipx.APACHE2_CONFDIR)/ssl/$(ssl_web)" comment => "install ssl info $(this.promiser)", create => "true", - copy_from => copy_from_cfdata("$(ssl_web)"), + copy_from => copy_from_cfdata("$(sipx.location_id)/$(ssl_web)"), perms => mog("600","root","root"), classes => if_repaired("restart_apache"); diff --git a/sipXconfig/neoconf/.classpath b/sipXconfig/neoconf/.classpath index c8e3d7cc71..f3e854ef64 100644 --- a/sipXconfig/neoconf/.classpath +++ b/sipXconfig/neoconf/.classpath @@ -15,5 +15,10 @@ + + + + + diff --git a/sipXconfig/neoconf/src/Makefile.am b/sipXconfig/neoconf/src/Makefile.am index 0d738f95cb..1758e844f3 100644 --- a/sipXconfig/neoconf/src/Makefile.am +++ b/sipXconfig/neoconf/src/Makefile.am @@ -18,7 +18,7 @@ neoconf_RESOURCES = \ $(shell cd $(srcdir); find . -type f \( \ -name '*.json' \ -o -name '*.properties' \ - -o -name '*.crt' \ + -o -name 'verisignclass3ca' \ -o -name '*.xml' \ -o -name '*.jpg' \ -o -name '*.xsd' \ diff --git a/sipXconfig/neoconf/src/beanRefContext.xml b/sipXconfig/neoconf/src/beanRefContext.xml index b83f3d548b..5b700983af 100644 --- a/sipXconfig/neoconf/src/beanRefContext.xml +++ b/sipXconfig/neoconf/src/beanRefContext.xml @@ -6,7 +6,9 @@ classpath:/org/sipfoundry/sipxconfig/system.beans.xml classpath*:/org/sipfoundry/sipxconfig/*/**/*.beans.xml + classpath*:/sipxplugin2.beans.xml classpath*:/sipxplugin.beans.xml + classpath*:/sipxplugin0.beans.xml diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/Pluggable0ResourceBundleMessageSource.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/Pluggable0ResourceBundleMessageSource.java new file mode 100644 index 0000000000..c51f3f41de --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/Pluggable0ResourceBundleMessageSource.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2015 eZuce, Inc. All rights reserved. + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) any later version. + * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + */ +package org.sipfoundry.sipxconfig; + +import org.springframework.context.support.ResourceBundleMessageSource; + +/** + * Any resource bundle that is NOT meant to be overwritten by an additional plugin should use + * this class. All resources that are kept in instances of this type are added last in + * the GlobalMessageSource.getDelegates() + * @see GlobalMessageSource.java + */ +public class Pluggable0ResourceBundleMessageSource extends ResourceBundleMessageSource { + +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/PluggableResourceBundleMessageSource.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/PluggableResourceBundleMessageSource.java new file mode 100644 index 0000000000..ee9eb0672a --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/PluggableResourceBundleMessageSource.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2015 eZuce, Inc. All rights reserved. + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) any later version. + * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + */ +package org.sipfoundry.sipxconfig; + +import org.springframework.context.support.ResourceBundleMessageSource; + +/** + * Any resource bundle that is meant to be overwritten by an additional plugin should use + * this class. All resources that are kept in instances of this type are added last in + * the GlobalMessageSource.getDelegates() + * @see GlobalMessageSource.java + */ +public class PluggableResourceBundleMessageSource extends ResourceBundleMessageSource { + +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/SipxUtil.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/SipxUtil.java index a8c4da0555..2106fc43d6 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/SipxUtil.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/SipxUtil.java @@ -37,4 +37,16 @@ public static File createTempDir(String name) throws IOException { tempDir.mkdirs(); return tempDir; } + + public static boolean isDirectoryEmpty(String dirPath) { + if (dirPath == null) { + return true; + } + File folder = new File(dirPath); + String[] fileList = folder.list(); + if (fileList != null && fileList.length > 0) { + return false; + } + return true; + } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/acccode/AuthCode.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/acccode/AuthCode.java index f7e8091b9f..31f047379e 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/acccode/AuthCode.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/acccode/AuthCode.java @@ -13,14 +13,21 @@ import static org.sipfoundry.commons.mongo.MongoConstants.PASSTOKEN; import static org.sipfoundry.commons.mongo.MongoConstants.UID; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; +import org.sipfoundry.commons.mongo.MongoConstants; +import org.sipfoundry.sipxconfig.branch.Branch; import org.sipfoundry.sipxconfig.common.BeanWithUserPermissions; public class AuthCode extends BeanWithUserPermissions { private String m_code; private String m_description; + private Set m_locations = new HashSet(); public String getCode() { return m_code; @@ -38,12 +45,34 @@ public void setDescription(String description) { m_description = description; } + public Set getLocations() { + return m_locations; + } + + public void setLocations(Set locations) { + m_locations = locations; + } + + public List getLocationsList() { + return new ArrayList(m_locations); + } + + public void setLocationsList(List locations) { + m_locations.clear(); + m_locations.addAll(locations); + } + @Override public Map getMongoProperties(String domain) { Map props = new HashMap(); props.put(AUTH_CODE, m_code); props.put(UID, getInternalUser().getUserName()); props.put(PASSTOKEN, getInternalUser().getSipPassword()); + List locations = new ArrayList(); + for (Branch branch : m_locations) { + locations.add(branch.getName()); + } + props.put(MongoConstants.LOCATIONS, locations); return props; } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/acccode/authcode.hbm.xml b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/acccode/authcode.hbm.xml index a1d4f9d5c8..d4eb9271fb 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/acccode/authcode.hbm.xml +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/acccode/authcode.hbm.xml @@ -15,6 +15,10 @@ + + + + diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminConfig.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminConfig.java index 23358b7a42..7a5dc265c3 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminConfig.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminConfig.java @@ -21,11 +21,14 @@ import java.util.Set; import org.apache.commons.io.IOUtils; +import org.sipfoundry.sipxconfig.cfgmgt.CfengineModuleConfiguration; import org.sipfoundry.sipxconfig.cfgmgt.ConfigManager; import org.sipfoundry.sipxconfig.cfgmgt.ConfigProvider; import org.sipfoundry.sipxconfig.cfgmgt.ConfigRequest; +import org.sipfoundry.sipxconfig.cfgmgt.KeyValueConfiguration; import org.sipfoundry.sipxconfig.cfgmgt.LoggerKeyValueConfiguration; import org.sipfoundry.sipxconfig.commserver.Location; +import org.sipfoundry.sipxconfig.proxy.ProxyManager; import org.sipfoundry.sipxconfig.setting.Setting; import org.sipfoundry.sipxconfig.setting.SettingUtil; @@ -42,12 +45,26 @@ public void replicate(ConfigManager manager, ConfigRequest request) throws IOExc Set locations = request.locations(manager); AdminSettings settings = m_adminContext.getSettings(); Setting adminSettings = settings.getSettings().getSetting(m_adminSettingsKey); + String password = settings.getPostgresPassword(); + for (Location l : locations) { + File dir = manager.getLocationDataDirectory(l); + if (l.isPrimary() || manager.getFeatureManager().isFeatureEnabled(ProxyManager.FEATURE, l)) { + Writer pwd = new FileWriter(new File(dir, "postgres-pwd.properties")); + Writer pwdCfdat = new FileWriter(new File(dir, "postgres-pwd.cfdat")); + try { + KeyValueConfiguration cfg = KeyValueConfiguration.equalsSeparated(pwd); + CfengineModuleConfiguration cfgCfdat = new CfengineModuleConfiguration(pwdCfdat); + cfg.write("password", password); + cfgCfdat.write("NEW_POSTGRESQL_PASSWORD", password); + } finally { + IOUtils.closeQuietly(pwd); + IOUtils.closeQuietly(pwdCfdat); + } + } if (!l.isPrimary()) { continue; } - File dir = manager.getLocationDataDirectory(l); - String log4jFileName = "log4j.properties.part"; String[] logLevelKeys = settings.getLogLevelKeys(); SettingUtil.writeLog4jSetting(adminSettings, dir, log4jFileName, logLevelKeys); diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminContext.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminContext.java index e7f0b50732..eac4f923a9 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminContext.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminContext.java @@ -54,5 +54,14 @@ public enum PasswordPolicyType { boolean isAuthEmailAddress(); - boolean isSystemAuditEnabled(); + String getNewLdapUserGroupNamePrefix(); + + int getStripUserName(); + + boolean isHazelcastEnabled(); + + boolean isSyncExtAvatar(); + + boolean isAllowSubscriptionsToSelf(); + } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminContextImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminContextImpl.java index 93b425c558..4708b93933 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminContextImpl.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminContextImpl.java @@ -119,6 +119,12 @@ protected void buildArchiveCommands(BackupPlan plan, BackupSettings settings) { if (!plan.isIncludeDeviceFiles()) { m_backup.append(" --no-device-files"); } + String tmpDirectory = settings.getTmpDir(); + if (StringUtils.isNotBlank(tmpDirectory)) { + String tmpDir = " --tmp-dir "; + m_backup.append(tmpDir).append(tmpDirectory); + m_restore.append(tmpDir).append(tmpDirectory); + } if (settings.isKeepDomain()) { m_restore.append(" --domain ") .append(m_domainManager.getDomain().getName()); @@ -147,6 +153,10 @@ protected void buildArchiveCommands(BackupPlan plan, BackupSettings settings) { } } + protected StringBuilder getBackup() { + return m_backup; + } + protected void setBackup(StringBuilder backup) { m_backup = backup; } @@ -217,13 +227,32 @@ public boolean isAuthEmailAddress() { return getSettings().isAuthEmailAddress(); } + public String getNewLdapUserGroupNamePrefix() { + return getSettings().getNewLdapUserGroupNamePrefix(); + } + @Required public void setDomainManager(DomainManager domainManager) { m_domainManager = domainManager; } @Override - public boolean isSystemAuditEnabled() { - return AdminSettings.isSystemAuditEnabled(); + public boolean isHazelcastEnabled() { + return (Boolean) getSettings().getSettingTypedValue(AdminSettings.HAZELCAST_NOTIFICATION); + } + + @Override + public boolean isSyncExtAvatar() { + return (Boolean) getSettings().getSettingTypedValue(AdminSettings.EXT_AVATAR_SYNC); + } + + @Override + public boolean isAllowSubscriptionsToSelf() { + return (Boolean) getSettings().isAllowSubscriptionsToSelf(); + } + + @Override + public int getStripUserName() { + return getSettings().getStripUsername(); } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminSettings.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminSettings.java old mode 100644 new mode 100755 index ed9ff33b26..c9f65c3dc5 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminSettings.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/AdminSettings.java @@ -32,6 +32,9 @@ * and we don't want to restart config server */ public class AdminSettings extends PersistableSettings implements DeployConfigOnEdit { + + public static final String HAZELCAST_NOTIFICATION = "configserver-config/hazelcastNotification"; + public static final String EXT_AVATAR_SYNC = "configserver-config/extAvatarSync"; private static final Log LOG = LogFactory.getLog(AdminSettings.class); private static final String LDAP_MANAGEMENT_DISABLE = "ldap-management/disable"; @@ -41,10 +44,17 @@ public class AdminSettings extends PersistableSettings implements DeployConfigOn private static final String AUTHENTICATION_AUTH_ACC_NAME = "configserver-config/account-name"; private static final String AUTHENTICATION_EMAIL_ADDRESS = "configserver-config/email-address"; private static final String CORS_DOMAIN_SETTING = "configserver-config/corsDomains"; - private static final String SYSTEM_AUDIT = "configserver-config/systemAudit"; - - private static boolean s_systemAuditEnabled = true; - + private static final String NEW_LDAP_USERS_GROUP_PREFIX = "ldap-management/newUserGroupPrefix"; + private static final String STRIP_USERNAME = "ldap-management/stripUserName"; + private static final String PASSWORD_POLICY = "configserver-config/password-policy"; + private static final String DEFAULT_PASSWORD = "configserver-config/password-default"; + private static final String DEFAULT_PASSWORD_CONFIRM = "configserver-config/password-default-confirm"; + private static final String VMPIN_DEFAULT = "configserver-config/vmpin-default"; + private static final String VMPIN_DEFAULT_CONFIRM = "configserver-config/vmpin-default-confirm"; + private static final String POSTGRES_PASSWORD = "configserver-config/postgres-pwd"; + private static final String POSTGRES_PASSWORD_CONFIRM = "configserver-config/postgres-pwd-confirm"; + private static final String SYSTEM_AUDIT_KEEP_CHANGES = "config-change-audit/keep-changes"; + private static final String ALLOW_SUBSCRIPTIONS_TO_SELF = "configserver-config/allow-subscription-to-self"; private PasswordPolicy m_passwordPolicy; private String[] m_logLevelKeys; @@ -66,15 +76,35 @@ protected Setting loadSettings() { } public String getSelectedPolicy() { - return getSettingValue("configserver-config/password-policy"); + return getSettingValue(PASSWORD_POLICY); } public String getDefaultPassword() { - return getSettingValue("configserver-config/password-default"); + return getSettingValue(DEFAULT_PASSWORD); + } + + public String getDefaultPasswordConfirmed() { + return getSettingValue(DEFAULT_PASSWORD_CONFIRM); } public String getDefaultVmPin() { - return getSettingValue("configserver-config/vmpin-default"); + return getSettingValue(VMPIN_DEFAULT); + } + + public String getVmpinDefaultConfirmed() { + return getSettingValue(VMPIN_DEFAULT_CONFIRM); + } + + public String getPostgresPassword() { + return getSettingValue(POSTGRES_PASSWORD); + } + + public String getPostgresPasswordConfirmed() { + return getSettingValue(POSTGRES_PASSWORD_CONFIRM); + } + + public int getSystemAuditKeepChanges() { + return (Integer) getSettingTypedValue(SYSTEM_AUDIT_KEEP_CHANGES); } public int getAge() { @@ -93,6 +123,14 @@ public boolean isDelete() { return (Boolean) getSettingTypedValue(LDAP_MANAGEMENT_DELETE); } + public String getNewLdapUserGroupNamePrefix() { + return (String) getSettingTypedValue(NEW_LDAP_USERS_GROUP_PREFIX); + } + + public int getStripUsername() { + return (Integer) getSettingTypedValue(STRIP_USERNAME); + } + public void setDisable(boolean disable) { setSettingTypedValue(LDAP_MANAGEMENT_DISABLE, disable); } @@ -127,13 +165,20 @@ public void setCorsDomains(String corsDomains) { setSettingValue(CORS_DOMAIN_SETTING, noSpaces); } - public static boolean isSystemAuditEnabled() { - return s_systemAuditEnabled; + public void setHazelcastNotification(boolean notification) { + setSettingTypedValue(HAZELCAST_NOTIFICATION, notification); + } + + public boolean isHazelcastNotification() { + return (Boolean) getSettingTypedValue(HAZELCAST_NOTIFICATION); + } + + public boolean isSyncExtAvatar() { + return (Boolean) getSettingTypedValue(EXT_AVATAR_SYNC); } - public void setSystemAuditEnabled(boolean systemAuditEnabled) { - s_systemAuditEnabled = systemAuditEnabled; - setSettingTypedValue(SYSTEM_AUDIT, systemAuditEnabled); + public boolean isAllowSubscriptionsToSelf() { + return (Boolean) getSettingTypedValue(ALLOW_SUBSCRIPTIONS_TO_SELF); } protected static String validateDomainList(String corsDomains) { @@ -161,7 +206,7 @@ public void setLogLevelKeys(String[] logLevelKeys) { } public class AdminSettingsDefaults { - @SettingEntry(path = "configserver-config/password-policy") + @SettingEntry(path = PASSWORD_POLICY) public String getDefaultPolicy() { return m_passwordPolicy.getDefaultPolicy(); } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/SyncExtAvatarTimer.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/SyncExtAvatarTimer.java new file mode 100644 index 0000000000..2974e79735 --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/SyncExtAvatarTimer.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + * Contributed to sipXcom under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.admin; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.sipfoundry.commons.userdb.profile.AvatarUploadException; +import org.sipfoundry.commons.userdb.profile.UserProfile; +import org.sipfoundry.commons.userdb.profile.UserProfileService; +import org.springframework.beans.factory.annotation.Required; + +public class SyncExtAvatarTimer { + public static final Log LOG = LogFactory.getLog(SyncExtAvatarTimer.class); + private UserProfileService m_userProfileService; + private ExecutorService m_executor = Executors.newSingleThreadExecutor(); + private AdminContext m_adminContext; + + public void syncExtAvatar() { + if (!m_adminContext.isSyncExtAvatar()) { + return; + } + try { + m_executor.execute(new SyncExtAvatar()); + } catch (Exception ex) { + LOG.error("Failed running disabled/delete task ", ex); + } + } + + @Required + public void setUserProfileService(UserProfileService userProfileService) { + m_userProfileService = userProfileService; + } + + @Required + public void setAdminContext(AdminContext adminContext) { + m_adminContext = adminContext; + } + + private final class SyncExtAvatar implements Runnable { + + @Override + public void run() { + List userProfiles = new ArrayList(); + try { + userProfiles = m_userProfileService.getUserProfilesByExtAvatarUrl(); + } catch (Exception ex) { + LOG.error("Cannot retrieve user profiles ", ex); + } + String userName; + for (UserProfile profile : userProfiles) { + try { + userName = profile.getUserName(); + LOG.debug("Save external avatar for: " + userName); + m_userProfileService.saveExtAvatar(userName, profile.getExtAvatar()); + //refresh user profile every time execute a save, to keep any concurent changes in the profile + UserProfile currentProfile = m_userProfileService.getUserProfileByUsername(userName); + currentProfile.setExtAvatarSyncDate(Calendar.getInstance().getTime()); + m_userProfileService.saveUserProfile(currentProfile); + } catch (AvatarUploadException e) { + LOG.error("Cannot upload external avatar"); + } + } + } + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/admin.beans.xml b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/admin.beans.xml index 08f04a6da6..c07b88b42f 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/admin.beans.xml +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/admin/admin.beans.xml @@ -1,6 +1,7 @@ - + @@ -30,6 +31,7 @@ log4j.logger.org.sipfoundry.sipxconfig log4j.logger.org.apache.cxf.interceptor + log4j.logger.org.sipxcom.sipxconfig @@ -63,5 +65,14 @@ + + + + + + + + + diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/AlarmConfiguration.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/AlarmConfiguration.java index 513748e05c..2946699693 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/AlarmConfiguration.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/AlarmConfiguration.java @@ -37,6 +37,7 @@ public class AlarmConfiguration implements ConfigProvider { private AlarmServerManager m_alarmServerManager; + private SnmpManager m_snmpManager; private MessageSource m_messageSource; @Override @@ -90,6 +91,9 @@ public void replicate(ConfigManager manager, ConfigRequest request) throws IOExc void writeCfdat(Writer w, boolean enabled, List fwd) throws IOException { CfengineModuleConfiguration cfdat = new CfengineModuleConfiguration(w); cfdat.writeClass("snmptrap", enabled); + for (AlarmTrapReceiver receiver : fwd) { + receiver.setCommunityString(m_snmpManager.getSettings().getCommunityString()); + } cfdat.writeList("snmptrapdForward", fwd); } @@ -163,4 +167,8 @@ public void setAlarmServerManager(AlarmServerManager alarmServerManager) { public void setMessageSource(MessageSource messageSource) { m_messageSource = messageSource; } + + public void setSnmpManager(SnmpManager snmpManager) { + m_snmpManager = snmpManager; + } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/Warning.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/Warning.java index b1f2d5103b..56b41f5baf 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/Warning.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/Warning.java @@ -18,6 +18,7 @@ public class Warning { private String m_warning; + private String m_parameter; private String m_page; public Warning(String warning, String page) { @@ -32,4 +33,13 @@ public String getWarning() { public String getPage() { return m_page; } + + public String getParameter() { + return m_parameter; + } + + public void setParameter(String parameter) { + this.m_parameter = parameter; + } + } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/alarm.beans.xml b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/alarm.beans.xml index de51a44769..70899a7e9f 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/alarm.beans.xml +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/alarm.beans.xml @@ -6,6 +6,7 @@ + diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/alarm.hbm.xml b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/alarm.hbm.xml index 664f185923..7fd453ad39 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/alarm.hbm.xml +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/alarm.hbm.xml @@ -46,7 +46,6 @@ - diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/alarm.properties b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/alarm.properties index d0fac1cf07..6af3a4161f 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/alarm.properties +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/alarm/alarm.properties @@ -25,4 +25,6 @@ alarm.BACKUP_FAILED.resolution=Check logs for more details alarm.LDAP_IMPORT_FAILED.label=Ldap import incomplete alarm.LDAP_IMPORT_FAILED.resolution=Check logs for more details alarm.DNS_LOOKUP_FAILED.label=Dns lookup failed -alarm.DNS_LOOKUP_FAILED.resolution=Check DNS settings \ No newline at end of file +alarm.DNS_LOOKUP_FAILED.resolution=Check DNS settings +alarm.LDAP_CONNECTION_FAILED.label=LDAP connection cannot be established +alarm.LDAP_CONNECTION_FAILED.resolution=Check network and LDAP connectivity \ No newline at end of file diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/MyMohApi.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/MyMohApi.java new file mode 100644 index 0000000000..ba86f56ed6 --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/MyMohApi.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2016 eZuce, Inc. All rights reserved. + * Contributed to sipXcom under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.api; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.cxf.jaxrs.model.wadl.Description; + +@Path("/my/moh/") +@Produces({ + MediaType.APPLICATION_JSON, MediaType.TEXT_XML, MediaType.APPLICATION_XML +}) +@Description("My Music On Hold's Management REST API") +public interface MyMohApi extends PromptsApi { + + @Path("settings/moh/audio-source") + @GET + public Response getMohAudioSourceSetting(@Context HttpServletRequest request); + + @Path("settings/moh/audio-source") + @PUT + @Consumes({ + MediaType.TEXT_PLAIN + }) + public Response setMohAudioSourceSetting(String value); + + @Path("settings/moh/audio-source") + @DELETE + public Response deleteMohAudioSourceSetting(); + + @Path("permission") + @GET + public Response getUserMohPermission(@Context HttpServletRequest request); + + @Path("path") + @PUT + public Response createCurrentUserPath(); +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/MyUserApi.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/MyUserApi.java new file mode 100644 index 0000000000..e7e7e6a145 --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/MyUserApi.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2017 eZuce, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.api; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.cxf.jaxrs.model.wadl.Description; +import org.sipfoundry.sipxconfig.api.model.SettingsList; +import org.sipfoundry.sipxconfig.api.model.UserBean; + +@Path("/my/user/") +@Produces({ + MediaType.APPLICATION_JSON, MediaType.TEXT_XML, MediaType.APPLICATION_XML +}) +@Description("My User Management REST API") +public interface MyUserApi { + + @Path("settings") + @GET + public Response getUserSettings( + @Context HttpServletRequest request); + + @Path("settings") + @PUT + @Consumes({ + MediaType.APPLICATION_JSON, MediaType.TEXT_XML, MediaType.APPLICATION_XML + }) + public Response setUserSettings( + @Description("Settings to save") SettingsList settingsList); + + @Path("settings/{path:.*}") + @GET + public Response getUserSetting( + @Description("Path to User setting") @PathParam("path") String path, @Context HttpServletRequest request); + + @Path("settings/{path:.*}") + @PUT + @Consumes({ + MediaType.TEXT_PLAIN + }) + public Response setUserSetting( + @Description("Path to User setting") @PathParam("path") String path, String value); + + @Path("settings/{path:.*}") + @DELETE + public Response deleteUserSetting( + @Description("Path to User setting") @PathParam("path") String path); + + @Path("") + @GET + public Response getUser(); + + @Path("") + @PUT + @Consumes({ + MediaType.APPLICATION_JSON, MediaType.TEXT_XML, MediaType.APPLICATION_XML + }) + public Response updateUser( + @Description("User bean to save") UserBean user); +} + diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/PhoneApi.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/PhoneApi.java index d4d42519cb..bb0735144f 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/PhoneApi.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/PhoneApi.java @@ -29,6 +29,7 @@ import javax.ws.rs.core.Response; import org.apache.cxf.jaxrs.model.wadl.Description; +import org.sipfoundry.sipxconfig.api.model.IdsList; import org.sipfoundry.sipxconfig.api.model.PhoneBean; @Path("/phones/") @@ -61,6 +62,30 @@ public Response getPhones(@Description("First Phone row") @QueryParam("start") I @DELETE public Response deletePhone(@Description("Phone internal id or MAC address") @PathParam("phoneId") String phoneId); + @Path("{phoneId}/sendProfile") + @PUT + public Response sendPhoneProfile(@Description("Phone internal id or MAC address") + @PathParam("phoneId") String phoneId); + + @Path("{phoneId}/sendProfile/restart") + @PUT + public Response sendPhoneProfileRestart(@Description("Phone internal id or MAC address") + @PathParam("phoneId") String phoneId); + + @Path("sendProfile") + @PUT + @Consumes({ + MediaType.APPLICATION_JSON, MediaType.TEXT_XML, MediaType.APPLICATION_XML + }) + public Response sendPhonesProfile(IdsList idsList); + + @Path("sendProfile/restart") + @PUT + @Consumes({ + MediaType.APPLICATION_JSON, MediaType.TEXT_XML, MediaType.APPLICATION_XML + }) + public Response sendPhonesProfileRestart(IdsList idsList); + @Path("{phoneId}") @PUT @Consumes({ diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/UserApi.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/UserApi.java new file mode 100644 index 0000000000..12f243beaa --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/UserApi.java @@ -0,0 +1,133 @@ +/** + * Copyright (c) 2015 eZuce, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.api; + +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.cxf.jaxrs.ext.multipart.Attachment; +import org.apache.cxf.jaxrs.model.wadl.Description; +import org.sipfoundry.sipxconfig.api.model.SettingsList; +import org.sipfoundry.sipxconfig.api.model.UserBean; + +@Path("/users/") +@Produces({ + MediaType.APPLICATION_JSON, MediaType.TEXT_XML, MediaType.APPLICATION_XML +}) +@Description("User Management REST API") +public interface UserApi { + @GET + public Response getUsers(@Description("First User row") @QueryParam("start") Integer startId, + @Description("Number of users to be returned") @QueryParam("limit") Integer limit); + + @Path("{userNameOrAlias}/settings") + @GET + public Response getUserSettings( + @Description("User name or alias") @PathParam("userNameOrAlias") String userNameOrAlias, + @Context HttpServletRequest request); + + @Path("{userNameOrAlias}/settings") + @PUT + @Consumes({ + MediaType.APPLICATION_JSON, MediaType.TEXT_XML, MediaType.APPLICATION_XML + }) + public Response setUserSettings( + @Description("User name or alias") @PathParam("userNameOrAlias") String userNameOrAlias, + @Description("Settings to save") SettingsList settingsList); + + @Path("{userNameOrAlias}/settings/{path:.*}") + @GET + public Response getUserSetting( + @Description("User name or alias") @PathParam("userNameOrAlias") String userNameOrAlias, + @Description("Path to User setting") @PathParam("path") String path, @Context HttpServletRequest request); + + @Path("{userNameOrAlias}/settings/{path:.*}") + @PUT + @Consumes({ + MediaType.TEXT_PLAIN + }) + public Response setUserSetting( + @Description("User extension") @PathParam("userNameOrAlias") String userNameOrAlias, + @Description("Path to User setting") @PathParam("path") String path, String value); + + @Path("{userNameOrAlias}/settings/{path:.*}") + @DELETE + public Response deleteUserSetting( + @Description("User extension") @PathParam("userNameOrAlias") String userNameOrAlias, + @Description("Path to User setting") @PathParam("path") String path); + @PUT + @Path("/upload/settings/{path:.*}") + @Consumes(MediaType.MULTIPART_FORM_DATA) + public Response setUsersSetting(List attachments, + @Description("Path to User setting") @PathParam("path") String path); + + @POST + @Consumes({ + MediaType.APPLICATION_JSON, MediaType.TEXT_XML, MediaType.APPLICATION_XML + }) + public Response newUser(@Description("User bean to save") UserBean user); + + @Path("{userNameOrAlias}") + @GET + public Response getUser(@Description("User name") @PathParam("userNameOrAlias") String userNameOrAlias); + + @Path("{userNameOrAlias}") + @PUT + @Consumes({ + MediaType.APPLICATION_JSON, MediaType.TEXT_XML, MediaType.APPLICATION_XML + }) + public Response updateUser( + @Description("User name") @PathParam("userNameOrAlias") String userNameOrAlias, + @Description("User bean to save") UserBean user); + + @Path("{userNameOrAlias}") + @DELETE + public Response deleteUser(@Description("User name or alias") @PathParam("userNameOrAlias") String userNameOrAlias); + + @Path("{userNameOrAlias}/groups") + @GET + public Response getUserGroups( + @Description("User name or alias") @PathParam("userNameOrAlias") String userId); + + @Path("{userNameOrAlias}/groups") + @DELETE + public Response removeUserGroups( + @Description("User name or alias") @PathParam("userNameOrAlias") String userId); + + @Path("{userNameOrAlias}/groups/{groupName}") + @POST + public Response addUserInGroup( + @Description("User name or alias") @PathParam("userNameOrAlias") String userId, + @Description("User Group name") @PathParam("groupName") String groupName); + + @Path("{userNameOrAlias}/groups/{groupName}") + @DELETE + public Response removeUserFromGroup( + @Description("User name or alias") @PathParam("userNameOrAlias") String phoneId, + @Description("Phone Group name") @PathParam("groupName") String groupName); +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/UserGroupApi.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/UserGroupApi.java new file mode 100644 index 0000000000..1d0e95d164 --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/UserGroupApi.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2015 eZuce, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.api; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.cxf.jaxrs.model.wadl.Description; +import org.sipfoundry.sipxconfig.api.model.GroupBean; +import org.sipfoundry.sipxconfig.api.model.SettingsList; + +@Path("/userGroups/") +@Produces({ + MediaType.APPLICATION_JSON, MediaType.TEXT_XML, MediaType.APPLICATION_XML +}) +@Description("User Groups Management REST API") + +public interface UserGroupApi { + @GET + public Response getUserGroups(); + + @POST + @Consumes({ + MediaType.APPLICATION_JSON, MediaType.TEXT_XML, MediaType.APPLICATION_XML + }) + public Response newUserGroup(@Description("Phone Group bean to save") GroupBean userGroup); + + @Path("{userGroupId}") + @GET + public Response getUserGroup(@Description("User group id or name") + @PathParam("userGroupId") String userGroupId); + + @Path("{groupId}") + @DELETE + public Response deleteUserGroup(@Description("Group internal id or name") @PathParam("groupId") String groupId); + + @Path("{groupId}") + @PUT + @Consumes({ + MediaType.APPLICATION_JSON, MediaType.TEXT_XML, MediaType.APPLICATION_XML + }) + public Response updateUserGroup( + @Description("Phone group internal id or name") @PathParam("groupId") String groupId, + @Description("Phone group bean to save") GroupBean groupBean); + + @Path("{groupId}/up") + @PUT + public Response moveUserGroupUp( + @Description("Phone group internal id or name") @PathParam("groupId") String groupId); + + @Path("{groupId}/down") + @PUT + public Response moveUserGroupDown( + @Description("Phone group internal id or name") @PathParam("groupId") String groupId); + + @Path("{groupName}/settings") + @GET + public Response getGroupSettings( + @Description("Group name") @PathParam("groupName") String groupName, + @Context HttpServletRequest request); + + @Path("{groupName}/settings") + @PUT + @Consumes({ + MediaType.APPLICATION_JSON, MediaType.TEXT_XML, MediaType.APPLICATION_XML + }) + public Response setGroupSettings( + @Description("Group name") @PathParam("groupName") String groupName, + @Description("Settings to save") SettingsList settingsList); + + @Path("{groupName}/settings/{path:.*}") + @GET + public Response getGroupSetting( + @Description("Group name") @PathParam("groupName") String groupName, + @Description("Path to Group setting") @PathParam("path") String path, @Context HttpServletRequest request); + + @Path("{groupName}/settings/{path:.*}") + @PUT + @Consumes({ + MediaType.TEXT_PLAIN + }) + public Response setGroupSetting( + @Description("User extension") @PathParam("groupName") String groupName, + @Description("Path to User setting") @PathParam("path") String path, String value); + + @Path("{groupName}/settings/{path:.*}") + @DELETE + public Response deleteGroupSetting( + @Description("Group name") @PathParam("groupName") String name, + @Description("Path to Group setting") @PathParam("path") String path); +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/api.beans.xml b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/api.beans.xml index 12701760b4..1a2d101f5a 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/api.beans.xml +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/api.beans.xml @@ -30,6 +30,9 @@ + + + @@ -48,6 +51,7 @@ + @@ -57,6 +61,7 @@ + @@ -123,6 +128,10 @@ + + + + @@ -145,5 +154,20 @@ - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/CurrentUser.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/CurrentUser.java new file mode 100644 index 0000000000..c70bc5ad23 --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/CurrentUser.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2016 eZuce, Inc. All rights reserved. + * Contributed to sipXcom under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.api.impl; + +import org.sipfoundry.sipxconfig.common.CoreContext; +import org.sipfoundry.sipxconfig.common.User; +import org.sipfoundry.sipxconfig.security.StandardUserDetailsService; +import org.sipfoundry.sipxconfig.security.UserDetailsImpl; + +public class CurrentUser { + + private CoreContext m_coreContext; + + public User getCurrentUser() { + if (m_coreContext != null) { + UserDetailsImpl userDetails = StandardUserDetailsService.getUserDetails(); + return (userDetails != null) ? m_coreContext.loadUser(userDetails.getUserId()) : null; + } else { + return null; + } + } + + public void setCoreContext(CoreContext coreContext) { + m_coreContext = coreContext; + } + + public CoreContext getCoreContext() { + return m_coreContext; + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/FileManager.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/FileManager.java index 9256acc925..c58333a44b 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/FileManager.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/FileManager.java @@ -31,7 +31,7 @@ import org.apache.cxf.jaxrs.ext.multipart.Attachment; import org.sipfoundry.sipxconfig.api.model.FileList; -public class FileManager { +public class FileManager extends CurrentUser { private String m_path; private String[] m_extensions; diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/GroupApiImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/GroupApiImpl.java new file mode 100644 index 0000000000..182d5afe5c --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/GroupApiImpl.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2015 eZuce, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.api.impl; + +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.sipfoundry.sipxconfig.api.model.GroupList; +import org.sipfoundry.sipxconfig.setting.Group; + +public abstract class GroupApiImpl { + protected Response buildGroupList(List groups, Map count) { + if (groups != null) { + return Response.ok().entity(GroupList.convertGroupList(groups, count)).build(); + } + return Response.status(Status.NOT_FOUND).build(); + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/MyMohApiImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/MyMohApiImpl.java new file mode 100644 index 0000000000..0a63954e3e --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/MyMohApiImpl.java @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2016 eZuce, Inc. All rights reserved. + * Contributed to sipXcom under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.api.impl; + +import java.io.File; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Response; + +import org.apache.cxf.jaxrs.ext.multipart.Attachment; +import org.sipfoundry.sipxconfig.api.MyMohApi; +import org.sipfoundry.sipxconfig.common.User; +import org.sipfoundry.sipxconfig.moh.MusicOnHoldManager; +import org.sipfoundry.sipxconfig.permission.PermissionName; + +public class MyMohApiImpl extends PromptsApiImpl implements MyMohApi { + + private MusicOnHoldManager m_manager; + + public void setMusicOnHoldManager(MusicOnHoldManager manager) { + m_manager = manager; + } + + @Override + public Response getPrompts() { + createPath(); + setPath(m_manager.getAudioDirectoryPath() + File.separator + getCurrentUser().getUserName()); + return super.getPrompts(); + } + + @Override + public Response uploadPrompts(List attachments, HttpServletRequest request) { + createPath(); + setPath(m_manager.getAudioDirectoryPath() + File.separator + getCurrentUser().getUserName()); + return super.uploadPrompts(attachments, request); + } + + public Response downloadPrompt(String promptName) { + createPath(); + setPath(m_manager.getAudioDirectoryPath() + File.separator + getCurrentUser().getUserName()); + return super.downloadPrompt(promptName); + } + + public Response removePrompt(String promptName) { + createPath(); + setPath(m_manager.getAudioDirectoryPath() + File.separator + getCurrentUser().getUserName()); + return super.removePrompt(promptName); + } + + public Response streamPrompt(String promptName) { + createPath(); + setPath(m_manager.getAudioDirectoryPath() + File.separator + getCurrentUser().getUserName()); + return super.streamPrompt(promptName); + } + + @Override + public Response getMohAudioSourceSetting(HttpServletRequest request) { + return ResponseUtils.buildSettingResponse(getCurrentUser(), User.MOH_AUDIO_SOURCE_SETTING, request.getLocale()); + } + + @Override + public Response setMohAudioSourceSetting(String value) { + User user = getCurrentUser(); + user.setSettingValue(User.MOH_AUDIO_SOURCE_SETTING, value); + getCoreContext().saveUser(user); + return Response.ok().build(); + } + + @Override + public Response deleteMohAudioSourceSetting() { + User user = getCurrentUser(); + user.setSettingValue(User.MOH_AUDIO_SOURCE_SETTING, user.getSettingDefaultValue(User.MOH_AUDIO_SOURCE_SETTING)); + getCoreContext().saveUser(user); + return Response.ok().build(); + } + + @Override + public Response createCurrentUserPath() { + createPath(); + return Response.ok().build(); + } + + private void createPath() { + String path = m_manager.getAudioDirectoryPath() + File.separator + getCurrentUser().getUserName(); + File f = new File(path); + if (!f.exists()) { + f.mkdir(); + } + } + + @Override + public Response getUserMohPermission(HttpServletRequest request) { + User user = getCurrentUser(); + Boolean hasPermission = user.hasPermission(PermissionName.MUSIC_ON_HOLD); + if (hasPermission && user.getGroupsAsList().size() > 0) { + hasPermission = user.hasPermission(PermissionName.GROUP_MUSIC_ON_HOLD); + } + + return Response.ok().entity(hasPermission).build(); + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/MyUserApiImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/MyUserApiImpl.java new file mode 100644 index 0000000000..84101ef74a --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/MyUserApiImpl.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2017 eZuce, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.api.impl; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.sipfoundry.sipxconfig.api.MyUserApi; +import org.sipfoundry.sipxconfig.api.model.SettingsList; +import org.sipfoundry.sipxconfig.api.model.UserBean; +import org.sipfoundry.sipxconfig.common.User; +import org.springframework.beans.factory.annotation.Required; + +public class MyUserApiImpl extends CurrentUser implements MyUserApi { + /** + * Only GET functionality is permitted for the moment due to security reasons + */ + private UserApiImpl m_userApiImpl; + + @Override + public Response getUserSettings(HttpServletRequest request) { + User user = getCurrentUser(); + return m_userApiImpl.getUserSettings(user, request); + } + + @Override + public Response setUserSettings(SettingsList settingsList) { + return Response.status(Status.FORBIDDEN).build(); + } + + @Override + public Response getUserSetting(String path, HttpServletRequest request) { + return ResponseUtils.buildSettingResponse(getCurrentUser(), path, request.getLocale()); + } + + @Override + public Response setUserSetting(String path, String value) { + return Response.status(Status.FORBIDDEN).build(); + } + + @Override + public Response deleteUserSetting(String path) { + return Response.status(Status.FORBIDDEN).build(); + + } + + @Override + public Response getUser() { + User user = getCurrentUser(); + return m_userApiImpl.getUser(user); + } + + @Override + public Response updateUser(UserBean userBean) { + return Response.status(Status.FORBIDDEN).build(); + } + + @Required + public void setUserApiImpl(UserApiImpl userApiImpl) { + m_userApiImpl = userApiImpl; + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PagingGroupApiImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PagingGroupApiImpl.java index dd23cb4315..41464c4ac9 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PagingGroupApiImpl.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PagingGroupApiImpl.java @@ -23,8 +23,8 @@ import org.mozilla.javascript.edu.emory.mathcs.backport.java.util.Collections; import org.sipfoundry.sipxconfig.api.PagingGroupApi; import org.sipfoundry.sipxconfig.api.model.PageGroupBean; -import org.sipfoundry.sipxconfig.api.model.PageGroupBean.UserBean; import org.sipfoundry.sipxconfig.api.model.PageGroupList; +import org.sipfoundry.sipxconfig.api.model.UserBean; import org.sipfoundry.sipxconfig.common.CoreContext; import org.sipfoundry.sipxconfig.common.User; import org.sipfoundry.sipxconfig.paging.PagingContext; @@ -106,7 +106,7 @@ private Response populateUsers(PageGroupBean bean, PagingGroup group) { group.setUsers(new HashSet()); for (UserBean userBean : bean.getUsers()) { User user = null; - if (userBean.getId() != null) { + if (userBean.getId() > 0) { user = m_coreContext.getUser(userBean.getId()); } else if (userBean.getUserName() != null) { user = m_coreContext.loadUserByUserNameOrAlias(userBean.getUserName()); diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PhoneApiImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PhoneApiImpl.java index 0664bb7edc..980fb55777 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PhoneApiImpl.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PhoneApiImpl.java @@ -26,6 +26,7 @@ import org.sipfoundry.sipxconfig.api.PhoneApi; import org.sipfoundry.sipxconfig.api.model.GroupBean; import org.sipfoundry.sipxconfig.api.model.GroupList; +import org.sipfoundry.sipxconfig.api.model.IdsList; import org.sipfoundry.sipxconfig.api.model.ModelBean; import org.sipfoundry.sipxconfig.api.model.ModelBean.ModelList; import org.sipfoundry.sipxconfig.api.model.PhoneBean; @@ -33,17 +34,20 @@ import org.sipfoundry.sipxconfig.api.model.SettingsList; import org.sipfoundry.sipxconfig.device.DeviceVersion; import org.sipfoundry.sipxconfig.device.ModelSource; +import org.sipfoundry.sipxconfig.device.ProfileManager; import org.sipfoundry.sipxconfig.phone.Phone; import org.sipfoundry.sipxconfig.phone.PhoneContext; import org.sipfoundry.sipxconfig.phone.PhoneModel; import org.sipfoundry.sipxconfig.setting.Group; import org.sipfoundry.sipxconfig.setting.Setting; import org.sipfoundry.sipxconfig.setting.SettingDao; +import org.springframework.beans.factory.annotation.Required; public class PhoneApiImpl implements PhoneApi { private PhoneContext m_phoneContext; private ModelSource m_modelSource; private SettingDao m_settingDao; + private ProfileManager m_profileManager; @Override public Response getPhones(Integer startId, Integer pageSize) { @@ -223,6 +227,34 @@ public Response deletePhoneSetting(String phoneId, String path) { return Response.status(Status.NOT_FOUND).build(); } + @Override + public Response sendPhoneProfile(String phoneId) { + return sendPhoneProfile(phoneId, false); + } + + @Override + public Response sendPhoneProfileRestart(String phoneId) { + return sendPhoneProfile(phoneId, true); + } + + @Override + public Response sendPhonesProfile(IdsList ids) { + m_profileManager.generateProfiles(ids.getIds(), false, null); + return Response.ok().build(); + } + + @Override + public Response sendPhonesProfileRestart(IdsList ids) { + m_profileManager.generateProfiles(ids.getIds(), true, null); + return Response.ok().build(); + } + + private Response sendPhoneProfile(String phoneId, boolean restart) { + Phone phone = getPhoneByIdOrMac(phoneId); + m_profileManager.generateProfile(phone.getId(), restart, null); + return Response.ok().build(); + } + private Phone getPhoneByIdOrMac(String id) { Phone phone = null; try { @@ -247,4 +279,8 @@ public void setSettingDao(SettingDao settingDao) { m_settingDao = settingDao; } + @Required + public void setProfileManager(ProfileManager profileManager) { + m_profileManager = profileManager; + } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PhoneGroupApiImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PhoneGroupApiImpl.java index e23167320f..43575485ca 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PhoneGroupApiImpl.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PhoneGroupApiImpl.java @@ -16,8 +16,6 @@ import java.util.Collection; import java.util.Collections; -import java.util.List; -import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Response; @@ -25,7 +23,6 @@ import org.sipfoundry.sipxconfig.api.PhoneGroupApi; import org.sipfoundry.sipxconfig.api.model.GroupBean; -import org.sipfoundry.sipxconfig.api.model.GroupList; import org.sipfoundry.sipxconfig.api.model.ModelBean.ModelList; import org.sipfoundry.sipxconfig.api.model.SettingsList; import org.sipfoundry.sipxconfig.device.ModelSource; @@ -37,14 +34,14 @@ import org.sipfoundry.sipxconfig.setting.SettingDao; import org.springframework.beans.factory.annotation.Required; -public class PhoneGroupApiImpl implements PhoneGroupApi { +public class PhoneGroupApiImpl extends GroupApiImpl implements PhoneGroupApi { private PhoneContext m_phoneContext; private SettingDao m_settingDao; private ModelSource m_phoneModelSource; @Override public Response getPhoneGroups() { - return buildPhoneGroupList(m_phoneContext.getGroups(), + return buildGroupList(m_phoneContext.getGroups(), m_settingDao.getGroupMemberCountIndexedByGroupId(Phone.class)); } @@ -52,7 +49,7 @@ public Response getPhoneGroups() { public Response newPhoneGroup(GroupBean groupBean) { Group group = new Group(); GroupBean.convertToPhoneGroup(groupBean, group); - m_phoneContext.saveGroup(group); + m_phoneContext.storeGroup(group); return Response.ok().entity(group.getId()).build(); } @@ -96,7 +93,7 @@ public Response updatePhoneGroup(String groupId, GroupBean groupBean) { Group group = getPhoneGroupByIdOrName(groupId); if (group != null) { GroupBean.convertToPhoneGroup(groupBean, group); - m_phoneContext.saveGroup(group); + m_phoneContext.storeGroup(group); return Response.ok().entity(group.getId()).build(); } return Response.status(Status.NOT_FOUND).build(); @@ -176,13 +173,6 @@ public Response deletePhoneGroupSetting(String groupId, String modelName, String return Response.status(Status.NOT_FOUND).build(); } - private Response buildPhoneGroupList(List phoneGroups, Map count) { - if (phoneGroups != null) { - return Response.ok().entity(GroupList.convertGroupList(phoneGroups, count)).build(); - } - return Response.status(Status.NOT_FOUND).build(); - } - @Required public void setPhoneContext(PhoneContext phoneContext) { m_phoneContext = phoneContext; diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PhoneLineApiImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PhoneLineApiImpl.java index bb21594f93..55a7aae30f 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PhoneLineApiImpl.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/PhoneLineApiImpl.java @@ -14,8 +14,7 @@ */ package org.sipfoundry.sipxconfig.api.impl; -import java.util.Collection; -import java.util.List; +import java.util.Collections; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Response; @@ -58,14 +57,14 @@ public Response newLine(String phoneId, LineBean lineBean) { if (phone == null) { return Response.status(Status.NOT_FOUND).entity(PHONE_NOT_FOUND).build(); } - Line line = phone.createLine(); if (lineBean.getUser() != null) { User user = m_coreContext.loadUserByUserNameOrAlias(lineBean.getUser()); if (user == null) { return Response.status(Status.NOT_FOUND).entity("User not found").build(); } - line.setUser(user); + m_phoneContext.addUsersToPhone(phone.getId(), Collections.singleton(user.getId())); } else { + Line line = phone.createLine(); LineInfo lineInfo = new LineInfo(); lineInfo.setUserId(lineBean.getUserId()); lineInfo.setDisplayName(lineBean.getDisplayName()); @@ -75,9 +74,9 @@ public Response newLine(String phoneId, LineBean lineBean) { lineInfo.setRegistrationServerPort(lineBean.getRegistrationServerPort()); lineInfo.setVoiceMail(lineBean.getVoicemail()); line.setLineInfo(lineInfo); + phone.addLine(line); + m_phoneContext.storePhone(phone); } - phone.addLine(line); - m_phoneContext.storePhone(phone); return Response.ok().build(); } @@ -105,8 +104,7 @@ public Response deletePhoneLine(String phoneId, Integer lineId) { Phone phone = getPhoneByIdOrMac(phoneId); if (phone != null) { if (line != null) { - Collection lines = DataCollectionUtil.removeByPrimaryKey(phone.getLines(), lineId); - phone.setLines((List) lines); + DataCollectionUtil.removeByPrimaryKey(phone.getLines(), line.getId()); m_phoneContext.storePhone(phone); return Response.ok().build(); } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/UserApiImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/UserApiImpl.java new file mode 100644 index 0000000000..0273b88e81 --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/UserApiImpl.java @@ -0,0 +1,348 @@ +/** + * Copyright (c) 2015 eZuce, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.api.impl; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Set; + +import javax.activation.DataHandler; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.apache.commons.beanutils.ConvertUtils; +import org.apache.commons.beanutils.converters.DateConverter; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.cxf.jaxrs.ext.multipart.Attachment; +import org.sipfoundry.commons.userdb.profile.UserProfile; +import org.sipfoundry.sipxconfig.api.UserApi; +import org.sipfoundry.sipxconfig.api.model.GroupBean; +import org.sipfoundry.sipxconfig.api.model.GroupList; +import org.sipfoundry.sipxconfig.api.model.SettingBean; +import org.sipfoundry.sipxconfig.api.model.SettingsList; +import org.sipfoundry.sipxconfig.api.model.UserBean; +import org.sipfoundry.sipxconfig.api.model.UserProfileBean; +import org.sipfoundry.sipxconfig.api.model.UsersList; +import org.sipfoundry.sipxconfig.branch.Branch; +import org.sipfoundry.sipxconfig.branch.BranchManager; +import org.sipfoundry.sipxconfig.common.CoreContext; +import org.sipfoundry.sipxconfig.common.User; +import org.sipfoundry.sipxconfig.setting.Group; +import org.sipfoundry.sipxconfig.setting.Setting; +import org.sipfoundry.sipxconfig.setting.SettingDao; +import org.springframework.beans.factory.annotation.Required; + +public class UserApiImpl implements UserApi { + private static final String COMMA = ","; + private static final Log LOG = LogFactory.getLog(UserApiImpl.class); + private CoreContext m_coreContext; + private SettingDao m_settingDao; + private BranchManager m_branchManager; + + public UserApiImpl() { + //Required by: https://issues.apache.org/jira/browse/BEANUTILS-387 + ConvertUtils.register(new DateConverter(null), Date.class); + } + + @Override + public Response setUserSetting(String userNameOrAlias, String path, String value) { + User user = m_coreContext.loadUserByUserNameOrAlias(userNameOrAlias); + return setUserSetting(user, path, value); + } + + public Response setUserSetting(User user, String path, String value) { + if (user != null) { + user.setSettingValue(path, value); + m_coreContext.saveUser(user); + return Response.ok().build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Override + public Response deleteUserSetting(String userNameOrAlias, String path) { + User user = m_coreContext.loadUserByUserNameOrAlias(userNameOrAlias); + return deleteUserSetting(user, path); + } + + public Response deleteUserSetting(User user, String path) { + if (user != null) { + Setting setting = user.getSettings().getSetting(path); + setting.setValue(setting.getDefaultValue()); + m_coreContext.saveUser(user); + return Response.ok().build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Override + public Response setUsersSetting(List attachments, String path) { + for (Attachment attachment : attachments) { + DataHandler dataHandler = attachment.getDataHandler(); + InputStream inputStream = null; + BufferedReader br = null; + try { + // parse csv file + inputStream = dataHandler.getInputStream(); + br = new BufferedReader(new InputStreamReader(inputStream)); + String line; + while ((line = br.readLine()) != null) { + String[] userSetting = line.split(COMMA); + User user = m_coreContext.loadUserByUserName(userSetting[0]); + if (user != null) { + Setting setting = user.getSettings().getSetting(path); + setting.setValue(userSetting[1]); + LOG.debug("User : " + userSetting[0] + " " + userSetting[1]); + m_coreContext.saveUser(user); + } + } + } catch (Exception ex) { + return Response.status(Status.EXPECTATION_FAILED).build(); + } finally { + IOUtils.closeQuietly(br); + IOUtils.closeQuietly(inputStream); + } + } + return Response.ok("upload success").build(); + } + + @Override + public Response getUsers(Integer startId, Integer pageSize) { + if (startId != null && pageSize != null) { + return buildUserList(m_coreContext.loadUsersByPage(startId, pageSize)); + } + return buildUserList(m_coreContext.loadUsers()); + } + + @Override + public Response newUser(UserBean userBean) { + User user = m_coreContext.newUser(); + convertToUser(userBean, user); + m_coreContext.saveUser(user); + return Response.ok().entity(user.getId()).build(); + } + + private Response buildUserList(List users) { + if (users != null) { + return Response.ok().entity(UsersList.convertUserList(users)).build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + public void convertToUser(UserBean userBean, User user) { + String beanUserName = userBean.getUserName(); + if (beanUserName != null) { + user.setUserName(userBean.getUserName()); + } + + String beanFirstName = userBean.getFirstName(); + if (beanFirstName != null) { + user.setFirstName(userBean.getFirstName()); + } + + String beanLastName = userBean.getLastName(); + if (beanLastName != null) { + user.setLastName(userBean.getLastName()); + } + + Set aliases = userBean.getAliases(); + if (aliases != null) { + user.setAliases(userBean.getAliases()); + } + + Boolean beanNotified = userBean.isNotified(); + if (beanNotified != null) { + user.setNotified(userBean.isNotified()); + } + + String branchName = userBean.getBranchName(); + if (branchName != null) { + m_branchManager.getBranch(branchName); + Branch branch = m_branchManager.getBranch(branchName); + user.setBranch(branch); + } + String beanVoicemailPin = userBean.getVoicemailPin(); + if (beanVoicemailPin != null) { + user.setVoicemailPin(userBean.getVoicemailPin()); + } + String beanPintoken = userBean.getPintoken(); + if (beanPintoken != null) { + user.setPintoken(userBean.getPintoken()); + } + String beanSipPassword = userBean.getSipPassword(); + if (beanSipPassword != null) { + user.setSipPassword(userBean.getSipPassword()); + } + UserProfileBean userProfileBean = userBean.getUserProfile(); + if (userProfileBean != null) { + UserProfile userProfile = user.getUserProfile(); + UserProfileBean.convertToUserProfile(userBean.getUserProfile(), userProfile); + user.setUserProfile(userProfile); + } + + if (userBean.getGroups() != null) { + String groupNames = user.getGroupsNames(); + for (GroupBean groupBean : userBean.getGroups()) { + String groupName = groupBean.getName(); + if (!StringUtils.contains(groupNames, groupName)) { + // add group only if it doesn't already exist + Group g = m_settingDao.getGroupCreateIfNotFound(User.GROUP_RESOURCE_ID, groupBean.getName()); + user.addGroup(g); + } + } + } + } + + @Override + public Response getUser(String userNameOrAlias) { + User user = m_coreContext.loadUserByUserNameOrAlias(userNameOrAlias); + return getUser(user); + } + + public Response getUser(User user) { + if (user != null) { + return Response.ok().entity(UserBean.convertUser(user)).build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Override + public Response updateUser(String userNameOrAlias, UserBean userBean) { + User user = m_coreContext.loadUserByUserNameOrAlias(userNameOrAlias); + return updateUser(user, userBean); + } + + public Response updateUser(User user, UserBean userBean) { + if (user != null) { + convertToUser(userBean, user); + m_coreContext.saveUser(user); + return Response.ok().entity(user.getId()).build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Override + public Response getUserGroups(String userNameOrAlias) { + User user = m_coreContext.loadUserByUserNameOrAlias(userNameOrAlias); + if (user != null) { + return Response.ok().entity(GroupList.convertGroupList(user.getGroupsAsList(), null)).build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Override + public Response removeUserGroups(String userNameOrAlias) { + User user = m_coreContext.loadUserByUserNameOrAlias(userNameOrAlias); + if (user != null) { + user.setGroupsAsList(new ArrayList()); + m_coreContext.saveUser(user); + return Response.ok().build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Override + public Response addUserInGroup(String userNameOrAlias, String groupName) { + User user = m_coreContext.loadUserByUserNameOrAlias(userNameOrAlias); + if (user != null) { + Group g = m_settingDao.getGroupCreateIfNotFound(User.GROUP_RESOURCE_ID, groupName); + user.addGroup(g); + m_coreContext.saveUser(user); + return Response.ok().build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Override + public Response removeUserFromGroup(String userNameOrAlias, String groupName) { + User user = m_coreContext.loadUserByUserNameOrAlias(userNameOrAlias); + if (user != null) { + Group group = m_settingDao.getGroupByName(User.GROUP_RESOURCE_ID, groupName); + if (group != null) { + user.removeGroup(group); + m_coreContext.saveUser(user); + return Response.ok().build(); + } + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Required + public void setCoreContext(CoreContext coreContext) { + m_coreContext = coreContext; + } + + @Required + public void setSettingDao(SettingDao settingDao) { + m_settingDao = settingDao; + } + + public void setBranchManager(BranchManager branchManager) { + m_branchManager = branchManager; + } + + @Override + public Response getUserSettings(String userNameOrAlias, HttpServletRequest request) { + User user = m_coreContext.loadUserByUserNameOrAlias(userNameOrAlias); + return getUserSettings(user, request); + } + + public Response getUserSettings(User user, HttpServletRequest request) { + if (user != null) { + Setting settings = user.getSettings(); + return Response.ok().entity(SettingsList.convertSettingsList(settings, request.getLocale())).build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Override + public Response getUserSetting(String userId, String path, HttpServletRequest request) { + return ResponseUtils.buildSettingResponse( + m_coreContext.loadUserByUserNameOrAlias(userId), path, request.getLocale()); + } + + @Override + public Response deleteUser(String userNameOrAlias) { + m_coreContext.deleteUsersByUserName(Collections.singleton(userNameOrAlias)); + return Response.ok().build(); + } + + @Override + public Response setUserSettings(String userNameOrAlias, SettingsList settingsList) { + User user = m_coreContext.loadUserByUserNameOrAlias(userNameOrAlias); + return setUserSettings(user, settingsList); + } + + public Response setUserSettings(User user, SettingsList settingsList) { + if (user != null) { + List settingsBean = settingsList.getSettings(); + for (SettingBean bean : settingsBean) { + user.setSettingValue(bean.getPath(), bean.getValue()); + } + m_coreContext.saveUser(user); + return Response.ok().build(); + } + return Response.status(Status.NOT_FOUND).build(); + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/UserGroupApiImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/UserGroupApiImpl.java new file mode 100644 index 0000000000..88cfc211f5 --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/impl/UserGroupApiImpl.java @@ -0,0 +1,183 @@ +/** + * Copyright (c) 2015 eZuce, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.api.impl; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.sipfoundry.sipxconfig.api.UserGroupApi; +import org.sipfoundry.sipxconfig.api.model.GroupBean; +import org.sipfoundry.sipxconfig.api.model.SettingBean; +import org.sipfoundry.sipxconfig.api.model.SettingsList; +import org.sipfoundry.sipxconfig.common.CoreContext; +import org.sipfoundry.sipxconfig.common.User; +import org.sipfoundry.sipxconfig.setting.Group; +import org.sipfoundry.sipxconfig.setting.SettingDao; +import org.springframework.beans.factory.annotation.Required; + +public class UserGroupApiImpl extends GroupApiImpl implements UserGroupApi { + private CoreContext m_coreContext; + private SettingDao m_settingDao; + + @Override + public Response getUserGroups() { + return buildGroupList(m_coreContext.getGroups(), + m_settingDao.getGroupMemberCountIndexedByGroupId(User.class)); + } + + @Override + public Response newUserGroup(GroupBean groupBean) { + Group group = new Group(); + GroupBean.convertToUserGroup(groupBean, group); + m_coreContext.storeGroup(group); + return Response.ok().entity(group.getId()).build(); + } + + @Override + public Response getUserGroup(String userGroupId) { + Group group = getUserGroupByIdOrName(userGroupId); + if (group != null) { + GroupBean groupBean = new GroupBean(); + GroupBean.convertGroup(group, groupBean); + return Response.ok().entity(groupBean).build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Override + public Response deleteUserGroup(String id) { + Group group = getUserGroupByIdOrName(id); + if (group != null) { + if (m_coreContext.deleteGroups(Collections.singletonList(group.getId()))) { + return Response.status(Status.FORBIDDEN).build(); + } + return Response.ok().build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + private Group getUserGroupByIdOrName(String id) { + Group group = null; + try { + int groupId = Integer.parseInt(id); + group = m_settingDao.loadGroup(groupId); + } catch (NumberFormatException e) { + group = m_coreContext.getGroupByName(id, false); + } + return group; + } + + @Override + public Response updateUserGroup(String groupId, GroupBean groupBean) { + Group group = getUserGroupByIdOrName(groupId); + if (group != null) { + GroupBean.convertToUserGroup(groupBean, group); + m_coreContext.storeGroup(group); + return Response.ok().entity(group.getId()).build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Override + public Response moveUserGroupUp(String groupId) { + Group group = getUserGroupByIdOrName(groupId); + if (group != null) { + m_settingDao.moveGroups(m_coreContext.getGroups(), Collections.singletonList(group.getId()), -1); + return Response.ok().entity(group.getId()).build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Override + public Response moveUserGroupDown(String groupId) { + Group group = getUserGroupByIdOrName(groupId); + if (group != null) { + m_settingDao.moveGroups(m_coreContext.getGroups(), Collections.singletonList(group.getId()), 1); + return Response.ok().entity(group.getId()).build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Required + public void setCoreContext(CoreContext coreContext) { + m_coreContext = coreContext; + } + + @Required + public void setSettingDao(SettingDao settingDao) { + m_settingDao = settingDao; + } + + @Override + public Response getGroupSettings(String groupName, HttpServletRequest request) { + Group group = m_coreContext.getGroupByName(groupName, false); + if (group != null) { + Map settings = group.getDatabaseValues(); + return Response.ok().entity(SettingsList.convertDatabaseValues(settings, request.getLocale())).build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Override + public Response getGroupSetting(String groupName, String path, HttpServletRequest request) { + Group group = m_coreContext.getGroupByName(groupName, false); + SettingBean bean = new SettingBean(); + bean.setPath(path); + bean.setValue(group.getSettingValue(path)); + + return Response.ok().entity(bean).build(); + } + + @Override + public Response setGroupSetting(String groupName, String path, String value) { + Group group = m_coreContext.getGroupByName(groupName, false); + if (group != null) { + group.setSettingValue(path, value); + m_coreContext.storeGroup(group); + return Response.ok().build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Override + public Response deleteGroupSetting(String name, String path) { + Group group = m_coreContext.getGroupByName(name, false); + if (group != null) { + group.getDatabaseValues().remove(path); + m_coreContext.storeGroup(group); + return Response.ok().build(); + } + return Response.status(Status.NOT_FOUND).build(); + } + + @Override + public Response setGroupSettings(String groupName, SettingsList settingsList) { + Group group = m_coreContext.getGroupByName(groupName, false); + if (group != null) { + List settingsBean = settingsList.getSettings(); + for (SettingBean bean : settingsBean) { + group.setSettingValue(bean.getPath(), bean.getValue()); + } + m_coreContext.storeGroup(group); + return Response.ok().build(); + } + return Response.status(Status.NOT_FOUND).build(); + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/BeanUtil.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/BeanUtil.java new file mode 100644 index 0000000000..d6aeebc2c5 --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/BeanUtil.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2015 eZuce, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ + +package org.sipfoundry.sipxconfig.api.model; + +import java.lang.reflect.InvocationTargetException; + +import org.apache.commons.beanutils.BeanUtilsBean; + +public final class BeanUtil { + + private BeanUtil() { + + } + public static void copyNotNullProperties(Object dest, Object source) + throws IllegalAccessException, InvocationTargetException { + new BeanUtilsBean() { + @Override + public void copyProperty(Object dest, String name, Object value) + throws IllegalAccessException, InvocationTargetException { + if (value != null) { + super.copyProperty(dest, name, value); + } + } + } .copyProperties(dest, source); + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/GroupBean.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/GroupBean.java index dc2087cea1..57bb046936 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/GroupBean.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/GroupBean.java @@ -22,6 +22,7 @@ import javax.xml.bind.annotation.XmlType; import org.codehaus.jackson.annotate.JsonPropertyOrder; +import org.sipfoundry.sipxconfig.common.User; import org.sipfoundry.sipxconfig.phone.Phone; import org.sipfoundry.sipxconfig.setting.Group; @@ -111,4 +112,8 @@ private static void convertToGroup(GroupBean groupBean, Group group, String res public static void convertToPhoneGroup(GroupBean groupBean, Group group) { convertToGroup(groupBean, group, Phone.GROUP_RESOURCE_ID); } + + public static void convertToUserGroup(GroupBean groupBean, Group group) { + convertToGroup(groupBean, group, User.GROUP_RESOURCE_ID); + } } diff --git a/sipXconfig/web/unitelite/scripts/modules/underscore.js b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/IdsList.java similarity index 56% rename from sipXconfig/web/unitelite/scripts/modules/underscore.js rename to sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/IdsList.java index 889de7a4ed..eadd0d757b 100644 --- a/sipXconfig/web/unitelite/scripts/modules/underscore.js +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/IdsList.java @@ -1,6 +1,6 @@ -/* - * Copyright (c) eZuce, Inc. All rights reserved. - * Contributed to SIPfoundry under a Contributor Agreement +/** + * Copyright (c) 2016 eZuce, Inc. All rights reserved. + * Contributed to sipXcom under a Contributor Agreement * * This software is free software; you can redistribute it and/or modify it under * the terms of the Affero General Public License (AGPL) as published by the @@ -12,16 +12,18 @@ * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. */ +package org.sipfoundry.sipxconfig.api.model; -(function() { - 'use strict'; - var underscore = angular.module('underscore', []); +import java.util.Collection; - /** - * angular DI for Underscore.js - * @return {Object} Underscore.js object - */ - underscore.factory('_', function() { - return window._; - }); -})(); +public class IdsList { + private Collection m_ids; + + public Collection getIds() { + return m_ids; + } + + public void setIds(Collection ids) { + m_ids = ids; + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/PageGroupBean.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/PageGroupBean.java index 6bf933e1cc..55e1e819b3 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/PageGroupBean.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/PageGroupBean.java @@ -16,6 +16,8 @@ import java.util.ArrayList; import java.util.List; +import java.util.Set; +import java.util.TreeSet; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; @@ -114,7 +116,7 @@ public static PageGroupBean convertGroup(PagingGroup group) { userBean.setUserName(user.getUserName()); userBean.setLastName(user.getLastName()); userBean.setFirstName(user.getFirstName()); - List aliases = new ArrayList(); + Set aliases = new TreeSet(); for (String alias : user.getAliases()) { aliases.add(alias); } @@ -134,58 +136,4 @@ public static void populateGroup(PageGroupBean bean, PagingGroup group) { group.setSound(bean.getSound()); group.setTimeout(bean.getTimeout()); } - - @XmlRootElement(name = "User") - @XmlType(propOrder = { - "id", "userName", "lastName", "firstName", "aliases" - }) - public static class UserBean { - private Integer m_id; - private String m_userName; - private String m_lastName; - private String m_firstName; - private List m_aliases; - - public Integer getId() { - return m_id; - } - - public void setId(Integer id) { - m_id = id; - } - - public String getUserName() { - return m_userName; - } - - public void setUserName(String name) { - m_userName = name; - } - - public String getLastName() { - return m_lastName; - } - - public void setLastName(String name) { - m_lastName = name; - } - - public String getFirstName() { - return m_firstName; - } - - public void setFirstName(String name) { - m_firstName = name; - } - - public void setAliases(List aliases) { - m_aliases = aliases; - } - - @XmlElementWrapper(name = "Aliases") - @XmlElement(name = "Alias") - public List getAliases() { - return m_aliases; - } - } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/SettingsList.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/SettingsList.java index e04132a975..41439473a1 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/SettingsList.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/SettingsList.java @@ -14,8 +14,11 @@ */ package org.sipfoundry.sipxconfig.api.model; +import static java.util.Map.Entry; + import java.util.ArrayList; import java.util.Collection; +import java.util.Map; import java.util.List; import java.util.Locale; @@ -51,6 +54,19 @@ public static SettingsList convertSettingsList(Setting settings, Locale locale) return list; } + public static SettingsList convertDatabaseValues(Map databaseValues, Locale locale) { + List settingsList = new ArrayList(); + for (Entry entry : databaseValues.entrySet()) { + SettingBean bean = new SettingBean(); + bean.setPath(entry.getKey()); + bean.setValue(entry.getValue()); + settingsList.add(bean); + } + SettingsList list = new SettingsList(); + list.setSettings(settingsList); + return list; + } + private static void addSettingToList(List settingsList, Setting settings, Locale locale) { Collection settingsToIterate = null; if (settings instanceof SettingArray) { diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/UserBean.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/UserBean.java new file mode 100644 index 0000000000..b1c63f1daf --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/UserBean.java @@ -0,0 +1,176 @@ +/** + * Copyright (c) 2015 eZuce, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ + +package org.sipfoundry.sipxconfig.api.model; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +import org.codehaus.jackson.annotate.JsonPropertyOrder; +import org.sipfoundry.sipxconfig.branch.Branch; +import org.sipfoundry.sipxconfig.common.User; + +@XmlRootElement(name = "User") +@XmlType(propOrder = { + "id", "userName", "lastName", "firstName", "aliases", "sipPassword", "pintoken", + "voicemailPin", "branchName", "userProfile", "notified", + "groups" + }) +@JsonPropertyOrder({ + "id", "userName", "lastName", "firstName", "aliases", "sipPassword", "pintoken", + "voicemailPin", "branchName", "userProfile", "notified", + "groups" + }) +public class UserBean { + private int m_id; + private String m_userName; + private String m_lastName; + private String m_firstName; + private Set m_aliases = new LinkedHashSet(); + private String m_sipPassword; + + private String m_pintoken; + private String m_voicemailPin; + private String m_branchName; + private UserProfileBean m_userProfile; + private Boolean m_notified; + private List m_groups; + + public int getId() { + return m_id; + } + + public void setId(int id) { + m_id = id; + } + + public String getUserName() { + return m_userName; + } + + public void setUserName(String name) { + m_userName = name; + } + + public String getLastName() { + return m_lastName; + } + + public void setLastName(String name) { + m_lastName = name; + } + + public String getFirstName() { + return m_firstName; + } + + public void setFirstName(String name) { + m_firstName = name; + } + + public void setAliases(Set aliases) { + m_aliases = aliases; + } + + @XmlElementWrapper(name = "Aliases") + @XmlElement(name = "Alias") + public Set getAliases() { + return m_aliases; + } + + public String getSipPassword() { + return m_sipPassword; + } + + public void setSipPassword(String sipPassword) { + m_sipPassword = sipPassword; + } + + public String getPintoken() { + return m_pintoken; + } + + public void setPintoken(String pintoken) { + m_pintoken = pintoken; + } + + public String getVoicemailPin() { + return m_voicemailPin; + } + + public void setVoicemailPin(String voicemailPin) { + m_voicemailPin = voicemailPin; + } + + public String getBranchName() { + return m_branchName; + } + + public void setBranchName(String branchName) { + m_branchName = branchName; + } + + public UserProfileBean getUserProfile() { + return m_userProfile; + } + + public void setUserProfile(UserProfileBean userProfile) { + m_userProfile = userProfile; + } + + public Boolean isNotified() { + return m_notified; + } + + public void setNotified(Boolean notified) { + m_notified = notified; + } + + public static UserBean convertUser(User user) { + UserBean bean = new UserBean(); + bean.setId(user.getId()); + bean.setAliases(user.getAliases()); + Branch branch = user.getBranch(); + if (branch != null) { + bean.setBranchName(user.getBranch().getName()); + } + bean.setVoicemailPin(user.getVoicemailPintoken()); + bean.setFirstName(user.getFirstName()); + bean.setLastName(user.getLastName()); + bean.setNotified(user.isNotified()); + bean.setPintoken(user.getPintoken()); + bean.setSipPassword(user.getSipPassword()); + bean.setUserName(user.getUserName()); + bean.setUserProfile(UserProfileBean.convertUserProfile(user.getUserProfile())); + bean.setGroups(GroupBean.buildGroupList(user.getGroupsAsList(), null)); + return bean; + } + + @XmlElementWrapper(name = "Groups") + @XmlElement(name = "Group") + public List getGroups() { + return m_groups; + } + + public void setGroups(List groups) { + m_groups = groups; + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/UserProfileBean.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/UserProfileBean.java new file mode 100644 index 0000000000..57ca77a446 --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/UserProfileBean.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2015 eZuce, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.api.model; + +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +import org.apache.commons.beanutils.BeanUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.codehaus.jackson.annotate.JsonPropertyOrder; +import org.sipfoundry.commons.userdb.profile.UserProfile; + +@XmlRootElement(name = "UserProfile") +@XmlType(propOrder = { + "userid", "userName", "authAccountName", "firstName", "lastName", "jobTitle", "jobDept", + "companyName", "assistantName", "location", + "homeAddress", "officeAddress", "branchAddress", "cellPhoneNumber", "homePhoneNumber", + "assistantPhoneNumber", "faxNumber", + "didNumber", "imId", "imDisplayName", "alternateImId", "emailAddress", "alternateEmailAddress", + "emailAddressAliasesSet", + "emailAddressAliases", "useBranchAddress", "branchName", "manager", "salutation", "employeeId", + "twiterName", "linkedinName", + "facebookName", "xingName", "timestamp", "avatar", "extAvatar", "useExtAvatar", "enabled", + "ldapManaged", "lastImportedDate", + "disabledDate", "custom1", "custom2", "custom3", "userId", "salutationId" + }) +@JsonPropertyOrder({ + "userid", "userName", "authAccountName", "firstName", "lastName", "jobTitle", "jobDept", + "companyName", "assistantName", "location", + "homeAddress", "officeAddress", "branchAddress", "cellPhoneNumber", "homePhoneNumber", + "assistantPhoneNumber", "faxNumber", + "didNumber", "imId", "imDisplayName", "alternateImId", "emailAddress", "alternateEmailAddress", + "emailAddressAliasesSet", + "emailAddressAliases", "useBranchAddress", "branchName", "manager", "salutation", "employeeId", + "twiterName", "linkedinName", + "facebookName", "xingName", "timestamp", "avatar", "extAvatar", "useExtAvatar", "enabled", + "ldapManaged", "lastImportedDate", + "disabledDate", "custom1", "custom2", "custom3", "userId", "salutationId" + }) + +public class UserProfileBean extends UserProfile { + private static final Log LOG = LogFactory.getLog(UserProfileBean.class); + + public static UserProfileBean convertUserProfile(UserProfile userProfile) { + if (userProfile == null) { + return null; + } + try { + UserProfileBean bean = new UserProfileBean(); + BeanUtils.copyProperties(bean, userProfile); + return bean; + } catch (Exception ex) { + return null; + } + } + + public static void convertToUserProfile(UserProfileBean userProfileBean, UserProfile userProfile) { + try { + BeanUtil.copyNotNullProperties(userProfile, userProfileBean); + } catch (Exception e) { + LOG.error("Cannot marshal properties"); + } + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/UsersList.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/UsersList.java new file mode 100644 index 0000000000..3af5a82ad1 --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/api/model/UsersList.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2015 eZuce, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.api.model; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.sipfoundry.sipxconfig.common.User; + +@XmlRootElement(name = "Users") +public class UsersList { + + private List m_users; + + public void setUsers(List users) { + m_users = users; + } + + @XmlElement(name = "User") + public List getUsers() { + if (m_users == null) { + m_users = new ArrayList(); + } + return m_users; + } + + public static UsersList convertUserList(List users) { + List userList = new ArrayList(); + for (User user : users) { + userList.add(UserBean.convertUser(user)); + } + UsersList list = new UsersList(); + list.setUsers(userList); + return list; + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/ArchiveDefinition.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/ArchiveDefinition.java index d262fe27e3..181eb997ee 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/ArchiveDefinition.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/ArchiveDefinition.java @@ -28,6 +28,7 @@ public Object transform(Object arg0) { //if the service is running on more than one node, execute backup only on one of them private boolean m_singleNodeBackup = true; private boolean m_singleNodeRestore = true; + private String m_backupHost; public ArchiveDefinition(String id, String backupCommand, String restoreCommand) { m_id = id; @@ -62,6 +63,14 @@ public boolean isSingleNodeRestore() { return m_singleNodeRestore; } + public String getBackupHost() { + return m_backupHost; + } + + public void setBackupHost(String backupHost) { + m_backupHost = backupHost; + } + @Override public int compareTo(Object arg0) { ArchiveDefinition arch = (ArchiveDefinition) arg0; diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/BackupApi.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/BackupApi.java index 5e31c0388d..c3dcbb3b93 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/BackupApi.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/BackupApi.java @@ -255,6 +255,7 @@ void putOrPost(Representation entity) throws ResourceException { settingJsonReader.read(m_settings.getDb(), meta.get("dbSettings")); m_plan.setIncludeDeviceFiles((Boolean) m_settings.getIncludeDeviceFiles().getTypedValue()); settingJsonReader.read(m_settings.getSettings().getSetting("ftp"), meta.get("ftpSettings")); + settingJsonReader.read(m_settings.getSettings().getSetting("general"), meta.get("generalSettings")); } catch (IOException e) { throw new ResourceException(Status.SERVER_ERROR_INTERNAL, e.getMessage()); } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/BackupConfig.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/BackupConfig.java index d6ef05fd18..ed70b92709 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/BackupConfig.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/BackupConfig.java @@ -23,7 +23,9 @@ import java.io.Writer; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.TreeSet; @@ -31,6 +33,8 @@ import org.apache.commons.collections.Predicate; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.sipfoundry.commons.security.Util; import org.sipfoundry.sipxconfig.cfgmgt.CfengineModuleConfiguration; import org.sipfoundry.sipxconfig.cfgmgt.ConfigManager; @@ -42,17 +46,22 @@ import org.sipfoundry.sipxconfig.feature.FeatureChangeValidator; import org.sipfoundry.sipxconfig.feature.FeatureListener; import org.sipfoundry.sipxconfig.feature.FeatureManager; +import org.sipfoundry.sipxconfig.ivr.Ivr; import org.sipfoundry.sipxconfig.setting.Setting; +import org.springframework.beans.factory.annotation.Required; public class BackupConfig implements ConfigProvider, FeatureListener { + private static final Log LOG = LogFactory.getLog(BackupConfig.class); + private static final String RESTORE = "restore"; private BackupManager m_backupManager; private ConfigManager m_configManager; + private Ivr m_ivr; private boolean m_dirty; @Override public void replicate(ConfigManager manager, ConfigRequest request) throws IOException { - if (!request.applies(BackupManager.FEATURE) && !m_dirty) { + if (!request.applies(BackupManager.FEATURE, Ivr.FEATURE) && !m_dirty) { return; } @@ -60,6 +69,9 @@ public void replicate(ConfigManager manager, ConfigRequest request) throws IOExc Collection plans = m_backupManager.getBackupPlans(); List hosts = manager.getLocationManager().getLocationsList(); + //save new default backup host if necessary + m_ivr.saveDefaultIvrBackupHost(); + writeCfConfig(manager.getGlobalDataDirectory(), hosts, settings); for (BackupPlan plan : plans) { @@ -97,10 +109,16 @@ void writeCfConfig(File dir, List hosts, BackupSettings settings) thro //Write definition ids where backup should take place (we can have restore to take place on one node //and backup on other node, therefore we need to check if backup command is not null, //otherwise we might end up with duplicates) - if (definition.getBackupCommand() != null) { - cfg.write(StringUtils.replace(definition.getId(), ".", "_"), host.getAddress()); + //Host might be specified where the backup to run, if service is redundant (voicemail for example) + String backupHost = definition.getBackupHost(); + LOG.debug("BACKUP CONFIG cf - backup host: " + backupHost); + if (!StringUtils.isEmpty(definition.getBackupCommand()) + && (backupHost == null || backupHost.equals(host.getAddress()))) { + cfg.write(StringUtils.replace(definition.getId(), ".", "_"), + backupHost == null ? host.getAddress() : backupHost); } } + cfg.write("mem", settings.getMem()); } } finally { IOUtils.closeQuietly(cfdat); @@ -115,24 +133,33 @@ void writeConfig(Writer w, BackupPlan plan, Collection hosts, BackupSe config.startStruct("hosts"); final Set configuredBackupArchives = new TreeSet(); final Set configuredRestoreArchives = new TreeSet(); - for (Location host : hosts) { + for (final Location host : hosts) { Collection possibleDefIds = m_backupManager.getArchiveDefinitions(host, plan, settings); final BackupRestore execBackupRestore = new BackupRestore(); - + LOG.debug("BACKUP CONFIG - host: " + host.getFqdn() + " possible definitions: " + possibleDefIds); Collection defIds = CollectionUtils.select(possibleDefIds, new Predicate() { @Override public boolean evaluate(Object arg0) { ArchiveDefinition def = (ArchiveDefinition) arg0; - boolean selected = selectedDefIds.contains((def).getId()); + String defId = def.getId(); + String backupHost = def.getBackupHost(); + LOG.debug("BACKUP CONFIG yaml - backup host: " + backupHost); + boolean selected = selectedDefIds.contains(defId); + LOG.debug("BACKUP CONFIG - is definition selected: " + def.getId() + " " + selected + + " already marked for backup " + configuredBackupArchives.contains(def) + + " already marked for restore " + configuredRestoreArchives.contains(def)); //at least one backup command - if (!execBackupRestore.isBackup() && !StringUtils.isEmpty(def.getBackupCommand()) && selected - && (!def.isSingleNodeBackup() || !configuredBackupArchives.contains(def))) { - execBackupRestore.setBackup(true); + if (!execBackupRestore.isBackup(defId) && !StringUtils.isEmpty(def.getBackupCommand()) && selected + && (!def.isSingleNodeBackup() || !configuredBackupArchives.contains(def) + && (backupHost == null || backupHost.equals(host.getAddress())))) { + execBackupRestore.setBackup(true, defId); + LOG.debug("BACKUP CONFIG - definition is selected for backup"); } //at least one restore command - if (!execBackupRestore.isRestore() && !StringUtils.isEmpty(def.getRestoreCommand()) && selected + if (!execBackupRestore.isRestore(defId) && !StringUtils.isEmpty(def.getRestoreCommand()) && selected && (!def.isSingleNodeRestore() || !configuredRestoreArchives.contains(def))) { - execBackupRestore.setRestore(true); + execBackupRestore.setRestore(true, defId); + LOG.debug("BACKUP CONFIG - definition is selected for restore"); } return selected; } @@ -169,25 +196,29 @@ void writeHostDefinitions(YamlConfiguration config, Location host, Collection m_backup = new HashMap(); + private Map m_restore = new HashMap(); - public boolean isBackup() { - return m_backup; + public boolean isBackup(String defId) { + Boolean value = m_backup.get(defId); + return value != null && value; } - public void setBackup(boolean backup) { - m_backup = backup; + public void setBackup(boolean backup, String defId) { + m_backup.put(defId, backup); } - public boolean isRestore() { - return m_restore; + public boolean isRestore(String defId) { + Boolean value = m_restore.get(defId); + return value != null && value; + } + public void setRestore(boolean restore, String defId) { + m_restore.put(defId, restore); } - public void setRestore(boolean restore) { - m_restore = restore; + public boolean isBackup() { + return !m_backup.isEmpty(); + } + public boolean isRestore() { + return !m_restore.isEmpty(); } } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/BackupSettings.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/BackupSettings.java index 7f0644da8e..61a6e5236f 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/BackupSettings.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/BackupSettings.java @@ -18,13 +18,14 @@ import java.io.File; import java.io.FileNotFoundException; +import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.codehaus.jackson.annotate.JsonIgnore; import org.sipfoundry.sipxconfig.cfgmgt.DeployConfigOnEdit; +import org.sipfoundry.sipxconfig.elasticsearch.ElasticsearchServiceImpl; import org.sipfoundry.sipxconfig.feature.Feature; import org.sipfoundry.sipxconfig.rest.RestUtilities; import org.sipfoundry.sipxconfig.setting.PersistableSettings; @@ -32,6 +33,10 @@ import org.springframework.beans.factory.annotation.Required; public class BackupSettings extends PersistableSettings implements DeployConfigOnEdit { + + private static final String TMP_DIR = "general/tmpDir"; + private static final String MEM = "general/mem"; + private static final Log LOG = LogFactory.getLog(BackupSettings.class); private String m_localBackupPath; private BackupDbSettings m_backupDbSettings; @@ -92,6 +97,15 @@ public Setting getIncludeDeviceFiles() { return m_backupDbSettings.getSettings().getSetting("db/includeDeviceFiles"); } + @JsonIgnore + public String getTmpDir() { + return (String) getSettingTypedValue(TMP_DIR); + } + + public String getMem() { + return (String) getSettingTypedValue(MEM); + } + public Setting getDbSettings() { return m_backupDbSettings.getSettings(); } @@ -103,7 +117,7 @@ public Setting getDb() { @Override @JsonIgnore public Collection getAffectedFeaturesOnChange() { - return Collections.singleton((Feature) BackupManager.FEATURE); + return Arrays.asList((Feature) BackupManager.FEATURE, (Feature) ElasticsearchServiceImpl.FEATURE); } @JsonIgnore diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/backup.beans.xml b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/backup.beans.xml index e9876664dd..c7fe1a6ac7 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/backup.beans.xml +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/backup/backup.beans.xml @@ -51,6 +51,7 @@ + diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/Branch.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/Branch.java index c7132a6e5d..65429e8122 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/Branch.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/Branch.java @@ -9,20 +9,43 @@ */ package org.sipfoundry.sipxconfig.branch; - +import static org.sipfoundry.commons.mongo.MongoConstants.LOCATION_NAME; +import static org.sipfoundry.commons.mongo.MongoConstants.LOCATION_RESTRICTIONS_DOMAINS; +import static org.sipfoundry.commons.mongo.MongoConstants.LOCATION_RESTRICTIONS_SUBNETS; +import static org.sipfoundry.commons.mongo.MongoConstants.LOCATION_ASSOCIATIONS; +import static org.sipfoundry.commons.mongo.MongoConstants.LOCATION_ASSOCIATIONS_INBOUND; +import static org.sipfoundry.commons.mongo.MongoConstants.LOCATION_ASSOCIATIONS_FALLBACK; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; import org.sipfoundry.sipxconfig.common.BeanWithId; import org.sipfoundry.sipxconfig.common.NamedObject; +import org.sipfoundry.sipxconfig.common.Replicable; +import org.sipfoundry.sipxconfig.commserver.imdb.AliasMapping; +import org.sipfoundry.sipxconfig.commserver.imdb.DataSet; import org.sipfoundry.sipxconfig.phonebook.Address; import org.sipfoundry.sipxconfig.systemaudit.SystemAuditable; -public class Branch extends BeanWithId implements NamedObject, SystemAuditable { +public class Branch extends BeanWithId implements NamedObject, SystemAuditable, Replicable { private String m_name; private String m_description; private Address m_address = new Address(); private String m_phoneNumber; private String m_faxNumber; private String m_timeZone; + private BranchRoutes m_routes = new BranchRoutes(); + private Set m_locations = new HashSet(); + private Set m_locationsInbound = new HashSet(); + private Branch m_fallbackBranch; public String getName() { return m_name; @@ -85,4 +108,118 @@ public String getEntityIdentifier() { public String getConfigChangeType() { return Branch.class.getSimpleName(); } + + public BranchRoutes getRoutes() { + return m_routes; + } + + public void setRoutes(BranchRoutes routes) { + m_routes = routes; + } + + @Override + public Set getDataSets() { + return Collections.emptySet(); + } + + @Override + public String getIdentity(String domainName) { + return null; + } + + @Override + public Collection getAliasMappings(String domainName) { + return null; + } + + public Set getLocations() { + return m_locations; + } + + public void setLocations(Set locations) { + m_locations = locations; + } + + public Set getLocationsInbound() { + return m_locationsInbound; + } + + public void setLocationsInbound(Set locationsInbound) { + m_locationsInbound = locationsInbound; + } + + public List getLocationsList() { + return new ArrayList(m_locations); + } + + public List getLocationsInboundList() { + return new ArrayList(m_locationsInbound); + } + + public List getLocationsNamesList() { + List names = new ArrayList(); + List locations = getLocationsList(); + for (Branch branch : locations) { + names.add(branch.getName()); + } + return names; + } + + public List getLocationsInboundNamesList() { + List names = new ArrayList(); + List locations = getLocationsInboundList(); + for (Branch branch : locations) { + names.add(branch.getName()); + } + return names; + } + + public void setLocationsList(List locations) { + m_locations.clear(); + m_locations.addAll(locations); + } + + public void setLocationsInboundList(List locations) { + m_locationsInbound.clear(); + m_locationsInbound.addAll(locations); + } + + public Branch getFallbackBranch() { + return m_fallbackBranch; + } + + public void setFallbackBranch(Branch fallbackBranch) { + m_fallbackBranch = fallbackBranch; + } + + @Override + public boolean isValidUser() { + return false; + } + + @Override + public Map getMongoProperties(String domain) { + Map props = new HashMap(); + props.put(LOCATION_NAME, getName()); + props.put(LOCATION_RESTRICTIONS_DOMAINS, m_routes.getDomains()); + props.put(LOCATION_RESTRICTIONS_SUBNETS, m_routes.getSubnets()); + props.put(LOCATION_ASSOCIATIONS, getLocationsNamesList()); + props.put(LOCATION_ASSOCIATIONS_INBOUND, getLocationsInboundNamesList()); + if (m_fallbackBranch != null) { + props.put(LOCATION_ASSOCIATIONS_FALLBACK, m_fallbackBranch.getName()); + } else { + props.put(LOCATION_ASSOCIATIONS_FALLBACK, StringUtils.EMPTY); + } + return props; + } + + @Override + public String getEntityName() { + return getClass().getSimpleName(); + } + + @Override + public boolean isReplicationEnabled() { + return true; + } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/BranchManager.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/BranchManager.java index 24f0298324..b62003f34e 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/BranchManager.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/BranchManager.java @@ -14,8 +14,46 @@ import java.util.List; import org.sipfoundry.sipxconfig.common.DataObjectSource; +import org.sipfoundry.sipxconfig.common.ReplicableProvider; -public interface BranchManager extends DataObjectSource { +public interface BranchManager extends DataObjectSource, ReplicableProvider { + + public static final String CONFERENCES_BY_BRANCH = "SELECT * from meetme_conference mc JOIN branch_conference bc " + + "ON mc.meetme_conference_id = bc.meetme_conference_id where bc.branch_id = :branchId"; + + public static final String GATEWAYS_BY_BRANCH = "SELECT * from gateway gw where gw.branch_id = :branchId"; + + public static final String SHARED_GATEWAYS = "SELECT * from gateway gw where gw.shared='TRUE'"; + + public static final String AUTH_CODES_BY_BRANCH = "SELECT * from auth_code ac JOIN branch_auth_code bac " + + "ON ac.auth_code_id = bac.auth_code_id where bac.branch_id = :branchId"; + + public static final String AUTO_ATTENDANDS_BY_BRANCH = + "SELECT * from auto_attendant aa JOIN branch_auto_attendant baa " + + "ON aa.auto_attendant_id = baa.auto_attendant_id where baa.branch_id = :branchId"; + + public static final String PARKS_BY_BRANCH = "SELECT * from park_orbit po JOIN branch_park_orbit bpo " + + "ON po.park_orbit_id = bpo.park_orbit_id where bpo.branch_id = :branchId"; + + public static final String CALL_GROUP_BY_BRANCH = "SELECT * from call_group cg JOIN branch_call_group bcg " + + "ON cg.call_group_id = bcg.call_group_id where bcg.branch_id = :branchId"; + + public static final String PAGING_GROUP_BY_BRANCH = "SELECT * from paging_group pg JOIN branch_paging_group bpg " + + "ON pg.paging_group_id = bpg.paging_group_id where bpg.branch_id = :branchId"; + + public static final String CALL_QUEUE_BY_BRANCH = + "SELECT * from freeswitch_extension fe JOIN branch_call_queue bcq " + + "ON fe.freeswitch_ext_id = bcq.freeswitch_ext_id where bcq.branch_id = :branchId"; + + public static final String GROUP_BY_BRANCH = + "SELECT * from group_storage where resource = 'user' and branch_id = :branchId "; + + public static final String AUTO_ATTENDANT_DIALING_RULES_BY_BRANCH = + "SELECT * from attendant_dialing_rule adr " + + "LEFT OUTER JOIN dialing_rule dr " + + "ON adr.attendant_dialing_rule_id=dr.dialing_rule_id " + + "JOIN branch_aa_dialing_rule badr " + + "ON adr.attendant_dialing_rule_id = badr.attendant_dialing_rule_id where badr.branch_id = :branchId"; Branch getBranch(String name); @@ -32,5 +70,15 @@ public interface BranchManager extends DataObjectSource { List loadBranchesByPage(final int firstRow, final int pageSize, final String[] orderBy, final boolean orderAscending); + /** + * returns features that apply to given branchId + * */ + List getFeatureNames(Integer branchId, String sqlQuery, Class c); + + /** + * returns features that apply to all branches + * */ + List getFeatureNames(String sqlQuery, Class c); + void clear(); } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/BranchManagerImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/BranchManagerImpl.java index af3fb22ac1..18c28bfcdf 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/BranchManagerImpl.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/BranchManagerImpl.java @@ -11,30 +11,44 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.Criteria; +import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.Restrictions; +import org.sipfoundry.sipxconfig.common.CoreContext; import org.sipfoundry.sipxconfig.common.DaoUtils; +import org.sipfoundry.sipxconfig.common.Replicable; import org.sipfoundry.sipxconfig.common.SipxHibernateDaoSupport; +import org.sipfoundry.sipxconfig.common.User; import org.sipfoundry.sipxconfig.common.UserException; +import org.sipfoundry.sipxconfig.common.event.DaoEventListener; +import org.sipfoundry.sipxconfig.commserver.imdb.ReplicationManager; +import org.sipfoundry.sipxconfig.setting.Group; import org.sipfoundry.sipxconfig.setup.SetupListener; import org.sipfoundry.sipxconfig.setup.SetupManager; import org.sipfoundry.sipxconfig.time.NtpManager; +import org.springframework.beans.factory.annotation.Required; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.orm.hibernate3.HibernateCallback; -public class BranchManagerImpl extends SipxHibernateDaoSupport implements BranchManager, SetupListener { +public class BranchManagerImpl extends SipxHibernateDaoSupport + implements BranchManager, SetupListener, DaoEventListener { private static final Log LOG = LogFactory.getLog(BranchManagerImpl.class); private static final String NAME_PROP_NAME = "name"; private static final String UPDATE_BRANCH_TIMEZONE = "update_branch_tz"; + private JdbcTemplate m_jdbcTemplate; private NtpManager m_ntpManager; + private ReplicationManager m_replicationManager; + private CoreContext m_coreContext; @Override public Branch getBranch(Integer branchId) { @@ -87,22 +101,42 @@ private void checkForDuplicateName(Branch branch) { */ @Override public void deleteBranches(Collection branchIds) { - List sqlUpdates = new ArrayList(); - Collection branches = new ArrayList(branchIds.size()); - for (Integer id : branchIds) { - Branch branch = getBranch(id); - branches.add(branch); - sqlUpdates.add("update users set branch_id=null where branch_id=" + id); - sqlUpdates.add("update group_storage set branch_id=null where branch_id=" + id); - sqlUpdates.add("delete from branch where branch_id=" + id); - getHibernateTemplate().evict(branch); - } - if (!sqlUpdates.isEmpty()) { - m_jdbcTemplate.batchUpdate(sqlUpdates.toArray(new String[sqlUpdates.size()])); - for (Branch branch : branches) { - getDaoEventPublisher().publishDelete(branch); + try { + List sqlUpdates = new ArrayList(); + Collection branches = new ArrayList(branchIds.size()); + Set affectedUsers = new HashSet(); + for (Integer id : branchIds) { + Branch branch = getBranch(id); + Collection branchUsers = m_coreContext.getUsersForBranch(branch); + affectedUsers.addAll(branchUsers); + branches.add(branch); + sqlUpdates.add("update users set branch_id=null where branch_id=" + id); + sqlUpdates.add("update group_storage set branch_id=null where branch_id=" + id); + sqlUpdates.add("delete from branch_route_domain where branch_id=" + id); + sqlUpdates.add("delete from branch_route_subnet where branch_id=" + id); + sqlUpdates.add("delete from branch_branch where branch_id=" + id); + sqlUpdates.add("delete from branch_branch where associated_branch_id=" + id); + sqlUpdates.add("delete from branch where branch_id=" + id); + getHibernateTemplate().evict(branch); } + if (!sqlUpdates.isEmpty()) { + m_jdbcTemplate.batchUpdate(sqlUpdates.toArray(new String[sqlUpdates.size()])); + for (Branch branch : branches) { + getDaoEventPublisher().publishDelete(branch); + } + for (User user : affectedUsers) { + // need to reload and replicate the affected users + getHibernateTemplate().refresh(user); + for (Group group : user.getGroups()) { + getHibernateTemplate().refresh(group); + } + m_replicationManager.replicateEntity(user); + } + } + } catch (Exception ex) { + throw new UserException("&branches.delete.err"); } + } @Override @@ -134,6 +168,21 @@ public List loadBranchesByPage(final int firstRow, final int pageSize, f return loadBeansByPage(Branch.class, null, null, firstRow, pageSize, orderBy, orderAscending); } + @Override + public List getFeatureNames(Integer branchId, String sqlQuery, Class c) { + Query q = getHibernateTemplate().getSessionFactory().getCurrentSession().createSQLQuery(sqlQuery).addEntity(c); + q.setInteger("branchId", branchId); + List names = q.list(); + return names; + } + + @Override + public List< ? > getFeatureNames(String sqlQuery, Class< ? > c) { + Query q = getHibernateTemplate().getSessionFactory().getCurrentSession().createSQLQuery(sqlQuery).addEntity(c); + List names = q.list(); + return names; + } + @Override public void clear() { removeAll(Branch.class); @@ -166,4 +215,35 @@ public void setNtpManager(NtpManager ntpManager) { m_ntpManager = ntpManager; } + @Override + public List getReplicables() { + List replicables = new ArrayList(); + replicables.addAll(getBranches()); + return replicables; + } + + @Required + public void setReplicationManager(ReplicationManager replicationManager) { + m_replicationManager = replicationManager; + } + + @Required + public void setCoreContext(CoreContext coreContext) { + m_coreContext = coreContext; + } + + @Override + public void onDelete(Object entity) { + } + + @Override + public void onSave(Object entity) { + if (entity instanceof Branch) { + Branch branch = (Branch) entity; + Collection users = m_coreContext.getUsersForBranch(branch); + for (User user : users) { + m_replicationManager.replicateEntity(user); + } + } + } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/BranchRoutes.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/BranchRoutes.java new file mode 100644 index 0000000000..dddc20f357 --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/BranchRoutes.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2015 sipXcom, certain elements licensed under a Contributor Agreement. + * Contributors retain copyright to elements licensed under a Contributor Agreement. + * Licensed to the User under the LGPL license. + */ +package org.sipfoundry.sipxconfig.branch; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.StringUtils; + +public class BranchRoutes { + private List m_domains = new ArrayList(); + private List m_subnets = new ArrayList(); + + public void setDomains(List domains) { + m_domains = domains; + } + + public List getDomains() { + return m_domains; + } + + public void setSubnets(List subnets) { + m_subnets = subnets; + } + + public List getSubnets() { + return m_subnets; + } + + public boolean addDomain() { + return getDomains().add(StringUtils.EMPTY); + } + + public String removeDomain(int index) { + return getDomains().remove(index); + } + + public boolean addSubnet() { + return getSubnets().add(StringUtils.EMPTY); + } + + public String removeSubnet(int index) { + return getSubnets().remove(index); + } + + public boolean isEmpty() { + return getSubnets().isEmpty() && getDomains().isEmpty(); + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/branch.beans.xml b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/branch.beans.xml index 54a288e499..682a481895 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/branch.beans.xml +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/branch.beans.xml @@ -5,6 +5,8 @@ + + diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/branch.hbm.xml b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/branch.hbm.xml index 9a2833e0f6..458e462ed4 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/branch.hbm.xml +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/branch/branch.hbm.xml @@ -12,11 +12,34 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/csv/CsvRowInserter.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/csv/CsvRowInserter.java index a28513d2d3..d6c3cbadb5 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/csv/CsvRowInserter.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/csv/CsvRowInserter.java @@ -185,7 +185,10 @@ public User userFromRow(String[] row) { // disable user email notification user.setNotified(true); - Index.PIN.setProperty(user, row); + String pin = Index.PIN.get(row); + if (!StringUtils.isEmpty(pin)) { + Index.PIN.setProperty(user, row); + } String voicemailPin = Index.VOICEMAIL_PIN.get(row); if (!StringUtils.isEmpty(voicemailPin)) { if (isHashed(voicemailPin)) { @@ -198,7 +201,10 @@ public User userFromRow(String[] row) { Index.FIRST_NAME.setProperty(user, row); Index.LAST_NAME.setProperty(user, row); Index.ALIAS.setProperty(user, row); - Index.SIP_PASSWORD.setProperty(user, row); + String sipPassword = Index.SIP_PASSWORD.get(row); + if (!StringUtils.isEmpty(sipPassword)) { + Index.SIP_PASSWORD.setProperty(user, row); + } Index.SALUTATION.setProperty(user, row); Index.MANAGER.setProperty(user, row); Index.EMPLOYEE_ID.setProperty(user, row); diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/csv/ExportCsv.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/csv/ExportCsv.java index 7f0351a6f0..3e345f14f9 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/csv/ExportCsv.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/csv/ExportCsv.java @@ -124,8 +124,11 @@ void exportUser(SimpleCsvWriter csv, String[] row, User user) throws IOException Index.USER_GROUP.set(row, user.getGroupsNames()); Index.EMAIL.set(row, user.getEmailAddress()); - Index.PIN.set(row, user.getPintoken()); - Index.VOICEMAIL_PIN.set(row, user.getVoicemailPintoken()); + if (!user.isAdmin()) { + Index.PIN.set(row, user.getPintoken()); + Index.VOICEMAIL_PIN.set(row, user.getVoicemailPintoken()); + } + // XMPP Index.IM_ID.set(row, user.getImId()); diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapManager.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapManager.java index 0e1a1008f3..8b9d04460d 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapManager.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapManager.java @@ -14,6 +14,7 @@ import javax.naming.NamingException; import javax.naming.directory.DirContext; +import org.sipfoundry.sipxconfig.alarm.AlarmDefinition; import org.sipfoundry.sipxconfig.common.CronSchedule; import org.sipfoundry.sipxconfig.feature.GlobalFeature; import org.springframework.ldap.core.DirContextProcessor; @@ -22,6 +23,7 @@ public interface LdapManager { public static final GlobalFeature FEATURE = new GlobalFeature("ldapConfig"); public static final String FILTER_ALL_CLASSES = "objectclass=*"; public static final String CONTEXT_BEAN_NAME = "ldapManager"; + public static final AlarmDefinition LDAP_CONNECTION_FAILED = new AlarmDefinition("LDAP_CONNECTION_FAILED"); public static final DirContextProcessor NULL_PROCESSOR = new DirContextProcessor() { @Override diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapManagerImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapManagerImpl.java index 455e5bf57b..c887e24d3c 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapManagerImpl.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapManagerImpl.java @@ -14,6 +14,8 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,6 +28,9 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.sipfoundry.sipxconfig.alarm.AlarmDefinition; +import org.sipfoundry.sipxconfig.alarm.AlarmProvider; +import org.sipfoundry.sipxconfig.alarm.AlarmServerManager; import org.sipfoundry.sipxconfig.common.CronSchedule; import org.sipfoundry.sipxconfig.common.SipxHibernateDaoSupport; import org.sipfoundry.sipxconfig.common.UserException; @@ -44,7 +49,8 @@ /** * Maintains LDAP connection params, attribute maps and schedule LdapManagerImpl */ -public class LdapManagerImpl extends SipxHibernateDaoSupport implements LdapManager, ApplicationContextAware { +public class LdapManagerImpl extends SipxHibernateDaoSupport implements LdapManager, + ApplicationContextAware, AlarmProvider { private static final Log LOG = LogFactory.getLog(LdapManagerImpl.class); private static final String QUERY_OVERRIDE_PIN = "SELECT value_storage_id, value from setting_value where path='ldap/overwrite_pin'"; @@ -112,7 +118,8 @@ public void processRow(ResultSet rs) throws SQLException { @Override public boolean verifyLdapConnection(LdapConnectionParams params) { - LOG.debug("verifying LDAP connection: " + params.getFullHost()); + String fullHost = params.getFullHost(); + LOG.debug("verifying LDAP connection: " + fullHost); try { String[] attrNames = new String[] { NAMING_CONTEXTS, SUBSCHEMA_SUBENTRY @@ -120,6 +127,7 @@ public boolean verifyLdapConnection(LdapConnectionParams params) { retrieveDefaultSearchBase(params, attrNames); return true; } catch (Exception e) { + LOG.error("ALARM_LDAP_CONNECTION_FAILED for host:" + fullHost + " reason: " + e.getMessage()); return false; } } @@ -227,7 +235,7 @@ private Map retrieveDefaultSearchBase(LdapConnectionParams param cons.setReturningAttributes(attrNames); cons.setSearchScope(SearchControls.OBJECT_SCOPE); - cons.setTimeLimit(30000); + cons.setTimeLimit(params.getTimeout()); List> results = m_templateFactory.getLdapTemplate(params).search("", FILTER_ALL_CLASSES, cons, new AttributesToValues(attrNames), NULL_PROCESSOR); @@ -372,4 +380,9 @@ public void setTemplateFactory(LdapTemplateFactory templateFactory) { public void setConfigJdbcTemplate(JdbcTemplate template) { m_jdbcTemplate = template; } + + @Override + public Collection getAvailableAlarms(AlarmServerManager manager) { + return Collections.singleton(LDAP_CONNECTION_FAILED); + } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapRowInserter.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapRowInserter.java index d4ca458a20..4bdd96be74 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapRowInserter.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapRowInserter.java @@ -22,6 +22,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.sipfoundry.sipxconfig.admin.AdminContext; import org.sipfoundry.sipxconfig.bulk.RowInserter; import org.sipfoundry.sipxconfig.common.CoreContext; import org.sipfoundry.sipxconfig.common.DuplicateType; @@ -34,6 +35,7 @@ import org.sipfoundry.sipxconfig.permission.PermissionManager; import org.sipfoundry.sipxconfig.setting.Group; import org.sipfoundry.sipxconfig.setting.GroupAutoAssign; +import org.sipfoundry.sipxconfig.setting.SettingDao; import org.sipfoundry.sipxconfig.vm.MailboxManager; import org.springframework.beans.factory.annotation.Required; @@ -57,6 +59,8 @@ public class LdapRowInserter extends RowInserter { private Set m_aliases; private List m_importedUserNames; private List m_notImportedUserNames; + private SettingDao m_settingDao; + private AdminContext m_adminContext; @Override protected Log getLog() { @@ -94,12 +98,17 @@ void insertRow(SearchResult searchResult, Attributes attrs) { String userNameWithDefault = null; try { String userName = m_userMapper.getUserName(attrs); + userName = StringUtils.substring(userName, m_adminContext.getStripUserName()); userNameWithDefault = StringUtils.defaultIfEmpty(userName, EMPTY); user = m_coreContext.loadUserByUserName(userName); newUser = (user == null); if (newUser) { user = m_coreContext.newUser(); + String newUserGroupName = StringUtils. + join(new String[] {m_adminContext.getNewLdapUserGroupNamePrefix(), m_domain}); user.setUserName(userName); + user.addGroup(m_settingDao. + getGroupCreateIfNotFound(CoreContext.USER_GROUP_RESOURCE_ID, newUserGroupName)); } else if (!user.isLdapManaged()) { // this user was already created but it is not supposed to be managed by LDAP LOG.info("STOP Inserting:" + attrs.toString() @@ -187,6 +196,11 @@ protected String dataToString(SearchResult sr) { return sr.getName(); } + @Required + public void setSettingDao(SettingDao settingDao) { + m_settingDao = settingDao; + } + @Override protected RowResult checkRowData(SearchResult sr) { Attributes attrs = sr.getAttributes(); @@ -276,4 +290,9 @@ public List getImportedUserNames() { public List getNotImportedUserNames() { return m_notImportedUserNames; } + + @Required + public void setAdminContext(AdminContext adminContext) { + m_adminContext = adminContext; + } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/DisableLdapUsersTimer.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapTimer.java similarity index 88% rename from sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/DisableLdapUsersTimer.java rename to sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapTimer.java index b1d309e98e..3bbcf38a35 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/DisableLdapUsersTimer.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/LdapTimer.java @@ -30,12 +30,13 @@ import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.annotation.Required; -public class DisableLdapUsersTimer implements BeanFactoryAware { - public static final Log LOG = LogFactory.getLog(DisableLdapUsersTimer.class); +public class LdapTimer implements BeanFactoryAware { + public static final Log LOG = LogFactory.getLog(LdapTimer.class); private BeanFactory m_beanFactory; private UserProfileService m_userProfileService; private ExecutorService m_executor = Executors.newSingleThreadExecutor(); private AdminContext m_adminContext; + private LdapManager m_ldapManager; public void disableDeleteUsers() { try { @@ -73,6 +74,11 @@ public void disableDeleteUsers() { } } + public void verifyLdapConnection() { + boolean verified = m_ldapManager.verifyAllLdapConnections(); + LOG.debug("LDAP connection is up: " + verified); + } + @Override @Required public void setBeanFactory(BeanFactory beanFactory) { @@ -88,4 +94,9 @@ public void setUserProfileService(UserProfileService userProfileService) { public void setAdminContext(AdminContext adminContext) { m_adminContext = adminContext; } + + @Required + public void setLdapManager(LdapManager ldapManager) { + m_ldapManager = ldapManager; + } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/ldap.beans.xml b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/ldap.beans.xml index 853dde9161..7c5e8f1e4d 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/ldap.beans.xml +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/bulk/ldap/ldap.beans.xml @@ -73,7 +73,9 @@ + + @@ -145,13 +147,17 @@ - + + + + - + + @@ -159,4 +165,5 @@ + diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/CallbackConfiguration.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/CallbackConfiguration.java new file mode 100644 index 0000000000..845b8c340c --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/CallbackConfiguration.java @@ -0,0 +1,76 @@ +/* + * + * + * Copyright (C) 2015 sipXcom, certain elements licensed under a Contributor Agreement. + * Contributors retain copyright to elements licensed under a Contributor Agreement. + * Licensed to the User under the LGPL license. + */ +package org.sipfoundry.sipxconfig.callback; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.Set; + +import org.apache.commons.io.IOUtils; +import org.sipfoundry.sipxconfig.address.Address; +import org.sipfoundry.sipxconfig.cfgmgt.ConfigManager; +import org.sipfoundry.sipxconfig.cfgmgt.ConfigProvider; +import org.sipfoundry.sipxconfig.cfgmgt.ConfigRequest; +import org.sipfoundry.sipxconfig.cfgmgt.ConfigUtils; +import org.sipfoundry.sipxconfig.cfgmgt.LoggerKeyValueConfiguration; +import org.sipfoundry.sipxconfig.commserver.Location; +import org.sipfoundry.sipxconfig.domain.Domain; +import org.sipfoundry.sipxconfig.freeswitch.FreeswitchFeature; +import org.sipfoundry.sipxconfig.setting.Setting; +import org.sipfoundry.sipxconfig.setting.SettingUtil; +import org.springframework.beans.factory.annotation.Required; + +public class CallbackConfiguration implements ConfigProvider { + private CallbackOnBusyImpl m_callbackImpl; + private String m_callbackSettingKeyString = "callback-config"; + + @Override + public void replicate(ConfigManager manager, ConfigRequest request) throws IOException { + if (!request.applies(CallbackOnBusy.FEATURE)) { + return; + } + + Set locations = request.locations(manager); + Address fs = manager.getAddressManager().getSingleAddress(FreeswitchFeature.CALLBACK_EVENT_ADDRESS); + Domain domain = manager.getDomainManager().getDomain(); + CallbackSettings settings = m_callbackImpl.getSettings(); + Setting calllbackSettings = settings.getSettings().getSetting(m_callbackSettingKeyString); + for (Location location : locations) { + File dir = manager.getLocationDataDirectory(location); + boolean enabled = manager.getFeatureManager().isFeatureEnabled(CallbackOnBusy.FEATURE, location); + ConfigUtils.enableCfengineClass(dir, "sipxcallback.cfdat", enabled, "sipxcallback"); + if (!enabled) { + continue; + } + + String log4jFileName = "log4j-callback.properties.part"; + String[] logLevelKeys = {"log4j.logger.org.sipfoundry.sipxcallback"}; + SettingUtil.writeLog4jSetting(calllbackSettings, dir, log4jFileName, logLevelKeys); + + Writer flat = new FileWriter(new File(dir, "sipxcallback.properties.part")); + try { + writeConfig(flat, settings, domain, fs.getPort()); + } finally { + IOUtils.closeQuietly(flat); + } + } + } + + void writeConfig(Writer wtr, CallbackSettings settings, Domain domain, int freeswithPort) throws IOException { + LoggerKeyValueConfiguration config = LoggerKeyValueConfiguration.equalsSeparated(wtr); + config.writeSettings(settings.getSettings().getSetting(m_callbackSettingKeyString)); + config.write("freeswitch.eventSocketPort", freeswithPort); + } + + @Required + public void setCallbackImpl(CallbackOnBusyImpl callbackImpl) { + m_callbackImpl = callbackImpl; + } +} diff --git a/sipXconfig/web/unitelite/scripts/directives/translate-schedules.js b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/CallbackOnBusy.java similarity index 55% rename from sipXconfig/web/unitelite/scripts/directives/translate-schedules.js rename to sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/CallbackOnBusy.java index 9cae21d78c..3f562d0c74 100644 --- a/sipXconfig/web/unitelite/scripts/directives/translate-schedules.js +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/CallbackOnBusy.java @@ -1,5 +1,7 @@ -/* - * Copyright (c) eZuce, Inc. All rights reserved. +/** + * + * + * Copyright (c) 2015 sipXcom, Inc. All rights reserved. * Contributed to SIPfoundry under a Contributor Agreement * * This software is free software; you can redistribute it and/or modify it under @@ -12,28 +14,15 @@ * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. */ +package org.sipfoundry.sipxconfig.callback; -(function() { - - 'use strict'; - - uw.filter('translateSchedules', function () { - return function (input, array) { - var translated = input; - - if (input === null) { - return 'Always' - } +import org.sipfoundry.sipxconfig.dialplan.DialingRuleProvider; +import org.sipfoundry.sipxconfig.feature.LocationFeature; - _.find(array, function (obj) { - if (obj.scheduleId === input) { - translated = obj.name; - return true; - } - }) +public interface CallbackOnBusy extends DialingRuleProvider { + public static final LocationFeature FEATURE = new LocationFeature("callback"); - return translated; - } - }) + public CallbackSettings getSettings(); -})(); + public void saveSettings(CallbackSettings settings); +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/CallbackOnBusyImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/CallbackOnBusyImpl.java new file mode 100755 index 0000000000..3a55be2b93 --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/CallbackOnBusyImpl.java @@ -0,0 +1,215 @@ +/** + * + * + * Copyright (c) 2015 sipXcom, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.callback; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.sipfoundry.sipxconfig.address.Address; +import org.sipfoundry.sipxconfig.address.AddressManager; +import org.sipfoundry.sipxconfig.address.AddressProvider; +import org.sipfoundry.sipxconfig.address.AddressType; +import org.sipfoundry.sipxconfig.commserver.Location; +import org.sipfoundry.sipxconfig.dialplan.CallbackRule; +import org.sipfoundry.sipxconfig.dialplan.DialingRule; +import org.sipfoundry.sipxconfig.dns.DnsManager; +import org.sipfoundry.sipxconfig.dns.DnsProvider; +import org.sipfoundry.sipxconfig.dns.ResourceRecord; +import org.sipfoundry.sipxconfig.dns.ResourceRecords; +import org.sipfoundry.sipxconfig.domain.DomainManager; +import org.sipfoundry.sipxconfig.feature.Bundle; +import org.sipfoundry.sipxconfig.feature.FeatureChangeRequest; +import org.sipfoundry.sipxconfig.feature.FeatureChangeValidator; +import org.sipfoundry.sipxconfig.feature.FeatureManager; +import org.sipfoundry.sipxconfig.feature.FeatureProvider; +import org.sipfoundry.sipxconfig.feature.GlobalFeature; +import org.sipfoundry.sipxconfig.feature.LocationFeature; +import org.sipfoundry.sipxconfig.freeswitch.FreeswitchFeature; +import org.sipfoundry.sipxconfig.setting.BeanWithSettingsDao; +import org.sipfoundry.sipxconfig.snmp.ProcessDefinition; +import org.sipfoundry.sipxconfig.snmp.ProcessProvider; +import org.sipfoundry.sipxconfig.snmp.SnmpManager; +import org.springframework.beans.factory.annotation.Required; + +public class CallbackOnBusyImpl implements FeatureProvider, CallbackOnBusy, + ProcessProvider, DnsProvider, AddressProvider { + + public static final AddressType CBB_SIP_ADDRESS = AddressType.sipTcp("cbb-sip"); + private static final Collection ADDRESSES = Arrays.asList(new AddressType[] { + CBB_SIP_ADDRESS + }); + private static final String CBB = "cbb"; + + private BeanWithSettingsDao m_settingsDao; + private FeatureManager m_featureManager; + private DomainManager m_domainManager; + private FreeswitchFeature m_fsFeature; + + @Override + public List getDialingRules(Location location) { + List locations = m_featureManager.getLocationsForEnabledFeature(FEATURE); + if (locations.isEmpty()) { + return Collections.emptyList(); + } + + List dialingRules = new ArrayList(); + CallbackSettings settings = getSettings(); + String prefix = settings.getCallbackPrefix(); + if (StringUtils.isEmpty(prefix)) { + return Collections.emptyList(); + } + String fsAddressLocation = locations.get(0).getFqdn(); + CallbackRule rule = new CallbackRule(prefix, fsAddressLocation); + rule.appendToGenerationRules(dialingRules); + return dialingRules; + } + + @Override + public void featureChangePrecommit(FeatureManager manager, + FeatureChangeValidator validator) { + validator.requiredOnSameHost(FEATURE, FreeswitchFeature.FEATURE); + } + + @Override + public void featureChangePostcommit(FeatureManager manager, + FeatureChangeRequest request) { + if (request.getAllNewlyEnabledFeatures().contains(FEATURE)) { + CallbackSettings settings = getSettings(); + if (settings.isNew()) { + saveSettings(settings); + } + } + } + + @Override + public Collection getProcessDefinitions(SnmpManager manager, Location location) { + boolean enabled = manager.getFeatureManager().isFeatureEnabled(FEATURE, location); + return (enabled ? Collections.singleton(ProcessDefinition.sipxByRegex("sipxcallback", + ".*\\s-Dprocname=sipxcallback\\s.*")) : null); + } + + @Override + public CallbackSettings getSettings() { + return m_settingsDao.findOrCreateOne(); + } + + @Override + public void saveSettings(CallbackSettings settings) { + m_settingsDao.upsert(settings); + } + + @Override + public Collection getAvailableGlobalFeatures(FeatureManager featureManager) { + return null; + } + + @Override + public Collection getAvailableLocationFeatures( + FeatureManager featureManager, Location l) { + return Collections.singleton(FEATURE); + } + + @Override + public void getBundleFeatures(FeatureManager featureManager, Bundle b) { + if (b == Bundle.CORE_TELEPHONY) { + b.addFeature(FEATURE); + } + } + + @Override + public Collection
getAvailableAddresses(AddressManager manager, AddressType type, Location requester) { + return getAvailableAddresses(manager, type); + } + + Collection
getAvailableAddresses(AddressManager manager, AddressType type) { + if (!ADDRESSES.contains(type)) { + return null; + } + List locations = manager.getFeatureManager().getLocationsForEnabledFeature(FEATURE); + List
addresses = new ArrayList
(locations.size()); + for (Location location : locations) { + Address address = null; + if (type.equals(CBB_SIP_ADDRESS)) { + address = new Address(CBB_SIP_ADDRESS, + location.getAddress(), m_fsFeature.getSettings(location).getFreeswitchSipPort()); + } + addresses.add(address); + } + return addresses; + } + + @Override + public Address getAddress(DnsManager manager, AddressType t, + Collection
addresses, Location whoIsAsking) { + if (!t.equals(CBB_SIP_ADDRESS) || !m_featureManager.isFeatureEnabled(FEATURE)) { + return null; + } + + if (whoIsAsking != null && m_featureManager.isFeatureEnabled(FEATURE, whoIsAsking)) { + return new Address(t, getAddress(whoIsAsking.getHostnameInSipDomain())); + } + return new Address(t, getAddress(m_domainManager.getDomainName())); + } + + private String getAddress(String host) { + return String.format("%s.%s", CBB, host); + } + + @Override + public Collection getResourceRecords(DnsManager manager) { + FeatureManager fm = manager.getAddressManager().getFeatureManager(); + List locations = fm.getLocationsForEnabledFeature(FEATURE); + if (locations == null || locations.isEmpty()) { + return Collections.emptyList(); + } + ResourceRecords records = new ResourceRecords("_sip._tcp", CBB, true); + for (Location l : locations) { + int port = m_fsFeature.getSettings(l).getFreeswitchSipPort(); + records.addRecord(new ResourceRecord(l.getHostname(), port, l.getRegionId())); + } + return Collections.singleton(records); + } + + public boolean isEnabled() { + return m_featureManager.isFeatureEnabled(FEATURE); + } + + @Required + public void setFeatureManager(FeatureManager featureManager) { + m_featureManager = featureManager; + } + + @Required + public void setSettingsDao(BeanWithSettingsDao settingsDao) { + m_settingsDao = settingsDao; + } + + @Required + public void setDomainManager(DomainManager domainManager) { + m_domainManager = domainManager; + } + + @Required + public void setFreeswitchFeature(FreeswitchFeature fsFeature) { + m_fsFeature = fsFeature; + } + +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/CallbackSettings.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/CallbackSettings.java new file mode 100644 index 0000000000..390d014335 --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/CallbackSettings.java @@ -0,0 +1,50 @@ +/** + * + * + * Copyright (c) 2015 sipXcom, Inc. All rights reserved. + * Contributed to SIPfoundry under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.callback; + +import java.util.Arrays; +import java.util.Collection; + +import org.sipfoundry.sipxconfig.cfgmgt.DeployConfigOnEdit; +import org.sipfoundry.sipxconfig.dialplan.DialPlanContext; +import org.sipfoundry.sipxconfig.feature.Feature; +import org.sipfoundry.sipxconfig.setting.PersistableSettings; +import org.sipfoundry.sipxconfig.setting.Setting; + +public class CallbackSettings extends PersistableSettings implements DeployConfigOnEdit { + + public static final String CALLBACK_PREFIX = "callback-config/callback-prefix"; + + @Override + public Collection getAffectedFeaturesOnChange() { + return Arrays.asList((Feature) CallbackOnBusy.FEATURE, DialPlanContext.FEATURE); + } + + @Override + public String getBeanId() { + return "callbackSettings"; + } + + @Override + protected Setting loadSettings() { + return getModelFilesContext().loadModelFile("sipxcallback/sipxcallback.xml"); + } + + public String getCallbackPrefix() { + return getSettingValue(CALLBACK_PREFIX); + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/callback.beans.xml b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/callback.beans.xml new file mode 100644 index 0000000000..78e1e6d4ee --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callback/callback.beans.xml @@ -0,0 +1,38 @@ + + + + + + + org.sipfoundry.sipxconfig.callback.CallbackOnBusy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callgroup/CallGroup.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callgroup/CallGroup.java index c6183e1099..418bb57115 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callgroup/CallGroup.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callgroup/CallGroup.java @@ -9,8 +9,10 @@ */ package org.sipfoundry.sipxconfig.callgroup; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -19,7 +21,9 @@ import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang.StringUtils; +import org.sipfoundry.commons.mongo.MongoConstants; import org.sipfoundry.commons.security.Md5Encoder; +import org.sipfoundry.sipxconfig.branch.Branch; import org.sipfoundry.sipxconfig.common.Replicable; import org.sipfoundry.sipxconfig.common.SipUri; import org.sipfoundry.sipxconfig.common.User; @@ -43,6 +47,7 @@ public class CallGroup extends AbstractCallSequence implements Replicable, Syste private String m_sipPassword; private boolean m_useFwdTimers; private Integer m_fallbackExpire = AbstractRing.DEFAULT_EXPIRATION; + private Set m_locations = new HashSet(); public CallGroup() { generateSipPassword(); @@ -60,6 +65,7 @@ protected Object clone() throws CloneNotSupportedException { ringClone.setCallGroup(clone); clone.insertRing(ringClone); } + clone.setLocations(new HashSet(m_locations)); return clone; } @@ -147,6 +153,23 @@ public void setFallbackExpire(Integer expire) { m_fallbackExpire = expire; } + public Set getLocations() { + return m_locations; + } + + public void setLocations(Set locations) { + m_locations = locations; + } + + public List getLocationsList() { + return new ArrayList(m_locations); + } + + public void setLocationsList(List locations) { + m_locations.clear(); + m_locations.addAll(locations); + } + /** * Inserts a new ring for a specific user * @@ -264,7 +287,13 @@ public boolean isValidUser() { @Override public Map getMongoProperties(String domain) { - return Collections.emptyMap(); + Map props = new HashMap(); + List locations = new ArrayList(); + for (Branch branch : m_locations) { + locations.add(branch.getName()); + } + props.put(MongoConstants.LOCATIONS, locations); + return props; } @Override diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callgroup/callGroup.hbm.xml b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callgroup/callGroup.hbm.xml index 94739c07c9..342694c9e8 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callgroup/callGroup.hbm.xml +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/callgroup/callGroup.hbm.xml @@ -42,6 +42,10 @@ + + + + diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/Cdr.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/Cdr.java index 957aa67968..0fd590727e 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/Cdr.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/Cdr.java @@ -12,6 +12,9 @@ import java.io.Serializable; import java.util.Date; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.sipfoundry.sipxconfig.dialplan.CallTag; import org.sipfoundry.sipxconfig.common.SipUri; @@ -55,8 +58,11 @@ public static Termination fromString(String t) { public static final String CALL_EMERGENCY = "EMERGENCY"; public static final String CALL_CUST = "CUSTOM"; + private static final Log LOG = LogFactory.getLog(Cdr.class); + private static final long serialVersionUID = 1L; private static final String CALLTAG_DELIM = ","; + private static final String USER_NAME_DELIM = " - "; private String m_callerAor; private String m_calleeAor; @@ -82,6 +88,27 @@ public static Termination fromString(String t) { private boolean m_callerInternal; private String m_calleeRoute; + private boolean m_privacy; + private int m_limit; + private String m_privacyExcluded = StringUtils.EMPTY; + private String m_mask = StringUtils.EMPTY; + + private String m_calledNumberAor; + private String m_calledNumber; + private int m_trunkId; + + public Cdr(boolean privacy, int limit, String privacyExcluded) { + m_privacy = privacy; + m_limit = limit; + m_privacyExcluded = privacyExcluded; + for (int i = 0; i < m_limit; i++) { + m_mask += "*"; + } + } + + public Cdr() { + } + public String getCalleeAor() { return m_calleeAor; } @@ -101,11 +128,17 @@ public String getRecipient() { public void setCalleeAor(String calleeAor) { m_calleeAor = calleeAor; m_callee = SipUri.extractUser(calleeAor); + if (m_privacy) { + m_callee = maskAor(m_callee); + } } public void setCalleeContact(String calleeContact) { m_calleeContact = calleeContact; m_recipient = SipUri.extractUser(calleeContact); + if (m_privacy) { + m_recipient = maskAor(m_recipient); + } } public String getCallerAor() { @@ -127,6 +160,9 @@ public String getOriginator() { public void setCallerAor(String callerAor) { m_callerAor = callerAor; m_caller = SipUri.extractFullUser(callerAor); + if (m_privacy) { + m_caller = maskAor(m_caller); + } } public void setCallerContact(String callerContact) { @@ -189,7 +225,6 @@ public void setCallId(String callid) { m_callid = callid; } - public String getReference() { return m_reference; } @@ -291,5 +326,51 @@ public String getCallTypeShortName() { return callType; } + public String getCalledNumber() { + return m_calledNumber; + } + + public void setCalledNumber(String calledNumber) { + m_calledNumberAor = calledNumber; + m_calledNumber = SipUri.extractUser(calledNumber); + } + + public int getGateway() { + return m_trunkId; + } + + public void setGateway(int id) { + m_trunkId = id; + } + + private String maskAor(String aor) { + if (aor != null) { + String tempAor; + if (aor.contains(USER_NAME_DELIM)) { + // AOR contains displayname, remove for correct check + tempAor = aor.substring(aor.indexOf(USER_NAME_DELIM) + 3); + } else { + tempAor = aor; + } + + if (tempAor.length() >= m_limit) { + // Mask if not excluded + String[] exclude = m_privacyExcluded.split(" "); + for (String prefix : exclude) { + if (StringUtils.isNotEmpty(prefix) + && tempAor.startsWith(prefix)) { + // Return unmodified (with Displayname) + return aor; + } + } + return tempAor.substring(0, tempAor.length() - m_limit) + m_mask; + } else { + // Is below limit, return (completely with displayname) + return aor; + } + } else { + return aor; + } + } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrConfiguration.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrConfiguration.java index 679212a453..494e96c0de 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrConfiguration.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrConfiguration.java @@ -55,7 +55,7 @@ public void replicate(ConfigManager manager, ConfigRequest request) throws IOExc continue; } ConfigUtils.enableCfengineClass(dir, datfile, true, CdrManager.FEATURE.getId(), "postgres"); - FileWriter wtr = new FileWriter(new File(dir, "callresolver-config")); + FileWriter wtr = new FileWriter(new File(dir, "callresolver-config.part")); try { write(wtr, proxyLocations, settings); } finally { diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrManager.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrManager.java index 5b7a63d7c4..694925b000 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrManager.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrManager.java @@ -13,6 +13,7 @@ import java.io.Writer; import java.util.Date; import java.util.List; +import java.util.TimeZone; import org.sipfoundry.sipxconfig.address.AddressType; import org.sipfoundry.sipxconfig.common.User; @@ -38,6 +39,7 @@ public interface CdrManager { */ List getCdrs(Date from, Date to, CdrSearch search, User user); List getCdrs(Date from, Date to, CdrSearch search, User user, int limit, int offset); + List getCdrs(Date from, Date to, CdrSearch search, User user, TimeZone timezone, int limit, int offset); /** @@ -54,9 +56,10 @@ public interface CdrManager { * @param writer CSV stream destination * @param from date of first CDR retrieved, pass null for oldest * @param to date of the last CDR retrieved, pass null for latest + * @param timezone * @param search specification - enumeration representing columns and string to search for */ - void dumpCdrs(Writer writer, Date from, Date to, CdrSearch search, User user) throws IOException; + void dumpCdrs(Writer writer, Date from, Date to, TimeZone timezone, CdrSearch search, User user) throws IOException; /** * Dump CDRs in a JSON format used by Exhibit platform. diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrManagerImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrManagerImpl.java index 09897d6589..266d6abd15 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrManagerImpl.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrManagerImpl.java @@ -39,6 +39,7 @@ import org.apache.commons.lang.time.DateUtils; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; +import org.sipfoundry.commons.util.TimeZoneUtils; import org.sipfoundry.sipxconfig.address.Address; import org.sipfoundry.sipxconfig.address.AddressManager; import org.sipfoundry.sipxconfig.address.AddressProvider; @@ -95,6 +96,8 @@ public class CdrManagerImpl extends JdbcDaoSupport implements CdrManager, Featur static final String CALLEE_ROUTE = "callee_route"; static final String CALLEE_CONTACT = "callee_contact"; static final String CALLER_CONTACT = "caller_contact"; + static final String CALLED_NUMBER = "called_number"; + static final String GATEWAY = "gateway"; private int m_csvLimit; private int m_jsonLimit; @@ -132,10 +135,28 @@ public List getCdrs(Date from, Date to, CdrSearch search, User user) { @Override public List getCdrs(Date from, Date to, CdrSearch search, User user, int limit, int offset) { - CdrsStatementCreator psc = new SelectAll(from, to, search, user, (user != null) ? (user.getTimezone()) - : m_tz, limit, offset); - CdrsResultReader resultReader = new CdrsResultReader((user != null) ? (user.getTimezone()) - : (getTimeZone())); + return getCdrs(from, to, search, user, null, 0, 0); + } + + @Override + public List getCdrs(Date fromDate, Date toDate, CdrSearch search, User user, + TimeZone timeZone, int limit, int offset) { + Date from = fromDate; + Date to = toDate; + if (timeZone != null) { + from = TimeZoneUtils.getSameDateWithNewTimezone(fromDate, timeZone); + to = TimeZoneUtils.getSameDateWithNewTimezone(toDate, timeZone); + } + CdrsStatementCreator psc = new SelectAll(from, to, search, user, (user != null) + ? (user.getTimezone()) : m_tz, limit, offset); + TimeZone resultsTimeZone = timeZone; + if (resultsTimeZone == null) { + resultsTimeZone = (user != null) ? (user.getTimezone()) : (getTimeZone()); + } + CdrsResultReader resultReader = new CdrsResultReader(resultsTimeZone, + getSettings().getPrivacyStatus(), getSettings().getPrivacyMinLength(), + getSettings().getPrivacyExcludeList()); + getJdbcTemplate().query(psc, resultReader); return resultReader.getResults(); } @@ -146,10 +167,11 @@ private TimeZone getTimeZone() { } @Override - public void dumpCdrs(Writer writer, Date from, Date to, CdrSearch search, User user) throws IOException { - ColumnInfoFactory columnInforFactory = new DefaultColumnInfoFactory(m_tz); + public void dumpCdrs(Writer writer, Date from, Date to, TimeZone displayTimeZone, + CdrSearch search, User user) throws IOException { + ColumnInfoFactory columnInforFactory = new DefaultColumnInfoFactory(m_tz, displayTimeZone); CdrsWriter resultReader = new CdrsCsvWriter(writer, columnInforFactory); - dump(resultReader, from, to, search, user, m_csvLimit); + dump(resultReader, from, to, displayTimeZone, search, user, m_csvLimit); } @Override @@ -160,7 +182,7 @@ public void dumpCdrsJson(Writer out) throws IOException { // if we cannot see all the result - get only the latest CdrSearch cdrSearch = new CdrSearch(); cdrSearch.setOrder(START_TIME, false); - dump(resultReader, null, null, cdrSearch, null, m_jsonLimit); + dump(resultReader, null, null, null, cdrSearch, null, m_jsonLimit); } /** @@ -173,9 +195,17 @@ public void dumpCdrsJson(Writer out) throws IOException { * If we had direct access to that connection we could try calling "setChunkedStreamingMode" * on it. */ - private void dump(CdrsWriter resultReader, Date from, Date to, CdrSearch search, User user, int limit) + private void dump(CdrsWriter resultReader, Date fromDate, Date toDate, TimeZone timezone, + CdrSearch search, User user, int limit) throws IOException { - PreparedStatementCreator psc = new SelectAll(from, to, search, user, m_tz, limit, 0); + Date from = fromDate; + Date to = toDate; + if (timezone != null) { + from = TimeZoneUtils.getSameDateWithNewTimezone(fromDate, timezone); + to = TimeZoneUtils.getSameDateWithNewTimezone(toDate, timezone); + } + PreparedStatementCreator psc = new SelectAll(from, to, search, user, (user != null) + ? (user.getTimezone()) : m_tz, limit, 0); try { resultReader.writeHeader(); getJdbcTemplate().query(psc, resultReader); @@ -392,6 +422,17 @@ static class CdrsResultReader implements RowCallbackHandler { private final Calendar m_calendar; private TimeZone m_systemTimeZone; + private boolean m_privacy; + private int m_privacyLimit; + private String m_privacyExcluded; + + public CdrsResultReader(TimeZone tz, boolean privacy, int limit, String excluded) { + m_calendar = Calendar.getInstance(tz); + m_privacy = privacy; + m_privacyLimit = limit; + m_privacyExcluded = excluded; + } + public CdrsResultReader(TimeZone tz) { m_calendar = Calendar.getInstance(tz); } @@ -402,7 +443,7 @@ public List getResults() { @Override public void processRow(ResultSet rs) throws SQLException { - Cdr cdr = new Cdr(); + Cdr cdr = new Cdr(m_privacy, m_privacyLimit, m_privacyExcluded); cdr.setCalleeAor(rs.getString(CALLEE_AOR)); cdr.setCallerAor(rs.getString(CALLER_AOR)); cdr.setCallId(rs.getString(CALL_ID)); @@ -421,6 +462,8 @@ public void processRow(ResultSet rs) throws SQLException { cdr.setFailureStatus(rs.getInt(FAILURE_STATUS)); String termination = rs.getString(TERMINATION); cdr.setTermination(Termination.fromString(termination)); + cdr.setCalledNumber(rs.getString(CALLED_NUMBER)); + cdr.setGateway(rs.getInt(GATEWAY)); m_cdrs.add(cdr); } } @@ -448,11 +491,14 @@ static class ColumnInfo { private final boolean m_timestamp; private Format m_format; private final Calendar m_calendar; + private final TimeZone m_displayTimeZone; - public ColumnInfo(ResultSet rs, int i, Calendar calendar, Format dateFormat, Format aorFormat) + public ColumnInfo(ResultSet rs, int i, Calendar calendar, TimeZone displayTimeZone, + Format dateFormat, Format aorFormat) throws SQLException { m_fieldIndex = i; m_calendar = calendar; + m_displayTimeZone = displayTimeZone; m_rsIndex = rs.findColumn(FIELDS[m_fieldIndex]); m_timestamp = TIME_FIELDS[m_fieldIndex]; if (AOR_FIELDS[m_fieldIndex]) { @@ -485,7 +531,13 @@ public String formatValue(ResultSet rs) throws SQLException { if (m_format == null) { return v.toString(); } - return m_format.format(v); + if (m_timestamp) { + Timestamp timestamp = (Timestamp) v; + Date cal = TimeZoneUtils.convertDateToNewTimezone(new Date( + timestamp.getTime()), m_displayTimeZone); + return m_format.format(cal); + } + return StringUtils.EMPTY; } public String getField() { @@ -501,16 +553,22 @@ static class DefaultColumnInfoFactory implements ColumnInfoFactory { private Format m_dateFormat = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT; private Format m_aorFormat; private final Calendar m_calendar; + private final TimeZone m_displayTimezone; public DefaultColumnInfoFactory(TimeZone tz) { + this(tz, null); + } + + public DefaultColumnInfoFactory(TimeZone tz, TimeZone displayTimeZone) { m_calendar = Calendar.getInstance(tz); + m_displayTimezone = displayTimeZone; } @Override public ColumnInfo[] create(ResultSet rs) throws SQLException { ColumnInfo[] fields = new ColumnInfo[ColumnInfo.FIELDS.length]; for (int i = 0; i < fields.length; i++) { - ColumnInfo ci = new ColumnInfo(rs, i, m_calendar, m_dateFormat, m_aorFormat); + ColumnInfo ci = new ColumnInfo(rs, i, m_calendar, m_displayTimezone, m_dateFormat, m_aorFormat); fields[i] = ci; } return fields; @@ -637,8 +695,15 @@ public Collection getArchiveDefinitions(BackupManager manager return null; } - ArchiveDefinition def = new ArchiveDefinition(ARCHIVE, "sipxcdr-archive --backup %s", - "sipxcdr-archive --restore %s"); + String tmpDir = ""; + if (manualSettings != null) { + String tmpDirectory = manualSettings.getTmpDir(); + if (StringUtils.isNotBlank(tmpDirectory)) { + tmpDir = " --tmp-dir " + tmpDirectory; + } + } + ArchiveDefinition def = new ArchiveDefinition(ARCHIVE, "sipxcdr-archive --backup %s" + tmpDir, + "sipxcdr-archive --restore %s" + tmpDir); return Collections.singleton(def); } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrSearch.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrSearch.java index 44032c3fa5..490d79bed5 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrSearch.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrSearch.java @@ -90,16 +90,12 @@ private void appendSearchTermSql(StringBuilder sql, String call) { private void appendCallerSql(StringBuilder sql) { sql.append(OPEN_PARANTHESIS); appendSearchTermSql(sql, CdrManagerImpl.CALLER_AOR); - sql.append(AND); - sql.append(CdrManagerImpl.CALLER_INTERNAL); - sql.append("=true)"); + sql.append(CLOSED_PARANTHESIS); } private void appendCalleeSql(StringBuilder sql) { sql.append(OPEN_PARANTHESIS); appendSearchTermSql(sql, CdrManagerImpl.CALLEE_AOR); - sql.append(AND); - appendCalleeInternalRouteSql(sql); sql.append(CLOSED_PARANTHESIS); } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrSettings.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrSettings.java index d8940db7ee..ab83648864 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrSettings.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cdr/CdrSettings.java @@ -30,6 +30,22 @@ public int getAgentPort() { return (Integer) getSettingTypedValue("callresolver/SIP_CALLRESOLVER_AGENT_PORT"); } + public boolean getPrivacyStatus() { + return (Boolean) getSettingTypedValue("callresolver/SIP_CALLRESOLVER_PRIVACY"); + } + + public int getPrivacyMinLength() { + return (Integer) getSettingTypedValue("callresolver/SIP_CALLRESOLVER_PRIVACY_LENGTH"); + } + + public String getPrivacyExcludeList() { + String excludeList = (String) getSettingTypedValue("callresolver/SIP_CALLRESOLVER_PRIVACY_EXCLUDE"); + if (excludeList == null) { + return ""; + } + return excludeList; + } + @Override protected Setting loadSettings() { return getModelFilesContext().loadModelFile("sipxcallresolver/sipxcallresolver.xml"); diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/AbstractCertificateCommon.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/AbstractCertificateCommon.java index cc15b5f775..6e3959ecb2 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/AbstractCertificateCommon.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/AbstractCertificateCommon.java @@ -9,6 +9,9 @@ import static java.lang.String.format; +import java.util.ArrayList; +import java.util.List; + public class AbstractCertificateCommon { public static final int DEFAULT_KEY_SIZE = 2048; @@ -18,6 +21,7 @@ public class AbstractCertificateCommon { private String m_organization; private String m_organizationUnit = "sipXecs"; private String m_commonName; + private List m_sanNames = new ArrayList(); private String m_email; private String m_dnsDomain; private String m_host; @@ -130,4 +134,12 @@ public void setBitCount(int bitCount) { public String getHost() { return m_host; } + + public void addSanName(String sanName) { + m_sanNames.add(sanName); + } + + public List getSanNames() { + return m_sanNames; + } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/CertificateConfig.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/CertificateConfig.java index 7e9f6fa6a2..02b25f76c1 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/CertificateConfig.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/CertificateConfig.java @@ -17,11 +17,11 @@ package org.sipfoundry.sipxconfig.cert; import java.io.File; - import java.io.FileWriter; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; +import java.util.Set; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; @@ -30,6 +30,7 @@ import org.sipfoundry.sipxconfig.cfgmgt.ConfigManager; import org.sipfoundry.sipxconfig.cfgmgt.ConfigProvider; import org.sipfoundry.sipxconfig.cfgmgt.ConfigRequest; +import org.sipfoundry.sipxconfig.commserver.Location; import org.sipfoundry.sipxconfig.domain.Domain; import org.springframework.beans.factory.annotation.Required; @@ -46,74 +47,79 @@ public void replicate(ConfigManager manager, ConfigRequest request) throws IOExc boolean chainCertificate = false; boolean caCertificate = false; - File dir = manager.getGlobalDataDirectory(); - String sipCert = m_certificateManager.getCommunicationsCertificate(); - FileUtils.writeStringToFile(new File(dir, "ssl.crt"), sipCert); - String sipKey = m_certificateManager.getCommunicationsPrivateKey(); - FileUtils.writeStringToFile(new File(dir, "ssl.key"), sipKey); - String webCert = m_certificateManager.getWebCertificate(); - FileUtils.writeStringToFile(new File(dir, "ssl-web.crt"), webCert); - StringBuffer openfireCert = new StringBuffer(); - openfireCert.append(webCert); - String webKey = m_certificateManager.getWebPrivateKey(); - File sslWebKey = new File(dir, "ssl-web.key"); - FileUtils.writeStringToFile(sslWebKey, webKey); - - String openfireSslKey = CertificateUtils.convertSslKeyToRSA(sslWebKey); - if (openfireSslKey != null) { - FileUtils.writeStringToFile(new File(dir, OPENFIRE_KEY), openfireSslKey); - } else { - FileUtils.writeStringToFile(new File(dir, OPENFIRE_KEY), webKey); - } - - String chainCert = m_certificateManager.getChainCertificate(); - if (chainCert != null) { - FileUtils.writeStringToFile(new File(dir, "server-chain.crt"), chainCert); - openfireCert.append(chainCert); - chainCertificate = true; - } - String caCert = m_certificateManager.getCACertificate(); - if (caCert != null) { - FileUtils.writeStringToFile(new File(dir, "ca-bundle.crt"), caCert); - openfireCert.append(caCert); - caCertificate = true; - } - FileUtils.writeStringToFile(new File(dir, "ssl-openfire.crt"), openfireCert.toString()); - Writer writer = new FileWriter(new File(dir, "ssl.conf")); - try { - write(writer, chainCertificate, caCertificate); - } finally { - IOUtils.closeQuietly(writer); - } - - String domain = Domain.getDomain().getName(); - - JavaKeyStore sslSip = new JavaKeyStore(); - sslSip.addKey(domain, sipCert, sipKey); - sslSip.storeIfDifferent(new File(dir, "ssl.keystore")); - - JavaKeyStore sslWeb = new JavaKeyStore(); - sslWeb.addKey(domain, webCert, webKey); - sslWeb.storeIfDifferent(new File(dir, "ssl-web.keystore")); - - //store the full chain for openfire certificate - JavaKeyStore sslOpenfire = new JavaKeyStore(); - sslOpenfire.addKeys(domain, openfireCert.toString(), new String(openfireSslKey)); - sslOpenfire.storeIfDifferent(new File(dir, "ssl-openfire.keystore")); - - File authDir = new File(dir, "authorities"); - authDir.mkdir(); - JavaKeyStore store = new JavaKeyStore(); - for (String authority : m_certificateManager.getAuthorities()) { - String authCert = m_certificateManager.getAuthorityCertificate(authority); - FileUtils.writeStringToFile(new File(authDir, authority + ".crt"), authCert); - store.addAuthority(authority, authCert); - } - OutputStream authoritiesStore = null; - try { - store.storeIfDifferent(new File(dir, "authorities.jks")); - } finally { - IOUtils.closeQuietly(authoritiesStore); + Set locations = request.locations(manager); + for (Location location : locations) { + File dir = manager.getLocationDataDirectory(location); + String sipCert = m_certificateManager.getCommunicationsCertificate(); + FileUtils.writeStringToFile(new File(dir, "ssl.crt"), sipCert); + String sipKey = m_certificateManager.getCommunicationsPrivateKey(); + FileUtils.writeStringToFile(new File(dir, "ssl.key"), sipKey); + String webCert = m_certificateManager.getWebCertificate(); + FileUtils.writeStringToFile(new File(dir, "ssl-web.crt"), webCert); + StringBuffer openfireCert = new StringBuffer(); + openfireCert.append(webCert); + String webKey = m_certificateManager.getWebPrivateKey(); + File sslWebKey = new File(dir, "ssl-web.key"); + FileUtils.writeStringToFile(sslWebKey, webKey); + + String openfireSslKey = CertificateUtils.convertSslKeyToRSA(sslWebKey); + if (openfireSslKey != null) { + FileUtils.writeStringToFile(new File(dir, OPENFIRE_KEY), openfireSslKey); + } else { + FileUtils.writeStringToFile(new File(dir, OPENFIRE_KEY), webKey); + } + + String chainCert = m_certificateManager.getChainCertificate(); + if (chainCert != null) { + FileUtils.writeStringToFile(new File(dir, "server-chain.crt"), chainCert); + openfireCert.append(chainCert); + chainCertificate = true; + } + String caCert = m_certificateManager.getCACertificate(); + if (caCert != null) { + FileUtils.writeStringToFile(new File(dir, "ca-bundle.crt"), caCert); + openfireCert.append(caCert); + caCertificate = true; + } + FileUtils.writeStringToFile(new File(dir, "ssl-openfire.crt"), openfireCert.toString()); + Writer writer = new FileWriter(new File(dir, "ssl.conf")); + try { + write(writer, chainCertificate, caCertificate); + } finally { + IOUtils.closeQuietly(writer); + } + + String domain = Domain.getDomain().getName(); + + JavaKeyStore sslSip = new JavaKeyStore(); + sslSip.addKey(domain, sipCert, sipKey); + sslSip.storeIfDifferent(new File(dir, "ssl.keystore")); + + JavaKeyStore sslWeb = new JavaKeyStore(); + sslWeb.addKey(domain, webCert, webKey); + sslWeb.storeIfDifferent(new File(dir, "ssl-web.keystore")); + + //store the full chain for openfire certificate + JavaKeyStore sslOpenfire = new JavaKeyStore(); + sslOpenfire.addKeys(domain, openfireCert.toString(), new String(openfireSslKey)); + sslOpenfire.storeIfDifferent(new File(dir, "ssl-openfire.keystore")); + + File authDir = new File(dir, "authorities"); + //remove old certs + FileUtils.deleteQuietly(authDir); + authDir.mkdir(); + JavaKeyStore store = new JavaKeyStore(); + for (String authority : m_certificateManager.getAuthorities()) { + String authCert = m_certificateManager.getAuthorityCertificate(authority); + FileUtils.writeStringToFile(new File(authDir, authority + ".crt"), authCert); + store.addAuthority(authority, authCert); + } + OutputStream authoritiesStore = null; + try { + store.storeIfDifferent(new File(dir, "authorities.jks")); + } finally { + IOUtils.closeQuietly(authoritiesStore); + } } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/CertificateGenerator.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/CertificateGenerator.java index 31fe29b815..c3cf2f64b0 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/CertificateGenerator.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/CertificateGenerator.java @@ -18,6 +18,7 @@ import java.util.List; import org.apache.commons.lang.StringUtils; + import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; @@ -37,12 +38,24 @@ protected CertificateGenerator(String domain, String fqdn, String issuer, String m_authorityKey = authorityKey; } - public static CertificateGenerator web(String domain, String fqdn, String issuer, String authorityKey) { - return new CertificateGenerator(domain, fqdn, issuer, authorityKey); + private static CertificateGenerator genCert(String domain, String fqdn, String issuer, + String authorityKey, List altHosts) { + CertificateGenerator gen = new CertificateGenerator(domain, fqdn, issuer, authorityKey); + for (String host : altHosts) { + gen.addSanName(host); + } + return gen; } - public static CertificateGenerator sip(String sipDomain, String fqdn, String issuer, String authorityKey) { - CertificateGenerator gen = new CertificateGenerator(sipDomain, fqdn, issuer, authorityKey); + public static CertificateGenerator web(String domain, String fqdn, String issuer, String authorityKey, + List altHosts) { + CertificateGenerator gen = genCert(domain, fqdn, issuer, authorityKey, altHosts); + return gen; + } + + public static CertificateGenerator sip(String sipDomain, String fqdn, String issuer, String authorityKey, + List altHosts) { + CertificateGenerator gen = genCert(sipDomain, fqdn, issuer, authorityKey, altHosts); gen.m_sipDomain = sipDomain; return gen; } @@ -69,6 +82,9 @@ public X509Certificate createCertificate() throws GeneralSecurityException { names.add(new GeneralName(GeneralName.uniformResourceIdentifier, format("sip:%s", m_sipDomain))); } names.add(new GeneralName(GeneralName.dNSName, getCommonName())); + for (String host : getSanNames()) { + names.add(new GeneralName(GeneralName.dNSName, host)); + } gen.addExtension(X509Extension.subjectAlternativeName, false, new GeneralNames((GeneralName[]) names.toArray(new GeneralName[names.size()]))); diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/CertificateManagerImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/CertificateManagerImpl.java index 409f1ab558..24b26f0d48 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/CertificateManagerImpl.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/CertificateManagerImpl.java @@ -16,6 +16,7 @@ import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.List; import org.apache.commons.io.IOUtils; @@ -25,6 +26,7 @@ import org.sipfoundry.sipxconfig.cfgmgt.ConfigManager; import org.sipfoundry.sipxconfig.common.DaoUtils; import org.sipfoundry.sipxconfig.common.UserException; +import org.sipfoundry.sipxconfig.commserver.Location; import org.sipfoundry.sipxconfig.commserver.LocationsManager; import org.sipfoundry.sipxconfig.domain.Domain; import org.sipfoundry.sipxconfig.setting.BeanWithSettingsDao; @@ -223,15 +225,27 @@ private void rebuildCert(String type, int keySize) { String issuer = getIssuer(authority); String authKey = getAuthorityKey(authority); CertificateGenerator gen; + List altLocations = getAltLocations(); if (type.equals(COMM_CERT)) { - gen = CertificateGenerator.sip(domain, fqdn, issuer, authKey); + gen = CertificateGenerator.sip(domain, fqdn, issuer, authKey, altLocations); } else { - gen = CertificateGenerator.web(domain, fqdn, issuer, authKey); + gen = CertificateGenerator.web(domain, fqdn, issuer, authKey, altLocations); } gen.setBitCount(keySize); updateCertificate(type, gen.getCertificateText(), gen.getPrivateKeyText(), authority); } + private List getAltLocations() { + Location[] locations = m_locationsManager.getLocations(); + List altLocations = new ArrayList(); + for (Location location : locations) { + if (!location.isPrimary()) { + altLocations.add(location.getFqdn()); + } + } + return altLocations; + } + @Override public void deleteTrustedAuthority(String authority) { if (authority.equals(getSelfSigningAuthority())) { @@ -281,13 +295,14 @@ public void checkSetup(int keySize) { String fqdn = m_locationsManager.getPrimaryLocation().getFqdn(); String issuer = getIssuer(authority); String authKey = getAuthorityKey(authority); + List altLocations = getAltLocations(); if (!hasCertificate(COMM_CERT, authority)) { - CertificateGenerator gen = CertificateGenerator.sip(domain, fqdn, issuer, authKey); + CertificateGenerator gen = CertificateGenerator.sip(domain, fqdn, issuer, authKey, altLocations); gen.setBitCount(keySize); updateCertificate(COMM_CERT, gen.getCertificateText(), gen.getPrivateKeyText(), authority); } if (!hasCertificate(WEB_CERT, authority)) { - CertificateGenerator gen = CertificateGenerator.web(domain, fqdn, issuer, authKey); + CertificateGenerator gen = CertificateGenerator.web(domain, fqdn, issuer, authKey, altLocations); gen.setBitCount(keySize); updateCertificate(WEB_CERT, gen.getCertificateText(), gen.getPrivateKeyText(), authority); } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/certificates.beans.xml b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/certificates.beans.xml index 5cdc8fca4e..cd2a52e853 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/certificates.beans.xml +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/certificates.beans.xml @@ -27,7 +27,7 @@ - verisignclass3ca.crt + verisignclass3ca diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/verisignclass3ca.crt b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/verisignclass3ca similarity index 100% rename from sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/verisignclass3ca.crt rename to sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cert/verisignclass3ca diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cfgmgt/ConfigManagerImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cfgmgt/ConfigManagerImpl.java index c538e61e56..64bdc7a830 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cfgmgt/ConfigManagerImpl.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cfgmgt/ConfigManagerImpl.java @@ -40,6 +40,7 @@ import org.sipfoundry.sipxconfig.alarm.AlarmDefinition; import org.sipfoundry.sipxconfig.alarm.AlarmProvider; import org.sipfoundry.sipxconfig.alarm.AlarmServerManager; +import org.sipfoundry.sipxconfig.common.ConfigManagerEvent; import org.sipfoundry.sipxconfig.common.LazyDaemon; import org.sipfoundry.sipxconfig.common.MongoGenerationFinishedEvent; import org.sipfoundry.sipxconfig.common.UserException; @@ -55,14 +56,18 @@ import org.sipfoundry.sipxconfig.setup.SetupManager; import org.sipfoundry.sipxconfig.systemaudit.ConfigChangeAction; import org.sipfoundry.sipxconfig.systemaudit.SystemAuditManager; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.annotation.Required; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; public class ConfigManagerImpl implements AddressProvider, ConfigManager, BeanFactoryAware, AlarmProvider, - ConfigCommands, SetupListener, ApplicationListener, DaoEventListener { + ConfigCommands, SetupListener, ApplicationListener, DaoEventListener, + ApplicationContextAware { private static final Log LOG = LogFactory.getLog(ConfigManagerImpl.class); private File m_cfDataDir; private DomainManager m_domainManager; @@ -90,6 +95,7 @@ public class ConfigManagerImpl implements AddressProvider, ConfigManager, BeanFa private String m_remoteHostsFile = "%s/.ssh/known_hosts"; private boolean m_flag; private SystemAuditManager m_systemAuditManager; + private ApplicationContext m_applicationContext; @Override public synchronized void configureEverywhere(Feature... features) { @@ -142,7 +148,7 @@ public synchronized void regenerateMongo(Collection locations) { public void sendProfiles(Collection locations) { regenerateMongo(locations); configureAllFeatures(locations); - + m_outstandingRequest[0].setSendProfiles(true); for (Location location : locations) { m_systemAuditManager.onConfigChangeAction(location, ConfigChangeAction.SEND_PROFILE, null, null, null); } @@ -180,6 +186,7 @@ public void doWork(ConfigRequest request) { runProviders(request, jobLabel); runCfengine(request, jobLabel); runPostProviders(request, jobLabel); + m_applicationContext.publishEvent(new ConfigManagerEvent(request)); } } @@ -555,4 +562,9 @@ public Collection getConfigurableLocations() { public void setSystemAuditManager(SystemAuditManager systemAuditManager) { m_systemAuditManager = systemAuditManager; } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + m_applicationContext = applicationContext; + } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cfgmgt/ConfigRequest.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cfgmgt/ConfigRequest.java index 798da8f4ba..6f62dc9f15 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cfgmgt/ConfigRequest.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/cfgmgt/ConfigRequest.java @@ -36,10 +36,24 @@ public final class ConfigRequest { private Set m_affectedFeatures; private boolean m_always; + private boolean m_sendProfiles; private Set m_locations; private Map m_data = new HashMap(); private ConfigRequest() { + this(false); + } + + private ConfigRequest(boolean sendProfiles) { + m_sendProfiles = sendProfiles; + } + + public void setSendProfiles(boolean sendProfiles) { + m_sendProfiles = sendProfiles; + } + + public boolean isSendProfiles() { + return m_sendProfiles; } /** diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CheckDST.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CheckDST.java index 91cf79f239..0b1fd7954f 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CheckDST.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CheckDST.java @@ -51,8 +51,15 @@ public void checkDst() { TimeZone tzLocal = TimeZone.getDefault(); Date dstChangeTime = findDstChangeTime(tzLocal, new Date()); if (dstChangeTime != null) { - LOG.info("DST change detected at " + dstChangeTime.toString()); - setupNotifyTask(dstChangeTime); + //remove minutes from the DST approximate date/time found - this will ensure an error of maximum 59 seconds + //in the night when DST is applied. DST is always applied at fixed hour so is safe to remove minutes + //above fixed hour + Calendar c = Calendar.getInstance(tzLocal); + c.setTime(dstChangeTime); + c.add(Calendar.MINUTE, (-1) * c.get(Calendar.MINUTE)); + Date time = c.getTime(); + LOG.info("DST change detected at " + time); + setupNotifyTask(time); } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/ConfigManagerEvent.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/ConfigManagerEvent.java new file mode 100644 index 0000000000..79566535bf --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/ConfigManagerEvent.java @@ -0,0 +1,28 @@ +/** + * + * + * Copyright (c) 2016 eZuce Corp. All rights reserved. + * Contributed to sipXcom under a Contributor Agreement + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.common; + +import org.springframework.context.ApplicationEvent; + +public class ConfigManagerEvent extends ApplicationEvent { + + private static final long serialVersionUID = 1L; + + public ConfigManagerEvent(Object source) { + super(source); + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CoreContext.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CoreContext.java index 6c1e91e975..b7b4632d04 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CoreContext.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CoreContext.java @@ -147,7 +147,7 @@ List loadUsersByPage(String search, Integer groupId, Integer branchId, int * @see spring 3 security guidelines * @param group */ - void saveGroup(Group group); + void storeGroup(Group group); /** * This method is meant to be intercepted when user group saving permissions are effective @@ -227,4 +227,6 @@ List loadUsersByPage(String search, Integer groupId, Integer branchId, int int getDisabledUsersCount(); int getPhantomUsersCount(); + + int getPhantomUsersWithoutSuperadminCount(); } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CoreContextImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CoreContextImpl.java index ed8a12718a..8cdf889de1 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CoreContextImpl.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CoreContextImpl.java @@ -82,6 +82,8 @@ public abstract class CoreContextImpl extends SipxHibernateDaoSupport impl "select count(u.user_name) from users " + "u inner join setting_value v " + "on u.value_storage_id = v.value_storage_id and v.path='phantom/enabled'"; + private static final String SQL_QUERY_PHANTOM_USERS_WITHOUT_SUPERADMIN = + SQL_QUERY_PHANTOM_USERS + " where u.user_type ='C' AND u.user_name != 'superadmin'"; private static final String PHANTOM_USERS_QUERY = "select u from User u join u.valueStorage vs where " + "vs.databaseValues['phantom/enabled'] != null " + "and vs.databaseValues['phantom/enabled'] = '1'"; @@ -713,7 +715,7 @@ public List getGroups() { } @Override - public void saveGroup(Group group) { + public void storeGroup(Group group) { m_settingDao.saveGroup(group); } @@ -984,6 +986,13 @@ public int getPhantomUsersCount() { return ((Number) q.uniqueResult()).intValue(); } + @Override + public int getPhantomUsersWithoutSuperadminCount() { + Query q = getHibernateTemplate().getSessionFactory().getCurrentSession() + .createSQLQuery(SQL_QUERY_PHANTOM_USERS_WITHOUT_SUPERADMIN); + return ((Number) q.uniqueResult()).intValue(); + } + @Override public void initializeSpecialUsers() { for (SpecialUserType type : SpecialUserType.values()) { diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CronSchedule.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CronSchedule.java index 04760474f7..942f36d72d 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CronSchedule.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/CronSchedule.java @@ -130,7 +130,7 @@ String getCronString(boolean quartzFormat) { String dayOfMonth = quartzFormat ? "?" : ANY; switch (m_type) { case WEEKLY: - return String.format("%s%d %d %s * %d", secs, m_min, m_hrs, dayOfMonth, m_dayOfWeek); + return String.format("%s%d %d %s * %d", secs, m_min, m_hrs, dayOfMonth, m_dayOfWeek - 1); case DAILY: return String.format("%s%d %d %s * *", secs, m_min, m_hrs, dayOfMonth); case HOURLY: @@ -155,7 +155,7 @@ public void setCronString(String cronString) { setScheduledDay(ScheduledDay.EVERYDAY); } else { setType(Type.WEEKLY); - setDayOfWeek(Integer.parseInt(dowString)); + setDayOfWeek(Integer.parseInt(dowString) + 1); } if (ANY.equals(hrsString)) { setHrs(0); diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/GlobalMessageSource.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/GlobalMessageSource.java index 2a96d1f26e..2d88251ede 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/GlobalMessageSource.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/GlobalMessageSource.java @@ -15,11 +15,12 @@ package org.sipfoundry.sipxconfig.common; import java.util.Collection; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Locale; import java.util.Map; -import java.util.Set; +import org.sipfoundry.sipxconfig.Pluggable0ResourceBundleMessageSource; +import org.sipfoundry.sipxconfig.PluggableResourceBundleMessageSource; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.ListableBeanFactory; @@ -91,8 +92,25 @@ public void setBeanFactory(BeanFactory bf) { Collection getDelegates() { if (m_delegates == null) { + //using LinkedHashSet to keep insertion order + LinkedHashSet copy = new LinkedHashSet(); + //pluggable 0 beans will be added first so plugin resource labels to come last + Map pluggable0Beans = m_beanFactory. + getBeansOfType(Pluggable0ResourceBundleMessageSource.class); + Collection pluggable0Values = pluggable0Beans.values(); + //Add pluggable 0 values first - make sure they are not getting overwritten + copy.removeAll(pluggable0Values); + copy.addAll(pluggable0Values); Map beans = m_beanFactory.getBeansOfType(MessageSource.class); - Set copy = new HashSet(beans.values()); + copy.addAll(beans.values()); + //pluggable beans will be added last so plugin resource labels to come first + Map pluggableBeans = m_beanFactory. + getBeansOfType(PluggableResourceBundleMessageSource.class); + Collection pluggableValues = pluggableBeans.values(); + //Add pluggable values last - might be needed to get overwritten + copy.removeAll(pluggableValues); + copy.addAll(pluggableValues); + // otherwise recursive! copy.remove(this); m_delegates = copy; diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/User.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/User.java index 265eeb0b8b..d8ada1cebd 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/User.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/User.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -40,11 +41,12 @@ import org.sipfoundry.sipxconfig.im.ImAccount; import org.sipfoundry.sipxconfig.ivr.Ivr; import org.sipfoundry.sipxconfig.permission.PermissionName; +import org.sipfoundry.sipxconfig.search.IndexedBean; /** * Can be user that logs in, can be superadmin, can be user for phone line */ -public class User extends AbstractUser implements Replicable { +public class User extends AbstractUser implements Replicable, IndexedBean { private static final Log LOG = LogFactory.getLog(User.class); private static final String VM_ENABLED_SETTING_PATH = "voicemail/vacation/vmEnabled"; private static final String ALIAS_RELATION = "alias"; @@ -53,6 +55,9 @@ public class User extends AbstractUser implements Replicable { private static final String E911_SETTING_PATH = "e911/location"; private static final String PHANTOM_USER = "phantom/enabled"; private static final String FORCE_PIN_CHANGE = "voicemail/security/force-pin-change"; + private static final String AUTO_ENTER_PIN_EXTENSION = "voicemail/security/auto-enter-pin-extension"; + private static final String AUTO_ENTER_PIN_EXTERNAL = "voicemail/security/auto-enter-pin-external"; + private static final String DAYS_TO_KEEP_VM = "voicemail/security/days-to-keep-vm"; private String m_identity; private boolean m_validUser = true; @@ -267,6 +272,30 @@ public void setForcePinChange(boolean force) { getSettings().getSetting(FORCE_PIN_CHANGE).setTypedValue(force); } + public boolean isAutoEnterPinExtension() { + return (Boolean) getSettingTypedValue(AUTO_ENTER_PIN_EXTENSION); + } + + public void setAutoEnterPinExtension(boolean autoExtension) { + getSettings().getSetting(AUTO_ENTER_PIN_EXTENSION).setTypedValue(autoExtension); + } + + public boolean isAutoEnterPinExternal() { + return (Boolean) getSettingTypedValue(AUTO_ENTER_PIN_EXTERNAL); + } + + public void setAutoEnterPinExternal(boolean autoExternal) { + getSettings().getSetting(AUTO_ENTER_PIN_EXTERNAL).setTypedValue(autoExternal); + } + + public Integer getDaysToKeepVM() { + return (Integer) getSettingTypedValue(DAYS_TO_KEEP_VM); + } + + public void setDaysToKeepVM(Integer daysToKeepVm) { + getSettings().getSetting(DAYS_TO_KEEP_VM).setTypedValue(daysToKeepVm); + } + public Integer getE911LocationId() { if (getSettingTypedValue(E911_SETTING_PATH) == null) { return null; @@ -296,4 +325,12 @@ public boolean isDepositVoicemail() { public void setDepositVoicemail(boolean value) { setSettingTypedValue(VM_ENABLED_SETTING_PATH, value); } + + @Override + public Set getIndexValues() { + Set values = new LinkedHashSet(); + values.add(getName()); + values.addAll(getAliases()); + return values; + } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/UtcTimestampType.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/UtcTimestampType.java index 4ae91991a5..2eb3616b60 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/UtcTimestampType.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/UtcTimestampType.java @@ -9,52 +9,54 @@ */ package org.sipfoundry.sipxconfig.common; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Calendar; +import java.util.Comparator; import java.util.Date; +import org.hibernate.HibernateException; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.SessionImplementor; +import org.hibernate.type.AbstractSingleColumnStandardBasicType; +import org.hibernate.type.LiteralType; import org.hibernate.type.TimestampType; +import org.hibernate.type.VersionType; +import org.hibernate.type.descriptor.java.JdbcTimestampTypeDescriptor; -/** - * Standard Hibernate assume that type is kept in local time zone and will normalize it to UTC. - * Use this type if your type is already normalized to UTC. - */ -public class UtcTimestampType extends TimestampType { - private Calendar m_local; - - private Calendar getLocalCalendar() { - if (m_local == null) { - m_local = Calendar.getInstance(); - } - return m_local; - } - - /** - * value has been already converted to what Java thought was UTC value, we need to revert the - * results of that conversion - */ - public void set(PreparedStatement st, Date value, int index) throws SQLException { - Date date = value; - Calendar local = getLocalCalendar(); - local.setTime(date); - int offset = local.get(Calendar.ZONE_OFFSET); - local.add(Calendar.MILLISECOND, -offset); - Date localTime = local.getTime(); - super.set(st, localTime, index); - } - - /** - * result of this function will be converted to local value, we need to add now offset that - * will be removed by that conversion - */ - public Object get(ResultSet rs, String name) throws SQLException { - Date value = (Date) super.get(rs, name); - Calendar local = getLocalCalendar(); - local.setTime(value); - int offset = local.get(Calendar.ZONE_OFFSET); - local.add(Calendar.MILLISECOND, offset); - return local.getTime(); +public class UtcTimestampType extends AbstractSingleColumnStandardBasicType implements VersionType, + LiteralType { + + public static final UtcTimestampType INSTANCE = new UtcTimestampType(); + + public UtcTimestampType() { + super(UtcTimestampTypeDescriptor.INSTANCE, JdbcTimestampTypeDescriptor.INSTANCE); + } + + public String getName() { + return TimestampType.INSTANCE.getName(); + } + + @Override + public String[] getRegistrationKeys() { + return TimestampType.INSTANCE.getRegistrationKeys(); + } + + public Date next(Date current, SessionImplementor session) { + return TimestampType.INSTANCE.next(current, session); + } + + public Date seed(SessionImplementor session) { + return TimestampType.INSTANCE.seed(session); + } + + public Comparator getComparator() { + return TimestampType.INSTANCE.getComparator(); + } + + public String objectToSQLString(Date value, Dialect dialect) throws Exception { + return TimestampType.INSTANCE.objectToSQLString(value, dialect); + } + + public Date fromStringValue(String xml) throws HibernateException { + return TimestampType.INSTANCE.fromStringValue(xml); } } + diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/UtcTimestampTypeDescriptor.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/UtcTimestampTypeDescriptor.java new file mode 100644 index 0000000000..f6bfc4c94b --- /dev/null +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/UtcTimestampTypeDescriptor.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2015 eZuce, Inc. All rights reserved. + * + * This software is free software; you can redistribute it and/or modify it under + * the terms of the Affero General Public License (AGPL) as published by the + * Free Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + */ +package org.sipfoundry.sipxconfig.common; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.TimeZone; + +import org.hibernate.type.descriptor.ValueBinder; +import org.hibernate.type.descriptor.ValueExtractor; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; +import org.hibernate.type.descriptor.sql.BasicBinder; +import org.hibernate.type.descriptor.sql.BasicExtractor; +import org.hibernate.type.descriptor.sql.TimestampTypeDescriptor; + +public class UtcTimestampTypeDescriptor extends TimestampTypeDescriptor { + + public static final UtcTimestampTypeDescriptor INSTANCE = new UtcTimestampTypeDescriptor(); + + private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); + + public ValueBinder getBinder(final JavaTypeDescriptor javaTypeDescriptor) { + return new BasicBinder(javaTypeDescriptor, this) { + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + st.setTimestamp(index, javaTypeDescriptor.unwrap(value, Timestamp.class, options), + Calendar.getInstance(UTC)); + } + }; + } + + public ValueExtractor getExtractor(final JavaTypeDescriptor javaTypeDescriptor) { + return new BasicExtractor(javaTypeDescriptor, this) { + @Override + protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException { + return javaTypeDescriptor.wrap(rs.getTimestamp(name, Calendar.getInstance(UTC)), options); + } + }; + } +} diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/common.beans.xml b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/common.beans.xml index 2d0620099c..5d4a6a524e 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/common.beans.xml +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/common.beans.xml @@ -74,7 +74,7 @@ - + diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/common.hbm.xml b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/common.hbm.xml index dabda211be..0a700bf57e 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/common.hbm.xml +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/common/common.hbm.xml @@ -107,7 +107,7 @@ - diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/LocationsConfig.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/LocationsConfig.java index c5648162ef..6d4c0f866a 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/LocationsConfig.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/LocationsConfig.java @@ -22,6 +22,8 @@ import java.io.FileWriter; import java.io.IOException; import java.io.Writer; +import java.util.HashMap; +import java.util.Map; import java.util.Set; import org.apache.commons.io.IOUtils; @@ -58,6 +60,27 @@ public void replicate(ConfigManager manager, ConfigRequest request) throws IOExc IOUtils.closeQuietly(host); } } + + Map hostsEntries = new HashMap(); + for (Location location : locations) { + hostsEntries.put(location.getId(), format("%s %s %s # sipXcom cluster\n", location.getAddress(), + location.getFqdn(), location.getHostname())); + } + + for (Location location : locations) { + File dir = manager.getLocationDataDirectory(location); + Writer hostsPart = new FileWriter(new File(dir, "hosts.part")); + try { + for (Map.Entry entry : hostsEntries.entrySet()) { + Integer locationId = entry.getKey(); + if (locationId != location.getId()) { + hostsPart.write(entry.getValue()); + } + } + } finally { + IOUtils.closeQuietly(hostsPart); + } + } } /** diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/Attendant.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/Attendant.java index 40cbc5ee12..67274fa0cc 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/Attendant.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/Attendant.java @@ -16,7 +16,6 @@ */ package org.sipfoundry.sipxconfig.commserver.imdb; -import static org.sipfoundry.commons.mongo.MongoConstants.ACCOUNT; import static org.sipfoundry.commons.mongo.MongoConstants.ALT_ATTACH_AUDIO; import static org.sipfoundry.commons.mongo.MongoConstants.ALT_EMAIL; import static org.sipfoundry.commons.mongo.MongoConstants.ALT_NOTIFICATION; @@ -28,7 +27,6 @@ import static org.sipfoundry.commons.mongo.MongoConstants.DISPLAY_NAME; import static org.sipfoundry.commons.mongo.MongoConstants.EMAIL; import static org.sipfoundry.commons.mongo.MongoConstants.HASHED_PASSTOKEN; -import static org.sipfoundry.commons.mongo.MongoConstants.HOST; import static org.sipfoundry.commons.mongo.MongoConstants.IM_ADVERTISE_ON_CALL_STATUS; import static org.sipfoundry.commons.mongo.MongoConstants.IM_DISPLAY_NAME; import static org.sipfoundry.commons.mongo.MongoConstants.IM_ENABLED; @@ -38,16 +36,11 @@ import static org.sipfoundry.commons.mongo.MongoConstants.LEAVE_MESSAGE_BEGIN_IM; import static org.sipfoundry.commons.mongo.MongoConstants.LEAVE_MESSAGE_END_IM; import static org.sipfoundry.commons.mongo.MongoConstants.NOTIFICATION; -import static org.sipfoundry.commons.mongo.MongoConstants.PASSWD; import static org.sipfoundry.commons.mongo.MongoConstants.PINTOKEN; -import static org.sipfoundry.commons.mongo.MongoConstants.PORT; -import static org.sipfoundry.commons.mongo.MongoConstants.SYNC; -import static org.sipfoundry.commons.mongo.MongoConstants.TLS; import static org.sipfoundry.commons.mongo.MongoConstants.USERBUSYPROMPT; import static org.sipfoundry.commons.mongo.MongoConstants.VMONDND; import static org.sipfoundry.commons.mongo.MongoConstants.VOICEMAILTUI; -import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils; import org.sipfoundry.sipxconfig.common.Replicable; import org.sipfoundry.sipxconfig.common.User; @@ -108,25 +101,6 @@ public void generate(Replicable entity, DBObject top) { removeField(top, ALT_ATTACH_AUDIO); } - boolean imapServerConfigured = mp.isImapServerConfigured(); - if (imapServerConfigured) { - top.put(SYNC, mp.isSynchronizeWithImapServer()); - top.put(HOST, mp.getImapHost()); - top.put(PORT, mp.getImapPort()); - top.put(TLS, mp.getImapTLS()); - top.put(ACCOUNT, StringUtils.defaultString(mp.getImapAccount())); - String pwd = StringUtils.defaultString(mp.getImapPassword()); - String encodedPwd = new String(Base64.encodeBase64(pwd.getBytes())); - top.put(PASSWD, encodedPwd); - } else { - removeField(top, SYNC); - removeField(top, HOST); - removeField(top, PORT); - removeField(top, TLS); - removeField(top, ACCOUNT); - removeField(top, PASSWD); - } - ImAccount imAccount = new ImAccount(user); top.put(IM_ENABLED, imAccount.isEnabled()); // The following settings used to be in contact-information.xml diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/Mailstore.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/Mailstore.java index 4b2014894a..abc595170c 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/Mailstore.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/Mailstore.java @@ -16,24 +16,26 @@ */ package org.sipfoundry.sipxconfig.commserver.imdb; -import static org.sipfoundry.commons.mongo.MongoConstants.ACCOUNT; import static org.sipfoundry.commons.mongo.MongoConstants.ACTIVEGREETING; import static org.sipfoundry.commons.mongo.MongoConstants.ALT_ATTACH_AUDIO; import static org.sipfoundry.commons.mongo.MongoConstants.ALT_EMAIL; import static org.sipfoundry.commons.mongo.MongoConstants.ALT_NOTIFICATION; import static org.sipfoundry.commons.mongo.MongoConstants.ATTACH_AUDIO; +import static org.sipfoundry.commons.mongo.MongoConstants.AUTO_ENTER_PIN_EXTENSION; +import static org.sipfoundry.commons.mongo.MongoConstants.AUTO_ENTER_PIN_EXTERNAL; import static org.sipfoundry.commons.mongo.MongoConstants.BUTTONS; import static org.sipfoundry.commons.mongo.MongoConstants.CALL_FROM_ANY_IM; import static org.sipfoundry.commons.mongo.MongoConstants.CALL_IM; import static org.sipfoundry.commons.mongo.MongoConstants.CONF_ENTRY_IM; import static org.sipfoundry.commons.mongo.MongoConstants.CONF_EXIT_IM; +import static org.sipfoundry.commons.mongo.MongoConstants.DAYS_TO_KEEP_VM; import static org.sipfoundry.commons.mongo.MongoConstants.DIALPAD; import static org.sipfoundry.commons.mongo.MongoConstants.DISPLAY_NAME; import static org.sipfoundry.commons.mongo.MongoConstants.DISTRIB_LISTS; import static org.sipfoundry.commons.mongo.MongoConstants.EMAIL; import static org.sipfoundry.commons.mongo.MongoConstants.FORCE_PIN_CHANGE; +import static org.sipfoundry.commons.mongo.MongoConstants.FORWARD_DELETE_VOICEMAIL; import static org.sipfoundry.commons.mongo.MongoConstants.HASHED_PASSTOKEN; -import static org.sipfoundry.commons.mongo.MongoConstants.HOST; import static org.sipfoundry.commons.mongo.MongoConstants.IM_ADVERTISE_ON_CALL_STATUS; import static org.sipfoundry.commons.mongo.MongoConstants.IM_DISPLAY_NAME; import static org.sipfoundry.commons.mongo.MongoConstants.IM_ENABLED; @@ -47,13 +49,9 @@ import static org.sipfoundry.commons.mongo.MongoConstants.MOH; import static org.sipfoundry.commons.mongo.MongoConstants.NOTIFICATION; import static org.sipfoundry.commons.mongo.MongoConstants.OPERATOR; -import static org.sipfoundry.commons.mongo.MongoConstants.PASSWD; import static org.sipfoundry.commons.mongo.MongoConstants.PERSONAL_ATT; import static org.sipfoundry.commons.mongo.MongoConstants.PINTOKEN; import static org.sipfoundry.commons.mongo.MongoConstants.PLAY_DEFAULT_VM; -import static org.sipfoundry.commons.mongo.MongoConstants.PORT; -import static org.sipfoundry.commons.mongo.MongoConstants.SYNC; -import static org.sipfoundry.commons.mongo.MongoConstants.TLS; import static org.sipfoundry.commons.mongo.MongoConstants.UNIFIED_MESSAGING_LANGUAGE; import static org.sipfoundry.commons.mongo.MongoConstants.USERBUSYPROMPT; import static org.sipfoundry.commons.mongo.MongoConstants.VMONDND; @@ -63,7 +61,6 @@ import java.util.ArrayList; import java.util.List; -import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils; import org.sipfoundry.sipxconfig.common.DialPad; import org.sipfoundry.sipxconfig.common.Replicable; @@ -106,6 +103,9 @@ public void generate(Replicable entity, DBObject top) { top.put(PINTOKEN, user.getPintoken()); MailboxPreferences mp = new MailboxPreferences(user); + putOnlyIfNotNull(top, FORWARD_DELETE_VOICEMAIL, + user.getSettingValue("voicemail/mailbox/forward-delete-voicemail")); + String emailAddress = mp.getEmailAddress(); boolean enableNotification = StringUtils.isNotBlank(emailAddress) && mp.isEmailNotificationEnabled(); if (StringUtils.isNotBlank(emailAddress)) { @@ -123,6 +123,9 @@ public void generate(Replicable entity, DBObject top) { } top.put(FORCE_PIN_CHANGE, user.getSettingValue("voicemail/security/force-pin-change")); + top.put(AUTO_ENTER_PIN_EXTENSION, user.getSettingValue("voicemail/security/auto-enter-pin-extension")); + top.put(AUTO_ENTER_PIN_EXTERNAL, user.getSettingValue("voicemail/security/auto-enter-pin-external")); + top.put(DAYS_TO_KEEP_VM, user.getSettingValue("voicemail/security/days-to-keep-vm")); String alternateEmailAddress = mp.getAlternateEmailAddress(); boolean enableAltNotification = StringUtils.isNotBlank(alternateEmailAddress) && mp.isEmailNotificationAlternateEnabled(); @@ -138,25 +141,6 @@ public void generate(Replicable entity, DBObject top) { removeField(top, ALT_ATTACH_AUDIO); } - boolean imapServerConfigured = mp.isImapServerConfigured(); - if (imapServerConfigured) { - top.put(SYNC, mp.isSynchronizeWithImapServer()); - top.put(HOST, mp.getImapHost()); - top.put(PORT, mp.getImapPort()); - top.put(TLS, mp.getImapTLS()); - top.put(ACCOUNT, StringUtils.defaultString(mp.getImapAccount())); - String pwd = StringUtils.defaultString(mp.getImapPassword()); - String encodedPwd = new String(Base64.encodeBase64(pwd.getBytes())); - top.put(PASSWD, encodedPwd); - } else { - removeField(top, SYNC); - removeField(top, HOST); - removeField(top, PORT); - removeField(top, TLS); - removeField(top, ACCOUNT); - removeField(top, PASSWD); - } - ImAccount imAccount = new ImAccount(user); top.put(IM_ENABLED, imAccount.isEnabled()); // The following settings used to be in contact-information.xml diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/RegistrationItem.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/RegistrationItem.java index 774f5ea1b2..9d7a1ab384 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/RegistrationItem.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/RegistrationItem.java @@ -9,12 +9,14 @@ */ package org.sipfoundry.sipxconfig.commserver.imdb; +import java.util.Date; + import org.apache.commons.lang.builder.CompareToBuilder; public class RegistrationItem implements Comparable { private String m_uri; private String m_contact; - private long m_expires; + private Date m_expires; private String m_primary; private String m_instrument; private String m_regCallId; @@ -28,11 +30,11 @@ public void setContact(String contact) { m_contact = contact; } - public long getExpires() { + public Date getExpires() { return m_expires; } - public void setExpires(long expires) { + public void setExpires(Date expires) { m_expires = expires; } @@ -82,6 +84,6 @@ public int compareTo(Object other) { } public long timeToExpireAsSeconds(long nowSeconds) { - return getExpires() - nowSeconds; + return getExpires().getTime() / 1000 - nowSeconds; } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/ReplicationManagerImpl.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/ReplicationManagerImpl.java index d3b79decb8..10ea86f02e 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/ReplicationManagerImpl.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/ReplicationManagerImpl.java @@ -407,8 +407,21 @@ private void replicate(DBObject top, DBObject cleanCopy, String name, boolean is } LOG.debug(String.format("Update query: %s: ", updateQ)); LOG.debug(String.format("Remove query: %s: ", removeQ)); - DBObject set = new BasicDBObject("$set", updateQ).append("$unset", removeQ); - getDbCollection().update(toUpdate, set); + BasicDBObject set = new BasicDBObject(); + BasicDBObject emptyObject = new BasicDBObject(); + boolean isUpdated = false; + if (!updateQ.equals(emptyObject)) { + set.append("$set", updateQ); + isUpdated = true; + } + if (!removeQ.equals(emptyObject)) { + set.append("$unset", removeQ); + isUpdated = true; + } + if (isUpdated) { + LOG.debug(String.format("Final query: %s: ", set)); + getDbCollection().update(toUpdate, set); + } } } @@ -436,17 +449,19 @@ public void replicateAllData(final DataSet ds) { long start = System.currentTimeMillis(); Map beanMap = m_beanFactory.getBeansOfType(ReplicableProvider.class); for (ReplicableProvider provider : beanMap.values()) { - for (Replicable entity : provider.getReplicables()) { - if (entity != null) { - if (!entity.getDataSets().contains(ds)) { /* - * Callable used for the - * replication of members in a - * group - */ - - continue; + if (provider instanceof Proxy) { + for (Replicable entity : provider.getReplicables()) { + if (entity != null) { + if (!entity.getDataSets().contains(ds)) { /* + * Callable used for the + * replication of members in a + * group + */ + + continue; + } + replicateEntity(entity); } - replicateEntity(entity); } } } diff --git a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/SpeedDials.java b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/SpeedDials.java index 6ae8185f5f..1b3052cfba 100644 --- a/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/SpeedDials.java +++ b/sipXconfig/neoconf/src/org/sipfoundry/sipxconfig/commserver/imdb/SpeedDials.java @@ -72,7 +72,9 @@ public void generate(Replicable entity, DBObject top) { List buttonsList = new ArrayList(); List