[asterisk-commits] irroot: branch irroot/patches r337964 - /team/irroot/patches/
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Sun Sep 25 09:03:54 CDT 2011
Author: irroot
Date: Sun Sep 25 09:03:49 2011
New Revision: 337964
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=337964
Log:
Patches for older versions
Added:
team/irroot/patches/distrotech-1.8.2.patch (with props)
team/irroot/patches/distrotech-1.8.3.patch (with props)
team/irroot/patches/distrotech-1.8.4.patch (with props)
team/irroot/patches/distrotech-1.8.5.patch (with props)
Added: team/irroot/patches/distrotech-1.8.2.patch
URL: http://svnview.digium.com/svn/asterisk/team/irroot/patches/distrotech-1.8.2.patch?view=auto&rev=337964
==============================================================================
--- team/irroot/patches/distrotech-1.8.2.patch (added)
+++ team/irroot/patches/distrotech-1.8.2.patch Sun Sep 25 09:03:49 2011
@@ -1,0 +1,1914 @@
+Index: channels/chan_dahdi.c
+===================================================================
+--- channels/chan_dahdi.c (revision 226)
++++ channels/chan_dahdi.c (working copy)
+@@ -9178,6 +9178,9 @@
+ case AST_CONTROL_SRCUPDATE:
+ res = 0;
+ break;
++ case AST_CONTROL_T38_PARAMETERS:
++ res = -1;
++ break;
+ case -1:
+ res = tone_zone_play_tone(p->subs[idx].dfd, -1);
+ break;
+Index: channels/chan_sip.c
+===================================================================
+--- channels/chan_sip.c (revision 226)
++++ channels/chan_sip.c (working copy)
+@@ -4068,6 +4068,9 @@
+ case T38_ENABLED:
+ state = T38_STATE_NEGOTIATED;
+ break;
++ case T38_REJECTED:
++ state = T38_STATE_UNAVAILABLE;
++ break;
+ default:
+ state = T38_STATE_UNKNOWN;
+ }
+@@ -4729,6 +4732,7 @@
+ parameters.request_response = AST_T38_NEGOTIATED;
+ ast_udptl_set_tag(p->udptl, "SIP/%s", p->username);
+ break;
++ case T38_REJECTED:
+ case T38_DISABLED:
+ if (old == T38_ENABLED) {
+ parameters.request_response = AST_T38_TERMINATED;
+@@ -6269,7 +6273,7 @@
+ case AST_T38_REQUEST_NEGOTIATE: /* Request T38 */
+ /* Negotiation can not take place without a valid max_ifp value. */
+ if (!parameters->max_ifp) {
+- change_t38_state(p, T38_DISABLED);
++ change_t38_state(p, T38_REJECTED);
+ if (p->t38.state == T38_PEER_REINVITE) {
+ AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
+ transmit_response_reliable(p, "488 Not acceptable here", &p->initreq);
+@@ -6311,7 +6315,7 @@
+ case AST_T38_REQUEST_TERMINATE: /* Shutdown T38 */
+ if (p->t38.state == T38_PEER_REINVITE) {
+ AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
+- change_t38_state(p, T38_DISABLED);
++ change_t38_state(p, T38_REJECTED);
+ transmit_response_reliable(p, "488 Not acceptable here", &p->initreq);
+ } else if (p->t38.state == T38_ENABLED)
+ transmit_reinvite_with_sdp(p, FALSE, FALSE);
+@@ -6961,6 +6965,12 @@
+
+ sip_pvt_lock(p);
+ fr = sip_rtp_read(ast, p, &faxdetected);
++
++ if (((fr == &ast_null_frame) && p->rtp && !ast_rtp_instance_get_bridged(p->rtp)) || (!p->rtp)) {
++ sip_pvt_unlock(p);
++ return fr;
++ }
++
+ p->lastrtprx = time(NULL);
+
+ /* If we detect a CNG tone and fax detection is enabled then send us off to the fax extension */
+@@ -8607,7 +8617,7 @@
+ }
+ }
+
+- if ((portno == -1) && (p->t38.state != T38_DISABLED)) {
++ if ((portno == -1) && (p->t38.state != T38_DISABLED) && (p->t38.state != T38_REJECTED)) {
+ ast_debug(3, "Have T.38 but no audio, accepting offer anyway\n");
+ return 0;
+ }
+@@ -18387,7 +18397,7 @@
+ } else if (!strcasecmp(data, "peername")) {
+ ast_copy_string(buf, p->peername, len);
+ } else if (!strcasecmp(data, "t38passthrough")) {
+- if (p->t38.state == T38_DISABLED) {
++ if ((p->t38.state == T38_DISABLED) || (p->t38.state == T38_REJECTED)) {
+ ast_copy_string(buf, "0", len);
+ } else { /* T38 is offered or enabled in this call */
+ ast_copy_string(buf, "1", len);
+@@ -19143,7 +19153,7 @@
+ case 606: /* Not Acceptable */
+ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+ if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) {
+- change_t38_state(p, T38_DISABLED);
++ change_t38_state(p, T38_REJECTED);
+ /* Try to reset RTP timers */
+ //ast_rtp_set_rtptimers_onhold(p->rtp);
+
+@@ -20807,7 +20817,7 @@
+ * want to abort the negotiation process
+ */
+ if (p->t38id != -1) {
+- change_t38_state(p, T38_DISABLED);
++ change_t38_state(p, T38_REJECTED);
+ transmit_response_reliable(p, "488 Not acceptable here", &p->initreq);
+ p->t38id = -1;
+ dialog_unref(p, "unref the dialog ptr from sip_t38_abort, because it held a dialog ptr");
+@@ -21678,7 +21688,7 @@
+ } else if (p->t38.state == T38_ENABLED) {
+ ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
+ transmit_response_with_t38_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL)));
+- } else if (p->t38.state == T38_DISABLED) {
++ } else if ((p->t38.state == T38_DISABLED) || (p->t38.state == T38_REJECTED)) {
+ /* If this is not a re-invite or something to ignore - it's critical */
+ if (p->srtp && !ast_test_flag(p->srtp, SRTP_CRYPTO_OFFER_OK)) {
+ ast_log(LOG_WARNING, "Target does not support required crypto\n");
+@@ -24345,14 +24355,10 @@
+ if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD) || (ast_rtp_instance_get_hold_timeout(dialog->rtp) && (t > dialog->lastrtprx + ast_rtp_instance_get_hold_timeout(dialog->rtp)))) {
+ /* Needs a hangup */
+ if (ast_rtp_instance_get_timeout(dialog->rtp)) {
+- while (dialog->owner && ast_channel_trylock(dialog->owner)) {
+- sip_pvt_unlock(dialog);
+- usleep(1);
+- sip_pvt_lock(dialog);
+- }
+- if (!dialog->owner) {
+- return; /* channel hangup can occur during deadlock avoidance. */
+- }
++ /* Don't block the monitor thread. This function is called often enough
++ * that we can wait for the next time around. */
++ if ((!dialog->owner) || (dialog->owner && ast_channel_trylock(dialog->owner)))
++ return;
+ ast_log(LOG_NOTICE, "Disconnecting call '%s' for lack of RTP activity in %ld seconds\n",
+ dialog->owner->name, (long) (t - dialog->lastrtprx));
+ /* Issue a softhangup */
+@@ -25692,6 +25698,11 @@
+ struct sip_mailbox *mailbox;
+ int duplicate = 0;
+
++ if (strstr("@",context) == NULL) {
++ strncat(context,"@",sizeof(context)-2);
++ strncat(context,peer->context,sizeof(context)-strlen(peer->context)-1);
++ }
++
+ strsep(&context, "@");
+
+ if (ast_strlen_zero(mbox)) {
+Index: channels/chan_iax2.c
+===================================================================
+--- channels/chan_iax2.c (revision 226)
++++ channels/chan_iax2.c (working copy)
+@@ -5623,7 +5623,12 @@
+ if (!ast_test_flag64(pvt, IAX_SENDCONNECTEDLINE))
+ goto done;
+ break;
+- }
++ case AST_CONTROL_T38_PARAMETERS:
++ /* Immediately signal back that T38 is not supported. Otherwise
++ * the bridged channel has to wait until a timeout. */
++ res = -1;
++ goto done;
++ }
+
+ res = send_command(pvt, AST_FRAME_CONTROL, condition, 0, data, datalen, -1);
+
+Index: channels/sip/include/sip.h
+===================================================================
+--- channels/sip/include/sip.h (revision 226)
++++ channels/sip/include/sip.h (working copy)
+@@ -592,7 +592,8 @@
+ T38_DISABLED = 0, /*!< Not enabled */
+ T38_LOCAL_REINVITE, /*!< Offered from local - REINVITE */
+ T38_PEER_REINVITE, /*!< Offered from peer - REINVITE */
+- T38_ENABLED /*!< Negotiated (enabled) */
++ T38_ENABLED, /*!< Negotiated (enabled) */
++ T38_REJECTED /*!< Refused */
+ };
+
+ /*! \brief Parameters to know status of transfer */
+Index: apps/app_faxgateway.c
+===================================================================
+--- apps/app_faxgateway.c (revision 0)
++++ apps/app_faxgateway.c (revision 0)
+@@ -0,0 +1,473 @@
++/*
++ * Asterisk -- An open source telephony toolkit.
++ *
++ * Copyright (C) 2008-2009, Digium, Inc.
++ *
++ * Initial T.38-gateway code
++ * 2008, Daniel Ferenci <daniel.ferenci at nethemba.com>
++ * Created by Nethemba s.r.o. http://www.nethemba.com
++ * Sponsored by IPEX a.s. http://www.ipex.cz
++ *
++ * T.38-gateway integration into asterisk app_fax and rework
++ * 2008, Gregory Hinton Nietsky <gregory at dnstelecom.co.za>
++ * dns Telecom http://www.dnstelecom.co.za
++ *
++ * Modified to make T.38-gateway compatible with Asterisk 1.6.2
++ * 2010, Anton Verevkin <mymail at verevkin.it>
++ * ViaNetTV http://www.vianettv.com
++ *
++ * Modified to make T.38-gateway work
++ * 2010, Klaus Darilion, IPCom GmbH, www.ipcom.at
++ *
++ * 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 Redundant fax gateway functions that are to be depricated
++ *
++ * \ingroup applications
++ */
++
++#include "asterisk.h"
++
++ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
++
++#include "asterisk/io.h"
++#include "asterisk/file.h"
++#include "asterisk/logger.h"
++#include "asterisk/module.h"
++#include "asterisk/app.h"
++#include "asterisk/lock.h"
++#include "asterisk/options.h"
++#include "asterisk/strings.h"
++#include "asterisk/cli.h"
++#include "asterisk/utils.h"
++#include "asterisk/config.h"
++#include "asterisk/astobj2.h"
++#include "asterisk/res_fax.h"
++#include "asterisk/file.h"
++#include "asterisk/channel.h"
++#include "asterisk/pbx.h"
++#include "asterisk/manager.h"
++#include "asterisk/dsp.h"
++#include "asterisk/indications.h"
++#include "asterisk/ast_version.h"
++#include "asterisk/features.h"
++
++/*** DOCUMENTATION
++ <application name="FaxGateway" language="en_US">
++ <synopsis>
++ T.38 Gateway Handling
++ </synopsis>
++ <syntax>
++ <parameter name="dialstring" required="true">
++ <para>Dial string in format (technology/[device:]number1)</para>
++ </parameter>
++ <parameter name="timeout" required="false">
++ <para>Specifies the number of seconds we attempt to dial the specified devices</para>
++ <para>If not specified, this defaults to 35 seconds.</para>
++ </parameter>
++ </syntax>
++ <description>
++ <para>Dials specified channel and bridges voice and fax. If both channels use voice or
++ T.38, then FaxGateway just acts as a dumb bridge. I one of the channels uses T.38 and
++ the other channel uses voice, then FaxGateway activates the T.38-Gateway and translates
++ between T.38 and voice.</para> + <para>Use instead of Dial application.</para>
++ <para>Note: This application is not yet complete as it does not forward indications (e.g. Rigning)
++ as long as the outgoing channel is not answered.</para>
++ </description>
++ </application>
++ <application name="FaxDetect" language="en_US">
++ <synopsis>
++ Generic Fax Detect CED/CNG/T.38
++ </synopsis>
++ <syntax>
++ <parameter name="timeout" required="true">
++ <para>Specifies the number of seconds we attempt to detect a fax tone on the channel</para>
++ </parameter>
++ </syntax>
++ <description>
++ <para>This application sets the following channel variables upon completion</para>
++ <para> FAXTONE - detected tone: CED | CNG | NONE</para>
++ <para> T38STATUS - T.38 negotiated: 0 | 1</para>
++ </description>
++ </application>
++***/
++
++static const char app_faxgateway[] = "FaxGateway";
++static const char app_faxdetect[] = "FaxDetect";
++
++/*! \brief Generic bridge enableing faxdetect/T.38 Negotiation */
++static int ast_bridge_frames(struct ast_channel *chan, struct ast_channel *peer)
++{
++ struct ast_dsp *dsp = NULL;
++ struct ast_channel *active = NULL;
++ struct ast_channel *inactive = NULL;
++ struct ast_channel *channels[2]={chan, peer};
++ struct ast_frame *f;
++ int timeout;
++ struct ast_control_t38_parameters t38_parameters = { .version = 0,
++ .max_ifp = 400,
++ .rate = AST_T38_RATE_14400,
++ .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
++ .fill_bit_removal = 1,
++ .transcoding_mmr = 1,
++ .transcoding_jbig = 1,
++ };
++
++ /* Setup DSP CNG/CED processing */
++ if ((dsp=ast_dsp_new())) {
++ ast_dsp_set_features(dsp, DSP_FEATURE_FAX_DETECT);
++ ast_dsp_set_faxmode(dsp, DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED);
++ } else {
++ ast_debug(1, "Unable to allocate Fax Detect DSP This may lead to problems with T.38 switchover!\n");
++ }
++
++ ast_debug(1, "Bridging started chan %s peer %s\n", chan->name, peer->name);
++
++ for(;;) {
++ timeout=1000;
++ if (!(active = ast_waitfor_n(channels, 2, &timeout))) {
++ if (!timeout) {
++ break;
++ }
++ continue;
++ }
++
++ if (!(f = ast_read(active))) {
++ ast_debug(1, "Error reading frame on %s, stop bridging as the call has finished.\n", active->name);
++ break;
++ }
++
++ if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_T38_PARAMETERS)) {
++ if ((ast_t38_gateway_handle_parameters == NULL) || (!ast_t38_gateway_handle_parameters(chan, peer, active, f))) {
++ ast_frfree(f);
++ continue;
++ }
++ }
++
++ inactive = (active == chan) ? peer : chan;
++
++ /* tickle the channel if we have fax tone */
++ if (dsp && ((f = ast_dsp_process(active, dsp, f))) && (f->frametype == AST_FRAME_DTMF)) {
++ switch (f->subclass.integer) {
++ case '5':
++ case 'f':
++ case 'e':
++ if (ast_channel_get_t38_state(active) == T38_STATE_UNKNOWN) {
++ t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
++ ast_indicate_data(active, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
++ }
++ if (ast_channel_get_t38_state(inactive) == T38_STATE_UNKNOWN) {
++ t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
++ ast_indicate_data(inactive, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
++ }
++ ast_frfree(f);
++ continue;
++ }
++ }
++
++ ast_write(inactive, f);
++ ast_frfree(f);
++ }
++ if (dsp) {
++ ast_dsp_free(dsp);
++ }
++ return 0;
++}
++
++/*!
++ * \brief Helper function called from app_dial.c
++ * \param chan,peer
++ */
++static int __ast_bridge_t38(struct ast_channel *chan, struct ast_channel *peer)
++{
++ int res = 0;
++ struct ast_cdr *peer_cdr=peer->cdr;
++ union ast_frame_subclass expected_framesubclass = { .integer = -1 };
++ unsigned int expected_frametype = -1;
++
++ while (peer_cdr) {
++ if (!ast_test_flag(peer_cdr,AST_CDR_FLAG_LOCKED))
++ break;
++ peer_cdr = peer_cdr->next;
++ }
++
++ if (! peer_cdr) {
++ peer_cdr=peer->cdr;
++ }
++
++ if (!chan || !peer) {
++ ast_debug(1, "Fax channel is NULL Or No Fax Handler Possible. Giving up.\n");
++ return -1;
++ }
++
++ /* pick up originating call */
++ if (chan->_state != AST_STATE_UP) {
++ if (ast_answer(chan)) {
++ ast_debug(1, "Could not answer channel '%s'. Can not run a gateway on a down channel\n", chan->name);
++ return -1;
++ }
++ }
++
++ pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
++ pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
++
++ manager_event(EVENT_FLAG_CALL, "Dial",
++ "SubEvent: Begin\r\n"
++ "Channel: %s\r\n"
++ "Destination: %s\r\n"
++ "CallerIDNum: %s\r\n"
++ "CallerIDName: %s\r\n"
++ "UniqueID: %s\r\n"
++ "DestUniqueID: %s\r\n"
++ "Dialstring: %s\r\n",
++ chan->name, peer->name,
++ S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, "<unknown>"),
++ S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, "<unknown>"),
++ chan->uniqueid,peer->uniqueid, "");
++
++ /* set the read format to slin to do fax detect*/
++ expected_frametype = AST_FRAME_VOICE;
++ expected_framesubclass.codec = AST_FORMAT_SLINEAR;
++ ast_set_read_format(chan, AST_FORMAT_SLINEAR);
++ ast_set_read_format(peer, AST_FORMAT_SLINEAR);
++ ast_channel_make_compatible(chan, peer);
++
++ /* start the gateway / switch over loop*/
++ /* Start bridging packets until all sides have negotiated T.38 capabilities and only one is capable*/
++ ast_bridge_frames(chan, peer);
++
++ if (peer) {
++ ast_cdr_specialized_reset(peer_cdr,0);
++ ast_hangup(peer);
++ }
++
++ return res;
++}
++
++static int faxgateway_exec(struct ast_channel *chan, const char *data)
++{
++ int res = 0;
++ char *parse;
++ struct ast_channel *peer;
++ int state, priority, timeout;
++ const char *account=NULL, *cid_name=NULL, *cid_num=NULL, *context=NULL, *exten=NULL;
++ struct ast_variable *vars=NULL;
++ struct outgoing_helper oh;
++ char *number, *tech;
++
++ parse = ast_strdupa(data);
++
++ AST_DECLARE_APP_ARGS(args,
++ AST_APP_ARG(dest);
++ AST_APP_ARG(timeout);
++ );
++ AST_STANDARD_APP_ARGS(args, parse);
++
++ /* Get a technology/[device:]number pair */
++ number = args.dest;
++ tech = strsep(&number, "/");
++ if (!number) {
++ ast_debug(1, "dialstring argument takes format (technology/[device:]number1)\n");
++ return -1;
++ }
++
++ if (args.timeout)
++ timeout = atoi(args.timeout) * 1000;
++ else
++ timeout = 35000;
++
++ char numsubst[256];
++ ast_copy_string(numsubst, number, sizeof(numsubst));
++
++ /* Setup the outgoing helper and dial waiting for timeout or answer*/
++ if (!ast_strlen_zero(chan->caller.id.number.str))
++ cid_num=ast_strdupa(chan->caller.id.number.str);
++ if (!ast_strlen_zero(chan->caller.id.name.str))
++ cid_name=ast_strdupa(chan->caller.id.name.str);
++ if (!ast_strlen_zero(chan->context))
++ context=ast_strdupa(chan->context);
++ if (!ast_strlen_zero(chan->exten))
++ exten=ast_strdupa(chan->exten);
++ priority=chan->priority;
++
++ oh.context = context;
++ oh.exten = exten;
++ oh.priority = priority;
++ oh.cid_num = cid_num;
++ oh.cid_name = cid_name;
++ oh.account = account;
++ oh.vars = vars;
++ oh.parent_channel = chan;
++ oh.parent_channel=chan;
++
++ if (!(peer = __ast_request_and_dial(tech, chan->nativeformats, NULL, numsubst, timeout, &state, chan->caller.id.number.str, chan->caller.id.name.str, &oh))) {
++ chan->hangupcause = state;
++ return -1;
++ }
++ ast_debug(1, "Outgoing channel '%s'\n", peer->name);
++
++ ast_channel_lock(chan);
++ ast_copy_string(chan->cdr->dstchannel, peer->name, sizeof(chan->cdr->dstchannel));
++ ast_channel_unlock(chan);
++
++ res=__ast_bridge_t38(chan, peer);
++
++ return res;
++}
++
++static int faxdetect_run(struct ast_channel *chan, int timeout, enum ast_t38_state *t38state)
++{
++ struct ast_dsp *dsp;
++ struct ast_frame *f;
++ int timeleft=timeout, res=0;
++ struct ast_control_t38_parameters t38_parameters = { .version = 0,
++ .max_ifp = 400,
++ .rate = AST_T38_RATE_14400,
++ .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
++ .fill_bit_removal = 1,
++ .transcoding_mmr = 1,
++ .transcoding_jbig = 1,
++ };
++
++ if (chan->_state != AST_STATE_UP)
++ ast_answer(chan);
++
++ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
++ if (res < 0) {
++ ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n");
++ return 0;
++ }
++
++ /* Setup DSP CNG/CED processing */
++ if ((dsp=ast_dsp_new())) {
++ ast_dsp_set_features(dsp, DSP_FEATURE_FAX_DETECT);
++ ast_dsp_set_faxmode(dsp, DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED);
++ } else {
++ ast_log(LOG_WARNING, "Unable to allocate All DSP Ignoring CNG/CED!\n");
++ return 0;
++ }
++
++ *t38state = ast_channel_get_t38_state(chan);
++
++ while((timeleft=ast_waitfor(chan, timeleft))) {
++ if (!(f=ast_read(chan))) {
++ break;
++ }
++ if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.codec == AST_FORMAT_SLINEAR)) {
++ f=ast_dsp_process(chan, dsp, f);
++ if ((f->frametype == AST_FRAME_DTMF) && ((f->subclass.integer == 'e') || (f->subclass.integer == 'f'))) {
++ switch (f->subclass.integer) {
++ case 'f':res=1;
++ case 'e':res=2;
++ }
++ if (*t38state == T38_STATE_UNKNOWN) {
++ t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
++ ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
++ }
++ }
++ }
++ ast_frfree(f);
++ if ((*t38state != T38_STATE_UNAVAILABLE) && (*t38state != T38_STATE_REJECTED) && (*t38state != T38_STATE_NEGOTIATED)) {
++ *t38state = ast_channel_get_t38_state(chan);
++ }
++ if (((res > 0) && (*t38state != T38_STATE_UNKNOWN) && (*t38state != T38_STATE_NEGOTIATING)) || (timeleft <= 0)) {
++ break;
++ }
++ }
++ if (dsp) {
++ ast_dsp_free(dsp);
++ }
++ return res;
++}
++
++static int faxdetect_exec(struct ast_channel *chan, const char *data)
++{
++ char *parse;
++ int timeout, tone;
++ enum ast_t38_state t38state = T38_STATE_UNKNOWN;
++
++ parse = ast_strdupa(data);
++
++ AST_DECLARE_APP_ARGS(args,
++ AST_APP_ARG(timeout);
++ );
++ AST_STANDARD_APP_ARGS(args, parse);
++
++ if (args.timeout) {
++ timeout = atoi(args.timeout) * 1000;
++ } else {
++ pbx_builtin_setvar_helper(chan, "FAXTONE", "NONE");
++ return 0;
++ }
++
++ tone=faxdetect_run(chan, timeout, &t38state);
++
++ if (t38state == T38_STATE_NEGOTIATED) {
++ pbx_builtin_setvar_helper(chan, "T38STATUS", "1");
++ } else {
++ pbx_builtin_setvar_helper(chan, "T38STATUS", "0");
++ }
++
++ switch (tone) {
++ case 0:
++ pbx_builtin_setvar_helper(chan, "FAXTONE", "NONE");
++ return 0;
++ case 1:
++ pbx_builtin_setvar_helper(chan, "FAXTONE", "CNG");
++ return 0;
++ case 2:
++ pbx_builtin_setvar_helper(chan, "FAXTONE", "CED");
++ return 0;
++ default:
++ return -1;
++ }
++}
++
++/*! \brief unload res_fax */
++static int unload_module(void)
++{
++ if (ast_unregister_application(app_faxgateway) < 0) {
++ ast_log(LOG_WARNING, "failed to unregister '%s'\n", app_faxgateway);
++ }
++
++ if (ast_unregister_application(app_faxdetect) < 0) {
++ ast_log(LOG_WARNING, "failed to unregister '%s'\n", app_faxdetect);
++ }
++
++ /* Unpublish __ast_bridge_t38 function */
++ ast_bridge_t38 = NULL;
++
++ return 0;
++}
++
++/*! \brief load res_fax */
++static int load_module(void)
++{
++ int res;
++
++ res = ast_register_application_xml(app_faxgateway, faxgateway_exec);
++ res |= ast_register_application_xml(app_faxdetect, faxdetect_exec);
++
++ /* Publish __ast_bridge_t38 function to be used by Dial application */
++ ast_bridge_t38 = __ast_bridge_t38;
++
++ return res;
++}
++
++
++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Generic FAX Applications",
++ .load = load_module,
++ .unload = unload_module,
++ .load_pri = AST_MODPRI_APP_DEPEND,
++ );
+Index: apps/app_directed_pickup.c
+===================================================================
+--- apps/app_directed_pickup.c (revision 226)
++++ apps/app_directed_pickup.c (working copy)
+@@ -141,13 +141,26 @@
+ ast_manager_event_multichan(EVENT_FLAG_CALL, "Pickup", 2, chans,
+ "Channel: %s\r\nTargetChannel: %s\r\n", chan->name, target->name);
+
++ struct ast_channel *bridge;
++ const char *macro;
++ const char *macro_args;
++
++ macro=pbx_builtin_getvar_helper(chan, "PICKUP_BRIDGE_MACRO");
++ macro_args=pbx_builtin_getvar_helper(chan, "PICKUP_BRIDGE_MACRO_ARGS");
++ if (!ast_strlen_zero(macro)) {
++ ast_channel_unlock(target);
++ while (target && !(bridge = ast_bridged_channel(target)))
++ usleep(1000);
++ ast_channel_lock(target);
++ ast_app_run_macro(NULL, bridge, macro, macro_args);
++ }
+ return res;
+ }
+
+ /* Helper function that determines whether a channel is capable of being picked up */
+ static int can_pickup(struct ast_channel *chan)
+ {
+- if (!chan->pbx && (chan->_state == AST_STATE_RINGING || chan->_state == AST_STATE_RING || chan->_state == AST_STATE_DOWN))
++ if (!chan->pbx && !chan->masq && (chan->_state == AST_STATE_RINGING || chan->_state == AST_STATE_RING))
+ return 1;
+ else
+ return 0;
+@@ -287,6 +300,30 @@
+ return res;
+ }
+
++static int find_channel_by_group(void *obj, void *arg, void *data, int flags)
++{
++ struct ast_channel *chan = obj;
++ struct ast_channel *c = data;
++
++ int i = (can_pickup(chan) && (c != chan) && (c->pickupgroup & chan->callgroup));
++
++ return i ? CMP_MATCH | CMP_STOP : 0;
++}
++
++static int pickup_by_group(struct ast_channel *chan)
++{
++ struct ast_channel *target=NULL;
++ int res=-1;
++
++ if ((target = ast_channel_callback(find_channel_by_group, NULL, chan, 0))) {
++ ast_channel_lock(target);
++ res=pickup_do(chan, target);
++ ast_channel_unlock(target);
++ target = ast_channel_unref(target);
++ }
++ return res;
++}
++
+ /* application entry point for Pickup() */
+ static int pickup_exec(struct ast_channel *chan, const char *data)
+ {
+@@ -295,7 +332,7 @@
+ char *exten = NULL, *context = NULL;
+
+ if (ast_strlen_zero(data)) {
+- res = ast_pickup_call(chan);
++ pickup_by_group(chan);
+ return res;
+ }
+
+Index: apps/app_dial.c
+===================================================================
+--- apps/app_dial.c (revision 226)
++++ apps/app_dial.c (working copy)
+@@ -106,6 +106,10 @@
+ channel before doing anything on the called channel. You will rarely need to use
+ this option, the default behavior is adequate in most cases.</para>
+ </option>
++ <option name="b">
++ <para>Bridge T.38 Fax. This disables other call features that are usually available after
++ the calling party answers. This includes transfer, call and DTMF recording,etc.</para>
++ </option>
+ <option name="C">
+ <para>Reset the call detail record (CDR) for this call.</para>
+ </option>
+@@ -559,6 +563,7 @@
+ #define OPT_CANCEL_TIMEOUT ((uint64_t)1 << 37)
+ #define OPT_FORCE_CID_TAG ((uint64_t)1 << 38)
+ #define OPT_FORCE_CID_PRES ((uint64_t)1 << 39)
++#define OPT_BRIDGE_T38 ((uint64_t)1 << 40)
+
+ enum {
+ OPT_ARG_ANNOUNCE = 0,
+@@ -584,6 +589,7 @@
+ AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS
+ AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE),
+ AST_APP_OPTION('a', OPT_CALLER_ANSWER),
++ AST_APP_OPTION('b', OPT_BRIDGE_T38),
+ AST_APP_OPTION('C', OPT_RESETCDR),
+ AST_APP_OPTION('c', OPT_CANCEL_ELSEWHERE),
+ AST_APP_OPTION('d', OPT_DTMF_EXIT),
+@@ -2638,8 +2644,14 @@
+
+ ast_channel_setoption(chan, AST_OPTION_OPRMODE, &oprmode, sizeof(oprmode), 0);
+ }
+- res = ast_bridge_call(chan, peer, &config);
+- }
++ /* Unless we want the T.38 fax bridge, use standard bridging function */
++ if (ast_test_flag64(&opts, OPT_BRIDGE_T38) && (ast_bridge_t38 == NULL))
++ ast_log(LOG_WARNING, "You need to load res_fax.so first to use the fax gateway option. Disabling T.38 gateway.\n");
++ if (!ast_test_flag64(&opts, OPT_BRIDGE_T38) || (ast_bridge_t38 == NULL))
++ res = ast_bridge_call(chan, peer, &config);
++ else
++ res = ast_bridge_t38(chan, peer);
++ }
+
+ strcpy(peer->context, chan->context);
+
+Index: apps/app_queue.c
+===================================================================
+--- apps/app_queue.c (revision 226)
++++ apps/app_queue.c (working copy)
+@@ -447,11 +447,25 @@
+ <enum name="count">
+ <para>Returns the total number of members for the specified queue.</para>
+ </enum>
++ <enum name="penalty">
++ <para>Gets or sets queue member penalty.</para>
++ </enum>
++ <enum name="paused">
++ <para>Gets or sets queue member paused status.</para>
++ </enum>
++ <enum name="ignorebusy">
++ <para>Gets or sets queue member ignorebusy.</para>
++ </enum>
+ </enumlist>
+ </parameter>
++ <parameter name="interface" required="false" />
+ </syntax>
+ <description>
+- <para>Returns the number of members currently associated with the specified <replaceable>queuename</replaceable>.</para>
++ <para>Allows access to queue counts [R] and member information [R/W].</para>
++ <para>
++ <replaceable>queuename</replaceable> is required for all operations
++ <replaceable>interface</replaceable> is required for all member operations.
++ </para>
+ </description>
+ </function>
+ <function name="QUEUE_MEMBER_COUNT" language="en_US">
+@@ -774,6 +788,9 @@
+ /*! \brief queues.conf [general] option */
+ static int update_cdr = 0;
+
++/*! \brief queues.conf [genral] option */
++static int negitive_penalty_invalid = 0;
++
+ enum queue_result {
+ QUEUE_UNKNOWN = 0,
+ QUEUE_TIMEOUT = 1,
+@@ -883,6 +900,7 @@
+ unsigned int dead:1; /*!< Used to detect members deleted in realtime */
+ unsigned int delme:1; /*!< Flag to delete entry on reload */
+ char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
++ unsigned int ignorebusy:1; /*!< Flag to ignore member if the status is not available */
+ };
+
+ enum empty_conditions {
+@@ -1000,6 +1018,7 @@
+ int timeout; /*!< How long to wait for an answer */
+ int weight; /*!< Respective weight */
+ int autopause; /*!< Auto pause queue members if they fail to answer */
++ int autopausedelay; /*!< Delay auto pause for autopausedelay seconds since last call */
+ int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */
+
+ /* Queue strategy things */
+@@ -1030,6 +1049,7 @@
+ static struct ao2_container *queues;
+
+ static void update_realtime_members(struct call_queue *q);
++static struct member *interface_exists(struct call_queue *q, const char *interface);
+ static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
+
+ static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
+@@ -1537,6 +1557,7 @@
+ q->numperiodicannounce = 0;
+ q->autopause = QUEUE_AUTOPAUSE_OFF;
+ q->timeoutpriority = TIMEOUT_PRIORITY_APP;
++ q->autopausedelay = 0;
+ if (!q->members) {
+ if (q->strategy == QUEUE_STRATEGY_LINEAR)
+ /* linear strategy depends on order, so we have to place all members in a single bucket */
+@@ -1842,6 +1863,8 @@
+ q->montype = 1;
+ } else if (!strcasecmp(param, "autopause")) {
+ q->autopause = autopause2int(val);
++ } else if (!strcasecmp(param, "autopausedelay")) {
++ q->autopausedelay = atoi(val);
+ } else if (!strcasecmp(param, "maxlen")) {
+ q->maxlen = atoi(val);
+ if (q->maxlen < 0)
+@@ -1913,14 +1936,22 @@
+ * Search for member in queue, if found update penalty/paused state,
+ * if no member exists create one flag it as a RT member and add to queue member list.
+ */
+-static void rt_handle_member_record(struct call_queue *q, char *interface, const char *rt_uniqueid, const char *membername, const char *penalty_str, const char *paused_str, const char* state_interface)
++static void rt_handle_member_record(struct call_queue *q, char *interface, struct ast_config *member_config)
+ {
+ struct member *m;
+ struct ao2_iterator mem_iter;
+ int penalty = 0;
+ int paused = 0;
+ int found = 0;
++ int ignorebusy = 0;
+
++ const char *config_val;
++ const char *rt_uniqueid=ast_variable_retrieve(member_config, interface, "uniqueid");
++ const char *membername=S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface);
++ const char *state_interface=S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface);
++ const char *penalty_str=ast_variable_retrieve(member_config, interface, "penalty");
++ const char *paused_str=ast_variable_retrieve(member_config, interface, "paused");
++
+ if (ast_strlen_zero(rt_uniqueid)) {
+ ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
+ return;
+@@ -1928,7 +1959,9 @@
+
+ if (penalty_str) {
+ penalty = atoi(penalty_str);
+- if (penalty < 0)
++ if ((penalty < 0) && negitive_penalty_invalid)
++ return;
++ else if (penalty < 0)
+ penalty = 0;
+ }
+
+@@ -1938,6 +1971,11 @@
+ paused = 0;
+ }
+
++ if ((config_val = ast_variable_retrieve(member_config, interface, "ignorebusy")))
++ ignorebusy=ast_true(config_val);
++ else
++ ignorebusy=1;
++
+ /* Find member by realtime uniqueid and update */
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((m = ao2_iterator_next(&mem_iter))) {
+@@ -1950,6 +1988,7 @@
+ ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
+ }
+ m->penalty = penalty;
++ m->ignorebusy = ignorebusy;
+ found = 1;
+ ao2_ref(m, -1);
+ break;
+@@ -1963,6 +2002,7 @@
+ if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
+ m->dead = 0;
+ m->realtime = 1;
++ m->ignorebusy = ignorebusy;
+ ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
+ ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
+ ao2_link(q->members, m);
+@@ -2138,12 +2178,7 @@
+ ao2_iterator_destroy(&mem_iter);
+
+ while ((interface = ast_category_browse(member_config, interface))) {
+- rt_handle_member_record(q, interface,
+- ast_variable_retrieve(member_config, interface, "uniqueid"),
+- S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
+- ast_variable_retrieve(member_config, interface, "penalty"),
+- ast_variable_retrieve(member_config, interface, "paused"),
+- S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
++ rt_handle_member_record(q, interface, member_config);
+ }
+
+ /* Delete all realtime members that have been deleted in DB. */
+@@ -2265,12 +2300,7 @@
+ ao2_iterator_destroy(&mem_iter);
+
+ while ((interface = ast_category_browse(member_config, interface))) {
+- rt_handle_member_record(q, interface,
+- ast_variable_retrieve(member_config, interface, "uniqueid"),
+- S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
+- ast_variable_retrieve(member_config, interface, "penalty"),
+- ast_variable_retrieve(member_config, interface, "paused"),
+- S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
++ rt_handle_member_record(q, interface, member_config);
+ }
+
+ /* Delete all realtime members that have been deleted in DB. */
+@@ -2707,7 +2737,7 @@
+ while ((mem = ao2_iterator_next(&mem_iter))) {
+ switch (mem->status) {
+ case AST_DEVICE_INUSE:
+- if (!q->ringinuse)
++ if ((!q->ringinuse) || (!mem->ignorebusy))
+ break;
+ /* else fall through */
+ case AST_DEVICE_NOT_INUSE:
+@@ -2856,7 +2886,7 @@
+ return 0;
+ }
+
+- if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
++ if ((!qe->parent->ringinuse || !tmp->member->ignorebusy) && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
+ ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
+ if (qe->chan->cdr)
+ ast_cdr_busy(qe->chan->cdr);
+@@ -3225,6 +3255,18 @@
+ }
+ ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
+ if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
++ if (qe->parent->autopausedelay > 0) {
++ struct member *mem;
++ ao2_lock(qe->parent);
++ if ((mem = interface_exists(qe->parent, interface))) {
++ time_t idletime = time(&idletime)-mem->lastcall;
++ if ((mem->lastcall != 0) && (qe->parent->autopausedelay > idletime)) {
++ ao2_unlock(qe->parent);
++ return;
++ }
++ }
++ ao2_unlock(qe->parent);
++ }
+ if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
+ if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
+ ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
+@@ -4996,7 +5038,10 @@
+ ao2_lock(q);
+ if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
+ /* XXX future changes should beware of this assumption!! */
+- if (!mem->dynamic) {
++ /*Change Penalty on realtime users*/
++ if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid) && negitive_penalty_invalid) {
++ update_realtime_member_field(mem, q->name, "penalty", "-1");
++ } else if (!mem->dynamic) {
+ ao2_ref(mem, -1);
+ ao2_unlock(q);
+ queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
+@@ -5172,6 +5217,7 @@
+ struct call_queue *q;
+ struct member *mem;
+ struct ao2_iterator queue_iter;
++ char rtpenalty[80];
+
+ if (penalty < 0) {
+ ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
+@@ -5185,8 +5231,12 @@
+ foundqueue++;
+ if ((mem = interface_exists(q, interface))) {
+ foundinterface++;
+- mem->penalty = penalty;
+-
++ if (!mem->realtime)
++ mem->penalty = penalty;
++ else {
++ sprintf(rtpenalty,"%i",penalty);
++ update_realtime_member_field(mem, q->name, "penalty", rtpenalty);
++ }
+ ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
+ "Queue: %s\r\n"
+@@ -5975,29 +6025,35 @@
+
+ /*!
+ * \brief Get number either busy / free / ready or total members of a specific queue
+- * \retval number of members (busy / free / ready / total)
++ * \brief Get or set member properties penalty / paused / ignorebusy
++ * \retval number of members (busy / free / ready / total) or member info (penalty / paused / ignorebusy)
+ * \retval -1 on error
+ */
+-static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
++static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+ {
+ int count = 0;
+ struct member *m;
+ struct ao2_iterator mem_iter;
+ struct call_queue *q;
+- char *option;
+
++ AST_DECLARE_APP_ARGS(args,
++ AST_APP_ARG(queuename);
++ AST_APP_ARG(option);
++ AST_APP_ARG(interface);
++ );
++ /* Make sure the returned value on error is NULL. */
++ buf[0] = '\0';
++
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
+ return -1;
+ }
+
+- if ((option = strchr(data, ',')))
+- *option++ = '\0';
+- else
+- option = "logged";
+- if ((q = load_realtime_queue(data))) {
++ AST_STANDARD_APP_ARGS(args, data);
++
++ if ((q = load_realtime_queue(args.queuename))) {
+ ao2_lock(q);
+- if (!strcasecmp(option, "logged")) {
++ if (!strcasecmp(args.option, "logged")) {
+ 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 */
+@@ -6007,7 +6063,7 @@
+ ao2_ref(m, -1);
+ }
+ ao2_iterator_destroy(&mem_iter);
+- } else if (!strcasecmp(option, "free")) {
++ } else if (!strcasecmp(args.option, "free")) {
+ 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 */
+@@ -6017,7 +6073,7 @@
+ ao2_ref(m, -1);
+ }
+ ao2_iterator_destroy(&mem_iter);
+- } else if (!strcasecmp(option, "ready")) {
++ } else if (!strcasecmp(args.option, "ready")) {
+ time_t now;
+ time(&now);
+ mem_iter = ao2_iterator_init(q->members, 0);
+@@ -6030,18 +6086,97 @@
+ ao2_ref(m, -1);
+ }
+ ao2_iterator_destroy(&mem_iter);
+- } else /* must be "count" */
++ } else if (!strcasecmp(args.option, "count") || ast_strlen_zero(args.option)) {
+ count = q->membercount;
++ } else if (!strcasecmp(args.option, "penalty") && !ast_strlen_zero(args.interface)) {
++ if ((m = interface_exists(q, args.interface)))
++ count = m->penalty;
[... 9652 lines stripped ...]
More information about the asterisk-commits
mailing list