app_queue: Cleanup queue_ref / queue_unref routines.
[asterisk/asterisk.git] / apps / app_fax.c
index bef5ed5..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>
@@ -40,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>
@@ -88,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>
@@ -97,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>
 
@@ -170,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);
        }
 }
 
@@ -199,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];
@@ -222,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;
@@ -240,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);
-       
-       ast_manager_event(s->chan, 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 === */
@@ -278,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;
 }
@@ -324,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.codec = AST_FORMAT_SLINEAR,
+               .subclass.format = ast_format_slin,
                .src = __FUNCTION__,
        };
 
@@ -335,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;
                }
        }
@@ -350,8 +355,8 @@ static int fax_generator_generate(struct ast_channel *chan, void *data, int len,
 }
 
 static struct ast_generator generator = {
-       alloc:          fax_generator_alloc,
-       generate:       fax_generator_generate,
+       .alloc = fax_generator_alloc,
+       .generate = fax_generator_generate,
 };
 
 
@@ -360,8 +365,8 @@ static 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_frame *inf = NULL;
@@ -373,8 +378,12 @@ static int transmit_audio(fax_session *s)
                                                             .rate = AST_T38_RATE_14400,
                                                             .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
                                                             .fill_bit_removal = 1,
-                                                            .transcoding_mmr = 1,
-                                                            .transcoding_jbig = 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 in called party mode, try to use T.38 */
@@ -391,12 +400,12 @@ static int transmit_audio(fax_session *s)
                        /* 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", s->chan->name);
+
+                       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", s->chan->name);
+                                       ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", ast_channel_name(s->chan));
                                        return -1;
                                }
                                if (!ms) {
@@ -405,7 +414,7 @@ static int transmit_audio(fax_session *s)
                                                timeout -= 1000;
                                                continue;
                                        } else {
-                                               ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", s->chan->name);
+                                               ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", ast_channel_name(s->chan));
                                                break;
                                        }
                                }
@@ -416,17 +425,17 @@ static int transmit_audio(fax_session *s)
                                    (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", s->chan->name);
+                                               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", s->chan->name);
+                                               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", s->chan->name);
+                                               ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", ast_channel_name(s->chan));
                                                break;
                                        }
                                        ast_frfree(inf);
@@ -449,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 */
@@ -517,12 +522,13 @@ static int transmit_audio(fax_session *s)
                        break;
                }
 
-               ast_debug(10, "frame %d/%llu, len=%d\n", inf->frametype, (unsigned long long) inf->subclass.codec, inf->datalen);
+               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 channel format
                   to slinear so it will still be received by ast_read */
-               if (inf->frametype == AST_FRAME_VOICE && inf->subclass.codec == 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");
@@ -555,6 +561,7 @@ static int transmit_audio(fax_session *s)
                }
 
                ast_frfree(inf);
+               inf = NULL;
        }
 
        ast_debug(1, "Loop finished, res=%d\n", res);
@@ -575,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;
@@ -660,7 +669,7 @@ static int transmit_t38(fax_session *s)
                        res = -1;
                        break;
                }
-               
+
                t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000));
 
                if (!res) {
@@ -694,6 +703,7 @@ static int transmit_t38(fax_session *s)
                }
 
                ast_frfree(inf);
+               inf = NULL;
        }
 
        ast_debug(1, "Loop finished, res=%d\n", res);
@@ -716,12 +726,12 @@ disable_t38:
                        /* 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", s->chan->name);
+
+                       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", s->chan->name);
+                                       ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", ast_channel_name(s->chan));
                                        return -1;
                                }
                                if (!ms) {
@@ -730,7 +740,7 @@ disable_t38:
                                                timeout -= 1000;
                                                continue;
                                        } else {
-                                               ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 shutdown.\n", s->chan->name);
+                                               ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 shutdown.\n", ast_channel_name(s->chan));
                                                break;
                                        }
                                }
@@ -741,16 +751,16 @@ disable_t38:
                                    (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", s->chan->name);
+                                               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", s->chan->name);
+                                               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", s->chan->name);
+                                               ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", ast_channel_name(s->chan));
                                                break;
                                        }
                                        ast_frfree(inf);
@@ -770,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", "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 */
@@ -804,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);
        }
 
@@ -848,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) {
@@ -917,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) {
@@ -964,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;
 }
@@ -985,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,
+);