Thu Oct 11 06:33:31 2012

Asterisk developer's documentation


app_fax.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Simple fax applications
00005  * 
00006  * 2007-2008, Dmitry Andrianov <asterisk@dima.spb.ru>
00007  *
00008  * Code based on original implementation by Steve Underwood <steveu@coppice.org>
00009  *
00010  * This program is free software, distributed under the terms of
00011  * the GNU General Public License
00012  *
00013  */
00014 
00015 /*** MODULEINFO
00016    <defaultenabled>no</defaultenabled>
00017    <depend>spandsp</depend>
00018    <conflict>res_fax</conflict>
00019    <support_level>extended</support_level>
00020 ***/
00021 
00022 #include "asterisk.h"
00023 
00024 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 357542 $")
00025 
00026 #include <string.h>
00027 #include <stdlib.h>
00028 #include <stdio.h>
00029 #include <inttypes.h>
00030 #include <pthread.h>
00031 #include <errno.h>
00032 #include <tiffio.h>
00033 
00034 #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
00035 #include <spandsp.h>
00036 #include <spandsp/version.h>
00037 
00038 #include "asterisk/lock.h"
00039 #include "asterisk/file.h"
00040 #include "asterisk/logger.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/app.h"
00044 #include "asterisk/dsp.h"
00045 #include "asterisk/module.h"
00046 #include "asterisk/manager.h"
00047 
00048 /*** DOCUMENTATION
00049    <application name="SendFAX" language="en_US" module="app_fax">
00050       <synopsis>
00051          Send a Fax
00052       </synopsis>
00053       <syntax>
00054          <parameter name="filename" required="true">
00055             <para>Filename of TIFF file to fax</para>
00056          </parameter>
00057          <parameter name="a" required="false">
00058             <para>Makes the application behave as the answering machine</para>
00059             <para>(Default behavior is as calling machine)</para>
00060          </parameter>
00061       </syntax>
00062       <description>
00063          <para>Send a given TIFF file to the channel as a FAX.</para>
00064          <para>This application sets the following channel variables:</para>
00065          <variablelist>
00066             <variable name="LOCALSTATIONID">
00067                <para>To identify itself to the remote end</para>
00068             </variable>
00069             <variable name="LOCALHEADERINFO">
00070                <para>To generate a header line on each page</para>
00071             </variable>
00072             <variable name="FAXSTATUS">
00073                <value name="SUCCESS"/>
00074                <value name="FAILED"/>
00075             </variable>
00076             <variable name="FAXERROR">
00077                <para>Cause of failure</para>
00078             </variable>
00079             <variable name="REMOTESTATIONID">
00080                <para>The CSID of the remote side</para>
00081             </variable>
00082             <variable name="FAXPAGES">
00083                <para>Number of pages sent</para>
00084             </variable>
00085             <variable name="FAXBITRATE">
00086                <para>Transmission rate</para>
00087             </variable>
00088             <variable name="FAXRESOLUTION">
00089                <para>Resolution of sent fax</para>
00090             </variable>
00091          </variablelist>
00092       </description>
00093    </application>
00094    <application name="ReceiveFAX" language="en_US" module="app_fax">
00095       <synopsis>
00096          Receive a Fax
00097       </synopsis>
00098       <syntax>
00099          <parameter name="filename" required="true">
00100             <para>Filename of TIFF file save incoming fax</para>
00101          </parameter>
00102          <parameter name="c" required="false">
00103             <para>Makes the application behave as the calling machine</para> 
00104             <para>(Default behavior is as answering machine)</para>
00105          </parameter>
00106       </syntax>
00107       <description>
00108          <para>Receives a FAX from the channel into the given filename 
00109          overwriting the file if it already exists.</para>
00110          <para>File created will be in TIFF format.</para>
00111 
00112          <para>This application sets the following channel variables:</para>
00113          <variablelist>
00114             <variable name="LOCALSTATIONID">
00115                <para>To identify itself to the remote end</para>
00116             </variable>
00117             <variable name="LOCALHEADERINFO">
00118                <para>To generate a header line on each page</para>
00119             </variable>
00120             <variable name="FAXSTATUS">
00121                <value name="SUCCESS"/>
00122                <value name="FAILED"/>
00123             </variable>
00124             <variable name="FAXERROR">
00125                <para>Cause of failure</para>
00126             </variable>
00127             <variable name="REMOTESTATIONID">
00128                <para>The CSID of the remote side</para>
00129             </variable>
00130             <variable name="FAXPAGES">
00131                <para>Number of pages sent</para>
00132             </variable>
00133             <variable name="FAXBITRATE">
00134                <para>Transmission rate</para>
00135             </variable>
00136             <variable name="FAXRESOLUTION">
00137                <para>Resolution of sent fax</para>
00138             </variable>
00139          </variablelist>
00140       </description>
00141    </application>
00142 
00143  ***/
00144 
00145 static const char app_sndfax_name[] = "SendFAX";
00146 static const char app_rcvfax_name[] = "ReceiveFAX";
00147 
00148 #define MAX_SAMPLES 240
00149 
00150 /* Watchdog. I have seen situations when remote fax disconnects (because of poor line
00151    quality) while SpanDSP continues staying in T30_STATE_IV_CTC state forever.
00152    To avoid this, we terminate when we see that T30 state does not change for 5 minutes.
00153    We also terminate application when more than 30 minutes passed regardless of
00154    state changes. This is just a precaution measure - no fax should take that long */
00155 
00156 #define WATCHDOG_TOTAL_TIMEOUT   30 * 60
00157 #define WATCHDOG_STATE_TIMEOUT   5 * 60
00158 
00159 typedef struct {
00160    struct ast_channel *chan;
00161    enum ast_t38_state t38state;  /* T38 state of the channel */
00162    int direction;       /* Fax direction: 0 - receiving, 1 - sending */
00163    int caller_mode;
00164    char *file_name;
00165    struct ast_control_t38_parameters t38parameters;
00166    volatile int finished;
00167 } fax_session;
00168 
00169 static void span_message(int level, const char *msg)
00170 {
00171    if (level == SPAN_LOG_ERROR) {
00172       ast_log(LOG_ERROR, "%s", msg);
00173    } else if (level == SPAN_LOG_WARNING) {
00174       ast_log(LOG_WARNING, "%s", msg);
00175    } else {
00176       ast_debug(1, "%s", msg);
00177    }
00178 }
00179 
00180 static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uint8_t *buf, int len, int count)
00181 {
00182    struct ast_channel *chan = (struct ast_channel *) user_data;
00183 
00184    struct ast_frame outf = {
00185       .frametype = AST_FRAME_MODEM,
00186       .subclass.integer = AST_MODEM_T38,
00187       .src = __FUNCTION__,
00188    };
00189 
00190    /* TODO: Asterisk does not provide means of resending the same packet multiple
00191      times so count is ignored at the moment */
00192 
00193    AST_FRAME_SET_BUFFER(&outf, buf, 0, len);
00194 
00195    if (ast_write(chan, &outf) < 0) {
00196       ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno));
00197       return -1;
00198    }
00199 
00200    return 0;
00201 }
00202 
00203 static void phase_e_handler(t30_state_t *f, void *user_data, int result)
00204 {
00205    const char *local_ident;
00206    const char *far_ident;
00207    char buf[20];
00208    fax_session *s = (fax_session *) user_data;
00209    t30_stats_t stat;
00210    int pages_transferred;
00211 
00212    ast_debug(1, "Fax phase E handler. result=%d\n", result);
00213 
00214    t30_get_transfer_statistics(f, &stat);
00215 
00216    s = (fax_session *) user_data;
00217 
00218    if (result != T30_ERR_OK) {
00219       s->finished = -1;
00220 
00221       /* FAXSTATUS is already set to FAILED */
00222       pbx_builtin_setvar_helper(s->chan, "FAXERROR", t30_completion_code_to_str(result));
00223 
00224       ast_log(LOG_WARNING, "Error transmitting fax. result=%d: %s.\n", result, t30_completion_code_to_str(result));
00225 
00226       return;
00227    }
00228 
00229    s->finished = 1;
00230 
00231    local_ident = S_OR(t30_get_tx_ident(f), "");
00232    far_ident = S_OR(t30_get_rx_ident(f), "");
00233    pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "SUCCESS");
00234    pbx_builtin_setvar_helper(s->chan, "FAXERROR", NULL);
00235    pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", far_ident);
00236 #if SPANDSP_RELEASE_DATE >= 20090220
00237    pages_transferred = (s->direction) ? stat.pages_tx : stat.pages_rx;
00238 #else
00239    pages_transferred = stat.pages_transferred;
00240 #endif
00241    snprintf(buf, sizeof(buf), "%d", pages_transferred);
00242    pbx_builtin_setvar_helper(s->chan, "FAXPAGES", buf);
00243    snprintf(buf, sizeof(buf), "%d", stat.y_resolution);
00244    pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", buf);
00245    snprintf(buf, sizeof(buf), "%d", stat.bit_rate);
00246    pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", buf);
00247 
00248    ast_debug(1, "Fax transmitted successfully.\n");
00249    ast_debug(1, "  Remote station ID: %s\n", far_ident);
00250    ast_debug(1, "  Pages transferred: %d\n", pages_transferred);
00251    ast_debug(1, "  Image resolution:  %d x %d\n", stat.x_resolution, stat.y_resolution);
00252    ast_debug(1, "  Transfer Rate:     %d\n", stat.bit_rate);
00253 
00254    ast_manager_event(s->chan, EVENT_FLAG_CALL,
00255       s->direction ? "FaxSent" : "FaxReceived",
00256       "Channel: %s\r\n"
00257       "Exten: %s\r\n"
00258       "CallerID: %s\r\n"
00259       "CallerIDName: %s\r\n"
00260       "ConnectedLineNum: %s\r\n"
00261       "ConnectedLineName: %s\r\n"
00262       "RemoteStationID: %s\r\n"
00263       "LocalStationID: %s\r\n"
00264       "PagesTransferred: %d\r\n"
00265       "Resolution: %d\r\n"
00266       "TransferRate: %d\r\n"
00267       "FileName: %s\r\n",
00268       ast_channel_name(s->chan),
00269       ast_channel_exten(s->chan),
00270       S_COR(ast_channel_caller(s->chan)->id.number.valid, ast_channel_caller(s->chan)->id.number.str, ""),
00271       S_COR(ast_channel_caller(s->chan)->id.name.valid, ast_channel_caller(s->chan)->id.name.str, ""),
00272       S_COR(ast_channel_connected(s->chan)->id.number.valid, ast_channel_connected(s->chan)->id.number.str, ""),
00273       S_COR(ast_channel_connected(s->chan)->id.name.valid, ast_channel_connected(s->chan)->id.name.str, ""),
00274       far_ident,
00275       local_ident,
00276       pages_transferred,
00277       stat.y_resolution,
00278       stat.bit_rate,
00279       s->file_name);
00280 }
00281 
00282 /* === Helper functions to configure fax === */
00283 
00284 /* Setup SPAN logging according to Asterisk debug level */
00285 static int set_logging(logging_state_t *state)
00286 {
00287    int level = SPAN_LOG_WARNING + option_debug;
00288 
00289    span_log_set_message_handler(state, span_message);
00290    span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level); 
00291 
00292    return 0;
00293 }
00294 
00295 static void set_local_info(t30_state_t *state, fax_session *s)
00296 {
00297    const char *x;
00298 
00299    x = pbx_builtin_getvar_helper(s->chan, "LOCALSTATIONID");
00300    if (!ast_strlen_zero(x))
00301       t30_set_tx_ident(state, x);
00302 
00303    x = pbx_builtin_getvar_helper(s->chan, "LOCALHEADERINFO");
00304    if (!ast_strlen_zero(x))
00305       t30_set_tx_page_header_info(state, x);
00306 }
00307 
00308 static void set_file(t30_state_t *state, fax_session *s)
00309 {
00310    if (s->direction)
00311       t30_set_tx_file(state, s->file_name, -1, -1);
00312    else
00313       t30_set_rx_file(state, s->file_name, -1);
00314 }
00315 
00316 static void set_ecm(t30_state_t *state, int ecm)
00317 {
00318    t30_set_ecm_capability(state, ecm);
00319    t30_set_supported_compressions(state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
00320 }
00321 
00322 /* === Generator === */
00323 
00324 /* This function is only needed to return passed params so
00325    generator_activate will save it to channel's generatordata */
00326 static void *fax_generator_alloc(struct ast_channel *chan, void *params)
00327 {
00328    return params;
00329 }
00330 
00331 static int fax_generator_generate(struct ast_channel *chan, void *data, int len, int samples)
00332 {
00333    fax_state_t *fax = (fax_state_t*) data;
00334    uint8_t buffer[AST_FRIENDLY_OFFSET + MAX_SAMPLES * sizeof(uint16_t)];
00335    int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET);
00336     
00337    struct ast_frame outf = {
00338       .frametype = AST_FRAME_VOICE,
00339       .src = __FUNCTION__,
00340    };
00341    ast_format_set(&outf.subclass.format, AST_FORMAT_SLINEAR, 0);
00342 
00343    if (samples > MAX_SAMPLES) {
00344       ast_log(LOG_WARNING, "Only generating %d samples, where %d requested\n", MAX_SAMPLES, samples);
00345       samples = MAX_SAMPLES;
00346    }
00347    
00348    if ((len = fax_tx(fax, buf, samples)) > 0) {
00349       outf.samples = len;
00350       AST_FRAME_SET_BUFFER(&outf, buffer, AST_FRIENDLY_OFFSET, len * sizeof(int16_t));
00351 
00352       if (ast_write(chan, &outf) < 0) {
00353          ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
00354          return -1;
00355       }
00356    }
00357 
00358    return 0;
00359 }
00360 
00361 static struct ast_generator generator = {
00362    alloc:      fax_generator_alloc,
00363    generate:   fax_generator_generate,
00364 };
00365 
00366 
00367 /* === Transmission === */
00368 
00369 static int transmit_audio(fax_session *s)
00370 {
00371    int res = -1;
00372    struct ast_format original_read_fmt;
00373    struct ast_format original_write_fmt;
00374    fax_state_t fax;
00375    t30_state_t *t30state;
00376    struct ast_frame *inf = NULL;
00377    int last_state = 0;
00378    struct timeval now, start, state_change;
00379    enum ast_t38_state t38_state;
00380    struct ast_control_t38_parameters t38_parameters = { .version = 0,
00381                           .max_ifp = 800,
00382                           .rate = AST_T38_RATE_14400,
00383                           .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
00384                           .fill_bit_removal = 1,
00385 /*
00386  * spandsp has API calls to support MMR and JBIG transcoding, but they aren't
00387  * implemented quite yet... so don't offer them to the remote endpoint
00388  *                        .transcoding_mmr = 1,
00389  *                        .transcoding_jbig = 1,
00390 */
00391    };
00392 
00393    ast_format_clear(&original_read_fmt);
00394    ast_format_clear(&original_write_fmt);
00395 
00396    /* if in called party mode, try to use T.38 */
00397    if (s->caller_mode == FALSE) {
00398       /* check if we are already in T.38 mode (unlikely), or if we can request
00399        * a switch... if so, request it now and wait for the result, rather
00400        * than starting an audio FAX session that will have to be cancelled
00401        */
00402       if ((t38_state = ast_channel_get_t38_state(s->chan)) == T38_STATE_NEGOTIATED) {
00403          return 1;
00404       } else if ((t38_state != T38_STATE_UNAVAILABLE) &&
00405             (t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE,
00406              (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0))) {
00407          /* wait up to five seconds for negotiation to complete */
00408          unsigned int timeout = 5000;
00409          int ms;
00410 
00411          ast_debug(1, "Negotiating T.38 for receive on %s\n", ast_channel_name(s->chan));
00412          while (timeout > 0) {
00413             ms = ast_waitfor(s->chan, 1000);
00414             if (ms < 0) {
00415                ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", ast_channel_name(s->chan));
00416                return -1;
00417             }
00418             if (!ms) {
00419                /* nothing happened */
00420                if (timeout > 0) {
00421                   timeout -= 1000;
00422                   continue;
00423                } else {
00424                   ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", ast_channel_name(s->chan));
00425                   break;
00426                }
00427             }
00428             if (!(inf = ast_read(s->chan))) {
00429                return -1;
00430             }
00431             if ((inf->frametype == AST_FRAME_CONTROL) &&
00432                 (inf->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
00433                 (inf->datalen == sizeof(t38_parameters))) {
00434                struct ast_control_t38_parameters *parameters = inf->data.ptr;
00435 
00436                switch (parameters->request_response) {
00437                case AST_T38_NEGOTIATED:
00438                   ast_debug(1, "Negotiated T.38 for receive on %s\n", ast_channel_name(s->chan));
00439                   res = 1;
00440                   break;
00441                case AST_T38_REFUSED:
00442                   ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", ast_channel_name(s->chan));
00443                   break;
00444                default:
00445                   ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", ast_channel_name(s->chan));
00446                   break;
00447                }
00448                ast_frfree(inf);
00449                if (res == 1) {
00450                   return 1;
00451                } else {
00452                   break;
00453                }
00454             }
00455             ast_frfree(inf);
00456          }
00457       }
00458    }
00459 
00460 #if SPANDSP_RELEASE_DATE >= 20080725
00461         /* for spandsp shaphots 0.0.6 and higher */
00462         t30state = &fax.t30;
00463 #else
00464         /* for spandsp release 0.0.5 */
00465         t30state = &fax.t30_state;
00466 #endif
00467 
00468    ast_format_copy(&original_read_fmt, ast_channel_readformat(s->chan));
00469    if (original_read_fmt.id != AST_FORMAT_SLINEAR) {
00470       res = ast_set_read_format_by_id(s->chan, AST_FORMAT_SLINEAR);
00471       if (res < 0) {
00472          ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n");
00473          goto done;
00474       }
00475    }
00476 
00477    ast_format_copy(&original_write_fmt, ast_channel_writeformat(s->chan));
00478    if (original_write_fmt.id != AST_FORMAT_SLINEAR) {
00479       res = ast_set_write_format_by_id(s->chan, AST_FORMAT_SLINEAR);
00480       if (res < 0) {
00481          ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n");
00482          goto done;
00483       }
00484    }
00485 
00486    /* Initialize T30 terminal */
00487    fax_init(&fax, s->caller_mode);
00488 
00489    /* Setup logging */
00490    set_logging(&fax.logging);
00491    set_logging(&t30state->logging);
00492 
00493    /* Configure terminal */
00494    set_local_info(t30state, s);
00495    set_file(t30state, s);
00496    set_ecm(t30state, TRUE);
00497 
00498    fax_set_transmit_on_idle(&fax, TRUE);
00499 
00500    t30_set_phase_e_handler(t30state, phase_e_handler, s);
00501 
00502    start = state_change = ast_tvnow();
00503 
00504    ast_activate_generator(s->chan, &generator, &fax);
00505 
00506    while (!s->finished) {
00507       inf = NULL;
00508 
00509       if ((res = ast_waitfor(s->chan, 25)) < 0) {
00510          ast_debug(1, "Error waiting for a frame\n");
00511          break;
00512       }
00513 
00514       /* Watchdog */
00515       now = ast_tvnow();
00516       if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
00517          ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
00518          res = -1;
00519          break;
00520       }
00521 
00522       if (!res) {
00523          /* There was timeout waiting for a frame. Loop around and wait again */
00524          continue;
00525       }
00526 
00527       /* There is a frame available. Get it */
00528       res = 0;
00529 
00530       if (!(inf = ast_read(s->chan))) {
00531          ast_debug(1, "Channel hangup\n");
00532          res = -1;
00533          break;
00534       }
00535 
00536       ast_debug(10, "frame %d/%u, len=%d\n", inf->frametype, (unsigned int) inf->subclass.format.id, inf->datalen);
00537 
00538       /* Check the frame type. Format also must be checked because there is a chance
00539          that a frame in old format was already queued before we set channel format
00540          to slinear so it will still be received by ast_read */
00541       if (inf->frametype == AST_FRAME_VOICE && inf->subclass.format.id == AST_FORMAT_SLINEAR) {
00542          if (fax_rx(&fax, inf->data.ptr, inf->samples) < 0) {
00543             /* I know fax_rx never returns errors. The check here is for good style only */
00544             ast_log(LOG_WARNING, "fax_rx returned error\n");
00545             res = -1;
00546             break;
00547          }
00548          if (last_state != t30state->state) {
00549             state_change = ast_tvnow();
00550             last_state = t30state->state;
00551          }
00552       } else if ((inf->frametype == AST_FRAME_CONTROL) &&
00553             (inf->subclass.integer == AST_CONTROL_T38_PARAMETERS)) {
00554          struct ast_control_t38_parameters *parameters = inf->data.ptr;
00555 
00556          if (parameters->request_response == AST_T38_NEGOTIATED) {
00557             /* T38 switchover completed */
00558             s->t38parameters = *parameters;
00559             ast_debug(1, "T38 negotiated, finishing audio loop\n");
00560             res = 1;
00561             break;
00562          } else if (parameters->request_response == AST_T38_REQUEST_NEGOTIATE) {
00563             t38_parameters.request_response = AST_T38_NEGOTIATED;
00564             ast_debug(1, "T38 request received, accepting\n");
00565             /* Complete T38 switchover */
00566             ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
00567             /* Do not break audio loop, wait until channel driver finally acks switchover
00568              * with AST_T38_NEGOTIATED
00569              */
00570          }
00571       }
00572 
00573       ast_frfree(inf);
00574       inf = NULL;
00575    }
00576 
00577    ast_debug(1, "Loop finished, res=%d\n", res);
00578 
00579    if (inf)
00580       ast_frfree(inf);
00581 
00582    ast_deactivate_generator(s->chan);
00583 
00584    /* If we are switching to T38, remove phase E handler. Otherwise it will be executed
00585       by t30_terminate, display diagnostics and set status variables although no transmittion
00586       has taken place yet. */
00587    if (res > 0) {
00588       t30_set_phase_e_handler(t30state, NULL, NULL);
00589    }
00590 
00591    t30_terminate(t30state);
00592    fax_release(&fax);
00593 
00594 done:
00595    if (original_write_fmt.id != AST_FORMAT_SLINEAR) {
00596       if (ast_set_write_format(s->chan, &original_write_fmt) < 0)
00597          ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", ast_channel_name(s->chan));
00598    }
00599 
00600    if (original_read_fmt.id != AST_FORMAT_SLINEAR) {
00601       if (ast_set_read_format(s->chan, &original_read_fmt) < 0)
00602          ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", ast_channel_name(s->chan));
00603    }
00604 
00605    return res;
00606 
00607 }
00608 
00609 static int transmit_t38(fax_session *s)
00610 {
00611    int res = 0;
00612    t38_terminal_state_t t38;
00613    struct ast_frame *inf = NULL;
00614    int last_state = 0;
00615    struct timeval now, start, state_change, last_frame;
00616    t30_state_t *t30state;
00617    t38_core_state_t *t38state;
00618 
00619 #if SPANDSP_RELEASE_DATE >= 20080725
00620    /* for spandsp shaphots 0.0.6 and higher */
00621    t30state = &t38.t30;
00622    t38state = &t38.t38_fe.t38;
00623 #else
00624    /* for spandsp releases 0.0.5 */
00625    t30state = &t38.t30_state;
00626    t38state = &t38.t38;
00627 #endif
00628 
00629    /* Initialize terminal */
00630    memset(&t38, 0, sizeof(t38));
00631    if (t38_terminal_init(&t38, s->caller_mode, t38_tx_packet_handler, s->chan) == NULL) {
00632       ast_log(LOG_WARNING, "Unable to start T.38 termination.\n");
00633       res = -1;
00634       goto disable_t38;
00635    }
00636 
00637    t38_set_max_datagram_size(t38state, s->t38parameters.max_ifp);
00638 
00639    if (s->t38parameters.fill_bit_removal) {
00640       t38_set_fill_bit_removal(t38state, TRUE);
00641    }
00642    if (s->t38parameters.transcoding_mmr) {
00643       t38_set_mmr_transcoding(t38state, TRUE);
00644    }
00645    if (s->t38parameters.transcoding_jbig) {
00646       t38_set_jbig_transcoding(t38state, TRUE);
00647    }
00648 
00649    /* Setup logging */
00650    set_logging(&t38.logging);
00651    set_logging(&t30state->logging);
00652    set_logging(&t38state->logging);
00653 
00654    /* Configure terminal */
00655    set_local_info(t30state, s);
00656    set_file(t30state, s);
00657    set_ecm(t30state, TRUE);
00658 
00659    t30_set_phase_e_handler(t30state, phase_e_handler, s);
00660 
00661    now = start = state_change = ast_tvnow();
00662 
00663    while (!s->finished) {
00664       inf = NULL;
00665 
00666       if ((res = ast_waitfor(s->chan, 25)) < 0) {
00667          ast_debug(1, "Error waiting for a frame\n");
00668          break;
00669       }
00670 
00671       last_frame = now;
00672 
00673       /* Watchdog */
00674       now = ast_tvnow();
00675       if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
00676          ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
00677          res = -1;
00678          break;
00679       }
00680 
00681       t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000));
00682 
00683       if (!res) {
00684          /* There was timeout waiting for a frame. Loop around and wait again */
00685          continue;
00686       }
00687 
00688       /* There is a frame available. Get it */
00689       res = 0;
00690 
00691       if (!(inf = ast_read(s->chan))) {
00692          ast_debug(1, "Channel hangup\n");
00693          res = -1;
00694          break;
00695       }
00696 
00697       ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass.integer, inf->datalen);
00698 
00699       if (inf->frametype == AST_FRAME_MODEM && inf->subclass.integer == AST_MODEM_T38) {
00700          t38_core_rx_ifp_packet(t38state, inf->data.ptr, inf->datalen, inf->seqno);
00701          if (last_state != t30state->state) {
00702             state_change = ast_tvnow();
00703             last_state = t30state->state;
00704          }
00705       } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass.integer == AST_CONTROL_T38_PARAMETERS) {
00706          struct ast_control_t38_parameters *parameters = inf->data.ptr;
00707          if (parameters->request_response == AST_T38_TERMINATED) {
00708             ast_debug(1, "T38 down, finishing\n");
00709             break;
00710          }
00711       }
00712 
00713       ast_frfree(inf);
00714       inf = NULL;
00715    }
00716 
00717    ast_debug(1, "Loop finished, res=%d\n", res);
00718 
00719    if (inf)
00720       ast_frfree(inf);
00721 
00722    t30_terminate(t30state);
00723    t38_terminal_release(&t38);
00724 
00725 disable_t38:
00726    /* if we are not the caller, it's our job to shut down the T.38
00727     * session when the FAX transmisson is complete.
00728     */
00729    if ((s->caller_mode == FALSE) &&
00730        (ast_channel_get_t38_state(s->chan) == T38_STATE_NEGOTIATED)) {
00731       struct ast_control_t38_parameters t38_parameters = { .request_response = AST_T38_REQUEST_TERMINATE, };
00732 
00733       if (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0) {
00734          /* wait up to five seconds for negotiation to complete */
00735          unsigned int timeout = 5000;
00736          int ms;
00737 
00738          ast_debug(1, "Shutting down T.38 on %s\n", ast_channel_name(s->chan));
00739          while (timeout > 0) {
00740             ms = ast_waitfor(s->chan, 1000);
00741             if (ms < 0) {
00742                ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", ast_channel_name(s->chan));
00743                return -1;
00744             }
00745             if (!ms) {
00746                /* nothing happened */
00747                if (timeout > 0) {
00748                   timeout -= 1000;
00749                   continue;
00750                } else {
00751                   ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 shutdown.\n", ast_channel_name(s->chan));
00752                   break;
00753                }
00754             }
00755             if (!(inf = ast_read(s->chan))) {
00756                return -1;
00757             }
00758             if ((inf->frametype == AST_FRAME_CONTROL) &&
00759                 (inf->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
00760                 (inf->datalen == sizeof(t38_parameters))) {
00761                struct ast_control_t38_parameters *parameters = inf->data.ptr;
00762 
00763                switch (parameters->request_response) {
00764                case AST_T38_TERMINATED:
00765                   ast_debug(1, "Shut down T.38 on %s\n", ast_channel_name(s->chan));
00766                   break;
00767                case AST_T38_REFUSED:
00768                   ast_log(LOG_WARNING, "channel '%s' refused to disable T.38\n", ast_channel_name(s->chan));
00769                   break;
00770                default:
00771                   ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", ast_channel_name(s->chan));
00772                   break;
00773                }
00774                ast_frfree(inf);
00775                break;
00776             }
00777             ast_frfree(inf);
00778          }
00779       }
00780    }
00781 
00782    return res;
00783 }
00784 
00785 static int transmit(fax_session *s)
00786 {
00787    int res = 0;
00788 
00789    /* Clear all channel variables which to be set by the application.
00790       Pre-set status to error so in case of any problems we can just leave */
00791    pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "FAILED"); 
00792    pbx_builtin_setvar_helper(s->chan, "FAXERROR", "Channel problems"); 
00793 
00794    pbx_builtin_setvar_helper(s->chan, "FAXMODE", NULL);
00795    pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", NULL);
00796    pbx_builtin_setvar_helper(s->chan, "FAXPAGES", "0");
00797    pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", NULL);
00798    pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", NULL); 
00799 
00800    if (ast_channel_state(s->chan) != AST_STATE_UP) {
00801       /* Shouldn't need this, but checking to see if channel is already answered
00802        * Theoretically asterisk should already have answered before running the app */
00803       res = ast_answer(s->chan);
00804       if (res) {
00805          ast_log(LOG_WARNING, "Could not answer channel '%s'\n", ast_channel_name(s->chan));
00806          return res;
00807       }
00808    }
00809 
00810    s->t38state = ast_channel_get_t38_state(s->chan);
00811    if (s->t38state != T38_STATE_NEGOTIATED) {
00812       /* T38 is not negotiated on the channel yet. First start regular transmission. If it switches to T38, follow */   
00813       pbx_builtin_setvar_helper(s->chan, "FAXMODE", "audio"); 
00814       res = transmit_audio(s);
00815       if (res > 0) {
00816          /* transmit_audio reports switchover to T38. Update t38state */
00817          s->t38state = ast_channel_get_t38_state(s->chan);
00818          if (s->t38state != T38_STATE_NEGOTIATED) {
00819             ast_log(LOG_ERROR, "Audio loop reports T38 switchover but t38state != T38_STATE_NEGOTIATED\n");
00820          }
00821       }
00822    }
00823 
00824    if (s->t38state == T38_STATE_NEGOTIATED) {
00825       pbx_builtin_setvar_helper(s->chan, "FAXMODE", "T38"); 
00826       res = transmit_t38(s);
00827    }
00828 
00829    if (res) {
00830       ast_log(LOG_WARNING, "Transmission error\n");
00831       res = -1;
00832    } else if (s->finished < 0) {
00833       ast_log(LOG_WARNING, "Transmission failed\n");
00834    } else if (s->finished > 0) {
00835       ast_debug(1, "Transmission finished Ok\n");
00836    }
00837 
00838    return res;
00839 }
00840 
00841 /* === Application functions === */
00842 
00843 static int sndfax_exec(struct ast_channel *chan, const char *data)
00844 {
00845    int res = 0;
00846    char *parse;
00847    fax_session session = { 0, };
00848    char restore_digit_detect = 0;
00849 
00850    AST_DECLARE_APP_ARGS(args,
00851       AST_APP_ARG(file_name);
00852       AST_APP_ARG(options);
00853    );
00854 
00855    if (chan == NULL) {
00856       ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
00857       return -1;
00858    }
00859 
00860    /* The next few lines of code parse out the filename and header from the input string */
00861    if (ast_strlen_zero(data)) {
00862       /* No data implies no filename or anything is present */
00863       ast_log(LOG_ERROR, "SendFAX requires an argument (filename)\n");
00864       return -1;
00865    }
00866 
00867    parse = ast_strdupa(data);
00868    AST_STANDARD_APP_ARGS(args, parse);
00869    
00870    session.caller_mode = TRUE;
00871 
00872    if (args.options) {
00873       if (strchr(args.options, 'a'))
00874          session.caller_mode = FALSE;
00875    }
00876 
00877    /* Done parsing */
00878    session.direction = 1;
00879    session.file_name = args.file_name;
00880    session.chan = chan;
00881    session.finished = 0;
00882 
00883    /* get current digit detection mode, then disable digit detection if enabled */
00884    {
00885       int dummy = sizeof(restore_digit_detect);
00886 
00887       ast_channel_queryoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, &dummy, 0);
00888    }
00889 
00890    if (restore_digit_detect) {
00891       char new_digit_detect = 0;
00892 
00893       ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &new_digit_detect, sizeof(new_digit_detect), 0);
00894    }
00895 
00896    /* disable FAX tone detection if enabled */
00897    {
00898       char new_fax_detect = 0;
00899 
00900       ast_channel_setoption(chan, AST_OPTION_FAX_DETECT, &new_fax_detect, sizeof(new_fax_detect), 0);
00901    }
00902 
00903    res = transmit(&session);
00904 
00905    if (restore_digit_detect) {
00906       ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, sizeof(restore_digit_detect), 0);
00907    }
00908 
00909    return res;
00910 }
00911 
00912 static int rcvfax_exec(struct ast_channel *chan, const char *data)
00913 {
00914    int res = 0;
00915    char *parse;
00916    fax_session session;
00917    char restore_digit_detect = 0;
00918 
00919    AST_DECLARE_APP_ARGS(args,
00920       AST_APP_ARG(file_name);
00921       AST_APP_ARG(options);
00922    );
00923 
00924    if (chan == NULL) {
00925       ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
00926       return -1;
00927    }
00928 
00929    /* The next few lines of code parse out the filename and header from the input string */
00930    if (ast_strlen_zero(data)) {
00931       /* No data implies no filename or anything is present */
00932       ast_log(LOG_ERROR, "ReceiveFAX requires an argument (filename)\n");
00933       return -1;
00934    }
00935 
00936    parse = ast_strdupa(data);
00937    AST_STANDARD_APP_ARGS(args, parse);
00938    
00939    session.caller_mode = FALSE;
00940 
00941    if (args.options) {
00942       if (strchr(args.options, 'c'))
00943          session.caller_mode = TRUE;
00944    }
00945 
00946    /* Done parsing */
00947    session.direction = 0;
00948    session.file_name = args.file_name;
00949    session.chan = chan;
00950    session.finished = 0;
00951 
00952    /* get current digit detection mode, then disable digit detection if enabled */
00953    {
00954       int dummy = sizeof(restore_digit_detect);
00955 
00956       ast_channel_queryoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, &dummy, 0);
00957    }
00958 
00959    if (restore_digit_detect) {
00960       char new_digit_detect = 0;
00961 
00962       ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &new_digit_detect, sizeof(new_digit_detect), 0);
00963    }
00964 
00965    /* disable FAX tone detection if enabled */
00966    {
00967       char new_fax_detect = 0;
00968 
00969       ast_channel_setoption(chan, AST_OPTION_FAX_DETECT, &new_fax_detect, sizeof(new_fax_detect), 0);
00970    }
00971 
00972    res = transmit(&session);
00973 
00974    if (restore_digit_detect) {
00975       ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, sizeof(restore_digit_detect), 0);
00976    }
00977 
00978    return res;
00979 }
00980 
00981 static int unload_module(void)
00982 {
00983    int res;
00984 
00985    res = ast_unregister_application(app_sndfax_name); 
00986    res |= ast_unregister_application(app_rcvfax_name);   
00987 
00988    return res;
00989 }
00990 
00991 static int load_module(void)
00992 {
00993    int res ;
00994 
00995    res = ast_register_application_xml(app_sndfax_name, sndfax_exec);
00996    res |= ast_register_application_xml(app_rcvfax_name, rcvfax_exec);
00997 
00998    /* The default SPAN message handler prints to stderr. It is something we do not want */
00999    span_set_message_handler(NULL);
01000 
01001    return res;
01002 }
01003 
01004 
01005 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Simple FAX Application",
01006       .load = load_module,
01007       .unload = unload_module,
01008       );
01009 
01010 

Generated on Thu Oct 11 06:33:31 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6