Minor formatting change to test a mantis change ...
[asterisk-bristuff.git] / apps / app_fax.c
blobcf98cc9a8092fbbd8dc725e848d84184e41816a8
1 /*
2 * Asterisk -- A telephony toolkit for Linux.
4 * Simple fax applications
5 *
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
15 /*** MODULEINFO
16 <depend>spandsp</depend>
17 ***/
19 #include "asterisk.h"
21 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
23 #include <string.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <inttypes.h>
27 #include <pthread.h>
28 #include <errno.h>
29 #include <tiffio.h>
31 #include <spandsp.h>
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"
51 "\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"
55 "\n"
56 "This application sets the following channel variables upon completion:\n"
57 " FAXSTATUS - status of operation:\n"
58 " SUCCESS | FAILED\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"
64 "\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"
77 "\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"
81 "\n"
82 "This application sets the following channel variables upon completion:\n"
83 " FAXSTATUS - status of operation:\n"
84 " SUCCESS | FAILED\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"
90 "\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
105 typedef struct {
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 */
109 int caller_mode;
110 char *file_name;
112 volatile int finished;
113 } fax_session;
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);
121 } else {
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,
133 .src = __FUNCTION__,
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));
143 return -1;
146 return 0;
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;
153 char buf[20];
154 fax_session *s = (fax_session *) user_data;
155 t30_stats_t stat;
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) {
164 s->finished = -1;
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));
171 return;
174 s->finished = 1;
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",
196 "Channel: %s\r\n"
197 "Exten: %s\r\n"
198 "CallerID: %s\r\n"
199 "RemoteStationID: %s\r\n"
200 "LocalStationID: %s\r\n"
201 "PagesTransferred: %d\r\n"
202 "Resolution: %d\r\n"
203 "TransferRate: %d\r\n"
204 "FileName: %s\r\n",
205 s->chan->name,
206 s->chan->exten,
207 S_OR(s->chan->cid.cid_num, ""),
208 far_ident,
209 local_ident,
210 stat.pages_transferred,
211 stat.y_resolution,
212 stat.bit_rate,
213 s->file_name);
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);
226 return 0;
229 static void set_local_info(t30_state_t *state, fax_session *s)
231 const char *x;
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)
244 if (s->direction)
245 t30_set_tx_file(state, s->file_name, -1, -1);
246 else
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)
262 return 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,
274 .src = __FUNCTION__,
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) {
283 outf.samples = len;
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));
288 return -1;
292 return 0;
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)
305 int res = -1;
306 int original_read_fmt = AST_FORMAT_SLINEAR;
307 int original_write_fmt = AST_FORMAT_SLINEAR;
308 fax_state_t fax;
309 struct ast_dsp *dsp = NULL;
310 int detect_tone = 0;
311 struct ast_frame *inf = NULL;
312 struct ast_frame *fr;
313 int last_state = 0;
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);
320 if (res < 0) {
321 ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n");
322 goto done;
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);
329 if (res < 0) {
330 ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n");
331 goto done;
335 /* Initialize T30 terminal */
336 fax_init(&fax, s->caller_mode);
338 /* Setup logging */
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);
357 dsp = ast_dsp_new();
358 ast_dsp_set_features(dsp, DSP_FEATURE_FAX_DETECT);
359 ast_dsp_set_faxmode(dsp, DSP_FAXMODE_DETECT_CNG);
360 detect_tone = 1;
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);
369 if (res < 0)
370 break;
371 else if (res > 0)
372 res = 0;
374 inf = ast_read(s->chan);
375 if (inf == NULL) {
376 ast_debug(1, "Channel hangup\n");
377 res = -1;
378 break;
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 */
386 fr = ast_frdup(inf);
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));
394 detect_tone = 0;
397 ast_frfree(fr);
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");
409 res = -1;
410 break;
413 /* Watchdog */
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");
424 res = 1;
425 break;
429 ast_frfree(inf);
430 inf = NULL;
432 /* Watchdog */
433 now = ast_tvnow();
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");
436 res = -1;
437 break;
441 ast_debug(1, "Loop finished, res=%d\n", res);
443 if (inf)
444 ast_frfree(inf);
446 if (dsp)
447 ast_dsp_free(dsp);
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);
456 fax_release(&fax);
458 done:
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);
469 return res;
473 static int transmit_t38(fax_session *s)
475 int res = 0;
476 t38_terminal_state_t t38;
477 struct ast_frame *inf = NULL;
478 int last_state = 0;
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");
486 return -1;
489 /* Setup logging */
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);
506 if (res < 0)
507 break;
508 else if (res > 0)
509 res = 0;
511 last_frame = now;
512 now = ast_tvnow();
513 t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000));
515 inf = ast_read(s->chan);
516 if (inf == NULL) {
517 ast_debug(1, "Channel hangup\n");
518 res = -1;
519 break;
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);
527 /* Watchdog */
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");
539 res = -1;
540 break;
544 ast_frfree(inf);
545 inf = NULL;
547 /* Watchdog */
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");
550 res = -1;
551 break;
555 ast_debug(1, "Loop finished, res=%d\n", res);
557 if (inf)
558 ast_frfree(inf);
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);
566 return res;
569 static int transmit(fax_session *s)
571 int res = 0;
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);
587 if (res) {
588 ast_log(LOG_WARNING, "Could not answer channel '%s'\n", s->chan->name);
589 return res;
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);
597 if (res > 0) {
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);
610 if (res) {
611 ast_log(LOG_WARNING, "Transmission error\n");
612 res = -1;
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");
619 return res;
622 /* === Application functions === */
624 static int sndfax_exec(struct ast_channel *chan, void *data)
626 int res = 0;
627 char *parse;
628 fax_session session;
630 AST_DECLARE_APP_ARGS(args,
631 AST_APP_ARG(file_name);
632 AST_APP_ARG(options);
635 if (chan == NULL) {
636 ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
637 return -1;
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");
644 return -1;
647 parse = ast_strdupa(data);
648 AST_STANDARD_APP_ARGS(args, parse);
650 session.caller_mode = TRUE;
652 if (args.options) {
653 if (strchr(args.options, 'a'))
654 session.caller_mode = FALSE;
657 /* Done parsing */
658 session.direction = 1;
659 session.file_name = args.file_name;
660 session.chan = chan;
661 session.finished = 0;
663 res = transmit(&session);
665 return res;
668 static int rcvfax_exec(struct ast_channel *chan, void *data)
670 int res = 0;
671 char *parse;
672 fax_session session;
674 AST_DECLARE_APP_ARGS(args,
675 AST_APP_ARG(file_name);
676 AST_APP_ARG(options);
679 if (chan == NULL) {
680 ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
681 return -1;
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");
688 return -1;
691 parse = ast_strdupa(data);
692 AST_STANDARD_APP_ARGS(args, parse);
694 session.caller_mode = FALSE;
696 if (args.options) {
697 if (strchr(args.options, 'c'))
698 session.caller_mode = TRUE;
701 /* Done parsing */
702 session.direction = 0;
703 session.file_name = args.file_name;
704 session.chan = chan;
705 session.finished = 0;
707 res = transmit(&session);
709 return res;
712 static int unload_module(void)
714 int res;
716 res = ast_unregister_application(app_sndfax_name);
717 res |= ast_unregister_application(app_rcvfax_name);
719 return res;
722 static int load_module(void)
724 int res ;
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);
732 return res;
736 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Simple FAX Application",
737 .load = load_module,
738 .unload = unload_module,