Add app_fax from asterisk-addons, with some additional changes to resolve compiler
[asterisk/asterisk.git] / apps / app_fax.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Simple fax applications
5  * 
6  * 2007-2008, Dmitry Andrianov <asterisk@dima.spb.ru>
7  *
8  * Code based on original implementation by Steve Underwood <steveu@coppice.org>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  *
13  */
14
15 /*** MODULEINFO
16          <depend>spandsp</depend>
17 ***/
18  
19 #include "asterisk.h"
20
21 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
22
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>
30
31 #include <spandsp.h>
32
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"
42
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";
67
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";
93
94 #define MAX_SAMPLES 240
95
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 */
101
102 #define WATCHDOG_TOTAL_TIMEOUT  30 * 60
103 #define WATCHDOG_STATE_TIMEOUT  5 * 60
104
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;
111         
112         volatile int finished;
113 } fax_session;
114
115 static void span_message(int level, const char *msg)
116 {
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);
123         }
124 }
125
126 static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uint8_t *buf, int len, int count)
127 {
128         struct ast_channel *chan = (struct ast_channel *) user_data;
129
130         struct ast_frame outf = {
131                 .frametype = AST_FRAME_MODEM,
132                 .subclass = AST_MODEM_T38,
133                 .src = __FUNCTION__,
134         };
135
136         /* TODO: Asterisk does not provide means of resending the same packet multiple
137           times so count is ignored at the moment */
138
139         AST_FRAME_SET_BUFFER(&outf, buf, 0, len);
140
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;
144         }
145
146         return 0;
147 }
148
149 static void phase_e_handler(t30_state_t *f, void *user_data, int result)
150 {
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;
156
157         ast_debug(1, "Fax phase E handler. result=%d\n", result);
158
159         t30_get_transfer_statistics(f, &stat);
160
161         s = (fax_session *) user_data;
162
163         if (result != T30_ERR_OK) {
164                 s->finished = -1;
165
166                 /* FAXSTATUS is already set to FAILED */
167                 pbx_builtin_setvar_helper(s->chan, "FAXERROR", t30_completion_code_to_str(result));
168
169                 ast_log(LOG_WARNING, "Error transmitting fax. result=%d: %s.\n", result, t30_completion_code_to_str(result));
170
171                 return;
172         }
173         
174         s->finished = 1; 
175         
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); 
187         
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);
193         
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);
214 }
215
216 /* === Helper functions to configure fax === */
217
218 /* Setup SPAN logging according to Asterisk debug level */
219 static int set_logging(logging_state_t *state)
220 {
221         int level = SPAN_LOG_WARNING + option_debug;
222
223         span_log_set_message_handler(state, span_message);
224         span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level); 
225
226         return 0;
227 }
228
229 static void set_local_info(t30_state_t *state, fax_session *s)
230 {
231         const char *x;
232
233         x = pbx_builtin_getvar_helper(s->chan, "LOCALSTATIONID");
234         if (!ast_strlen_zero(x))
235                 t30_set_tx_ident(state, x);
236
237         x = pbx_builtin_getvar_helper(s->chan, "LOCALHEADERINFO");
238         if (!ast_strlen_zero(x))
239                 t30_set_tx_page_header_info(state, x);
240 }
241
242 static void set_file(t30_state_t *state, fax_session *s)
243 {
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);
248 }
249
250 static void set_ecm(t30_state_t *state, int ecm)
251 {
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);
254 }
255
256 /* === Generator === */
257
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)
261 {
262         return params;
263 }
264
265 static int fax_generator_generate(struct ast_channel *chan, void *data, int len, int samples)
266 {
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);
270     
271         struct ast_frame outf = {
272                 .frametype = AST_FRAME_VOICE,
273                 .subclass = AST_FORMAT_SLINEAR,
274                 .src = __FUNCTION__,
275         };
276
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;
280         }
281         
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));
285
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;
289                 }
290         }
291
292         return 0;
293 }
294
295 struct ast_generator generator = {
296         alloc:          fax_generator_alloc,
297         generate:       fax_generator_generate,
298 };
299
300
301 /* === Transmission === */
302
303 static int transmit_audio(fax_session *s)
304 {
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;
316
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;
323                 }
324         }
325
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;
332                 }
333         }
334
335         /* Initialize T30 terminal */
336         fax_init(&fax, s->caller_mode);
337
338         /* Setup logging */
339         set_logging(&fax.logging);
340         set_logging(&fax.t30_state.logging);
341
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);
346
347         fax_set_transmit_on_idle(&fax, TRUE);
348
349         t30_set_phase_e_handler(&fax.t30_state, phase_e_handler, s);
350
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;
361         }
362
363         start = state_change = ast_tvnow();
364
365         ast_activate_generator(s->chan, &generator, &fax);
366
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;
373
374                 inf = ast_read(s->chan);
375                 if (inf == NULL) {
376                         ast_debug(1, "Channel hangup\n");
377                         res = -1;
378                         break;
379                 }
380
381                 ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);
382
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);
387
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;
395                         }
396
397                         ast_frfree(fr);
398                 }
399
400
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) {
405
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;
411                         }
412
413                         /* Watchdog */
414                         if (last_state != fax.t30_state.state) {
415                                 state_change = ast_tvnow();
416                                 last_state = fax.t30_state.state;
417                         }
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;
426                         }
427                 }
428
429                 ast_frfree(inf);
430                 inf = NULL;
431
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;
438                 }
439         }
440
441         ast_debug(1, "Loop finished, res=%d\n", res);
442
443         if (inf)
444                 ast_frfree(inf);
445
446         if (dsp)
447                 ast_dsp_free(dsp);
448
449         ast_deactivate_generator(s->chan);
450
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);
454
455         t30_terminate(&fax.t30_state);
456         fax_release(&fax);
457
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);
462         }
463
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);
467         }
468
469         return res;
470
471 }
472
473 static int transmit_t38(fax_session *s)
474 {
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;
481
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;
487         }
488
489         /* Setup logging */
490         set_logging(&t38.logging);
491         set_logging(&t38.t30_state.logging);
492         set_logging(&t38.t38.logging);
493
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);
498
499         t30_set_phase_e_handler(&t38.t30_state, phase_e_handler, s);
500
501         now = start = state_change = ast_tvnow();
502
503         while (!s->finished) {
504
505                 res = ast_waitfor(s->chan, 20);
506                 if (res < 0)
507                         break;
508                 else if (res > 0)
509                         res = 0;
510
511                 last_frame = now;
512                 now = ast_tvnow();
513                 t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000));
514
515                 inf = ast_read(s->chan);
516                 if (inf == NULL) {
517                         ast_debug(1, "Channel hangup\n");
518                         res = -1;
519                         break;
520                 }
521
522                 ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);
523
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);
526
527                         /* Watchdog */
528                         if (last_state != t38.t30_state.state) {
529                                 state_change = ast_tvnow();
530                                 last_state = t38.t30_state.state;
531                         }
532                 } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38 &&
533                                 inf->datalen == sizeof(enum ast_control_t38)) {
534
535                         t38control = *((enum ast_control_t38 *) inf->data.ptr);
536
537                         if (t38control == AST_T38_TERMINATED || t38control == AST_T38_REFUSED) {
538                                 ast_debug(1, "T38 down, terminating\n");
539                                 res = -1;
540                                 break;
541                         }
542                 }
543
544                 ast_frfree(inf);
545                 inf = NULL;
546
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;
552                 }
553         }
554
555         ast_debug(1, "Loop finished, res=%d\n", res);
556
557         if (inf)
558                 ast_frfree(inf);
559
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);
563
564         t30_terminate(&t38.t30_state);
565
566         return res;
567 }
568
569 static int transmit(fax_session *s)
570 {
571         int res = 0;
572
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"); 
577
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); 
582
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;
590                 }
591         }
592
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");
602                         }
603                 }
604         }
605
606         if (s->t38state == T38_STATE_NEGOTIATED) {
607                 res = transmit_t38(s);
608         }
609
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");
617         }
618
619         return res;
620 }
621
622 /* === Application functions === */
623
624 static int sndfax_exec(struct ast_channel *chan, void *data)
625 {
626         int res = 0;
627         char *parse;
628         fax_session session;
629
630         AST_DECLARE_APP_ARGS(args,
631                 AST_APP_ARG(file_name);
632                 AST_APP_ARG(options);
633         );
634
635         if (chan == NULL) {
636                 ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
637                 return -1;
638         }
639
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;
645         }
646
647         parse = ast_strdupa(data);
648         AST_STANDARD_APP_ARGS(args, parse);
649         
650         session.caller_mode = TRUE;
651
652         if (args.options) {
653                 if (strchr(args.options, 'a'))
654                         session.caller_mode = FALSE;
655         }
656
657         /* Done parsing */
658         session.direction = 1;
659         session.file_name = args.file_name;
660         session.chan = chan;
661         session.finished = 0;
662
663         res = transmit(&session);
664
665         return res;
666 }
667
668 static int rcvfax_exec(struct ast_channel *chan, void *data)
669 {
670         int res = 0;
671         char *parse;
672         fax_session session;
673
674         AST_DECLARE_APP_ARGS(args,
675                 AST_APP_ARG(file_name);
676                 AST_APP_ARG(options);
677         );
678
679         if (chan == NULL) {
680                 ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
681                 return -1;
682         }
683
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;
689         }
690
691         parse = ast_strdupa(data);
692         AST_STANDARD_APP_ARGS(args, parse);
693         
694         session.caller_mode = FALSE;
695
696         if (args.options) {
697                 if (strchr(args.options, 'c'))
698                         session.caller_mode = TRUE;
699         }
700
701         /* Done parsing */
702         session.direction = 0;
703         session.file_name = args.file_name;
704         session.chan = chan;
705         session.finished = 0;
706
707         res = transmit(&session);
708
709         return res;
710 }
711
712 static int unload_module(void)
713 {
714         int res;
715
716         res = ast_unregister_application(app_sndfax_name);      
717         res |= ast_unregister_application(app_rcvfax_name);     
718
719         return res;
720 }
721
722 static int load_module(void)
723 {
724         int res ;
725
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);
728
729         /* The default SPAN message handler prints to stderr. It is something we do not want */
730         span_set_message_handler(NULL);
731
732         return res;
733 }
734
735
736 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Simple FAX Application",
737                 .load = load_module,
738                 .unload = unload_module,
739                 );
740
741