2 * Asterisk -- A telephony toolkit for Linux.
4 * Simple fax applications
6 * 2007-2008, Dmitry Andrianov <asterisk@dima.spb.ru>
8 * Code based on original implementation by Steve Underwood <steveu@coppice.org>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
16 <depend>spandsp</depend>
21 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
33 #include "asterisk/lock.h"
34 #include "asterisk/file.h"
35 #include "asterisk/logger.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/pbx.h"
38 #include "asterisk/app.h"
39 #include "asterisk/dsp.h"
40 #include "asterisk/module.h"
41 #include "asterisk/manager.h"
43 static char *app_sndfax_name
= "SendFAX";
44 static char *app_sndfax_synopsis
= "Send a FAX";
45 static char *app_sndfax_desc
=
46 " SendFAX(filename[|options]):\n"
47 "Send a given TIFF file to the channel as a FAX.\n"
48 "The option string may contain zero or more of the following characters:\n"
49 " 'a' -- makes the application behave as an answering machine\n"
50 " The default behaviour is to behave as a calling machine.\n"
52 "This application uses following variables:\n"
53 " LOCALSTATIONID to identify itself to the remote end.\n"
54 " LOCALHEADERINFO to generate a header line on each page.\n"
56 "This application sets the following channel variables upon completion:\n"
57 " FAXSTATUS - status of operation:\n"
59 " FAXERROR - Error when FAILED\n"
60 " REMOTESTATIONID - CSID of the remote side.\n"
61 " FAXPAGES - number of pages sent.\n"
62 " FAXBITRATE - transmition rate.\n"
63 " FAXRESOLUTION - resolution.\n"
65 "Returns -1 in case of user hang up or any channel error.\n"
66 "Returns 0 on success.\n";
68 static char *app_rcvfax_name
= "ReceiveFAX";
69 static char *app_rcvfax_synopsis
= "Receive a FAX";
70 static char *app_rcvfax_desc
=
71 " ReceiveFAX(filename[|options]):\n"
72 "Receives a fax from the channel into the given filename overwriting\n"
73 "the file if it already exists. File created will have TIFF format.\n"
74 "The option string may contain zero or more of the following characters:\n"
75 " 'c' -- makes the application behave as a calling machine\n"
76 " The default behaviour is to behave as an answering machine.\n"
78 "This application uses following variables:\n"
79 " LOCALSTATIONID to identify itself to the remote end.\n"
80 " LOCALHEADERINFO to generate a header line on each page.\n"
82 "This application sets the following channel variables upon completion:\n"
83 " FAXSTATUS - status of operation:\n"
85 " FAXERROR - Error when FAILED\n"
86 " REMOTESTATIONID - CSID of the remote side.\n"
87 " FAXPAGES - number of pages sent.\n"
88 " FAXBITRATE - transmition rate.\n"
89 " FAXRESOLUTION - resolution.\n"
91 "Returns -1 in case of user hang up or any channel error.\n"
92 "Returns 0 on success.\n";
94 #define MAX_SAMPLES 240
96 /* Watchdog. I have seen situations when remote fax disconnects (because of poor line
97 quality) while SpanDSP continues staying in T30_STATE_IV_CTC state forever.
98 To avoid this, we terminate when we see that T30 state does not change for 5 minutes.
99 We also terminate application when more than 30 minutes passed regardless of
100 state changes. This is just a precaution measure - no fax should take that long */
102 #define WATCHDOG_TOTAL_TIMEOUT 30 * 60
103 #define WATCHDOG_STATE_TIMEOUT 5 * 60
106 struct ast_channel
*chan
;
107 enum ast_t38_state t38state
; /* T38 state of the channel */
108 int direction
; /* Fax direction: 0 - receiving, 1 - sending */
112 volatile int finished
;
115 static void span_message(int level
, const char *msg
)
117 if (level
== SPAN_LOG_ERROR
) {
118 ast_log(LOG_ERROR
, "%s", msg
);
119 } else if (level
== SPAN_LOG_WARNING
) {
120 ast_log(LOG_WARNING
, "%s", msg
);
122 ast_log(LOG_DEBUG
, "%s", msg
);
126 static int t38_tx_packet_handler(t38_core_state_t
*s
, void *user_data
, const uint8_t *buf
, int len
, int count
)
128 struct ast_channel
*chan
= (struct ast_channel
*) user_data
;
130 struct ast_frame outf
= {
131 .frametype
= AST_FRAME_MODEM
,
132 .subclass
= AST_MODEM_T38
,
136 /* TODO: Asterisk does not provide means of resending the same packet multiple
137 times so count is ignored at the moment */
139 AST_FRAME_SET_BUFFER(&outf
, buf
, 0, len
);
141 if (ast_write(chan
, &outf
) < 0) {
142 ast_log(LOG_WARNING
, "Unable to write frame to channel; %s\n", strerror(errno
));
149 static void phase_e_handler(t30_state_t
*f
, void *user_data
, int result
)
151 const char *local_ident
;
152 const char *far_ident
;
154 fax_session
*s
= (fax_session
*) user_data
;
157 ast_debug(1, "Fax phase E handler. result=%d\n", result
);
159 t30_get_transfer_statistics(f
, &stat
);
161 s
= (fax_session
*) user_data
;
163 if (result
!= T30_ERR_OK
) {
166 /* FAXSTATUS is already set to FAILED */
167 pbx_builtin_setvar_helper(s
->chan
, "FAXERROR", t30_completion_code_to_str(result
));
169 ast_log(LOG_WARNING
, "Error transmitting fax. result=%d: %s.\n", result
, t30_completion_code_to_str(result
));
176 local_ident
= t30_get_tx_ident(f
);
177 far_ident
= t30_get_rx_ident(f
);
178 pbx_builtin_setvar_helper(s
->chan
, "FAXSTATUS", "SUCCESS");
179 pbx_builtin_setvar_helper(s
->chan
, "FAXERROR", NULL
);
180 pbx_builtin_setvar_helper(s
->chan
, "REMOTESTATIONID", far_ident
);
181 snprintf(buf
, sizeof(buf
), "%d", stat
.pages_transferred
);
182 pbx_builtin_setvar_helper(s
->chan
, "FAXPAGES", buf
);
183 snprintf(buf
, sizeof(buf
), "%d", stat
.y_resolution
);
184 pbx_builtin_setvar_helper(s
->chan
, "FAXRESOLUTION", buf
);
185 snprintf(buf
, sizeof(buf
), "%d", stat
.bit_rate
);
186 pbx_builtin_setvar_helper(s
->chan
, "FAXBITRATE", buf
);
188 ast_debug(1, "Fax transmitted successfully.\n");
189 ast_debug(1, " Remote station ID: %s\n", far_ident
);
190 ast_debug(1, " Pages transferred: %d\n", stat
.pages_transferred
);
191 ast_debug(1, " Image resolution: %d x %d\n", stat
.x_resolution
, stat
.y_resolution
);
192 ast_debug(1, " Transfer Rate: %d\n", stat
.bit_rate
);
194 manager_event(EVENT_FLAG_CALL
,
195 s
->direction
? "FaxSent" : "FaxReceived",
199 "RemoteStationID: %s\r\n"
200 "LocalStationID: %s\r\n"
201 "PagesTransferred: %d\r\n"
203 "TransferRate: %d\r\n"
207 S_OR(s
->chan
->cid
.cid_num
, ""),
210 stat
.pages_transferred
,
216 /* === Helper functions to configure fax === */
218 /* Setup SPAN logging according to Asterisk debug level */
219 static int set_logging(logging_state_t
*state
)
221 int level
= SPAN_LOG_WARNING
+ option_debug
;
223 span_log_set_message_handler(state
, span_message
);
224 span_log_set_level(state
, SPAN_LOG_SHOW_SEVERITY
| SPAN_LOG_SHOW_PROTOCOL
| level
);
229 static void set_local_info(t30_state_t
*state
, fax_session
*s
)
233 x
= pbx_builtin_getvar_helper(s
->chan
, "LOCALSTATIONID");
234 if (!ast_strlen_zero(x
))
235 t30_set_tx_ident(state
, x
);
237 x
= pbx_builtin_getvar_helper(s
->chan
, "LOCALHEADERINFO");
238 if (!ast_strlen_zero(x
))
239 t30_set_tx_page_header_info(state
, x
);
242 static void set_file(t30_state_t
*state
, fax_session
*s
)
245 t30_set_tx_file(state
, s
->file_name
, -1, -1);
247 t30_set_rx_file(state
, s
->file_name
, -1);
250 static void set_ecm(t30_state_t
*state
, int ecm
)
252 t30_set_ecm_capability(state
, ecm
);
253 t30_set_supported_compressions(state
, T30_SUPPORT_T4_1D_COMPRESSION
| T30_SUPPORT_T4_2D_COMPRESSION
| T30_SUPPORT_T6_COMPRESSION
);
256 /* === Generator === */
258 /* This function is only needed to return passed params so
259 generator_activate will save it to channel's generatordata */
260 static void *fax_generator_alloc(struct ast_channel
*chan
, void *params
)
265 static int fax_generator_generate(struct ast_channel
*chan
, void *data
, int len
, int samples
)
267 fax_state_t
*fax
= (fax_state_t
*) data
;
268 uint8_t buffer
[AST_FRIENDLY_OFFSET
+ MAX_SAMPLES
* sizeof(uint16_t)];
269 int16_t *buf
= (int16_t *) (buffer
+ AST_FRIENDLY_OFFSET
);
271 struct ast_frame outf
= {
272 .frametype
= AST_FRAME_VOICE
,
273 .subclass
= AST_FORMAT_SLINEAR
,
277 if (samples
> MAX_SAMPLES
) {
278 ast_log(LOG_WARNING
, "Only generating %d samples, where %d requested\n", MAX_SAMPLES
, samples
);
279 samples
= MAX_SAMPLES
;
282 if ((len
= fax_tx(fax
, buf
, samples
)) > 0) {
284 AST_FRAME_SET_BUFFER(&outf
, buffer
, AST_FRIENDLY_OFFSET
, len
* sizeof(int16_t));
286 if (ast_write(chan
, &outf
) < 0) {
287 ast_log(LOG_WARNING
, "Failed to write frame to '%s': %s\n", chan
->name
, strerror(errno
));
295 struct ast_generator generator
= {
296 alloc
: fax_generator_alloc
,
297 generate
: fax_generator_generate
,
301 /* === Transmission === */
303 static int transmit_audio(fax_session
*s
)
306 int original_read_fmt
= AST_FORMAT_SLINEAR
;
307 int original_write_fmt
= AST_FORMAT_SLINEAR
;
309 struct ast_dsp
*dsp
= NULL
;
311 struct ast_frame
*inf
= NULL
;
312 struct ast_frame
*fr
;
314 struct timeval now
, start
, state_change
;
315 enum ast_control_t38 t38control
;
317 original_read_fmt
= s
->chan
->readformat
;
318 if (original_read_fmt
!= AST_FORMAT_SLINEAR
) {
319 res
= ast_set_read_format(s
->chan
, AST_FORMAT_SLINEAR
);
321 ast_log(LOG_WARNING
, "Unable to set to linear read mode, giving up\n");
326 original_write_fmt
= s
->chan
->writeformat
;
327 if (original_write_fmt
!= AST_FORMAT_SLINEAR
) {
328 res
= ast_set_write_format(s
->chan
, AST_FORMAT_SLINEAR
);
330 ast_log(LOG_WARNING
, "Unable to set to linear write mode, giving up\n");
335 /* Initialize T30 terminal */
336 fax_init(&fax
, s
->caller_mode
);
339 set_logging(&fax
.logging
);
340 set_logging(&fax
.t30_state
.logging
);
342 /* Configure terminal */
343 set_local_info(&fax
.t30_state
, s
);
344 set_file(&fax
.t30_state
, s
);
345 set_ecm(&fax
.t30_state
, TRUE
);
347 fax_set_transmit_on_idle(&fax
, TRUE
);
349 t30_set_phase_e_handler(&fax
.t30_state
, phase_e_handler
, s
);
351 if (s
->t38state
== T38_STATE_UNAVAILABLE
) {
352 ast_debug(1, "T38 is unavailable on %s\n", s
->chan
->name
);
353 } else if (!s
->direction
) {
354 /* We are receiving side and this means we are the side which should
355 request T38 when the fax is detected. Use DSP to detect fax tone */
356 ast_debug(1, "Setting up CNG detection on %s\n", s
->chan
->name
);
358 ast_dsp_set_features(dsp
, DSP_FEATURE_FAX_DETECT
);
359 ast_dsp_set_faxmode(dsp
, DSP_FAXMODE_DETECT_CNG
);
363 start
= state_change
= ast_tvnow();
365 ast_activate_generator(s
->chan
, &generator
, &fax
);
367 while (!s
->finished
) {
368 res
= ast_waitfor(s
->chan
, 20);
374 inf
= ast_read(s
->chan
);
376 ast_debug(1, "Channel hangup\n");
381 ast_debug(10, "frame %d/%d, len=%d\n", inf
->frametype
, inf
->subclass
, inf
->datalen
);
383 /* Detect fax tone */
384 if (detect_tone
&& inf
->frametype
== AST_FRAME_VOICE
) {
385 /* Duplicate frame because ast_dsp_process may free the frame passed */
388 /* Do not pass channel to ast_dsp_process otherwise it may queue modified audio frame back */
389 fr
= ast_dsp_process(NULL
, dsp
, fr
);
390 if (fr
&& fr
->frametype
== AST_FRAME_DTMF
&& fr
->subclass
== 'f') {
391 ast_debug(1, "Fax tone detected. Requesting T38\n");
392 t38control
= AST_T38_REQUEST_NEGOTIATE
;
393 ast_indicate_data(s
->chan
, AST_CONTROL_T38
, &t38control
, sizeof(t38control
));
401 /* Check the frame type. Format also must be checked because there is a chance
402 that a frame in old format was already queued before we set chanel format
403 to slinear so it will still be received by ast_read */
404 if (inf
->frametype
== AST_FRAME_VOICE
&& inf
->subclass
== AST_FORMAT_SLINEAR
) {
406 if (fax_rx(&fax
, inf
->data
.ptr
, inf
->samples
) < 0) {
407 /* I know fax_rx never returns errors. The check here is for good style only */
408 ast_log(LOG_WARNING
, "fax_rx returned error\n");
414 if (last_state
!= fax
.t30_state
.state
) {
415 state_change
= ast_tvnow();
416 last_state
= fax
.t30_state
.state
;
418 } else if (inf
->frametype
== AST_FRAME_CONTROL
&& inf
->subclass
== AST_CONTROL_T38
&&
419 inf
->datalen
== sizeof(enum ast_control_t38
)) {
420 t38control
=*((enum ast_control_t38
*) inf
->data
.ptr
);
421 if (t38control
== AST_T38_NEGOTIATED
) {
422 /* T38 switchover completed */
423 ast_debug(1, "T38 negotiated, finishing audio loop\n");
434 if (ast_tvdiff_sec(now
, start
) > WATCHDOG_TOTAL_TIMEOUT
|| ast_tvdiff_sec(now
, state_change
) > WATCHDOG_STATE_TIMEOUT
) {
435 ast_log(LOG_WARNING
, "It looks like we hung. Aborting.\n");
441 ast_debug(1, "Loop finished, res=%d\n", res
);
449 ast_deactivate_generator(s
->chan
);
451 /* Remove phase E handler because we do not want it to be executed
452 only because we called t30_terminate */
453 t30_set_phase_e_handler(&fax
.t30_state
, NULL
, NULL
);
455 t30_terminate(&fax
.t30_state
);
459 if (original_write_fmt
!= AST_FORMAT_SLINEAR
) {
460 if (ast_set_write_format(s
->chan
, original_write_fmt
) < 0)
461 ast_log(LOG_WARNING
, "Unable to restore write format on '%s'\n", s
->chan
->name
);
464 if (original_read_fmt
!= AST_FORMAT_SLINEAR
) {
465 if (ast_set_read_format(s
->chan
, original_read_fmt
) < 0)
466 ast_log(LOG_WARNING
, "Unable to restore read format on '%s'\n", s
->chan
->name
);
473 static int transmit_t38(fax_session
*s
)
476 t38_terminal_state_t t38
;
477 struct ast_frame
*inf
= NULL
;
479 struct timeval now
, start
, state_change
, last_frame
;
480 enum ast_control_t38 t38control
;
482 /* Initialize terminal */
483 memset(&t38
, 0, sizeof(t38
));
484 if (t38_terminal_init(&t38
, s
->caller_mode
, t38_tx_packet_handler
, s
->chan
) == NULL
) {
485 ast_log(LOG_WARNING
, "Unable to start T.38 termination.\n");
490 set_logging(&t38
.logging
);
491 set_logging(&t38
.t30_state
.logging
);
492 set_logging(&t38
.t38
.logging
);
494 /* Configure terminal */
495 set_local_info(&t38
.t30_state
, s
);
496 set_file(&t38
.t30_state
, s
);
497 set_ecm(&t38
.t30_state
, TRUE
);
499 t30_set_phase_e_handler(&t38
.t30_state
, phase_e_handler
, s
);
501 now
= start
= state_change
= ast_tvnow();
503 while (!s
->finished
) {
505 res
= ast_waitfor(s
->chan
, 20);
513 t38_terminal_send_timeout(&t38
, ast_tvdiff_us(now
, last_frame
) / (1000000 / 8000));
515 inf
= ast_read(s
->chan
);
517 ast_debug(1, "Channel hangup\n");
522 ast_debug(10, "frame %d/%d, len=%d\n", inf
->frametype
, inf
->subclass
, inf
->datalen
);
524 if (inf
->frametype
== AST_FRAME_MODEM
&& inf
->subclass
== AST_MODEM_T38
) {
525 t38_core_rx_ifp_packet(&t38
.t38
, inf
->data
.ptr
, inf
->datalen
, inf
->seqno
);
528 if (last_state
!= t38
.t30_state
.state
) {
529 state_change
= ast_tvnow();
530 last_state
= t38
.t30_state
.state
;
532 } else if (inf
->frametype
== AST_FRAME_CONTROL
&& inf
->subclass
== AST_CONTROL_T38
&&
533 inf
->datalen
== sizeof(enum ast_control_t38
)) {
535 t38control
= *((enum ast_control_t38
*) inf
->data
.ptr
);
537 if (t38control
== AST_T38_TERMINATED
|| t38control
== AST_T38_REFUSED
) {
538 ast_debug(1, "T38 down, terminating\n");
548 if (ast_tvdiff_sec(now
, start
) > WATCHDOG_TOTAL_TIMEOUT
|| ast_tvdiff_sec(now
, state_change
) > WATCHDOG_STATE_TIMEOUT
) {
549 ast_log(LOG_WARNING
, "It looks like we hung. Aborting.\n");
555 ast_debug(1, "Loop finished, res=%d\n", res
);
560 /* Remove phase E handler because we do not want it to be executed
561 only because we called t30_terminate */
562 t30_set_phase_e_handler(&t38
.t30_state
, NULL
, NULL
);
564 t30_terminate(&t38
.t30_state
);
569 static int transmit(fax_session
*s
)
573 /* Clear all channel variables which to be set by the application.
574 Pre-set status to error so in case of any problems we can just leave */
575 pbx_builtin_setvar_helper(s
->chan
, "FAXSTATUS", "FAILED");
576 pbx_builtin_setvar_helper(s
->chan
, "FAXERROR", "Channel problems");
578 pbx_builtin_setvar_helper(s
->chan
, "REMOTESTATIONID", NULL
);
579 pbx_builtin_setvar_helper(s
->chan
, "FAXPAGES", NULL
);
580 pbx_builtin_setvar_helper(s
->chan
, "FAXRESOLUTION", NULL
);
581 pbx_builtin_setvar_helper(s
->chan
, "FAXBITRATE", NULL
);
583 if (s
->chan
->_state
!= AST_STATE_UP
) {
584 /* Shouldn't need this, but checking to see if channel is already answered
585 * Theoretically asterisk should already have answered before running the app */
586 res
= ast_answer(s
->chan
);
588 ast_log(LOG_WARNING
, "Could not answer channel '%s'\n", s
->chan
->name
);
593 s
->t38state
= ast_channel_get_t38_state(s
->chan
);
594 if (s
->t38state
!= T38_STATE_NEGOTIATED
) {
595 /* T38 is not negotiated on the channel yet. First start regular transmission. If it switches to T38, follow */
596 res
= transmit_audio(s
);
598 /* transmit_audio reports switchover to T38. Update t38state */
599 s
->t38state
= ast_channel_get_t38_state(s
->chan
);
600 if (s
->t38state
!= T38_STATE_NEGOTIATED
) {
601 ast_log(LOG_ERROR
, "Audio loop reports T38 switchover but t38state != T38_STATE_NEGOTIATED\n");
606 if (s
->t38state
== T38_STATE_NEGOTIATED
) {
607 res
= transmit_t38(s
);
611 ast_log(LOG_WARNING
, "Transmission error\n");
613 } else if (s
->finished
< 0) {
614 ast_log(LOG_WARNING
, "Transmission failed\n");
615 } else if (s
->finished
> 0) {
616 ast_debug(1, "Transmission finished Ok\n");
622 /* === Application functions === */
624 static int sndfax_exec(struct ast_channel
*chan
, void *data
)
630 AST_DECLARE_APP_ARGS(args
,
631 AST_APP_ARG(file_name
);
632 AST_APP_ARG(options
);
636 ast_log(LOG_ERROR
, "Fax channel is NULL. Giving up.\n");
640 /* The next few lines of code parse out the filename and header from the input string */
641 if (ast_strlen_zero(data
)) {
642 /* No data implies no filename or anything is present */
643 ast_log(LOG_ERROR
, "SendFAX requires an argument (filename)\n");
647 parse
= ast_strdupa(data
);
648 AST_STANDARD_APP_ARGS(args
, parse
);
650 session
.caller_mode
= TRUE
;
653 if (strchr(args
.options
, 'a'))
654 session
.caller_mode
= FALSE
;
658 session
.direction
= 1;
659 session
.file_name
= args
.file_name
;
661 session
.finished
= 0;
663 res
= transmit(&session
);
668 static int rcvfax_exec(struct ast_channel
*chan
, void *data
)
674 AST_DECLARE_APP_ARGS(args
,
675 AST_APP_ARG(file_name
);
676 AST_APP_ARG(options
);
680 ast_log(LOG_ERROR
, "Fax channel is NULL. Giving up.\n");
684 /* The next few lines of code parse out the filename and header from the input string */
685 if (ast_strlen_zero(data
)) {
686 /* No data implies no filename or anything is present */
687 ast_log(LOG_ERROR
, "ReceiveFAX requires an argument (filename)\n");
691 parse
= ast_strdupa(data
);
692 AST_STANDARD_APP_ARGS(args
, parse
);
694 session
.caller_mode
= FALSE
;
697 if (strchr(args
.options
, 'c'))
698 session
.caller_mode
= TRUE
;
702 session
.direction
= 0;
703 session
.file_name
= args
.file_name
;
705 session
.finished
= 0;
707 res
= transmit(&session
);
712 static int unload_module(void)
716 res
= ast_unregister_application(app_sndfax_name
);
717 res
|= ast_unregister_application(app_rcvfax_name
);
722 static int load_module(void)
726 res
= ast_register_application(app_sndfax_name
, sndfax_exec
, app_sndfax_synopsis
, app_sndfax_desc
);
727 res
|= ast_register_application(app_rcvfax_name
, rcvfax_exec
, app_rcvfax_synopsis
, app_rcvfax_desc
);
729 /* The default SPAN message handler prints to stderr. It is something we do not want */
730 span_set_message_handler(NULL
);
736 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "Simple FAX Application",
738 .unload
= unload_module
,