--- ../clean/asterisk-1.8.3/res/res_fax.c 2011-01-26 18:31:16.000000000 +0200 +++ ./res/res_fax.c 2011-03-05 14:53:39.000000000 +0200 @@ -6,6 +6,22 @@ * Dwayne M. Hubbard * Kevin P. Fleming * + * 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 + * * 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; @@ -507,6 +523,13 @@ ast_build_string(&buf, &size, "MULTI_DOC"); first = 0; } + if (caps & AST_FAX_TECH_GATEWAY) { + if (!first) { + ast_build_string(&buf, &size, ","); + } + ast_build_string(&buf, &size, "T38_GATEWAY"); + first = 0; + } return out; } @@ -2280,6 +2303,248 @@ return (!channel_alive) ? -1 : 0; } +static int ast_t38_gateway(struct ast_channel *chan, struct ast_channel *peer) +{ + int res=-1; + int timeout; + struct ast_channel *active = NULL; + struct ast_channel *inactive = NULL; + enum ast_t38_state activestate, inactivestate; + struct ast_frame *f; + struct ast_channel *channels[2]; + struct ast_fax_session *s; + struct ast_fax_tech_token *token = NULL; + struct ast_fax_session_details *details; + union ast_frame_subclass expected_framesubclass = { .integer = -1 }; + unsigned int expected_frametype = -1; + + /* Get a FAX session details structure from the channel's FAX datastore this must exist at this point */ + if (!(details = find_details(chan))) { + ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1); + pbx_builtin_setvar_helper(chan, "FAXERROR", "MEMORY_ERROR"); + pbx_builtin_setvar_helper(chan, "FAXSTATUSSTRING", "error allocating memory"); + ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n"); + return res; + } + + if (!(s = fax_session_reserve(details, &token))) { + ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1); + ast_string_field_set(details, resultstr, "error reserving fax session"); + set_channel_variables(chan, details); + ast_log(LOG_ERROR, "Unable to reserve FAX session.\n"); + ao2_ref(details, -1); + return res; + } + + /* create the FAX session */ + if (!(s = fax_session_new(details, chan, s, token))) { + ast_log(LOG_ERROR, "Can't create a FAX session, FAX attempt failed.\n"); + report_fax_status(chan, details, "No Available Resource"); + return res; + } + + s->state = AST_FAX_STATE_ACTIVE; + s->chan = chan; + s->details = details; + + /* channels[0] is the T.38 channel, channels[1] is the voice channel */ + if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) { + channels[0]=chan; + channels[1]=peer; + } else { + channels[0]=peer; + channels[1]=chan; + } + + /*Set Read And Write To SLINEAR*/ + expected_frametype = AST_FRAME_VOICE;; + expected_framesubclass.codec = AST_FORMAT_SLINEAR; + ast_set_read_format(channels[1], AST_FORMAT_SLINEAR); + ast_set_write_format(channels[1], AST_FORMAT_SLINEAR); + + if (!s->tech->start_gw_session(s, channels[0])) { + ast_log(LOG_ERROR, "T.38 gateway failed to init\n"); + ao2_ref(s, -1); + ao2_ref(details, -1); + return res; + } + + ast_debug(1, "T.38 Gateway starting for chan %s and peer %s\n", chan->name, peer->name); + for(;;) { + timeout = 5000; + if (!(active = ast_waitfor_n(channels, 2, &timeout))) { + if (!timeout) { + ast_log(LOG_ERROR, "Channel Timeout\n"); + 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; + } + + inactive = (active == channels[0]) ? channels[1] : channels[0]; + activestate = ast_channel_get_t38_state(active); + inactivestate = ast_channel_get_t38_state(inactive); + + /* this is not right we have a broken link only one side must be negotioated */ + if ((activestate == inactivestate) || ((activestate != T38_STATE_NEGOTIATED) && (inactivestate != T38_STATE_NEGOTIATED))) { + ast_log(LOG_ERROR, "this is not right we have a broken link only one side must be negotioated\n"); + break; + } + + /* packet received from T38 channel */ + if ((activestate == T38_STATE_NEGOTIATED) && (f->frametype == AST_FRAME_MODEM) && (f->subclass.codec == AST_MODEM_T38)) { + if (s->tech->read_gw_t38(s, f)) { + ast_log(LOG_ERROR, "Error processing T.38 packet, terminating T.38-Gateway bridge.\n"); + ast_frfree(f); + break; + } + /* packet received from non-T38 channel */ + } else if ((activestate != T38_STATE_NEGOTIATED) && (f->frametype == AST_FRAME_VOICE)) { + if ((f->samples = s->tech->read_gw_t30(s, f))) { + f->datalen = f->samples * sizeof(int16_t); + f->src = "T38Gateway"; + ast_write(active, f); + } + /* we should not be T.38 if we are something went wrong with T.38 negotiation*/ + } else if ((activestate != T38_STATE_NEGOTIATED) && (f->frametype == AST_FRAME_MODEM) && (f->subclass.codec == AST_MODEM_T38)) { + ast_log(LOG_ERROR, "Error: voice channel (%s) mutated into T.38 channel, terminating T.38-Gateway bridge.\n", active->name); + ast_frfree(f); + break; + } + ast_frfree(f); + if ((f = s->tech->read(s))) { + ast_write(channels[0], f); + ast_frfree(f); + } + } + /*empty out the buffer*/ + while ((f = s->tech->read(s))) { + ast_write(channels[0], f); + ast_frfree(f); + } + s->tech->clean_gw_session(s); + s->state=AST_FAX_STATE_COMPLETE; + + if (s->state == AST_FAX_STATE_ACTIVE) { + 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->state == AST_FAX_STATE_COMPLETE) { + 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; + } + ao2_ref(s, -1); + ao2_ref(details, -1); + return res; +} + +/*! \brief T38 Gateway Negotiate t38 parameters callback */ +static int __ast_t38_gateway_handle_parameters(struct ast_channel *chan,struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f) +{ + struct ast_control_t38_parameters *active_param=NULL, tmp_param; + struct ast_fax_t38_parameters *t38_param_l, *t38_param_r; + struct ast_channel *inactive; + enum ast_t38_state activestate, inactivestate; + struct ast_fax_session_details *details; + int use_t38_gateway = 1; + + /* use some intelegence FAXOPT to enable/disable faxgateway */ + if (!use_t38_gateway) { + return 0; + } + + /* Get a FAX session details structure from the channel's FAX datastore and create one if it does not already exist. */ + if (!(details = find_or_create_details(chan))) { + ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1); + pbx_builtin_setvar_helper(chan, "FAXERROR", "MEMORY_ERROR"); + pbx_builtin_setvar_helper(chan, "FAXSTATUSSTRING", "error allocating memory"); + ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n"); + return 0; + } + + /* Let channel driver deal with bogus frames*/ + if (!(f->datalen == sizeof(struct ast_control_t38_parameters))) { + return 0; + } + + inactive = (active == chan) ? peer : chan; + + /*update info and pass it on*/ + active_param=f->data.ptr; + switch (active_param->request_response) { + /* i dont care about passing on refused im the mighty gateway */ + case AST_T38_REFUSED: + break; + case AST_T38_REQUEST_NEGOTIATE: + t38_param_l = (active == chan) ? &details->our_t38_parameters : &details->their_t38_parameters; + /* must use far end settings as ours on a request*/ + t38_parameters_ast_to_fax(t38_param_l, active_param); + /* ive got a request for T.38 can the other side do it need to make sure + * AST_CONTROL_T38_PARAMETERS match this side if im to bridge them */ + if (ast_channel_get_t38_state(inactive) == T38_STATE_UNKNOWN) { + t38_param_r = (active != chan) ? &details->our_t38_parameters : &details->their_t38_parameters; + t38_parameters_ast_to_fax(t38_param_r, active_param); + t38_parameters_fax_to_ast(&tmp_param, t38_param_r); + } + default: + ast_indicate_data(inactive, AST_CONTROL_T38_PARAMETERS, active_param, sizeof(struct ast_control_t38_parameters)); + } + + /* Start gateway if both channels are in a stable T.38 (not negotiating) state and only one of them is not negotiated! */ + activestate = ast_channel_get_t38_state(active); + inactivestate = ast_channel_get_t38_state(inactive); + ast_debug(1, "Current State activestate=%d (%s) and inactivestate=%d (%s)\n", activestate, active->name,inactivestate, inactive->name); + if (((activestate == T38_STATE_NEGOTIATED) && ((inactivestate == T38_STATE_UNAVAILABLE) || (inactivestate == T38_STATE_REJECTED))) || + ((inactivestate == T38_STATE_NEGOTIATED) && ((activestate == T38_STATE_UNAVAILABLE) || (activestate == T38_STATE_REJECTED)))) { + ast_verb(3, "Running Gateway activestate=%d (%s) and inactivestate=%d (%s)\n", activestate, active->name,inactivestate, inactive->name); + details->caps = AST_FAX_TECH_GATEWAY; + ast_t38_gateway(chan ,peer); + } + return 1; +} + +/*! \brief trigger a T38 switchover on a channel */ +static void __ast_fax_request_t38_switchover(struct ast_channel *chan) +{ + struct ast_control_t38_parameters t38_parameters = our_t38_parameters; + struct ast_fax_session_details *details; + + /* Get a FAX session details structure from the channel's FAX datastore and create one if it does not already exist. */ + if (!(details = find_details(chan))) { + if (!(details = find_or_create_details(chan))) { + ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1); + pbx_builtin_setvar_helper(chan, "FAXERROR", "MEMORY_ERROR"); + pbx_builtin_setvar_helper(chan, "FAXSTATUSSTRING", "error allocating memory"); + ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n"); + return; + } + + /* we should have this configrable in the config file */ + t38_parameters.fill_bit_removal = 1; + t38_parameters.transcoding_mmr = 1; + t38_parameters.transcoding_jbig = 1; + t38_parameters_ast_to_fax(&details->our_t38_parameters, &t38_parameters); + } else { + t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters); + } + + t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE; + /*this will be sent across the bridge*/ + ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)); +} + /*! \brief hash callback for ao2 */ static int session_hash_cb(const void *obj, const int flags) { @@ -2812,6 +3077,10 @@ ao2_ref(faxregistry.container, -1); + /* Unpublish gateway functions */ + ast_t38_gateway_handle_parameters = NULL; + ast_fax_request_t38_switchover = NULL; + return 0; } @@ -2849,6 +3118,10 @@ res = ast_custom_function_register(&acf_faxopt); fax_logger_level = ast_logger_register_level("FAX"); + /* Publish gateway functions to be used by core */ + ast_t38_gateway_handle_parameters = __ast_t38_gateway_handle_parameters; + ast_fax_request_t38_switchover = __ast_fax_request_t38_switchover; + return res; } --- ../clean/asterisk-1.8.3/res/res_fax_spandsp.c 2010-07-21 17:54:29.000000000 +0200 +++ ./res/res_fax_spandsp.c 2011-03-05 13:55:09.000000000 +0200 @@ -5,6 +5,22 @@ * * Matthew Nicholson * + * 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 + * * 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; @@ -49,6 +65,7 @@ #define SPANDSP_FAX_SAMPLES 160 #define SPANDSP_FAX_TIMER_RATE 8000 / SPANDSP_FAX_SAMPLES /* 50 ticks per second, 20ms, 160 samples per second */ +#define SPANDSP_ENGAGE_UDPTL_NAT_RETRY 3 static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token); static void spandsp_fax_destroy(struct ast_fax_session *s); @@ -57,6 +74,10 @@ static int spandsp_fax_start(struct ast_fax_session *s); static int spandsp_fax_cancel(struct ast_fax_session *s); static int spandsp_fax_switch_to_t38(struct ast_fax_session *s); +static int spandsp_fax_gateway_start(struct ast_fax_session *s, struct ast_channel *t38chan); +static int spandsp_fax_gateway_read_t30(struct ast_fax_session *s, struct ast_frame *f); +static int spandsp_fax_gateway_read_t38(struct ast_fax_session *s, struct ast_frame *f); +static void spandsp_fax_gateway_clean(struct ast_fax_session *s); static char *spandsp_fax_cli_show_capabilities(int fd); static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd); @@ -75,7 +96,7 @@ */ .version = "pre-20090220", #endif - .caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE, + .caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE | AST_FAX_TECH_GATEWAY, .new_session = spandsp_fax_new, .destroy_session = spandsp_fax_destroy, .read = spandsp_fax_read, @@ -87,6 +108,10 @@ .cli_show_session = spandsp_fax_cli_show_session, .cli_show_stats = spandsp_fax_cli_show_stats, .cli_show_settings = spandsp_fax_cli_show_settings, + .start_gw_session = spandsp_fax_gateway_start, + .read_gw_t30 = spandsp_fax_gateway_read_t30, + .read_gw_t38 = spandsp_fax_gateway_read_t38, + .clean_gw_session = spandsp_fax_gateway_clean, }; struct spandsp_fax_stats { @@ -121,6 +146,9 @@ struct spandsp_fax_stats *stats; + struct spandsp_fax_gw_stats *t38stats; + t38_gateway_state_t t38_gw_state; + struct ast_timer *timer; AST_LIST_HEAD(frame_queue, ast_frame) read_frames; }; @@ -428,6 +456,8 @@ caller_mode = 0; } else if (s->details->caps & AST_FAX_TECH_SEND) { caller_mode = 1; + } else if (s->details->caps & AST_FAX_TECH_GATEWAY) { + caller_mode = 2; } else { ast_log(LOG_ERROR, "Are we sending or receiving? The FAX requirements (capabilities: 0x%X) were not properly set.\n", s->details->caps); goto e_free; @@ -550,6 +580,103 @@ } } +static int spandsp_fax_gateway_start(struct ast_fax_session *s, struct ast_channel *t38chan) { + struct spandsp_pvt *p = s->tech_pvt; + struct ast_fax_t38_parameters *t38_param; + int i; + + if (!(p->timer = ast_timer_open())) { + ast_log(LOG_ERROR, "Channel '%s' FAX session '%d' failed to create timing source.\n", s->channame, s->id); + return 0; + } + + /* start the timer */ + if (ast_timer_set_rate(p->timer, SPANDSP_FAX_TIMER_RATE)) { + ast_log(LOG_ERROR, "FAX session '%d' error setting rate on timing source.\n", s->id); + return 0; + } + +#if SPANDSP_RELEASE_DATE >= 20081012 + /* for spandsp shaphots 0.0.6 and higher */ + p->t38_core_state=&p->t38_gw_state.t38x.t38; +#else + /* for spandsp release 0.0.5 */ + p->t38_core_state=&t38_state.t38; +#endif + + if (! t38_gateway_init(&p->t38_gw_state, t38_tx_packet_handler, p)) { + return 0; + } + + AST_LIST_HEAD_INIT(&p->read_frames); + p->ist38 = 1; + + t38_param = (s->chan == t38chan)? &s->details->our_t38_parameters : &s->details->their_t38_parameters; + + t38_gateway_set_transmit_on_idle(&p->t38_gw_state, TRUE); + span_log_set_message_handler(&p->t38_gw_state.logging, spandsp_log); + span_log_set_message_handler(&p->t38_core_state->logging, spandsp_log); + span_log_set_level(&p->t38_gw_state.logging, SPAN_LOG_WARNING + option_debug); + span_log_set_level(&p->t38_core_state->logging, SPAN_LOG_WARNING + option_debug); + + t38_set_t38_version(p->t38_core_state, t38_param->version); + t38_set_max_datagram_size(p->t38_core_state, t38_param->max_ifp); + t38_set_fill_bit_removal(p->t38_core_state, t38_param->fill_bit_removal); + t38_set_mmr_transcoding(p->t38_core_state, t38_param->transcoding_mmr); + t38_set_jbig_transcoding(p->t38_core_state, t38_param->transcoding_jbig); + t38_set_data_rate_management_method(p->t38_core_state, + (t38_param->rate_management == AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF)? 1 : 2); + + t38_gateway_set_ecm_capability(&p->t38_gw_state, TRUE); + t38_set_sequence_number_handling(p->t38_core_state, TRUE); + t38_gateway_set_supported_modems(&p->t38_gw_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 < SPANDSP_ENGAGE_UDPTL_NAT_RETRY; i++) { +#if SPANDSP_RELEASE_DATE >= 20091228 + t38_core_send_indicator(&p->t38_gw_state.t38x.t38, T38_IND_NO_SIGNAL); +#elif SPANDSP_RELEASE_DATE >= 20081012 + t38_core_send_indicator(&p->t38_gw_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 + } + + return 1; +} + +/*! \brief */ +static int spandsp_fax_gateway_read_t30(struct ast_fax_session *s, struct ast_frame *f) +{ + struct spandsp_pvt *p = s->tech_pvt; + t38_gateway_rx(&p->t38_gw_state, f->data.ptr, f->samples); + return t38_gateway_tx(&p->t38_gw_state, f->data.ptr, f->samples); +} + +/*! \brief */ +static int spandsp_fax_gateway_read_t38(struct ast_fax_session *s, struct ast_frame *f) +{ + struct spandsp_pvt *p = s->tech_pvt; + return t38_core_rx_ifp_packet(p->t38_core_state, f->data.ptr, f->datalen, f->seqno); +} + +static void spandsp_fax_gateway_clean(struct ast_fax_session *s) +{ + struct spandsp_pvt *p = s->tech_pvt; + t38_stats_t t38_stats; + + ast_timer_close(p->timer); + + t38_gateway_get_transfer_statistics(&p->t38_gw_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); +} + /*! \brief */ static int spandsp_fax_start(struct ast_fax_session *s) { --- ../clean/asterisk-1.8.3/include/asterisk/res_fax.h 2011-01-26 18:31:16.000000000 +0200 +++ ./include/asterisk/res_fax.h 2011-03-01 08:52:04.000000000 +0200 @@ -42,6 +42,8 @@ AST_FAX_TECH_T38 = (1 << 3), /*! sending mulitple documents supported */ AST_FAX_TECH_MULTI_DOC = (1 << 4), + /*! T.38 - T.30 Gateway */ + AST_FAX_TECH_GATEWAY = (1 << 5), }; /*! \brief fax modem capabilities */ @@ -244,6 +246,14 @@ char * (* const cli_show_stats)(int); /*! displays settings from the fax technology module */ char * (* const cli_show_settings)(int); + /*! starts the gateway session */ + int (* const start_gw_session)(struct ast_fax_session *, struct ast_channel *); + /*! process frame recieved on T30 side */ + int (* const read_gw_t30)(struct ast_fax_session *, struct ast_frame *); + /*! process frame recieved on T38 side */ + int (* const read_gw_t38)(struct ast_fax_session *, struct ast_frame *); + /*! gateway has finished */ + void (* const clean_gw_session)(struct ast_fax_session *); }; /*! \brief register a fax technology */ --- ../clean/asterisk-1.8.3/include/asterisk/channel.h 2010-11-22 21:36:10.000000000 +0200 +++ ./include/asterisk/channel.h 2011-03-01 14:27:58.000000000 +0200 @@ -1584,6 +1584,23 @@ /* Misc stuff ------------------------------------------------ */ /*! + * \brief Manage AST_CONTROL_T38_PARAMETERS when T38 Gateway is required + * \param chan + * \param peer + * \param active + * \param f + * Function is provided by res_fax.so + */ +extern int (*ast_t38_gateway_handle_parameters)(struct ast_channel *chan,struct ast_channel *peer,struct ast_channel *active, struct ast_frame *f); + +/*! + * \brief send a T38 Switchover Request for channel + * \param chan + * Function is provided by res_fax.so + */ +extern void (*ast_fax_request_t38_switchover)(struct ast_channel *chan); + +/*! * \brief Wait for input on a channel * \param chan channel to wait on * \param ms length of time to wait on the channel --- ../clean/asterisk-1.8.3/include/asterisk/dsp.h 2009-10-21 05:09:04.000000000 +0200 +++ ./include/asterisk/dsp.h 2011-03-01 08:52:04.000000000 +0200 @@ -69,6 +69,9 @@ struct ast_dsp *ast_dsp_new(void); void ast_dsp_free(struct ast_dsp *dsp); +/*! \brief disable dtmf inband warnings */ +void ast_dsp_disable_inband_dtmf_warning(struct ast_dsp *dsp); + /*! \brief Set threshold value for silence */ void ast_dsp_set_threshold(struct ast_dsp *dsp, int threshold); --- ../clean/asterisk-1.8.3/main/channel.c 2011-01-12 20:12:08.000000000 +0200 +++ ./main/channel.c 2011-03-01 14:27:31.000000000 +0200 @@ -6714,6 +6714,22 @@ ast_autoservice_stop(peer); } +/*! + * \brief handle AST_CONTROL_T38_PARAMETERS + * \param chan,peer,active,frame + * + * Function is provided by res_fax.so + */ +int (*ast_t38_gateway_handle_parameters)(struct ast_channel *chan,struct ast_channel *peer, struct ast_channel *active, struct ast_frame *frame) = NULL; + +/*! + * \brief send a T38 Switchover Request for channel + * \param chan + * + * Function is provided by res_fax.so + */ +void (*ast_fax_request_t38_switchover)(struct ast_channel *chan) = NULL; + static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct ast_channel *c1, struct ast_bridge_config *config, struct ast_frame **fo, struct ast_channel **rc) @@ -6755,6 +6771,21 @@ config->nexteventts = ast_tvadd(ast_tvnow(), ast_samp2tv(config->feature_timer, 1000)); } + /*check if i should queue a frame to switchover T.38 on peer*/ + + switch (ast_channel_get_t38_state(c0)) { + case T38_STATE_NEGOTIATED: + case T38_STATE_REJECTED: + case T38_STATE_UNAVAILABLE: + if (ast_fax_request_t38_switchover) { + ast_fax_request_t38_switchover(c0); + } + break; + case T38_STATE_UNKNOWN: + case T38_STATE_NEGOTIATING: + break; + } + for (;;) { struct ast_channel *who, *other; @@ -6851,12 +6882,23 @@ case AST_CONTROL_VIDUPDATE: case AST_CONTROL_SRCUPDATE: case AST_CONTROL_SRCCHANGE: - case AST_CONTROL_T38_PARAMETERS: ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen); if (jb_in_use) { ast_jb_empty_and_reset(c0, c1); } break; + case AST_CONTROL_T38_PARAMETERS: + if (ast_t38_gateway_handle_parameters) { + if (!ast_t38_gateway_handle_parameters(c0, c1, who, f)) { + ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen); + } + } else { + ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen); + } + if (jb_in_use) { + ast_jb_empty_and_reset(c0, c1); + } + break; default: *fo = f; *rc = who; --- ../clean/asterisk-1.8.3/main/dsp.c 2010-06-07 19:34:45.000000000 +0200 +++ ./main/dsp.c 2011-03-01 08:52:04.000000000 +0200 @@ -401,6 +401,11 @@ tone_detect_state_t ced_tone_state; }; +void ast_dsp_disable_inband_dtmf_warning(struct ast_dsp *dsp) +{ + dsp->display_inband_dtmf_warning = 0; +} + static void mute_fragment(struct ast_dsp *dsp, fragment_t *fragment) { if (dsp->mute_fragments >= ARRAY_LEN(dsp->mute_data)) { --- ../clean/asterisk-1.8.3/channels/sip/include/sip.h 2010-09-15 21:22:15.000000000 +0200 +++ ./channels/sip/include/sip.h 2011-03-01 08:52:04.000000000 +0200 @@ -336,6 +336,7 @@ #define SIP_PAGE2_VIDEOSUPPORT_ALWAYS (1 << 26) /*!< DP: Always set up video, even if endpoints don't support it */ #define SIP_PAGE2_HAVEPEERCONTEXT (1 << 27) /*< Are we associated with a configured peer context? */ #define SIP_PAGE2_USE_SRTP (1 << 28) /*!< DP: Whether we should offer (only) SRTP */ +#define SIP_PAGE2_T38SWITCHOVER (1 << 29) /*!< GP: Request T.38 Negotiation on faxdetect in audio */ #define SIP_PAGE2_FLAGS_TO_COPY \ (SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_IGNORESDPVERSION | \ @@ -592,7 +593,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 */ --- ../clean/asterisk-1.8.3/channels/chan_sip.c 2011-01-14 19:32:52.000000000 +0200 +++ ./channels/chan_sip.c 2011-03-01 12:46:13.000000000 +0200 @@ -3981,6 +3981,12 @@ return; } + /* disable pointless warning about inband dtmf if faxdetect is enabled */ + if ((ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_INBAND) && + (ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_AUTO)) { + ast_dsp_disable_inband_dtmf_warning(p->dsp); + } + ast_dsp_set_features(p->dsp, features); if (global_relaxdtmf) { ast_dsp_set_digitmode(p->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF); @@ -4068,6 +4074,9 @@ case T38_ENABLED: state = T38_STATE_NEGOTIATED; break; + case T38_REJECTED: + state = T38_STATE_REJECTED; + break; default: state = T38_STATE_UNKNOWN; } @@ -4729,6 +4738,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,11 +6279,11 @@ 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); 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); } + change_t38_state(p, T38_REJECTED); break; } else 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")); @@ -6311,7 +6321,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); @@ -6963,6 +6973,12 @@ fr = sip_rtp_read(ast, p, &faxdetected); p->lastrtprx = time(NULL); + if (faxdetected && ast_fax_request_t38_switchover && p->udptl && + ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && + ast_test_flag(&p->flags[1], SIP_PAGE2_T38SWITCHOVER)) { + ast_fax_request_t38_switchover(ast); + } + /* If we detect a CNG tone and fax detection is enabled then send us off to the fax extension */ if (faxdetected && ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_CNG)) { ast_channel_lock(ast); @@ -8610,7 +8626,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; } @@ -18390,7 +18406,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); @@ -19146,7 +19162,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); @@ -20810,7 +20826,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"); @@ -21681,7 +21697,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"); @@ -25412,6 +25424,9 @@ } else if (!strcasecmp(v->name, "ignoresdpversion")) { ast_set_flag(&mask[1], SIP_PAGE2_IGNORESDPVERSION); ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_IGNORESDPVERSION); + } else if (!strcasecmp(v->name, "t38switchover")) { + ast_set_flag(&mask[1], SIP_PAGE2_T38SWITCHOVER); + ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SWITCHOVER); } else if (!strcasecmp(v->name, "faxdetect")) { ast_set_flag(&mask[1], SIP_PAGE2_FAX_DETECT); if (ast_true(v->value)) { @@ -26658,6 +26678,7 @@ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); ast_clear_flag(&global_flags[1], SIP_PAGE2_FAX_DETECT); + ast_clear_flag(&global_flags[1], SIP_PAGE2_T38SWITCHOVER); ast_clear_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT | SIP_PAGE2_VIDEOSUPPORT_ALWAYS); ast_clear_flag(&global_flags[1], SIP_PAGE2_TEXTSUPPORT); ast_clear_flag(&global_flags[1], SIP_PAGE2_IGNORESDPVERSION); --- ../clean/asterisk-1.8.3/apps/app_faxgateway.c 2011-03-01 17:15:15.000000000 +0200 +++ ./apps/app_faxgateway.c 2011-03-01 14:16:56.000000000 +0200 @@ -0,0 +1,455 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2008-2009, Digium, Inc. + * + * 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 + * + * 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: 304143 $") + +#include "asterisk/module.h" +#include "asterisk/app.h" +#include "asterisk/manager.h" +#include "asterisk/dsp.h" +#include "asterisk/features.h" + +/*** DOCUMENTATION + + + 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 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. + 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. + + + + + Generic Fax Detect CED/CNG/T.38 + + + + Specifies the number of seconds we attempt to detect a fax tone on the channel + + + + This application sets the following channel variables upon completion + FAXTONE - detected tone: CED | CNG | NONE + T38STATUS - T.38 negotiated: 0 | 1 + + +***/ + +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; + union ast_frame_subclass expected_framesubclass = { .integer = -1 }; + unsigned int expected_frametype = -1; + + /* Setup DSP CNG/CED processing */ + if ((!ast_set_read_format(chan, AST_FORMAT_SLINEAR)) || (!ast_set_read_format(peer, AST_FORMAT_SLINEAR))) { + 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); + } + } + + /* set the read format to slin to do fax detect*/ + expected_frametype = AST_FRAME_VOICE; + expected_framesubclass.codec = AST_FORMAT_SLINEAR; + ast_channel_make_compatible(chan, peer); + + 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) && + ast_t38_gateway_handle_parameters) { + ast_t38_gateway_handle_parameters(chan, peer, active, f); + ast_frfree(f); + continue; + } + + if (dsp && (f->frametype == AST_FRAME_VOICE)) { + switch (f->subclass.codec) { + case AST_FORMAT_SLINEAR: + case AST_FORMAT_TESTLAW: + case AST_FORMAT_ULAW: + case AST_FORMAT_ALAW: + f = ast_dsp_process(active, dsp, f); + default: + break; + } + } + + inactive = (active == chan) ? peer : chan; + /* tickle the channel if we have fax tone T38 switchover on faxdetect*/ + if (ast_fax_request_t38_switchover && (ast_channel_get_t38_state(active) == T38_STATE_UNKNOWN) && + (f->frametype == AST_FRAME_DTMF)) { + switch (f->subclass.integer) { + case '5': + case 'f': + case 'e': + ast_fax_request_t38_switchover(inactive); + if (dsp) { + ast_dsp_free(dsp); + dsp=NULL; + } + 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; + + 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, ""), + S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, ""), + chan->uniqueid,peer->uniqueid, ""); + + /* 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_exec(struct ast_channel *chan, const char *data) +{ + char *parse; + int timeout, tone = 0; + struct ast_dsp *dsp = NULL; + struct ast_frame *f; + 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; + } + + if (chan->_state != AST_STATE_UP) + ast_answer(chan); + + /* Setup DSP CNG/CED processing */ + if (!ast_set_read_format(chan, AST_FORMAT_SLINEAR)) { + 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); + } + } + + while ((timeout=ast_waitfor(chan, timeout))) { + t38state = ast_channel_get_t38_state(chan); + + if (!(f=ast_read(chan))) { + break; + } + + if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_T38_PARAMETERS)) { + ast_indicate_data(chan, f->subclass.integer, f->data.ptr, f->datalen); + } + + if (dsp && (f->frametype == AST_FRAME_VOICE)) { + switch (f->subclass.codec) { + case AST_FORMAT_SLINEAR: + case AST_FORMAT_TESTLAW: + case AST_FORMAT_ULAW: + case AST_FORMAT_ALAW: + f = ast_dsp_process(chan, dsp, f); + default: + break; + } + } + + if (f->frametype == AST_FRAME_DTMF) { + switch (f->subclass.integer) { + case 'f': + tone=1; + break; + case 'e': + case '5': + tone=2; + break; + } + if (ast_fax_request_t38_switchover && (tone > 0) && + ((t38state == T38_STATE_UNKNOWN) || (t38state == T38_STATE_UNAVAILABLE))) { + if (dsp) { + ast_dsp_free(dsp); + dsp = NULL; + } + ast_fax_request_t38_switchover(chan); + } + } + + ast_write(chan, &ast_null_frame); + ast_frfree(f); + + if ((tone > 0) && (t38state != T38_STATE_UNKNOWN) && (t38state != T38_STATE_NEGOTIATING)) { + break; + } + } + + 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"); + break; + case 1: + pbx_builtin_setvar_helper(chan, "FAXTONE", "CNG"); + break; + case 2: + pbx_builtin_setvar_helper(chan, "FAXTONE", "CED"); + break; + } + if (dsp) { + ast_dsp_free(dsp); + } + return 0; +} + +/*! \brief unload app_faxgateway */ +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 app_faxgateway */ +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, + ); --- ../clean/asterisk-1.8.3/apps/app_dial.c 2010-11-24 19:13:08.000000000 +0200 +++ ./apps/app_dial.c 2011-03-01 08:52:04.000000000 +0200 @@ -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. + @@ -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); --- ../clean/asterisk-1.8.3/include/asterisk/features.h 2010-09-15 21:22:15.000000000 +0200 +++ ./include/asterisk/features.h 2011-03-01 08:52:04.000000000 +0200 @@ -116,6 +116,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 res_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); --- ../clean/asterisk-1.8.3/main/features.c 2011-01-20 22:24:36.000000000 +0200 +++ ./main/features.c 2011-03-01 08:52:04.000000000 +0200 @@ -3376,6 +3376,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 *