diff -r -u asterisk-1.6.2.6/apps/app_dial.c asterisk-1.6.2.6-t38gw/apps/app_dial.c
--- asterisk-1.6.2.6/apps/app_dial.c 2010-02-02 21:35:44.000000000 +0100
+++ asterisk-1.6.2.6-t38gw/apps/app_dial.c 2010-03-15 15:39:46.000000000 +0100
@@ -93,6 +93,10 @@
Play an announcement to the called party, where x is the prompt to be played
+
@@ -472,6 +476,7 @@
OPT_GO_ON = (1 << 5),
OPT_CALLEE_HANGUP = (1 << 6),
OPT_CALLER_HANGUP = (1 << 7),
+ OPT_BRIDGE_T38 = (1 << 8),
OPT_DURATION_LIMIT = (1 << 9),
OPT_MUSICBACK = (1 << 10),
OPT_CALLEE_MACRO = (1 << 11),
@@ -521,6 +526,7 @@
AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS
AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE),
+ 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),
@@ -2255,7 +2261,17 @@
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 app_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);
diff -r -u asterisk-1.6.2.6/apps/app_fax.c asterisk-1.6.2.6-t38gw/apps/app_fax.c
--- asterisk-1.6.2.6/apps/app_fax.c 2010-02-09 19:09:34.000000000 +0100
+++ asterisk-1.6.2.6-t38gw/apps/app_fax.c 2010-03-15 15:58:51.000000000 +0100
@@ -7,6 +7,22 @@
*
* Code based on original implementation by Steve Underwood
*
+ * Initial T.38-gateway code
+ * 2008, Daniel Ferenci
+ * 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
+ * dns Telecom http://www.dnstelecom.co.za
+ *
+ * Modified to make T.38-gateway compatible with Asterisk 1.6.2
+ * 2010, Anton Verevkin
+ * ViaNetTV http://www.vianettv.com
+ *
+ * Modified to make T.38-gateway work
+ * 2010, Klaus Darilion, IPCom GmbH, www.ipcom.at
+ *
* This program is free software, distributed under the terms of
* the GNU General Public License
*
@@ -35,6 +51,7 @@
#include
#include "asterisk/lock.h"
+#include "asterisk/features.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
@@ -138,13 +155,37 @@
-
+
+
+ T.38 Gateway Handling
+
+
+
+ Dial string in format (technology/[device:]number1)
+
+
+ Specifies the number of seconds we attempt to dial the specified devices
+ If not specified, this defaults to 35 seconds.
+
+
+
+ Dials specified channel and bridges voice and fax. If both channels use voice or
+ T.38, then FaxGateway just acts as a thumb 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.
+ Use instead of Dial application.
+ 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.
+
+
***/
static char *app_sndfax_name = "SendFAX";
static char *app_rcvfax_name = "ReceiveFAX";
+static char *app_t38gateway_name = "FaxGateway";
#define MAX_SAMPLES 240
+#define ENGAGE_UDPTL_NAT_RETRY 3
/* Watchdog. I have seen situations when remote fax disconnects (because of poor line
quality) while SpanDSP continues staying in T30_STATE_IV_CTC state forever.
@@ -157,6 +198,7 @@
typedef struct {
struct ast_channel *chan;
+ struct ast_channel *peer;
enum ast_t38_state t38state; /* T38 state of the channel */
int direction; /* Fax direction: 0 - receiving, 1 - sending */
int caller_mode;
@@ -191,6 +233,8 @@
AST_FRAME_SET_BUFFER(&outf, buf, 0, len);
+ ast_debug(9, "sending T.38 frame (%d/%d) with len=%d on channel %s\n", outf.frametype, outf.subclass, outf.datalen, chan->name);
+
if (ast_write(chan, &outf) < 0) {
ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno));
return -1;
@@ -825,6 +869,271 @@
return res;
}
+static void ast_t38_gateway(fax_session *s)
+{
+ int timeout, i;
+// int samples, len;
+ struct ast_channel *active = NULL;
+ struct ast_channel *inactive = NULL;
+ enum ast_t38_state activestate, inactivestate;
+ struct ast_frame *f;
+ t38_core_state_t *t38dsp;
+ struct ast_channel *channels[2];
+// int16_t buffer[T38_MAX_HDLC_LEN];
+// int res, original_read_fmt, original_write_fmt;
+ t38_gateway_state_t t38_state;
+ t38_stats_t t38_stats;
+
+// struct ast_frame outf = {
+// .frametype = AST_FRAME_VOICE,
+// .subclass = AST_FORMAT_SLINEAR,
+// .src = "T38Gateway",
+// };
+
+ ast_debug(1, "T.38-Gateway starting for chan %s and peer %s\n", s->chan->name, s->peer->name);
+
+ /* channels[0] is the T.38 channel, channels[1] is the voice channel */
+ if (ast_channel_get_t38_state(s->chan) == T38_STATE_NEGOTIATED) {
+ channels[0]=s->chan;
+ channels[1]=s->peer;
+ } else {
+ channels[0]=s->peer;
+ channels[1]=s->chan;
+ }
+
+#if SPANDSP_RELEASE_DATE >= 20081012
+ /* for spandsp shaphots 0.0.6 and higher */
+ t38dsp=&t38_state.t38x.t38;
+#else
+ /* for spandsp release 0.0.5 */
+ t38dsp=&t38_state.t38;
+#endif
+
+// /* shouldn't be necessary as formats are already set in t38gateway_exec */
+// original_read_fmt = s->chan->readformat;
+// if (original_read_fmt != AST_FORMAT_SLINEAR) {
+// res = ast_set_read_format(s->chan, AST_FORMAT_SLINEAR);
+// if (res < 0) {
+// ast_log(LOG_ERROR, "Unable to set to linear read mode, giving up\n");
+// s->finished=-1;
+// return;
+// }
+// }
+//
+// original_write_fmt = s->chan->writeformat;
+// if (original_write_fmt != AST_FORMAT_SLINEAR) {
+// res = ast_set_write_format(s->chan, AST_FORMAT_SLINEAR);
+// if (res < 0) {
+// ast_log(LOG_ERROR, "Unable to set to linear write mode, giving up\n");
+// s->finished=-1;
+// return;
+// }
+// }
+
+ if (t38_gateway_init(&t38_state, t38_tx_packet_handler, channels[0])) {
+
+ t38_gateway_set_transmit_on_idle(&t38_state, TRUE);
+
+ span_log_set_message_handler(&t38_state.logging, span_message);
+ span_log_set_message_handler(&t38dsp->logging, span_message);
+ span_log_set_level(&t38_state.logging, SPAN_LOG_WARNING + option_debug);
+ span_log_set_level(&t38dsp->logging, SPAN_LOG_WARNING + option_debug);
+
+ t38_set_t38_version(t38dsp, 0);
+ t38_gateway_set_ecm_capability(&t38_state, TRUE);
+ t38_set_sequence_number_handling(t38dsp, TRUE);
+ t38_gateway_set_supported_modems(&t38_state, T30_SUPPORT_V27TER | T30_SUPPORT_V17 | T30_SUPPORT_V29);
+
+ /* engage udptl nat on other side of T38 line
+ * (Asterisk changes media ports thus we send a few packets to reinitialize
+ * pinholes in NATs and FWs
+ */
+ for (i=0; i= 20091228
+ t38_core_send_indicator(&t38_state.t38x.t38, T38_IND_NO_SIGNAL);
+#else
+#if SPANDSP_RELEASE_DATE >= 20081012
+ t38_core_send_indicator(&t38_state.t38x.t38, T38_IND_NO_SIGNAL, t38_state.t38x.t38.indicator_tx_count);
+#else
+ t38_core_send_indicator(&t38_state.t38, T38_IND_NO_SIGNAL, t38_state.t38.indicator_tx_count);
+#endif
+#endif
+ }
+
+ for(;;) {
+ timeout=20;
+ active = ast_waitfor_n(channels, 2, &timeout);
+ if (active) {
+ inactive = (active == channels[0]) ? channels[1] : channels[0];
+ activestate = ast_channel_get_t38_state(active);
+ inactivestate = ast_channel_get_t38_state(inactive);
+
+ if ((activestate == inactivestate)
+ || ((activestate != T38_STATE_NEGOTIATED) && (inactivestate != T38_STATE_NEGOTIATED)))
+ {
+ ast_debug(1, "Gatewaying finished for bridge state0 %d state1 %d\n", activestate, inactivestate);
+ break;
+ }
+
+ f = ast_read(active);
+ if (f) {
+ if (active == channels[0]) {
+ /* packet received from T38 channel */
+ if (f->frametype == AST_FRAME_MODEM && f->subclass == AST_MODEM_T38) {
+ if (t38_core_rx_ifp_packet(t38dsp, f->data.ptr, f->datalen, f->seqno)) {
+ ast_log(LOG_ERROR, "Error processing T.38 packet, terminating T.38-Gateway bridge.\n");
+ s->finished=-1;
+ break;
+ }
+ }
+ } else {
+ /* packet received from non-T38 channel */
+ /* we should not be T.38 if we are something went wrong with T.38 negotiation*/
+ if (f->frametype == AST_FRAME_MODEM && f->subclass == AST_MODEM_T38) {
+ ast_log(LOG_ERROR, "Error: voice channel mutated into T.38 channel, terminating T.38-Gateway bridge.\n");
+ s->finished=-1;
+ break;
+ }
+ t38_gateway_rx(&t38_state, f->data.ptr, f->samples);
+ /* Every time we receive a voice frame, we also generate a voice frame.
+ * Thus, we rely on the voice-peer to make proper RTP timing
+ */
+// /* Note: this might cause problems if the received frame contains more samples
+// * than T38_MAX_HDLC_LEN.
+// */
+// samples = (f->samples <= T38_MAX_HDLC_LEN) ? f->samples : T38_MAX_HDLC_LEN;
+// if ((len = t38_gateway_tx(&t38_state, buffer, samples))) {
+// AST_FRAME_SET_BUFFER(&outf, buffer, 0, len*sizeof(int16_t));
+// outf.samples=len;
+// ast_write(channels[1], &outf);
+// }
+ if ((f->samples = t38_gateway_tx(&t38_state, f->data.ptr, f->samples))) {
+ f->datalen = f->samples * sizeof(int16_t);
+ f->src = "T38Gateway";
+ ast_write(channels[1], f);
+ }
+ }
+ ast_frfree(f);
+ } else {
+ ast_debug(1, "Error reading frame on %s, stop bridging as the call has finished.\n", active->name);
+ s->finished=-1;
+ break;
+ }
+ }
+ }
+
+ t38_gateway_get_transfer_statistics(&t38_state, &t38_stats);
+ ast_debug(1, "Connection Statistics\n\tBit Rate :%i\n\tECM : %s\n\tPages : %i\n",t38_stats.bit_rate, (t38_stats.error_correcting_mode?"Yes":"No"), t38_stats.pages_transferred);
+ } else {
+ ast_log(LOG_ERROR, "T.38 gateway failed to init\n");
+ s->finished=-1;
+ }
+}
+
+static void ast_bridge_frames(fax_session *s)
+{
+ struct ast_dsp *dsp = NULL;
+ struct ast_channel *active = NULL;
+ struct ast_channel *inactive = NULL;
+ struct ast_channel *channels[2]={s->chan, s->peer};
+ enum ast_t38_state activestate, inactivestate;
+ struct ast_frame *f;
+ int res, timeout, ftone=0;
+ struct ast_control_t38_parameters t38_parameters = { .version = 0,
+ .max_ifp = 800,
+ .rate = AST_T38_RATE_9600,
+ .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", s->chan->name, s->peer->name);
+
+ for(;;) {
+ timeout=20;
+ if ((active = ast_waitfor_n(channels, 2, &timeout))) {
+ inactive = (active == channels[0]) ? channels[1] : channels[0];
+ activestate = ast_channel_get_t38_state(active);
+ inactivestate = ast_channel_get_t38_state(inactive);
+
+ /* Leave and gateway if both channels are in a stable T.38 (not negotiating) state and only one of them ist negotiated! */
+ if ( ((activestate == T38_STATE_NEGOTIATED) && (inactivestate != T38_STATE_NEGOTIATED && inactivestate != T38_STATE_NEGOTIATING))
+ ||
+ ((inactivestate == T38_STATE_NEGOTIATED) && (activestate != T38_STATE_NEGOTIATED && activestate != T38_STATE_NEGOTIATING))
+ )
+ {
+ ast_debug(1, "Bridging finished for gateway as activestate=%d and inactivestate=%d\n", activestate, inactivestate);
+ break;
+ }
+ if ((f = ast_read(active))) {
+ /* Check if one of the channels has refused T.38 negotiation. If yes, fake a "NEGOTIATED" frame
+ * so that the bridged channel will finish T.38 negotiation. Once the negotiation is settled down
+ * the above code will exit the loop and start T.38 gatewaying.
+ */
+ if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_T38_PARAMETERS)) {
+ if (f->datalen != sizeof(struct ast_control_t38_parameters)) {
+ ast_debug(1, "======== invalid T38 control frame on active channel %s\n", active->name);
+ } else {
+ struct ast_control_t38_parameters *parameters = f->data.ptr;
+ if (parameters->request_response == AST_T38_REFUSED) {
+ ast_debug(1, "Change the AST_T38_REFUSED control message receive from %s into AST_T38_NEGOTIATED and send it to %s!\n", active->name, inactive->name);
+ parameters->request_response = AST_T38_NEGOTIATED;
+ parameters->max_ifp = 800;
+ parameters->version = 0,
+ parameters->rate = AST_T38_RATE_9600,
+// parameters->rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
+// parameters->fill_bit_removal = 1,
+// parameters->transcoding_mmr = 1,
+// parameters->transcoding_jbig = 1,
+ ast_write(inactive, f);
+ ast_frfree(f);
+ continue;
+ }
+ }
+ }
+
+ /* Dont send packets to a channel negotiating T.38 ignore them*/
+ if ((inactivestate != T38_STATE_NEGOTIATED) && (inactivestate != T38_STATE_NEGOTIATING)) {
+ if ((dsp) && (inactivestate == T38_STATE_UNKNOWN)) {
+ f = ast_dsp_process(active, dsp, f);
+ if ((f->frametype == AST_FRAME_DTMF) &&
+ ((f->subclass == 'f') || (f->subclass == 'e'))) {
+ switch (f->subclass) {
+ case 'f':ftone=1;break;
+ case 'e':ftone=2;
+ }
+ /* tickle the channel as we have fax tone */
+ ast_debug(1, "Fax %s Tone Detected On %s\n", (ftone == 1) ? "CNG" : "CED", active->name);
+ t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
+ res=ast_indicate_data(inactive, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
+ ast_debug(1, "T.38 negotiation requested on channel %s returned %d\n", inactive->name, res);
+ } else {
+ ast_write(inactive, f);
+ }
+ } else
+ ast_write(inactive, f);
+ }
+ ast_frfree(f);
+ } else {
+ ast_debug(1, "Error reading frame on %s, stop bridging as the call has finished.\n", active->name);
+ s->finished=1;
+ break;
+ }
+ }
+ }
+ if (dsp)
+ ast_dsp_free(dsp);
+}
+
/* === Application functions === */
static int sndfax_exec(struct ast_channel *chan, void *data)
@@ -915,12 +1224,288 @@
return res;
}
+/*!
+ * \brief return the first unlocked cdr in a possible chain (copy-pasted from features.c)
+ */
+static struct ast_cdr *pick_unlocked_cdr(struct ast_cdr *cdr)
+{
+ struct ast_cdr *cdr_orig = cdr;
+ while (cdr) {
+ if (!ast_test_flag(cdr,AST_CDR_FLAG_LOCKED))
+ return cdr;
+ cdr = cdr->next;
+ }
+ return cdr_orig; /* everybody LOCKED or some other weirdness, like a NULL */
+}
+
+/*!
+ * \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;
+ fax_session *s;
+ struct ast_cdr *peer_cdr = pick_unlocked_cdr(peer->cdr); /* the proper chan cdr, if there are forked cdrs */
+
+ s=ast_malloc(sizeof(fax_session));
+ if (!s) {
+ ast_log(LOG_ERROR, "Error allocating memory, giving up.\n");
+ return -1;
+ }
+ if (!chan || !peer) {
+ ast_debug(1, "Fax channel is NULL Or No Fax Handler Possible. Giving up.\n");
+ ast_free(s);
+ return -1;
+ }
+
+ s->finished = 0;
+ s->chan = chan;
+ s->peer = peer;
+
+ pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
+ pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
+
+ /* pick up originating call */
+ if (chan->_state != AST_STATE_UP) {
+ res = ast_answer(chan);
+ if (res) {
+ ast_debug(1, "Could not answer channel '%s'. Can not run a gateway on a down channel\n", chan->name);
+ ast_free(s);
+ return res;
+ }
+ }
+
+ 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, s->peer->name, S_OR(chan->cid.cid_num, ""),
+ S_OR(chan->cid.cid_name, ""), chan->uniqueid,
+ s->peer->uniqueid, "");
+
+ /* Stop monitor and set channels to signed linear codec */
+ /* Klaus Darilion: no need to stop the monitor, as only voice frames will be recorded */
+ //ast_monitor_stop(chan, 1);
+ ast_set_write_format(s->chan, AST_FORMAT_SLINEAR);
+ ast_set_read_format(s->chan, AST_FORMAT_SLINEAR);
+ ast_set_write_format(s->peer, AST_FORMAT_SLINEAR);
+ ast_set_read_format(s->peer, AST_FORMAT_SLINEAR);
+
+ ast_channel_make_compatible(s->chan, s->peer);
+
+ /* start the gateway*/
+ /* switch over loop*/
+ while (s->finished == 0)
+ {
+ /* Start bridging packets until all sides have negotiated T.38 capabilities and only one is capable*/
+ ast_bridge_frames(s);
+ /* We are now T.38 One Side Gateway*/
+ if (!s->finished)
+ ast_t38_gateway(s);
+ }
+
+ if (s->finished < 0) {
+ ast_debug(1, "Transmission failed or call hung up\n");
+ pbx_builtin_setvar_helper(chan, "FAXSTATUS", "FAILED");
+ pbx_builtin_setvar_helper(chan, "FAXERROR", "Channel problems");
+ res = -1;
+ } else if (s->finished > 0) {
+ ast_debug(1, "Transmission finished Ok\n");
+ pbx_builtin_setvar_helper(chan, "FAXSTATUS", "PASSED");
+ pbx_builtin_setvar_helper(chan, "FAXERROR", "OK");
+ res = 0;
+ } else {
+ ast_log(LOG_WARNING, "Transmission error\n");
+ pbx_builtin_setvar_helper(chan, "FAXSTATUS", "FAILED");
+ pbx_builtin_setvar_helper(chan, "FAXERROR", "Transmission error");
+ res = -1;
+ }
+
+ ast_cdr_specialized_reset(peer_cdr,0);
+
+ ast_free(s);
+ return res;
+}
+
+static int t38gateway_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char *parse;
+ fax_session *s;
+ 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;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(dest);
+ AST_APP_ARG(timeout);
+ );
+
+ s=ast_malloc(sizeof(fax_session));
+ if (!s) {
+ ast_log(LOG_ERROR, "Error allocating memory, giving up.\n");
+ return -1;
+ }
+ if (!chan) {
+ ast_log(LOG_ERROR, "Channel is NULL, giving up.\n");
+ ast_free(s);
+ return -1;
+ }
+
+ s->finished = 0;
+ s->chan = chan;
+
+ parse = ast_strdupa(data);
+ 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");
+ ast_free(s);
+ 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->cid.cid_num))
+ cid_num=ast_strdupa(chan->cid.cid_num);
+ if (!ast_strlen_zero(chan->cid.cid_name))
+ cid_name=ast_strdupa(chan->cid.cid_name);
+ 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 (!(s->peer = __ast_request_and_dial(tech, AST_FORMAT_SLINEAR, numsubst, timeout, &state, chan->cid.cid_num, chan->cid.cid_name, &oh))) {
+ chan->hangupcause = state;
+ ast_free(s);
+ return -1;
+ }
+ ast_debug(1, "Outgoing channel '%s'\n", s->peer->name);
+
+ /* pick up originating call */
+ if (chan->_state != AST_STATE_UP) {
+ ast_debug(1, "Answering channel '%s'\n", chan->name);
+ res = ast_answer(chan);
+ if (res) {
+ ast_debug(1, "Could not answer channel '%s' cant run a gateway on a down channel\n", chan->name);
+ ast_free(s);
+ return res;
+ }
+ }
+
+ pbx_builtin_setvar_helper(chan, "DIALSTATUS", ast_strdup(ast_cause2str(state)));
+ 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, s->peer->name, S_OR(chan->cid.cid_num, ""),
+ S_OR(chan->cid.cid_name, ""), chan->uniqueid,
+ s->peer->uniqueid, number ? number : "");
+
+ if (chan->cdr)
+ ast_cdr_setdestchan(chan->cdr, s->peer->name);
+
+ /* Inherit context and extension */
+ if (!ast_strlen_zero(chan->macrocontext))
+ ast_string_field_set(s->peer, dialcontext, chan->macrocontext);
+ if (!ast_strlen_zero(chan->macroexten))
+ ast_copy_string(s->peer->exten, chan->macroexten, sizeof(s->peer->exten));
+ s->peer->macropriority=chan->macropriority;
+
+ /* Clear all channel variables which to be set by the application.
+ Pre-set status to error so in case of any problems we can just leave */
+ pbx_builtin_setvar_helper(s->peer, "DIALEDPEERNUMBER", numsubst);
+
+ pbx_builtin_setvar_helper(chan, "FAXSTATUS", "FAILED");
+ pbx_builtin_setvar_helper(chan, "FAXERROR", "Channel problems");
+
+ /* Stop monitor and set channels to signed linear codec */
+// ast_monitor_stop(chan, 1);
+ ast_set_write_format(s->chan, AST_FORMAT_SLINEAR);
+ ast_set_read_format(s->chan, AST_FORMAT_SLINEAR);
+ ast_set_write_format(s->peer, AST_FORMAT_SLINEAR);
+ ast_set_read_format(s->peer, AST_FORMAT_SLINEAR);
+
+ ast_channel_make_compatible(s->chan, s->peer);
+
+ /* start the gateway*/
+ /* switch over loop*/
+ while (s->finished == 0)
+ {
+ /* Start bridging packets until all sides have negotiated T.38 capabilities and only one is capable*/
+ ast_bridge_frames(s);
+ /* We are now T.38 One Side Gateway*/
+ if (!s->finished)
+ ast_t38_gateway(s);
+ }
+
+ if (s->finished < 0) {
+ ast_log(LOG_WARNING, "Transmission failed\n");
+ pbx_builtin_setvar_helper(chan, "FAXSTATUS", "FAILED");
+ pbx_builtin_setvar_helper(chan, "FAXERROR", "Channel problems");
+ res = -1;
+ } else if (s->finished > 0) {
+ ast_debug(1, "Transmission finished Ok\n");
+ pbx_builtin_setvar_helper(chan, "FAXSTATUS", "PASSED");
+ pbx_builtin_setvar_helper(chan, "FAXERROR", "OK");
+ res = 0;
+ } else {
+ ast_log(LOG_WARNING, "Transmission error\n");
+ pbx_builtin_setvar_helper(chan, "FAXSTATUS", "FAILED");
+ pbx_builtin_setvar_helper(chan, "FAXERROR", "Transmission error");
+ res = -1;
+ }
+ if (s->peer) {
+ ast_hangup(s->peer);
+ }
+ ast_free(s);
+ return res;
+}
+
static int unload_module(void)
{
int res;
+ /* Unpublish __ast_bridge_t38 function */
+ ast_bridge_t38 = NULL;
+
res = ast_unregister_application(app_sndfax_name);
res |= ast_unregister_application(app_rcvfax_name);
+ res |= ast_unregister_application(app_t38gateway_name);
return res;
}
@@ -929,8 +1514,12 @@
{
int res ;
+ /* Publish __ast_bridge_t38 function to be used by Dial application */
+ ast_bridge_t38 = __ast_bridge_t38;
+
res = ast_register_application_xml(app_sndfax_name, sndfax_exec);
res |= ast_register_application_xml(app_rcvfax_name, rcvfax_exec);
+ res |= ast_register_application_xml(app_t38gateway_name, t38gateway_exec);
/* The default SPAN message handler prints to stderr. It is something we do not want */
span_set_message_handler(NULL);
diff -r -u asterisk-1.6.2.6/channels/chan_iax2.c asterisk-1.6.2.6-t38gw/channels/chan_iax2.c
--- asterisk-1.6.2.6/channels/chan_iax2.c 2010-03-03 19:05:00.000000000 +0100
+++ asterisk-1.6.2.6-t38gw/channels/chan_iax2.c 2010-03-15 10:33:32.000000000 +0100
@@ -5362,6 +5362,12 @@
ast_moh_stop(c);
goto done;
}
+ 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);
diff -r -u asterisk-1.6.2.6/channels/chan_sip.c asterisk-1.6.2.6-t38gw/channels/chan_sip.c
--- asterisk-1.6.2.6/channels/chan_sip.c 2010-03-03 01:19:57.000000000 +0100
+++ asterisk-1.6.2.6-t38gw/channels/chan_sip.c 2010-03-15 10:33:32.000000000 +0100
@@ -5050,7 +5050,7 @@
case T38_DISABLED:
if (old == T38_ENABLED) {
parameters.request_response = AST_T38_TERMINATED;
- } else if (old == T38_LOCAL_REINVITE) {
+ } else if (old == T38_LOCAL_REINVITE || old == T38_DISABLED) {
parameters.request_response = AST_T38_REFUSED;
}
break;
@@ -6424,6 +6424,7 @@
static void interpret_t38_parameters(struct sip_pvt *p, const struct ast_control_t38_parameters *parameters)
{
if (!ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) {
+ change_t38_state(p, T38_DISABLED);
return;
}
switch (parameters->request_response) {
@@ -6493,6 +6494,8 @@
struct sip_pvt *p = ast->tech_pvt;
int res = 0;
+ ast_debug(1, "Requested indication %d on channel %s\n", condition, ast->name);
+
sip_pvt_lock(p);
switch(condition) {
case AST_CONTROL_RINGING:
@@ -6573,6 +6576,12 @@
} else {
const struct ast_control_t38_parameters *parameters = data;
interpret_t38_parameters(p, parameters);
+ if (!ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) {
+ /* Explicitly signal back that the T.38 request was not handled
+ * (to avoid timeouts on bridged channels)
+ */
+ res = -1;
+ }
}
break;
case AST_CONTROL_SRCUPDATE:
diff -r -u asterisk-1.6.2.6/include/asterisk/features.h asterisk-1.6.2.6-t38gw/include/asterisk/features.h
--- asterisk-1.6.2.6/include/asterisk/features.h 2009-02-04 22:17:53.000000000 +0100
+++ asterisk-1.6.2.6-t38gw/include/asterisk/features.h 2010-03-15 15:41:34.000000000 +0100
@@ -107,6 +107,12 @@
/*! \brief Determine system call pickup extension */
const char *ast_pickup_ext(void);
+/*!
+ * \brief Bridge a call with fax gateway capabilities.
+ * Function is provided by app_fax.so and used in app_dial.so
+*/
+extern int (*ast_bridge_t38)(struct ast_channel *chan,struct ast_channel *peer);
+
/*! \brief Bridge a call, optionally allowing redirection */
int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct ast_bridge_config *config);
diff -r -u asterisk-1.6.2.6/main/channel.c asterisk-1.6.2.6-t38gw/main/channel.c
--- asterisk-1.6.2.6/main/channel.c 2010-02-16 18:10:42.000000000 +0100
+++ asterisk-1.6.2.6-t38gw/main/channel.c 2010-03-15 10:33:32.000000000 +0100
@@ -3197,13 +3197,16 @@
/* deprecated T.38 control frame */
return -1;
case AST_CONTROL_T38_PARAMETERS:
- /* there is no way to provide 'default' behavior for these
- * control frames, so we need to return failure, but there
- * is also no value in the log message below being emitted
- * since failure to handle these frames is not an 'error'
- * so just return right now.
- */
- return -1;
+ /* The channel does not know how to handle T38: Explicitly
+ * refuse the T38 request, so that the bridged channel can perform
+ * fallback to G.711 or an T.38-Gatewaying application can translate
+ */
+ {
+ struct ast_control_t38_parameters parameters = { .request_response = AST_T38_REFUSED };
+ ast_debug(1, "Channel %s was unable to handle indication %d, faking AST_T38_REFUSED\n", chan->name, condition);
+ ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, ¶meters, sizeof(parameters));
+ }
+ return 0;
case AST_CONTROL_RINGING:
ts = ast_get_indication_tone(chan->zone, "ring");
/* It is common practice for channel drivers to return -1 if trying
@@ -3466,6 +3469,16 @@
case AST_FRAME_CONTROL:
res = (chan->tech->indicate == NULL) ? 0 :
chan->tech->indicate(chan, fr->subclass, fr->data.ptr, fr->datalen);
+ if ( res && (fr->subclass==AST_CONTROL_T38_PARAMETERS) ) {
+ /* The channel does not know how to handle T38: Explicitly
+ * refuse the T38 request, so that the bridged channel can perform
+ * fallback to G.711 or an T.38-Gatewaying application can translate
+ */
+ struct ast_control_t38_parameters parameters = { .request_response = AST_T38_REFUSED };
+ ast_debug(1, "Channel %s was unable to handle AST_CONTROL_T38_PARAMETERS indication, faking AST_T38_REFUSED\n", chan->name);
+ ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, ¶meters, sizeof(parameters));
+ res = 0;
+ }
break;
case AST_FRAME_DTMF_BEGIN:
if (chan->audiohooks) {
diff -r -u asterisk-1.6.2.6/main/features.c asterisk-1.6.2.6-t38gw/main/features.c
--- asterisk-1.6.2.6/main/features.c 2010-03-02 20:26:35.000000000 +0100
+++ asterisk-1.6.2.6-t38gw/main/features.c 2010-03-15 15:42:23.000000000 +0100
@@ -2430,6 +2430,14 @@
}
/*!
+ * \brief bridge the call with fax gateway capabilities.
+ * \param chan,peer
+ *
+ * Function is provided by app_fax.so and used in app_dial.so
+ */
+int (*ast_bridge_t38)(struct ast_channel *chan,struct ast_channel *peer) = NULL;
+
+/*!
* \brief bridge the call and set CDR
* \param chan,peer,config
*