]> git.pld-linux.org Git - packages/asterisk.git/blame - app_txfax.c
noarch 190MiB apidocs subpackage
[packages/asterisk.git] / app_txfax.c
CommitLineData
d7e66fb6 1/*
b568f055
CM
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
d7e66fb6 8 */
b568f055
CM
9
10/*** MODULEINFO
11Depends: libspandsp
12Desciption: Send a FAX file
13DisplayName: TxFAX
14 ***/
d7e66fb6 15
b568f055
CM
16#include "asterisk.h"
17
18ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
19
d7e66fb6 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>
b568f055 29#include <spandsp/version.h>
d7e66fb6 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"
b568f055 37#include "asterisk/manager.h"
d7e66fb6 38
b568f055
CM
39#ifndef AST_MODULE
40#define AST_MODULE "app_txfax"
41#endif
d7e66fb6 42
43static char *app = "TxFAX";
44
45static char *synopsis = "Send a FAX file";
46
47static char *descrip =
b568f055
CM
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";
d7e66fb6 63
b568f055 64#define MAX_BLOCK_SIZE 240
d7e66fb6 65
b568f055 66static FILE *txfax_logfile = NULL;
d7e66fb6 67
b568f055
CM
68static 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}
d7e66fb6 76
77static void span_message(int level, const char *msg)
78{
b568f055
CM
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);
d7e66fb6 89}
90/*- End of function --------------------------------------------------------*/
91
b568f055
CM
92typedef struct {
93 struct ast_channel *chan;
94 fax_state_t fax;
95 volatile int finished;
96} fax_session;
97
98static void phase_b_handler(t30_state_t *s, void *user_data, int result)
d7e66fb6 99{
b568f055
CM
100 if (txfax_logfile!=NULL) {
101 fprintf( txfax_logfile, "[phase_b_handler] mark\n" );
102 fflush(txfax_logfile);
103 }
d7e66fb6 104}
d7e66fb6 105
106static void phase_e_handler(t30_state_t *s, void *user_data, int result)
107{
b568f055
CM
108 struct ast_channel *chan;
109 char local_ident[21];
110 char far_ident[21];
111 char buf[128];
112 t30_stats_t t;
d7e66fb6 113
b568f055
CM
114 fax_session *fax = (fax_session *) user_data;
115 chan = fax->chan;
116 t30_get_transfer_statistics(s, &t);
d7e66fb6 117
b568f055
CM
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");
d7e66fb6 171}
172/*- End of function --------------------------------------------------------*/
173
b568f055 174static void phase_d_handler(t30_state_t *s, void *user_data, int result)
d7e66fb6 175{
b568f055
CM
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 }
d7e66fb6 198}
199/*- End of function --------------------------------------------------------*/
200
b568f055 201static int txfax_exec(struct ast_channel *chan, void *data)
d7e66fb6 202{
b568f055
CM
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;
d7e66fb6 516}
517/*- End of function --------------------------------------------------------*/
518
b568f055 519static int unload_module(void)
d7e66fb6 520{
b568f055
CM
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;
d7e66fb6 529}
530/*- End of function --------------------------------------------------------*/
531
b568f055 532static int load_module(void)
d7e66fb6 533{
b568f055
CM
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);
d7e66fb6 539}
540/*- End of function --------------------------------------------------------*/
b568f055
CM
541
542AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Trivial FAX Transmit Application");
543
d7e66fb6 544/*- End of file ------------------------------------------------------------*/
This page took 0.214884 seconds and 4 git commands to generate.