mirror of https://github.com/sipwise/asterisk.git
parent
5f8b89f9b0
commit
c8fce34c2b
@ -0,0 +1,67 @@
|
||||
Original Asterisk version: 1.4.24.1
|
||||
|
||||
asterisk_patches = ["patches/app_queue-state_interface.patch",
|
||||
"patches/chan_sip-ironxfers.patch",
|
||||
"patches/misdn-bug13488.patch",
|
||||
"patches/app_queue-exitwithtimeout-waittime.patch",
|
||||
"patches/app_queue-linear-strategy.patch",
|
||||
"patches/app_queue-xfer-origpos.patch",
|
||||
"patches/asterisk_queue_log_realtime_1.4.19.patch",
|
||||
"patches/asterisk_realtime_store_destroy_1.4.19.patch",
|
||||
"patches/res_config_odbc-queue_log.patch",
|
||||
"patches/rtp_dtmf_0014815.patch",
|
||||
"patches/voicemail_imap_crash_14508.patch",
|
||||
"patches/misdn_release_call.patch",
|
||||
"patches/misdn_showconfig_crash_14976.patch",
|
||||
"patches/sip_410causecode_14993.patch",
|
||||
"patches/crash_removeextensiontab_14689.patch",
|
||||
"patches/app_queue_crash-large-queue-members.patch",
|
||||
"patches/chan_sip_realtime-rtupdate_14885.patch",
|
||||
"patches/res_odbc_maxlimits_14888.patch",
|
||||
"patches/chan_sip_rtp-NAT_14546.patch",
|
||||
"patches/chan_sip_T38-gateways_12437.patch",
|
||||
"patches/app_queue_crash-badconfig_14796.patch",
|
||||
"patches/chan_sip_glarereinvite_12013.patch",
|
||||
"patches/chan_sip_reinvite-before-ACK_13849.patch",
|
||||
"patches/app_dial_crash-retrydial_14852.patch",
|
||||
"patches/chan_sip-thomson.patch",
|
||||
"patches/chan_dahdi_bris.patch",
|
||||
"patches/chan_sip_nat-realtime_15194.patch",
|
||||
"patches/chan_sip_directrtp_14244.patch",
|
||||
"patches/pbx-multiple_hints_state-15057.patch",
|
||||
"patches/chan_sip_loop_12215.patch",
|
||||
"patches/app_meetme_D-option_15050.patch",
|
||||
"patches/makefile_bash_15209.patch",
|
||||
"patches/chan_sip_rport_13823.patch",
|
||||
"patches/res_musiconhold-crash_15109_15123_15195.patch",
|
||||
"patches/crash_smdi-14561.patch",
|
||||
"patches/moh_reload-14759.patch",
|
||||
"patches/stack_size-14932.patch",
|
||||
"patches/chan_sip-15330.patch",
|
||||
"patches/chan_sip-add_info_supported_methods.patch",
|
||||
"patches/return_code_in_ringing-15158.patch",
|
||||
"patches/voicemail_maxsilence-minmessage_15331.patch",
|
||||
"patches/multiple_hints-15413.patch",
|
||||
"patches/AST-2009-008-1.4.patch",
|
||||
"patches/queue_atxfer_S_OR.patch",
|
||||
"patches/queue_atxfer_bug_14260.patch",
|
||||
"patches/AST-2009-009-1.4.patch",
|
||||
"patches/say.c-issue16105.patch",
|
||||
"patches/console_colors.patch",
|
||||
"patches/queue_magic_number.patch",
|
||||
"patches/queue_wrandom.patch",
|
||||
"patches/052-debian-runlevel-Makefile.patch",
|
||||
"patches/053-make-es-sounds.patch",
|
||||
"patches/055-version-RSP.patch",
|
||||
"patches/g722-20090218.patch",
|
||||
"patches/app_queue-sharedlastcall.patch",
|
||||
"patches/app_queue-R-option.patch",
|
||||
"patches/tranfer_moh-16513.patch",
|
||||
#### new files in patch form (diff -uNr)
|
||||
"patches/live_ast.patch",
|
||||
"patches/app_pickup2.c.patch",
|
||||
"patches/app_syslog.c.patch",
|
||||
"patches/cdr_adaptive_odbc.c.patch",
|
||||
"patches/func_devstate.c.patch"]
|
||||
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
--- Makefile (revision 138)
|
||||
+++ Makefile (working copy)
|
||||
@@ -681,7 +681,7 @@
|
||||
if [ -z "$(DESTDIR)" ]; then /sbin/chkconfig --add asterisk; fi; \
|
||||
elif [ -f /etc/debian_version ]; then \
|
||||
$(INSTALL) -m 755 contrib/init.d/rc.debian.asterisk $(DESTDIR)/etc/init.d/asterisk; \
|
||||
- if [ -z "$(DESTDIR)" ]; then /usr/sbin/update-rc.d asterisk start 50 2 3 4 5 . stop 91 2 3 4 5 .; fi; \
|
||||
+ if [ -z "$(DESTDIR)" ]; then /usr/sbin/update-rc.d asterisk start 50 2 3 4 5 . stop 20 0 1 6 .; fi; \
|
||||
elif [ -f /etc/gentoo-release ]; then \
|
||||
$(INSTALL) -m 755 contrib/init.d/rc.gentoo.asterisk $(DESTDIR)/etc/init.d/asterisk; \
|
||||
if [ -z "$(DESTDIR)" ]; then /sbin/rc-update add asterisk default; fi; \
|
||||
@ -0,0 +1,202 @@
|
||||
--- Makefile 2009-12-10 21:58:09.000000000 +0100
|
||||
+++ Makefile 2010-03-04 14:54:21.000000000 +0100
|
||||
@@ -553,13 +544,29 @@
|
||||
endif
|
||||
@echo " + +"
|
||||
@echo " + **Note** This requires that you have +"
|
||||
@echo " + doxygen installed on your local system +"
|
||||
- @echo " +-------------------------------------------+"
|
||||
+ @echo " + +"
|
||||
+ @echo " + ------------------ or ----------------- +"
|
||||
+ @echo " + +"
|
||||
+ @echo " + +"
|
||||
+ @echo " + Puedes instalar las voces en castellano +"
|
||||
+ @echo " + proporcionadas por voipnovatos con: +"
|
||||
+ @echo " + +"
|
||||
+ifeq ($(MAKE), gmake)
|
||||
+ @echo " + $(MAKE) es-sounds +"
|
||||
+else
|
||||
+ @echo " + $(MAKE) es-sounds +"
|
||||
+endif
|
||||
+ @echo " + +"
|
||||
+ @echo " +------------------------------------------ +"
|
||||
@$(MAKE) -s oldmodcheck
|
||||
|
||||
upgrade: bininstall
|
||||
|
||||
+es-sounds:
|
||||
+ sh contrib/scripts/asterisk-sounds-es.sh
|
||||
+
|
||||
adsi:
|
||||
mkdir -p $(DESTDIR)$(ASTETCDIR)
|
||||
for x in configs/*.adsi; do \
|
||||
if [ ! -f $(DESTDIR)$(ASTETCDIR)/$$x ]; then \
|
||||
--- contrib/scripts/asterisk-sounds-es.sh 2010-03-04 17:59:22.000000000 +0100
|
||||
+++ contrib/scripts/asterisk-sounds-es.sh 2010-03-04 14:54:20.000000000 +0100
|
||||
@@ -0,0 +1,166 @@
|
||||
+#!/usr/bin/env bash
|
||||
+
|
||||
+#####################################################################
|
||||
+# Script de instalacion y configuracion de las voces de VoIPNovatos #
|
||||
+# Version 0.3 #
|
||||
+# Por Elio Rojano (http://www.sinologic.net) #
|
||||
+# Modificado por Saúl Ibarra (http://www.saghul.net) #
|
||||
+# Modificado por Jon Bonilla (http://sipdoc.net) #
|
||||
+# Licencia: GPL #
|
||||
+#####################################################################
|
||||
+
|
||||
+
|
||||
+ESPANOL=`echo $LANG |grep "es"`
|
||||
+if [ "$ESPANOL" ]; then
|
||||
+ MSG1="Aceptas la licencia de uso? [S/N]: "
|
||||
+ MSG2="Escribe tu nombre: "
|
||||
+ MSG3="Escribe tu email: "
|
||||
+ MSG4="Escribe la empresa que tendra esta instalación: "
|
||||
+ MSG5="Registrando usuario ..."
|
||||
+ MSG6="Descargando el conjunto de sonidos seleccionado..."
|
||||
+ MSG8="Creando enlaces para compatibilizar Asterisk..."
|
||||
+ MSG12="Instalando ..."
|
||||
+ MSG14="¿Quieres los sonidos en GSM? [S/N]: "
|
||||
+ MSG15="¿Quieres los sonidos en ALAW? [S/N]: "
|
||||
+ MSG16="¿Quieres los sonidos en ULAW? [S/N]: "
|
||||
+ MSG17="¿Quieres los sonidos en G729? [S/N]: "
|
||||
+else
|
||||
+ MSG1="Are you AGREE with the license? [Y/N]: "
|
||||
+ MSG2="Type your name: "
|
||||
+ MSG3="Type your email: "
|
||||
+ MSG4="Type your company: "
|
||||
+ MSG5="Registry user ..."
|
||||
+ MSG6="Downloading sound set CORE ..."
|
||||
+ MSG7="Downloading sound set EXTRA ..."
|
||||
+ MSG8="Creating symbolic links to add some more compatibility ..."
|
||||
+ MSG12="Installing ..."
|
||||
+ MSG14="Do you wish to install GSM format sounds? [Y/N]: "
|
||||
+ MSG15="Do you wish to install ALAW format sounds? [Y/N]: "
|
||||
+ MSG16="Do you wish to install ULAW format sounds? [Y/N]: "
|
||||
+ MSG17="Do you wish to install G729 format sounds? [Y/N]: "
|
||||
+fi
|
||||
+
|
||||
+if [ -d /var/lib/asterisk/sounds ]; then
|
||||
+ pushd /var/lib/asterisk/sounds
|
||||
+ mkdir -p es
|
||||
+ wget -qc http://www.voipnovatos.es/voces/licenciadeuso.txt -O- | iconv -f latin1 -t utf8 | more
|
||||
+ echo ""
|
||||
+ while [ ! "$ACEPTADA" ]; do
|
||||
+ echo -n $MSG1 && read ACEPTADA
|
||||
+ case $ACEPTADA in
|
||||
+ S|s|Y|y) ACEPTADA="Si" ;;
|
||||
+ N|n) ACEPTADA="No" ;;
|
||||
+ *) ACEPTADA="" ;;
|
||||
+ esac
|
||||
+ done
|
||||
+ if [ "$ACEPTADA" == "Si" ]; then
|
||||
+ while [ ! "$NOMBRE" ]; do echo -n $MSG2; read NOMBRE; done
|
||||
+ while [ ! "$EMAIL" ]; do echo -n $MSG3; read EMAIL; done
|
||||
+ while [ ! "$EMPRESA" ]; do echo -n $MSG4; read EMPRESA; done
|
||||
+
|
||||
+
|
||||
+ # Qué formatos quieres?
|
||||
+ echo ""
|
||||
+ while [ ! "$format_gsm" ]; do
|
||||
+ echo -n $MSG14 && read format_gsm
|
||||
+ case $format_gsm in
|
||||
+ S|s|Y|y) format_gsm="Si" ;;
|
||||
+ N|n) format_gsm="No" ;;
|
||||
+ *) format_gsm="" ;;
|
||||
+ esac
|
||||
+ done
|
||||
+
|
||||
+ echo ""
|
||||
+ while [ ! "$format_alaw" ]; do
|
||||
+ echo -n $MSG15 && read format_alaw
|
||||
+ case $format_alaw in
|
||||
+ S|s|Y|y) format_alaw="Si" ;;
|
||||
+ N|n) format_alaw="No" ;;
|
||||
+ *) format_alaw="" ;;
|
||||
+ esac
|
||||
+ done
|
||||
+
|
||||
+ echo ""
|
||||
+ while [ ! "$format_ulaw" ]; do
|
||||
+ echo -n $MSG16 && read format_ulaw
|
||||
+ case $format_ulaw in
|
||||
+ S|s|Y|y) format_ulaw="Si" ;;
|
||||
+ N|n) format_ulaw="No" ;;
|
||||
+ *) format_ulaw="" ;;
|
||||
+ esac
|
||||
+ done
|
||||
+
|
||||
+ echo ""
|
||||
+ while [ ! "$format_g729" ]; do
|
||||
+ echo -n $MSG17 && read format_g729
|
||||
+ case $format_g729 in
|
||||
+ S|s|Y|y) format_g729="Si" ;;
|
||||
+ N|n) format_g729="No" ;;
|
||||
+ *) format_g729="" ;;
|
||||
+ esac
|
||||
+ done
|
||||
+
|
||||
+
|
||||
+ # Para hacer uso de las locuciones de cara al publico, deben enviarse estos datos al creador...
|
||||
+ echo $MSG5
|
||||
+ wget -cqF "http://voipnovatos.es/voces.php?name=$NOMBRE&email=$EMAIL&empresa=$EMPRESA&format=$formato" -O /dev/null
|
||||
+ sleep 1
|
||||
+ echo $MSG12
|
||||
+ sleep 1
|
||||
+ echo $MSG6
|
||||
+ if [ "$format_gsm" == "Si" ]; then
|
||||
+ wget -c http://www.voipnovatos.es/voces/voipnovatos-core-sounds-es-gsm-1.4.tar.gz
|
||||
+ wget -c http://www.voipnovatos.es/voces/voipnovatos-extra-sounds-es-gsm-1.4.tar.gz
|
||||
+ fi
|
||||
+ if [ "$format_alaw" == "Si" ]; then
|
||||
+ wget -c http://www.voipnovatos.es/voces/voipnovatos-core-sounds-es-alaw-1.4.tar.gz
|
||||
+ wget -c http://www.voipnovatos.es/voces/voipnovatos-extra-sounds-es-alaw-1.4.tar.gz
|
||||
+ fi
|
||||
+ if [ "$format_ulaw" == "Si" ]; then
|
||||
+ wget -c http://www.voipnovatos.es/voces/voipnovatos-core-sounds-es-ulaw-1.4.tar.gz
|
||||
+ wget -c http://www.voipnovatos.es/voces/voipnovatos-extra-sounds-es-ulaw-1.4.tar.gz
|
||||
+ fi
|
||||
+ if [ "$format_g729" == "Si" ]; then
|
||||
+ wget -c http://www.voipnovatos.es/voces/voipnovatos-core-sounds-es-g729-1.4.tar.gz
|
||||
+ wget -c http://www.voipnovatos.es/voces/voipnovatos-extra-sounds-es-g729-1.4.tar.gz
|
||||
+ fi
|
||||
+
|
||||
+ for f in *.tar.gz; do tar xvzf $f; done
|
||||
+ rm -f *.tar.gz
|
||||
+
|
||||
+ #Arreglando permisos...
|
||||
+ chmod 644 /var/lib/asterisk/sounds/dictate/es/*
|
||||
+ chmod 644 /var/lib/asterisk/sounds/digits/es/*
|
||||
+ chmod 644 /var/lib/asterisk/sounds/followme/es/*
|
||||
+ chmod 644 /var/lib/asterisk/sounds/letters/es/*
|
||||
+ chmod 644 /var/lib/asterisk/sounds/phonetic/es/*
|
||||
+ chmod 644 /var/lib/asterisk/sounds/silence/es/*
|
||||
+ chmod 755 /var/lib/asterisk/sounds/dictate
|
||||
+ chmod 755 /var/lib/asterisk/sounds/digits
|
||||
+ chmod 755 /var/lib/asterisk/sounds/followme
|
||||
+ chmod 755 /var/lib/asterisk/sounds/letters
|
||||
+ chmod 755 /var/lib/asterisk/sounds/phonetic
|
||||
+ chmod 755 /var/lib/asterisk/sounds/silence
|
||||
+ chmod 755 /var/lib/asterisk/sounds/dictate/es
|
||||
+ chmod 755 /var/lib/asterisk/sounds/digits/es
|
||||
+ chmod 755 /var/lib/asterisk/sounds/followme/es
|
||||
+ chmod 755 /var/lib/asterisk/sounds/letters/es
|
||||
+ chmod 755 /var/lib/asterisk/sounds/phonetic/es
|
||||
+ chmod 755 /var/lib/asterisk/sounds/silence/es
|
||||
+ chmod 755 /var/lib/asterisk/sounds/es
|
||||
+ chmod 644 /var/lib/asterisk/sounds/es/*
|
||||
+
|
||||
+ echo $MSG8
|
||||
+ ln -s /var/lib/asterisk/sounds/dictate/es /var/lib/asterisk/sounds/es/dictate >/dev/null 2>/dev/null
|
||||
+ ln -s /var/lib/asterisk/sounds/digits/es /var/lib/asterisk/sounds/es/digits >/dev/null 2>/dev/null
|
||||
+ ln -s /var/lib/asterisk/sounds/followme/es /var/lib/asterisk/sounds/es/followme >/dev/null 2>/dev/null
|
||||
+ ln -s /var/lib/asterisk/sounds/letters/es /var/lib/asterisk/sounds/es/letters >/dev/null 2>/dev/null
|
||||
+ ln -s /var/lib/asterisk/sounds/phonetic/es /var/lib/asterisk/sounds/es/phonetic >/dev/null 2>/dev/null
|
||||
+ ln -s /var/lib/asterisk/sounds/silence/es /var/lib/asterisk/sounds/es/silence >/dev/null 2>/dev/null
|
||||
+
|
||||
+ fi
|
||||
+fi
|
||||
+
|
||||
+popd >/dev/null
|
||||
+exit 0
|
||||
+
|
||||
@ -0,0 +1,50 @@
|
||||
--- build_tools/make_version_h 2007-02-23 19:59:09.000000000 +0100
|
||||
+++ build_tools/make_version_h 2010-05-06 22:06:58.000000000 +0200
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
-if [ ! -f ../.flavor ]; then
|
||||
+if [ ! -f .flavor ]; then
|
||||
cat << END
|
||||
/*
|
||||
* version.h
|
||||
@@ -10,14 +10,14 @@
|
||||
|
||||
END
|
||||
else
|
||||
- aadkver=`cat ../.version`
|
||||
- aadkflavor=`cat ../.flavor`
|
||||
+ aadkver=`cat .version`
|
||||
+ aadkflavor=`cat .flavor`
|
||||
cat << END
|
||||
/*
|
||||
* version.h
|
||||
* Automatically generated
|
||||
*/
|
||||
-#define ASTERISK_VERSION "${ASTERISKVERSION} (${aadkflavor} ${aadkver})"
|
||||
+#define ASTERISK_VERSION "${ASTERISKVERSION}-${aadkflavor}"
|
||||
#define ASTERISK_VERSION_NUM ${ASTERISKVERSIONNUM}
|
||||
|
||||
END
|
||||
--- .flavor 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ .flavor 2010-05-06 22:05:31.000000000 +0200
|
||||
@@ -0,0 +1 @@
|
||||
+RSP (Community supported branch)
|
||||
--- main/asterisk.c 2009-02-25 13:43:36.000000000 +0100
|
||||
+++ main/asterisk.c 2010-05-06 22:05:31.000000000 +0200
|
||||
@@ -144,7 +144,15 @@
|
||||
ast_verbose("This is free software, with components licensed under the GNU General Public\n"); \
|
||||
ast_verbose("License version 2 and other licenses; you are welcome to redistribute it under\n"); \
|
||||
ast_verbose("certain conditions. Type 'core show license' for details.\n"); \
|
||||
- ast_verbose("=========================================================================\n")
|
||||
+ ast_verbose("=========================================================================\n"); \
|
||||
+ ast_verbose("\n"); \
|
||||
+ ast_verbose("Versión RSP de Asterisk " ASTERISK_VERSION ", mantenida por la comunidad\n"); \
|
||||
+ ast_verbose("Lista de correo de la versión RSP: http://groups.google.com/group/asterisk-es-rsp \n"); \
|
||||
+ ast_verbose("Wiki de la versión RSP: http://www.asterisk-es-rsp.org \n"); \
|
||||
+ ast_verbose("Rep. SVN Asterisk-rsp: http://asterisk-es-rsp.irontec.com/svn/asterisk-es-rsp/branches/ \n"); \
|
||||
+ ast_verbose("Visor web del repositorio SVN: http://asterisk-es-rsp.irontec.com \n"); \
|
||||
+ ast_verbose("\n"); \
|
||||
+ ast_verbose("=========================================================================\n")
|
||||
|
||||
/*! \defgroup main_options Main Configuration Options
|
||||
\brief Main configuration options from \ref Config_ast "asterisk.conf" or
|
||||
@ -0,0 +1,178 @@
|
||||
--- channels/chan_sip.c (revision 183280)
|
||||
+++ channels/chan_sip.c (working copy)
|
||||
@@ -1266,7 +1266,7 @@
|
||||
static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported);
|
||||
static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *rand, enum xmittype reliable, const char *header, int stale);
|
||||
static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
|
||||
-static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable);
|
||||
+static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct sip_request *req, enum xmittype reliable);
|
||||
static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, enum xmittype reliable, int newbranch);
|
||||
static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch);
|
||||
static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init);
|
||||
@@ -8873,10 +8873,96 @@
|
||||
/*! \brief Send a fake 401 Unauthorized response when the administrator
|
||||
wants to hide the names of local users/peers from fishers
|
||||
*/
|
||||
-static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable)
|
||||
+static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct sip_request *req, enum xmittype reliable)
|
||||
{
|
||||
- ast_string_field_build(p, randdata, "%08lx", ast_random()); /* Create nonce for challenge */
|
||||
- transmit_response_with_auth(p, "401 Unauthorized", req, p->randdata, reliable, "WWW-Authenticate", 0);
|
||||
+ /* We have to emulate EXACTLY what we'd get with a good peer
|
||||
+ * and a bad password, or else we leak information. */
|
||||
+ const char *response = "407 Proxy Authentication Required";
|
||||
+ const char *reqheader = "Proxy-Authorization";
|
||||
+ const char *respheader = "Proxy-Authenticate";
|
||||
+ const char *authtoken;
|
||||
+ struct ast_dynamic_str *buf;
|
||||
+ char *c;
|
||||
+
|
||||
+ /* table of recognised keywords, and their value in the digest */
|
||||
+ enum keys { K_NONCE, K_LAST };
|
||||
+ struct x {
|
||||
+ const char *key;
|
||||
+ const char *s;
|
||||
+ } *i, keys[] = {
|
||||
+ [K_NONCE] = { "nonce=", "" },
|
||||
+ [K_LAST] = { NULL, NULL}
|
||||
+ };
|
||||
+
|
||||
+ if (sipmethod == SIP_REGISTER || sipmethod == SIP_SUBSCRIBE) {
|
||||
+ response = "401 Unauthorized";
|
||||
+ reqheader = "Authorization";
|
||||
+ respheader = "WWW-Authenticate";
|
||||
+ }
|
||||
+ authtoken = get_header(req, reqheader);
|
||||
+ if (ast_test_flag(req, SIP_PKT_IGNORE) && !ast_strlen_zero(p->randdata) && ast_strlen_zero(authtoken)) {
|
||||
+ /* This is a retransmitted invite/register/etc, don't reconstruct authentication
|
||||
+ * information */
|
||||
+ transmit_response_with_auth(p, response, req, p->randdata, 0, respheader, 0);
|
||||
+ /* Schedule auto destroy in 32 seconds (according to RFC 3261) */
|
||||
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
|
||||
+ return;
|
||||
+ } else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) {
|
||||
+ /* We have no auth, so issue challenge and request authentication */
|
||||
+ ast_string_field_build(p, randdata, "%08lx", ast_random()); /* Create nonce for challenge */
|
||||
+ transmit_response_with_auth(p, response, req, p->randdata, 0, respheader, 0);
|
||||
+ /* Schedule auto destroy in 32 seconds */
|
||||
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (!(buf = ast_dynamic_str_thread_get(&check_auth_buf, CHECK_AUTH_BUF_INITLEN))) {
|
||||
+ transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /* Make a copy of the response and parse it */
|
||||
+ if (ast_dynamic_str_thread_set(&buf, 0, &check_auth_buf, "%s", authtoken) == AST_DYNSTR_BUILD_FAILED) {
|
||||
+ transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ c = buf->str;
|
||||
+
|
||||
+ while (c && *(c = ast_skip_blanks(c))) { /* lookup for keys */
|
||||
+ for (i = keys; i->key != NULL; i++) {
|
||||
+ const char *separator = ","; /* default */
|
||||
+
|
||||
+ if (strncasecmp(c, i->key, strlen(i->key)) != 0) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ /* Found. Skip keyword, take text in quotes or up to the separator. */
|
||||
+ c += strlen(i->key);
|
||||
+ if (*c == '"') { /* in quotes. Skip first and look for last */
|
||||
+ c++;
|
||||
+ separator = "\"";
|
||||
+ }
|
||||
+ i->s = c;
|
||||
+ strsep(&c, separator);
|
||||
+ break;
|
||||
+ }
|
||||
+ if (i->key == NULL) { /* not found, jump after space or comma */
|
||||
+ strsep(&c, " ,");
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Verify nonce from request matches our nonce. If not, send 401 with new nonce */
|
||||
+ if (strcasecmp(p->randdata, keys[K_NONCE].s)) {
|
||||
+ if (!ast_test_flag(req, SIP_PKT_IGNORE)) {
|
||||
+ ast_string_field_build(p, randdata, "%08lx", ast_random());
|
||||
+ }
|
||||
+ transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, FALSE);
|
||||
+
|
||||
+ /* Schedule auto destroy in 32 seconds */
|
||||
+ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
|
||||
+ } else {
|
||||
+ transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
|
||||
+ }
|
||||
}
|
||||
|
||||
/*! \brief Verify registration of user
|
||||
@@ -9010,6 +9096,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
+ if (!peer && global_alwaysauthreject) {
|
||||
+ /* If we found a peer, we transmit a 100 Trying. Therefore, if we're
|
||||
+ * trying to avoid leaking information, we MUST also transmit the same
|
||||
+ * response when we DON'T find a peer. */
|
||||
+ transmit_response(p, "100 Trying", req);
|
||||
+ /* Insert a fake delay between the 100 and the subsequent failure. */
|
||||
+ sched_yield();
|
||||
+ }
|
||||
if (!res) {
|
||||
ast_device_state_changed("SIP/%s", peer->name);
|
||||
}
|
||||
@@ -9020,7 +9114,7 @@
|
||||
transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
|
||||
break;
|
||||
case AUTH_USERNAME_MISMATCH:
|
||||
- /* Username and digest username does not match.
|
||||
+ /* Username and digest username does not match.
|
||||
Asterisk uses the From: username for authentication. We need the
|
||||
users to use the same authentication user name until we support
|
||||
proper authentication by digest auth name */
|
||||
@@ -9030,7 +9124,7 @@
|
||||
case AUTH_PEER_NOT_DYNAMIC:
|
||||
case AUTH_ACL_FAILED:
|
||||
if (global_alwaysauthreject) {
|
||||
- transmit_fake_auth_response(p, &p->initreq, XMIT_UNRELIABLE);
|
||||
+ transmit_fake_auth_response(p, SIP_REGISTER, &p->initreq, XMIT_UNRELIABLE);
|
||||
} else {
|
||||
/* URI not found */
|
||||
if (res == AUTH_PEER_NOT_DYNAMIC)
|
||||
@@ -14557,7 +14651,7 @@
|
||||
if (res < 0) { /* Something failed in authentication */
|
||||
if (res == AUTH_FAKE_AUTH) {
|
||||
ast_log(LOG_NOTICE, "Sending fake auth rejection for user %s\n", get_header(req, "From"));
|
||||
- transmit_fake_auth_response(p, req, XMIT_RELIABLE);
|
||||
+ transmit_fake_auth_response(p, SIP_INVITE, req, XMIT_RELIABLE);
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "Failed to authenticate user %s\n", get_header(req, "From"));
|
||||
transmit_response_reliable(p, "403 Forbidden", req);
|
||||
@@ -15594,7 +15688,7 @@
|
||||
if (res < 0) {
|
||||
if (res == AUTH_FAKE_AUTH) {
|
||||
ast_log(LOG_NOTICE, "Sending fake auth rejection for user %s\n", get_header(req, "From"));
|
||||
- transmit_fake_auth_response(p, req, XMIT_UNRELIABLE);
|
||||
+ transmit_fake_auth_response(p, SIP_SUBSCRIBE, req, XMIT_UNRELIABLE);
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "Failed to authenticate user %s for SUBSCRIBE\n", get_header(req, "From"));
|
||||
transmit_response_reliable(p, "403 Forbidden", req);
|
||||
--- configs/sip.conf.sample (revision 183280)
|
||||
+++ configs/sip.conf.sample (working copy)
|
||||
@@ -141,9 +141,11 @@
|
||||
;callevents=no ; generate manager events when sip ua
|
||||
; performs events (e.g. hold)
|
||||
;alwaysauthreject = yes ; When an incoming INVITE or REGISTER is to be rejected,
|
||||
- ; for any reason, always reject with '401 Unauthorized'
|
||||
+ ; for any reason, always reject with an identical response
|
||||
+ ; equivalent to valid username and invalid password/hash
|
||||
; instead of letting the requester know whether there was
|
||||
- ; a matching user or peer for their request
|
||||
+ ; a matching user or peer for their request. This reduces
|
||||
+ ; the ability of an attacker to scan for valid SIP usernames.
|
||||
|
||||
;g726nonstandard = yes ; If the peer negotiates G726-32 audio, use AAL2 packing
|
||||
; order instead of RFC3551 packing order (this is required
|
||||
@ -0,0 +1,11 @@
|
||||
--- channels/chan_sip.c (revisión: 131)
|
||||
+++ channels/chan_sip.c (copia de trabajo)
|
||||
@@ -9097,8 +9097,6 @@
|
||||
Asterisk uses the From: username for authentication. We need the
|
||||
users to use the same authentication user name until we support
|
||||
proper authentication by digest auth name */
|
||||
- transmit_response(p, "403 Authentication user name does not match account name", &p->initreq);
|
||||
- break;
|
||||
case AUTH_NOT_FOUND:
|
||||
case AUTH_PEER_NOT_DYNAMIC:
|
||||
case AUTH_ACL_FAILED:
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,169 @@
|
||||
Parches aplicados a Asterisk-es-RSP
|
||||
================================
|
||||
|
||||
app_queue-state_interface.patch /* Añade la opcion state_interface a AddQueueMember() de manera que el estado de un Local/
|
||||
pueda ser el de un SIP/ */
|
||||
|
||||
chan_sip-ironxfers.patch /* al recivir un REFER escribe las variables de canal ORIGINAL_CALLID y ORIGINAL_CALLERID
|
||||
para que se puedan tracear las transferencias */
|
||||
|
||||
misdn-bug13488.patch /* Soluciona el famoso bug 13488 que provocaba que no se pudieran recibir llamadas por mISDN
|
||||
al quedarse saturados los canales */
|
||||
|
||||
AST-2009-003-1.4.diff.txt /* Soluciona el bug de seguridad AST-2009-003 que provocó la salida de
|
||||
Asterisk 1.4.24.1 */
|
||||
|
||||
app_queue-exitwithtimeout-waittime.patch /* Guarda el wait time en el queue_log cuando el evento es
|
||||
EXITWITHTIMEOUT */
|
||||
|
||||
app_queue-linear-strategy.patch /* Backport de la estratégia 'linear' presente sen Asterisk 1.6
|
||||
*/
|
||||
|
||||
app_queue-xfer-origpos.patch /* Guarda en el queue_log la posición original del llamante
|
||||
en la cola cuando el evento es TRANSFER */
|
||||
|
||||
asterisk_queue_log_realtime_1.4.19.patch /* Guarda el queue_log en RealTime */
|
||||
|
||||
|
||||
asterisk_realtime_store_destroy_1.4.19.patch /* Funciones necesarias para guardar el queue_log en RealTime */
|
||||
|
||||
|
||||
res_config_odbc-queue_log.patch /* Backport de las funciones store y destroy en ODBC de Asterisk 1.6 */
|
||||
|
||||
|
||||
rtp_dtmf_0014815.patch /* Fix para DTMF duplicados de 1.4.25RC1 */
|
||||
|
||||
|
||||
voicemail_imap_crash_14508.patch /* Fix para crash usando voicemail con imap storage */
|
||||
|
||||
|
||||
misdn_release_call.patch /* Fixea forma de colgar llamada cuando el destino aún no ha contestado */
|
||||
|
||||
|
||||
misdn_showconfig_crash_14976.patch /* Resuelve posible crash al ejecutar "misdn show config" */
|
||||
|
||||
|
||||
sip_410causecode_14993.patch /* Mapea respuesta SIP 410 a ISDN 22 */
|
||||
|
||||
|
||||
crash_removeextensiontab_14689.patch /* Crash cuando se usaba el tabulador para autocompletar "remove extension " */
|
||||
|
||||
|
||||
app_queue_crash-large-queue-members.patch /* Crash cuando hay muchos miembros en una cola */
|
||||
|
||||
|
||||
chan_sip_realtime-rtupdate_14885.patch /* Fix para que no se intente actualizar lastms en realtime cuando rtupdate=no */
|
||||
|
||||
|
||||
res_odbc_maxlimits_14888.patch /* Setea el límite a 1023 aunque se le indiquen límites mayores */
|
||||
|
||||
|
||||
chan_sip_rtp-NAT_14546.patch /* Arregla problema de NAT y RTP en chan_sip */
|
||||
|
||||
|
||||
chan_sip_T38-gateways_12437.patch /* Parche enorme que arregla algunos errores de llamadas cuando Asterisk está conectado a un Gateway PSTN que tiene activado T38 */
|
||||
|
||||
|
||||
app_queue_crash-badconfig_14796.patch /* Crash si se configuraba un "member" vacío en queues.conf */
|
||||
|
||||
|
||||
chan_sip_glarereinvite_12013.patch /* Se corrige el envío de 491 en la recepción de invites */
|
||||
|
||||
|
||||
chan_sip_reinvite-before-ACK_13849.patch /* Acepta Reinvites antes de recibir ACK en vez de mandar un 491 */
|
||||
|
||||
|
||||
app_dial_crash-retrydial_14852.patch /* Crash en la aplicación retrydial */
|
||||
|
||||
|
||||
chan_sip-thomson.patch /* PickUp para terminales Thomson */
|
||||
|
||||
|
||||
chan_dahdi_bris.patch /* Soporte para dispositivos BRI desde DAHDI. */
|
||||
|
||||
|
||||
chan_sip_nat-realtime_15194.patch /* Fix para que los usuarios realtime sip no se gareticen cuando se hace un "sip reload" */
|
||||
|
||||
|
||||
chan_sip_directrtp_14244.patch /* a veces salta direcrtp incluso cuando está desactivado si se ha contestado el canal llamante. Está en el bug 14244 pero no es el que lo resuelve sino que lo han encontrado mirando ese */
|
||||
|
||||
|
||||
pbx-multiple_hints_state-15057.patch /* Resuelve resultados incongruentes en la combinación de múltiples hints */
|
||||
|
||||
|
||||
chan_sip_loop_12215.patch /* Fix para casos en los que se detectaba loop en reinvites muy rápidos */
|
||||
|
||||
|
||||
app_meetme_D-option_15050.patch /* Fix para que la opción D de Meetme pida PIN realmente */
|
||||
|
||||
|
||||
makefile_bash_15209.patch /* Llamar explícitamente a bash en vez de a sh cuando se van a usar funciones de bash al hacer make*/
|
||||
|
||||
|
||||
chan_sip_rport_13823.patch /* Fix para las respuestas a REGISTER cuando se usa rport */
|
||||
|
||||
|
||||
res_musiconhold-crash_15109_15123_15195.patch /* Posible crash a la hora de usar res_musiconhold en algunas circunstancias */
|
||||
|
||||
|
||||
crash_smdi-14561.patch /* Posible crash en MWI SMDI */
|
||||
|
||||
|
||||
moh_reload-14759.patch /* Evitar que moh se detenga tras "moh reload" */
|
||||
|
||||
|
||||
stack_size-14932.patch /* Fix de stack_size para arquitectura de 64 bits */
|
||||
|
||||
|
||||
chan_sip-15330.patch /* Fic de canales zombie*/
|
||||
|
||||
|
||||
chan_sip-add_info_supported_methods.patch /* Añade INFO a la cabecera "allowed"*/
|
||||
|
||||
|
||||
return_code_in_ringing-15158.patch /* Return code en ringing mejorado */
|
||||
|
||||
|
||||
voicemail_maxsilence-minmessage_15331.patch /* Elimina un warning que salía cuando no debía porque la comparación era errónea*/
|
||||
|
||||
|
||||
multiple_hints-15413.patch /* Fix para estados de hints múltiples combinados */
|
||||
|
||||
|
||||
AST-2009-008-1.4.patch /* patch de seguridad para no publicitar usuarios SIP validos */
|
||||
|
||||
|
||||
queue_atxfer_S_OR.patch /* patch para evitar coreumps al realizar una atxfer cuando se han derreferenciado los punteros chan->appl y chan->data */
|
||||
|
||||
|
||||
queue_atxfer_bug_14260.patch /* backport del bug 14260, atxfer desde una cola */
|
||||
|
||||
|
||||
AST-2009-009-1.4.diff /* patch de seguridad para evitar una vulnerabilidad de cross-site scripting AJAX en el manager HTTP. */
|
||||
|
||||
|
||||
say.c-issue16105.patch /* pronunciar correctamente las 13:xx */
|
||||
|
||||
|
||||
console_colors.patch /* Habilita los colores al conectarnos con asterisk -r aunque asterisk no haya sido arrancado con -c. */
|
||||
|
||||
|
||||
queue_magic_number.patch /* adapta call_queue a la estructura astobj2 y elimina el bug que provoca el mensaje "bad magic number" */
|
||||
|
||||
|
||||
live_ast /* Permite ejecutar Asterisk sin realizar la instalacion, sin "ensuciar" el sistema. */
|
||||
|
||||
|
||||
queue_wrandom.patch /* añade la estrategia de cola wrandom, random con penalty */
|
||||
|
||||
|
||||
052-debian-runlevel-Makefile.patch /* Modifica el Makefile para solucionar el problema de los runlevels en debian al hacer make config de asterisk */
|
||||
|
||||
|
||||
app_queue-sharedlastcall.patch /* Añade el parámetro global shared_lastcall que permite que el tiempo WRAPUPTIME sea por miembro de
|
||||
forma global, evita que si está en varias colas le entren llamadas seguidas sin respetar el tiempo */
|
||||
|
||||
app_queue-R-option.patch /* Añade la opción 'R' a Queue() para que el usuario escuche MOH mientras está esperando en
|
||||
en la cola y RINGING cuando la llamada está sonando en el terminal del agente.*/
|
||||
|
||||
tranfer_moh-16513.patch /* Solventa el issue 16513, con las transferencias atendidas y silencios del MOH */
|
||||
@ -0,0 +1,11 @@
|
||||
--- apps/app_dial.c (revisión: 187134)
|
||||
+++ apps/app_dial.c (revisión: 187135)
|
||||
@@ -1872,7 +1872,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- if ((dialdata = strchr(dialdata, '|'))) {
|
||||
+ if (dialdata && (dialdata = strchr(dialdata, '|'))) {
|
||||
*dialdata++ = '\0';
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "%s requires more arguments\n",rapp);
|
||||
@ -0,0 +1,11 @@
|
||||
--- apps/app_meetme.c (revisión: 195634)
|
||||
+++ apps/app_meetme.c (revisión: 195635)
|
||||
@@ -2710,7 +2710,7 @@
|
||||
|
||||
empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
|
||||
empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
|
||||
- always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
|
||||
+ always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
|
||||
}
|
||||
|
||||
do {
|
||||
@ -0,0 +1,282 @@
|
||||
--- apps/app_pickup2.c 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ apps/app_pickup2.c 2010-04-15 22:40:04.000000000 +0200
|
||||
@@ -0,0 +1,279 @@
|
||||
+/*
|
||||
+ * Asterisk -- A telephony toolkit for Linux.
|
||||
+ *
|
||||
+ * Pickup, channel independent call pickup
|
||||
+ *
|
||||
+ * Copyright (C) 2005-2007, Thorsten Knabe <ast@thorsten-knabe.de>
|
||||
+ *
|
||||
+ * Copyright (C) 2004, Junghanns.NET GmbH
|
||||
+ *
|
||||
+ * Klaus-Peter Junghanns <kpj@junghanns.net>
|
||||
+ *
|
||||
+ * Copyright (C) 2004, Florian Overkamp <florian@obsimref.com>
|
||||
+ *
|
||||
+ * This program is free software, distributed under the terms of
|
||||
+ * the GNU General Public License
|
||||
+ */
|
||||
+
|
||||
+/*** MODULEINFO
|
||||
+ <defaultenabled>yes</defaultenabled>
|
||||
+ ***/
|
||||
+
|
||||
+#include "asterisk.h"
|
||||
+
|
||||
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 2 $")
|
||||
+
|
||||
+#include <stdlib.h>
|
||||
+#include <unistd.h>
|
||||
+#include <string.h>
|
||||
+#include <stdio.h>
|
||||
+#include <signal.h>
|
||||
+#include <pthread.h>
|
||||
+#include "asterisk/lock.h"
|
||||
+#include "asterisk/file.h"
|
||||
+#include "asterisk/logger.h"
|
||||
+#include "asterisk/channel.h"
|
||||
+#include "asterisk/pbx.h"
|
||||
+#include "asterisk/module.h"
|
||||
+#include "asterisk/musiconhold.h"
|
||||
+#include "asterisk/features.h"
|
||||
+#include "asterisk/options.h"
|
||||
+
|
||||
+static char *app = "PickUp2";
|
||||
+static char *synopsis = "PickUp ringing channel.";
|
||||
+static char *descrip =
|
||||
+" PickUp2(Technology/resource[&Technology2/resource2&...][|<option>]):\n"
|
||||
+"Matches the list of prefixes in the parameter list against channels in\n"
|
||||
+"state RINGING. If a match is found the channel is picked up and\n"
|
||||
+"PICKUP_CHANNEL is set to the picked up channel name. If no matching\n"
|
||||
+"channel is found PICKUP_CHANNEL is empty.\n"
|
||||
+"Possible options:\n"
|
||||
+"B: match on channel name of bridged channel.\n";
|
||||
+
|
||||
+static char *app2 = "PickDown2";
|
||||
+static char *synopsis2 = "Hangup ringing channel.";
|
||||
+static char *descrip2 =
|
||||
+" PickDown2(Technology/resource[&Technology2/resource2&...][|<option>]):\n"
|
||||
+"Matches the list of prefixes in the parameter list against channels in\n"
|
||||
+"state RINGING. If a match is found the channel is hung up and\n"
|
||||
+"PICKDOWN_CHANNEL is set to the hung up channel name. If no matching\n"
|
||||
+"channel is found PICKDOWN_CHANNEL is empty.\n"
|
||||
+"Possible options:\n"
|
||||
+"B: match on channel name of bridged channel.\n";
|
||||
+
|
||||
+static char *app3 = "Steal2";
|
||||
+static char *synopsis3 = "Steal a connected channel.";
|
||||
+
|
||||
+static char *descrip3 =
|
||||
+" Steal2(Technology/resource[&Technology2/resource2&...][|<option>]):\n"
|
||||
+"Matches the list of prefixes in the parameter list against channels in\n"
|
||||
+"state UP. If a match is found the channel is stolen and\n"
|
||||
+"STEAL_CHANNEL is set to the stolen channel name. If no matching\n"
|
||||
+"channel is found STEAL_CHANNEL is empty.\n"
|
||||
+"Possible options:\n"
|
||||
+"B: match on channel name of bridged channel.\n";
|
||||
+
|
||||
+/* Find channel matching given pattern and state, skipping our own channel.
|
||||
+ * Returns locked channel, which has to be unlocked using ast_mutex_unlock().
|
||||
+ * Returns NULL when no matching channel is found.
|
||||
+ */
|
||||
+static struct ast_channel *find_matching_channel(struct ast_channel *chan,
|
||||
+ void *pattern, int chanstate)
|
||||
+{
|
||||
+ struct ast_channel *cur;
|
||||
+ char *pat = "";
|
||||
+ char *next_pat = NULL;
|
||||
+ char *option = "";
|
||||
+ struct ast_channel *bridged;
|
||||
+
|
||||
+ /* copy original pattern or use empty pattern if no pattern has been given*/
|
||||
+ if (pattern) {
|
||||
+ pat = alloca(strlen(pattern) + 1);
|
||||
+ strcpy(pat, pattern);
|
||||
+ }
|
||||
+ for (option = pat; *option; option++) {
|
||||
+ if (*option == '|') {
|
||||
+ *option = '\0';
|
||||
+ option++;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ ast_verbose(VERBOSE_PREFIX_4
|
||||
+ "find_matching_channel: pattern='%s' option='%s' state=%d\n",
|
||||
+ (char *)pat, option, chanstate);
|
||||
+
|
||||
+ /* match on bridged channel name */
|
||||
+ if (!strcmp("B", option)) {
|
||||
+ /* Iterate over each part of the pattern */
|
||||
+ while (pat) {
|
||||
+ /* find pattern for next iteration,
|
||||
+ * terminate current pattern */
|
||||
+ for (next_pat = pat;
|
||||
+ *next_pat && *next_pat != '&'; next_pat++);
|
||||
+ if (*next_pat == '&') {
|
||||
+ *next_pat = 0;
|
||||
+ next_pat++;
|
||||
+ } else
|
||||
+ next_pat = NULL;
|
||||
+ /* Iterate over all channels */
|
||||
+ cur = ast_channel_walk_locked(NULL);
|
||||
+ while (cur) {
|
||||
+ bridged = ast_bridged_channel(cur);
|
||||
+ if (bridged) {
|
||||
+ ast_verbose(VERBOSE_PREFIX_4
|
||||
+ "find_matching_channel: trying channel='%s' bridged='%s' "
|
||||
+ "state=%d pattern='%s'\n",
|
||||
+ cur->name, bridged->name, cur->_state, pat);
|
||||
+ if ((cur != chan) && (bridged != chan) &&
|
||||
+ (cur->_state == chanstate) &&
|
||||
+ !strncmp(pat, bridged->name, strlen(pat))) {
|
||||
+ ast_verbose(VERBOSE_PREFIX_4
|
||||
+ "find_matching_channel: "
|
||||
+ "found channel='%s' bridged='%s'\n",
|
||||
+ cur->name, bridged->name);
|
||||
+ return(cur);
|
||||
+ }
|
||||
+ }
|
||||
+ ast_mutex_unlock(&cur->lock);
|
||||
+ cur = ast_channel_walk_locked(cur);
|
||||
+ }
|
||||
+ pat = next_pat;
|
||||
+ }
|
||||
+ } else {
|
||||
+ /* Iterate over each part of the pattern */
|
||||
+ while (pat) {
|
||||
+ /* find pattern for next iteration,
|
||||
+ * terminate current pattern */
|
||||
+ for (next_pat = pat;
|
||||
+ *next_pat && *next_pat != '&'; next_pat++);
|
||||
+ if (*next_pat == '&') {
|
||||
+ *next_pat = 0;
|
||||
+ next_pat++;
|
||||
+ } else
|
||||
+ next_pat = NULL;
|
||||
+ /* Iterate over all channels */
|
||||
+ cur = ast_channel_walk_locked(NULL);
|
||||
+ while (cur) {
|
||||
+ ast_verbose(VERBOSE_PREFIX_4
|
||||
+ "find_matching_channel: trying channel='%s' "
|
||||
+ "state=%d pattern='%s'\n",
|
||||
+ cur->name, cur->_state, pat);
|
||||
+ if ((cur != chan) &&
|
||||
+ (cur->_state == chanstate) &&
|
||||
+ !strncmp(pat, cur->name, strlen(pat))) {
|
||||
+ ast_verbose(VERBOSE_PREFIX_4
|
||||
+ "find_matching_channel: "
|
||||
+ "found channel='%s'\n",
|
||||
+ cur->name);
|
||||
+ return(cur);
|
||||
+ }
|
||||
+ ast_mutex_unlock(&cur->lock);
|
||||
+ cur = ast_channel_walk_locked(cur);
|
||||
+ }
|
||||
+ pat = next_pat;
|
||||
+ }
|
||||
+ }
|
||||
+ return(NULL);
|
||||
+}
|
||||
+
|
||||
+static int pickup_channel(struct ast_channel *chan, void *pattern)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+ struct ast_module_user *u;
|
||||
+ struct ast_channel *cur;
|
||||
+ u = ast_module_user_add(chan);
|
||||
+ cur = find_matching_channel(chan, pattern, AST_STATE_RINGING);
|
||||
+ if (cur) {
|
||||
+ ast_verbose(VERBOSE_PREFIX_3
|
||||
+ "Channel %s picked up ringing channel %s\n",
|
||||
+ chan->name, cur->name);
|
||||
+ pbx_builtin_setvar_helper(chan, "PICKUP_CHANNEL", cur->name);
|
||||
+ if (chan->_state != AST_STATE_UP) {
|
||||
+ ast_answer(chan);
|
||||
+ }
|
||||
+ if (ast_channel_masquerade(cur, chan)) {
|
||||
+ ast_log(LOG_ERROR, "unable to masquerade\n");
|
||||
+ ret = -1;
|
||||
+ }
|
||||
+ ast_mutex_unlock(&cur->lock);
|
||||
+ ast_mutex_unlock(&chan->lock);
|
||||
+ } else {
|
||||
+ pbx_builtin_setvar_helper(chan, "PICKUP_CHANNEL", "");
|
||||
+ }
|
||||
+ ast_module_user_remove(u);
|
||||
+ return(ret);
|
||||
+}
|
||||
+
|
||||
+static int pickdown_channel(struct ast_channel *chan, void *pattern)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+ struct ast_module_user *u;
|
||||
+ struct ast_channel *cur;
|
||||
+ u = ast_module_user_add(chan);
|
||||
+ cur = find_matching_channel(chan, pattern, AST_STATE_RINGING);
|
||||
+ if (cur) {
|
||||
+ ast_verbose(VERBOSE_PREFIX_3
|
||||
+ "Channel %s hung up ringing channel %s\n",
|
||||
+ chan->name, cur->name);
|
||||
+ pbx_builtin_setvar_helper(chan, "PICKDOWN_CHANNEL", cur->name);
|
||||
+ ast_softhangup_nolock(cur, AST_SOFTHANGUP_DEV);
|
||||
+ ast_mutex_unlock(&cur->lock);
|
||||
+ } else {
|
||||
+ pbx_builtin_setvar_helper(chan, "PICKDOWN_CHANNEL", "");
|
||||
+ }
|
||||
+ ast_module_user_remove(u);
|
||||
+ return(ret);
|
||||
+}
|
||||
+
|
||||
+static int steal_channel(struct ast_channel *chan, void *pattern)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+ struct ast_module_user *u;
|
||||
+ struct ast_channel *cur;
|
||||
+ u = ast_module_user_add(chan);
|
||||
+ cur = find_matching_channel(chan, pattern, AST_STATE_UP);
|
||||
+ if (cur) {
|
||||
+ ast_verbose(VERBOSE_PREFIX_3
|
||||
+ "Channel %s stole channel %s\n",
|
||||
+ chan->name, cur->name);
|
||||
+ pbx_builtin_setvar_helper(chan, "STEAL_CHANNEL", cur->name);
|
||||
+ if (chan->_state != AST_STATE_UP) {
|
||||
+ ast_answer(chan);
|
||||
+ }
|
||||
+ if (cur->_bridge) {
|
||||
+ if (!ast_mutex_lock(&cur->_bridge->lock)) {
|
||||
+ ast_moh_stop(cur->_bridge);
|
||||
+ ast_mutex_unlock(&cur->_bridge->lock);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (ast_channel_masquerade(cur, chan)) {
|
||||
+ ast_log(LOG_ERROR, "unable to masquerade\n");
|
||||
+ ret = -1;
|
||||
+ }
|
||||
+ ast_mutex_unlock(&cur->lock);
|
||||
+ ast_mutex_unlock(&chan->lock);
|
||||
+ } else {
|
||||
+ pbx_builtin_setvar_helper(chan, "STEAL_CHANNEL", "");
|
||||
+ }
|
||||
+ ast_module_user_remove(u);
|
||||
+ return(ret);
|
||||
+}
|
||||
+
|
||||
+static int unload_module(void)
|
||||
+{
|
||||
+ ast_module_user_hangup_all();
|
||||
+ ast_unregister_application(app3);
|
||||
+ ast_unregister_application(app2);
|
||||
+ return ast_unregister_application(app);
|
||||
+}
|
||||
+
|
||||
+static int load_module(void)
|
||||
+{
|
||||
+ ast_register_application(app3, steal_channel, synopsis3, descrip3);
|
||||
+ ast_register_application(app2, pickdown_channel, synopsis2, descrip2);
|
||||
+ return ast_register_application(app, pickup_channel, synopsis, descrip);
|
||||
+}
|
||||
+
|
||||
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY,
|
||||
+ "PickUp2, PickDown2, Steal2 Application");
|
||||
@ -0,0 +1,57 @@
|
||||
--- apps/app_queue.c 2010-08-11 10:03:58.000000000 +0200
|
||||
+++ apps/app_queue.c 2010-08-11 10:10:40.000000000 +0200
|
||||
@@ -181,6 +181,7 @@
|
||||
" 'W' -- allow the calling user to write the conversation to disk via Monitor\n"
|
||||
" by pressing the automon sequence defined in the featuremap section in\n"
|
||||
" features.conf\n"
|
||||
+" 'R' -- Ring instead of playing MOH when a member channel is actually ringing.\n"
|
||||
" In addition to transferring the call, a call may be parked and then picked\n"
|
||||
"up by another user, by transferring to the parking lot extension. See features.conf.\n"
|
||||
" The optional URL will be sent to the called party if the channel supports\n"
|
||||
@@ -347,6 +348,7 @@
|
||||
char announce[80]; /*!< Announcement to play for member when call is answered */
|
||||
char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
|
||||
char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
|
||||
+ int ring_when_ringing; /*!< Should we only use ring indication when a channel is ringing? */
|
||||
int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
|
||||
int pos; /*!< Where we are in the queue */
|
||||
int prio; /*!< Our priority */
|
||||
@@ -2243,6 +2245,12 @@
|
||||
{
|
||||
if (option_verbose > 2)
|
||||
ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
|
||||
+ /* Stop ringing, and resume MOH if specified */
|
||||
+ if (qe->ring_when_ringing) {
|
||||
+ ast_indicate(qe->chan, -1);
|
||||
+ ast_moh_start(qe->chan, qe->moh, NULL);
|
||||
+ }
|
||||
+
|
||||
ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
|
||||
if (qe->parent->autopause && pause) {
|
||||
if (!set_member_paused(qe->parent->name, interface, 1)) {
|
||||
@@ -2448,6 +2456,12 @@
|
||||
case AST_CONTROL_RINGING:
|
||||
if (option_verbose > 2)
|
||||
ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
|
||||
+ /* Start ring indication when the channel is ringing, if specified */
|
||||
+ if (qe->ring_when_ringing) {
|
||||
+ ast_moh_stop(qe->chan);
|
||||
+ ast_indicate(qe->chan, AST_CONTROL_RINGING);
|
||||
+ }
|
||||
+
|
||||
break;
|
||||
case AST_CONTROL_OFFHOOK:
|
||||
/* Ignore going off hook */
|
||||
@@ -4154,6 +4168,12 @@
|
||||
if (args.options && (strchr(args.options, 'r')))
|
||||
ringing = 1;
|
||||
|
||||
+ if (ringing != 1 && args.options && (strchr(args.options, 'R'))) {
|
||||
+ qe.ring_when_ringing = 1;
|
||||
+ } else {
|
||||
+ qe.ring_when_ringing = 0;
|
||||
+ }
|
||||
+
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
|
||||
args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
|
||||
@ -0,0 +1,47 @@
|
||||
--- apps/app_queue.c 2010-04-15 22:23:22.000000000 +0200
|
||||
+++ apps/app_queue.c 2010-04-15 22:27:05.000000000 +0200
|
||||
@@ -3992,7 +3992,7 @@
|
||||
record_abandoned(&qe);
|
||||
reason = QUEUE_TIMEOUT;
|
||||
res = 0;
|
||||
- ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
|
||||
+ ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d|%ld", qe.pos,(long) (time(NULL) - qe.start));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4010,7 +4010,7 @@
|
||||
record_abandoned(&qe);
|
||||
reason = QUEUE_TIMEOUT;
|
||||
res = 0;
|
||||
- ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
|
||||
+ ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d|%ld", qe.pos,(long) (time(NULL) - qe.start));
|
||||
break;
|
||||
}
|
||||
/* Make a periodic announcement, if enabled */
|
||||
@@ -4023,7 +4023,7 @@
|
||||
record_abandoned(&qe);
|
||||
reason = QUEUE_TIMEOUT;
|
||||
res = 0;
|
||||
- ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
|
||||
+ ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d|%ld", qe.pos,(long) (time(NULL) - qe.start));
|
||||
break;
|
||||
}
|
||||
/* Try calling all queue members for 'timeout' seconds */
|
||||
@@ -4037,7 +4037,7 @@
|
||||
if (noption && tries >= qe.parent->membercount) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
|
||||
- ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
|
||||
+ ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d|%ld", qe.pos,(long) (time(NULL) - qe.start));
|
||||
record_abandoned(&qe);
|
||||
reason = QUEUE_TIMEOUT;
|
||||
res = 0;
|
||||
@@ -4067,7 +4067,7 @@
|
||||
record_abandoned(&qe);
|
||||
reason = QUEUE_TIMEOUT;
|
||||
res = 0;
|
||||
- ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
|
||||
+ ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d|%ld", qe.pos,(long) (time(NULL) - qe.start));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -0,0 +1,191 @@
|
||||
--- apps/app_queue.c 2009-02-06 12:10:44.000000000 +0100
|
||||
+++ apps/app_queue.c 2009-02-06 12:14:49.000000000 +0100
|
||||
@@ -119,7 +119,8 @@
|
||||
QUEUE_STRATEGY_LEASTRECENT,
|
||||
QUEUE_STRATEGY_FEWESTCALLS,
|
||||
QUEUE_STRATEGY_RANDOM,
|
||||
- QUEUE_STRATEGY_RRMEMORY
|
||||
+ QUEUE_STRATEGY_RRMEMORY,
|
||||
+ QUEUE_STRATEGY_LINEAR
|
||||
};
|
||||
|
||||
static struct strategy {
|
||||
@@ -132,6 +133,7 @@
|
||||
{ QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
|
||||
{ QUEUE_STRATEGY_RANDOM, "random" },
|
||||
{ QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
|
||||
+ { QUEUE_STRATEGY_LINEAR, "linear" }
|
||||
};
|
||||
|
||||
#define DEFAULT_RETRY 5
|
||||
@@ -346,6 +348,8 @@
|
||||
int handled; /*!< Whether our call was handled */
|
||||
int pending; /*!< Non-zero if we are attempting to call a member */
|
||||
int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
|
||||
+ int linpos; /*!< If using linear strategy, position in queue */
|
||||
+ int linwrapped; /*!< Is the linpos wrapped? */
|
||||
time_t start; /*!< When we started holding */
|
||||
time_t expire; /*!< When this entry should expire (time out of queue) */
|
||||
struct ast_channel *chan; /*!< Our channel */
|
||||
@@ -841,8 +845,16 @@
|
||||
q->eventwhencalled = 0;
|
||||
q->weight = 0;
|
||||
q->timeoutrestart = 0;
|
||||
- if (!q->members)
|
||||
- q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
|
||||
+ if(!q->members) {
|
||||
+ if(q->strategy == QUEUE_STRATEGY_LINEAR) {
|
||||
+ /*linear strategy depnds on order, so only use one hash bucket */
|
||||
+ q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
|
||||
+ ast_log(LOG_NOTICE, "Using linear strategy.\n");
|
||||
+ } else {
|
||||
+ q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
|
||||
+ ast_log(LOG_NOTICE, "Strategy is NOT linear.\n");
|
||||
+ }
|
||||
+ }
|
||||
q->membercount = 0;
|
||||
q->found = 1;
|
||||
ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
|
||||
@@ -1239,6 +1251,30 @@
|
||||
ast_mutex_lock(&q->lock);
|
||||
clear_queue(q);
|
||||
q->realtime = 1;
|
||||
+
|
||||
+ /* manwe & saghul FTW!!*/
|
||||
+
|
||||
+ /*Before we initialize the queue, we need to set the strategy, so that linear strategy
|
||||
+ * will allocate the members properly
|
||||
+ */
|
||||
+ struct ast_variable *tmpvar = NULL;
|
||||
+ for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
|
||||
+ if (!strcasecmp(tmpvar->name, "strategy")) {
|
||||
+ q->strategy = strat2int(tmpvar->value);
|
||||
+ if (q->strategy < 0) {
|
||||
+ ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
|
||||
+ tmpvar->value, q->name);
|
||||
+ q->strategy = QUEUE_STRATEGY_RINGALL;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ /* We traversed all variables and didn't find a strategy */
|
||||
+ if (!tmpvar)
|
||||
+ q->strategy = QUEUE_STRATEGY_RINGALL;
|
||||
+
|
||||
+ /* /manwe & saghul FTW!! */
|
||||
+
|
||||
AST_LIST_INSERT_HEAD(&queues, q, list);
|
||||
}
|
||||
init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
|
||||
@@ -1857,6 +1893,8 @@
|
||||
qe->parent->rrpos++;
|
||||
ast_mutex_unlock(&qe->parent->lock);
|
||||
|
||||
+ qe->linpos++;
|
||||
+
|
||||
(*busies)++;
|
||||
return 0;
|
||||
}
|
||||
@@ -1987,7 +2025,7 @@
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static int store_next(struct queue_ent *qe, struct callattempt *outgoing)
|
||||
+static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
|
||||
{
|
||||
struct callattempt *best = find_best(outgoing);
|
||||
|
||||
@@ -2011,6 +2049,30 @@
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
|
||||
+{
|
||||
+ struct callattempt *best = find_best(outgoing);
|
||||
+
|
||||
+ if (best) {
|
||||
+ /* Ring just the best channel */
|
||||
+ if (option_debug)
|
||||
+ ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
|
||||
+ qe->linpos = best->metric % 1000;
|
||||
+ } else {
|
||||
+ /* Just increment rrpos */
|
||||
+ if (qe->linwrapped) {
|
||||
+ /* No more channels, start over */
|
||||
+ qe->linpos = 0;
|
||||
+ } else {
|
||||
+ /* Prioritize next entry */
|
||||
+ qe->linpos++;
|
||||
+ }
|
||||
+ }
|
||||
+ qe->linwrapped = 0;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int say_periodic_announcement(struct queue_ent *qe)
|
||||
{
|
||||
int res = 0;
|
||||
@@ -2553,6 +2615,17 @@
|
||||
}
|
||||
tmp->metric += mem->penalty * 1000000;
|
||||
break;
|
||||
+ case QUEUE_STRATEGY_LINEAR:
|
||||
+ if (pos < qe->linpos) {
|
||||
+ tmp->metric = 1000 + pos;
|
||||
+ } else {
|
||||
+ if (pos > qe->linpos)
|
||||
+ /* Indicate there is another priority */
|
||||
+ qe->linwrapped = 1;
|
||||
+ tmp->metric = pos;
|
||||
+ }
|
||||
+ tmp->metric += mem->penalty * 1000000;
|
||||
+ break;
|
||||
case QUEUE_STRATEGY_RANDOM:
|
||||
tmp->metric = ast_random() % 1000;
|
||||
tmp->metric += mem->penalty * 1000000;
|
||||
@@ -2774,7 +2847,7 @@
|
||||
ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
|
||||
break;
|
||||
case 'n':
|
||||
- if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY)
|
||||
+ if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
|
||||
(*tries)++;
|
||||
else
|
||||
*tries = qe->parent->membercount;
|
||||
@@ -2916,7 +2989,10 @@
|
||||
ast_channel_unlock(qe->chan);
|
||||
ast_mutex_lock(&qe->parent->lock);
|
||||
if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
|
||||
- store_next(qe, outgoing);
|
||||
+ store_next_rr(qe, outgoing);
|
||||
+ }
|
||||
+ if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
|
||||
+ store_next_lin(qe, outgoing);
|
||||
}
|
||||
ast_mutex_unlock(&qe->parent->lock);
|
||||
peer = lpeer ? lpeer->chan : NULL;
|
||||
@@ -4351,6 +4427,23 @@
|
||||
ast_mutex_unlock(&q->lock);
|
||||
continue;
|
||||
}
|
||||
+ /* manwe & saghul FTW!! */
|
||||
+
|
||||
+ /* Due to the fact that the "linear" strategy will have a different allocation
|
||||
+ * scheme for queue members, we must devise the queue's strategy before other initializations
|
||||
+ */
|
||||
+ const char *tmpvar = NULL;
|
||||
+ if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
|
||||
+ q->strategy = strat2int(tmpvar);
|
||||
+ if (q->strategy < 0) {
|
||||
+ ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
|
||||
+ tmpvar, q->name);
|
||||
+ q->strategy = QUEUE_STRATEGY_RINGALL;
|
||||
+ }
|
||||
+ } else
|
||||
+ q->strategy = QUEUE_STRATEGY_RINGALL;
|
||||
+ /* /manwe & saghul FTW!! */
|
||||
+
|
||||
/* Re-initialize the queue, and clear statistics */
|
||||
init_queue(q);
|
||||
clear_queue(q);
|
||||
@ -0,0 +1,109 @@
|
||||
Index: app_queue.c
|
||||
===================================================================
|
||||
--- apps/app_queue.c (revisión: 232)
|
||||
+++ apps/app_queue.c (copia de trabajo)
|
||||
@@ -289,6 +289,8 @@
|
||||
/*! \brief queues.conf [general] option */
|
||||
static int montype_default = 0;
|
||||
|
||||
+/*! \brief queues.conf [general] option */
|
||||
+static int shared_lastcall = 0;
|
||||
|
||||
|
||||
enum queue_result {
|
||||
@@ -334,6 +336,7 @@
|
||||
int metric;
|
||||
int oldstatus;
|
||||
time_t lastcall;
|
||||
+ struct call_queue *lastqueue;
|
||||
struct member *member;
|
||||
};
|
||||
|
||||
@@ -374,6 +377,7 @@
|
||||
int status; /*!< Status of queue member */
|
||||
int paused; /*!< Are we paused (not accepting calls)? */
|
||||
time_t lastcall; /*!< When last successful call was hungup */
|
||||
+ struct call_queue *lastqueue; /*!< Last queue we received a call */
|
||||
unsigned int dead:1; /*!< Used to detect members deleted in realtime */
|
||||
unsigned int delme:1; /*!< Flag to delete entry on reload */
|
||||
};
|
||||
@@ -1942,9 +1946,10 @@
|
||||
const char *macrocontext, *macroexten;
|
||||
|
||||
/* on entry here, we know that tmp->chan == NULL */
|
||||
- if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
|
||||
+ if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
|
||||
+ (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
|
||||
if (option_debug)
|
||||
- ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
|
||||
+ ast_log(LOG_DEBUG, "Wrapuptime not yet expired on queue %s for %s\n", (tmp->lastqueue? tmp->lastqueue->name : qe->parent->name), tmp->interface);
|
||||
if (qe->chan->cdr)
|
||||
ast_cdr_busy(qe->chan->cdr);
|
||||
tmp->stillgoing = 0;
|
||||
@@ -2669,12 +2674,37 @@
|
||||
|
||||
static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
|
||||
{
|
||||
+ struct member *mem;
|
||||
+ struct call_queue *qtmp;
|
||||
+
|
||||
+ if (shared_lastcall) {
|
||||
+ AST_LIST_LOCK(&queues);
|
||||
+ AST_LIST_TRAVERSE(&queues, qtmp, list) {
|
||||
+ ao2_lock(qtmp);
|
||||
+ if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
|
||||
+ time(&mem->lastcall);
|
||||
+ mem->calls++;
|
||||
+ mem->lastqueue = q;
|
||||
+ ao2_ref(mem, -1);
|
||||
+ }
|
||||
+ ao2_unlock(qtmp);
|
||||
+ }
|
||||
+ AST_LIST_UNLOCK(&queues);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
ao2_lock(q);
|
||||
- time(&member->lastcall);
|
||||
- member->calls++;
|
||||
+
|
||||
q->callscompleted++;
|
||||
if (callcompletedinsl)
|
||||
q->callscompletedinsl++;
|
||||
+
|
||||
+ if (!shared_lastcall) {
|
||||
+ time(&member->lastcall);
|
||||
+ member->calls++;
|
||||
+ member->lastqueue = q;
|
||||
+ }
|
||||
+
|
||||
ao2_unlock(q);
|
||||
return 0;
|
||||
}
|
||||
@@ -3057,6 +3087,7 @@
|
||||
tmp->member = cur;
|
||||
tmp->oldstatus = cur->status;
|
||||
tmp->lastcall = cur->lastcall;
|
||||
+ tmp->lastqueue = cur->lastqueue;
|
||||
ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
|
||||
/* Special case: If we ring everyone, go ahead and ring them, otherwise
|
||||
just calculate their metric for the appropriate strategy */
|
||||
@@ -3209,7 +3240,7 @@
|
||||
pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
|
||||
|
||||
/* Begin Monitoring */
|
||||
- if (qe->parent->monfmt && *qe->parent->monfmt) {
|
||||
+ if (qe->parent->monfmt && strlen(qe->parent->monfmt) > 2){
|
||||
if (!qe->parent->montype) {
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
|
||||
@@ -4532,6 +4563,9 @@
|
||||
if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
|
||||
if (!strcasecmp(general_val, "mixmonitor"))
|
||||
montype_default = 1;
|
||||
+ shared_lastcall = 0;
|
||||
+ if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
|
||||
+ shared_lastcall = ast_true(general_val);
|
||||
} else { /* Define queue */
|
||||
/* Look for an existing one */
|
||||
AST_LIST_TRAVERSE(&queues, q, list) {
|
||||
@ -0,0 +1,431 @@
|
||||
--- apps/app_queue.c (revision 184672)
|
||||
+++ apps/app_queue.c (working copy)
|
||||
@@ -192,7 +192,7 @@
|
||||
static char *app_aqm = "AddQueueMember" ;
|
||||
static char *app_aqm_synopsis = "Dynamically adds queue members" ;
|
||||
static char *app_aqm_descrip =
|
||||
-" AddQueueMember(queuename[|interface[|penalty[|options[|membername]]]]):\n"
|
||||
+" AddQueueMember(queuename[|interface[|penalty[|options[|membername[|state_interface]]]]]):\n"
|
||||
"Dynamically adds interface to an existing queue.\n"
|
||||
"If the interface is already in the queue and there exists an n+101 priority\n"
|
||||
"then it will then jump to this priority. Otherwise it will return an error\n"
|
||||
@@ -202,6 +202,9 @@
|
||||
" AQMSTATUS The status of the attempt to add a queue member as a \n"
|
||||
" text string, one of\n"
|
||||
" ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
|
||||
+"If a device is provided in the state_interface parameter, then this will\n"
|
||||
+"be the device which will be used to determine the device state of the\n"
|
||||
+"added queue member.\n"
|
||||
"Example: AddQueueMember(techsupport|SIP/3000)\n"
|
||||
"";
|
||||
|
||||
@@ -354,6 +357,7 @@
|
||||
|
||||
struct member {
|
||||
char interface[80]; /*!< Technology/Location */
|
||||
+ char state_interface[80]; /*!< Technology/Location from which to read device state changes */
|
||||
char membername[80]; /*!< Member name to use in queue logs */
|
||||
int penalty; /*!< Are we a last resort? */
|
||||
int calls; /*!< Number of calls serviced by this member */
|
||||
@@ -598,7 +602,7 @@
|
||||
while ((cur = ao2_iterator_next(&mem_iter))) {
|
||||
char *tmp_interface;
|
||||
char *slash_pos;
|
||||
- tmp_interface = ast_strdupa(cur->interface);
|
||||
+ tmp_interface = ast_strdupa(cur->state_interface);
|
||||
if ((slash_pos = strchr(tmp_interface, '/')))
|
||||
if ((slash_pos = strchr(slash_pos + 1, '/')))
|
||||
*slash_pos = '\0';
|
||||
@@ -751,7 +755,7 @@
|
||||
return 0;
|
||||
}
|
||||
/*! \brief allocate space for new queue member and set fields based on parameters passed */
|
||||
-static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused)
|
||||
+static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
|
||||
{
|
||||
struct member *cur;
|
||||
|
||||
@@ -759,13 +763,18 @@
|
||||
cur->penalty = penalty;
|
||||
cur->paused = paused;
|
||||
ast_copy_string(cur->interface, interface, sizeof(cur->interface));
|
||||
+ if (!ast_strlen_zero(state_interface)) {
|
||||
+ ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
|
||||
+ } else {
|
||||
+ ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
|
||||
+ }
|
||||
if (!ast_strlen_zero(membername))
|
||||
ast_copy_string(cur->membername, membername, sizeof(cur->membername));
|
||||
else
|
||||
ast_copy_string(cur->membername, interface, sizeof(cur->membername));
|
||||
if (!strchr(cur->interface, '/'))
|
||||
ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
|
||||
- cur->status = ast_device_state(interface);
|
||||
+ cur->status = ast_device_state(cur->state_interface);
|
||||
}
|
||||
|
||||
return cur;
|
||||
@@ -899,17 +908,21 @@
|
||||
static int interface_exists_global(const char *interface)
|
||||
{
|
||||
struct call_queue *q;
|
||||
- struct member *mem, tmpmem;
|
||||
+ struct member *mem;
|
||||
+ struct ao2_iterator mem_iter;
|
||||
int ret = 0;
|
||||
|
||||
- ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
|
||||
-
|
||||
AST_LIST_LOCK(&queues);
|
||||
AST_LIST_TRAVERSE(&queues, q, list) {
|
||||
ast_mutex_lock(&q->lock);
|
||||
- if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
|
||||
+ mem_iter = ao2_iterator_init(q->members, 0);
|
||||
+ while ((mem = ao2_iterator_next(&mem_iter))) {
|
||||
+ if (!strcasecmp(mem->state_interface, interface)) {
|
||||
+ ao2_ref(mem, -1);
|
||||
+ ret = 1;
|
||||
+ break;
|
||||
+ }
|
||||
ao2_ref(mem, -1);
|
||||
- ret = 1;
|
||||
}
|
||||
ast_mutex_unlock(&q->lock);
|
||||
if (ret)
|
||||
@@ -1108,7 +1121,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
-static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str)
|
||||
+static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str, const char *state_interface)
|
||||
{
|
||||
struct member *m, tmpmem;
|
||||
int penalty = 0;
|
||||
@@ -1132,10 +1145,10 @@
|
||||
|
||||
/* Create a new one if not found, else update penalty */
|
||||
if (!m) {
|
||||
- if ((m = create_queue_member(interface, membername, penalty, paused))) {
|
||||
+ if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
|
||||
m->dead = 0;
|
||||
m->realtime = 1;
|
||||
- add_to_interfaces(interface);
|
||||
+ add_to_interfaces(m->state_interface);
|
||||
ao2_link(q->members, m);
|
||||
ao2_ref(m, -1);
|
||||
m = NULL;
|
||||
@@ -1145,6 +1158,11 @@
|
||||
m->dead = 0; /* Do not delete this one. */
|
||||
if (paused_str)
|
||||
m->paused = paused;
|
||||
+ if (strcasecmp(state_interface, m->state_interface)) {
|
||||
+ remove_from_interfaces(m->state_interface);
|
||||
+ ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
|
||||
+ add_to_interfaces(m->state_interface);
|
||||
+ }
|
||||
m->penalty = penalty;
|
||||
ao2_ref(m, -1);
|
||||
}
|
||||
@@ -1159,7 +1177,7 @@
|
||||
while ((cur = ao2_iterator_next(&mem_iter))) {
|
||||
if (all || !cur->dynamic) {
|
||||
ao2_unlink(q->members, cur);
|
||||
- remove_from_interfaces(cur->interface);
|
||||
+ remove_from_interfaces(cur->state_interface);
|
||||
q->membercount--;
|
||||
}
|
||||
ao2_ref(cur, -1);
|
||||
@@ -1278,7 +1296,8 @@
|
||||
rt_handle_member_record(q, interface,
|
||||
ast_variable_retrieve(member_config, interface, "membername"),
|
||||
ast_variable_retrieve(member_config, interface, "penalty"),
|
||||
- ast_variable_retrieve(member_config, interface, "paused"));
|
||||
+ ast_variable_retrieve(member_config, interface, "paused"),
|
||||
+ S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
|
||||
}
|
||||
|
||||
/* Delete all realtime members that have been deleted in DB. */
|
||||
@@ -1287,7 +1306,7 @@
|
||||
if (m->dead) {
|
||||
ao2_unlink(q->members, m);
|
||||
ast_mutex_unlock(&q->lock);
|
||||
- remove_from_interfaces(m->interface);
|
||||
+ remove_from_interfaces(m->state_interface);
|
||||
ast_mutex_lock(&q->lock);
|
||||
q->membercount--;
|
||||
}
|
||||
@@ -1348,7 +1367,8 @@
|
||||
rt_handle_member_record(q, interface,
|
||||
S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
|
||||
ast_variable_retrieve(member_config, interface, "penalty"),
|
||||
- ast_variable_retrieve(member_config, interface, "paused"));
|
||||
+ ast_variable_retrieve(member_config, interface, "paused"),
|
||||
+ S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
|
||||
}
|
||||
|
||||
/* Delete all realtime members that have been deleted in DB. */
|
||||
@@ -1357,7 +1377,7 @@
|
||||
if (m->dead) {
|
||||
ao2_unlink(q->members, m);
|
||||
ast_mutex_unlock(&q->lock);
|
||||
- remove_from_interfaces(m->interface);
|
||||
+ remove_from_interfaces(m->state_interface);
|
||||
ast_mutex_lock(&q->lock);
|
||||
q->membercount--;
|
||||
}
|
||||
@@ -1855,7 +1875,7 @@
|
||||
ast_cdr_busy(qe->chan->cdr);
|
||||
tmp->stillgoing = 0;
|
||||
|
||||
- update_status(tmp->member->interface, ast_device_state(tmp->member->interface));
|
||||
+ update_status(tmp->member->interface, ast_device_state(tmp->member->state_interface));
|
||||
|
||||
ast_mutex_lock(&qe->parent->lock);
|
||||
qe->parent->rrpos++;
|
||||
@@ -3258,7 +3278,7 @@
|
||||
|
||||
/* Dump all members in a specific queue to the database
|
||||
*
|
||||
- * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
|
||||
+ * <pm_family>/<queuename> = <interface>;<penalty>;<paused>;<state_interface>[|...]
|
||||
*
|
||||
*/
|
||||
static void dump_queue_members(struct call_queue *pm_queue)
|
||||
@@ -3281,8 +3301,8 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
- res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
|
||||
- value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
|
||||
+ res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
|
||||
+ value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
|
||||
|
||||
ao2_ref(cur_member, -1);
|
||||
|
||||
@@ -3332,6 +3352,7 @@
|
||||
"MemberName: %s\r\n",
|
||||
q->name, mem->interface, mem->membername);
|
||||
ao2_unlink(q->members, mem);
|
||||
+ remove_from_interfaces(mem->state_interface);
|
||||
ao2_ref(mem, -1);
|
||||
|
||||
if (queue_persistent_members)
|
||||
@@ -3345,16 +3366,13 @@
|
||||
break;
|
||||
}
|
||||
|
||||
- if (res == RES_OKAY)
|
||||
- remove_from_interfaces(interface);
|
||||
-
|
||||
AST_LIST_UNLOCK(&queues);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
-static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump)
|
||||
+static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
|
||||
{
|
||||
struct call_queue *q;
|
||||
struct member *new_member, *old_member;
|
||||
@@ -3369,8 +3387,8 @@
|
||||
|
||||
ast_mutex_lock(&q->lock);
|
||||
if ((old_member = interface_exists(q, interface)) == NULL) {
|
||||
- add_to_interfaces(interface);
|
||||
- if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
|
||||
+ if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
|
||||
+ add_to_interfaces(new_member->state_interface);
|
||||
new_member->dynamic = 1;
|
||||
ao2_link(q->members, new_member);
|
||||
q->membercount++;
|
||||
@@ -3462,6 +3480,7 @@
|
||||
char *member;
|
||||
char *interface;
|
||||
char *membername = NULL;
|
||||
+ char *state_interface;
|
||||
char *penalty_tok;
|
||||
int penalty = 0;
|
||||
char *paused_tok;
|
||||
@@ -3510,6 +3529,7 @@
|
||||
penalty_tok = strsep(&member, ";");
|
||||
paused_tok = strsep(&member, ";");
|
||||
membername = strsep(&member, ";");
|
||||
+ state_interface = strsep(&member,";");
|
||||
|
||||
if (!penalty_tok) {
|
||||
ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
|
||||
@@ -3536,7 +3556,7 @@
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
|
||||
|
||||
- if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) {
|
||||
+ if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
|
||||
ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
|
||||
break;
|
||||
}
|
||||
@@ -3735,11 +3755,12 @@
|
||||
AST_APP_ARG(penalty);
|
||||
AST_APP_ARG(options);
|
||||
AST_APP_ARG(membername);
|
||||
+ AST_APP_ARG(state_interface);
|
||||
);
|
||||
int penalty = 0;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
- ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
|
||||
+ ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|interface[|penalty[|options[|membername[|state_interface]]]]])\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -3768,7 +3789,7 @@
|
||||
priority_jump = 1;
|
||||
}
|
||||
|
||||
- switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) {
|
||||
+ switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
|
||||
case RES_OKAY:
|
||||
ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
|
||||
ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
|
||||
@@ -4293,13 +4314,14 @@
|
||||
int new;
|
||||
const char *general_val = NULL;
|
||||
char parse[80];
|
||||
- char *interface;
|
||||
+ char *interface, *state_interface;
|
||||
char *membername = NULL;
|
||||
int penalty;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(interface);
|
||||
AST_APP_ARG(penalty);
|
||||
AST_APP_ARG(membername);
|
||||
+ AST_APP_ARG(state_interface);
|
||||
);
|
||||
|
||||
if (!(cfg = ast_config_load("queues.conf"))) {
|
||||
@@ -4377,8 +4399,7 @@
|
||||
|
||||
interface = args.interface;
|
||||
if (!ast_strlen_zero(args.penalty)) {
|
||||
- tmp = args.penalty;
|
||||
- while (*tmp && *tmp < 33) tmp++;
|
||||
+ tmp = ast_skip_blanks(args.penalty);
|
||||
penalty = atoi(tmp);
|
||||
if (penalty < 0) {
|
||||
penalty = 0;
|
||||
@@ -4387,15 +4408,28 @@
|
||||
penalty = 0;
|
||||
|
||||
if (!ast_strlen_zero(args.membername)) {
|
||||
- membername = args.membername;
|
||||
- while (*membername && *membername < 33) membername++;
|
||||
+ membername = ast_skip_blanks(args.membername);
|
||||
+ }
|
||||
+
|
||||
+ if (!ast_strlen_zero(args.state_interface)) {
|
||||
+ state_interface = ast_skip_blanks(args.state_interface);
|
||||
+ } else {
|
||||
+ state_interface = interface;
|
||||
}
|
||||
|
||||
/* Find the old position in the list */
|
||||
ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
|
||||
cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
|
||||
|
||||
- newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
|
||||
+ /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
|
||||
+ if (cur && strcasecmp(cur->state_interface, state_interface)) {
|
||||
+ remove_from_interfaces(cur->state_interface);
|
||||
+ }
|
||||
+
|
||||
+ newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
|
||||
+ if (!cur || (cur && strcasecmp(cur->state_interface, state_interface))) {
|
||||
+ add_to_interfaces(state_interface);
|
||||
+ }
|
||||
ao2_link(q->members, newm);
|
||||
ao2_ref(newm, -1);
|
||||
newm = NULL;
|
||||
@@ -4403,8 +4437,6 @@
|
||||
if (cur)
|
||||
ao2_ref(cur, -1);
|
||||
else {
|
||||
- /* Add them to the master int list if necessary */
|
||||
- add_to_interfaces(interface);
|
||||
q->membercount++;
|
||||
}
|
||||
} else {
|
||||
@@ -4422,7 +4454,7 @@
|
||||
|
||||
q->membercount--;
|
||||
ao2_unlink(q->members, cur);
|
||||
- remove_from_interfaces(cur->interface);
|
||||
+ remove_from_interfaces(cur->state_interface);
|
||||
ao2_ref(cur, -1);
|
||||
}
|
||||
|
||||
@@ -4751,7 +4783,7 @@
|
||||
|
||||
static int manager_add_queue_member(struct mansession *s, const struct message *m)
|
||||
{
|
||||
- const char *queuename, *interface, *penalty_s, *paused_s, *membername;
|
||||
+ const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
|
||||
int paused, penalty = 0;
|
||||
|
||||
queuename = astman_get_header(m, "Queue");
|
||||
@@ -4759,6 +4791,7 @@
|
||||
penalty_s = astman_get_header(m, "Penalty");
|
||||
paused_s = astman_get_header(m, "Paused");
|
||||
membername = astman_get_header(m, "MemberName");
|
||||
+ state_interface = astman_get_header(m, "StateInterface");
|
||||
|
||||
if (ast_strlen_zero(queuename)) {
|
||||
astman_send_error(s, m, "'Queue' not specified.");
|
||||
@@ -4780,7 +4813,7 @@
|
||||
else
|
||||
paused = abs(ast_true(paused_s));
|
||||
|
||||
- switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) {
|
||||
+ switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
|
||||
case RES_OKAY:
|
||||
ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
|
||||
astman_send_ack(s, m, "Added interface to queue");
|
||||
@@ -4858,7 +4891,7 @@
|
||||
|
||||
static int handle_queue_add_member(int fd, int argc, char *argv[])
|
||||
{
|
||||
- char *queuename, *interface, *membername = NULL;
|
||||
+ char *queuename, *interface, *membername = NULL, *state_interface = NULL;
|
||||
int penalty;
|
||||
|
||||
if ((argc != 6) && (argc != 8) && (argc != 10)) {
|
||||
@@ -4891,7 +4924,11 @@
|
||||
membername = argv[9];
|
||||
}
|
||||
|
||||
- switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) {
|
||||
+ if (argc >= 12) {
|
||||
+ state_interface = argv[11];
|
||||
+ }
|
||||
+
|
||||
+ switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
|
||||
case RES_OKAY:
|
||||
ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
|
||||
ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
|
||||
@@ -4936,6 +4973,8 @@
|
||||
return state == 0 ? ast_strdup("as") : NULL;
|
||||
case 9: /* Don't attempt to complete name of member (infinite possibilities) */
|
||||
return NULL;
|
||||
+ case 10:
|
||||
+ return state == 0 ? ast_strdup("state_interface") : NULL;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@@ -5019,7 +5058,7 @@
|
||||
" Provides summary information on a specified queue.\n";
|
||||
|
||||
static char qam_cmd_usage[] =
|
||||
-"Usage: queue add member <channel> to <queue> [penalty <penalty>]\n";
|
||||
+"Usage: queue add member <channel> to <queue> [penalty <penalty> [as <membername> [state_interface <state_interface>]]]\n";
|
||||
|
||||
static char qrm_cmd_usage[] =
|
||||
"Usage: queue remove member <channel> from <queue>\n";
|
||||
@ -0,0 +1,26 @@
|
||||
--- apps/app_queue.c 2009-02-20 10:40:46.000000000 +0100
|
||||
+++ apps/app_queue.c 2009-02-20 10:44:28.000000000 +0100
|
||||
@@ -2687,9 +2687,9 @@
|
||||
int callcompletedinsl = qtds->callcompletedinsl;
|
||||
struct ast_datastore *datastore;
|
||||
|
||||
- ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
|
||||
+ ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
|
||||
new_chan->exten, new_chan->context, (long) (callstart - qe->start),
|
||||
- (long) (time(NULL) - callstart));
|
||||
+ (long) (time(NULL) - callstart), qe->opos);
|
||||
|
||||
update_queue(qe->parent, member, callcompletedinsl);
|
||||
|
||||
@@ -3240,9 +3240,9 @@
|
||||
if (!attended_transfer_occurred(qe->chan)) {
|
||||
struct ast_datastore *tds;
|
||||
if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
|
||||
- ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
|
||||
+ ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
|
||||
qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
|
||||
- (long) (time(NULL) - callstart));
|
||||
+ (long) (time(NULL) - callstart), qe->opos);
|
||||
} else if (qe->chan->_softhangup) {
|
||||
ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
|
||||
(long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
|
||||
@ -0,0 +1,14 @@
|
||||
--- apps/app_queue.c (revisión: 185598)
|
||||
+++ apps/app_queue.c (revisión: 185599)
|
||||
@@ -4404,6 +4404,11 @@
|
||||
struct member tmpmem;
|
||||
membername = NULL;
|
||||
|
||||
+ if (ast_strlen_zero(var->value)) {
|
||||
+ ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
/* Add a new member */
|
||||
ast_copy_string(parse, var->value, sizeof(parse));
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
--- apps/app_queue.c (revisión: 191040)
|
||||
+++ apps/app_queue.c (revisión: 191041)
|
||||
@@ -600,9 +600,9 @@
|
||||
ast_mutex_lock(&q->lock);
|
||||
mem_iter = ao2_iterator_init(q->members, 0);
|
||||
while ((cur = ao2_iterator_next(&mem_iter))) {
|
||||
- char *tmp_interface;
|
||||
+ char tmp_interface[80];
|
||||
char *slash_pos;
|
||||
- tmp_interface = ast_strdupa(cur->state_interface);
|
||||
+ ast_copy_string(tmp_interface, cur->state_interface, sizeof(tmp_interface));
|
||||
if ((slash_pos = strchr(tmp_interface, '/')))
|
||||
if ((slash_pos = strchr(slash_pos + 1, '/')))
|
||||
*slash_pos = '\0';
|
||||
@@ -658,9 +658,9 @@
|
||||
|
||||
AST_LIST_LOCK(&interfaces);
|
||||
AST_LIST_TRAVERSE(&interfaces, curint, list) {
|
||||
- char *interface;
|
||||
+ char interface[80];
|
||||
char *slash_pos;
|
||||
- interface = ast_strdupa(curint->interface);
|
||||
+ ast_copy_string(interface, curint->interface, sizeof(interface));
|
||||
if ((slash_pos = strchr(interface, '/')))
|
||||
if ((slash_pos = strchr(slash_pos + 1, '/')))
|
||||
*slash_pos = '\0';
|
||||
@ -0,0 +1,218 @@
|
||||
--- apps/app_syslog.c 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ apps/app_syslog.c 2010-04-15 22:53:01.000000000 +0200
|
||||
@@ -0,0 +1,215 @@
|
||||
+/*
|
||||
+ * Asterisk -- An open source telephony toolkit.
|
||||
+ *
|
||||
+ * Copyright (C) 2009, Jon Bonilla
|
||||
+ *
|
||||
+ * Jon Bonilla <manwe@aholab.ehu.es>
|
||||
+ *
|
||||
+ * See http://www.asterisk.org for more information about
|
||||
+ * the Asterisk project. Please do not directly contact
|
||||
+ * any of the maintainers of this project for assistance;
|
||||
+ * the project provides a web site, mailing lists and IRC
|
||||
+ * channels for your use.
|
||||
+ *
|
||||
+ * This program is free software, distributed under the terms of
|
||||
+ * the GNU General Public License Version 2. See the LICENSE file
|
||||
+ * at the top of the source tree.
|
||||
+ */
|
||||
+
|
||||
+/*! \file
|
||||
+ *
|
||||
+ * \brief Syslogging Application
|
||||
+ *
|
||||
+ * \author Jon Bonilla (Manwe) manwe@aholab.ehu.es
|
||||
+ *
|
||||
+ * \ingroup applications
|
||||
+ */
|
||||
+
|
||||
+/*** MODULEINFO
|
||||
+ <defaultenabled>no</defaultenabled>
|
||||
+ ***/
|
||||
+
|
||||
+#include "asterisk.h"
|
||||
+
|
||||
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 2 $")
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <unistd.h>
|
||||
+#include <string.h>
|
||||
+#include <syslog.h>
|
||||
+
|
||||
+#include "asterisk/channel.h"
|
||||
+#include "asterisk/pbx.h"
|
||||
+#include "asterisk/module.h"
|
||||
+#include "asterisk/app.h"
|
||||
+
|
||||
+/*
|
||||
+Had to redefine these constants as appear in syslog.h because asterisk developers redefine them their own way
|
||||
+and they don't work as expected
|
||||
+*/
|
||||
+#undef LOG_EMERG
|
||||
+#undef LOG_ALERT
|
||||
+#undef LOG_CRIT
|
||||
+#undef LOG_ERR
|
||||
+#undef LOG_WARNING
|
||||
+#undef LOG_NOTICE
|
||||
+#undef LOG_INFO
|
||||
+#undef LOG_DEBUG
|
||||
+#define LOG_EMERG 0 /* system is unusable */
|
||||
+#define LOG_ALERT 1 /* action must be taken immediately */
|
||||
+#define LOG_CRIT 2 /* critical conditions */
|
||||
+#define LOG_ERR 3 /* error conditions */
|
||||
+#define LOG_WARNING 4 /* warning conditions */
|
||||
+#define LOG_NOTICE 5 /* normal but significant condition */
|
||||
+#define LOG_INFO 6 /* informational */
|
||||
+#define LOG_DEBUG 7 /* debug-level messages */
|
||||
+
|
||||
+
|
||||
+static char *app_syslog = "Syslog";
|
||||
+static char *syslog_synopsis = "Syslog a given text";
|
||||
+static char *syslog_descrip =
|
||||
+"Syslog(message|[severity|facility|syslogtag|setuniqueid])\n"
|
||||
+" severity must be one of ERROR, WARNING, NOTICE, DEBUG, INFO, CRIT, ALERT, EMERG. Defaults to DEBUG\n"
|
||||
+" facility must be local0...local7. Defaults to USER\n"
|
||||
+" syslogtag if not present defaults to \"asterisk\"\n"
|
||||
+" setuniqueid is a 0-1 boolean that prepends or not channel's uniqueid to logging message. Defaults to 0";
|
||||
+
|
||||
+
|
||||
+struct params {
|
||||
+ char message[240];
|
||||
+ char severity[10];
|
||||
+ char facility[10];
|
||||
+ char syslogtag[50];
|
||||
+ int setuniqueid;
|
||||
+};
|
||||
+
|
||||
+
|
||||
+
|
||||
+static int syslog_exec(struct ast_channel *chan, void *data)
|
||||
+{
|
||||
+ char *parse;
|
||||
+ char message[255];
|
||||
+ int log_severity, log_facility;
|
||||
+ struct ast_module_user *u;
|
||||
+ AST_DECLARE_APP_ARGS(args,
|
||||
+ AST_APP_ARG(message);
|
||||
+ AST_APP_ARG(severity);
|
||||
+ AST_APP_ARG(facility);
|
||||
+ AST_APP_ARG(syslogtag);
|
||||
+ AST_APP_ARG(setuniqueid);
|
||||
+ );
|
||||
+
|
||||
+ u = ast_module_user_add(chan);
|
||||
+ if (ast_strlen_zero(data)) {
|
||||
+ ast_module_user_remove(u);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ parse = ast_strdupa(data);
|
||||
+ AST_STANDARD_APP_ARGS(args, parse);
|
||||
+
|
||||
+ struct params *p;
|
||||
+ p = ast_malloc(sizeof(struct params));
|
||||
+ if (p) {
|
||||
+ ast_copy_string(p->message, args.message, sizeof(p->message));
|
||||
+
|
||||
+ if (!ast_strlen_zero(args.severity)) {
|
||||
+ ast_copy_string(p->severity, args.severity, sizeof(p->severity));
|
||||
+ }
|
||||
+ else {
|
||||
+ ast_copy_string(p->severity, "DEBUG", sizeof(p->severity));
|
||||
+ }
|
||||
+ if (!ast_strlen_zero(args.facility)) {
|
||||
+ ast_copy_string(p->facility, args.facility, sizeof(p->facility));
|
||||
+ }
|
||||
+ if (!ast_strlen_zero(args.syslogtag)) {
|
||||
+ ast_copy_string(p->syslogtag, args.syslogtag, sizeof(p->syslogtag));
|
||||
+ }
|
||||
+ else {
|
||||
+ ast_copy_string(p->syslogtag, "asterisk", sizeof(p->syslogtag));
|
||||
+ }
|
||||
+ if (!ast_strlen_zero(args.setuniqueid)) {
|
||||
+ p->setuniqueid = atoi(args.setuniqueid);
|
||||
+ }
|
||||
+ else {
|
||||
+ p->setuniqueid = 0;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!strcasecmp(p->severity, "DEBUG")) {
|
||||
+ log_severity = LOG_DEBUG;
|
||||
+ } else if (!strcasecmp(p->severity, "WARNING")) {
|
||||
+ log_severity = LOG_WARNING;
|
||||
+ } else if (!strcasecmp(p->severity, "NOTICE")) {
|
||||
+ log_severity = LOG_NOTICE;
|
||||
+ } else if (!strcasecmp(p->severity, "ERROR")) {
|
||||
+ log_severity = LOG_ERR;
|
||||
+ } else if (!strcasecmp(p->severity, "INFO")) {
|
||||
+ log_severity = LOG_INFO;
|
||||
+ } else if (!strcasecmp(p->severity, "CRIT")) {
|
||||
+ log_severity = LOG_CRIT;
|
||||
+ } else if (!strcasecmp(p->severity, "ALERT")) {
|
||||
+ log_severity = LOG_ALERT;
|
||||
+ } else if (!strcasecmp(p->severity, "EMERG")) {
|
||||
+ log_severity = LOG_EMERG;
|
||||
+ } else {
|
||||
+ log_severity = LOG_DEBUG;
|
||||
+ }
|
||||
+
|
||||
+ if (!strcasecmp(p->facility, "LOCAL0")) {
|
||||
+ log_facility = LOG_LOCAL0;
|
||||
+ } else if (!strcasecmp(p->facility, "LOCAL1")) {
|
||||
+ log_facility = LOG_LOCAL1;
|
||||
+ } else if (!strcasecmp(p->facility, "LOCAL2")) {
|
||||
+ log_facility = LOG_LOCAL2;
|
||||
+ } else if (!strcasecmp(p->facility, "LOCAL3")) {
|
||||
+ log_facility = LOG_LOCAL3;
|
||||
+ } else if (!strcasecmp(p->facility, "LOCAL4")) {
|
||||
+ log_facility = LOG_LOCAL4;
|
||||
+ } else if (!strcasecmp(p->facility, "LOCAL5")) {
|
||||
+ log_facility = LOG_LOCAL5;
|
||||
+ } else if (!strcasecmp(p->facility, "LOCAL6")) {
|
||||
+ log_facility = LOG_LOCAL6;
|
||||
+ } else if (!strcasecmp(p->facility, "LOCAL7")) {
|
||||
+ log_facility = LOG_LOCAL7;
|
||||
+ } else {
|
||||
+ log_facility = LOG_USER;
|
||||
+ }
|
||||
+
|
||||
+ if (p->setuniqueid == 1) {
|
||||
+ sprintf(message,"[%s] %s",chan->uniqueid,p->message);
|
||||
+ }
|
||||
+ else {
|
||||
+ ast_copy_string(message, p->message, sizeof(message));
|
||||
+ }
|
||||
+
|
||||
+ openlog(p->syslogtag, LOG_ODELAY,log_facility);
|
||||
+ syslog(log_severity,message);
|
||||
+ closelog();
|
||||
+ ast_free(p);
|
||||
+ ast_module_user_remove(u);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int unload_module(void)
|
||||
+{
|
||||
+ int res = 0;
|
||||
+
|
||||
+ res |= ast_unregister_application(app_syslog);
|
||||
+
|
||||
+ ast_module_user_hangup_all();
|
||||
+
|
||||
+ return res;
|
||||
+}
|
||||
+
|
||||
+static int load_module(void)
|
||||
+{
|
||||
+ int res = 0;
|
||||
+
|
||||
+ res = ast_register_application(app_syslog, syslog_exec, syslog_synopsis, syslog_descrip);
|
||||
+
|
||||
+ return res;
|
||||
+}
|
||||
+
|
||||
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Syslog a message");
|
||||
@ -0,0 +1,44 @@
|
||||
--- main/logger.c (revision 138020)
|
||||
+++ main/logger.c (working copy)
|
||||
@@ -353,16 +353,34 @@
|
||||
void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
- AST_LIST_LOCK(&logchannels);
|
||||
- if (qlog) {
|
||||
+ char qlog_msg[8192];
|
||||
+ char time_str[16];
|
||||
+
|
||||
+ if (ast_check_realtime("queue_log")) {
|
||||
va_start(ap, fmt);
|
||||
- fprintf(qlog, "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
|
||||
- vfprintf(qlog, fmt, ap);
|
||||
- fprintf(qlog, "\n");
|
||||
+ vsnprintf(qlog_msg, sizeof(qlog_msg), fmt, ap);
|
||||
va_end(ap);
|
||||
- fflush(qlog);
|
||||
+
|
||||
+ snprintf(time_str, sizeof(time_str), "%ld", (long)time(NULL));
|
||||
+ ast_store_realtime("queue_log", "time", time_str,
|
||||
+ "callid", callid,
|
||||
+ "queuename", queuename,
|
||||
+ "agent", agent,
|
||||
+ "event", event,
|
||||
+ "data", qlog_msg,
|
||||
+ NULL);
|
||||
+ } else {
|
||||
+ if (qlog) {
|
||||
+ AST_LIST_LOCK(&logchannels);
|
||||
+ va_start(ap, fmt);
|
||||
+ fprintf(qlog, "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
|
||||
+ vfprintf(qlog, fmt, ap);
|
||||
+ fprintf(qlog, "\n");
|
||||
+ va_end(ap);
|
||||
+ fflush(qlog);
|
||||
+ AST_LIST_UNLOCK(&logchannels);
|
||||
+ }
|
||||
}
|
||||
- AST_LIST_UNLOCK(&logchannels);
|
||||
}
|
||||
|
||||
int reload_logger(int rotate)
|
||||
@ -0,0 +1,88 @@
|
||||
--- include/asterisk/config.h (revision 112598)
|
||||
+++ include/asterisk/config.h (working copy)
|
||||
@@ -49,6 +49,8 @@
|
||||
typedef struct ast_variable *realtime_var_get(const char *database, const char *table, va_list ap);
|
||||
typedef struct ast_config *realtime_multi_get(const char *database, const char *table, va_list ap);
|
||||
typedef int realtime_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
|
||||
+typedef int realtime_store(const char *database, const char *table, va_list ap);
|
||||
+typedef int realtime_destroy(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
|
||||
|
||||
struct ast_config_engine {
|
||||
char *name;
|
||||
@@ -56,6 +58,8 @@
|
||||
realtime_var_get *realtime_func;
|
||||
realtime_multi_get *realtime_multi_func;
|
||||
realtime_update *update_func;
|
||||
+ realtime_store *store_func;
|
||||
+ realtime_destroy *destroy_func;
|
||||
struct ast_config_engine *next;
|
||||
};
|
||||
|
||||
@@ -156,6 +160,25 @@
|
||||
*/
|
||||
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...);
|
||||
|
||||
+/*!
|
||||
+ * \brief Create realtime configuration
|
||||
+ * \param family which family/config to be created
|
||||
+ * This function is used to create a parameter in realtime configuration space.
|
||||
+ *
|
||||
+ */
|
||||
+int ast_store_realtime(const char *family, ...);
|
||||
+
|
||||
+/*!
|
||||
+ * \brief Destroy realtime configuration
|
||||
+ * \param family which family/config to be destroyed
|
||||
+ * \param keyfield which field to use as the key
|
||||
+ * \param lookup which value to look for in the key field to match the entry.
|
||||
+ * This function is used to destroy an entry in realtime configuration space.
|
||||
+ * Additional params are used as keys.
|
||||
+ *
|
||||
+ */
|
||||
+int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...);
|
||||
+
|
||||
/*! \brief Check if realtime engine is configured for family
|
||||
* returns 1 if family is configured in realtime and engine exists
|
||||
* \param family which family/config to be checked
|
||||
--- main/config.c (revision 112598)
|
||||
+++ main/config.c (working copy)
|
||||
@@ -1469,6 +1469,39 @@
|
||||
return res;
|
||||
}
|
||||
|
||||
+int ast_store_realtime(const char *family, ...) {
|
||||
+ struct ast_config_engine *eng;
|
||||
+ int res = -1;
|
||||
+ char db[256]="";
|
||||
+ char table[256]="";
|
||||
+ va_list ap;
|
||||
+
|
||||
+ va_start(ap, family);
|
||||
+ eng = find_engine(family, db, sizeof(db), table, sizeof(table));
|
||||
+ if (eng && eng->store_func)
|
||||
+ res = eng->store_func(db, table, ap);
|
||||
+ va_end(ap);
|
||||
+
|
||||
+ return res;
|
||||
+}
|
||||
+
|
||||
+int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...) {
|
||||
+ struct ast_config_engine *eng;
|
||||
+ int res = -1;
|
||||
+ char db[256]="";
|
||||
+ char table[256]="";
|
||||
+ va_list ap;
|
||||
+
|
||||
+ va_start(ap, lookup);
|
||||
+ eng = find_engine(family, db, sizeof(db), table, sizeof(table));
|
||||
+ if (eng && eng->destroy_func)
|
||||
+ res = eng->destroy_func(db, table, keyfield, lookup, ap);
|
||||
+ va_end(ap);
|
||||
+
|
||||
+ return res;
|
||||
+}
|
||||
+
|
||||
+
|
||||
static int config_command(int fd, int argc, char **argv)
|
||||
{
|
||||
struct ast_config_engine *eng;
|
||||
@ -0,0 +1,685 @@
|
||||
--- cdr/cdr_adaptive_odbc.c 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ cdr/cdr_adaptive_odbc.c 2010-04-15 22:41:22.000000000 +0200
|
||||
@@ -0,0 +1,682 @@
|
||||
+/*
|
||||
+ * Asterisk -- An open source telephony toolkit.
|
||||
+ *
|
||||
+ * Copyright (C) 2007, Tilghman Lesher
|
||||
+ *
|
||||
+ * Tilghman Lesher <cdr_adaptive_odbc__v1@the-tilghman.com>
|
||||
+ *
|
||||
+ * See http://www.asterisk.org for more information about
|
||||
+ * the Asterisk project. Please do not directly contact
|
||||
+ * any of the maintainers of this project for assistance;
|
||||
+ * the project provides a web site, mailing lists and IRC
|
||||
+ * channels for your use.
|
||||
+ *
|
||||
+ * This program is free software, distributed under the terms of
|
||||
+ * the GNU General Public License Version 2. See the LICENSE file
|
||||
+ * at the top of the source tree.
|
||||
+ */
|
||||
+
|
||||
+/*! \file
|
||||
+ *
|
||||
+ * \brief Adaptive ODBC CDR backend
|
||||
+ *
|
||||
+ * \author Tilghman Lesher <cdr_adaptive_odbc__v1@the-tilghman.com>
|
||||
+ * \ingroup cdr_drivers
|
||||
+ */
|
||||
+
|
||||
+/*** MODULEINFO
|
||||
+ <depend>unixodbc</depend>
|
||||
+ ***/
|
||||
+
|
||||
+#include "asterisk.h"
|
||||
+
|
||||
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 9 $")
|
||||
+
|
||||
+#include <sys/types.h>
|
||||
+#include <stdio.h>
|
||||
+#include <string.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <unistd.h>
|
||||
+#include <time.h>
|
||||
+
|
||||
+#include <sql.h>
|
||||
+#include <sqlext.h>
|
||||
+#include <sqltypes.h>
|
||||
+
|
||||
+#include "asterisk/config.h"
|
||||
+#include "asterisk/options.h"
|
||||
+#include "asterisk/channel.h"
|
||||
+#include "asterisk/lock.h"
|
||||
+#include "asterisk/linkedlists.h"
|
||||
+#include "asterisk/res_odbc.h"
|
||||
+#include "asterisk/cdr.h"
|
||||
+#include "asterisk/module.h"
|
||||
+#include "asterisk/logger.h"
|
||||
+
|
||||
+#define CONFIG "cdr_adaptive_odbc.conf"
|
||||
+
|
||||
+static char *name = "Adaptive ODBC";
|
||||
+/* Optimization to reduce number of memory allocations */
|
||||
+static int maxsize = 512, maxsize2 = 512;
|
||||
+
|
||||
+struct columns {
|
||||
+ char *name;
|
||||
+ char *cdrname;
|
||||
+ char *filtervalue;
|
||||
+ SQLSMALLINT type;
|
||||
+ SQLINTEGER size;
|
||||
+ SQLSMALLINT decimals;
|
||||
+ SQLSMALLINT radix;
|
||||
+ SQLSMALLINT nullable;
|
||||
+ SQLINTEGER octetlen;
|
||||
+ AST_LIST_ENTRY(columns) list;
|
||||
+};
|
||||
+
|
||||
+struct tables {
|
||||
+ char *connection;
|
||||
+ char *table;
|
||||
+ AST_LIST_HEAD_NOLOCK(odbc_columns, columns) columns;
|
||||
+ AST_RWLIST_ENTRY(tables) list;
|
||||
+};
|
||||
+
|
||||
+static AST_RWLIST_HEAD_STATIC(odbc_tables, tables);
|
||||
+
|
||||
+static int load_config(void)
|
||||
+{
|
||||
+ struct ast_config *cfg;
|
||||
+ struct ast_variable *var;
|
||||
+ const char *tmp, *catg;
|
||||
+ struct tables *tableptr;
|
||||
+ struct columns *entry;
|
||||
+ struct odbc_obj *obj;
|
||||
+ char columnname[80];
|
||||
+ char connection[40];
|
||||
+ char table[40];
|
||||
+ int lenconnection, lentable;
|
||||
+ SQLLEN sqlptr;
|
||||
+ int res = 0;
|
||||
+ SQLHSTMT stmt = NULL;
|
||||
+
|
||||
+ cfg = ast_config_load(CONFIG);
|
||||
+ if (!cfg) {
|
||||
+ ast_log(LOG_WARNING, "Unable to load " CONFIG ". No adaptive ODBC CDRs.\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
|
||||
+ var = ast_variable_browse(cfg, catg);
|
||||
+ if (!var)
|
||||
+ continue;
|
||||
+
|
||||
+ if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) {
|
||||
+ ast_log(LOG_WARNING, "No connection parameter found in '%s'. Skipping.\n", catg);
|
||||
+ continue;
|
||||
+ }
|
||||
+ ast_copy_string(connection, tmp, sizeof(connection));
|
||||
+ lenconnection = strlen(connection);
|
||||
+
|
||||
+ /* When loading, we want to be sure we can connect. */
|
||||
+ obj = ast_odbc_request_obj(connection, 1);
|
||||
+ if (!obj) {
|
||||
+ ast_log(LOG_WARNING, "No such connection '%s' in the '%s' section of " CONFIG ". Check res_odbc.conf.\n", connection, catg);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "table"))) {
|
||||
+ ast_log(LOG_NOTICE, "No table name found. Assuming 'cdr'.\n");
|
||||
+ tmp = "cdr";
|
||||
+ }
|
||||
+ ast_copy_string(table, tmp, sizeof(table));
|
||||
+ lentable = strlen(table);
|
||||
+
|
||||
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
|
||||
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
||||
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection);
|
||||
+ ast_odbc_release_obj(obj);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS);
|
||||
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
||||
+ ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'. Skipping.\n", connection);
|
||||
+ ast_odbc_release_obj(obj);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1);
|
||||
+ if (!tableptr) {
|
||||
+ ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", table, connection);
|
||||
+ ast_odbc_release_obj(obj);
|
||||
+ res = -1;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ tableptr->connection = (char *)tableptr + sizeof(*tableptr);
|
||||
+ tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1;
|
||||
+ ast_copy_string(tableptr->connection, connection, lenconnection + 1);
|
||||
+ ast_copy_string(tableptr->table, table, lentable + 1);
|
||||
+
|
||||
+ if (option_verbose > 2)
|
||||
+ ast_verbose(VERBOSE_PREFIX_3 "Found adaptive CDR table %s@%s.\n", tableptr->table, tableptr->connection);
|
||||
+
|
||||
+ /* Check for filters first */
|
||||
+ for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
|
||||
+ if (strncmp(var->name, "filter", 6) == 0) {
|
||||
+ char *cdrvar = ast_strdupa(var->name + 6);
|
||||
+ cdrvar = ast_strip(cdrvar);
|
||||
+ if (option_verbose > 2)
|
||||
+ ast_verbose(VERBOSE_PREFIX_3 "Found filter %s for cdr variable %s in %s@%s\n", var->value, cdrvar, tableptr->table, tableptr->connection);
|
||||
+
|
||||
+ entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(cdrvar) + 1 + strlen(var->value) + 1);
|
||||
+ if (!entry) {
|
||||
+ ast_log(LOG_ERROR, "Out of memory creating filter entry for CDR variable '%s' in table '%s' on connection '%s'\n", cdrvar, table, connection);
|
||||
+ res = -1;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* NULL column entry means this isn't a column in the database */
|
||||
+ entry->name = NULL;
|
||||
+ entry->cdrname = (char *)entry + sizeof(*entry);
|
||||
+ entry->filtervalue = (char *)entry + sizeof(*entry) + strlen(cdrvar) + 1;
|
||||
+ strcpy(entry->cdrname, cdrvar);
|
||||
+ strcpy(entry->filtervalue, var->value);
|
||||
+
|
||||
+ AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
|
||||
+ char *cdrvar = "";
|
||||
+
|
||||
+ SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
|
||||
+
|
||||
+ /* Is there an alias for this column? */
|
||||
+
|
||||
+ /* NOTE: This seems like a non-optimal parse method, but I'm going
|
||||
+ * for user configuration readability, rather than fast parsing. We
|
||||
+ * really don't parse this file all that often, anyway.
|
||||
+ */
|
||||
+ for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
|
||||
+ if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, columnname) == 0) {
|
||||
+ char *tmp = ast_strdupa(var->name + 5);
|
||||
+ cdrvar = ast_strip(tmp);
|
||||
+ if (option_verbose > 2)
|
||||
+ ast_verbose(VERBOSE_PREFIX_3 "Found alias %s for column %s in %s@%s\n", cdrvar, columnname, tableptr->table, tableptr->connection);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1 + strlen(cdrvar) + 1);
|
||||
+ if (!entry) {
|
||||
+ ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection);
|
||||
+ res = -1;
|
||||
+ break;
|
||||
+ }
|
||||
+ entry->name = (char *)entry + sizeof(*entry);
|
||||
+ strcpy(entry->name, columnname);
|
||||
+
|
||||
+ if (!ast_strlen_zero(cdrvar)) {
|
||||
+ entry->cdrname = entry->name + strlen(columnname) + 1;
|
||||
+ strcpy(entry->cdrname, cdrvar);
|
||||
+ } else /* Point to same place as the column name */
|
||||
+ entry->cdrname = (char *)entry + sizeof(*entry);
|
||||
+
|
||||
+ SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
|
||||
+ SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
|
||||
+ SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
|
||||
+ SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
|
||||
+ SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
|
||||
+ SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
|
||||
+
|
||||
+ /* Specification states that the octenlen should be the maximum number of bytes
|
||||
+ * returned in a char or binary column, but it seems that some drivers just set
|
||||
+ * it to NULL. (Bad Postgres! No biscuit!) */
|
||||
+ if (entry->octetlen == 0)
|
||||
+ entry->octetlen = entry->size;
|
||||
+
|
||||
+ if (option_verbose > 9)
|
||||
+ ast_verbose(VERBOSE_PREFIX_4 "Found %s column with type %hd with len %ld, octetlen %ld, and numlen (%hd,%hd)\n", entry->name, entry->type, (long) entry->size, (long) entry->octetlen, entry->decimals, entry->radix);
|
||||
+ /* Insert column info into column list */
|
||||
+ AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
|
||||
+ res = 0;
|
||||
+ }
|
||||
+
|
||||
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
+ ast_odbc_release_obj(obj);
|
||||
+
|
||||
+ if (AST_LIST_FIRST(&(tableptr->columns)))
|
||||
+ AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
|
||||
+ else
|
||||
+ ast_free(tableptr);
|
||||
+ }
|
||||
+ return res;
|
||||
+}
|
||||
+
|
||||
+static int free_config(void)
|
||||
+{
|
||||
+ struct tables *table;
|
||||
+ struct columns *entry;
|
||||
+ while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
|
||||
+ while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {
|
||||
+ ast_free(entry);
|
||||
+ }
|
||||
+ ast_free(table);
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
|
||||
+{
|
||||
+ int res, i;
|
||||
+ char *sql = data;
|
||||
+ SQLHSTMT stmt;
|
||||
+ SQLINTEGER nativeerror = 0, numfields = 0;
|
||||
+ SQLSMALLINT diagbytes = 0;
|
||||
+ unsigned char state[10], diagnostic[256];
|
||||
+
|
||||
+ res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
|
||||
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
||||
+ ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
|
||||
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
||||
+ ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
|
||||
+ SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
|
||||
+ for (i = 0; i < numfields; i++) {
|
||||
+ SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
|
||||
+ ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
|
||||
+ if (i > 10) {
|
||||
+ ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ return stmt;
|
||||
+}
|
||||
+
|
||||
+#define LENGTHEN_BUF1(size) \
|
||||
+ do { \
|
||||
+ /* Lengthen buffer, if necessary */ \
|
||||
+ if ((newsize = lensql + (size) + 3) > sizesql) { \
|
||||
+ if ((tmp = ast_realloc(sql, (newsize / 512 + 1) * 512))) { \
|
||||
+ sql = tmp; \
|
||||
+ sizesql = (newsize / 512 + 1) * 512; \
|
||||
+ } else { \
|
||||
+ ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
|
||||
+ ast_free(sql); \
|
||||
+ ast_free(sql2); \
|
||||
+ AST_RWLIST_UNLOCK(&odbc_tables); \
|
||||
+ return -1; \
|
||||
+ } \
|
||||
+ } \
|
||||
+ } while (0)
|
||||
+
|
||||
+#define LENGTHEN_BUF2(size) \
|
||||
+ do { \
|
||||
+ if ((newsize = lensql2 + (size) + 3) > sizesql2) { \
|
||||
+ if ((tmp = ast_realloc(sql2, (newsize / 512 + 1) * 512))) { \
|
||||
+ sql2 = tmp; \
|
||||
+ sizesql2 = (newsize / 512 + 1) * 512; \
|
||||
+ } else { \
|
||||
+ ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
|
||||
+ ast_free(sql); \
|
||||
+ ast_free(sql2); \
|
||||
+ AST_RWLIST_UNLOCK(&odbc_tables); \
|
||||
+ return -1; \
|
||||
+ } \
|
||||
+ } \
|
||||
+ } while (0)
|
||||
+
|
||||
+static int odbc_log(struct ast_cdr *cdr)
|
||||
+{
|
||||
+ struct tables *tableptr;
|
||||
+ struct columns *entry;
|
||||
+ struct odbc_obj *obj;
|
||||
+ int lensql, lensql2, sizesql = maxsize, sizesql2 = maxsize2, newsize;
|
||||
+ /* Allocated, so we can realloc() */
|
||||
+ char *sql = ast_calloc(sizeof(char), sizesql), *sql2 = ast_calloc(sizeof(char), sizesql2), *tmp;
|
||||
+ char colbuf[1024], *colptr;
|
||||
+ SQLHSTMT stmt = NULL;
|
||||
+ SQLLEN rows = 0;
|
||||
+
|
||||
+ if (!sql || !sql2) {
|
||||
+ if (sql)
|
||||
+ ast_free(sql);
|
||||
+ if (sql2)
|
||||
+ ast_free(sql2);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ if (AST_RWLIST_RDLOCK(&odbc_tables)) {
|
||||
+ ast_log(LOG_ERROR, "Unable to lock table list. Insert CDR(s) failed.\n");
|
||||
+ ast_free(sql);
|
||||
+ ast_free(sql2);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
|
||||
+ lensql = snprintf(sql, sizesql, "INSERT INTO %s (", tableptr->table);
|
||||
+ lensql2 = snprintf(sql2, sizesql2, " VALUES (");
|
||||
+
|
||||
+ /* No need to check the connection now; we'll handle any failure in prepare_and_execute */
|
||||
+ if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) {
|
||||
+ ast_log(LOG_WARNING, "cdr_adaptive_odbc: Unable to retrieve database handle for '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, sql);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) {
|
||||
+ /* Check if we have a similarly named variable */
|
||||
+ ast_cdr_getvar(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), 0,
|
||||
+ (strcasecmp(entry->cdrname, "start") == 0 ||
|
||||
+ strcasecmp(entry->cdrname, "answer") == 0 ||
|
||||
+ strcasecmp(entry->cdrname, "end") == 0) ? 0 : 1);
|
||||
+
|
||||
+ if (colptr) {
|
||||
+ /* Check first if the column filters this entry. Note that this
|
||||
+ * is very specifically NOT ast_strlen_zero(), because the filter
|
||||
+ * could legitimately specify that the field is blank, which is
|
||||
+ * different from the field being unspecified (NULL). */
|
||||
+ if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) {
|
||||
+ if (option_verbose > 3)
|
||||
+ ast_verbose(VERBOSE_PREFIX_4 "CDR column '%s' with value '%s' does not match filter of"
|
||||
+ " '%s'. Cancelling this CDR.\n",
|
||||
+ entry->cdrname, colptr, entry->filtervalue);
|
||||
+ goto early_release;
|
||||
+ }
|
||||
+
|
||||
+ /* Only a filter? */
|
||||
+ if (ast_strlen_zero(entry->name))
|
||||
+ continue;
|
||||
+
|
||||
+ LENGTHEN_BUF1(strlen(entry->name));
|
||||
+
|
||||
+ switch (entry->type) {
|
||||
+ case SQL_CHAR:
|
||||
+ case SQL_VARCHAR:
|
||||
+ case SQL_LONGVARCHAR:
|
||||
+ case SQL_BINARY:
|
||||
+ case SQL_VARBINARY:
|
||||
+ case SQL_LONGVARBINARY:
|
||||
+ case SQL_GUID:
|
||||
+ /* For these two field names, get the rendered form, instead of the raw
|
||||
+ * form (but only when we're dealing with a character-based field).
|
||||
+ */
|
||||
+ if (strcasecmp(entry->name, "disposition") == 0)
|
||||
+ ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
|
||||
+ else if (strcasecmp(entry->name, "amaflags") == 0)
|
||||
+ ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
|
||||
+
|
||||
+ /* Truncate too-long fields */
|
||||
+ if (entry->type != SQL_GUID) {
|
||||
+ if (strlen(colptr) > entry->octetlen)
|
||||
+ colptr[entry->octetlen] = '\0';
|
||||
+ }
|
||||
+
|
||||
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
|
||||
+ LENGTHEN_BUF2(strlen(colptr));
|
||||
+
|
||||
+ /* Encode value, with escaping */
|
||||
+ strcpy(sql2 + lensql2, "'");
|
||||
+ lensql2++;
|
||||
+ for (tmp = colptr; *tmp; tmp++) {
|
||||
+ if (*tmp == '\'') {
|
||||
+ strcpy(sql2 + lensql2, "''");
|
||||
+ lensql2 += 2;
|
||||
+ } else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) {
|
||||
+ strcpy(sql2 + lensql2, "\\\\");
|
||||
+ lensql2 += 2;
|
||||
+ } else {
|
||||
+ sql2[lensql2++] = *tmp;
|
||||
+ sql2[lensql2] = '\0';
|
||||
+ }
|
||||
+ }
|
||||
+ strcpy(sql2 + lensql2, "',");
|
||||
+ lensql2 += 2;
|
||||
+ break;
|
||||
+ case SQL_TYPE_DATE:
|
||||
+ {
|
||||
+ int year = 0, month = 0, day = 0;
|
||||
+ if (sscanf(colptr, "%d-%d-%d", &year, &month, &day) != 3 || year <= 0 ||
|
||||
+ month <= 0 || month > 12 || day < 0 || day > 31 ||
|
||||
+ ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
|
||||
+ (month == 2 && year % 400 == 0 && day > 29) ||
|
||||
+ (month == 2 && year % 100 == 0 && day > 28) ||
|
||||
+ (month == 2 && year % 4 == 0 && day > 29) ||
|
||||
+ (month == 2 && year % 4 != 0 && day > 28)) {
|
||||
+ ast_log(LOG_WARNING, "CDR variable %s is not a valid date ('%s').\n", entry->name, colptr);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (year > 0 && year < 100)
|
||||
+ year += 2000;
|
||||
+
|
||||
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
|
||||
+ LENGTHEN_BUF2(17);
|
||||
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "{ d '%04d-%02d-%02d' },", year, month, day);
|
||||
+ }
|
||||
+ break;
|
||||
+ case SQL_TYPE_TIME:
|
||||
+ {
|
||||
+ int hour = 0, minute = 0, second = 0;
|
||||
+ int count = sscanf(colptr, "%d:%d:%d", &hour, &minute, &second);
|
||||
+
|
||||
+ if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
|
||||
+ ast_log(LOG_WARNING, "CDR variable %s is not a valid time ('%s').\n", entry->name, colptr);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
|
||||
+ LENGTHEN_BUF2(15);
|
||||
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "{ t '%02d:%02d:%02d' },", hour, minute, second);
|
||||
+ }
|
||||
+ break;
|
||||
+ case SQL_TYPE_TIMESTAMP:
|
||||
+ case SQL_TIMESTAMP:
|
||||
+ {
|
||||
+ int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
|
||||
+ int count = sscanf(colptr, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);
|
||||
+
|
||||
+ if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
|
||||
+ month <= 0 || month > 12 || day < 0 || day > 31 ||
|
||||
+ ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
|
||||
+ (month == 2 && year % 400 == 0 && day > 29) ||
|
||||
+ (month == 2 && year % 100 == 0 && day > 28) ||
|
||||
+ (month == 2 && year % 4 == 0 && day > 29) ||
|
||||
+ (month == 2 && year % 4 != 0 && day > 28) ||
|
||||
+ hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) {
|
||||
+ ast_log(LOG_WARNING, "CDR variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (year > 0 && year < 100)
|
||||
+ year += 2000;
|
||||
+
|
||||
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
|
||||
+ LENGTHEN_BUF2(26);
|
||||
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "{ ts '%04d-%02d-%02d %02d:%02d:%02d' },", year, month, day, hour, minute, second);
|
||||
+ }
|
||||
+ break;
|
||||
+ case SQL_INTEGER:
|
||||
+ {
|
||||
+ int integer = 0;
|
||||
+ if (sscanf(colptr, "%d", &integer) != 1) {
|
||||
+ ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
|
||||
+ LENGTHEN_BUF2(12);
|
||||
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%d,", integer);
|
||||
+ }
|
||||
+ break;
|
||||
+ case SQL_BIGINT:
|
||||
+ {
|
||||
+ long long integer = 0;
|
||||
+ if (sscanf(colptr, "%lld", &integer) != 1) {
|
||||
+ ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
|
||||
+ LENGTHEN_BUF2(24);
|
||||
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%lld,", integer);
|
||||
+ }
|
||||
+ break;
|
||||
+ case SQL_SMALLINT:
|
||||
+ {
|
||||
+ short integer = 0;
|
||||
+ if (sscanf(colptr, "%hd", &integer) != 1) {
|
||||
+ ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
|
||||
+ LENGTHEN_BUF2(6);
|
||||
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%d,", integer);
|
||||
+ }
|
||||
+ break;
|
||||
+ case SQL_TINYINT:
|
||||
+ {
|
||||
+ char integer = 0;
|
||||
+ if (sscanf(colptr, "%hhd", &integer) != 1) {
|
||||
+ ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
|
||||
+ LENGTHEN_BUF2(4);
|
||||
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%d,", integer);
|
||||
+ }
|
||||
+ break;
|
||||
+ case SQL_BIT:
|
||||
+ {
|
||||
+ char integer = 0;
|
||||
+ if (sscanf(colptr, "%hhd", &integer) != 1) {
|
||||
+ ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
|
||||
+ break;
|
||||
+ }
|
||||
+ if (integer != 0)
|
||||
+ integer = 1;
|
||||
+
|
||||
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
|
||||
+ LENGTHEN_BUF2(2);
|
||||
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%d,", integer);
|
||||
+ }
|
||||
+ break;
|
||||
+ case SQL_NUMERIC:
|
||||
+ case SQL_DECIMAL:
|
||||
+ {
|
||||
+ double number = 0.0;
|
||||
+ if (sscanf(colptr, "%lf", &number) != 1) {
|
||||
+ ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
|
||||
+ LENGTHEN_BUF2(entry->decimals);
|
||||
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%*.*lf,", entry->decimals, entry->radix, number);
|
||||
+ }
|
||||
+ break;
|
||||
+ case SQL_FLOAT:
|
||||
+ case SQL_REAL:
|
||||
+ case SQL_DOUBLE:
|
||||
+ {
|
||||
+ double number = 0.0;
|
||||
+ if (sscanf(colptr, "%lf", &number) != 1) {
|
||||
+ ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ lensql += snprintf(sql + lensql, sizesql - lensql, "%s,", entry->name);
|
||||
+ LENGTHEN_BUF2(entry->decimals);
|
||||
+ lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%lf,", number);
|
||||
+ }
|
||||
+ break;
|
||||
+ default:
|
||||
+ ast_log(LOG_WARNING, "Column type %d (field '%s:%s:%s') is unsupported at this time.\n", entry->type, tableptr->connection, tableptr->table, entry->name);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Concatenate the two constructed buffers */
|
||||
+ LENGTHEN_BUF1(lensql2);
|
||||
+ sql[lensql - 1] = ')';
|
||||
+ sql2[lensql2 - 1] = ')';
|
||||
+ strcat(sql + lensql, sql2);
|
||||
+
|
||||
+
|
||||
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, sql);
|
||||
+ if (stmt) {
|
||||
+ SQLRowCount(stmt, &rows);
|
||||
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
+ }
|
||||
+ if (rows == 0) {
|
||||
+ ast_log(LOG_WARNING, "cdr_adaptive_odbc: Insert failed on '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, sql);
|
||||
+ }
|
||||
+early_release:
|
||||
+ ast_odbc_release_obj(obj);
|
||||
+ }
|
||||
+ AST_RWLIST_UNLOCK(&odbc_tables);
|
||||
+
|
||||
+ /* Next time, just allocate buffers that are that big to start with. */
|
||||
+ if (sizesql > maxsize)
|
||||
+ maxsize = sizesql;
|
||||
+ if (sizesql2 > maxsize2)
|
||||
+ maxsize2 = sizesql2;
|
||||
+
|
||||
+ ast_free(sql);
|
||||
+ ast_free(sql2);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int unload_module(void)
|
||||
+{
|
||||
+ ast_cdr_unregister(name);
|
||||
+ usleep(1);
|
||||
+ if (AST_RWLIST_WRLOCK(&odbc_tables)) {
|
||||
+ ast_cdr_register(name, ast_module_info->description, odbc_log);
|
||||
+ ast_log(LOG_ERROR, "Unable to lock column list. Unload failed.\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ free_config();
|
||||
+ AST_RWLIST_UNLOCK(&odbc_tables);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int load_module(void)
|
||||
+{
|
||||
+ if (AST_RWLIST_WRLOCK(&odbc_tables)) {
|
||||
+ ast_log(LOG_ERROR, "Unable to lock column list. Load failed.\n");
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ load_config();
|
||||
+ AST_RWLIST_UNLOCK(&odbc_tables);
|
||||
+ ast_cdr_register(name, ast_module_info->description, odbc_log);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int reload(void)
|
||||
+{
|
||||
+ if (AST_RWLIST_WRLOCK(&odbc_tables)) {
|
||||
+ ast_log(LOG_ERROR, "Unable to lock column list. Reload failed.\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ free_config();
|
||||
+ load_config();
|
||||
+ AST_RWLIST_UNLOCK(&odbc_tables);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Adaptive ODBC CDR backend",
|
||||
+ .load = load_module,
|
||||
+ .unload = unload_module,
|
||||
+ .reload = reload,
|
||||
+);
|
||||
+
|
||||
@ -0,0 +1,379 @@
|
||||
--- channels/chan_dahdi.c (revisión: 37)
|
||||
+++ channels/chan_dahdi.c (copia de trabajo)
|
||||
@@ -175,6 +175,8 @@
|
||||
#define SIG_FXOGS DAHDI_SIG_FXOGS
|
||||
#define SIG_FXOKS DAHDI_SIG_FXOKS
|
||||
#define SIG_PRI DAHDI_SIG_CLEAR
|
||||
+#define SIG_BRI (0x2000000 | DAHDI_SIG_CLEAR)
|
||||
+#define SIG_BRI_PTMP (0X4000000 | DAHDI_SIG_CLEAR)
|
||||
#define SIG_SF DAHDI_SIG_SF
|
||||
#define SIG_SFWINK (0x0100000 | DAHDI_SIG_SF)
|
||||
#define SIG_SF_FEATD (0x0200000 | DAHDI_SIG_SF)
|
||||
@@ -342,8 +344,10 @@
|
||||
#ifdef HAVE_PRI_INBANDDISCONNECT
|
||||
unsigned int inbanddisconnect:1; /*!< Should we support inband audio after receiving DISCONNECT? */
|
||||
#endif
|
||||
+ unsigned int bri_l1_check:1; /*!< Whether we should ignore the l1 up/down events on a BRI port */
|
||||
time_t lastreset; /*!< time when unused channels were last reset */
|
||||
long resetinterval; /*!< Interval (in seconds) for resetting unused channels */
|
||||
+ int sig;
|
||||
struct dahdi_pvt *pvts[MAX_CHANNELS]; /*!< Member channel pvt structs */
|
||||
struct dahdi_pvt *crvs; /*!< Member CRV structs */
|
||||
struct dahdi_pvt *crvend; /*!< Pointer to end of CRV structs */
|
||||
@@ -928,6 +932,7 @@
|
||||
.privateprefix = "",
|
||||
.unknownprefix = "",
|
||||
|
||||
+ .bri_l1_check = -1,
|
||||
.resetinterval = 3600
|
||||
},
|
||||
#endif
|
||||
@@ -1358,7 +1363,8 @@
|
||||
goto out;
|
||||
|
||||
#ifdef HAVE_PRI
|
||||
- if ((pvt->sig == SIG_PRI) && (chan->_state == AST_STATE_DIALING) && !pvt->proceeding) {
|
||||
+ if (((pvt->sig == SIG_PRI) || (pvt->sig == SIG_BRI) || (pvt->sig == SIG_BRI_PTMP))
|
||||
+ && (chan->_state == AST_STATE_DIALING) && !pvt->proceeding) {
|
||||
if (pvt->setup_ack) {
|
||||
if (!pri_grab(pvt, pvt->pri)) {
|
||||
pri_information(pvt->pri->pri, pvt->call, digit);
|
||||
@@ -1420,7 +1426,8 @@
|
||||
|
||||
#ifdef HAVE_PRI
|
||||
/* This means that the digit was already sent via PRI signalling */
|
||||
- if (pvt->sig == SIG_PRI && !pvt->begindigit)
|
||||
+ if (((pvt->sig == SIG_PRI) || (pvt->sig == SIG_BRI) || (pvt->sig == SIG_BRI_PTMP))
|
||||
+ && !pvt->begindigit)
|
||||
goto out;
|
||||
#endif
|
||||
|
||||
@@ -1540,6 +1547,10 @@
|
||||
return "FXO Kewlstart";
|
||||
case SIG_PRI:
|
||||
return "ISDN PRI";
|
||||
+ case SIG_BRI:
|
||||
+ return "ISDN BRI Point to Point";
|
||||
+ case SIG_BRI_PTMP:
|
||||
+ return "ISDN BRI Point to MultiPoint";
|
||||
case SIG_SF:
|
||||
return "SF (Tone) Immediate";
|
||||
case SIG_SFWINK:
|
||||
@@ -1764,7 +1775,7 @@
|
||||
return;
|
||||
}
|
||||
if (p->echocancel) {
|
||||
- if (p->sig == SIG_PRI) {
|
||||
+ if ((p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP) || (p->sig == SIG_PRI)) {
|
||||
x = 1;
|
||||
res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_AUDIOMODE, &x);
|
||||
if (res)
|
||||
@@ -1972,7 +1983,8 @@
|
||||
{
|
||||
int x, y, res;
|
||||
x = muted;
|
||||
- if (p->sig == SIG_PRI) {
|
||||
+ if ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) {
|
||||
+
|
||||
y = 1;
|
||||
res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_AUDIOMODE, &y);
|
||||
if (res)
|
||||
@@ -2380,6 +2392,8 @@
|
||||
ast_setstate(ast, AST_STATE_UP);
|
||||
break;
|
||||
case SIG_PRI:
|
||||
+ case SIG_BRI:
|
||||
+ case SIG_BRI_PTMP:
|
||||
/* We'll get it in a moment -- but use dialdest to store pre-setup_ack digits */
|
||||
p->dialdest[0] = '\0';
|
||||
break;
|
||||
@@ -2818,7 +2832,7 @@
|
||||
|
||||
index = dahdi_get_index(ast, p, 1);
|
||||
|
||||
- if (p->sig == SIG_PRI) {
|
||||
+ if ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) {
|
||||
x = 1;
|
||||
ast_channel_setoption(ast,AST_OPTION_AUDIO_MODE,&x,sizeof(char),0);
|
||||
}
|
||||
@@ -3025,7 +3039,7 @@
|
||||
}
|
||||
}
|
||||
#endif
|
||||
- if (p->sig && (p->sig != SIG_PRI))
|
||||
+ if (p->sig && ((p->sig != SIG_PRI) && (p->sig != SIG_BRI) && (p->sig != SIG_BRI_PTMP)))
|
||||
res = dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_ONHOOK);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name);
|
||||
@@ -3076,7 +3090,7 @@
|
||||
update_conf(p);
|
||||
reset_conf(p);
|
||||
/* Restore data mode */
|
||||
- if (p->sig == SIG_PRI) {
|
||||
+ if ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) {
|
||||
x = 0;
|
||||
ast_channel_setoption(ast,AST_OPTION_AUDIO_MODE,&x,sizeof(char),0);
|
||||
}
|
||||
@@ -3193,6 +3207,8 @@
|
||||
break;
|
||||
#ifdef HAVE_PRI
|
||||
case SIG_PRI:
|
||||
+ case SIG_BRI:
|
||||
+ case SIG_BRI_PTMP:
|
||||
/* Send a pri acknowledge */
|
||||
if (!pri_grab(p, p->pri)) {
|
||||
p->proceeding = 1;
|
||||
@@ -4135,7 +4151,7 @@
|
||||
|
||||
ast_log(LOG_DEBUG, "Detected %sdigit '%c'\n", p->pulsedial ? "pulse ": "", res & 0xff);
|
||||
#ifdef HAVE_PRI
|
||||
- if (!p->proceeding && p->sig == SIG_PRI && p->pri && p->pri->overlapdial) {
|
||||
+ if (!p->proceeding && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) && p->pri && p->pri->overlapdial) {
|
||||
/* absorb event */
|
||||
} else {
|
||||
#endif
|
||||
@@ -4223,22 +4239,24 @@
|
||||
break;
|
||||
case DAHDI_EVENT_ALARM:
|
||||
#ifdef HAVE_PRI
|
||||
- if (!p->pri || !p->pri->pri || (pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0)) {
|
||||
- /* T309 is not enabled : hangup calls when alarm occurs */
|
||||
- if (p->call) {
|
||||
- if (p->pri && p->pri->pri) {
|
||||
- if (!pri_grab(p, p->pri)) {
|
||||
- pri_hangup(p->pri->pri, p->call, -1);
|
||||
- pri_destroycall(p->pri->pri, p->call);
|
||||
- p->call = NULL;
|
||||
- pri_rel(p->pri);
|
||||
+ if ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) {
|
||||
+ if (!p->pri || !p->pri->pri || (pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0)) {
|
||||
+ /* T309 is not enabled : hangup calls when alarm occurs */
|
||||
+ if (p->call) {
|
||||
+ if (p->pri && p->pri->pri) {
|
||||
+ if (!pri_grab(p, p->pri)) {
|
||||
+ pri_hangup(p->pri->pri, p->call, -1);
|
||||
+ pri_destroycall(p->pri->pri, p->call);
|
||||
+ p->call = NULL;
|
||||
+ pri_rel(p->pri);
|
||||
+ } else
|
||||
+ ast_log(LOG_WARNING, "Failed to grab PRI!\n");
|
||||
} else
|
||||
- ast_log(LOG_WARNING, "Failed to grab PRI!\n");
|
||||
- } else
|
||||
- ast_log(LOG_WARNING, "The PRI Call has not been destroyed\n");
|
||||
+ ast_log(LOG_WARNING, "The PRI Call has not been destroyed\n");
|
||||
+ }
|
||||
+ if (p->owner)
|
||||
+ p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
|
||||
}
|
||||
- if (p->owner)
|
||||
- p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
|
||||
}
|
||||
if (p->bearer)
|
||||
p->bearer->inalarm = 1;
|
||||
@@ -5320,7 +5338,7 @@
|
||||
}
|
||||
} else if (f->frametype == AST_FRAME_DTMF) {
|
||||
#ifdef HAVE_PRI
|
||||
- if (!p->proceeding && p->sig==SIG_PRI && p->pri && p->pri->overlapdial) {
|
||||
+ if (!p->proceeding && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) && p->pri && p->pri->overlapdial) {
|
||||
/* Don't accept in-band DTMF when in overlap dial mode */
|
||||
f->frametype = AST_FRAME_NULL;
|
||||
f->subclass = 0;
|
||||
@@ -5464,11 +5482,11 @@
|
||||
switch (condition) {
|
||||
case AST_CONTROL_BUSY:
|
||||
#ifdef HAVE_PRI
|
||||
- if (p->priindication_oob && p->sig == SIG_PRI) {
|
||||
+ if (p->priindication_oob && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP))) {
|
||||
chan->hangupcause = AST_CAUSE_USER_BUSY;
|
||||
chan->_softhangup |= AST_SOFTHANGUP_DEV;
|
||||
res = 0;
|
||||
- } else if (!p->progress && p->sig==SIG_PRI && p->pri && !p->outgoing) {
|
||||
+ } else if (!p->progress && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) && p->pri && !p->outgoing) {
|
||||
if (p->pri->pri) {
|
||||
if (!pri_grab(p, p->pri)) {
|
||||
pri_progress(p->pri->pri,p->call, PVT_TO_CHANNEL(p), 1);
|
||||
@@ -5485,7 +5503,7 @@
|
||||
break;
|
||||
case AST_CONTROL_RINGING:
|
||||
#ifdef HAVE_PRI
|
||||
- if ((!p->alerting) && p->sig==SIG_PRI && p->pri && !p->outgoing && (chan->_state != AST_STATE_UP)) {
|
||||
+ if ((!p->alerting) && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) && p->pri && !p->outgoing && (chan->_state != AST_STATE_UP)) {
|
||||
if (p->pri->pri) {
|
||||
if (!pri_grab(p, p->pri)) {
|
||||
pri_acknowledge(p->pri->pri,p->call, PVT_TO_CHANNEL(p), !p->digital);
|
||||
@@ -5509,7 +5527,7 @@
|
||||
case AST_CONTROL_PROCEEDING:
|
||||
ast_log(LOG_DEBUG,"Received AST_CONTROL_PROCEEDING on %s\n",chan->name);
|
||||
#ifdef HAVE_PRI
|
||||
- if (!p->proceeding && p->sig==SIG_PRI && p->pri && !p->outgoing) {
|
||||
+ if (!p->proceeding && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) && p->pri && !p->outgoing) {
|
||||
if (p->pri->pri) {
|
||||
if (!pri_grab(p, p->pri)) {
|
||||
pri_proceeding(p->pri->pri,p->call, PVT_TO_CHANNEL(p), !p->digital);
|
||||
@@ -5528,7 +5546,7 @@
|
||||
ast_log(LOG_DEBUG,"Received AST_CONTROL_PROGRESS on %s\n",chan->name);
|
||||
#ifdef HAVE_PRI
|
||||
p->digital = 0; /* Digital-only calls isn't allows any inband progress messages */
|
||||
- if (!p->progress && p->sig==SIG_PRI && p->pri && !p->outgoing) {
|
||||
+ if (!p->progress && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) && p->pri && !p->outgoing) {
|
||||
if (p->pri->pri) {
|
||||
if (!pri_grab(p, p->pri)) {
|
||||
pri_progress(p->pri->pri,p->call, PVT_TO_CHANNEL(p), 1);
|
||||
@@ -5546,11 +5564,11 @@
|
||||
case AST_CONTROL_CONGESTION:
|
||||
chan->hangupcause = AST_CAUSE_CONGESTION;
|
||||
#ifdef HAVE_PRI
|
||||
- if (p->priindication_oob && p->sig == SIG_PRI) {
|
||||
+ if (p->priindication_oob && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP))) {
|
||||
chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
|
||||
chan->_softhangup |= AST_SOFTHANGUP_DEV;
|
||||
res = 0;
|
||||
- } else if (!p->progress && p->sig==SIG_PRI && p->pri && !p->outgoing) {
|
||||
+ } else if (!p->progress && ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) && p->pri && !p->outgoing) {
|
||||
if (p->pri) {
|
||||
if (!pri_grab(p, p->pri)) {
|
||||
pri_progress(p->pri->pri,p->call, PVT_TO_CHANNEL(p), 1);
|
||||
@@ -5726,7 +5744,7 @@
|
||||
i->dsp_features = features;
|
||||
#ifdef HAVE_PRI
|
||||
/* We cannot do progress detection until receives PROGRESS message */
|
||||
- if (i->outgoing && (i->sig == SIG_PRI)) {
|
||||
+ if (i->outgoing && ((i->sig == SIG_PRI) || (i->sig == SIG_BRI) || (i->sig == SIG_BRI_PTMP))) {
|
||||
/* Remember requested DSP features, don't treat
|
||||
talking as ANSWER */
|
||||
i->dsp_features = features & ~DSP_PROGRESS_TALK;
|
||||
@@ -5904,6 +5922,8 @@
|
||||
switch (p->sig) {
|
||||
#ifdef HAVE_PRI
|
||||
case SIG_PRI:
|
||||
+ case SIG_BRI:
|
||||
+ case SIG_BRI_PTMP:
|
||||
/* Now loop looking for an extension */
|
||||
ast_copy_string(exten, p->exten, sizeof(exten));
|
||||
len = strlen(exten);
|
||||
@@ -7192,6 +7212,8 @@
|
||||
dahdi_set_hook(i->subs[SUB_REAL].dfd, DAHDI_ONHOOK);
|
||||
break;
|
||||
case SIG_PRI:
|
||||
+ case SIG_BRI:
|
||||
+ case SIG_BRI_PTMP:
|
||||
dahdi_disable_ec(i);
|
||||
res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, -1);
|
||||
break;
|
||||
@@ -7687,13 +7709,14 @@
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_PRI
|
||||
- if ((chan_sig == SIG_PRI) || (chan_sig == SIG_GR303FXOKS) || (chan_sig == SIG_GR303FXSKS)) {
|
||||
+ if ((chan_sig == SIG_PRI) || (chan_sig == SIG_BRI) || (chan_sig == SIG_BRI_PTMP) || (chan_sig == SIG_GR303FXOKS) || (chan_sig == SIG_GR303FXSKS)) {
|
||||
int offset;
|
||||
int myswitchtype;
|
||||
int matchesdchan;
|
||||
int x,y;
|
||||
offset = 0;
|
||||
- if ((chan_sig == SIG_PRI) && ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_AUDIOMODE, &offset)) {
|
||||
+ if (((chan_sig == SIG_PRI) || (chan_sig == SIG_BRI) || (chan_sig == SIG_BRI_PTMP))
|
||||
+ && ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_AUDIOMODE, &offset)) {
|
||||
ast_log(LOG_ERROR, "Unable to set clear mode on clear channel %d of span %d: %s\n", channel, p.spanno, strerror(errno));
|
||||
destroy_dahdi_pvt(&tmp);
|
||||
return NULL;
|
||||
@@ -7718,7 +7741,9 @@
|
||||
destroy_dahdi_pvt(&tmp);
|
||||
return NULL;
|
||||
}
|
||||
- if (chan_sig == SIG_PRI)
|
||||
+ if ((chan_sig == SIG_PRI) ||
|
||||
+ (chan_sig == SIG_BRI) ||
|
||||
+ (chan_sig == SIG_BRI_PTMP))
|
||||
myswitchtype = conf->pri.switchtype;
|
||||
else
|
||||
myswitchtype = PRI_SWITCH_GR303_TMC;
|
||||
@@ -7775,6 +7800,7 @@
|
||||
destroy_dahdi_pvt(&tmp);
|
||||
return NULL;
|
||||
}
|
||||
+ pris[span].sig = chan_sig;
|
||||
pris[span].nodetype = conf->pri.nodetype;
|
||||
pris[span].switchtype = myswitchtype;
|
||||
pris[span].nsf = conf->pri.nsf;
|
||||
@@ -7784,6 +7810,8 @@
|
||||
pris[span].minunused = conf->pri.minunused;
|
||||
pris[span].minidle = conf->pri.minidle;
|
||||
pris[span].overlapdial = conf->pri.overlapdial;
|
||||
+ pris[span].bri_l1_check = ((SIG_BRI == chan_sig) | (SIG_BRI_PTMP == chan_sig))
|
||||
+ ? conf->pri.bri_l1_check : 1;
|
||||
#ifdef HAVE_PRI_INBANDDISCONNECT
|
||||
pris[span].inbanddisconnect = conf->pri.inbanddisconnect;
|
||||
#endif
|
||||
@@ -7995,7 +8023,7 @@
|
||||
ast_dsp_digitmode(tmp->dsp, DSP_DIGITMODE_DTMF | tmp->dtmfrelax);
|
||||
update_conf(tmp);
|
||||
if (!here) {
|
||||
- if (chan_sig != SIG_PRI)
|
||||
+ if ((chan_sig != SIG_BRI) && (chan_sig != SIG_BRI_PTMP) && (chan_sig != SIG_PRI))
|
||||
/* Hang it up to be sure it's good */
|
||||
dahdi_set_hook(tmp->subs[SUB_REAL].dfd, DAHDI_ONHOOK);
|
||||
}
|
||||
@@ -9056,7 +9084,7 @@
|
||||
ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d up\n", pri_order(which), pri->span);
|
||||
}
|
||||
pri->dchanavail[which] |= DCHAN_UP;
|
||||
- } else {
|
||||
+ } else if (pri->bri_l1_check) {
|
||||
if (pri->dchanavail[which] & DCHAN_UP) {
|
||||
if (option_verbose > 1)
|
||||
ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d down\n", pri_order(which), pri->span);
|
||||
@@ -9993,7 +10021,16 @@
|
||||
dahdi_close_pri_fd(pri, i);
|
||||
return -1;
|
||||
}
|
||||
- pri->dchans[i] = pri_new(pri->fds[i], pri->nodetype, pri->switchtype);
|
||||
+ switch (pri->sig) {
|
||||
+ case SIG_BRI:
|
||||
+ pri->dchans[i] = pri_new_bri(pri->fds[i], 1, pri->nodetype, pri->switchtype);
|
||||
+ break;
|
||||
+ case SIG_BRI_PTMP:
|
||||
+ pri->dchans[i] = pri_new_bri(pri->fds[i], 0, pri->nodetype, pri->switchtype);
|
||||
+ break;
|
||||
+ default:
|
||||
+ pri->dchans[i] = pri_new(pri->fds[i], pri->nodetype, pri->switchtype);
|
||||
+ }
|
||||
/* Force overlap dial if we're doing GR-303! */
|
||||
if (pri->switchtype == PRI_SWITCH_GR303_TMC)
|
||||
pri->overlapdial = 1;
|
||||
@@ -11526,6 +11563,10 @@
|
||||
confp->chan.hanguponpolarityswitch = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "sendcalleridafter")) {
|
||||
confp->chan.sendcalleridafter = atoi(v->value);
|
||||
+#ifdef HAVE_PRI
|
||||
+ } else if (!strcasecmp(v->name, "bri_l1_check")) {
|
||||
+ confp->pri.bri_l1_check = ast_true(v->value);
|
||||
+#endif
|
||||
} else if (reload != 1){
|
||||
if (!strcasecmp(v->name, "signalling")) {
|
||||
confp->chan.outsigmod = -1;
|
||||
@@ -11638,6 +11679,22 @@
|
||||
confp->chan.sig = SIG_PRI;
|
||||
confp->chan.radio = 0;
|
||||
confp->pri.nodetype = PRI_CPE;
|
||||
+ } else if (!strcasecmp(v->value, "bri_cpe")) {
|
||||
+ confp->chan.sig = SIG_BRI;
|
||||
+ confp->chan.radio = 0;
|
||||
+ confp->pri.nodetype = PRI_CPE;
|
||||
+ } else if (!strcasecmp(v->value, "bri_net")) {
|
||||
+ confp->chan.sig = SIG_BRI;
|
||||
+ confp->chan.radio = 0;
|
||||
+ confp->pri.nodetype = PRI_NETWORK;
|
||||
+ } else if (!strcasecmp(v->value, "bri_cpe_ptmp")) {
|
||||
+ confp->chan.sig = SIG_BRI_PTMP;
|
||||
+ confp->chan.radio = 0;
|
||||
+ confp->pri.nodetype = PRI_CPE;
|
||||
+ } else if (!strcasecmp(v->value, "bri_net_ptmp")) {
|
||||
+ confp->chan.sig = SIG_BRI_PTMP;
|
||||
+ confp->chan.radio = 0;
|
||||
+ confp->pri.nodetype = PRI_NETWORK;
|
||||
} else if (!strcasecmp(v->value, "gr303fxoks_net")) {
|
||||
confp->chan.sig = SIG_GR303FXOKS;
|
||||
confp->chan.radio = 0;
|
||||
@ -0,0 +1,13 @@
|
||||
--- channels/chan_sip.c (revisión: 201379)
|
||||
+++ channels/chan_sip.c (revisión: 201380)
|
||||
@@ -15519,6 +15519,10 @@
|
||||
memset(buf, 0, buflen);
|
||||
memset(&qos, 0, sizeof(qos));
|
||||
|
||||
+ if (p == NULL) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
if (strcasecmp(args.type, "AUDIO") == 0) {
|
||||
all = ast_rtp_get_quality(p->rtp, &qos);
|
||||
} else if (strcasecmp(args.type, "VIDEO") == 0) {
|
||||
@ -0,0 +1,11 @@
|
||||
--- channels/chan_sip.c (revisión: 200512)
|
||||
+++ channels/chan_sip.c (revisión: 200513)
|
||||
@@ -474,7 +474,7 @@
|
||||
|
||||
|
||||
/*! \brief SIP Methods we support */
|
||||
-#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY"
|
||||
+#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO"
|
||||
|
||||
/*! \brief SIP Extensions we support */
|
||||
#define SUPPORTED_EXTENSIONS "replaces"
|
||||
@ -0,0 +1,26 @@
|
||||
--- channels/chan_sip.c 2010-08-12 22:55:38.000000000 +0200
|
||||
+++ channels/chan_sip.c 2010-08-12 23:44:18.000000000 +0200
|
||||
@@ -14984,6 +14984,23 @@
|
||||
target.chan1 = targetcall_pvt->owner; /* Transferer to Asterisk */
|
||||
target.chan2 = ast_bridged_channel(targetcall_pvt->owner); /* Asterisk to target */
|
||||
|
||||
+ /* IRONTEC */
|
||||
+ ast_log(LOG_NOTICE, "Hola, soy un REFER!\n");
|
||||
+ ast_log(LOG_NOTICE, "Chan1: %s\n", current->chan1->name);
|
||||
+ const char *ironcallid = pbx_builtin_getvar_helper(current->chan1, "SIPCALLID");
|
||||
+ ast_log(LOG_NOTICE, "Call-ID pata 1: %s\n", ironcallid);
|
||||
+ ast_log(LOG_NOTICE, "Hola, soy la otra parte del REFER!\n");
|
||||
+ ast_log(LOG_NOTICE, "Target Chan1: %s\n", target.chan1->name);
|
||||
+ if (target.chan2) {
|
||||
+ pbx_builtin_setvar_helper(target.chan1, "__ORIGINAL_CALLID", ironcallid);
|
||||
+ if(current->chan2) {
|
||||
+ ast_log(LOG_NOTICE, "Chan2: %s\n", current->chan2->name);
|
||||
+ ast_log(LOG_NOTICE, "Target Chan2: %s\n", target.chan2->name);
|
||||
+ pbx_builtin_setvar_helper(target.chan1, "__ORIGINAL_CALLERID", current->chan2->cid.cid_num);
|
||||
+ }
|
||||
+ }
|
||||
+ /* /IRONTEC */
|
||||
+
|
||||
if (!target.chan2 || !(target.chan2->_state == AST_STATE_UP || target.chan2->_state == AST_STATE_RINGING) ) {
|
||||
/* Wrong state of new channel */
|
||||
if (option_debug > 3) {
|
||||
@ -0,0 +1,16 @@
|
||||
--- channels/chan_sip.c (revision 46)
|
||||
+++ channels/chan_sip.c (working copy)
|
||||
@@ -7543,6 +7543,13 @@
|
||||
else
|
||||
ast_build_string(&t, &maxbytes, "<dialog id=\"%s\">\n", p->exten);
|
||||
ast_build_string(&t, &maxbytes, "<state>%s</state>\n", statestring);
|
||||
+
|
||||
+ if((state & AST_EXTENSION_RINGING) && global_notifyringing && strstr(p->useragent,"THOMSON")){
|
||||
+ ast_build_string(&t, &maxbytes, "<local><identity display=\"%s\">%s</identity><target uri=\"%s\"/></local>\n", p->exten, mfrom, mfrom);
|
||||
+ ast_build_string(&t, &maxbytes, "<remote><identity display=\"%s\">sip:*8%s</identity><target uri=\"sip:*8%s\"/></remote>\n", "pickup", mto+4, mto+4);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
if (state == AST_EXTENSION_ONHOLD) {
|
||||
ast_build_string(&t, &maxbytes, "<local>\n<target uri=\"%s\">\n"
|
||||
"<param pname=\"+sip.rendering\" pvalue=\"no\"/>\n"
|
||||
@ -0,0 +1,767 @@
|
||||
--- channels/chan_sip.c (revisión: 184946)
|
||||
+++ channels/chan_sip.c (revisión: 184947)
|
||||
@@ -847,7 +847,6 @@
|
||||
/*! \brief T38 States for a call */
|
||||
enum t38state {
|
||||
T38_DISABLED = 0, /*!< Not enabled */
|
||||
- T38_LOCAL_DIRECT, /*!< Offered from local */
|
||||
T38_LOCAL_REINVITE, /*!< Offered from local - REINVITE */
|
||||
T38_PEER_DIRECT, /*!< Offered from peer */
|
||||
T38_PEER_REINVITE, /*!< Offered from peer - REINVITE */
|
||||
@@ -861,6 +860,7 @@
|
||||
int peercapability; /*!< Peers T38 capability */
|
||||
int jointcapability; /*!< Supported T38 capability at both ends */
|
||||
enum t38state state; /*!< T.38 state */
|
||||
+ unsigned int direct:1; /*!< Whether the T38 came from the initial invite or not */
|
||||
};
|
||||
|
||||
/*! \brief Parameters to know status of transfer */
|
||||
@@ -1325,7 +1325,7 @@
|
||||
static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_rate,
|
||||
char **m_buf, size_t *m_size, char **a_buf, size_t *a_size,
|
||||
int debug);
|
||||
-static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p);
|
||||
+static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int add_audio, int add_t38);
|
||||
static void stop_media_flows(struct sip_pvt *p);
|
||||
|
||||
/*--- Authentication stuff */
|
||||
@@ -3053,12 +3053,7 @@
|
||||
} else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) {
|
||||
/* We're replacing a call. */
|
||||
p->options->replaces = ast_var_value(current);
|
||||
- } else if (!strcasecmp(ast_var_name(current), "T38CALL")) {
|
||||
- p->t38.state = T38_LOCAL_DIRECT;
|
||||
- if (option_debug)
|
||||
- ast_log(LOG_DEBUG,"T38State change to %d on channel %s\n", p->t38.state, ast->name);
|
||||
}
|
||||
-
|
||||
}
|
||||
|
||||
res = 0;
|
||||
@@ -3756,16 +3751,9 @@
|
||||
ast_setstate(ast, AST_STATE_UP);
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "SIP answering channel: %s\n", ast->name);
|
||||
- if (p->t38.state == T38_PEER_DIRECT) {
|
||||
- p->t38.state = T38_ENABLED;
|
||||
- if (option_debug > 1)
|
||||
- ast_log(LOG_DEBUG,"T38State change to %d on channel %s\n", p->t38.state, ast->name);
|
||||
- res = transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
|
||||
- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
|
||||
- } else {
|
||||
- res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
|
||||
- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
|
||||
- }
|
||||
+
|
||||
+ res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
|
||||
+ ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
|
||||
}
|
||||
ast_mutex_unlock(&p->lock);
|
||||
return res;
|
||||
@@ -3802,9 +3790,13 @@
|
||||
p->invitestate = INV_EARLY_MEDIA;
|
||||
transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE);
|
||||
ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
|
||||
+ } else if (p->t38.state == T38_ENABLED && !p->t38.direct) {
|
||||
+ p->t38.state = T38_DISABLED;
|
||||
+ transmit_reinvite_with_sdp(p);
|
||||
+ } else {
|
||||
+ p->lastrtptx = time(NULL);
|
||||
+ res = ast_rtp_write(p->rtp, frame);
|
||||
}
|
||||
- p->lastrtptx = time(NULL);
|
||||
- res = ast_rtp_write(p->rtp, frame);
|
||||
}
|
||||
ast_mutex_unlock(&p->lock);
|
||||
}
|
||||
@@ -3837,8 +3829,16 @@
|
||||
we simply forget the frames if we get modem frames before the bridge is up.
|
||||
Fax will re-transmit.
|
||||
*/
|
||||
- if (p->udptl && ast->_state == AST_STATE_UP)
|
||||
- res = ast_udptl_write(p->udptl, frame);
|
||||
+ if (ast->_state == AST_STATE_UP) {
|
||||
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && p->t38.state == T38_DISABLED) {
|
||||
+ if (!p->pendinginvite) {
|
||||
+ p->t38.state = T38_LOCAL_REINVITE;
|
||||
+ transmit_reinvite_with_t38_sdp(p);
|
||||
+ }
|
||||
+ } else if (p->t38.state == T38_ENABLED) {
|
||||
+ res = ast_udptl_write(p->udptl, frame);
|
||||
+ }
|
||||
+ }
|
||||
ast_mutex_unlock(&p->lock);
|
||||
}
|
||||
break;
|
||||
@@ -4218,10 +4218,6 @@
|
||||
if (i->rtp)
|
||||
ast_jb_configure(tmp, &global_jbconf);
|
||||
|
||||
- /* If the INVITE contains T.38 SDP information set the proper channel variable so a created outgoing call will also have T.38 */
|
||||
- if (i->udptl && i->t38.state == T38_PEER_DIRECT)
|
||||
- pbx_builtin_setvar_helper(tmp, "_T38CALL", "1");
|
||||
-
|
||||
/* Set channel variables for this call from configuration */
|
||||
for (v = i->chanvars ; v ; v = v->next)
|
||||
pbx_builtin_setvar_helper(tmp, v->name, v->value);
|
||||
@@ -5256,6 +5252,7 @@
|
||||
ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>" );
|
||||
} else {
|
||||
p->t38.state = T38_PEER_DIRECT; /* T38 Offered directly from peer in first invite */
|
||||
+ p->t38.direct = 1;
|
||||
if (option_debug > 1)
|
||||
ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
|
||||
}
|
||||
@@ -6506,106 +6503,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
-/*! \brief Add T.38 Session Description Protocol message */
|
||||
-static int add_t38_sdp(struct sip_request *resp, struct sip_pvt *p)
|
||||
-{
|
||||
- int len = 0;
|
||||
- int x = 0;
|
||||
- struct sockaddr_in udptlsin;
|
||||
- char v[256] = "";
|
||||
- char s[256] = "";
|
||||
- char o[256] = "";
|
||||
- char c[256] = "";
|
||||
- char t[256] = "";
|
||||
- char m_modem[256];
|
||||
- char a_modem[1024];
|
||||
- char *m_modem_next = m_modem;
|
||||
- size_t m_modem_left = sizeof(m_modem);
|
||||
- char *a_modem_next = a_modem;
|
||||
- size_t a_modem_left = sizeof(a_modem);
|
||||
- struct sockaddr_in udptldest = { 0, };
|
||||
- int debug;
|
||||
-
|
||||
- debug = sip_debug_test_pvt(p);
|
||||
- len = 0;
|
||||
- if (!p->udptl) {
|
||||
- ast_log(LOG_WARNING, "No way to add SDP without an UDPTL structure\n");
|
||||
- return -1;
|
||||
- }
|
||||
-
|
||||
- if (!p->sessionid) {
|
||||
- p->sessionid = getpid();
|
||||
- p->sessionversion = p->sessionid;
|
||||
- } else
|
||||
- p->sessionversion++;
|
||||
-
|
||||
- /* Our T.38 end is */
|
||||
- ast_udptl_get_us(p->udptl, &udptlsin);
|
||||
-
|
||||
- /* Determine T.38 UDPTL destination */
|
||||
- if (p->udptlredirip.sin_addr.s_addr) {
|
||||
- udptldest.sin_port = p->udptlredirip.sin_port;
|
||||
- udptldest.sin_addr = p->udptlredirip.sin_addr;
|
||||
- } else {
|
||||
- udptldest.sin_addr = p->ourip;
|
||||
- udptldest.sin_port = udptlsin.sin_port;
|
||||
- }
|
||||
-
|
||||
- if (debug)
|
||||
- ast_log(LOG_DEBUG, "T.38 UDPTL is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(udptlsin.sin_port));
|
||||
-
|
||||
- /* We break with the "recommendation" and send our IP, in order that our
|
||||
- peer doesn't have to ast_gethostbyname() us */
|
||||
-
|
||||
- if (debug) {
|
||||
- ast_log(LOG_DEBUG, "Our T38 capability (%d), peer T38 capability (%d), joint capability (%d)\n",
|
||||
- p->t38.capability,
|
||||
- p->t38.peercapability,
|
||||
- p->t38.jointcapability);
|
||||
- }
|
||||
- snprintf(v, sizeof(v), "v=0\r\n");
|
||||
- snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", p->sessionid, p->sessionversion, ast_inet_ntoa(udptldest.sin_addr));
|
||||
- snprintf(s, sizeof(s), "s=session\r\n");
|
||||
- snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", ast_inet_ntoa(udptldest.sin_addr));
|
||||
- snprintf(t, sizeof(t), "t=0 0\r\n");
|
||||
- ast_build_string(&m_modem_next, &m_modem_left, "m=image %d udptl t38\r\n", ntohs(udptldest.sin_port));
|
||||
-
|
||||
- if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_0)
|
||||
- ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxVersion:0\r\n");
|
||||
- if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_1)
|
||||
- ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxVersion:1\r\n");
|
||||
- if ((x = t38_get_rate(p->t38.jointcapability)))
|
||||
- ast_build_string(&a_modem_next, &a_modem_left, "a=T38MaxBitRate:%d\r\n",x);
|
||||
- if ((p->t38.jointcapability & T38FAX_FILL_BIT_REMOVAL) == T38FAX_FILL_BIT_REMOVAL)
|
||||
- ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxFillBitRemoval\r\n");
|
||||
- if ((p->t38.jointcapability & T38FAX_TRANSCODING_MMR) == T38FAX_TRANSCODING_MMR)
|
||||
- ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxTranscodingMMR\r\n");
|
||||
- if ((p->t38.jointcapability & T38FAX_TRANSCODING_JBIG) == T38FAX_TRANSCODING_JBIG)
|
||||
- ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxTranscodingJBIG\r\n");
|
||||
- ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxRateManagement:%s\r\n", (p->t38.jointcapability & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) ? "localTCF" : "transferredTCF");
|
||||
- x = ast_udptl_get_local_max_datagram(p->udptl);
|
||||
- ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxMaxBuffer:%d\r\n",x);
|
||||
- ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxMaxDatagram:%d\r\n",x);
|
||||
- if (p->t38.jointcapability != T38FAX_UDP_EC_NONE)
|
||||
- ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxUdpEC:%s\r\n", (p->t38.jointcapability & T38FAX_UDP_EC_REDUNDANCY) ? "t38UDPRedundancy" : "t38UDPFEC");
|
||||
- len = strlen(v) + strlen(s) + strlen(o) + strlen(c) + strlen(t) + strlen(m_modem) + strlen(a_modem);
|
||||
- add_header(resp, "Content-Type", "application/sdp");
|
||||
- add_header_contentLength(resp, len);
|
||||
- add_line(resp, v);
|
||||
- add_line(resp, o);
|
||||
- add_line(resp, s);
|
||||
- add_line(resp, c);
|
||||
- add_line(resp, t);
|
||||
- add_line(resp, m_modem);
|
||||
- add_line(resp, a_modem);
|
||||
-
|
||||
- /* Update lastrtprx when we send our SDP */
|
||||
- p->lastrtprx = p->lastrtptx = time(NULL);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-
|
||||
/*! \brief Add RFC 2833 DTMF offer to SDP */
|
||||
static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_rate,
|
||||
char **m_buf, size_t *m_size, char **a_buf, size_t *a_size,
|
||||
@@ -6635,7 +6532,7 @@
|
||||
#define SDP_SAMPLE_RATE(x) 8000
|
||||
|
||||
/*! \brief Add Session Description Protocol message */
|
||||
-static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p)
|
||||
+static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int add_audio, int add_t38)
|
||||
{
|
||||
int len = 0;
|
||||
int alreadysent = 0;
|
||||
@@ -6655,26 +6552,33 @@
|
||||
char *hold;
|
||||
char m_audio[256]; /* Media declaration line for audio */
|
||||
char m_video[256]; /* Media declaration line for video */
|
||||
+ char m_modem[256]; /* Media declaration line for t38 */
|
||||
char a_audio[1024]; /* Attributes for audio */
|
||||
char a_video[1024]; /* Attributes for video */
|
||||
+ char a_modem[1024]; /* Attributes for t38 */
|
||||
char *m_audio_next = m_audio;
|
||||
char *m_video_next = m_video;
|
||||
+ char *m_modem_next = m_modem;
|
||||
size_t m_audio_left = sizeof(m_audio);
|
||||
size_t m_video_left = sizeof(m_video);
|
||||
+ size_t m_modem_left = sizeof(m_modem);
|
||||
char *a_audio_next = a_audio;
|
||||
char *a_video_next = a_video;
|
||||
+ char *a_modem_next = a_modem;
|
||||
size_t a_audio_left = sizeof(a_audio);
|
||||
size_t a_video_left = sizeof(a_video);
|
||||
+ size_t a_modem_left = sizeof(a_modem);
|
||||
|
||||
int x;
|
||||
- int capability;
|
||||
+ int capability = 0;
|
||||
int needvideo = FALSE;
|
||||
int debug = sip_debug_test_pvt(p);
|
||||
int min_audio_packet_size = 0;
|
||||
int min_video_packet_size = 0;
|
||||
|
||||
m_video[0] = '\0'; /* Reset the video media string if it's not needed */
|
||||
-
|
||||
+ m_modem[0] = '\0';
|
||||
+
|
||||
if (!p->rtp) {
|
||||
ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n");
|
||||
return AST_FAILURE;
|
||||
@@ -6701,164 +6605,211 @@
|
||||
dest.sin_port = sin.sin_port;
|
||||
}
|
||||
|
||||
- capability = p->jointcapability;
|
||||
+ snprintf(owner, sizeof(owner), "o=root %d %d IN IP4 %s\r\n", p->sessionid, p->sessionversion, ast_inet_ntoa(dest.sin_addr));
|
||||
+ snprintf(connection, sizeof(connection), "c=IN IP4 %s\r\n", ast_inet_ntoa(dest.sin_addr));
|
||||
|
||||
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR)
|
||||
+ hold = "a=recvonly\r\n";
|
||||
+ else if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_INACTIVE)
|
||||
+ hold = "a=inactive\r\n";
|
||||
+ else
|
||||
+ hold = "a=sendrecv\r\n";
|
||||
|
||||
- if (option_debug > 1) {
|
||||
- char codecbuf[SIPBUFSIZE];
|
||||
- ast_log(LOG_DEBUG, "** Our capability: %s Video flag: %s\n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability), ast_test_flag(&p->flags[0], SIP_NOVIDEO) ? "True" : "False");
|
||||
- ast_log(LOG_DEBUG, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec));
|
||||
- }
|
||||
-
|
||||
+ if (add_audio) {
|
||||
+ capability = p->jointcapability;
|
||||
+
|
||||
+
|
||||
+ if (option_debug > 1) {
|
||||
+ char codecbuf[SIPBUFSIZE];
|
||||
+ ast_log(LOG_DEBUG, "** Our capability: %s Video flag: %s\n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability), ast_test_flag(&p->flags[0], SIP_NOVIDEO) ? "True" : "False");
|
||||
+ ast_log(LOG_DEBUG, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec));
|
||||
+ }
|
||||
+
|
||||
#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS
|
||||
- if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_RTP)) {
|
||||
- ast_build_string(&m_audio_next, &m_audio_left, " %d", 191);
|
||||
- ast_build_string(&a_audio_next, &a_audio_left, "a=rtpmap:%d %s/%d\r\n", 191, "t38", 8000);
|
||||
- }
|
||||
+ if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_RTP)) {
|
||||
+ ast_build_string(&m_audio_next, &m_audio_left, " %d", 191);
|
||||
+ ast_build_string(&a_audio_next, &a_audio_left, "a=rtpmap:%d %s/%d\r\n", 191, "t38", 8000);
|
||||
+ }
|
||||
#endif
|
||||
|
||||
- /* Check if we need video in this call */
|
||||
- if ((capability & AST_FORMAT_VIDEO_MASK) && !ast_test_flag(&p->flags[0], SIP_NOVIDEO)) {
|
||||
- if (p->vrtp) {
|
||||
- needvideo = TRUE;
|
||||
- if (option_debug > 1)
|
||||
- ast_log(LOG_DEBUG, "This call needs video offers!\n");
|
||||
- } else if (option_debug > 1)
|
||||
- ast_log(LOG_DEBUG, "This call needs video offers, but there's no video support enabled!\n");
|
||||
- }
|
||||
-
|
||||
+ /* Check if we need video in this call */
|
||||
+ if ((capability & AST_FORMAT_VIDEO_MASK) && !ast_test_flag(&p->flags[0], SIP_NOVIDEO)) {
|
||||
+ if (p->vrtp) {
|
||||
+ needvideo = TRUE;
|
||||
+ if (option_debug > 1)
|
||||
+ ast_log(LOG_DEBUG, "This call needs video offers!\n");
|
||||
+ } else if (option_debug > 1)
|
||||
+ ast_log(LOG_DEBUG, "This call needs video offers, but there's no video support enabled!\n");
|
||||
+ }
|
||||
|
||||
- /* Ok, we need video. Let's add what we need for video and set codecs.
|
||||
- Video is handled differently than audio since we can not transcode. */
|
||||
- if (needvideo) {
|
||||
- /* Determine video destination */
|
||||
- if (p->vredirip.sin_addr.s_addr) {
|
||||
- vdest.sin_addr = p->vredirip.sin_addr;
|
||||
- vdest.sin_port = p->vredirip.sin_port;
|
||||
- } else {
|
||||
- vdest.sin_addr = p->ourip;
|
||||
- vdest.sin_port = vsin.sin_port;
|
||||
+
|
||||
+ /* Ok, we need video. Let's add what we need for video and set codecs.
|
||||
+ Video is handled differently than audio since we can not transcode. */
|
||||
+ if (needvideo) {
|
||||
+ /* Determine video destination */
|
||||
+ if (p->vredirip.sin_addr.s_addr) {
|
||||
+ vdest.sin_addr = p->vredirip.sin_addr;
|
||||
+ vdest.sin_port = p->vredirip.sin_port;
|
||||
+ } else {
|
||||
+ vdest.sin_addr = p->ourip;
|
||||
+ vdest.sin_port = vsin.sin_port;
|
||||
+ }
|
||||
+ ast_build_string(&m_video_next, &m_video_left, "m=video %d RTP/AVP", ntohs(vdest.sin_port));
|
||||
+
|
||||
+ /* Build max bitrate string */
|
||||
+ if (p->maxcallbitrate)
|
||||
+ snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate);
|
||||
+ if (debug)
|
||||
+ ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(vsin.sin_port));
|
||||
}
|
||||
- ast_build_string(&m_video_next, &m_video_left, "m=video %d RTP/AVP", ntohs(vdest.sin_port));
|
||||
|
||||
- /* Build max bitrate string */
|
||||
- if (p->maxcallbitrate)
|
||||
- snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate);
|
||||
if (debug)
|
||||
- ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(vsin.sin_port));
|
||||
- }
|
||||
+ ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(sin.sin_port));
|
||||
|
||||
- if (debug)
|
||||
- ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(sin.sin_port));
|
||||
+ ast_build_string(&m_audio_next, &m_audio_left, "m=audio %d RTP/AVP", ntohs(dest.sin_port));
|
||||
|
||||
- /* Start building generic SDP headers */
|
||||
+ /* Now, start adding audio codecs. These are added in this order:
|
||||
+ - First what was requested by the calling channel
|
||||
+ - Then preferences in order from sip.conf device config for this peer/user
|
||||
+ - Then other codecs in capabilities, including video
|
||||
+ */
|
||||
|
||||
- /* We break with the "recommendation" and send our IP, in order that our
|
||||
- peer doesn't have to ast_gethostbyname() us */
|
||||
+ /* Prefer the audio codec we were requested to use, first, no matter what
|
||||
+ Note that p->prefcodec can include video codecs, so mask them out
|
||||
+ */
|
||||
+ if (capability & p->prefcodec) {
|
||||
+ int codec = p->prefcodec & AST_FORMAT_AUDIO_MASK;
|
||||
|
||||
- snprintf(owner, sizeof(owner), "o=root %d %d IN IP4 %s\r\n", p->sessionid, p->sessionversion, ast_inet_ntoa(dest.sin_addr));
|
||||
- snprintf(connection, sizeof(connection), "c=IN IP4 %s\r\n", ast_inet_ntoa(dest.sin_addr));
|
||||
- ast_build_string(&m_audio_next, &m_audio_left, "m=audio %d RTP/AVP", ntohs(dest.sin_port));
|
||||
+ add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec),
|
||||
+ &m_audio_next, &m_audio_left,
|
||||
+ &a_audio_next, &a_audio_left,
|
||||
+ debug, &min_audio_packet_size);
|
||||
+ alreadysent |= codec;
|
||||
+ }
|
||||
|
||||
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR)
|
||||
- hold = "a=recvonly\r\n";
|
||||
- else if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_INACTIVE)
|
||||
- hold = "a=inactive\r\n";
|
||||
- else
|
||||
- hold = "a=sendrecv\r\n";
|
||||
+ /* Start by sending our preferred audio codecs */
|
||||
+ for (x = 0; x < 32; x++) {
|
||||
+ int codec;
|
||||
|
||||
- /* Now, start adding audio codecs. These are added in this order:
|
||||
- - First what was requested by the calling channel
|
||||
- - Then preferences in order from sip.conf device config for this peer/user
|
||||
- - Then other codecs in capabilities, including video
|
||||
- */
|
||||
+ if (!(codec = ast_codec_pref_index(&p->prefs, x)))
|
||||
+ break;
|
||||
|
||||
- /* Prefer the audio codec we were requested to use, first, no matter what
|
||||
- Note that p->prefcodec can include video codecs, so mask them out
|
||||
- */
|
||||
- if (capability & p->prefcodec) {
|
||||
- int codec = p->prefcodec & AST_FORMAT_AUDIO_MASK;
|
||||
+ if (!(capability & codec))
|
||||
+ continue;
|
||||
|
||||
- add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec),
|
||||
- &m_audio_next, &m_audio_left,
|
||||
- &a_audio_next, &a_audio_left,
|
||||
- debug, &min_audio_packet_size);
|
||||
- alreadysent |= codec;
|
||||
- }
|
||||
+ if (alreadysent & codec)
|
||||
+ continue;
|
||||
|
||||
- /* Start by sending our preferred audio codecs */
|
||||
- for (x = 0; x < 32; x++) {
|
||||
- int codec;
|
||||
+ add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec),
|
||||
+ &m_audio_next, &m_audio_left,
|
||||
+ &a_audio_next, &a_audio_left,
|
||||
+ debug, &min_audio_packet_size);
|
||||
+ alreadysent |= codec;
|
||||
+ }
|
||||
|
||||
- if (!(codec = ast_codec_pref_index(&p->prefs, x)))
|
||||
- break;
|
||||
+ /* Now send any other common audio and video codecs, and non-codec formats: */
|
||||
+ for (x = 1; x <= (needvideo ? AST_FORMAT_MAX_VIDEO : AST_FORMAT_MAX_AUDIO); x <<= 1) {
|
||||
+ if (!(capability & x)) /* Codec not requested */
|
||||
+ continue;
|
||||
|
||||
- if (!(capability & codec))
|
||||
- continue;
|
||||
+ if (alreadysent & x) /* Already added to SDP */
|
||||
+ continue;
|
||||
|
||||
- if (alreadysent & codec)
|
||||
- continue;
|
||||
+ if (x <= AST_FORMAT_MAX_AUDIO)
|
||||
+ add_codec_to_sdp(p, x, SDP_SAMPLE_RATE(x),
|
||||
+ &m_audio_next, &m_audio_left,
|
||||
+ &a_audio_next, &a_audio_left,
|
||||
+ debug, &min_audio_packet_size);
|
||||
+ else
|
||||
+ add_codec_to_sdp(p, x, 90000,
|
||||
+ &m_video_next, &m_video_left,
|
||||
+ &a_video_next, &a_video_left,
|
||||
+ debug, &min_video_packet_size);
|
||||
+ }
|
||||
|
||||
- add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec),
|
||||
- &m_audio_next, &m_audio_left,
|
||||
- &a_audio_next, &a_audio_left,
|
||||
- debug, &min_audio_packet_size);
|
||||
- alreadysent |= codec;
|
||||
- }
|
||||
+ /* Now add DTMF RFC2833 telephony-event as a codec */
|
||||
+ for (x = 1; x <= AST_RTP_MAX; x <<= 1) {
|
||||
+ if (!(p->jointnoncodeccapability & x))
|
||||
+ continue;
|
||||
|
||||
- /* Now send any other common audio and video codecs, and non-codec formats: */
|
||||
- for (x = 1; x <= (needvideo ? AST_FORMAT_MAX_VIDEO : AST_FORMAT_MAX_AUDIO); x <<= 1) {
|
||||
- if (!(capability & x)) /* Codec not requested */
|
||||
- continue;
|
||||
+ add_noncodec_to_sdp(p, x, 8000,
|
||||
+ &m_audio_next, &m_audio_left,
|
||||
+ &a_audio_next, &a_audio_left,
|
||||
+ debug);
|
||||
+ }
|
||||
|
||||
- if (alreadysent & x) /* Already added to SDP */
|
||||
- continue;
|
||||
+ if (option_debug > 2)
|
||||
+ ast_log(LOG_DEBUG, "-- Done with adding codecs to SDP\n");
|
||||
|
||||
- if (x <= AST_FORMAT_MAX_AUDIO)
|
||||
- add_codec_to_sdp(p, x, SDP_SAMPLE_RATE(x),
|
||||
- &m_audio_next, &m_audio_left,
|
||||
- &a_audio_next, &a_audio_left,
|
||||
- debug, &min_audio_packet_size);
|
||||
- else
|
||||
- add_codec_to_sdp(p, x, 90000,
|
||||
- &m_video_next, &m_video_left,
|
||||
- &a_video_next, &a_video_left,
|
||||
- debug, &min_video_packet_size);
|
||||
- }
|
||||
+ if (!p->owner || !ast_internal_timing_enabled(p->owner))
|
||||
+ ast_build_string(&a_audio_next, &a_audio_left, "a=silenceSupp:off - - - -\r\n");
|
||||
|
||||
- /* Now add DTMF RFC2833 telephony-event as a codec */
|
||||
- for (x = 1; x <= AST_RTP_MAX; x <<= 1) {
|
||||
- if (!(p->jointnoncodeccapability & x))
|
||||
- continue;
|
||||
+ if (min_audio_packet_size)
|
||||
+ ast_build_string(&a_audio_next, &a_audio_left, "a=ptime:%d\r\n", min_audio_packet_size);
|
||||
|
||||
- add_noncodec_to_sdp(p, x, 8000,
|
||||
- &m_audio_next, &m_audio_left,
|
||||
- &a_audio_next, &a_audio_left,
|
||||
- debug);
|
||||
+ if (min_video_packet_size)
|
||||
+ ast_build_string(&a_video_next, &a_video_left, "a=ptime:%d\r\n", min_video_packet_size);
|
||||
+
|
||||
+ if ((m_audio_left < 2) || (m_video_left < 2) || (a_audio_left == 0) || (a_video_left == 0))
|
||||
+ ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n");
|
||||
+
|
||||
+ ast_build_string(&m_audio_next, &m_audio_left, "\r\n");
|
||||
+ if (needvideo)
|
||||
+ ast_build_string(&m_video_next, &m_video_left, "\r\n");
|
||||
}
|
||||
|
||||
- if (option_debug > 2)
|
||||
- ast_log(LOG_DEBUG, "-- Done with adding codecs to SDP\n");
|
||||
+ if (add_t38 && p->udptl) {
|
||||
+ struct sockaddr_in udptlsin;
|
||||
+ struct sockaddr_in udptldest = { 0, };
|
||||
|
||||
- if (!p->owner || !ast_internal_timing_enabled(p->owner))
|
||||
- ast_build_string(&a_audio_next, &a_audio_left, "a=silenceSupp:off - - - -\r\n");
|
||||
+ ast_udptl_get_us(p->udptl, &udptlsin);
|
||||
|
||||
- if (min_audio_packet_size)
|
||||
- ast_build_string(&a_audio_next, &a_audio_left, "a=ptime:%d\r\n", min_audio_packet_size);
|
||||
+ if (p->udptlredirip.sin_addr.s_addr) {
|
||||
+ udptldest.sin_port = p->udptlredirip.sin_port;
|
||||
+ udptldest.sin_addr = p->udptlredirip.sin_addr;
|
||||
+ } else {
|
||||
+ udptldest.sin_addr = p->ourip;
|
||||
+ udptldest.sin_port = udptlsin.sin_port;
|
||||
+ }
|
||||
|
||||
- if (min_video_packet_size)
|
||||
- ast_build_string(&a_video_next, &a_video_left, "a=ptime:%d\r\n", min_video_packet_size);
|
||||
+ if (debug) {
|
||||
+ ast_log(LOG_DEBUG, "T.38 UDPTL is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(udptlsin.sin_port));
|
||||
+ ast_log(LOG_DEBUG, "Our T38 capability (%d), peer T38 capability (%d), joint capability (%d)\n",
|
||||
+ p->t38.capability,
|
||||
+ p->t38.peercapability,
|
||||
+ p->t38.jointcapability);
|
||||
+ }
|
||||
|
||||
- if ((m_audio_left < 2) || (m_video_left < 2) || (a_audio_left == 0) || (a_video_left == 0))
|
||||
- ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n");
|
||||
+ ast_build_string(&m_modem_next, &m_modem_left, "m=image %d udptl t38\r\n", ntohs(udptldest.sin_port));
|
||||
|
||||
- ast_build_string(&m_audio_next, &m_audio_left, "\r\n");
|
||||
- if (needvideo)
|
||||
- ast_build_string(&m_video_next, &m_video_left, "\r\n");
|
||||
+ if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_0)
|
||||
+ ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxVersion:0\r\n");
|
||||
+ if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_1)
|
||||
+ ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxVersion:1\r\n");
|
||||
+ if ((x = t38_get_rate(p->t38.jointcapability)))
|
||||
+ ast_build_string(&a_modem_next, &a_modem_left, "a=T38MaxBitRate:%d\r\n",x);
|
||||
+ if ((p->t38.jointcapability & T38FAX_FILL_BIT_REMOVAL) == T38FAX_FILL_BIT_REMOVAL)
|
||||
+ ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxFillBitRemoval\r\n");
|
||||
+ if ((p->t38.jointcapability & T38FAX_TRANSCODING_MMR) == T38FAX_TRANSCODING_MMR)
|
||||
+ ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxTranscodingMMR\r\n");
|
||||
+ if ((p->t38.jointcapability & T38FAX_TRANSCODING_JBIG) == T38FAX_TRANSCODING_JBIG)
|
||||
+ ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxTranscodingJBIG\r\n");
|
||||
+ ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxRateManagement:%s\r\n", (p->t38.jointcapability & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) ? "localTCF" : "transferredTCF");
|
||||
+ x = ast_udptl_get_local_max_datagram(p->udptl);
|
||||
+ ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxMaxBuffer:%d\r\n",x);
|
||||
+ ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxMaxDatagram:%d\r\n",x);
|
||||
+ if (p->t38.jointcapability != T38FAX_UDP_EC_NONE)
|
||||
+ ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxUdpEC:%s\r\n", (p->t38.jointcapability & T38FAX_UDP_EC_REDUNDANCY) ? "t38UDPRedundancy" : "t38UDPFEC");
|
||||
+ }
|
||||
|
||||
- len = strlen(version) + strlen(subject) + strlen(owner) + strlen(connection) + strlen(stime) + strlen(m_audio) + strlen(a_audio) + strlen(hold);
|
||||
+ len = strlen(version) + strlen(subject) + strlen(owner) + strlen(connection) + strlen(stime);
|
||||
+ if (add_audio)
|
||||
+ len += strlen(m_audio) + strlen(a_audio) + strlen(hold);
|
||||
if (needvideo) /* only if video response is appropriate */
|
||||
len += strlen(m_video) + strlen(a_video) + strlen(bandwidth) + strlen(hold);
|
||||
+ if (add_t38) {
|
||||
+ len += strlen(m_modem) + strlen(a_modem);
|
||||
+ }
|
||||
|
||||
add_header(resp, "Content-Type", "application/sdp");
|
||||
add_header_contentLength(resp, len);
|
||||
@@ -6869,14 +6820,20 @@
|
||||
if (needvideo) /* only if video response is appropriate */
|
||||
add_line(resp, bandwidth);
|
||||
add_line(resp, stime);
|
||||
- add_line(resp, m_audio);
|
||||
- add_line(resp, a_audio);
|
||||
- add_line(resp, hold);
|
||||
+ if (add_audio) {
|
||||
+ add_line(resp, m_audio);
|
||||
+ add_line(resp, a_audio);
|
||||
+ add_line(resp, hold);
|
||||
+ }
|
||||
if (needvideo) { /* only if video response is appropriate */
|
||||
add_line(resp, m_video);
|
||||
add_line(resp, a_video);
|
||||
add_line(resp, hold); /* Repeat hold for the video stream */
|
||||
}
|
||||
+ if (add_t38) {
|
||||
+ add_line(resp, m_modem);
|
||||
+ add_line(resp, a_modem);
|
||||
+ }
|
||||
|
||||
/* Update lastrtprx when we send our SDP */
|
||||
p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
|
||||
@@ -6901,8 +6858,7 @@
|
||||
}
|
||||
respprep(&resp, p, msg, req);
|
||||
if (p->udptl) {
|
||||
- ast_udptl_offered_from_local(p->udptl, 0);
|
||||
- add_t38_sdp(&resp, p);
|
||||
+ add_sdp(&resp, p, 0, 1);
|
||||
} else
|
||||
ast_log(LOG_ERROR, "Can't add SDP to response, since we have no UDPTL session allocated. Call-ID %s\n", p->callid);
|
||||
if (retrans && !p->pendinginvite)
|
||||
@@ -6945,8 +6901,13 @@
|
||||
ast_log(LOG_DEBUG, "Setting framing from config on incoming call\n");
|
||||
ast_rtp_codec_setpref(p->rtp, &p->prefs);
|
||||
}
|
||||
- try_suggested_sip_codec(p);
|
||||
- add_sdp(&resp, p);
|
||||
+ try_suggested_sip_codec(p);
|
||||
+ if (p->t38.state == T38_PEER_DIRECT || p->t38.state == T38_ENABLED) {
|
||||
+ p->t38.state = T38_ENABLED;
|
||||
+ add_sdp(&resp, p, 1, 1);
|
||||
+ } else {
|
||||
+ add_sdp(&resp, p, 1, 0);
|
||||
+ }
|
||||
} else
|
||||
ast_log(LOG_ERROR, "Can't add SDP to response, since we have no RTP session allocated. Call-ID %s\n", p->callid);
|
||||
if (reliable && !p->pendinginvite)
|
||||
@@ -7013,7 +6974,7 @@
|
||||
add_header(&req, "X-asterisk-Info", "SIP re-invite (External RTP bridge)");
|
||||
if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY))
|
||||
append_history(p, "ReInv", "Re-invite sent");
|
||||
- add_sdp(&req, p);
|
||||
+ add_sdp(&req, p, 1, 0);
|
||||
/* Use this as the basis */
|
||||
initialize_initreq(p, &req);
|
||||
p->lastinvite = p->ocseq;
|
||||
@@ -7035,8 +6996,8 @@
|
||||
add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
|
||||
if (sipdebug)
|
||||
add_header(&req, "X-asterisk-info", "SIP re-invite (T38 switchover)");
|
||||
- ast_udptl_offered_from_local(p->udptl, 1);
|
||||
- add_t38_sdp(&req, p);
|
||||
+ add_sdp(&req, p, 0, 1);
|
||||
+
|
||||
/* Use this as the basis */
|
||||
initialize_initreq(p, &req);
|
||||
ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Change direction of this dialog */
|
||||
@@ -7362,13 +7323,13 @@
|
||||
ast_channel_unlock(chan);
|
||||
}
|
||||
if (sdp) {
|
||||
- if (p->udptl && (p->t38.state == T38_LOCAL_DIRECT || p->t38.state == T38_LOCAL_REINVITE)) {
|
||||
+ if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) {
|
||||
ast_udptl_offered_from_local(p->udptl, 1);
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "T38 is in state %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
|
||||
- add_t38_sdp(&req, p);
|
||||
+ add_sdp(&req, p, 0, 1);
|
||||
} else if (p->rtp)
|
||||
- add_sdp(&req, p);
|
||||
+ add_sdp(&req, p, 1, 0);
|
||||
} else {
|
||||
add_header_contentLength(&req, 0);
|
||||
}
|
||||
@@ -12506,11 +12467,6 @@
|
||||
ast_rtp_set_rtptimers_onhold(p->rtp);
|
||||
if (p->vrtp)
|
||||
ast_rtp_set_rtptimers_onhold(p->vrtp); /* Turn off RTP timers while we send fax */
|
||||
- } else if (p->t38.state == T38_DISABLED && bridgepeer && (bridgepvt->t38.state == T38_ENABLED)) {
|
||||
- ast_log(LOG_WARNING, "RTP re-invite after T38 session not handled yet !\n");
|
||||
- /* Insted of this we should somehow re-invite the other side of the bridge to RTP */
|
||||
- /* XXXX Should we really destroy this session here, without any response at all??? */
|
||||
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
|
||||
}
|
||||
} else {
|
||||
if (option_debug > 1)
|
||||
@@ -12533,7 +12489,7 @@
|
||||
ast_log(LOG_DEBUG,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
|
||||
}
|
||||
}
|
||||
- if ((p->t38.state == T38_LOCAL_REINVITE) || (p->t38.state == T38_LOCAL_DIRECT)) {
|
||||
+ if (p->t38.state == T38_LOCAL_REINVITE) {
|
||||
/* If there was T38 reinvite and we are supposed to answer with 200 OK than this should set us to T38 negotiated mode */
|
||||
p->t38.state = T38_ENABLED;
|
||||
if (option_debug)
|
||||
@@ -12643,21 +12599,7 @@
|
||||
/* While figuring that out, hangup the call */
|
||||
if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE))
|
||||
ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
|
||||
- ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
|
||||
- } else if (p->udptl && p->t38.state == T38_LOCAL_DIRECT) {
|
||||
- /* We tried to send T.38 out in an initial INVITE and the remote side rejected it,
|
||||
- right now we can't fall back to audio so totally abort.
|
||||
- */
|
||||
- p->t38.state = T38_DISABLED;
|
||||
- /* Try to reset RTP timers */
|
||||
- ast_rtp_set_rtptimers_onhold(p->rtp);
|
||||
- ast_log(LOG_ERROR, "Got error on T.38 initial invite. Bailing out.\n");
|
||||
-
|
||||
- /* The dialog is now terminated */
|
||||
- if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE))
|
||||
- ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
|
||||
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
|
||||
- sip_alreadygone(p);
|
||||
} else {
|
||||
/* We can't set up this call, so give up */
|
||||
if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE))
|
||||
@@ -14817,34 +14759,9 @@
|
||||
ast_log(LOG_DEBUG,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
|
||||
}
|
||||
} else if (p->t38.state == T38_DISABLED) { /* Channel doesn't have T38 offered or enabled */
|
||||
- int sendok = TRUE;
|
||||
-
|
||||
- /* If we are bridged to a channel that has T38 enabled than this is a case of RTP re-invite after T38 session */
|
||||
- /* so handle it here (re-invite other party to RTP) */
|
||||
- struct ast_channel *bridgepeer = NULL;
|
||||
- struct sip_pvt *bridgepvt = NULL;
|
||||
- if ((bridgepeer = ast_bridged_channel(p->owner))) {
|
||||
- if ((bridgepeer->tech == &sip_tech || bridgepeer->tech == &sip_tech_info) && !ast_check_hangup(bridgepeer)) {
|
||||
- bridgepvt = (struct sip_pvt*)bridgepeer->tech_pvt;
|
||||
- /* Does the bridged peer have T38 ? */
|
||||
- if (bridgepvt->t38.state == T38_ENABLED) {
|
||||
- ast_log(LOG_WARNING, "RTP re-invite after T38 session not handled yet !\n");
|
||||
- /* Insted of this we should somehow re-invite the other side of the bridge to RTP */
|
||||
- if (ast_test_flag(req, SIP_PKT_IGNORE))
|
||||
- transmit_response(p, "488 Not Acceptable Here (unsupported)", req);
|
||||
- else
|
||||
- transmit_response_reliable(p, "488 Not Acceptable Here (unsupported)", req);
|
||||
- sendok = FALSE;
|
||||
- }
|
||||
- /* No bridged peer with T38 enabled*/
|
||||
- }
|
||||
- }
|
||||
- /* Respond to normal re-invite */
|
||||
- if (sendok) {
|
||||
- /* If this is not a re-invite or something to ignore - it's critical */
|
||||
- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
|
||||
- transmit_response_with_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (ast_test_flag(req, SIP_PKT_IGNORE) ? XMIT_UNRELIABLE : XMIT_CRITICAL)));
|
||||
- }
|
||||
+ /* If this is not a re-invite or something to ignore - it's critical */
|
||||
+ ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
|
||||
+ transmit_response_with_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (ast_test_flag(req, SIP_PKT_IGNORE) ? XMIT_UNRELIABLE : XMIT_CRITICAL)));
|
||||
}
|
||||
p->invitestate = INV_TERMINATED;
|
||||
break;
|
||||
@ -0,0 +1,11 @@
|
||||
--- channels/chan_sip.c (revisión: 195447)
|
||||
+++ channels/chan_sip.c (revisión: 195448)
|
||||
@@ -18528,7 +18528,7 @@
|
||||
return -1;
|
||||
|
||||
/* Disable early RTP bridge */
|
||||
- if (chan->_state != AST_STATE_UP && !global_directrtpsetup) /* We are in early state */
|
||||
+ if (!ast_bridged_channel(chan) && !global_directrtpsetup) /* We are in early state */
|
||||
return 0;
|
||||
|
||||
ast_mutex_lock(&p->lock);
|
||||
@ -0,0 +1,98 @@
|
||||
--- channels/chan_sip.c (revisión: 185844)
|
||||
+++ channels/chan_sip.c (revisión: 185845)
|
||||
@@ -998,9 +998,12 @@
|
||||
char lastmsg[256]; /*!< Last Message sent/received */
|
||||
int amaflags; /*!< AMA Flags */
|
||||
int pendinginvite; /*!< Any pending INVITE or state NOTIFY (in subscribe pvt's) ? (seqno of this) */
|
||||
+ int glareinvite; /*!< A invite received while a pending invite is already present is stored here. Its seqno is the
|
||||
+ value. Since this glare invite's seqno is not the same as the pending invite's, it must be
|
||||
+ held in order to properly process acknowledgements for our 491 response. */
|
||||
struct sip_request initreq; /*!< Request that opened the latest transaction
|
||||
within this SIP dialog */
|
||||
-
|
||||
+
|
||||
int maxtime; /*!< Max time for first response */
|
||||
int initid; /*!< Auto-congest ID if appropriate (scheduler) */
|
||||
int waitid; /*!< Wait ID for scheduler after 491 or other delays */
|
||||
@@ -12313,7 +12316,7 @@
|
||||
ast_log(LOG_DEBUG, "Sending pending reinvite on '%s'\n", p->callid);
|
||||
/* Didn't get to reinvite yet, so do it now */
|
||||
transmit_reinvite_with_sdp(p);
|
||||
- ast_clear_flag(&p->flags[0], SIP_NEEDREINVITE);
|
||||
+ ast_clear_flag(&p->flags[0], SIP_NEEDREINVITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12322,12 +12325,15 @@
|
||||
to avoid race conditions between asterisk servers.
|
||||
Called from the scheduler.
|
||||
*/
|
||||
-static int sip_reinvite_retry(const void *data)
|
||||
+static int sip_reinvite_retry(const void *data)
|
||||
{
|
||||
struct sip_pvt *p = (struct sip_pvt *) data;
|
||||
|
||||
- ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
|
||||
+ ast_mutex_lock(&p->lock); /* called from schedule thread which requires a lock */
|
||||
+ ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
|
||||
p->waitid = -1;
|
||||
+ check_pendings(p);
|
||||
+ ast_mutex_unlock(&p->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12340,7 +12346,7 @@
|
||||
int xmitres = 0;
|
||||
int reinvite = (p->owner && p->owner->_state == AST_STATE_UP);
|
||||
struct ast_channel *bridgepeer = NULL;
|
||||
-
|
||||
+
|
||||
if (option_debug > 3) {
|
||||
if (reinvite)
|
||||
ast_log(LOG_DEBUG, "SIP response %d to RE-invite on %s call %s\n", resp, outgoing ? "outgoing" : "incoming", p->callid);
|
||||
@@ -12617,13 +12623,20 @@
|
||||
if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE)) {
|
||||
if (p->owner->_state != AST_STATE_UP) {
|
||||
ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
|
||||
- ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
|
||||
+ ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
|
||||
} else {
|
||||
/* This is a re-invite that failed.
|
||||
* Reset the flag after a while
|
||||
*/
|
||||
- int wait = 3 + ast_random() % 5;
|
||||
- p->waitid = ast_sched_add(sched, wait, sip_reinvite_retry, p);
|
||||
+ int wait;
|
||||
+ /* RFC 3261, if owner of call, wait between 2.1 to 4 seconds,
|
||||
+ * if not owner of call, wait 0 to 2 seconds */
|
||||
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_OUTGOING_CALL)) {
|
||||
+ wait = 2100 + ast_random() % 2000;
|
||||
+ } else {
|
||||
+ wait = ast_random() % 2000;
|
||||
+ }
|
||||
+ p->waitid = ast_sched_add(sched, wait, sip_reinvite_retry, p);
|
||||
if (option_debug > 2)
|
||||
ast_log(LOG_DEBUG, "Reinvite race. Waiting %d secs before retry\n", wait);
|
||||
}
|
||||
@@ -14322,6 +14335,7 @@
|
||||
|
||||
if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->pendinginvite) {
|
||||
/* We already have a pending invite. Sorry. You are on hold. */
|
||||
+ p->glareinvite = seqno;
|
||||
transmit_response_reliable(p, "491 Request Pending", req);
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Got INVITE on call where we already have pending INVITE, deferring that - %s\n", p->callid);
|
||||
@@ -15972,8 +15986,12 @@
|
||||
if (find_sdp(req)) {
|
||||
if (process_sdp(p, req))
|
||||
return -1;
|
||||
- }
|
||||
+ }
|
||||
check_pendings(p);
|
||||
+ } else if (p->glareinvite == seqno) {
|
||||
+ /* handle ack for the 491 pending send for glareinvite */
|
||||
+ p->glareinvite = 0;
|
||||
+ __sip_ack(p, seqno, 1, 0);
|
||||
}
|
||||
/* Got an ACK that we did not match. Ignore silently */
|
||||
if (!p->lastinvite && ast_strlen_zero(p->randdata))
|
||||
@ -0,0 +1,36 @@
|
||||
--- channels/chan_sip.c (revisión: 194483)
|
||||
+++ channels/chan_sip.c (revisión: 194484)
|
||||
@@ -14395,7 +14395,7 @@
|
||||
}
|
||||
|
||||
/* Check if this is a loop */
|
||||
- if (ast_test_flag(&p->flags[0], SIP_OUTGOING) && p->owner && (p->owner->_state != AST_STATE_UP)) {
|
||||
+ if (ast_test_flag(&p->flags[0], SIP_OUTGOING) && p->owner && (p->invitestate != INV_TERMINATED && p->invitestate != INV_CONFIRMED)) {
|
||||
/* This is a call to ourself. Send ourselves an error code and stop
|
||||
processing immediately, as SIP really has no good mechanism for
|
||||
being able to call yourself */
|
||||
@@ -14824,9 +14824,21 @@
|
||||
p->invitestate = INV_PROCEEDING;
|
||||
break;
|
||||
case AST_STATE_RINGING:
|
||||
- transmit_response(p, "180 Ringing", req);
|
||||
- p->invitestate = INV_PROCEEDING;
|
||||
- break;
|
||||
+ if (reinvite && (p->invitestate == INV_TERMINATED || p->invitestate == INV_CONFIRMED)) {
|
||||
+ /* If these conditions are true, and the channel is still in the 'ringing'
|
||||
+ * state, then this likely means that we have a situation where the initial
|
||||
+ * INVITE transaction has completed *but* the channel's state has not yet been
|
||||
+ * changed to UP. The reason this could happen is if the reinvite is received
|
||||
+ * on the SIP socket prior to an application calling ast_read on this channel
|
||||
+ * to read the answer frame we earlier queued on it. In this case, the reinvite
|
||||
+ * is completely legitimate so we need to handle this the same as if the channel
|
||||
+ * were already UP. Thus we are purposely falling through to the AST_STATE_UP case.
|
||||
+ */
|
||||
+ } else {
|
||||
+ transmit_response(p, "180 Ringing", req);
|
||||
+ p->invitestate = INV_PROCEEDING;
|
||||
+ break;
|
||||
+ }
|
||||
case AST_STATE_UP:
|
||||
if (option_debug > 1)
|
||||
ast_log(LOG_DEBUG, "%s: This call is UP.... \n", c->name);
|
||||
@ -0,0 +1,16 @@
|
||||
--- channels/chan_sip.c (revision 196376)
|
||||
+++ channels/chan_sip.c (working copy)
|
||||
@@ -17693,8 +17693,11 @@
|
||||
* address listed on the entry (or if it's 'dynamic'), then we need to
|
||||
* parse the entry to obtain the IP address, so a dynamic host can be
|
||||
* contacted immediately after reload (as opposed to waiting for it to
|
||||
- * register once again). */
|
||||
- __set_address_from_contact(fullcontact, &peer->addr);
|
||||
+ * register once again). But if we have an address for this peer and NAT was
|
||||
+ * specified, use that address instead. */
|
||||
+ if (!ast_test_flag(&peer->flags[0], SIP_NAT_ROUTE) || !peer->addr.sin_addr.s_addr) {
|
||||
+ __set_address_from_contact(fullcontact, &peer->addr);
|
||||
+ }
|
||||
}
|
||||
|
||||
if (!ast_test_flag(&global_flags[1], SIP_PAGE2_IGNOREREGEXPIRE) && ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC) && realtime) {
|
||||
@ -0,0 +1,33 @@
|
||||
--- channels/chan_sip.c (revisión: 188834)
|
||||
+++ channels/chan_sip.c (revisión: 188835)
|
||||
@@ -8082,7 +8082,7 @@
|
||||
static void destroy_association(struct sip_peer *peer)
|
||||
{
|
||||
if (!ast_test_flag(&global_flags[1], SIP_PAGE2_IGNOREREGEXPIRE)) {
|
||||
- if (ast_test_flag(&peer->flags[1], SIP_PAGE2_RT_FROMCONTACT)) {
|
||||
+ if (ast_test_flag(&peer->flags[1], SIP_PAGE2_RT_FROMCONTACT) && ast_test_flag(&global_flags[1], SIP_PAGE2_RTUPDATE)) {
|
||||
ast_update_realtime("sippeers", "name", peer->name, "fullcontact", "", "ipaddr", "", "port", "", "regseconds", "0", "username", "", "regserver", "", NULL);
|
||||
ast_update_realtime("sippeers", "name", peer->name, "lastms", "", NULL);
|
||||
} else
|
||||
@@ -12983,7 +12983,9 @@
|
||||
ast_log(LOG_NOTICE, "Peer '%s' is now %s. (%dms / %dms)\n",
|
||||
peer->name, s, pingtime, peer->maxms);
|
||||
ast_device_state_changed("SIP/%s", peer->name);
|
||||
- ast_update_realtime("sippeers", "name", peer->name, "lastms", str_lastms, NULL);
|
||||
+ if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTUPDATE)) {
|
||||
+ ast_update_realtime("sippeers", "name", peer->name, "lastms", str_lastms, NULL);
|
||||
+ }
|
||||
manager_event(EVENT_FLAG_SYSTEM, "PeerStatus",
|
||||
"Peer: SIP/%s\r\nPeerStatus: %s\r\nTime: %d\r\n",
|
||||
peer->name, s, pingtime);
|
||||
@@ -16607,7 +16609,9 @@
|
||||
peer->pokeexpire = -1;
|
||||
if (peer->lastms > -1) {
|
||||
ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE! Last qualify: %d\n", peer->name, peer->lastms);
|
||||
- ast_update_realtime("sippeers", "name", peer->name, "lastms", "-1", NULL);
|
||||
+ if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTUPDATE)) {
|
||||
+ ast_update_realtime("sippeers", "name", peer->name, "lastms", "-1", NULL);
|
||||
+ }
|
||||
manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unreachable\r\nTime: %d\r\n", peer->name, -1);
|
||||
}
|
||||
if (peer->call)
|
||||
@ -0,0 +1,36 @@
|
||||
--- channels/chan_sip.c (revisión: 187483)
|
||||
+++ channels/chan_sip.c (revisión: 187484)
|
||||
@@ -14435,13 +14435,26 @@
|
||||
}
|
||||
|
||||
if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->pendinginvite) {
|
||||
- /* We already have a pending invite. Sorry. You are on hold. */
|
||||
- p->glareinvite = seqno;
|
||||
- transmit_response_reliable(p, "491 Request Pending", req);
|
||||
- if (option_debug)
|
||||
- ast_log(LOG_DEBUG, "Got INVITE on call where we already have pending INVITE, deferring that - %s\n", p->callid);
|
||||
- /* Don't destroy dialog here */
|
||||
- return 0;
|
||||
+ if (!ast_test_flag(&p->flags[0], SIP_OUTGOING) && ast_test_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) {
|
||||
+ /* We have received a reINVITE on an incoming call to which we have sent a 200 OK but not yet received
|
||||
+ * an ACK. According to RFC 5407, Section 3.1.4, the proper way to handle this race condition is to accept
|
||||
+ * the reINVITE since we have established a dialog.
|
||||
+ */
|
||||
+
|
||||
+ /* Note that this will both clear the pendinginvite flag and cancel the
|
||||
+ * retransmission of the 200 OK. Basically, we're accepting this reINVITE as both an ACK
|
||||
+ * and a reINVITE in one request.
|
||||
+ */
|
||||
+ __sip_ack(p, p->lastinvite, FLAG_RESPONSE, 0);
|
||||
+ } else {
|
||||
+ /* We already have a pending invite. Sorry. You are on hold. */
|
||||
+ p->glareinvite = seqno;
|
||||
+ transmit_response_reliable(p, "491 Request Pending", req);
|
||||
+ if (option_debug)
|
||||
+ ast_log(LOG_DEBUG, "Got INVITE on call where we already have pending INVITE, deferring that - %s\n", p->callid);
|
||||
+ /* Don't destroy dialog here */
|
||||
+ return 0;
|
||||
+ }
|
||||
}
|
||||
|
||||
p_replaces = get_header(req, "Replaces");
|
||||
@ -0,0 +1,31 @@
|
||||
--- channels/chan_sip.c (revisión: 197465)
|
||||
+++ channels/chan_sip.c (revisión: 197466)
|
||||
@@ -803,6 +803,7 @@
|
||||
#define SIP_PAGE2_OUTGOING_CALL (1 << 27) /*!< 27: Is this an outgoing call? */
|
||||
#define SIP_PAGE2_UDPTL_DESTINATION (1 << 28) /*!< 28: Use source IP of RTP as destination if NAT is enabled */
|
||||
#define SIP_PAGE2_DIALOG_ESTABLISHED (1 << 29) /*!< 29: Has a dialog been established? */
|
||||
+#define SIP_PAGE2_RPORT_PRESENT (1 << 30) /*!< 30: Was rport received in the Via header? */
|
||||
|
||||
#define SIP_PAGE2_FLAGS_TO_COPY \
|
||||
(SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_VIDEOSUPPORT | \
|
||||
@@ -9580,7 +9581,7 @@
|
||||
/* Check for rport */
|
||||
c = strstr(via, ";rport");
|
||||
if (c && (c[6] != '=')) /* rport query, not answer */
|
||||
- ast_set_flag(&p->flags[0], SIP_NAT_ROUTE);
|
||||
+ ast_set_flag(&p->flags[1], SIP_PAGE2_RPORT_PRESENT);
|
||||
|
||||
c = strchr(via, ';');
|
||||
if (c)
|
||||
@@ -10013,6 +10014,11 @@
|
||||
|
||||
if (user)
|
||||
ASTOBJ_UNREF(user, sip_destroy_user);
|
||||
+
|
||||
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_RPORT_PRESENT)) {
|
||||
+ ast_set_flag(&p->flags[0], SIP_NAT_ROUTE);
|
||||
+ }
|
||||
+
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
--- channels/chan_sip.c (revisión: 184564)
|
||||
+++ channels/chan_sip.c (revisión: 184565)
|
||||
@@ -2947,7 +2947,9 @@
|
||||
ASTOBJ_UNREF(p, sip_destroy_peer);
|
||||
return res;
|
||||
}
|
||||
-
|
||||
+
|
||||
+ do_setnat(dialog, ast_test_flag(&dialog->flags[0], SIP_NAT) & SIP_NAT_ROUTE);
|
||||
+
|
||||
ast_string_field_set(dialog, tohost, peer);
|
||||
|
||||
if (sin) {
|
||||
@ -0,0 +1,21 @@
|
||||
--- main/term.c 2006-12-27 19:06:56.000000000 -0300
|
||||
+++ main/term.c 2009-11-06 19:06:43.000000000 -0300
|
||||
@@ -80,8 +80,17 @@
|
||||
int termfd = -1, parseokay = 0, i;
|
||||
|
||||
if (!term)
|
||||
+#ifdef linux
|
||||
+ term = "linux";
|
||||
+#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
|
||||
+ term = "cons25";
|
||||
+#elif defined(SOLARIS)
|
||||
+ term = "sun-color";
|
||||
+#else
|
||||
return 0;
|
||||
- if (!ast_opt_console || ast_opt_no_color || !ast_opt_no_fork)
|
||||
+#endif
|
||||
+
|
||||
+ if (ast_opt_no_color)
|
||||
return 0;
|
||||
|
||||
for (i=0 ;; i++) {
|
||||
@ -0,0 +1,159 @@
|
||||
--- pbx/pbx_config.c (revisión: 191095)
|
||||
+++ pbx/pbx_config.c (revisión: 191096)
|
||||
@@ -620,72 +620,12 @@
|
||||
return ret;
|
||||
}
|
||||
|
||||
-#define BROKEN_READLINE 1
|
||||
-
|
||||
-#ifdef BROKEN_READLINE
|
||||
-/*
|
||||
- * There is one funny thing, when you have word like 300@ and you hit
|
||||
- * <tab>, you arguments will like as your word is '300 ', so it '@'
|
||||
- * characters acts sometimes as word delimiter and sometimes as a part
|
||||
- * of word
|
||||
- *
|
||||
- * This fix function, allocates new word variable and store here every
|
||||
- * time xxx@yyy always as one word and correct pos is set too
|
||||
- *
|
||||
- * It's ugly, I know, but I'm waiting for Mark suggestion if upper is
|
||||
- * bug or feature ...
|
||||
- */
|
||||
-static int fix_complete_args(const char *line, char **word, int *pos)
|
||||
-{
|
||||
- char *_line, *_strsep_line, *_previous_word = NULL, *_word = NULL;
|
||||
- int words = 0;
|
||||
-
|
||||
- _line = strdup(line);
|
||||
-
|
||||
- _strsep_line = _line;
|
||||
- while (_strsep_line) {
|
||||
- _previous_word = _word;
|
||||
- _word = strsep(&_strsep_line, " ");
|
||||
-
|
||||
- if (_word && strlen(_word)) words++;
|
||||
- }
|
||||
-
|
||||
-
|
||||
- if (_word || _previous_word) {
|
||||
- if (_word) {
|
||||
- if (!strlen(_word)) words++;
|
||||
- *word = strdup(_word);
|
||||
- } else
|
||||
- *word = strdup(_previous_word);
|
||||
- *pos = words - 1;
|
||||
- free(_line);
|
||||
- return 0;
|
||||
- }
|
||||
-
|
||||
- free(_line);
|
||||
- return -1;
|
||||
-}
|
||||
-#endif /* BROKEN_READLINE */
|
||||
-
|
||||
static char *complete_context_remove_extension_deprecated(const char *line, const char *word, int pos,
|
||||
int state)
|
||||
{
|
||||
char *ret = NULL;
|
||||
int which = 0;
|
||||
|
||||
-#ifdef BROKEN_READLINE
|
||||
- char *word2;
|
||||
- /*
|
||||
- * Fix arguments, *word is a new allocated structure, REMEMBER to
|
||||
- * free *word when you want to return from this function ...
|
||||
- */
|
||||
- if (fix_complete_args(line, &word2, &pos)) {
|
||||
- ast_log(LOG_ERROR, "Out of free memory\n");
|
||||
- return NULL;
|
||||
- }
|
||||
- word = word2;
|
||||
-#endif
|
||||
-
|
||||
if (pos == 2) { /* 'remove extension _X_' (exten/cid@context ... */
|
||||
struct ast_context *c = NULL;
|
||||
char *context = NULL, *exten = NULL, *cid = NULL;
|
||||
@@ -694,9 +634,6 @@
|
||||
int lcid = 0; /* length of cid */
|
||||
|
||||
lc = split_ec(word, &exten, &context, &cid);
|
||||
-#ifdef BROKEN_READLINE
|
||||
- free(word2);
|
||||
-#endif
|
||||
if (lc) /* error */
|
||||
return NULL;
|
||||
le = strlen(exten);
|
||||
@@ -762,7 +699,11 @@
|
||||
*p = '\0';
|
||||
le = strlen(exten);
|
||||
lc = strlen(context);
|
||||
- lcid = strlen(cid);
|
||||
+ if (cid == NULL) {
|
||||
+ lcid = 0;
|
||||
+ } else {
|
||||
+ lcid = strlen(cid);
|
||||
+ }
|
||||
len = strlen(word);
|
||||
if (le == 0 || lc == 0)
|
||||
goto error3;
|
||||
@@ -806,9 +747,6 @@
|
||||
if (exten)
|
||||
free(exten);
|
||||
}
|
||||
-#ifdef BROKEN_READLINE
|
||||
- free(word2);
|
||||
-#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -818,19 +756,6 @@
|
||||
char *ret = NULL;
|
||||
int which = 0;
|
||||
|
||||
-#ifdef BROKEN_READLINE
|
||||
- char *word2;
|
||||
- /*
|
||||
- * Fix arguments, *word is a new allocated structure, REMEMBER to
|
||||
- * free *word when you want to return from this function ...
|
||||
- */
|
||||
- if (fix_complete_args(line, &word2, &pos)) {
|
||||
- ast_log(LOG_ERROR, "Out of free memory\n");
|
||||
- return NULL;
|
||||
- }
|
||||
- word = word2;
|
||||
-#endif
|
||||
-
|
||||
if (pos == 3) { /* 'dialplan remove extension _X_' (exten@context ... */
|
||||
struct ast_context *c = NULL;
|
||||
char *context = NULL, *exten = NULL, *cid = NULL;
|
||||
@@ -840,9 +765,6 @@
|
||||
|
||||
lc = split_ec(word, &exten, &context, &cid);
|
||||
if (lc) { /* error */
|
||||
-#ifdef BROKEN_READLINE
|
||||
- free(word2);
|
||||
-#endif
|
||||
return NULL;
|
||||
}
|
||||
le = strlen(exten);
|
||||
@@ -888,10 +810,6 @@
|
||||
if (e) /* got a match */
|
||||
break;
|
||||
}
|
||||
-#ifdef BROKEN_READLINE
|
||||
- free(word2);
|
||||
-#endif
|
||||
-
|
||||
ast_unlock_contexts();
|
||||
error2:
|
||||
if (exten)
|
||||
@@ -954,9 +872,6 @@
|
||||
error3:
|
||||
if (exten)
|
||||
free(exten);
|
||||
-#ifdef BROKEN_READLINE
|
||||
- free(word2);
|
||||
-#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
--- res/res_smdi.c (revisión: 198310)
|
||||
+++ res/res_smdi.c (revisión: 198311)
|
||||
@@ -309,8 +309,10 @@
|
||||
switch (type) {
|
||||
case SMDI_MWI:
|
||||
ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
|
||||
+ break;
|
||||
case SMDI_MD:
|
||||
ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,231 @@
|
||||
--- funcs/func_devstate.c 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ funcs/func_devstate.c 2010-04-15 22:42:27.000000000 +0200
|
||||
@@ -0,0 +1,228 @@
|
||||
+/*
|
||||
+ * Asterisk -- An open source telephony toolkit.
|
||||
+ *
|
||||
+ * Copyright (C) 2007, Digium, Inc.
|
||||
+ *
|
||||
+ * Russell Bryant <russell@digium.com>
|
||||
+ *
|
||||
+ * See http://www.asterisk.org for more information about
|
||||
+ * the Asterisk project. Please do not directly contact
|
||||
+ * any of the maintainers of this project for assistance;
|
||||
+ * the project provides a web site, mailing lists and IRC
|
||||
+ * channels for your use.
|
||||
+ *
|
||||
+ * This program is free software, distributed under the terms of
|
||||
+ * the GNU General Public License Version 2. See the LICENSE file
|
||||
+ * at the top of the source tree.
|
||||
+ */
|
||||
+
|
||||
+/*! \file
|
||||
+ *
|
||||
+ * \brief Manually controlled blinky lights
|
||||
+ *
|
||||
+ * \author Russell Bryant <russell@digium.com>
|
||||
+ *
|
||||
+ * \ingroup functions
|
||||
+ *
|
||||
+ * \note Props go out to Ahrimanes in #asterisk for requesting this at 4:30 AM
|
||||
+ * when I couldn't sleep. :)
|
||||
+ */
|
||||
+
|
||||
+#include "asterisk.h"
|
||||
+
|
||||
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 4 $")
|
||||
+
|
||||
+#include <stdlib.h>
|
||||
+
|
||||
+#include "asterisk/module.h"
|
||||
+#include "asterisk/channel.h"
|
||||
+#include "asterisk/pbx.h"
|
||||
+#include "asterisk/utils.h"
|
||||
+#include "asterisk/linkedlists.h"
|
||||
+#include "asterisk/devicestate.h"
|
||||
+#include "asterisk/cli.h"
|
||||
+#include "asterisk/astdb.h"
|
||||
+
|
||||
+static const char astdb_family[] = "CustomDevstate";
|
||||
+
|
||||
+static const char *ast_devstate_str(int state)
|
||||
+{
|
||||
+ const char *res = "UNKNOWN";
|
||||
+
|
||||
+ switch (state) {
|
||||
+ case AST_DEVICE_UNKNOWN:
|
||||
+ break;
|
||||
+ case AST_DEVICE_NOT_INUSE:
|
||||
+ res = "NOT_INUSE";
|
||||
+ break;
|
||||
+ case AST_DEVICE_INUSE:
|
||||
+ res = "INUSE";
|
||||
+ break;
|
||||
+ case AST_DEVICE_BUSY:
|
||||
+ res = "BUSY";
|
||||
+ break;
|
||||
+ case AST_DEVICE_INVALID:
|
||||
+ res = "INVALID";
|
||||
+ break;
|
||||
+ case AST_DEVICE_UNAVAILABLE:
|
||||
+ res = "UNAVAILABLE";
|
||||
+ break;
|
||||
+ case AST_DEVICE_RINGING:
|
||||
+ res = "RINGING";
|
||||
+ break;
|
||||
+ case AST_DEVICE_RINGINUSE:
|
||||
+ res = "RINGINUSE";
|
||||
+ break;
|
||||
+ case AST_DEVICE_ONHOLD:
|
||||
+ res = "ONHOLD";
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return res;
|
||||
+}
|
||||
+
|
||||
+static int ast_devstate_val(const char *val)
|
||||
+{
|
||||
+ if (!strcasecmp(val, "NOT_INUSE"))
|
||||
+ return AST_DEVICE_NOT_INUSE;
|
||||
+ else if (!strcasecmp(val, "INUSE"))
|
||||
+ return AST_DEVICE_INUSE;
|
||||
+ else if (!strcasecmp(val, "BUSY"))
|
||||
+ return AST_DEVICE_BUSY;
|
||||
+ else if (!strcasecmp(val, "INVALID"))
|
||||
+ return AST_DEVICE_INVALID;
|
||||
+ else if (!strcasecmp(val, "UNAVAILABLE"))
|
||||
+ return AST_DEVICE_UNAVAILABLE;
|
||||
+ else if (!strcasecmp(val, "RINGING"))
|
||||
+ return AST_DEVICE_RINGING;
|
||||
+ else if (!strcasecmp(val, "RINGINUSE"))
|
||||
+ return AST_DEVICE_RINGINUSE;
|
||||
+ else if (!strcasecmp(val, "ONHOLD"))
|
||||
+ return AST_DEVICE_ONHOLD;
|
||||
+
|
||||
+ return AST_DEVICE_UNKNOWN;
|
||||
+}
|
||||
+
|
||||
+static int devstate_read(struct ast_channel *chan, char *cmd, char *data,
|
||||
+ char *buf, size_t len)
|
||||
+{
|
||||
+ ast_copy_string(buf, ast_devstate_str(ast_device_state(data)), len);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int devstate_write(struct ast_channel *chan, char *function,
|
||||
+ char *data, const char *value)
|
||||
+{
|
||||
+ size_t len = strlen("Custom:");
|
||||
+
|
||||
+ if (strncasecmp(data, "Custom:", len)) {
|
||||
+ ast_log(LOG_WARNING, "The DEVSTATE function can only be used to set 'Custom:' device state!\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+ data += len;
|
||||
+ if (ast_strlen_zero(data)) {
|
||||
+ ast_log(LOG_WARNING, "DEVSTATE function called with no custom device name!\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ ast_db_put(astdb_family, data, (char *) value);
|
||||
+
|
||||
+ ast_device_state_changed("Custom:%s", data);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int custom_devstate_callback(const char *data)
|
||||
+{
|
||||
+ char buf[256] = "";
|
||||
+
|
||||
+ ast_db_get(astdb_family, data, buf, sizeof(buf));
|
||||
+
|
||||
+ return ast_devstate_val(buf);
|
||||
+}
|
||||
+
|
||||
+static int cli_funcdevstate_list(int fd, int argc, char *argv[])
|
||||
+{
|
||||
+ struct ast_db_entry *db_entry, *db_tree;
|
||||
+
|
||||
+ if (argc != 2)
|
||||
+ return RESULT_SHOWUSAGE;
|
||||
+
|
||||
+ ast_cli(fd, "\n"
|
||||
+ "---------------------------------------------------------------------\n"
|
||||
+ "--- Custom Device States --------------------------------------------\n"
|
||||
+ "---------------------------------------------------------------------\n"
|
||||
+ "---\n");
|
||||
+
|
||||
+ db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
|
||||
+ for (; db_entry; db_entry = db_entry->next) {
|
||||
+ const char *dev_name = strrchr(db_entry->key, '/') + 1;
|
||||
+ if (dev_name <= (const char *) 1)
|
||||
+ continue;
|
||||
+ ast_cli(fd, "--- name: 'custom:%s' state: '%s'\n"
|
||||
+ "---\n", dev_name, db_entry->data);
|
||||
+ }
|
||||
+ ast_db_freetree(db_tree);
|
||||
+ db_tree = NULL;
|
||||
+
|
||||
+ ast_cli(fd,
|
||||
+ "---------------------------------------------------------------------\n"
|
||||
+ "---------------------------------------------------------------------\n"
|
||||
+ "\n");
|
||||
+
|
||||
+ return RESULT_SUCCESS;
|
||||
+}
|
||||
+
|
||||
+static struct ast_cli_entry cli_funcdevstate[] = {
|
||||
+ { { "funcdevstate", "list", }, cli_funcdevstate_list, NULL, NULL },
|
||||
+};
|
||||
+
|
||||
+static struct ast_custom_function devstate_function = {
|
||||
+ .name = "DEVSTATE",
|
||||
+ .synopsis = "Get or Set a device state",
|
||||
+ .syntax = "DEVSTATE(device)",
|
||||
+ .desc =
|
||||
+ " The DEVSTATE function can be used to retrieve the device state from any\n"
|
||||
+ "device state provider. For example:\n"
|
||||
+ " NoOp(SIP/mypeer has state ${DEVSTATE(SIP/mypeer)})\n"
|
||||
+ " NoOp(Conference number 1234 has state ${DEVSTATE(MeetMe:1234)})\n"
|
||||
+ "\n"
|
||||
+ " The DEVSTATE function can also be used to set custom device state from\n"
|
||||
+ "the dialplan. The \"Custom:\" prefix must be used. For example:\n"
|
||||
+ " Set(DEVSTATE(Custom:lamp1)=BUSY)\n"
|
||||
+ " Set(DEVSTATE(Custom:lamp2)=NOT_INUSE)\n"
|
||||
+ "You can subscribe to the status of a custom device state using a hint in\n"
|
||||
+ "the dialplan:\n"
|
||||
+ " exten => 1234,hint,Custom:lamp1\n"
|
||||
+ "\n"
|
||||
+ " The possible values for both uses of this function are:\n"
|
||||
+ "UNKNOWN | NOT_INUSE | INUSE | BUSY | INVALID | UNAVAILABLE | RINGING\n"
|
||||
+ "RINGINUSE | ONHOLD\n",
|
||||
+ .read = devstate_read,
|
||||
+ .write = devstate_write,
|
||||
+};
|
||||
+
|
||||
+static int unload_module(void)
|
||||
+{
|
||||
+ int res = 0;
|
||||
+
|
||||
+ res |= ast_custom_function_unregister(&devstate_function);
|
||||
+ ast_devstate_prov_del("Custom");
|
||||
+ ast_cli_unregister_multiple(cli_funcdevstate, ARRAY_LEN(cli_funcdevstate));
|
||||
+
|
||||
+ return res;
|
||||
+}
|
||||
+
|
||||
+static int load_module(void)
|
||||
+{
|
||||
+ int res = 0;
|
||||
+
|
||||
+ res |= ast_custom_function_register(&devstate_function);
|
||||
+ res |= ast_devstate_prov_add("Custom", custom_devstate_callback);
|
||||
+ ast_cli_register_multiple(cli_funcdevstate, ARRAY_LEN(cli_funcdevstate));
|
||||
+
|
||||
+ return res;
|
||||
+}
|
||||
+
|
||||
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Gets or sets a device state in the dialplan");
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,250 @@
|
||||
--- contrib/scripts/live_ast 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ contrib/scripts/live_ast 2010-04-14 19:33:38.000000000 +0200
|
||||
@@ -0,0 +1,247 @@
|
||||
+#!/bin/sh
|
||||
+
|
||||
+# live_ast: run asterisk from a newly-built copy with minimal changes.
|
||||
+
|
||||
+# Copyright (C) 2007 Tzafrir Cohen <tzafrir.cohen@xorcom.com>
|
||||
+#
|
||||
+# This program is free software; you can redistribute it and/or modify
|
||||
+# it under the terms of the GNU General Public License as published by
|
||||
+# the Free Software Foundation; either version 2 of the License, or
|
||||
+# (at your option) any later version.
|
||||
+#
|
||||
+# This program 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 General Public License for more details.
|
||||
+#
|
||||
+# You should have received a copy of the GNU General Public License
|
||||
+# along with this program; if not, write to the Free Software
|
||||
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
+# USA
|
||||
+
|
||||
+# This script allows you to install Asterisk into a subdirectory of
|
||||
+# your source distribution and run it from there.
|
||||
+#
|
||||
+# Example usage:
|
||||
+#
|
||||
+# contrib/scripts/live_ast conf-file # optionally. and now edit live/live.conf
|
||||
+# # edit live/live.conf
|
||||
+# contrib/scripts/live_ast configure
|
||||
+# make
|
||||
+# contrib/scripts/live_ast install
|
||||
+# contrib/scripts/live_ast samples
|
||||
+# contrib/scripts/live_ast run
|
||||
+# contrib/scripts/live_ast run -r
|
||||
+# ./live/asterisk -r # Same as run -r
|
||||
+#
|
||||
+# A standard debugging cycle of a code in a module:
|
||||
+#
|
||||
+# # edit apps/app_skel.c
|
||||
+# contrib/scripts/live_ast install
|
||||
+# contrib/scripts/live_ast run -r
|
||||
+# # and in the CLI:
|
||||
+# module unload app_skel.so
|
||||
+# module load app_skel.so
|
||||
+#
|
||||
+# If you have external scripts that run asterisk, use the script
|
||||
+# live/asterisk that is generated by the 'samples' command. In this case you
|
||||
+# should probably also rem-out the line 'astvarrundir' and maybe also
|
||||
+# 'astetcdir' in live/etc/asterisk.conf .
|
||||
+#
|
||||
+####################### Begin Samples
|
||||
+# optional environment variables. Set those in live/live.conf or in
|
||||
+# your envirnment.
|
||||
+#
|
||||
+# LIVE_AST_LIBPRI_PATH:
|
||||
+# To use a libpri SVN directory (without running 'make install': make include
|
||||
+# a symlink to the current directory:
|
||||
+# ln -s . /path/to/checkout/of/libpri/include
|
||||
+# Be sure to run there 'make'. Then set in live.conf:
|
||||
+#LIVE_AST_LIBPRI_PATH="/path/to/checkout/of/libpri"
|
||||
+#
|
||||
+# LIVE_AST_ZAPTEL_PATH:
|
||||
+# Likewise, the same trick can be used to build vs. a local copy of zaptel:
|
||||
+# ln -s /path/to/checkout/of/zaptel/include .
|
||||
+# ln -s /path/to/checkout/of/zaptel/zaptel .
|
||||
+#LIVE_AST_ZAPTEL_PATH="/path/to/checkout/of/zaptel"
|
||||
+#
|
||||
+#LIVE_AST_DAHDI_PATH="/path/to/dahdi-linux/dir"
|
||||
+#LIVE_AST_DAHDITOOLS_PATH="/path/to/dahdi-tools/dir"
|
||||
+#
|
||||
+# Another alternative for Zaptel is to use the live_zap script included
|
||||
+# with the Zaptel distribution. The following should work after you used
|
||||
+# './live_zap install' or even with a copy generated with
|
||||
+# './live_zap rsync' . '/apth/to/zaptel' is the directory containing the
|
||||
+# live/ subdirectory:
|
||||
+#
|
||||
+#LIVE_AST_ZAPLIVE_PATH="/path/to/zaptel"
|
||||
+#
|
||||
+# LIVE_AST_LD_PATH_EXTRA:
|
||||
+# space-separated or colon-separated directories to add to
|
||||
+# LD_LIBRARY_PATH. It is added before any existing components of
|
||||
+# LD_LIBRARY_PATH.
|
||||
+#LIVE_AST_LD_PATH_EXTRA="$HOME/lib:$HOME/libtest /opt/testapp"
|
||||
+#
|
||||
+# LIVE_AST_CONFIGURE_PARAMS:
|
||||
+# Extra parameters to pass to ./configure.
|
||||
+#LIVE_AST_CONFIGURE_PARAMS="--enable-dev-mode"
|
||||
+#
|
||||
+# LIVE_AST_FORCE_DEF_CONF:
|
||||
+# If set to a non-empty value, always regenerate menuselect config.
|
||||
+# This avoids emenselect's nag about "configuration has changed" that
|
||||
+# happens occasionally after an svn update.
|
||||
+#LIVE_AST_FORCE_DEF_CONF=yes
|
||||
+#
|
||||
+# LIVE_AST_BRISTUFFED_LIBPRI
|
||||
+# A hack to use the second, "bristuffed" copy of libpri that exists
|
||||
+# e.g. in the Debian libpri-dev package. If set to a non-empty value,
|
||||
+# live_ast will edit makeopts to use that second copy after ./configure
|
||||
+# is run.
|
||||
+#LIVE_AST_BRISTUFFED_LIBPRI=yes
|
||||
+#
|
||||
+# LIVE_AST_FOR_SYSTEM
|
||||
+# When generating asterisk.conf, use most components from the installed
|
||||
+# system. Also provide a sane var-run directory for those of us who want
|
||||
+# to do the right thing and run asterisk as non-root.
|
||||
+#LIVE_AST_FOR_SYSTEM=yes
|
||||
+####################### End Samples
|
||||
+
|
||||
+BASE_DIR="$PWD/live"
|
||||
+AST_CONF_DIR="$BASE_DIR/etc/asterisk"
|
||||
+AST_CONF="$AST_CONF_DIR/asterisk.conf"
|
||||
+AST_BIN="$BASE_DIR/usr/sbin/asterisk"
|
||||
+GDB_INIT="$BASE_DIR/gdbinit"
|
||||
+LIVE_CONF="$BASE_DIR/live.conf"
|
||||
+DISABLED_MODS="chan_h323 pbx_dundi"
|
||||
+DISABLED_MODS_FILE="modules-disabled.conf"
|
||||
+
|
||||
+if [ -r "$LIVE_CONF" ]; then . "$LIVE_CONF"; fi
|
||||
+
|
||||
+if [ "$LIVE_AST_LIBPRI_PATH" != '' ]; then
|
||||
+ LIVE_AST_CONFIGURE_PARAMS="$LIVE_AST_CONFIGURE_PARAMS --with-pri=$LIVE_AST_LIBPRI_PATH"
|
||||
+ LIVE_AST_LD_PATH_EXTRA="$LIVE_AST_LD_PATH_EXTRA $LIVE_AST_LIBPRI_PATH"
|
||||
+fi
|
||||
+
|
||||
+if [ "$LIVE_AST_ZAPTEL_PATH" != '' ]; then
|
||||
+ LIVE_AST_CONFIGURE_PARAMS="$LIVE_AST_CONFIGURE_PARAMS --with-tonezone=$LIVE_AST_ZAPTEL_PATH"
|
||||
+ LIVE_AST_CONFIGURE_PARAMS="$LIVE_AST_CONFIGURE_PARAMS --with-zaptel=$LIVE_AST_ZAPTEL_PATH"
|
||||
+ LIVE_AST_CONFIGURE_PARAMS="$LIVE_AST_CONFIGURE_PARAMS --with-zaptel_transcode=$LIVE_AST_ZAPTEL_PATH"
|
||||
+ LIVE_AST_CONFIGURE_PARAMS="$LIVE_AST_CONFIGURE_PARAMS --with-zaptel_vldtmf=$LIVE_AST_ZAPTEL_PATH"
|
||||
+ LIVE_AST_LD_PATH_EXTRA="$LIVE_AST_LD_PATH_EXTRA $LIVE_AST_ZAPTEL_PATH"
|
||||
+fi
|
||||
+
|
||||
+if [ "$LIVE_AST_DAHDI_PATH" != '' ]; then
|
||||
+ LIVE_AST_CONFIGURE_PARAMS="$LIVE_AST_CONFIGURE_PARAMS --with-dahdi=$LIVE_AST_DAHDI_PATH"
|
||||
+fi
|
||||
+
|
||||
+if [ "$LIVE_AST_DAHDITOOLS_PATH" != '' ]; then
|
||||
+ LIVE_AST_CONFIGURE_PARAMS="$LIVE_AST_CONFIGURE_PARAMS --with-tonezone=$LIVE_AST_DAHDITOOLS_PATH"
|
||||
+ LIVE_AST_LD_PATH_EXTRA="$LIVE_AST_LD_PATH_EXTRA $LIVE_AST_DAHDITOOLS_PATH"
|
||||
+fi
|
||||
+
|
||||
+if [ "$LIVE_AST_ZAPLIVE_PATH" != '' ]; then
|
||||
+ ZAPLIVE_USR_DIR="$LIVE_AST_ZAPLIVE_PATH/live/usr"
|
||||
+ LIVE_AST_CONFIGURE_PARAMS="$LIVE_AST_CONFIGURE_PARAMS --with-tonezone=$ZAPLIVE_USR_DIR"
|
||||
+ LIVE_AST_CONFIGURE_PARAMS="$LIVE_AST_CONFIGURE_PARAMS --with-zaptel=$ZAPALIVE_USR_DIR"
|
||||
+ LIVE_AST_CONFIGURE_PARAMS="$LIVE_AST_CONFIGURE_PARAMS --with-zaptel_transcode=$ZAPALIVE_USR_DIR"
|
||||
+ LIVE_AST_CONFIGURE_PARAMS="$LIVE_AST_CONFIGURE_PARAMS --with-zaptel_vldtmf=$ZAPALIVE_USR_DIR"
|
||||
+ LIVE_AST_LD_PATH_EXTRA="$LIVE_AST_LD_PATH_EXTRA $ZAPLIVE_USR_DIR/lib"
|
||||
+fi
|
||||
+
|
||||
+# gets rid of excessive spaces. Leves nothing if there were only white spaces:
|
||||
+LIVE_AST_LD_PATH_EXTRA=`echo $LIVE_AST_LD_PATH_EXTRA | tr ' ' :`
|
||||
+
|
||||
+set_ld_env() {
|
||||
+ if [ "$LIVE_AST_LD_PATH_EXTRA$LD_LIBRARY_PATH" = '' ]; then return; fi
|
||||
+
|
||||
+ LD_LIBRARY_PATH=`echo $LIVE_AST_LD_PATH_EXTRA $LD_LIBRARY_PATH | tr ' ' :`
|
||||
+ export LD_LIBRARY_PATH
|
||||
+}
|
||||
+
|
||||
+# if live.conf does not exist, generate it from the sample
|
||||
+gen_live_conf() {
|
||||
+ if [ -r $LIVE_CONF ]; then return; fi
|
||||
+ # TODO: `dirname $LIVE_CONF` in case someone redefines it?
|
||||
+ mkdir -p $BASE_DIR
|
||||
+ sed -n -e '/^#* Begin Samples/,/^#* End Samples/p' "$0" \
|
||||
+ | sed -e '/^#* \(Begin\|End\) Samples/d' >"$LIVE_CONF"
|
||||
+}
|
||||
+
|
||||
+command="$1"
|
||||
+shift
|
||||
+
|
||||
+case "$command" in
|
||||
+configure)
|
||||
+ ./configure $LIVE_AST_CONFIGURE_PARAMS "$@"
|
||||
+ if [ "$LIVE_AST_FORCE_DEF_CONF" != '' ]; then
|
||||
+ rm -f menuselect.makeopts
|
||||
+ fi
|
||||
+ if [ "$LIVE_AST_BRISTUFFED_LIBPRI" != '' ]; then
|
||||
+ sed -i \
|
||||
+ -e 's|^\(PRI_INCLUDE=\).*|\1-I/usr/include/bristuffed|' \
|
||||
+ -e 's|^\(PRI_LIB=\).*|\1-lpri-bristuffed|' \
|
||||
+ makeopts
|
||||
+ fi
|
||||
+ ;;
|
||||
+install)
|
||||
+ make install DESTDIR="$BASE_DIR" "$@"
|
||||
+ ;;
|
||||
+samples)
|
||||
+ make samples DESTDIR="$BASE_DIR" "$@"
|
||||
+ sed -r -i \
|
||||
+ -e '/^\[directories\]\(!\)/s/\(!\).*//' \
|
||||
+ -e "/^\[directories\]/a; rem-out any of the following to use Asterisk's defaults:" \
|
||||
+ -e "/^ast(etc|mod|varlib|data|agi|run|spool|log|db|key)dir\>/s| /| $BASE_DIR/|" \
|
||||
+ "$AST_CONF"
|
||||
+ if [ "$LIVE_AST_FOR_SYSTEM" != '' ]; then
|
||||
+ sed -r -i \
|
||||
+ -e "/^ast(etc|varlib|data|agi|run|spool|log|db|key)dir\>/s|^|;|" \
|
||||
+ -e "/^;astrundir\>/aastrundir => /var/run/asterisk" \
|
||||
+ "$AST_CONF"
|
||||
+ fi
|
||||
+ # disable some modules that bind on a port that is already in use by a
|
||||
+ # main Asterisk copy, and would crash asterisk in failing:
|
||||
+ rm -f "$AST_CONF_DIR/$DISABLED_MODS_FILE"
|
||||
+ for mod in $DISABLED_MODS; do
|
||||
+ echo "noload => $mod.so" >> "$AST_CONF_DIR/$DISABLED_MODS_FILE"
|
||||
+ done
|
||||
+ echo "#include $DISABLED_MODS_FILE" >> "$AST_CONF_DIR/modules.conf"
|
||||
+
|
||||
+ cat <<EOF >"$GDB_INIT"
|
||||
+set args -C "$AST_CONF" -c
|
||||
+EOF
|
||||
+cat <<EOF >"$BASE_DIR/asterisk"
|
||||
+#!/bin/sh
|
||||
+# a wrapper to run asterisk from the "live" copy:
|
||||
+cd "$PWD"
|
||||
+exec "$0" run "\$@"
|
||||
+EOF
|
||||
+ chmod +x "$BASE_DIR/asterisk"
|
||||
+ # Generate a sample config file for live_ast itself:
|
||||
+ gen_live_conf
|
||||
+ ;;
|
||||
+conf-file)
|
||||
+ # Just regenerate live.conf from the sample if it does not already exist:
|
||||
+ gen_live_conf
|
||||
+ ;;
|
||||
+run)
|
||||
+ set_ld_env
|
||||
+ $AST_BIN -C $AST_CONF "$@"
|
||||
+ ;;
|
||||
+gdb)
|
||||
+ set_ld_env
|
||||
+ gdb -x $GDB_INIT $AST_BIN
|
||||
+ ;;
|
||||
+*)
|
||||
+ echo "$0: Unknown command '$command'. Aborting"
|
||||
+ echo
|
||||
+ echo "$0: Usage: Equivalent of:"
|
||||
+ echo "$0 configure [params] ./configure [params]"
|
||||
+ echo "$0 install make install"
|
||||
+ echo "$0 samples make samples"
|
||||
+ echo "$0 run [params] asterisk [params]"
|
||||
+ echo "$0 gdb gdb asterisk"
|
||||
+ echo "$0 conf-file create live.conf if it does exist"
|
||||
+ exit 1
|
||||
+ ;;
|
||||
+esac
|
||||
@ -0,0 +1,11 @@
|
||||
--- Makefile (revisión: 197263)
|
||||
+++ Makefile (revisión: 197264)
|
||||
@@ -404,7 +404,7 @@
|
||||
rm -f build_tools/menuselect-deps
|
||||
|
||||
datafiles: _all
|
||||
- if [ x`$(ID) -un` = xroot ]; then CFLAGS="$(ASTCFLAGS)" sh build_tools/mkpkgconfig $(DESTDIR)/usr/lib/pkgconfig; fi
|
||||
+ if [ x`$(ID) -un` = xroot ]; then CFLAGS="$(ASTCFLAGS)" bash build_tools/mkpkgconfig $(DESTDIR)/usr/lib/pkgconfig; fi
|
||||
# Should static HTTP be installed during make samples or even with its own target ala
|
||||
# webvoicemail? There are portions here that *could* be customized but might also be
|
||||
# improved a lot. I'll put it here for now.
|
||||
@ -0,0 +1,11 @@
|
||||
--- channels/misdn/isdn_lib.c (revisión: 168622)
|
||||
+++ channels/misdn/isdn_lib.c (revisión: 185120)
|
||||
@@ -915,7 +915,7 @@
|
||||
bc->l3_id = l3_id;
|
||||
cb_log(3, stack->port, " --> new_l3id %x\n", l3_id);
|
||||
} else {
|
||||
- if (stack->ptp || bc->te_choose_channel) {
|
||||
+ if ((stack->pri && stack->ptp) || bc->te_choose_channel) {
|
||||
/* we know exactly which channels are in use */
|
||||
if (find_free_chan_in_stack(stack, bc, bc->channel_preselected ? bc->channel : 0, bc->dec) < 0) {
|
||||
return -1;
|
||||
@ -0,0 +1,23 @@
|
||||
--- channels/chan_misdn.c (revisión: 193612)
|
||||
+++ channels/chan_misdn.c (revisión: 193613)
|
||||
@@ -2610,11 +2610,10 @@
|
||||
|
||||
switch (p->state) {
|
||||
case MISDN_INCOMING_SETUP:
|
||||
- case MISDN_CALLING:
|
||||
/* This is the only place in misdn_hangup, where we
|
||||
* can call release_chan, else it might create lot's of trouble
|
||||
* */
|
||||
- ast_log(LOG_NOTICE, "release channel, in CALLING/INCOMING_SETUP state.. no other events happened\n");
|
||||
+ ast_log(LOG_NOTICE, "release channel, in INCOMING_SETUP state.. no other events happened\n");
|
||||
release_chan(bc);
|
||||
|
||||
p->state = MISDN_CLEANING;
|
||||
@@ -2637,6 +2636,7 @@
|
||||
misdn_lib_send_event( bc, EVENT_DISCONNECT);
|
||||
break;
|
||||
|
||||
+ case MISDN_CALLING:
|
||||
case MISDN_ALERTING:
|
||||
case MISDN_PROGRESS:
|
||||
case MISDN_PROCEEDING:
|
||||
@ -0,0 +1,13 @@
|
||||
--- channels/misdn_config.c (revisión: 193261)
|
||||
+++ channels/misdn_config.c (revisión: 193262)
|
||||
@@ -798,7 +798,9 @@
|
||||
for (; iter; iter = iter->next) {
|
||||
strncat(tempbuf, iter->msn, sizeof(tempbuf) - strlen(tempbuf) - 1);
|
||||
}
|
||||
- tempbuf[strlen(tempbuf)-2] = 0;
|
||||
+ if (strlen(tempbuf) > 1) {
|
||||
+ tempbuf[strlen(tempbuf)-2] = 0;
|
||||
+ }
|
||||
}
|
||||
snprintf(buf, bufsize, " -> msns: %s", *tempbuf ? tempbuf : "none");
|
||||
break;
|
||||
@ -0,0 +1,66 @@
|
||||
--- res/res_musiconhold.c (revisión: 198664)
|
||||
+++ res/res_musiconhold.c (revisión: 198665)
|
||||
@@ -157,6 +157,7 @@
|
||||
/*! FD for timing source */
|
||||
int pseudofd;
|
||||
unsigned int delete:1;
|
||||
+ unsigned int deprecated:1;
|
||||
AST_LIST_HEAD_NOLOCK(, mohdata) members;
|
||||
AST_LIST_ENTRY(mohclass) list;
|
||||
};
|
||||
@@ -1243,7 +1244,8 @@
|
||||
*args++ = '\0';
|
||||
}
|
||||
|
||||
- if ((tmp_class = get_mohbyname(var->name, 0))) {
|
||||
+ /* Only skip if this is a duplicate of an above item */
|
||||
+ if ((tmp_class = get_mohbyname(var->name, 0)) && !tmp_class->deprecated && !tmp_class->delete) {
|
||||
tmp_class = mohclass_unref(tmp_class);
|
||||
continue;
|
||||
}
|
||||
@@ -1251,14 +1253,15 @@
|
||||
if (!(class = moh_class_malloc())) {
|
||||
break;
|
||||
}
|
||||
-
|
||||
+
|
||||
+ class->deprecated = 1;
|
||||
ast_copy_string(class->name, var->name, sizeof(class->name));
|
||||
ast_copy_string(class->dir, data, sizeof(class->dir));
|
||||
ast_copy_string(class->mode, var->value, sizeof(class->mode));
|
||||
if (args) {
|
||||
ast_copy_string(class->args, args, sizeof(class->args));
|
||||
}
|
||||
-
|
||||
+
|
||||
moh_register(class, reload);
|
||||
class = NULL;
|
||||
|
||||
@@ -1273,7 +1276,8 @@
|
||||
dep_warning = 1;
|
||||
}
|
||||
|
||||
- if ((tmp_class = get_mohbyname(var->name, 0))) {
|
||||
+ /* Only skip if this is a duplicate of an above item */
|
||||
+ if ((tmp_class = get_mohbyname(var->name, 0)) && !tmp_class->deprecated && !tmp_class->delete) {
|
||||
tmp_class = mohclass_unref(tmp_class);
|
||||
continue;
|
||||
}
|
||||
@@ -1285,14 +1289,15 @@
|
||||
if (!(class = moh_class_malloc())) {
|
||||
break;
|
||||
}
|
||||
-
|
||||
+
|
||||
+ class->deprecated = 1;
|
||||
ast_copy_string(class->name, var->name, sizeof(class->name));
|
||||
ast_copy_string(class->dir, var->value, sizeof(class->dir));
|
||||
ast_copy_string(class->mode, "files", sizeof(class->mode));
|
||||
if (args) {
|
||||
ast_copy_string(class->args, args, sizeof(class->args));
|
||||
}
|
||||
-
|
||||
+
|
||||
moh_register(class, reload);
|
||||
class = NULL;
|
||||
|
||||
@ -0,0 +1,425 @@
|
||||
--- include/asterisk/devicestate.h (revisión: 204680)
|
||||
+++ include/asterisk/devicestate.h (revisión: 204681)
|
||||
@@ -27,24 +27,18 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
-/*! Device is valid but channel didn't know state */
|
||||
-#define AST_DEVICE_UNKNOWN 0
|
||||
-/*! Device is not used */
|
||||
-#define AST_DEVICE_NOT_INUSE 1
|
||||
-/*! Device is in use */
|
||||
-#define AST_DEVICE_INUSE 2
|
||||
-/*! Device is busy */
|
||||
-#define AST_DEVICE_BUSY 3
|
||||
-/*! Device is invalid */
|
||||
-#define AST_DEVICE_INVALID 4
|
||||
-/*! Device is unavailable */
|
||||
-#define AST_DEVICE_UNAVAILABLE 5
|
||||
-/*! Device is ringing */
|
||||
-#define AST_DEVICE_RINGING 6
|
||||
-/*! Device is ringing *and* in use */
|
||||
-#define AST_DEVICE_RINGINUSE 7
|
||||
-/*! Device is on hold */
|
||||
-#define AST_DEVICE_ONHOLD 8
|
||||
+enum ast_device_state {
|
||||
+ AST_DEVICE_UNKNOWN, /*!< Device is valid but channel didn't know state */
|
||||
+ AST_DEVICE_NOT_INUSE, /*!< Device is not used */
|
||||
+ AST_DEVICE_INUSE, /*!< Device is in use */
|
||||
+ AST_DEVICE_BUSY, /*!< Device is busy */
|
||||
+ AST_DEVICE_INVALID, /*!< Device is invalid */
|
||||
+ AST_DEVICE_UNAVAILABLE, /*!< Device is unavailable */
|
||||
+ AST_DEVICE_RINGING, /*!< Device is ringing */
|
||||
+ AST_DEVICE_RINGINUSE, /*!< Device is ringing *and* in use */
|
||||
+ AST_DEVICE_ONHOLD, /*!< Device is on hold */
|
||||
+ AST_DEVICE_TOTAL, /*!< Total num of device states, used for testing */
|
||||
+};
|
||||
|
||||
/*! \brief Devicestate watcher call back */
|
||||
typedef int (*ast_devstate_cb_type)(const char *dev, int state, void *data);
|
||||
@@ -55,7 +49,7 @@
|
||||
/*! \brief Convert device state to text string for output
|
||||
* \param devstate Current device state
|
||||
*/
|
||||
-const char *devstate2str(int devstate);
|
||||
+const char *devstate2str(enum ast_device_state devstate);
|
||||
|
||||
/*! \brief Search the Channels by Name
|
||||
* \param device like a dialstring
|
||||
--- include/asterisk/pbx.h (revisión: 204680)
|
||||
+++ include/asterisk/pbx.h (revisión: 204681)
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "asterisk/sched.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
+#include "asterisk/devicestate.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
@@ -305,6 +306,64 @@
|
||||
*/
|
||||
int ast_unregister_application(const char *app);
|
||||
|
||||
+/*!
|
||||
+ * \brief An object to hold state when calculating aggregate device state
|
||||
+ */
|
||||
+struct ast_devstate_aggregate;
|
||||
+
|
||||
+/*!
|
||||
+ * \brief Initialize aggregate device state
|
||||
+ *
|
||||
+ * \param[in] agg the state object
|
||||
+ *
|
||||
+ * \return nothing
|
||||
+ */
|
||||
+void ast_devstate_aggregate_init(struct ast_devstate_aggregate *agg);
|
||||
+
|
||||
+/*!
|
||||
+ * \brief Add a device state to the aggregate device state
|
||||
+ *
|
||||
+ * \param[in] agg the state object
|
||||
+ * \param[in] state the state to add
|
||||
+ *
|
||||
+ * \return nothing
|
||||
+ */
|
||||
+void ast_devstate_aggregate_add(struct ast_devstate_aggregate *agg, enum ast_device_state state);
|
||||
+
|
||||
+/*!
|
||||
+ * \brief Get the aggregate device state result
|
||||
+ *
|
||||
+ * \param[in] agg the state object
|
||||
+ *
|
||||
+ * \return the aggregate device state after adding some number of device states.
|
||||
+ */
|
||||
+enum ast_device_state ast_devstate_aggregate_result(struct ast_devstate_aggregate *agg);
|
||||
+
|
||||
+/*!
|
||||
+ * \brief Map devstate to an extension state.
|
||||
+ *
|
||||
+ * \param[in] device state
|
||||
+ *
|
||||
+ * \return the extension state mapping.
|
||||
+ */
|
||||
+enum ast_extension_states ast_devstate_to_extenstate(enum ast_device_state devstate);
|
||||
+
|
||||
+/*!
|
||||
+ * \brief You shouldn't care about the contents of this struct
|
||||
+ *
|
||||
+ * This struct is only here so that it can be easily declared on the stack.
|
||||
+ */
|
||||
+struct ast_devstate_aggregate {
|
||||
+ unsigned int all_unavail:1;
|
||||
+ unsigned int all_busy:1;
|
||||
+ unsigned int all_free:1;
|
||||
+ unsigned int all_unknown:1;
|
||||
+ unsigned int on_hold:1;
|
||||
+ unsigned int busy:1;
|
||||
+ unsigned int in_use:1;
|
||||
+ unsigned int ring:1;
|
||||
+};
|
||||
+
|
||||
/*!
|
||||
* \brief Uses hint and devicestate callback to get the state of an extension
|
||||
*
|
||||
--- main/devicestate.c (revisión: 204680)
|
||||
+++ main/devicestate.c (revisión: 204681)
|
||||
@@ -45,15 +45,15 @@
|
||||
|
||||
/*! \brief Device state strings for printing */
|
||||
static const char *devstatestring[] = {
|
||||
- /* 0 AST_DEVICE_UNKNOWN */ "Unknown", /*!< Valid, but unknown state */
|
||||
- /* 1 AST_DEVICE_NOT_INUSE */ "Not in use", /*!< Not used */
|
||||
- /* 2 AST_DEVICE IN USE */ "In use", /*!< In use */
|
||||
- /* 3 AST_DEVICE_BUSY */ "Busy", /*!< Busy */
|
||||
- /* 4 AST_DEVICE_INVALID */ "Invalid", /*!< Invalid - not known to Asterisk */
|
||||
- /* 5 AST_DEVICE_UNAVAILABLE */ "Unavailable", /*!< Unavailable (not registered) */
|
||||
- /* 6 AST_DEVICE_RINGING */ "Ringing", /*!< Ring, ring, ring */
|
||||
- /* 7 AST_DEVICE_RINGINUSE */ "Ring+Inuse", /*!< Ring and in use */
|
||||
- /* 8 AST_DEVICE_ONHOLD */ "On Hold" /*!< On Hold */
|
||||
+ /* 0 AST_DEVICE_UNKNOWN */ "Unknown", /*!< Valid, but unknown state */
|
||||
+ /* 1 AST_DEVICE_NOT_INUSE */ "Not in use", /*!< Not used */
|
||||
+ /* 2 AST_DEVICE IN USE */ "In use", /*!< In use */
|
||||
+ /* 3 AST_DEVICE_BUSY */ "Busy", /*!< Busy */
|
||||
+ /* 4 AST_DEVICE_INVALID */ "Invalid", /*!< Invalid - not known to Asterisk */
|
||||
+ /* 5 AST_DEVICE_UNAVAILABLE */"Unavailable",/*!< Unavailable (not registered) */
|
||||
+ /* 6 AST_DEVICE_RINGING */ "Ringing", /*!< Ring, ring, ring */
|
||||
+ /* 7 AST_DEVICE_RINGINUSE */ "Ring+Inuse", /*!< Ring and in use */
|
||||
+ /* 8 AST_DEVICE_ONHOLD */ "On Hold" /*!< On Hold */
|
||||
};
|
||||
|
||||
/*! \brief A device state provider (not a channel) */
|
||||
@@ -95,7 +95,7 @@
|
||||
static int getproviderstate(const char *provider, const char *address);
|
||||
|
||||
/*! \brief Find devicestate as text message for output */
|
||||
-const char *devstate2str(int devstate)
|
||||
+const char *devstate2str(enum ast_device_state devstate)
|
||||
{
|
||||
return devstatestring[devstate];
|
||||
}
|
||||
--- main/pbx.c (revisión: 204680)
|
||||
+++ main/pbx.c (revisión: 204681)
|
||||
@@ -58,7 +58,6 @@
|
||||
#include "asterisk/causes.h"
|
||||
#include "asterisk/musiconhold.h"
|
||||
#include "asterisk/app.h"
|
||||
-#include "asterisk/devicestate.h"
|
||||
#include "asterisk/stringfields.h"
|
||||
#include "asterisk/threadstorage.h"
|
||||
|
||||
@@ -1915,14 +1914,128 @@
|
||||
return e;
|
||||
}
|
||||
|
||||
+void ast_devstate_aggregate_init(struct ast_devstate_aggregate *agg)
|
||||
+{
|
||||
+ memset(agg, 0, sizeof(*agg));
|
||||
+ agg->all_unknown = 1;
|
||||
+ agg->all_unavail = 1;
|
||||
+ agg->all_busy = 1;
|
||||
+ agg->all_free = 1;
|
||||
+}
|
||||
+
|
||||
+void ast_devstate_aggregate_add(struct ast_devstate_aggregate *agg, enum ast_device_state state)
|
||||
+{
|
||||
+ switch (state) {
|
||||
+ case AST_DEVICE_NOT_INUSE:
|
||||
+ agg->all_unknown = 0;
|
||||
+ agg->all_unavail = 0;
|
||||
+ agg->all_busy = 0;
|
||||
+ break;
|
||||
+ case AST_DEVICE_INUSE:
|
||||
+ agg->in_use = 1;
|
||||
+ agg->all_unavail = 0;
|
||||
+ agg->all_free = 0;
|
||||
+ agg->all_unknown = 0;
|
||||
+ break;
|
||||
+ case AST_DEVICE_RINGING:
|
||||
+ agg->ring = 1;
|
||||
+ agg->all_unavail = 0;
|
||||
+ agg->all_free = 0;
|
||||
+ agg->all_unknown = 0;
|
||||
+ break;
|
||||
+ case AST_DEVICE_RINGINUSE:
|
||||
+ agg->in_use = 1;
|
||||
+ agg->ring = 1;
|
||||
+ agg->all_unavail = 0;
|
||||
+ agg->all_free = 0;
|
||||
+ agg->all_unknown = 0;
|
||||
+ break;
|
||||
+ case AST_DEVICE_ONHOLD:
|
||||
+ agg->all_unknown = 0;
|
||||
+ agg->all_unavail = 0;
|
||||
+ agg->all_free = 0;
|
||||
+ agg->on_hold = 1;
|
||||
+ break;
|
||||
+ case AST_DEVICE_BUSY:
|
||||
+ agg->all_unknown = 0;
|
||||
+ agg->all_unavail = 0;
|
||||
+ agg->all_free = 0;
|
||||
+ agg->busy = 1;
|
||||
+ agg->in_use = 1;
|
||||
+ break;
|
||||
+ case AST_DEVICE_UNAVAILABLE:
|
||||
+ agg->all_unknown = 0;
|
||||
+ case AST_DEVICE_INVALID:
|
||||
+ agg->all_busy = 0;
|
||||
+ agg->all_free = 0;
|
||||
+ break;
|
||||
+ case AST_DEVICE_UNKNOWN:
|
||||
+ agg->all_busy = 0;
|
||||
+ agg->all_free = 0;
|
||||
+ break;
|
||||
+ case AST_DEVICE_TOTAL: /* not a device state, included for completeness. */
|
||||
+ break;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+enum ast_extension_states ast_devstate_to_extenstate(enum ast_device_state devstate)
|
||||
+{
|
||||
+ switch (devstate) {
|
||||
+ case AST_DEVICE_ONHOLD:
|
||||
+ return AST_EXTENSION_ONHOLD;
|
||||
+ case AST_DEVICE_BUSY:
|
||||
+ return AST_EXTENSION_BUSY;
|
||||
+ case AST_DEVICE_UNAVAILABLE:
|
||||
+ case AST_DEVICE_UNKNOWN:
|
||||
+ case AST_DEVICE_INVALID:
|
||||
+ return AST_EXTENSION_UNAVAILABLE;
|
||||
+ case AST_DEVICE_RINGINUSE:
|
||||
+ return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
|
||||
+ case AST_DEVICE_RINGING:
|
||||
+ return AST_EXTENSION_RINGING;
|
||||
+ case AST_DEVICE_INUSE:
|
||||
+ return AST_EXTENSION_INUSE;
|
||||
+ case AST_DEVICE_NOT_INUSE:
|
||||
+ return AST_EXTENSION_NOT_INUSE;
|
||||
+ case AST_DEVICE_TOTAL: /* not a device state, included for completeness */
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return AST_EXTENSION_NOT_INUSE;
|
||||
+}
|
||||
+enum ast_device_state ast_devstate_aggregate_result(struct ast_devstate_aggregate *agg)
|
||||
+{
|
||||
+ if (agg->all_free)
|
||||
+ return AST_DEVICE_NOT_INUSE;
|
||||
+ if ((agg->in_use || agg->on_hold) && agg->ring)
|
||||
+ return AST_DEVICE_RINGINUSE;
|
||||
+ if (agg->ring)
|
||||
+ return AST_DEVICE_RINGING;
|
||||
+ if (agg->busy)
|
||||
+ return AST_DEVICE_BUSY;
|
||||
+ if (agg->in_use)
|
||||
+ return AST_DEVICE_INUSE;
|
||||
+ if (agg->on_hold)
|
||||
+ return AST_DEVICE_ONHOLD;
|
||||
+ if (agg->all_busy)
|
||||
+ return AST_DEVICE_BUSY;
|
||||
+ if (agg->all_unknown)
|
||||
+ return AST_DEVICE_UNKNOWN;
|
||||
+ if (agg->all_unavail)
|
||||
+ return AST_DEVICE_UNAVAILABLE;
|
||||
+
|
||||
+ return AST_DEVICE_NOT_INUSE;
|
||||
+}
|
||||
+
|
||||
/*! \brief ast_extensions_state2: Check state of extension by using hints */
|
||||
static int ast_extension_state2(struct ast_exten *e)
|
||||
{
|
||||
char hint[AST_MAX_EXTENSION];
|
||||
char *cur, *rest;
|
||||
- int allunavailable = 1, allbusy = 1, allfree = 1;
|
||||
- int busy = 0, inuse = 0, ring = 0, onhold = 0;
|
||||
+ struct ast_devstate_aggregate agg;
|
||||
|
||||
+ ast_devstate_aggregate_init(&agg);
|
||||
+
|
||||
if (!e)
|
||||
return -1;
|
||||
|
||||
@@ -1931,66 +2044,9 @@
|
||||
rest = hint; /* One or more devices separated with a & character */
|
||||
while ( (cur = strsep(&rest, "&")) ) {
|
||||
int res = ast_device_state(cur);
|
||||
- switch (res) {
|
||||
- case AST_DEVICE_NOT_INUSE:
|
||||
- allunavailable = 0;
|
||||
- allbusy = 0;
|
||||
- break;
|
||||
- case AST_DEVICE_INUSE:
|
||||
- inuse = 1;
|
||||
- allunavailable = 0;
|
||||
- allfree = 0;
|
||||
- break;
|
||||
- case AST_DEVICE_RINGING:
|
||||
- ring = 1;
|
||||
- allunavailable = 0;
|
||||
- allfree = 0;
|
||||
- break;
|
||||
- case AST_DEVICE_RINGINUSE:
|
||||
- inuse = 1;
|
||||
- ring = 1;
|
||||
- allunavailable = 0;
|
||||
- allfree = 0;
|
||||
- break;
|
||||
- case AST_DEVICE_ONHOLD:
|
||||
- allunavailable = 0;
|
||||
- allfree = 0;
|
||||
- onhold = 1;
|
||||
- break;
|
||||
- case AST_DEVICE_BUSY:
|
||||
- allunavailable = 0;
|
||||
- allfree = 0;
|
||||
- busy = 1;
|
||||
- inuse = 1;
|
||||
- break;
|
||||
- case AST_DEVICE_UNAVAILABLE:
|
||||
- case AST_DEVICE_INVALID:
|
||||
- allbusy = 0;
|
||||
- allfree = 0;
|
||||
- break;
|
||||
- default:
|
||||
- allunavailable = 0;
|
||||
- allbusy = 0;
|
||||
- allfree = 0;
|
||||
- }
|
||||
+ ast_devstate_aggregate_add(&agg, res);
|
||||
}
|
||||
-
|
||||
- if (allfree)
|
||||
- return AST_EXTENSION_NOT_INUSE;
|
||||
- if ((inuse || onhold) && ring)
|
||||
- return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
|
||||
- if (allbusy)
|
||||
- return AST_EXTENSION_BUSY;
|
||||
- if (inuse)
|
||||
- return AST_EXTENSION_INUSE;
|
||||
- if (ring)
|
||||
- return AST_EXTENSION_RINGING;
|
||||
- if (onhold)
|
||||
- return AST_EXTENSION_ONHOLD;
|
||||
- if (allunavailable)
|
||||
- return AST_EXTENSION_UNAVAILABLE;
|
||||
-
|
||||
- return AST_EXTENSION_NOT_INUSE;
|
||||
+ return ast_devstate_to_extenstate(ast_devstate_aggregate_result(&agg));
|
||||
}
|
||||
|
||||
/*! \brief ast_extension_state2str: Return extension_state as string */
|
||||
@@ -3042,6 +3098,13 @@
|
||||
/*
|
||||
* Help for CLI commands ...
|
||||
*/
|
||||
+
|
||||
+#ifdef AST_DEVMODE
|
||||
+static char show_device2extenstate_help[] =
|
||||
+"Usage: core show device2extenstate\n"
|
||||
+" Lists device state to extension state combinations.\n";
|
||||
+#endif
|
||||
+
|
||||
static char show_applications_help[] =
|
||||
"Usage: core show applications [{like|describing} <text>]\n"
|
||||
" List applications which are currently available.\n"
|
||||
@@ -3756,9 +3819,27 @@
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
+#ifdef AST_DEVMODE
|
||||
+static int handle_show_device2extenstate(int fd, int argc, char *argv[])
|
||||
+{
|
||||
+ struct ast_devstate_aggregate agg;
|
||||
+ int i, j, exten, combined;
|
||||
|
||||
+ for (i = 0; i < AST_DEVICE_TOTAL; i++) {
|
||||
+ for (j = 0; j < AST_DEVICE_TOTAL; j++) {
|
||||
+ ast_devstate_aggregate_init(&agg);
|
||||
+ ast_devstate_aggregate_add(&agg, i);
|
||||
+ ast_devstate_aggregate_add(&agg, j);
|
||||
+ combined = ast_devstate_aggregate_result(&agg);
|
||||
+ exten = ast_devstate_to_extenstate(combined);
|
||||
+ ast_cli(fd, "\n Exten:%14s CombinedDevice:%12s Dev1:%12s Dev2:%12s", ast_extension_state2str(exten), devstate2str(combined), devstate2str(j), devstate2str(i));
|
||||
+ }
|
||||
+ }
|
||||
+ ast_cli(fd, "\n");
|
||||
+ return RESULT_SUCCESS;
|
||||
+}
|
||||
+#endif
|
||||
|
||||
-
|
||||
/*
|
||||
* CLI entries for upper commands ...
|
||||
*/
|
||||
@@ -3828,6 +3909,12 @@
|
||||
handle_show_globals, "Show global dialplan variables",
|
||||
show_globals_help, NULL, &cli_show_globals_deprecated },
|
||||
|
||||
+#ifdef AST_DEVMODE
|
||||
+ { { "core", "show", "device2extenstate", NULL },
|
||||
+ handle_show_device2extenstate, "Show expected exten state from multiple device states",
|
||||
+ show_device2extenstate_help, NULL, NULL },
|
||||
+#endif
|
||||
+
|
||||
{ { "core", "show" , "function", NULL },
|
||||
handle_show_function, "Describe a specific dialplan function",
|
||||
show_function_help, complete_show_function, &cli_show_function_deprecated },
|
||||
@ -0,0 +1,91 @@
|
||||
--- main/pbx.c (revisión: 199296)
|
||||
+++ main/pbx.c (revisión: 199297)
|
||||
@@ -1920,8 +1920,8 @@
|
||||
{
|
||||
char hint[AST_MAX_EXTENSION];
|
||||
char *cur, *rest;
|
||||
- int allunavailable = 1, allbusy = 1, allfree = 1, allonhold = 1;
|
||||
- int busy = 0, inuse = 0, ring = 0;
|
||||
+ int allunavailable = 1, allbusy = 1, allfree = 1;
|
||||
+ int busy = 0, inuse = 0, ring = 0, onhold = 0;
|
||||
|
||||
if (!e)
|
||||
return -1;
|
||||
@@ -1935,67 +1935,60 @@
|
||||
case AST_DEVICE_NOT_INUSE:
|
||||
allunavailable = 0;
|
||||
allbusy = 0;
|
||||
- allonhold = 0;
|
||||
break;
|
||||
case AST_DEVICE_INUSE:
|
||||
inuse = 1;
|
||||
allunavailable = 0;
|
||||
allfree = 0;
|
||||
- allonhold = 0;
|
||||
break;
|
||||
case AST_DEVICE_RINGING:
|
||||
ring = 1;
|
||||
allunavailable = 0;
|
||||
allfree = 0;
|
||||
- allonhold = 0;
|
||||
break;
|
||||
case AST_DEVICE_RINGINUSE:
|
||||
inuse = 1;
|
||||
ring = 1;
|
||||
allunavailable = 0;
|
||||
allfree = 0;
|
||||
- allonhold = 0;
|
||||
break;
|
||||
case AST_DEVICE_ONHOLD:
|
||||
allunavailable = 0;
|
||||
allfree = 0;
|
||||
+ onhold = 1;
|
||||
break;
|
||||
case AST_DEVICE_BUSY:
|
||||
allunavailable = 0;
|
||||
allfree = 0;
|
||||
- allonhold = 0;
|
||||
busy = 1;
|
||||
+ inuse = 1;
|
||||
break;
|
||||
case AST_DEVICE_UNAVAILABLE:
|
||||
case AST_DEVICE_INVALID:
|
||||
allbusy = 0;
|
||||
allfree = 0;
|
||||
- allonhold = 0;
|
||||
break;
|
||||
default:
|
||||
allunavailable = 0;
|
||||
allbusy = 0;
|
||||
allfree = 0;
|
||||
- allonhold = 0;
|
||||
}
|
||||
}
|
||||
|
||||
- if (!inuse && ring)
|
||||
- return AST_EXTENSION_RINGING;
|
||||
- if (inuse && ring)
|
||||
- return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
|
||||
- if (inuse)
|
||||
- return AST_EXTENSION_INUSE;
|
||||
if (allfree)
|
||||
return AST_EXTENSION_NOT_INUSE;
|
||||
- if (allonhold)
|
||||
- return AST_EXTENSION_ONHOLD;
|
||||
+ if ((inuse || onhold) && ring)
|
||||
+ return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
|
||||
if (allbusy)
|
||||
return AST_EXTENSION_BUSY;
|
||||
+ if (inuse)
|
||||
+ return AST_EXTENSION_INUSE;
|
||||
+ if (ring)
|
||||
+ return AST_EXTENSION_RINGING;
|
||||
+ if (onhold)
|
||||
+ return AST_EXTENSION_ONHOLD;
|
||||
if (allunavailable)
|
||||
return AST_EXTENSION_UNAVAILABLE;
|
||||
- if (busy)
|
||||
- return AST_EXTENSION_INUSE;
|
||||
|
||||
return AST_EXTENSION_NOT_INUSE;
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
--- res/res_features.c (revisión: 131)
|
||||
+++ res/res_features.c (copia de trabajo)
|
||||
@@ -1675,8 +1675,8 @@
|
||||
ast_set_flag(chan_cdr, AST_CDR_FLAG_MAIN);
|
||||
ast_cdr_update(chan);
|
||||
bridge_cdr = ast_cdr_dup(chan_cdr);
|
||||
- ast_copy_string(bridge_cdr->lastapp, chan->appl, sizeof(bridge_cdr->lastapp));
|
||||
- ast_copy_string(bridge_cdr->lastdata, chan->data, sizeof(bridge_cdr->lastdata));
|
||||
+ ast_copy_string(bridge_cdr->lastapp, S_OR(chan->appl, ""), sizeof(bridge_cdr->lastapp));
|
||||
+ ast_copy_string(bridge_cdr->lastdata, S_OR(chan->data, ""), sizeof(bridge_cdr->lastdata));
|
||||
} else {
|
||||
/* better yet, in a xfer situation, find out why the chan cdr got zapped (pun unintentional) */
|
||||
bridge_cdr = ast_cdr_alloc(); /* this should be really, really rare/impossible? */
|
||||
@ -0,0 +1,182 @@
|
||||
--- apps/app_queue.c (revisión: 131)
|
||||
+++ apps/app_queue.c (copia de trabajo)
|
||||
@@ -287,6 +287,8 @@
|
||||
/*! \brief queues.conf [general] option */
|
||||
static int montype_default = 0;
|
||||
|
||||
+
|
||||
+
|
||||
enum queue_result {
|
||||
QUEUE_UNKNOWN = 0,
|
||||
QUEUE_TIMEOUT = 1,
|
||||
@@ -401,6 +403,7 @@
|
||||
unsigned int leavewhenempty:2;
|
||||
unsigned int ringinuse:1;
|
||||
unsigned int setinterfacevar:1;
|
||||
+ unsigned int setqueuevar:1;
|
||||
unsigned int reportholdtime:1;
|
||||
unsigned int wrapped:1;
|
||||
unsigned int timeoutrestart:1;
|
||||
@@ -459,8 +462,69 @@
|
||||
|
||||
static int set_member_paused(const char *queuename, const char *interface, int paused);
|
||||
|
||||
-static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
|
||||
+static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
|
||||
|
||||
+static char *int2strat(int strategy);
|
||||
+
|
||||
+static inline struct call_queue *queue_ref(struct call_queue *q)
|
||||
+{
|
||||
+ ao2_ref(q, 1);
|
||||
+ return q;
|
||||
+}
|
||||
+
|
||||
+static inline struct call_queue *queue_unref(struct call_queue *q)
|
||||
+{
|
||||
+ ao2_ref(q, -1);
|
||||
+ return q;
|
||||
+}
|
||||
+
|
||||
+static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
|
||||
+{
|
||||
+
|
||||
+ char interfacevar[256]="";
|
||||
+ float sl = 0;
|
||||
+
|
||||
+ if (q->setqueuevar) {
|
||||
+ sl = 0;
|
||||
+ if (q->callscompleted > 0)
|
||||
+ sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
|
||||
+
|
||||
+ snprintf(interfacevar, sizeof(interfacevar),
|
||||
+ "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
|
||||
+ q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
|
||||
+
|
||||
+ pbx_builtin_setvar_multiple(chan, interfacevar);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+
|
||||
+struct queue_end_bridge {
|
||||
+ struct call_queue *q;
|
||||
+ struct ast_channel *chan;
|
||||
+};
|
||||
+
|
||||
+static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
|
||||
+{
|
||||
+ struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
|
||||
+ ao2_ref(qeb, +1);
|
||||
+ qeb->chan = originator;
|
||||
+
|
||||
+}
|
||||
+ static void end_bridge_callback(void *data)
|
||||
+ {
|
||||
+ struct queue_end_bridge *qeb = data;
|
||||
+ struct call_queue *q = qeb->q;
|
||||
+ struct ast_channel *chan = qeb->chan;
|
||||
+
|
||||
+ if (ao2_ref(qeb, -1) == 1) {
|
||||
+ ao2_lock(q);
|
||||
+ set_queue_variables(q, chan);
|
||||
+ ao2_unlock(q);
|
||||
+ /* This unrefs the reference we made in try_calling when we allocated qeb */
|
||||
+ queue_unref(q);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
static void rr_dep_warning(void)
|
||||
{
|
||||
static unsigned int warned = 0;
|
||||
@@ -2830,6 +2894,7 @@
|
||||
int callcompletedinsl;
|
||||
struct ao2_iterator memi;
|
||||
struct ast_datastore *datastore, *transfer_ds;
|
||||
+ struct queue_end_bridge *queue_end_bridge = NULL;
|
||||
|
||||
ast_channel_lock(qe->chan);
|
||||
datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
|
||||
@@ -3256,6 +3321,19 @@
|
||||
|
||||
if (member->status == AST_DEVICE_NOT_INUSE)
|
||||
ast_log(LOG_WARNING, "The device state of this queue member, %s, is still 'Not in Use' when it probably should not be! Please check UPGRADE.txt for correct configuration settings.\n", member->membername);
|
||||
+
|
||||
+ if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
|
||||
+ queue_end_bridge->q = qe->parent;
|
||||
+ queue_end_bridge->chan = qe->chan;
|
||||
+ bridge_config.end_bridge_callback = end_bridge_callback;
|
||||
+ bridge_config.end_bridge_callback_data = queue_end_bridge;
|
||||
+ bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
|
||||
+ /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
|
||||
+ * to make sure to increase the refcount of this queue so it cannot be freed until we
|
||||
+ * are done with it. We remove this reference in end_bridge_callback.
|
||||
+ */
|
||||
+ queue_ref(qe->parent);
|
||||
+ }
|
||||
|
||||
transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
|
||||
bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
|
||||
--- include/asterisk/pbx.h (revisión: 131)
|
||||
+++ include/asterisk/pbx.h (copia de trabajo)
|
||||
@@ -886,6 +886,7 @@
|
||||
* \note Will lock the channel.
|
||||
*/
|
||||
int pbx_builtin_setvar(struct ast_channel *chan, void *data);
|
||||
+int pbx_builtin_setvar_multiple(struct ast_channel *chan, void *data);
|
||||
|
||||
void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count);
|
||||
void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count);
|
||||
--- main/pbx.c (revisión: 131)
|
||||
+++ main/pbx.c (copia de trabajo)
|
||||
@@ -236,6 +236,7 @@
|
||||
static int pbx_builtin_saycharacters(struct ast_channel *, void *);
|
||||
static int pbx_builtin_sayphonetic(struct ast_channel *, void *);
|
||||
int pbx_builtin_setvar(struct ast_channel *, void *);
|
||||
+int pbx_builtin_setvar_multiple(struct ast_channel *, void *);
|
||||
static int pbx_builtin_importvar(struct ast_channel *, void *);
|
||||
|
||||
AST_MUTEX_DEFINE_STATIC(globalslock);
|
||||
@@ -5938,6 +5939,43 @@
|
||||
ast_mutex_unlock(&globalslock);
|
||||
}
|
||||
|
||||
+int pbx_builtin_setvar_multiple(struct ast_channel *chan, void *vdata)
|
||||
+{
|
||||
+ char *data;
|
||||
+ int x;
|
||||
+ AST_DECLARE_APP_ARGS(args,
|
||||
+ AST_APP_ARG(pair)[24];
|
||||
+ );
|
||||
+ AST_DECLARE_APP_ARGS(pair,
|
||||
+ AST_APP_ARG(name);
|
||||
+ AST_APP_ARG(value);
|
||||
+ );
|
||||
+
|
||||
+ if (ast_strlen_zero(vdata)) {
|
||||
+ ast_log(LOG_WARNING, "MSet requires at least one variable name/value pair.\n");
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ data = ast_strdupa(vdata);
|
||||
+ AST_STANDARD_APP_ARGS(args, data);
|
||||
+
|
||||
+ for (x = 0; x < args.argc; x++) {
|
||||
+ AST_NONSTANDARD_APP_ARGS(pair, args.pair[x], '=');
|
||||
+ if (pair.argc == 2) {
|
||||
+ pbx_builtin_setvar_helper(chan, pair.name, pair.value);
|
||||
+ if (strchr(pair.name, ' '))
|
||||
+ ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", pair.name, pair.value);
|
||||
+ } else if (!chan) {
|
||||
+ ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '='\n", pair.name);
|
||||
+ } else {
|
||||
+ ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '=' (in %s@%s:%d\n", pair.name, chan->exten, chan->context, chan->priority);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
|
||||
{
|
||||
struct ast_var_t *newvariable;
|
||||
@ -0,0 +1,767 @@
|
||||
--- apps/app_queue.c (revision 1)
|
||||
+++ apps/app_queue.c (working copy)
|
||||
@@ -391,7 +391,7 @@
|
||||
#define QUEUE_EVENT_VARIABLES 3
|
||||
|
||||
struct call_queue {
|
||||
- ast_mutex_t lock;
|
||||
+
|
||||
char name[80]; /*!< Name */
|
||||
char moh[80]; /*!< Music On Hold class to be used */
|
||||
char announce[80]; /*!< Announcement to play when call is answered */
|
||||
@@ -463,6 +463,7 @@
|
||||
static int set_member_paused(const char *queuename, const char *interface, int paused);
|
||||
|
||||
static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
|
||||
+static void free_members(struct call_queue *q, int all);
|
||||
|
||||
static char *int2strat(int strategy);
|
||||
|
||||
@@ -580,6 +581,29 @@
|
||||
return -1;
|
||||
}
|
||||
|
||||
+/*!
|
||||
+ * \brief removes a call_queue from the list of call_queues
|
||||
+ */
|
||||
+static void remove_queue(struct call_queue *q)
|
||||
+{
|
||||
+ AST_LIST_LOCK(&queues);
|
||||
+ if (AST_LIST_REMOVE(&queues, q, list)) {
|
||||
+ ao2_ref(q, -1);
|
||||
+ }
|
||||
+ AST_LIST_UNLOCK(&queues);
|
||||
+}
|
||||
+
|
||||
+static void destroy_queue(void *obj)
|
||||
+{
|
||||
+ struct call_queue *q = obj;
|
||||
+
|
||||
+ if (q->members) {
|
||||
+ free_members(q, 1);
|
||||
+ ao2_ref(q->members, -1);
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
+
|
||||
/*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
|
||||
static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
|
||||
{
|
||||
@@ -595,6 +619,11 @@
|
||||
q->head = new;
|
||||
}
|
||||
new->next = cur;
|
||||
+
|
||||
+ /* every queue_ent must have a reference to it's parent call_queue, this
|
||||
+ * reference does not go away until the end of the queue_ent's life, meaning
|
||||
+ * that even when the queue_ent leaves the call_queue this ref must remain. */
|
||||
+ ao2_ref(q, +1);
|
||||
new->parent = q;
|
||||
new->pos = ++(*pos);
|
||||
new->opos = *pos;
|
||||
@@ -618,7 +647,7 @@
|
||||
struct ao2_iterator mem_iter;
|
||||
enum queue_member_status result = QUEUE_NO_MEMBERS;
|
||||
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
mem_iter = ao2_iterator_init(q->members, 0);
|
||||
while ((member = ao2_iterator_next(&mem_iter))) {
|
||||
if (max_penalty && (member->penalty > max_penalty)) {
|
||||
@@ -640,14 +669,13 @@
|
||||
result = QUEUE_NO_REACHABLE_MEMBERS;
|
||||
ao2_ref(member, -1);
|
||||
break;
|
||||
- default:
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ default:
|
||||
+ ao2_unlock(q);
|
||||
ao2_ref(member, -1);
|
||||
return QUEUE_NORMAL;
|
||||
}
|
||||
}
|
||||
-
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -665,7 +693,7 @@
|
||||
|
||||
AST_LIST_LOCK(&queues);
|
||||
AST_LIST_TRAVERSE(&queues, q, list) {
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
mem_iter = ao2_iterator_init(q->members, 0);
|
||||
while ((cur = ao2_iterator_next(&mem_iter))) {
|
||||
char tmp_interface[80];
|
||||
@@ -702,7 +730,7 @@
|
||||
}
|
||||
ao2_ref(cur, -1);
|
||||
}
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
}
|
||||
AST_LIST_UNLOCK(&queues);
|
||||
|
||||
@@ -852,8 +880,7 @@
|
||||
{
|
||||
struct call_queue *q;
|
||||
|
||||
- if ((q = ast_calloc(1, sizeof(*q)))) {
|
||||
- ast_mutex_init(&q->lock);
|
||||
+ if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
|
||||
ast_copy_string(q->name, queuename, sizeof(q->name));
|
||||
}
|
||||
return q;
|
||||
@@ -990,7 +1017,7 @@
|
||||
|
||||
AST_LIST_LOCK(&queues);
|
||||
AST_LIST_TRAVERSE(&queues, q, list) {
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
mem_iter = ao2_iterator_init(q->members, 0);
|
||||
while ((mem = ao2_iterator_next(&mem_iter))) {
|
||||
if (!strcasecmp(mem->state_interface, interface)) {
|
||||
@@ -1000,7 +1027,7 @@
|
||||
}
|
||||
ao2_ref(mem, -1);
|
||||
}
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
@@ -1260,17 +1287,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
-static void destroy_queue(struct call_queue *q)
|
||||
-{
|
||||
- free_members(q, 1);
|
||||
- ast_mutex_destroy(&q->lock);
|
||||
- ao2_ref(q->members, -1);
|
||||
- free(q);
|
||||
-}
|
||||
-
|
||||
/*!\brief Reload a single queue via realtime.
|
||||
\return Return the queue, or NULL if it doesn't exist.
|
||||
\note Should be called with the global qlock locked. */
|
||||
+
|
||||
static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
|
||||
{
|
||||
struct ast_variable *v;
|
||||
@@ -1289,14 +1309,14 @@
|
||||
|
||||
/* Static queues override realtime. */
|
||||
if (q) {
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
if (!q->realtime) {
|
||||
if (q->dead) {
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
return NULL;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
return q;
|
||||
}
|
||||
}
|
||||
@@ -1317,11 +1337,10 @@
|
||||
/* Delete if unused (else will be deleted when last caller leaves). */
|
||||
if (!q->count) {
|
||||
/* Delete. */
|
||||
- AST_LIST_REMOVE(&queues, q, list);
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
- destroy_queue(q);
|
||||
+ ao2_unlock(q);
|
||||
+ remove_queue(q);
|
||||
} else
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -1330,10 +1349,10 @@
|
||||
if (!q) {
|
||||
if (!(q = alloc_queue(queuename)))
|
||||
return NULL;
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
clear_queue(q);
|
||||
q->realtime = 1;
|
||||
-
|
||||
+
|
||||
/* manwe & saghul FTW!!*/
|
||||
|
||||
/*Before we initialize the queue, we need to set the strategy, so that linear strategy
|
||||
@@ -1405,16 +1424,15 @@
|
||||
while ((m = ao2_iterator_next(&mem_iter))) {
|
||||
if (m->dead) {
|
||||
ao2_unlink(q->members, m);
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
remove_from_interfaces(m->state_interface);
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
q->membercount--;
|
||||
}
|
||||
ao2_ref(m, -1);
|
||||
}
|
||||
+ ao2_unlock(q);
|
||||
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
-
|
||||
return q;
|
||||
}
|
||||
|
||||
@@ -1453,7 +1471,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
|
||||
/* Temporarily set realtime members dead so we can detect deleted ones.*/
|
||||
mem_iter = ao2_iterator_init(q->members, 0);
|
||||
@@ -1476,14 +1494,14 @@
|
||||
while ((m = ao2_iterator_next(&mem_iter))) {
|
||||
if (m->dead) {
|
||||
ao2_unlink(q->members, m);
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
remove_from_interfaces(m->state_interface);
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
q->membercount--;
|
||||
}
|
||||
ao2_ref(m, -1);
|
||||
}
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
ast_config_destroy(member_config);
|
||||
}
|
||||
|
||||
@@ -1550,7 +1568,7 @@
|
||||
return res;
|
||||
|
||||
AST_LIST_LOCK(&queues);
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
|
||||
/* This is our one */
|
||||
stat = get_member_status(q, qe->max_penalty);
|
||||
@@ -1596,7 +1614,7 @@
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
|
||||
}
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
AST_LIST_UNLOCK(&queues);
|
||||
|
||||
return res;
|
||||
@@ -1769,11 +1787,10 @@
|
||||
/* Calculate holdtime using an exponential average */
|
||||
/* Thanks to SRT for this contribution */
|
||||
/* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
|
||||
-
|
||||
- ast_mutex_lock(&qe->parent->lock);
|
||||
+ ao2_lock(qe->parent);
|
||||
oldvalue = qe->parent->holdtime;
|
||||
qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
|
||||
- ast_mutex_unlock(&qe->parent->lock);
|
||||
+ ao2_unlock(qe->parent);
|
||||
}
|
||||
|
||||
|
||||
@@ -1785,7 +1802,7 @@
|
||||
|
||||
if (!(q = qe->parent))
|
||||
return;
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
|
||||
prev = NULL;
|
||||
for (cur = q->head; cur; cur = cur->next) {
|
||||
@@ -1809,14 +1826,11 @@
|
||||
prev = cur;
|
||||
}
|
||||
}
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
|
||||
if (q->dead && !q->count) {
|
||||
/* It's dead and nobody is in it, so kill it */
|
||||
- AST_LIST_LOCK(&queues);
|
||||
- AST_LIST_REMOVE(&queues, q, list);
|
||||
- AST_LIST_UNLOCK(&queues);
|
||||
- destroy_queue(q);
|
||||
+ remove_queue(q);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1851,7 +1865,7 @@
|
||||
AST_LIST_TRAVERSE(&queues, q, list) {
|
||||
if (q == rq) /* don't check myself, could deadlock */
|
||||
continue;
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
if (q->count && q->members) {
|
||||
if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
|
||||
ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
|
||||
@@ -1862,7 +1876,7 @@
|
||||
ao2_ref(mem, -1);
|
||||
}
|
||||
}
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
@@ -1976,10 +1990,9 @@
|
||||
tmp->stillgoing = 0;
|
||||
|
||||
update_status(tmp->member->interface, ast_device_state(tmp->member->state_interface));
|
||||
-
|
||||
- ast_mutex_lock(&qe->parent->lock);
|
||||
+ ao2_lock(qe->parent);
|
||||
qe->parent->rrpos++;
|
||||
- ast_mutex_unlock(&qe->parent->lock);
|
||||
+ ao2_unlock(qe->parent);
|
||||
|
||||
qe->linpos++;
|
||||
|
||||
@@ -2205,7 +2218,7 @@
|
||||
|
||||
static void record_abandoned(struct queue_ent *qe)
|
||||
{
|
||||
- ast_mutex_lock(&qe->parent->lock);
|
||||
+ ao2_lock(qe->parent);
|
||||
manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
|
||||
"Queue: %s\r\n"
|
||||
"Uniqueid: %s\r\n"
|
||||
@@ -2215,7 +2228,7 @@
|
||||
qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
|
||||
|
||||
qe->parent->callsabandoned++;
|
||||
- ast_mutex_unlock(&qe->parent->lock);
|
||||
+ ao2_unlock(qe->parent);
|
||||
}
|
||||
|
||||
/*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
|
||||
@@ -2516,7 +2529,7 @@
|
||||
|
||||
} else {
|
||||
/* This needs a lock. How many members are available to be served? */
|
||||
- ast_mutex_lock(&qe->parent->lock);
|
||||
+ ao2_lock(qe->parent);
|
||||
|
||||
ch = qe->parent->head;
|
||||
|
||||
@@ -2562,7 +2575,7 @@
|
||||
res = 0;
|
||||
}
|
||||
|
||||
- ast_mutex_unlock(&qe->parent->lock);
|
||||
+ ao2_unlock(qe->parent);
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -2654,13 +2667,13 @@
|
||||
|
||||
static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
|
||||
{
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
time(&member->lastcall);
|
||||
member->calls++;
|
||||
q->callscompleted++;
|
||||
if (callcompletedinsl)
|
||||
q->callscompletedinsl++;
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2950,7 +2963,7 @@
|
||||
/* Hold the lock while we setup the outgoing calls */
|
||||
if (use_weight)
|
||||
AST_LIST_LOCK(&queues);
|
||||
- ast_mutex_lock(&qe->parent->lock);
|
||||
+ ao2_lock(qe->parent);
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
|
||||
qe->chan->name);
|
||||
@@ -2967,7 +2980,7 @@
|
||||
AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
|
||||
if (!tmp) {
|
||||
ao2_ref(cur, -1);
|
||||
- ast_mutex_unlock(&qe->parent->lock);
|
||||
+ ao2_unlock(qe->parent);
|
||||
if (use_weight)
|
||||
AST_LIST_UNLOCK(&queues);
|
||||
goto out;
|
||||
@@ -2975,7 +2988,7 @@
|
||||
if (!datastore) {
|
||||
if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
|
||||
ao2_ref(cur, -1);
|
||||
- ast_mutex_unlock(&qe->parent->lock);
|
||||
+ ao2_unlock(qe->parent);
|
||||
if (use_weight)
|
||||
AST_LIST_UNLOCK(&queues);
|
||||
free(tmp);
|
||||
@@ -2984,7 +2997,7 @@
|
||||
datastore->inheritance = DATASTORE_INHERIT_FOREVER;
|
||||
if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
|
||||
ao2_ref(cur, -1);
|
||||
- ast_mutex_unlock(&qe->parent->lock);
|
||||
+ ao2_unlock(qe->parent);
|
||||
if (use_weight)
|
||||
AST_LIST_UNLOCK(&queues);
|
||||
free(tmp);
|
||||
@@ -3021,7 +3034,7 @@
|
||||
if (strncasecmp(cur->interface, "Local/", 6)) {
|
||||
if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
|
||||
ao2_ref(cur, -1);
|
||||
- ast_mutex_unlock(&qe->parent->lock);
|
||||
+ ao2_unlock(qe->parent);
|
||||
if (use_weight)
|
||||
AST_LIST_UNLOCK(&queues);
|
||||
free(tmp);
|
||||
@@ -3060,7 +3073,7 @@
|
||||
else
|
||||
to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
|
||||
++qe->pending;
|
||||
- ast_mutex_unlock(&qe->parent->lock);
|
||||
+ ao2_unlock(qe->parent);
|
||||
ring_one(qe, outgoing, &numbusies);
|
||||
if (use_weight)
|
||||
AST_LIST_UNLOCK(&queues);
|
||||
@@ -3076,14 +3089,14 @@
|
||||
ast_channel_datastore_free(datastore);
|
||||
}
|
||||
ast_channel_unlock(qe->chan);
|
||||
- ast_mutex_lock(&qe->parent->lock);
|
||||
+ ao2_lock(qe->parent);
|
||||
if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
|
||||
store_next_rr(qe, outgoing);
|
||||
}
|
||||
if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
|
||||
store_next_lin(qe, outgoing);
|
||||
}
|
||||
- ast_mutex_unlock(&qe->parent->lock);
|
||||
+ ao2_unlock(qe->parent);
|
||||
peer = lpeer ? lpeer->chan : NULL;
|
||||
if (!peer) {
|
||||
qe->pending = 0;
|
||||
@@ -3107,9 +3120,9 @@
|
||||
/* Update parameters for the queue */
|
||||
time(&now);
|
||||
recalc_holdtime(qe, (now - qe->start));
|
||||
- ast_mutex_lock(&qe->parent->lock);
|
||||
+ ao2_lock(qe->parent);
|
||||
callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
|
||||
- ast_mutex_unlock(&qe->parent->lock);
|
||||
+ ao2_unlock(qe->parent);
|
||||
member = lpeer->member;
|
||||
/* Increment the refcount for this member, since we're going to be using it for awhile in here. */
|
||||
ao2_ref(member, 1);
|
||||
@@ -3325,9 +3338,10 @@
|
||||
if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
|
||||
queue_end_bridge->q = qe->parent;
|
||||
queue_end_bridge->chan = qe->chan;
|
||||
- bridge_config.end_bridge_callback = end_bridge_callback;
|
||||
- bridge_config.end_bridge_callback_data = queue_end_bridge;
|
||||
- bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
|
||||
+
|
||||
+ bridge_config.end_bridge_callback = end_bridge_callback;
|
||||
+ bridge_config.end_bridge_callback_data = queue_end_bridge;
|
||||
+ bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
|
||||
/* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
|
||||
* to make sure to increase the refcount of this queue so it cannot be freed until we
|
||||
* are done with it. We remove this reference in end_bridge_callback.
|
||||
@@ -3485,9 +3499,9 @@
|
||||
|
||||
AST_LIST_LOCK(&queues);
|
||||
AST_LIST_TRAVERSE(&queues, q, list) {
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
if (strcmp(q->name, queuename)) {
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3496,7 +3510,7 @@
|
||||
if (!mem->dynamic) {
|
||||
res = RES_NOT_DYNAMIC;
|
||||
ao2_ref(mem, -1);
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
break;
|
||||
}
|
||||
q->membercount--;
|
||||
@@ -3516,7 +3530,7 @@
|
||||
} else {
|
||||
res = RES_EXISTS;
|
||||
}
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3539,7 +3553,7 @@
|
||||
|
||||
AST_LIST_LOCK(&queues);
|
||||
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
if ((old_member = interface_exists(q, interface)) == NULL) {
|
||||
if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
|
||||
add_to_interfaces(new_member->state_interface);
|
||||
@@ -3575,7 +3589,7 @@
|
||||
ao2_ref(old_member, -1);
|
||||
res = RES_EXISTS;
|
||||
}
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
AST_LIST_UNLOCK(&queues);
|
||||
|
||||
return res;
|
||||
@@ -3594,7 +3608,7 @@
|
||||
|
||||
AST_LIST_LOCK(&queues);
|
||||
AST_LIST_TRAVERSE(&queues, q, list) {
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
|
||||
if ((mem = interface_exists(q, interface))) {
|
||||
found++;
|
||||
@@ -3619,7 +3633,7 @@
|
||||
ao2_ref(mem, -1);
|
||||
}
|
||||
}
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
}
|
||||
AST_LIST_UNLOCK(&queues);
|
||||
|
||||
@@ -3653,10 +3667,10 @@
|
||||
queue_name = entry->key + strlen(pm_family) + 2;
|
||||
|
||||
AST_LIST_TRAVERSE(&queues, cur_queue, list) {
|
||||
- ast_mutex_lock(&cur_queue->lock);
|
||||
+ ao2_lock(cur_queue);
|
||||
if (!strcmp(queue_name, cur_queue->name))
|
||||
break;
|
||||
- ast_mutex_unlock(&cur_queue->lock);
|
||||
+ ao2_unlock(cur_queue);
|
||||
}
|
||||
|
||||
if (!cur_queue)
|
||||
@@ -3669,7 +3683,7 @@
|
||||
ast_db_del(pm_family, queue_name);
|
||||
continue;
|
||||
} else
|
||||
- ast_mutex_unlock(&cur_queue->lock);
|
||||
+ ao2_unlock(cur_queue);
|
||||
|
||||
if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
|
||||
continue;
|
||||
@@ -4046,7 +4060,7 @@
|
||||
AST_APP_ARG(agi);
|
||||
);
|
||||
/* Our queue entry */
|
||||
- struct queue_ent qe;
|
||||
+ struct queue_ent qe = { 0 };
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
|
||||
@@ -4059,7 +4073,6 @@
|
||||
lu = ast_module_user_add(chan);
|
||||
|
||||
/* Setup our queue entry */
|
||||
- memset(&qe, 0, sizeof(qe));
|
||||
qe.start = time(NULL);
|
||||
|
||||
/* set the expire time based on the supplied timeout; */
|
||||
@@ -4279,6 +4292,12 @@
|
||||
set_queue_result(chan, reason);
|
||||
res = 0;
|
||||
}
|
||||
+ if (qe.parent) {
|
||||
+ /* every queue_ent is given a reference to it's parent call_queue when it joins the queue.
|
||||
+ * This ref must be taken away right before the queue_ent is destroyed. In this case
|
||||
+ * the queue_ent is about to be returned on the stack */
|
||||
+ ao2_ref(qe.parent, -1);
|
||||
+ }
|
||||
ast_module_user_remove(lu);
|
||||
|
||||
return res;
|
||||
@@ -4302,7 +4321,7 @@
|
||||
lu = ast_module_user_add(chan);
|
||||
|
||||
if ((q = load_realtime_queue(data))) {
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
mem_iter = ao2_iterator_init(q->members, 0);
|
||||
while ((m = ao2_iterator_next(&mem_iter))) {
|
||||
/* Count the agents who are logged in and presently answering calls */
|
||||
@@ -4311,7 +4330,7 @@
|
||||
}
|
||||
ao2_ref(m, -1);
|
||||
}
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
} else
|
||||
ast_log(LOG_WARNING, "queue %s was not found\n", data);
|
||||
|
||||
@@ -4340,7 +4359,7 @@
|
||||
AST_LIST_LOCK(&queues);
|
||||
AST_LIST_TRAVERSE(&queues, q, list) {
|
||||
if (!strcasecmp(q->name, data)) {
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -4348,7 +4367,7 @@
|
||||
|
||||
if (q) {
|
||||
count = q->count;
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
} else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
|
||||
/* if the queue is realtime but was not found in memory, this
|
||||
* means that the queue had been deleted from memory since it was
|
||||
@@ -4383,7 +4402,7 @@
|
||||
AST_LIST_LOCK(&queues);
|
||||
AST_LIST_TRAVERSE(&queues, q, list) {
|
||||
if (!strcasecmp(q->name, data)) {
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -4409,7 +4428,7 @@
|
||||
}
|
||||
ao2_ref(m, -1);
|
||||
}
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
} else
|
||||
ast_log(LOG_WARNING, "queue %s was not found\n", data);
|
||||
|
||||
@@ -4523,12 +4542,12 @@
|
||||
new = 0;
|
||||
if (q) {
|
||||
if (!new)
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
/* Check if a queue with this name already exists */
|
||||
if (q->found) {
|
||||
ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
|
||||
if (!new)
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
continue;
|
||||
}
|
||||
/* manwe & saghul FTW!! */
|
||||
@@ -4640,7 +4659,7 @@
|
||||
if (new) {
|
||||
AST_LIST_INSERT_HEAD(&queues, q, list);
|
||||
} else
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4648,12 +4667,9 @@
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
|
||||
if (q->dead) {
|
||||
AST_LIST_REMOVE_CURRENT(&queues, list);
|
||||
- if (!q->count)
|
||||
- destroy_queue(q);
|
||||
- else
|
||||
- ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
|
||||
+ ao2_ref(q, -1);
|
||||
} else {
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
mem_iter = ao2_iterator_init(q->members, 0);
|
||||
while ((cur = ao2_iterator_next(&mem_iter))) {
|
||||
if (cur->dynamic)
|
||||
@@ -4661,7 +4677,7 @@
|
||||
cur->status = ast_device_state(cur->interface);
|
||||
ao2_ref(cur, -1);
|
||||
}
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
}
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
@@ -4722,10 +4738,10 @@
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
AST_LIST_TRAVERSE(&queues, q, list) {
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
if (queue_show) {
|
||||
if (strcasecmp(q->name, argv[2]) != 0) {
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
if (!AST_LIST_NEXT(q, list)) {
|
||||
ast_cli(fd, "No such queue: %s.%s",argv[2], term);
|
||||
break;
|
||||
@@ -4811,7 +4827,7 @@
|
||||
astman_append(s, "%s", term);
|
||||
else
|
||||
ast_cli(fd, "%s", term);
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
if (queue_show)
|
||||
break;
|
||||
}
|
||||
@@ -4885,7 +4901,7 @@
|
||||
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
|
||||
|
||||
AST_LIST_TRAVERSE(&queues, q, list) {
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
|
||||
/* List queue properties */
|
||||
if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
|
||||
@@ -4943,7 +4959,7 @@
|
||||
(long) (now - qe->start), idText);
|
||||
}
|
||||
}
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
}
|
||||
|
||||
astman_append(s,
|
||||
@@ -5210,19 +5226,19 @@
|
||||
/* here is the case for 3, <member> */
|
||||
if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
|
||||
AST_LIST_TRAVERSE(&queues, q, list) {
|
||||
- ast_mutex_lock(&q->lock);
|
||||
+ ao2_lock(q);
|
||||
mem_iter = ao2_iterator_init(q->members, 0);
|
||||
while ((m = ao2_iterator_next(&mem_iter))) {
|
||||
if (++which > state) {
|
||||
char *tmp;
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
tmp = ast_strdup(m->interface);
|
||||
ao2_ref(m, -1);
|
||||
return tmp;
|
||||
}
|
||||
ao2_ref(m, -1);
|
||||
}
|
||||
- ast_mutex_unlock(&q->lock);
|
||||
+ ao2_unlock(q);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
--- apps/app_queue.c (revisión: 137)
|
||||
+++ apps/app_queue.c (copia de trabajo)
|
||||
@@ -120,7 +120,8 @@
|
||||
QUEUE_STRATEGY_FEWESTCALLS,
|
||||
QUEUE_STRATEGY_RANDOM,
|
||||
QUEUE_STRATEGY_RRMEMORY,
|
||||
- QUEUE_STRATEGY_LINEAR
|
||||
+ QUEUE_STRATEGY_LINEAR,
|
||||
+ QUEUE_STRATEGY_WRANDOM
|
||||
};
|
||||
|
||||
static struct strategy {
|
||||
@@ -133,7 +134,8 @@
|
||||
{ QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
|
||||
{ QUEUE_STRATEGY_RANDOM, "random" },
|
||||
{ QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
|
||||
- { QUEUE_STRATEGY_LINEAR, "linear" }
|
||||
+ { QUEUE_STRATEGY_LINEAR, "linear" },
|
||||
+ { QUEUE_STRATEGY_WRANDOM, "wrandom"},
|
||||
};
|
||||
|
||||
#define DEFAULT_RETRY 5
|
||||
@@ -2731,6 +2733,10 @@
|
||||
tmp->metric = ast_random() % 1000;
|
||||
tmp->metric += mem->penalty * 1000000;
|
||||
break;
|
||||
+ case QUEUE_STRATEGY_WRANDOM:
|
||||
+ tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
|
||||
+ ast_log(LOG_DEBUG,"calc_metric() Calculated metric %d for member %s (penalty %d)\n",tmp->metric,mem->membername,mem->penalty);
|
||||
+ break;
|
||||
case QUEUE_STRATEGY_FEWESTCALLS:
|
||||
tmp->metric = mem->calls;
|
||||
tmp->metric += mem->penalty * 1000000;
|
||||
@ -0,0 +1,158 @@
|
||||
--- res/res_config_odbc.c (revisión: 54)
|
||||
+++ res/res_config_odbc.c (copia de trabajo)
|
||||
@@ -412,6 +412,146 @@
|
||||
return -1;
|
||||
}
|
||||
|
||||
+/*!
|
||||
+ * \brief Excute an INSERT query
|
||||
+ * \param database
|
||||
+ * \param table
|
||||
+ * \param ap list containing one or more field/value set(s)
|
||||
+ *
|
||||
+ * Insert a new record into database table, prepare the sql statement.
|
||||
+ * All values to be changed are stored in ap list.
|
||||
+ * Sub-in the values to the prepared statement and execute it.
|
||||
+ *
|
||||
+ * \retval number of rows affected
|
||||
+ * \retval -1 on failure
|
||||
+*/
|
||||
+static int store_odbc(const char *database, const char *table, va_list ap)
|
||||
+{
|
||||
+ struct odbc_obj *obj;
|
||||
+ SQLHSTMT stmt;
|
||||
+ char sql[256];
|
||||
+ char keys[256];
|
||||
+ char vals[256];
|
||||
+ SQLLEN rowcount=0;
|
||||
+ const char *newparam, *newval;
|
||||
+ int res;
|
||||
+ va_list aq;
|
||||
+ struct custom_prepare_struct cps = { .sql = sql, .extra = NULL };
|
||||
+
|
||||
+ va_copy(cps.ap, ap);
|
||||
+ va_copy(aq, ap);
|
||||
+
|
||||
+ if (!table)
|
||||
+ return -1;
|
||||
+
|
||||
+ obj = ast_odbc_request_obj(database, 0);
|
||||
+ if (!obj)
|
||||
+ return -1;
|
||||
+
|
||||
+ newparam = va_arg(aq, const char *);
|
||||
+ if (!newparam) {
|
||||
+ ast_odbc_release_obj(obj);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ newval = va_arg(aq, const char *);
|
||||
+ snprintf(keys, sizeof(keys), "%s", newparam);
|
||||
+ ast_copy_string(vals, "?", sizeof(vals));
|
||||
+ while ((newparam = va_arg(aq, const char *))) {
|
||||
+ snprintf(keys + strlen(keys), sizeof(keys) - strlen(keys), ", %s", newparam);
|
||||
+ snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", ?");
|
||||
+ newval = va_arg(aq, const char *);
|
||||
+ }
|
||||
+ va_end(aq);
|
||||
+ snprintf(sql, sizeof(sql), "INSERT INTO %s (%s) VALUES (%s)", table, keys, vals);
|
||||
+
|
||||
+ stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
|
||||
+
|
||||
+ if (!stmt) {
|
||||
+ ast_odbc_release_obj(obj);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ res = SQLRowCount(stmt, &rowcount);
|
||||
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
|
||||
+ ast_odbc_release_obj(obj);
|
||||
+
|
||||
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
||||
+ ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ if (rowcount >= 0)
|
||||
+ return (int)rowcount;
|
||||
+
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
+/*!
|
||||
+ * \brief Excute an DELETE query
|
||||
+ * \param database
|
||||
+ * \param table
|
||||
+ * \param keyfield where clause field
|
||||
+ * \param lookup value of field for where clause
|
||||
+ * \param ap list containing one or more field/value set(s)
|
||||
+ *
|
||||
+ * Delete a row from a database table, prepare the sql statement using keyfield and lookup
|
||||
+ * control the number of records to change. Additional params to match rows are stored in ap list.
|
||||
+ * Sub-in the values to the prepared statement and execute it.
|
||||
+ *
|
||||
+ * \retval number of rows affected
|
||||
+ * \retval -1 on failure
|
||||
+*/
|
||||
+static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
|
||||
+{
|
||||
+ struct odbc_obj *obj;
|
||||
+ SQLHSTMT stmt;
|
||||
+ char sql[256];
|
||||
+ SQLLEN rowcount=0;
|
||||
+ const char *newparam, *newval;
|
||||
+ int res;
|
||||
+ va_list aq;
|
||||
+ struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
|
||||
+
|
||||
+ va_copy(cps.ap, ap);
|
||||
+ va_copy(aq, ap);
|
||||
+
|
||||
+ if (!table)
|
||||
+ return -1;
|
||||
+
|
||||
+ obj = ast_odbc_request_obj(database, 0);
|
||||
+ if (!obj)
|
||||
+ return -1;
|
||||
+
|
||||
+ snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE ", table);
|
||||
+ while((newparam = va_arg(aq, const char *))) {
|
||||
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=? AND ", newparam);
|
||||
+ newval = va_arg(aq, const char *);
|
||||
+ }
|
||||
+ va_end(aq);
|
||||
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", keyfield);
|
||||
+
|
||||
+ stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
|
||||
+
|
||||
+ if (!stmt) {
|
||||
+ ast_odbc_release_obj(obj);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ res = SQLRowCount(stmt, &rowcount);
|
||||
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
|
||||
+ ast_odbc_release_obj(obj);
|
||||
+
|
||||
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
||||
+ ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ if (rowcount >= 0)
|
||||
+ return (int)rowcount;
|
||||
+
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
struct config_odbc_obj {
|
||||
char *sql;
|
||||
unsigned long cat_metric;
|
||||
@@ -538,6 +678,8 @@
|
||||
.load_func = config_odbc,
|
||||
.realtime_func = realtime_odbc,
|
||||
.realtime_multi_func = realtime_multi_odbc,
|
||||
+ .store_func = store_odbc,
|
||||
+ .destroy_func = destroy_odbc,
|
||||
.update_func = update_odbc
|
||||
};
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
--- res/res_musiconhold.c (revisión: 201599)
|
||||
+++ res/res_musiconhold.c (revisión: 201600)
|
||||
@@ -1103,9 +1103,10 @@
|
||||
while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
|
||||
free(member);
|
||||
}
|
||||
-
|
||||
+
|
||||
if (class->thread) {
|
||||
pthread_cancel(class->thread);
|
||||
+ pthread_join(class->thread, NULL);
|
||||
class->thread = AST_PTHREADT_NULL;
|
||||
}
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
--- res/res_odbc.c (revisión: 188148)
|
||||
+++ res/res_odbc.c (revisión: 188149)
|
||||
@@ -252,9 +252,13 @@
|
||||
ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Setting limit to 1023 for ODBC class '%s'.\n", v->value, cat);
|
||||
limit = 1023;
|
||||
} else if (ast_false(v->value)) {
|
||||
- ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat);
|
||||
- enabled = 0;
|
||||
+ /* Limit=no probably means "no limit", which is the maximum */
|
||||
+ ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Setting limit to 1023 for ODBC class '%s'.\n", v->value, cat);
|
||||
+ limit = 1023;
|
||||
break;
|
||||
+ } else if (limit > 1023) {
|
||||
+ ast_log(LOG_WARNING, "Maximum limit in 1.4 is 1023. Setting limit to 1023 for ODBC class '%s'.\n", cat);
|
||||
+ limit = 1023;
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "idlecheck")) {
|
||||
sscanf(v->value, "%d", &idlecheck);
|
||||
@ -0,0 +1,19 @@
|
||||
--- main/channel.c (revisión: 200359)
|
||||
+++ main/channel.c (revisión: 200360)
|
||||
@@ -2555,6 +2555,16 @@
|
||||
switch (condition) {
|
||||
case AST_CONTROL_RINGING:
|
||||
ts = ast_get_indication_tone(chan->zone, "ring");
|
||||
+ /* It is common practice for channel drivers to return -1 if trying
|
||||
+ * to indicate ringing on a channel which is up. The idea is to let the
|
||||
+ * core generate the ringing inband. However, we don't want the
|
||||
+ * warning message about not being able to handle the specific indication
|
||||
+ * to print nor do we want ast_indicate_data to return an "error" for this
|
||||
+ * condition
|
||||
+ */
|
||||
+ if (chan->_state == AST_STATE_UP) {
|
||||
+ res = 0;
|
||||
+ }
|
||||
break;
|
||||
case AST_CONTROL_BUSY:
|
||||
ts = ast_get_indication_tone(chan->zone, "busy");
|
||||
@ -0,0 +1,142 @@
|
||||
--- main/rtp.c (revision 191953)
|
||||
+++ main/rtp.c (working copy)
|
||||
@@ -73,7 +73,7 @@
|
||||
|
||||
#define RTP_MTU 1200
|
||||
|
||||
-#define DEFAULT_DTMF_TIMEOUT 3000 /*!< samples */
|
||||
+#define DEFAULT_DTMF_TIMEOUT (150 * (8000 / 1000)) /*!< samples */
|
||||
|
||||
static int dtmftimeout = DEFAULT_DTMF_TIMEOUT;
|
||||
|
||||
@@ -139,7 +139,8 @@
|
||||
/* DTMF Reception Variables */
|
||||
char resp;
|
||||
unsigned int lastevent;
|
||||
- int dtmfcount;
|
||||
+ unsigned int dtmf_duration; /*!< Total duration in samples since the digit start event */
|
||||
+ unsigned int dtmf_timeout; /*!< When this timestamp is reached we consider END frame lost and forcibly abort digit */
|
||||
/* DTMF Transmission Variables */
|
||||
unsigned int lastdigitts;
|
||||
char sending_digit; /*!< boolean - are we sending digits */
|
||||
@@ -689,7 +690,7 @@
|
||||
f = send_dtmf(rtp, AST_FRAME_DTMF_END);
|
||||
}
|
||||
rtp->resp = resp;
|
||||
- rtp->dtmfcount = dtmftimeout;
|
||||
+ rtp->dtmf_timeout = 0;
|
||||
return f;
|
||||
}
|
||||
|
||||
@@ -745,23 +746,59 @@
|
||||
if (ast_test_flag(rtp, FLAG_DTMF_COMPENSATE)) {
|
||||
if ((rtp->lastevent != timestamp) || (rtp->resp && rtp->resp != resp)) {
|
||||
rtp->resp = resp;
|
||||
- rtp->dtmfcount = 0;
|
||||
+ rtp->dtmf_timeout = 0;
|
||||
f = send_dtmf(rtp, AST_FRAME_DTMF_END);
|
||||
f->len = 0;
|
||||
rtp->lastevent = timestamp;
|
||||
}
|
||||
} else {
|
||||
- if ((!(rtp->resp) && (!(event_end & 0x80))) || (rtp->resp && rtp->resp != resp)) {
|
||||
- rtp->resp = resp;
|
||||
- f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN);
|
||||
- rtp->dtmfcount = dtmftimeout;
|
||||
- } else if ((event_end & 0x80) && (rtp->lastevent != seqno) && rtp->resp) {
|
||||
- f = send_dtmf(rtp, AST_FRAME_DTMF_END);
|
||||
- f->len = ast_tvdiff_ms(ast_samp2tv(samples, 8000), ast_tv(0, 0)); /* XXX hard coded 8kHz */
|
||||
- rtp->resp = 0;
|
||||
- rtp->dtmfcount = 0;
|
||||
- rtp->lastevent = seqno;
|
||||
+ /* The duration parameter measures the complete
|
||||
+ duration of the event (from the beginning) - RFC2833.
|
||||
+ Account for the fact that duration is only 16 bits long
|
||||
+ (about 8 seconds at 8000 Hz) and can wrap is digit
|
||||
+ is hold for too long. */
|
||||
+ unsigned int new_duration = rtp->dtmf_duration;
|
||||
+ unsigned int last_duration = new_duration & 0xFFFF;
|
||||
+
|
||||
+ if (last_duration > 64000 && samples < last_duration)
|
||||
+ new_duration += 0xFFFF + 1;
|
||||
+ new_duration = (new_duration & ~0xFFFF) | samples;
|
||||
+
|
||||
+ if (event_end & 0x80) {
|
||||
+ /* End event */
|
||||
+ if ((rtp->lastevent != seqno) && rtp->resp) {
|
||||
+ rtp->dtmf_duration = new_duration;
|
||||
+ f = send_dtmf(rtp, AST_FRAME_DTMF_END);
|
||||
+ f->len = ast_tvdiff_ms(ast_samp2tv(rtp->dtmf_duration, 8000), ast_tv(0, 0));
|
||||
+ rtp->resp = 0;
|
||||
+ rtp->dtmf_duration = rtp->dtmf_timeout = 0;
|
||||
+ }
|
||||
+ } else {
|
||||
+ /* Begin/continuation */
|
||||
+
|
||||
+ if (rtp->resp && rtp->resp != resp) {
|
||||
+ /* Another digit already began. End it */
|
||||
+ f = send_dtmf(rtp, AST_FRAME_DTMF_END);
|
||||
+ f->len = ast_tvdiff_ms(ast_samp2tv(rtp->dtmf_duration, 8000), ast_tv(0, 0));
|
||||
+ rtp->resp = 0;
|
||||
+ rtp->dtmf_duration = rtp->dtmf_timeout = 0;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ if (rtp->resp) {
|
||||
+ /* Digit continues */
|
||||
+ rtp->dtmf_duration = new_duration;
|
||||
+ } else {
|
||||
+ /* New digit began */
|
||||
+ rtp->resp = resp;
|
||||
+ f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN);
|
||||
+ rtp->dtmf_duration = samples;
|
||||
+ }
|
||||
+
|
||||
+ rtp->dtmf_timeout = timestamp + rtp->dtmf_duration + dtmftimeout;
|
||||
}
|
||||
+
|
||||
+ rtp->lastevent = seqno;
|
||||
}
|
||||
|
||||
return f;
|
||||
@@ -1291,17 +1328,15 @@
|
||||
|
||||
rtp->rxseqno = seqno;
|
||||
|
||||
- if (rtp->dtmfcount) {
|
||||
- rtp->dtmfcount -= (timestamp - rtp->lastrxts);
|
||||
+ if (rtp->dtmf_timeout && rtp->dtmf_timeout < timestamp) {
|
||||
+ rtp->dtmf_timeout = 0;
|
||||
|
||||
- if (rtp->dtmfcount < 0) {
|
||||
- rtp->dtmfcount = 0;
|
||||
- }
|
||||
-
|
||||
- if (rtp->resp && !rtp->dtmfcount) {
|
||||
+ if (rtp->resp) {
|
||||
struct ast_frame *f;
|
||||
f = send_dtmf(rtp, AST_FRAME_DTMF_END);
|
||||
+ f->len = ast_tvdiff_ms(ast_samp2tv(rtp->dtmf_duration, 8000), ast_tv(0, 0));
|
||||
rtp->resp = 0;
|
||||
+ rtp->dtmf_timeout = rtp->dtmf_duration = 0;
|
||||
return f;
|
||||
}
|
||||
}
|
||||
@@ -2085,7 +2120,7 @@
|
||||
rtp->lastevent = 0;
|
||||
rtp->lasttxformat = 0;
|
||||
rtp->lastrxformat = 0;
|
||||
- rtp->dtmfcount = 0;
|
||||
+ rtp->dtmf_timeout = 0;
|
||||
rtp->seqno = 0;
|
||||
rtp->rxseqno = 0;
|
||||
}
|
||||
@@ -3848,7 +3883,7 @@
|
||||
}
|
||||
if ((s = ast_variable_retrieve(cfg, "general", "dtmftimeout"))) {
|
||||
dtmftimeout = atoi(s);
|
||||
- if ((dtmftimeout < 0) || (dtmftimeout > 20000)) {
|
||||
+ if ((dtmftimeout < 0) || (dtmftimeout > 64000)) {
|
||||
ast_log(LOG_WARNING, "DTMF timeout of '%d' outside range, using default of '%d' instead\n",
|
||||
dtmftimeout, DEFAULT_DTMF_TIMEOUT);
|
||||
dtmftimeout = DEFAULT_DTMF_TIMEOUT;
|
||||
@ -0,0 +1,11 @@
|
||||
--- main/say.c 2009-07-08 12:26:15.000000000 -0400
|
||||
+++ main/say.c 2009-10-21 20:40:46.930743413 -0400
|
||||
@@ -4123,6 +4123,8 @@
|
||||
/* 12-Hour */
|
||||
if (tm.tm_hour == 0)
|
||||
snprintf(nextmsg,sizeof(nextmsg), "digits/12");
|
||||
+ else if (tm.tm_hour == 1 || tm.tm_hour == 13)
|
||||
+ snprintf(nextmsg,sizeof(nextmsg), "digits/1F");
|
||||
else if (tm.tm_hour > 12)
|
||||
snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
|
||||
else
|
||||
@ -0,0 +1,11 @@
|
||||
--- channels/chan_sip.c (revisión: 191558)
|
||||
+++ channels/chan_sip.c (revisión: 191559)
|
||||
@@ -3420,7 +3420,7 @@
|
||||
case 409: /* Conflict */
|
||||
return AST_CAUSE_NORMAL_TEMPORARY_FAILURE;
|
||||
case 410: /* Gone */
|
||||
- return AST_CAUSE_UNALLOCATED;
|
||||
+ return AST_CAUSE_NUMBER_CHANGED;
|
||||
case 411: /* Length required */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 413: /* Request entity too large */
|
||||
@ -0,0 +1,18 @@
|
||||
--- include/asterisk/utils.h (revisión: 199624)
|
||||
+++ include/asterisk/utils.h (revisión: 199856)
|
||||
@@ -262,12 +262,12 @@
|
||||
|| (sin1->sin_port != sin2->sin_port));
|
||||
}
|
||||
|
||||
-#define AST_STACKSIZE 240 * 1024
|
||||
+#define AST_STACKSIZE (((sizeof(void *) * 8 * 8) - 16) * 1024)
|
||||
|
||||
#if defined(LOW_MEMORY)
|
||||
-#define AST_BACKGROUND_STACKSIZE 48 * 1024
|
||||
+#define AST_BACKGROUND_STACKSIZE (((sizeof(void *) * 8 * 2) - 16) * 1024)
|
||||
#else
|
||||
-#define AST_BACKGROUND_STACKSIZE 240 * 1024
|
||||
+#define AST_BACKGROUND_STACKSIZE AST_STACKSIZE
|
||||
#endif
|
||||
|
||||
void ast_register_thread(char *name);
|
||||
@ -0,0 +1,75 @@
|
||||
--- res/res_features.c 2010-01-05 15:53:50.000000000 +0100
|
||||
+++ res/res_features.c 2010-01-05 16:08:22.000000000 +0100
|
||||
@@ -964,22 +961,55 @@
|
||||
return FEATURE_RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
- if (check_compat(transferer, newchan)) {
|
||||
- /* we do mean transferee here, NOT transferer */
|
||||
- finishup(transferee);
|
||||
- return -1;
|
||||
- }
|
||||
- memset(&bconfig,0,sizeof(struct ast_bridge_config));
|
||||
- ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
|
||||
- ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
|
||||
- res = ast_bridge_call(transferer, newchan, &bconfig);
|
||||
- if (newchan->_softhangup || !transferer->_softhangup) {
|
||||
- ast_hangup(newchan);
|
||||
- if (ast_stream_and_wait(transferer, xfersound, transferer->language, ""))
|
||||
- ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
|
||||
- finishup(transferee);
|
||||
- transferer->_softhangup = 0;
|
||||
- return FEATURE_RETURN_SUCCESS;
|
||||
+ if (!ast_check_hangup(transferer)) {
|
||||
+ if (check_compat(transferer, newchan)) {
|
||||
+ /* we do mean transferee here, NOT transferer */
|
||||
+ finishup(transferee);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ memset(&bconfig,0,sizeof(struct ast_bridge_config));
|
||||
+ ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
|
||||
+ ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
|
||||
+ res = ast_bridge_call(transferer, newchan, &bconfig);
|
||||
+ if (newchan->_softhangup || !transferer->_softhangup) {
|
||||
+ ast_hangup(newchan);
|
||||
+ if (ast_stream_and_wait(transferer, xfersound, transferer->language, ""))
|
||||
+ ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
|
||||
+ finishup(transferee);
|
||||
+ transferer->_softhangup = 0;
|
||||
+ return FEATURE_RETURN_SUCCESS;
|
||||
+ }
|
||||
+ } else {
|
||||
+ ast_log(LOG_DEBUG, "transferer hangup; outstate = %d\n", outstate);
|
||||
+ switch (outstate) {
|
||||
+ case AST_CONTROL_RINGING:
|
||||
+ {
|
||||
+ int connected = 0;
|
||||
+ while (!connected && (ast_waitfor(newchan, -1) >= 0)) {
|
||||
+ f = ast_read(newchan);
|
||||
+ if (f == NULL)
|
||||
+ break;
|
||||
+ if (f->frametype == AST_FRAME_CONTROL
|
||||
+ && f->subclass == AST_CONTROL_ANSWER)
|
||||
+ connected = 1;
|
||||
+ ast_frfree(f);
|
||||
+ }
|
||||
+ if (!connected) {
|
||||
+ ast_hangup(newchan);
|
||||
+ finishup(transferee);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ /* fall through */
|
||||
+ }
|
||||
+ case AST_CONTROL_ANSWER:
|
||||
+ ast_log(LOG_DEBUG, "transferer hangup; callee answered\n");
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ ast_hangup(newchan);
|
||||
+ finishup(transferee);
|
||||
+ return FEATURE_RETURN_SUCCESS;
|
||||
+ }
|
||||
}
|
||||
|
||||
if (check_compat(transferee, newchan)) {
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
--- apps/app_voicemail.c (revisión: 193954)
|
||||
+++ apps/app_voicemail.c (revisión: 193955)
|
||||
@@ -7441,6 +7441,10 @@
|
||||
/* If ADSI is supported, setup login screen */
|
||||
adsi_begin(chan, &useadsi);
|
||||
|
||||
+ if (!valid) {
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
#ifdef IMAP_STORAGE
|
||||
pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
|
||||
pthread_setspecific(ts_vmstate.key, &vms);
|
||||
@@ -7452,9 +7456,6 @@
|
||||
vmstate_insert(&vms);
|
||||
init_vm_state(&vms);
|
||||
#endif
|
||||
- if (!valid)
|
||||
- goto out;
|
||||
-
|
||||
if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
|
||||
/* TODO: Handle memory allocation failure */
|
||||
}
|
||||
@@ -7869,7 +7870,9 @@
|
||||
}
|
||||
/* before we delete the state, we should copy pertinent info
|
||||
* back to the persistent model */
|
||||
- vmstate_delete(&vms);
|
||||
+ if (vmu) {
|
||||
+ vmstate_delete(&vms);
|
||||
+ }
|
||||
#endif
|
||||
if (vmu)
|
||||
free_user(vmu);
|
||||
@ -0,0 +1,11 @@
|
||||
--- apps/app_voicemail.c (revisión: 203718)
|
||||
+++ apps/app_voicemail.c (revisión: 203719)
|
||||
@@ -8510,7 +8510,7 @@
|
||||
if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
|
||||
if (sscanf(s, "%d", &x) == 1) {
|
||||
vmminmessage = x;
|
||||
- if (maxsilence <= vmminmessage)
|
||||
+ if (maxsilence / 1000 >= vmminmessage)
|
||||
ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Invalid min message time length\n");
|
||||
Loading…
Reference in new issue