]> git.pld-linux.org Git - packages/asterisk.git/blob - app_txfax.c
disable h323
[packages/asterisk.git] / app_txfax.c
1 /*
2  * Application to send a TIFF file as a FAX
3  * based on app_txfax.c from: Copyright (C) 2003, Steve Underwood <steveu@coppice.org>
4  * PATCHED BY (C) 20007 by Antonio Gallo <agx@linux.it>
5  * - added ECM support
6  * - added more env variables
7  * - added logging to external file
8  */
9
10 /*** MODULEINFO
11 Depends: libspandsp
12 Desciption: Send a FAX file
13 DisplayName: TxFAX
14  ***/
15  
16 #include "asterisk.h"
17
18 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
19
20 #include <string.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <inttypes.h>
24 #include <pthread.h>
25 #include <errno.h>
26 #include <tiffio.h>
27
28 #include <spandsp.h>
29 #include <spandsp/version.h>
30
31 #include "asterisk/lock.h"
32 #include "asterisk/file.h"
33 #include "asterisk/logger.h"
34 #include "asterisk/channel.h"
35 #include "asterisk/pbx.h"
36 #include "asterisk/module.h"
37 #include "asterisk/manager.h"
38
39 #ifndef AST_MODULE
40 #define AST_MODULE "app_txfax"
41 #endif
42
43 static char *app = "TxFAX";
44
45 static char *synopsis = "Send a FAX file";
46
47 static char *descrip = 
48         "  TxFAX(filename[|caller][|debug][|ecm]):  Send a given TIFF file to the channel as a FAX.\n"
49         "The \"caller\" option makes the application behave as a calling machine,\n"
50         "rather than the answering machine. The default behaviour is to behave as\n"
51         "an answering machine.\n"
52         "The \"ecm\" option enables ECM.\n"
53         "Uses LOCALSTATIONID to identify itself to the remote end.\n"
54         "     LOCALHEADERINFO to generate a header line on each page.\n"
55         "Sets REMOTESTATIONID to the receiver CSID.\n"
56         "     FAXPAGES to the number of pages sent.\n"
57         "     FAXBITRATE to the transmition rate.\n"
58         "     FAXRESOLUTION to the resolution.\n"
59         "     PHASEESTATUS to the phase E result status.\n"
60         "     PHASEESTRING to the phase E result string.\n"
61         "Returns -1 when the user hangs up, or if the file does not exist.\n"
62         "Returns 0 otherwise.\n";
63
64 #define MAX_BLOCK_SIZE 240
65
66 static FILE *txfax_logfile = NULL;
67
68 static void file_log(const char *msg)
69 {
70         if (msg==NULL) 
71                 return;
72         if (txfax_logfile==NULL)
73                 return;
74         fprintf(txfax_logfile, msg);
75 }
76
77 static void span_message(int level, const char *msg)
78 {
79         if (msg==NULL) return;
80         int ast_level;
81         if (level == SPAN_LOG_ERROR)
82                 ast_level = __LOG_ERROR;
83         else if (level == SPAN_LOG_WARNING)
84                 ast_level = __LOG_WARNING;
85         else
86                 ast_level = __LOG_DEBUG;
87         ast_log(ast_level, _A_, "%s", msg);
88         file_log(msg);
89 }
90 /*- End of function --------------------------------------------------------*/
91
92 typedef struct {
93         struct ast_channel *chan;
94         fax_state_t fax;
95         volatile int finished;
96 } fax_session;
97
98 static void phase_b_handler(t30_state_t *s, void *user_data, int result)
99 {
100         if (txfax_logfile!=NULL) {
101                 fprintf( txfax_logfile, "[phase_b_handler] mark\n" );
102                 fflush(txfax_logfile);
103         }
104 }
105
106 static void phase_e_handler(t30_state_t *s, void *user_data, int result)
107 {
108         struct ast_channel *chan;
109         char local_ident[21];
110         char far_ident[21];
111         char buf[128];
112         t30_stats_t t;
113
114         fax_session *fax = (fax_session *) user_data;
115         chan = fax->chan;
116         t30_get_transfer_statistics(s, &t);
117
118         t30_get_local_ident(s, local_ident);
119         t30_get_far_ident(s, far_ident);
120         pbx_builtin_setvar_helper(chan, "REMOTESTATIONID", far_ident);
121         snprintf(buf, sizeof(buf), "%d", t.pages_transferred);
122         pbx_builtin_setvar_helper(chan, "FAXPAGES", buf);
123         snprintf(buf, sizeof(buf), "%d", t.y_resolution);
124         pbx_builtin_setvar_helper(chan, "FAXRESOLUTION", buf);
125         snprintf(buf, sizeof(buf), "%d", t.bit_rate);
126         pbx_builtin_setvar_helper(chan, "FAXBITRATE", buf);
127         snprintf(buf, sizeof(buf), "%d", result);
128         pbx_builtin_setvar_helper(chan, "PHASEESTATUS", buf);
129         snprintf(buf, sizeof(buf), "%s", t30_completion_code_to_str(result));
130         pbx_builtin_setvar_helper(chan, "PHASEESTRING", buf);
131
132         // This is to tell asterisk later that the fax has finished (with or without error)
133         fax->finished = 1; 
134
135         ast_log(LOG_DEBUG, "==============================================================================\n");
136         if (result == T30_ERR_OK)
137         {
138                 ast_log(LOG_DEBUG, "Fax successfully sent.\n");
139                 ast_log(LOG_DEBUG, "Remote station id: %s\n", far_ident);
140                 ast_log(LOG_DEBUG, "Local station id:  %s\n", local_ident);
141                 ast_log(LOG_DEBUG, "Pages transferred: %i\n", t.pages_transferred);
142                 ast_log(LOG_DEBUG, "Image resolution:  %i x %i\n", t.x_resolution, t.y_resolution);
143                 ast_log(LOG_DEBUG, "Transfer Rate:     %i\n", t.bit_rate);
144                 manager_event(EVENT_FLAG_CALL,
145                                 "FaxSent", "Channel: %s\nExten: %s\nCallerID: %s\nRemoteStationID: %s\nLocalStationID: %s\nPagesTransferred: %i\nResolution: %i\nTransferRate: %i\nFileName: %s\n",
146                                 chan->name,
147                                 chan->exten,
148                                 (chan->cid.cid_num)  ?  chan->cid.cid_num  :  "",
149                                 far_ident,
150                                 local_ident,
151                                 t.pages_transferred,
152                                 t.y_resolution,
153                                 t.bit_rate,
154                                 s->rx_file);
155                 if (txfax_logfile!=NULL) {
156                         fprintf( txfax_logfile, "\n[FAX OK] Remote: %s Local: %s Pages: %i Speed: %i\n\n", 
157                                 far_ident, local_ident, t.pages_transferred, t.bit_rate
158                         );
159                         fflush(txfax_logfile);
160                 }
161         }
162         else
163         {
164                 ast_log(LOG_DEBUG, "Fax send not successful - result (%d) %s.\n", result, t30_completion_code_to_str(result));
165                 if (txfax_logfile!=NULL) {
166                         fprintf( txfax_logfile, "\n[FAX ERROR] code: %d %s\n\n", result, t30_completion_code_to_str(result) );
167                         fflush(txfax_logfile);
168                 }
169         }
170         ast_log(LOG_DEBUG, "==============================================================================\n");
171 }
172 /*- End of function --------------------------------------------------------*/
173
174 static void phase_d_handler(t30_state_t *s, void *user_data, int result)
175 {
176 //    struct ast_channel *chan;
177         t30_stats_t t;
178
179 //    chan = (struct ast_channel *) user_data;
180         if (result)
181         {
182                 t30_get_transfer_statistics(s, &t);
183                 ast_log(LOG_DEBUG, "[ TXFAX ]=====================================================================\n");
184                 ast_log(LOG_DEBUG, "Pages transferred:  %i\n", t.pages_transferred);
185                 ast_log(LOG_DEBUG, "Image size:         %i x %i\n", t.width, t.length);
186                 ast_log(LOG_DEBUG, "Image resolution    %i x %i\n", t.x_resolution, t.y_resolution);
187                 ast_log(LOG_DEBUG, "Transfer Rate:      %i\n", t.bit_rate);
188                 ast_log(LOG_DEBUG, "Bad rows            %i\n", t.bad_rows);
189                 ast_log(LOG_DEBUG, "Longest bad row run %i\n", t.longest_bad_row_run);
190                 ast_log(LOG_DEBUG, "Compression type    %s\n", t4_encoding_to_str(t.encoding));
191                 ast_log(LOG_DEBUG, "Image size (bytes)  %i\n", t.image_size);
192                 ast_log(LOG_DEBUG, "==============================================================================\n");
193                 if (txfax_logfile!=NULL) {
194                         fprintf( txfax_logfile, "\n[phase_d_handler] Page: %i at %i\n\n",  t.pages_transferred, t.bit_rate );
195                         fflush(txfax_logfile);
196                 }
197         }
198 }
199 /*- End of function --------------------------------------------------------*/
200
201 static int txfax_exec(struct ast_channel *chan, void *data)
202 {
203         int res = 0;
204         char source_file[256];
205         int samples;
206         char *s;
207         char *t;
208         char *v;
209         const char *x;
210         int option;
211         int len;
212         fax_state_t fax;
213         struct ast_frame *inf = NULL;
214         struct ast_frame outf;
215         int calling_party;
216         int verbose;
217         int ecm = FALSE;
218
219         struct ast_module_user *u;
220
221         int original_read_fmt;
222         int original_write_fmt;
223
224         fax_session session;
225         session.chan = chan;
226         session.finished = 0;
227
228         /* Basic initial checkings */
229
230         if (chan == NULL) {
231                 ast_log(LOG_WARNING, "Fax transmit channel is NULL. Giving up.\n");
232                 file_log("ERROR: Fax receive channel is NULL. Giving up.\n");
233                 return -1;
234         }
235
236         span_set_message_handler(span_message);
237         /* make sure they are initialized to zero */
238         memset( &fax, 0, sizeof(fax));
239
240         /* Resetting channel variables related to T38 */
241         pbx_builtin_setvar_helper(chan, "REMOTESTATIONID", "");
242         pbx_builtin_setvar_helper(chan, "FAXPAGES", "");
243         pbx_builtin_setvar_helper(chan, "FAXRESOLUTION", "");
244         pbx_builtin_setvar_helper(chan, "FAXBITRATE", "");
245         pbx_builtin_setvar_helper(chan, "PHASEESTATUS", "");
246         pbx_builtin_setvar_helper(chan, "PHASEESTRING", "");
247
248         /* Parsig parameters */
249
250         /* The next few lines of code parse out the filename and header from the input string */
251         if (data == NULL)
252         {
253                 /* No data implies no filename or anything is present */
254                 ast_log(LOG_WARNING, "Txfax requires an argument (filename)\n");
255                 file_log("ERROR: Txfax requires an argument (filename)\n");
256                 return -1;
257         }
258
259         calling_party = FALSE;
260         verbose = FALSE;
261         source_file[0] = '\0'; 
262
263         char tbuf[256];
264         for (option = 0, v = s = data;  v;  option++, s++) {
265                 t = s;
266                 v = strchr(s, '|');
267                 s = (v)  ?  v  :  s + strlen(s);
268                 strncpy((char *) tbuf, t, s - t);
269                 tbuf[s - t] = '\0';
270                 if (option == 0) {
271                         /* The first option is always the file name */
272                         len = s - t;
273                         if (len > 255)
274                                 len = 255;
275                         strncpy(source_file, t, len);
276                         source_file[len] = '\0';
277                 } else if (strncmp("caller", t, s - t) == 0) {
278                         calling_party = TRUE;
279                 } else if (strncmp("debug", t, s - t) == 0) {
280                         verbose = TRUE;
281                 } else if (strncmp("ecm", t, s - t) == 0) {
282                         ecm = TRUE;
283                 }
284         }
285
286         /* Done parsing */
287
288         u = ast_module_user_add(chan);
289
290         if (chan->_state != AST_STATE_UP)
291         {
292                 /* Shouldn't need this, but checking to see if channel is already answered
293                  * Theoretically asterisk should already have answered before running the app */
294                 res = ast_answer(chan);
295                 /* NO NEED TO WARN ANYMORE 
296                 if (!res)
297                 {
298                         ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
299                         file_log("Could not answer channel\n" );
300                 }
301                 */
302         }
303
304         /* Setting read and write formats */
305
306         original_read_fmt = chan->readformat;
307         if (original_read_fmt != AST_FORMAT_SLINEAR)
308         {
309                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
310                 if (res < 0)
311                 {
312                         ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n");
313                         file_log("ERROR: Unable to set to linear read mode, giving up\n");
314                         ast_module_user_remove(u);
315                         return -1;
316                 }
317         }
318
319         original_write_fmt = chan->writeformat;
320         if (original_write_fmt != AST_FORMAT_SLINEAR)
321         {
322                 res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
323                 if (res < 0)
324                 {
325                         ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n");
326                         file_log("Unable to set to linear write mode, giving up\n");
327                         res = ast_set_read_format(chan, original_read_fmt);
328                         if (res)
329                                 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
330                         ast_module_user_remove(u);
331                         return -1;
332                 }
333         }
334
335         /* Remove any app level gain adjustments and disable echo cancel. */
336         signed char sc;
337         sc = 0;
338         ast_channel_setoption(chan, AST_OPTION_RXGAIN, &sc, sizeof(sc), 0);
339         ast_channel_setoption(chan, AST_OPTION_TXGAIN, &sc, sizeof(sc), 0);
340         ast_channel_setoption(chan, AST_OPTION_ECHOCAN, &sc, sizeof(sc), 0);
341
342         /* This is the main loop */
343
344         uint8_t __buf[sizeof(uint16_t)*MAX_BLOCK_SIZE + 2*AST_FRIENDLY_OFFSET];
345         uint8_t *buf = __buf + AST_FRIENDLY_OFFSET;
346
347         memset(&fax, 0, sizeof(fax));
348
349         if (fax_init(&fax, calling_party) == NULL)
350         {
351                 ast_log(LOG_WARNING, "Unable to start FAX\n");
352                 file_log("Unable to set to start fax_init\n");
353                 ast_module_user_remove(u);
354                 return -1;
355         }
356         fax_set_transmit_on_idle(&fax, TRUE);
357         span_log_set_message_handler(&fax.logging, span_message);
358         span_log_set_message_handler(&fax.t30_state.logging, span_message);
359         if (verbose)
360         {
361                 span_log_set_level(&fax.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
362                 span_log_set_level(&fax.t30_state.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
363         }
364         x = pbx_builtin_getvar_helper(chan, "LOCALSTATIONID");
365         if (x  &&  x[0])
366                 t30_set_local_ident(&fax.t30_state, x);
367         x = pbx_builtin_getvar_helper(chan, "LOCALHEADERINFO");
368         if (x  &&  x[0])
369                 t30_set_header_info(&fax.t30_state, x);
370         t30_set_tx_file(&fax.t30_state, source_file, -1, -1);
371         t30_set_phase_b_handler(&fax.t30_state, phase_b_handler, chan);
372         t30_set_phase_d_handler(&fax.t30_state, phase_d_handler, chan);
373         t30_set_phase_e_handler(&fax.t30_state, phase_e_handler, &session);
374
375         x = pbx_builtin_getvar_helper(chan, "FAX_DISABLE_V17");
376         if (x  &&  x[0])
377                 t30_set_supported_modems(&(fax.t30_state), T30_SUPPORT_V29 | T30_SUPPORT_V27TER);
378         else
379                 t30_set_supported_modems(&(fax.t30_state), T30_SUPPORT_V29 | T30_SUPPORT_V27TER | T30_SUPPORT_V17 );
380
381         /* Support for different image sizes && resolutions*/
382         t30_set_supported_image_sizes(&fax.t30_state, T30_SUPPORT_US_LETTER_LENGTH | T30_SUPPORT_US_LEGAL_LENGTH | T30_SUPPORT_UNLIMITED_LENGTH
383                         | T30_SUPPORT_215MM_WIDTH | T30_SUPPORT_255MM_WIDTH | T30_SUPPORT_303MM_WIDTH);
384         t30_set_supported_resolutions(&fax.t30_state, T30_SUPPORT_STANDARD_RESOLUTION | T30_SUPPORT_FINE_RESOLUTION | T30_SUPPORT_SUPERFINE_RESOLUTION
385                         | T30_SUPPORT_R8_RESOLUTION | T30_SUPPORT_R16_RESOLUTION);
386         if (ecm) {
387                 t30_set_ecm_capability(&(fax.t30_state), TRUE);
388                 t30_set_supported_compressions(&(fax.t30_state), T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
389                 ast_log(LOG_DEBUG, "Enabling ECM mode for app_txfax\n"  );
390                 file_log("Enabling ECM mode for app_rxfax\n"  );
391         }
392
393
394         /* This is the main loop */
395
396         res = 0;
397         while ( (!session.finished) && chan )
398         {
399                 if (ast_check_hangup(chan)) {
400                         ast_log(LOG_WARNING, "TXFAX: Channel has been hanged at fax.\n");
401                         file_log("INFO: Channel has been hanged at fax.\n");
402                         res = 0;
403                         break;
404                 }
405
406                 if ((res = ast_waitfor(chan, 20)) < 0) {
407                         ast_log(LOG_WARNING, "TXFAX: ast_waitfor returned less then 0.\n");
408                         file_log("WARNING: Channel ast_waitfor < 0.\n");
409                         res = 0;
410                         break;
411                 }
412
413                 /*if ((fax.current_rx_type == T30_MODEM_DONE)  ||  (fax.current_tx_type == T30_MODEM_DONE)) {
414                         ast_log(LOG_WARNING, "Channel T30 DONE < 0.\n");
415                         file_log("Channel T30 DONE.\n");
416                         res = 0;
417                         break;
418                 }*/
419
420                 inf = ast_read(chan);
421                 if (inf == NULL)
422                 {
423                         //ast_log(LOG_WARNING, "TXFAX: transmission done with ast_read(chan) == NULL\n");
424                         ast_log(LOG_WARNING, "Channel INF is NULL.\n");
425                         file_log("DEBUG: Channel INF is NULL.\n");
426
427                         // While trasmiitting i got: Received a DCN from remote after sending a page
428                         // at last page
429                         continue;
430 #ifdef AGX_DEBUGGING
431                         res = 0;
432                         break;
433 #endif
434                 }
435
436                 /* We got a frame */
437                 //if (inf->frametype == AST_FRAME_VOICE) {
438                 /* Check the frame type. Format also must be checked because there is a chance
439                    that a frame in old format was already queued before we set chanel format
440                    to slinear so it will still be received by ast_read */
441                 if (inf->frametype == AST_FRAME_VOICE && inf->subclass == AST_FORMAT_SLINEAR) {
442                         if (fax_rx(&fax, inf->data, inf->samples)) {
443                                 ast_log(LOG_WARNING, "TXFAX: fax_rx returned error\n");
444                                 res = -1;
445                                 break;
446                         }
447
448                         samples = (inf->samples <= MAX_BLOCK_SIZE) ? inf->samples : MAX_BLOCK_SIZE;
449                         len = fax_tx(&fax, (int16_t *) &buf[AST_FRIENDLY_OFFSET], samples);
450                         if (len>0) {
451                                 /*if (len <= 0) {
452                                         ast_log(LOG_WARNING, "len <=0 using samples.\n");
453                                         file_log("len <= 0 using samples.\n");
454                                         len = samples;
455                                 }*/
456                                 memset(&outf, 0, sizeof(outf));
457                                 outf.frametype = AST_FRAME_VOICE;
458                                 outf.subclass = AST_FORMAT_SLINEAR;
459                                 outf.datalen = len*sizeof(int16_t);
460                                 outf.samples = len;
461                                 outf.data = &buf[AST_FRIENDLY_OFFSET];
462                                 outf.offset = AST_FRIENDLY_OFFSET;
463                                 outf.src = "TxFAX";
464                                 /*if (len <= 0) {
465                                         memset(&buf[AST_FRIENDLY_OFFSET], 0, outf.datalen);
466                                 }*/
467                                 if (ast_write(chan, &outf) < 0)
468                                 {
469                                         ast_log(LOG_WARNING, "TXFAX: Unable to write frame to channel; %s\n", strerror(errno));
470                                         file_log("FATAL ERROR: Unable to write frame to channel\n");
471                                         res = -1;
472                                         break;
473                                 }
474                         }
475                 }
476                 ast_frfree(inf);
477                 inf = NULL;
478                 /* TODO put a Watchdog here */
479         }
480
481         if (inf != NULL)
482         {
483                 ast_frfree(inf);
484                 inf = NULL;
485         }
486
487         t30_terminate(&fax.t30_state);
488         fax_release(&fax);
489         if (session.finished > 0) {
490                 ast_log(LOG_WARNING, "Fax Transmission complete, check return code\n");
491                 res = 0;
492         } else {
493                 ast_log(LOG_WARNING, "Fax Transmission INCOMPLETE, check error code\n");
494                 res = -1;
495         }
496         if (res!=0) {
497                 ast_log(LOG_WARNING, "Transmission RES error\n");
498         }
499
500         /* Restoring initial channel formats. */
501
502         if (original_read_fmt != AST_FORMAT_SLINEAR)
503         {
504                 res = ast_set_read_format(chan, original_read_fmt);
505                 if (res)
506                         ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
507         }
508         if (original_write_fmt != AST_FORMAT_SLINEAR)
509         {
510                 res = ast_set_write_format(chan, original_write_fmt);
511                 if (res)
512                         ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", chan->name);
513         }
514         ast_module_user_remove(u);
515         return res;
516 }
517 /*- End of function --------------------------------------------------------*/
518
519 static int unload_module(void)
520 {
521         int res;
522         ast_module_user_hangup_all();
523         res = ast_unregister_application(app);  
524         if (txfax_logfile) {
525                 fclose(txfax_logfile);
526                 txfax_logfile = NULL;
527         }
528         return res;
529 }
530 /*- End of function --------------------------------------------------------*/
531
532 static int load_module(void)
533 {
534         ast_log(LOG_NOTICE, "TxFax using spandsp %i %i\n", SPANDSP_RELEASE_DATE, SPANDSP_RELEASE_TIME );
535         txfax_logfile = fopen("/var/log/txfax.log", "w+" );
536         if (txfax_logfile)
537                 ast_log(LOG_WARNING, "TxFax output also available in /var/log/txfax.log\n" );
538         return ast_register_application(app, txfax_exec, synopsis, descrip);
539 }
540 /*- End of function --------------------------------------------------------*/
541
542 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Trivial FAX Transmit Application");
543
544 /*- End of file ------------------------------------------------------------*/
This page took 0.132034 seconds and 3 git commands to generate.