app_queue: Cleanup queue_ref / queue_unref routines.
[asterisk/asterisk.git] / apps / app_fax.c
index 6cb1884..c5d0f51 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Asterisk -- A telephony toolkit for Linux.
+ * Asterisk -- An open source telephony toolkit.
  *
  * Simple fax applications
- * 
+ *
  * 2007-2008, Dmitry Andrianov <asterisk@dima.spb.ru>
  *
  * Code based on original implementation by Steve Underwood <steveu@coppice.org>
  */
 
 /*** MODULEINFO
-        <depend>spandsp</depend>
+       <defaultenabled>no</defaultenabled>
+       <depend>spandsp</depend>
+       <conflict>res_fax</conflict>
+       <support_level>deprecated</support_level>
+       <replacement>res_fax</replacement>
 ***/
-#include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include "asterisk.h"
 
 #include <string.h>
 #include <stdlib.h>
@@ -28,10 +30,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <errno.h>
 #include <tiffio.h>
 
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
 #include <spandsp.h>
-#ifdef HAVE_SPANDSP_EXPOSE_H
-#include <spandsp/expose.h>
-#endif
 #include <spandsp/version.h>
 
 #include "asterisk/lock.h"
@@ -42,10 +42,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/app.h"
 #include "asterisk/dsp.h"
 #include "asterisk/module.h"
-#include "asterisk/manager.h"
+#include "asterisk/stasis.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/format_cache.h"
 
 /*** DOCUMENTATION
-       <application name="SendFAX" language="en_US">
+       <application name="SendFAX" language="en_US" module="app_fax">
                <synopsis>
                        Send a Fax
                </synopsis>
@@ -90,7 +92,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        </variablelist>
                </description>
        </application>
-       <application name="ReceiveFAX" language="en_US">
+       <application name="ReceiveFAX" language="en_US" module="app_fax">
                <synopsis>
                        Receive a Fax
                </synopsis>
@@ -99,12 +101,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                                <para>Filename of TIFF file save incoming fax</para>
                        </parameter>
                        <parameter name="c" required="false">
-                               <para>Makes the application behave as the calling machine</para> 
+                               <para>Makes the application behave as the calling machine</para>
                                <para>(Default behavior is as answering machine)</para>
                        </parameter>
                </syntax>
                <description>
-                       <para>Receives a FAX from the channel into the given filename 
+                       <para>Receives a FAX from the channel into the given filename
                        overwriting the file if it already exists.</para>
                        <para>File created will be in TIFF format.</para>
 
@@ -141,8 +143,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
  ***/
 
-static char *app_sndfax_name = "SendFAX";
-static char *app_rcvfax_name = "ReceiveFAX";
+static const char app_sndfax_name[] = "SendFAX";
+static const char app_rcvfax_name[] = "ReceiveFAX";
 
 #define MAX_SAMPLES 240
 
@@ -161,7 +163,7 @@ typedef struct {
        int direction;                  /* Fax direction: 0 - receiving, 1 - sending */
        int caller_mode;
        char *file_name;
-       
+       struct ast_control_t38_parameters t38parameters;
        volatile int finished;
 } fax_session;
 
@@ -172,7 +174,7 @@ static void span_message(int level, const char *msg)
        } else if (level == SPAN_LOG_WARNING) {
                ast_log(LOG_WARNING, "%s", msg);
        } else {
-               ast_log(LOG_DEBUG, "%s", msg);
+               ast_debug(1, "%s", msg);
        }
 }
 
@@ -182,7 +184,7 @@ static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uin
 
        struct ast_frame outf = {
                .frametype = AST_FRAME_MODEM,
-               .subclass = AST_MODEM_T38,
+               .subclass.integer = AST_MODEM_T38,
                .src = __FUNCTION__,
        };
 
@@ -201,6 +203,9 @@ static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uin
 
 static void phase_e_handler(t30_state_t *f, void *user_data, int result)
 {
+       RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
+       RAII_VAR(struct ast_json *, json_filenames, NULL, ast_json_unref);
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
        const char *local_ident;
        const char *far_ident;
        char buf[20];
@@ -224,13 +229,13 @@ static void phase_e_handler(t30_state_t *f, void *user_data, int result)
 
                return;
        }
-       
-       s->finished = 1; 
-       
-       local_ident = t30_get_tx_ident(f);
-       far_ident = t30_get_rx_ident(f);
-       pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "SUCCESS"); 
-       pbx_builtin_setvar_helper(s->chan, "FAXERROR", NULL); 
+
+       s->finished = 1;
+
+       local_ident = S_OR(t30_get_tx_ident(f), "");
+       far_ident = S_OR(t30_get_rx_ident(f), "");
+       pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "SUCCESS");
+       pbx_builtin_setvar_helper(s->chan, "FAXERROR", NULL);
        pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", far_ident);
 #if SPANDSP_RELEASE_DATE >= 20090220
        pages_transferred = (s->direction) ? stat.pages_tx : stat.pages_rx;
@@ -242,34 +247,32 @@ static void phase_e_handler(t30_state_t *f, void *user_data, int result)
        snprintf(buf, sizeof(buf), "%d", stat.y_resolution);
        pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", buf);
        snprintf(buf, sizeof(buf), "%d", stat.bit_rate);
-       pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", buf); 
-       
+       pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", buf);
+
        ast_debug(1, "Fax transmitted successfully.\n");
        ast_debug(1, "  Remote station ID: %s\n", far_ident);
        ast_debug(1, "  Pages transferred: %d\n", pages_transferred);
        ast_debug(1, "  Image resolution:  %d x %d\n", stat.x_resolution, stat.y_resolution);
        ast_debug(1, "  Transfer Rate:     %d\n", stat.bit_rate);
-       
-       manager_event(EVENT_FLAG_CALL,
-                     s->direction ? "FaxSent" : "FaxReceived", 
-                     "Channel: %s\r\n"
-                     "Exten: %s\r\n"
-                     "CallerID: %s\r\n"
-                     "RemoteStationID: %s\r\n"
-                     "LocalStationID: %s\r\n"
-                     "PagesTransferred: %d\r\n"
-                     "Resolution: %d\r\n"
-                     "TransferRate: %d\r\n"
-                     "FileName: %s\r\n",
-                     s->chan->name,
-                     s->chan->exten,
-                     S_OR(s->chan->cid.cid_num, ""),
-                     far_ident,
-                     local_ident,
-                     pages_transferred,
-                     stat.y_resolution,
-                     stat.bit_rate,
-                     s->file_name);
+
+       json_filenames = ast_json_pack("[s]", s->file_name);
+       if (!json_filenames) {
+               return;
+       }
+       ast_json_ref(json_filenames);
+       json_object = ast_json_pack("{s: s, s: s, s: s, s: i, s: i, s: i, s: o}",
+               "type", s->direction ? "send" : "receive",
+               "remote_station_id", AST_JSON_UTF8_VALIDATE(far_ident),
+               "local_station_id", AST_JSON_UTF8_VALIDATE(local_ident),
+               "fax_pages", pages_transferred,
+               "fax_resolution", stat.y_resolution,
+               "fax_bitrate", stat.bit_rate,
+               "filenames", json_filenames);
+       message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(s->chan), ast_channel_fax_type(), json_object);
+       if (!message) {
+               return;
+       }
+       stasis_publish(ast_channel_topic(s->chan), message);
 }
 
 /* === Helper functions to configure fax === */
@@ -280,7 +283,7 @@ static int set_logging(logging_state_t *state)
        int level = SPAN_LOG_WARNING + option_debug;
 
        span_log_set_message_handler(state, span_message);
-       span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level); 
+       span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level);
 
        return 0;
 }
@@ -326,10 +329,10 @@ static int fax_generator_generate(struct ast_channel *chan, void *data, int len,
        fax_state_t *fax = (fax_state_t*) data;
        uint8_t buffer[AST_FRIENDLY_OFFSET + MAX_SAMPLES * sizeof(uint16_t)];
        int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET);
-    
+
        struct ast_frame outf = {
                .frametype = AST_FRAME_VOICE,
-               .subclass = AST_FORMAT_SLINEAR,
+               .subclass.format = ast_format_slin,
                .src = __FUNCTION__,
        };
 
@@ -337,13 +340,13 @@ static int fax_generator_generate(struct ast_channel *chan, void *data, int len,
                ast_log(LOG_WARNING, "Only generating %d samples, where %d requested\n", MAX_SAMPLES, samples);
                samples = MAX_SAMPLES;
        }
-       
+
        if ((len = fax_tx(fax, buf, samples)) > 0) {
                outf.samples = len;
                AST_FRAME_SET_BUFFER(&outf, buffer, AST_FRIENDLY_OFFSET, len * sizeof(int16_t));
 
                if (ast_write(chan, &outf) < 0) {
-                       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
+                       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
                        return -1;
                }
        }
@@ -351,9 +354,9 @@ static int fax_generator_generate(struct ast_channel *chan, void *data, int len,
        return 0;
 }
 
-struct ast_generator generator = {
-       alloc:          fax_generator_alloc,
-       generate:       fax_generator_generate,
+static struct ast_generator generator = {
+       .alloc = fax_generator_alloc,
+       .generate = fax_generator_generate,
 };
 
 
@@ -362,19 +365,92 @@ struct ast_generator generator = {
 static int transmit_audio(fax_session *s)
 {
        int res = -1;
-       int original_read_fmt = AST_FORMAT_SLINEAR;
-       int original_write_fmt = AST_FORMAT_SLINEAR;
+       struct ast_format *original_read_fmt;
+       struct ast_format *original_write_fmt = NULL;
        fax_state_t fax;
        t30_state_t *t30state;
-       struct ast_dsp *dsp = NULL;
-       int detect_tone = 0;
        struct ast_frame *inf = NULL;
-       struct ast_frame *fr;
        int last_state = 0;
        struct timeval now, start, state_change;
-       enum ast_control_t38 t38control;
+       enum ast_t38_state t38_state;
+       struct ast_control_t38_parameters t38_parameters = { .version = 0,
+                                                            .max_ifp = 800,
+                                                            .rate = AST_T38_RATE_14400,
+                                                            .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
+                                                            .fill_bit_removal = 1,
+/*
+ * spandsp has API calls to support MMR and JBIG transcoding, but they aren't
+ * implemented quite yet... so don't offer them to the remote endpoint
+ *                                                          .transcoding_mmr = 1,
+ *                                                          .transcoding_jbig = 1,
+*/
+       };
 
-#if SPANDSP_RELEASE_DATE >= 20081012
+       /* if in called party mode, try to use T.38 */
+       if (s->caller_mode == FALSE) {
+               /* check if we are already in T.38 mode (unlikely), or if we can request
+                * a switch... if so, request it now and wait for the result, rather
+                * than starting an audio FAX session that will have to be cancelled
+                */
+               if ((t38_state = ast_channel_get_t38_state(s->chan)) == T38_STATE_NEGOTIATED) {
+                       return 1;
+               } else if ((t38_state != T38_STATE_UNAVAILABLE) &&
+                          (t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE,
+                           (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0))) {
+                       /* wait up to five seconds for negotiation to complete */
+                       unsigned int timeout = 5000;
+                       int ms;
+
+                       ast_debug(1, "Negotiating T.38 for receive on %s\n", ast_channel_name(s->chan));
+                       while (timeout > 0) {
+                               ms = ast_waitfor(s->chan, 1000);
+                               if (ms < 0) {
+                                       ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", ast_channel_name(s->chan));
+                                       return -1;
+                               }
+                               if (!ms) {
+                                       /* nothing happened */
+                                       if (timeout > 0) {
+                                               timeout -= 1000;
+                                               continue;
+                                       } else {
+                                               ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", ast_channel_name(s->chan));
+                                               break;
+                                       }
+                               }
+                               if (!(inf = ast_read(s->chan))) {
+                                       return -1;
+                               }
+                               if ((inf->frametype == AST_FRAME_CONTROL) &&
+                                   (inf->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
+                                   (inf->datalen == sizeof(t38_parameters))) {
+                                       struct ast_control_t38_parameters *parameters = inf->data.ptr;
+
+                                       switch (parameters->request_response) {
+                                       case AST_T38_NEGOTIATED:
+                                               ast_debug(1, "Negotiated T.38 for receive on %s\n", ast_channel_name(s->chan));
+                                               res = 1;
+                                               break;
+                                       case AST_T38_REFUSED:
+                                               ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", ast_channel_name(s->chan));
+                                               break;
+                                       default:
+                                               ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", ast_channel_name(s->chan));
+                                               break;
+                                       }
+                                       ast_frfree(inf);
+                                       if (res == 1) {
+                                               return 1;
+                                       } else {
+                                               break;
+                                       }
+                               }
+                               ast_frfree(inf);
+                       }
+               }
+       }
+
+#if SPANDSP_RELEASE_DATE >= 20080725
         /* for spandsp shaphots 0.0.6 and higher */
         t30state = &fax.t30;
 #else
@@ -382,22 +458,18 @@ static int transmit_audio(fax_session *s)
         t30state = &fax.t30_state;
 #endif
 
-       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_WARNING, "Unable to set to linear read mode, giving up\n");
-                       goto done;
-               }
+    original_read_fmt = ao2_bump(ast_channel_readformat(s->chan));
+       res = ast_set_read_format(s->chan, ast_format_slin);
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n");
+               goto done;
        }
 
-       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_WARNING, "Unable to set to linear write mode, giving up\n");
-                       goto done;
-               }
+       original_write_fmt = ao2_bump(ast_channel_writeformat(s->chan));
+       res = ast_set_write_format(s->chan, ast_format_slin);
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n");
+               goto done;
        }
 
        /* Initialize T30 terminal */
@@ -416,94 +488,80 @@ static int transmit_audio(fax_session *s)
 
        t30_set_phase_e_handler(t30state, phase_e_handler, s);
 
-       if (s->t38state == T38_STATE_UNAVAILABLE) {
-               ast_debug(1, "T38 is unavailable on %s\n", s->chan->name);
-       } else if (!s->direction) {
-               /* We are receiving side and this means we are the side which should
-                  request T38 when the fax is detected. Use DSP to detect fax tone */
-               ast_debug(1, "Setting up CNG detection on %s\n", s->chan->name);
-               dsp = ast_dsp_new();
-               ast_dsp_set_features(dsp, DSP_FEATURE_FAX_DETECT);
-               ast_dsp_set_faxmode(dsp, DSP_FAXMODE_DETECT_CNG);
-               detect_tone = 1;
-       }
-
        start = state_change = ast_tvnow();
 
        ast_activate_generator(s->chan, &generator, &fax);
 
        while (!s->finished) {
-               res = ast_waitfor(s->chan, 20);
-               if (res < 0)
+               inf = NULL;
+
+               if ((res = ast_waitfor(s->chan, 25)) < 0) {
+                       ast_debug(1, "Error waiting for a frame\n");
                        break;
-               else if (res > 0)
-                       res = 0;
+               }
 
-               inf = ast_read(s->chan);
-               if (inf == NULL) {
-                       ast_debug(1, "Channel hangup\n");
+               /* Watchdog */
+               now = ast_tvnow();
+               if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
+                       ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
                        res = -1;
                        break;
                }
 
-               ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);
-
-               /* Detect fax tone */
-               if (detect_tone && inf->frametype == AST_FRAME_VOICE) {
-                       /* Duplicate frame because ast_dsp_process may free the frame passed */
-                       fr = ast_frdup(inf);
+               if (!res) {
+                       /* There was timeout waiting for a frame. Loop around and wait again */
+                       continue;
+               }
 
-                       /* Do not pass channel to ast_dsp_process otherwise it may queue modified audio frame back */
-                       fr = ast_dsp_process(NULL, dsp, fr);
-                       if (fr && fr->frametype == AST_FRAME_DTMF && fr->subclass == 'f') {
-                               ast_debug(1, "Fax tone detected. Requesting T38\n");
-                               t38control = AST_T38_REQUEST_NEGOTIATE;
-                               ast_indicate_data(s->chan, AST_CONTROL_T38, &t38control, sizeof(t38control));
-                               detect_tone = 0;
-                       }
+               /* There is a frame available. Get it */
+               res = 0;
 
-                       ast_frfree(fr);
+               if (!(inf = ast_read(s->chan))) {
+                       ast_debug(1, "Channel hangup\n");
+                       res = -1;
+                       break;
                }
 
+               ast_debug(10, "frame %d/%s, len=%d\n", inf->frametype, ast_format_get_name(inf->subclass.format), inf->datalen);
 
                /* Check the frame type. Format also must be checked because there is a chance
-                  that a frame in old format was already queued before we set chanel format
+                  that a frame in old format was already queued before we set channel format
                   to slinear so it will still be received by ast_read */
-               if (inf->frametype == AST_FRAME_VOICE && inf->subclass == AST_FORMAT_SLINEAR) {
-
+               if (inf->frametype == AST_FRAME_VOICE &&
+                       (ast_format_cmp(inf->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL)) {
                        if (fax_rx(&fax, inf->data.ptr, inf->samples) < 0) {
                                /* I know fax_rx never returns errors. The check here is for good style only */
                                ast_log(LOG_WARNING, "fax_rx returned error\n");
                                res = -1;
                                break;
                        }
-
-                       /* Watchdog */
                        if (last_state != t30state->state) {
                                state_change = ast_tvnow();
                                last_state = t30state->state;
                        }
-               } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38 &&
-                               inf->datalen == sizeof(enum ast_control_t38)) {
-                       t38control =*((enum ast_control_t38 *) inf->data.ptr);
-                       if (t38control == AST_T38_NEGOTIATED) {
+               } else if ((inf->frametype == AST_FRAME_CONTROL) &&
+                          (inf->subclass.integer == AST_CONTROL_T38_PARAMETERS)) {
+                       struct ast_control_t38_parameters *parameters = inf->data.ptr;
+
+                       if (parameters->request_response == AST_T38_NEGOTIATED) {
                                /* T38 switchover completed */
+                               s->t38parameters = *parameters;
                                ast_debug(1, "T38 negotiated, finishing audio loop\n");
                                res = 1;
                                break;
+                       } else if (parameters->request_response == AST_T38_REQUEST_NEGOTIATE) {
+                               t38_parameters.request_response = AST_T38_NEGOTIATED;
+                               ast_debug(1, "T38 request received, accepting\n");
+                               /* Complete T38 switchover */
+                               ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
+                               /* Do not break audio loop, wait until channel driver finally acks switchover
+                                * with AST_T38_NEGOTIATED
+                                */
                        }
                }
 
                ast_frfree(inf);
                inf = NULL;
-
-               /* Watchdog */
-               now = ast_tvnow();
-               if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
-                       ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
-                       res = -1;
-                       break;
-               }
        }
 
        ast_debug(1, "Loop finished, res=%d\n", res);
@@ -511,9 +569,6 @@ static int transmit_audio(fax_session *s)
        if (inf)
                ast_frfree(inf);
 
-       if (dsp)
-               ast_dsp_free(dsp);
-
        ast_deactivate_generator(s->chan);
 
        /* If we are switching to T38, remove phase E handler. Otherwise it will be executed
@@ -527,14 +582,16 @@ static int transmit_audio(fax_session *s)
        fax_release(&fax);
 
 done:
-       if (original_write_fmt != AST_FORMAT_SLINEAR) {
+       if (original_write_fmt) {
                if (ast_set_write_format(s->chan, original_write_fmt) < 0)
-                       ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", s->chan->name);
+                       ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", ast_channel_name(s->chan));
+               ao2_ref(original_write_fmt, -1);
        }
 
-       if (original_read_fmt != AST_FORMAT_SLINEAR) {
+       if (original_read_fmt) {
                if (ast_set_read_format(s->chan, original_read_fmt) < 0)
-                       ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", s->chan->name);
+                       ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", ast_channel_name(s->chan));
+               ao2_ref(original_read_fmt, -1);
        }
 
        return res;
@@ -548,12 +605,10 @@ static int transmit_t38(fax_session *s)
        struct ast_frame *inf = NULL;
        int last_state = 0;
        struct timeval now, start, state_change, last_frame;
-       enum ast_control_t38 t38control;
-
        t30_state_t *t30state;
        t38_core_state_t *t38state;
 
-#if SPANDSP_RELEASE_DATE >= 20081012
+#if SPANDSP_RELEASE_DATE >= 20080725
        /* for spandsp shaphots 0.0.6 and higher */
        t30state = &t38.t30;
        t38state = &t38.t38_fe.t38;
@@ -567,7 +622,20 @@ static int transmit_t38(fax_session *s)
        memset(&t38, 0, sizeof(t38));
        if (t38_terminal_init(&t38, s->caller_mode, t38_tx_packet_handler, s->chan) == NULL) {
                ast_log(LOG_WARNING, "Unable to start T.38 termination.\n");
-               return -1;
+               res = -1;
+               goto disable_t38;
+       }
+
+       t38_set_max_datagram_size(t38state, s->t38parameters.max_ifp);
+
+       if (s->t38parameters.fill_bit_removal) {
+               t38_set_fill_bit_removal(t38state, TRUE);
+       }
+       if (s->t38parameters.transcoding_mmr) {
+               t38_set_mmr_transcoding(t38state, TRUE);
+       }
+       if (s->t38parameters.transcoding_jbig) {
+               t38_set_jbig_transcoding(t38state, TRUE);
        }
 
        /* Setup logging */
@@ -585,55 +653,57 @@ static int transmit_t38(fax_session *s)
        now = start = state_change = ast_tvnow();
 
        while (!s->finished) {
+               inf = NULL;
 
-               res = ast_waitfor(s->chan, 20);
-               if (res < 0)
+               if ((res = ast_waitfor(s->chan, 25)) < 0) {
+                       ast_debug(1, "Error waiting for a frame\n");
                        break;
-               else if (res > 0)
-                       res = 0;
+               }
 
                last_frame = now;
+
+               /* Watchdog */
                now = ast_tvnow();
+               if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
+                       ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
+                       res = -1;
+                       break;
+               }
+
                t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000));
 
-               inf = ast_read(s->chan);
-               if (inf == NULL) {
+               if (!res) {
+                       /* There was timeout waiting for a frame. Loop around and wait again */
+                       continue;
+               }
+
+               /* There is a frame available. Get it */
+               res = 0;
+
+               if (!(inf = ast_read(s->chan))) {
                        ast_debug(1, "Channel hangup\n");
                        res = -1;
                        break;
                }
 
-               ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);
+               ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass.integer, inf->datalen);
 
-               if (inf->frametype == AST_FRAME_MODEM && inf->subclass == AST_MODEM_T38) {
+               if (inf->frametype == AST_FRAME_MODEM && inf->subclass.integer == AST_MODEM_T38) {
                        t38_core_rx_ifp_packet(t38state, inf->data.ptr, inf->datalen, inf->seqno);
-
-                       /* Watchdog */
                        if (last_state != t30state->state) {
                                state_change = ast_tvnow();
                                last_state = t30state->state;
                        }
-               } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38 &&
-                               inf->datalen == sizeof(enum ast_control_t38)) {
-
-                       t38control = *((enum ast_control_t38 *) inf->data.ptr);
-
-                       if (t38control == AST_T38_TERMINATED || t38control == AST_T38_REFUSED) {
-                               ast_debug(1, "T38 down, terminating\n");
-                               res = -1;
+               } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass.integer == AST_CONTROL_T38_PARAMETERS) {
+                       struct ast_control_t38_parameters *parameters = inf->data.ptr;
+                       if (parameters->request_response == AST_T38_TERMINATED) {
+                               ast_debug(1, "T38 down, finishing\n");
                                break;
                        }
                }
 
                ast_frfree(inf);
                inf = NULL;
-
-               /* Watchdog */
-               if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
-                       ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
-                       res = -1;
-                       break;
-               }
        }
 
        ast_debug(1, "Loop finished, res=%d\n", res);
@@ -644,6 +714,63 @@ static int transmit_t38(fax_session *s)
        t30_terminate(t30state);
        t38_terminal_release(&t38);
 
+disable_t38:
+       /* if we are not the caller, it's our job to shut down the T.38
+        * session when the FAX transmisson is complete.
+        */
+       if ((s->caller_mode == FALSE) &&
+           (ast_channel_get_t38_state(s->chan) == T38_STATE_NEGOTIATED)) {
+               struct ast_control_t38_parameters t38_parameters = { .request_response = AST_T38_REQUEST_TERMINATE, };
+
+               if (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0) {
+                       /* wait up to five seconds for negotiation to complete */
+                       unsigned int timeout = 5000;
+                       int ms;
+
+                       ast_debug(1, "Shutting down T.38 on %s\n", ast_channel_name(s->chan));
+                       while (timeout > 0) {
+                               ms = ast_waitfor(s->chan, 1000);
+                               if (ms < 0) {
+                                       ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", ast_channel_name(s->chan));
+                                       return -1;
+                               }
+                               if (!ms) {
+                                       /* nothing happened */
+                                       if (timeout > 0) {
+                                               timeout -= 1000;
+                                               continue;
+                                       } else {
+                                               ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 shutdown.\n", ast_channel_name(s->chan));
+                                               break;
+                                       }
+                               }
+                               if (!(inf = ast_read(s->chan))) {
+                                       return -1;
+                               }
+                               if ((inf->frametype == AST_FRAME_CONTROL) &&
+                                   (inf->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
+                                   (inf->datalen == sizeof(t38_parameters))) {
+                                       struct ast_control_t38_parameters *parameters = inf->data.ptr;
+
+                                       switch (parameters->request_response) {
+                                       case AST_T38_TERMINATED:
+                                               ast_debug(1, "Shut down T.38 on %s\n", ast_channel_name(s->chan));
+                                               break;
+                                       case AST_T38_REFUSED:
+                                               ast_log(LOG_WARNING, "channel '%s' refused to disable T.38\n", ast_channel_name(s->chan));
+                                               break;
+                                       default:
+                                               ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", ast_channel_name(s->chan));
+                                               break;
+                                       }
+                                       ast_frfree(inf);
+                                       break;
+                               }
+                               ast_frfree(inf);
+                       }
+               }
+       }
+
        return res;
 }
 
@@ -653,29 +780,29 @@ static int transmit(fax_session *s)
 
        /* 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->chan, "FAXSTATUS", "FAILED"); 
-       pbx_builtin_setvar_helper(s->chan, "FAXERROR", "Channel problems"); 
+       pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "FAILED");
+       pbx_builtin_setvar_helper(s->chan, "FAXERROR", "Channel problems");
 
        pbx_builtin_setvar_helper(s->chan, "FAXMODE", NULL);
        pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", NULL);
-       pbx_builtin_setvar_helper(s->chan, "FAXPAGES", NULL);
+       pbx_builtin_setvar_helper(s->chan, "FAXPAGES", "0");
        pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", NULL);
-       pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", NULL); 
+       pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", NULL);
 
-       if (s->chan->_state != AST_STATE_UP) {
+       if (ast_channel_state(s->chan) != AST_STATE_UP) {
                /* Shouldn't need this, but checking to see if channel is already answered
                 * Theoretically asterisk should already have answered before running the app */
                res = ast_answer(s->chan);
                if (res) {
-                       ast_log(LOG_WARNING, "Could not answer channel '%s'\n", s->chan->name);
+                       ast_log(LOG_WARNING, "Could not answer channel '%s'\n", ast_channel_name(s->chan));
                        return res;
                }
        }
 
        s->t38state = ast_channel_get_t38_state(s->chan);
        if (s->t38state != T38_STATE_NEGOTIATED) {
-               /* T38 is not negotiated on the channel yet. First start regular transmission. If it switches to T38, follow */ 
-               pbx_builtin_setvar_helper(s->chan, "FAXMODE", "audio"); 
+               /* T38 is not negotiated on the channel yet. First start regular transmission. If it switches to T38, follow */
+               pbx_builtin_setvar_helper(s->chan, "FAXMODE", "audio");
                res = transmit_audio(s);
                if (res > 0) {
                        /* transmit_audio reports switchover to T38. Update t38state */
@@ -687,7 +814,7 @@ static int transmit(fax_session *s)
        }
 
        if (s->t38state == T38_STATE_NEGOTIATED) {
-               pbx_builtin_setvar_helper(s->chan, "FAXMODE", "T38"); 
+               pbx_builtin_setvar_helper(s->chan, "FAXMODE", "T38");
                res = transmit_t38(s);
        }
 
@@ -709,7 +836,8 @@ static int sndfax_exec(struct ast_channel *chan, const char *data)
 {
        int res = 0;
        char *parse;
-       fax_session session;
+       fax_session session = { 0, };
+       char restore_digit_detect = 0;
 
        AST_DECLARE_APP_ARGS(args,
                AST_APP_ARG(file_name);
@@ -730,7 +858,7 @@ static int sndfax_exec(struct ast_channel *chan, const char *data)
 
        parse = ast_strdupa(data);
        AST_STANDARD_APP_ARGS(args, parse);
-       
+
        session.caller_mode = TRUE;
 
        if (args.options) {
@@ -744,8 +872,32 @@ static int sndfax_exec(struct ast_channel *chan, const char *data)
        session.chan = chan;
        session.finished = 0;
 
+       /* get current digit detection mode, then disable digit detection if enabled */
+       {
+               int dummy = sizeof(restore_digit_detect);
+
+               ast_channel_queryoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, &dummy, 0);
+       }
+
+       if (restore_digit_detect) {
+               char new_digit_detect = 0;
+
+               ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &new_digit_detect, sizeof(new_digit_detect), 0);
+       }
+
+       /* disable FAX tone detection if enabled */
+       {
+               char new_fax_detect = 0;
+
+               ast_channel_setoption(chan, AST_OPTION_FAX_DETECT, &new_fax_detect, sizeof(new_fax_detect), 0);
+       }
+
        res = transmit(&session);
 
+       if (restore_digit_detect) {
+               ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, sizeof(restore_digit_detect), 0);
+       }
+
        return res;
 }
 
@@ -754,6 +906,7 @@ static int rcvfax_exec(struct ast_channel *chan, const char *data)
        int res = 0;
        char *parse;
        fax_session session;
+       char restore_digit_detect = 0;
 
        AST_DECLARE_APP_ARGS(args,
                AST_APP_ARG(file_name);
@@ -774,7 +927,7 @@ static int rcvfax_exec(struct ast_channel *chan, const char *data)
 
        parse = ast_strdupa(data);
        AST_STANDARD_APP_ARGS(args, parse);
-       
+
        session.caller_mode = FALSE;
 
        if (args.options) {
@@ -788,8 +941,32 @@ static int rcvfax_exec(struct ast_channel *chan, const char *data)
        session.chan = chan;
        session.finished = 0;
 
+       /* get current digit detection mode, then disable digit detection if enabled */
+       {
+               int dummy = sizeof(restore_digit_detect);
+
+               ast_channel_queryoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, &dummy, 0);
+       }
+
+       if (restore_digit_detect) {
+               char new_digit_detect = 0;
+
+               ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &new_digit_detect, sizeof(new_digit_detect), 0);
+       }
+
+       /* disable FAX tone detection if enabled */
+       {
+               char new_fax_detect = 0;
+
+               ast_channel_setoption(chan, AST_OPTION_FAX_DETECT, &new_fax_detect, sizeof(new_fax_detect), 0);
+       }
+
        res = transmit(&session);
 
+       if (restore_digit_detect) {
+               ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, sizeof(restore_digit_detect), 0);
+       }
+
        return res;
 }
 
@@ -797,8 +974,8 @@ static int unload_module(void)
 {
        int res;
 
-       res = ast_unregister_application(app_sndfax_name);      
-       res |= ast_unregister_application(app_rcvfax_name);     
+       res = ast_unregister_application(app_sndfax_name);
+       res |= ast_unregister_application(app_rcvfax_name);
 
        return res;
 }
@@ -818,8 +995,7 @@ static int load_module(void)
 
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Simple FAX Application",
-               .load = load_module,
-               .unload = unload_module,
-               );
-
-
+       .support_level = AST_MODULE_SUPPORT_DEPRECATED,
+       .load = load_module,
+       .unload = unload_module,
+);