/* * Asterisk -- A telephony toolkit for Linux. * * Trivial application to receive a TIFF FAX file * * Copyright (C) 2003, Steve Underwood * * Steve Underwood * * This program is free software, distributed under the terms of * the GNU General Public License */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char *tdesc = "Trivial FAX Receive Application"; static char *app = "RxFAX"; static char *synopsis = "Receive a FAX to a file"; static char *descrip = " RxFAX(filename): Receives a FAX from the channel into a\n" "given filename. If the file exists it will be overwritten. The file\n" "should be in TIFF/F format.\n" "Returns -1 when the user hangs up.\n"; STANDARD_LOCAL_USER; LOCAL_USER_DECL; #define BLOCK_SIZE 160 void t30_flush(t30_state_t *s, int which) { //TODO: } /*- End of function --------------------------------------------------------*/ void phase_e_handler(t30_state_t *s, void *user_data, int result) { struct ast_channel *chan; chan = (struct ast_channel *) user_data; if (result) { ast_log(LOG_DEBUG, "==============================================================================\n"); ast_log(LOG_DEBUG, "Fax successfully received.\n"); ast_log(LOG_DEBUG, "Remote station id: %s\n", s->far_ident); ast_log(LOG_DEBUG, "Local station id: %s\n", s->local_ident); ast_log(LOG_DEBUG, "Pages transferred: %i\n", s->t4.pages_transferred); ast_log(LOG_DEBUG, "Resolution: %i\n", s->t4.resolution); ast_log(LOG_DEBUG, "Transfer Rate: %i\n", s->bit_rate); ast_log(LOG_DEBUG, "==============================================================================\n"); manager_event(EVENT_FLAG_CALL, "FaxReceived", "Channel: %s\nExten: %s\nCallerID: %s\nRemoteStationID: %s\nLocalStationID: %s\nPagesTransferred: %i\nResolution: %i\nTransferRate: %i\nFileName: %s\n", chan->name, chan->exten, chan->callerid, s->far_ident, s->local_ident, s->t4.pages_transferred, s->t4.resolution, s->bit_rate, s->t4.rx_file); } else { ast_log(LOG_DEBUG, "==============================================================================\n"); ast_log(LOG_DEBUG, "Fax receive not successful.\n"); ast_log(LOG_DEBUG, "==============================================================================\n"); } } /*- End of function --------------------------------------------------------*/ static int rxfax_exec(struct ast_channel *chan, void *data) { int res = 0; int count = 0; int percentflag = 0; char fil[256]; char tmp[256]; char *vdata; char *localident; int i; t30_state_t fax; int calling_party; struct localuser *u; struct ast_frame *inf = NULL; struct ast_frame outf; int original_read_fmt; int original_write_fmt; uint8_t __buf[sizeof(uint16_t)*BLOCK_SIZE + 2*AST_FRIENDLY_OFFSET]; uint8_t *buf = __buf + AST_FRIENDLY_OFFSET; int len; extern int errno; if (chan == NULL) { ast_log(LOG_WARNING, "Fax receive channel is NULL. Giving up.\n"); return -1; } vdata = data; /* The next few lines of code parse out the filename and header from the input string */ if (!vdata) { /* No data implies no filename or anything is present */ ast_log(LOG_WARNING, "Rxfax requires an argument (filename)\n"); return -1; } for (i = 0; vdata[i] && (vdata[i] != ':') && (vdata[i] != '|'); i++) { if ((vdata[i] == '%') && (vdata[i + 1] == 'd')) percentflag = 1; /* the wildcard is used */ if (i == strlen(vdata)) { ast_log(LOG_WARNING, "No extension found\n"); return -1; } fil[i] = vdata[i]; } fil[i] = '\0'; calling_party = FALSE; if (vdata[i] == '|') { i++; if (strcmp(vdata + 1, "caller") == 0) calling_party = TRUE; } /* Done parsing */ /* These are to allow the use of the %d in the config file for a wild card of sort to create a new file with the inputed name scheme */ if (percentflag) { do { snprintf(tmp, 256, fil, count); count++; } while (ast_fileexists(tmp, "", chan->language) != -1); } else { strncpy(tmp, fil, 256 - 1); } LOCAL_USER_ADD(u); if (chan->_state != AST_STATE_UP) { /* Shouldn't need this, but checking to see if channel is already answered * Theoretically asterisk should already have answered before running the app */ res = ast_answer(chan); } if (!res) { original_read_fmt = chan->readformat; if (original_read_fmt != AST_FORMAT_SLINEAR) { res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); if (res < 0) { ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n"); return -1; } } original_write_fmt = chan->writeformat; if (original_write_fmt != AST_FORMAT_SLINEAR) { res = ast_set_write_format(chan, AST_FORMAT_SLINEAR); if (res < 0) { ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n"); res = ast_set_read_format(chan, original_read_fmt); if (res) ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); return -1; } } fax_init(&fax, calling_party, NULL); localident = pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"); fax_set_local_ident(&fax, localident); fax_set_rx_file(&fax, tmp); //fax_set_phase_b_handler(&fax, phase_b_handler, chan); //fax_set_phase_d_handler(&fax, phase_d_handler, chan); fax_set_phase_e_handler(&fax, phase_e_handler, chan); while (ast_waitfor(chan, -1) > -1) { inf = ast_read(chan); if (inf == NULL) { res = -1; break; } if (inf->frametype == AST_FRAME_VOICE) { if (fax_rx_process(&fax, inf->data, inf->samples)) break; len = fax_tx_process(&fax, (int16_t *) &buf[AST_FRIENDLY_OFFSET], inf->samples); if (len) { memset(&outf, 0, sizeof(outf)); outf.frametype = AST_FRAME_VOICE; outf.subclass = AST_FORMAT_SLINEAR; outf.datalen = len*sizeof(int16_t); outf.samples = len; outf.data = &buf[AST_FRIENDLY_OFFSET]; outf.offset = AST_FRIENDLY_OFFSET; if (ast_write(chan, &outf) < 0) { ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno)); break; } } } ast_frfree(inf); } if (inf == NULL) { ast_log(LOG_DEBUG, "Got hangup\n"); res = -1; } if (original_read_fmt != AST_FORMAT_SLINEAR) { res = ast_set_read_format(chan, original_read_fmt); if (res) ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); } if (original_write_fmt != AST_FORMAT_SLINEAR) { res = ast_set_write_format(chan, original_write_fmt); if (res) ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", chan->name); } } else { ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name); } LOCAL_USER_REMOVE(u); return res; } int unload_module(void) { STANDARD_HANGUP_LOCALUSERS; return ast_unregister_application(app); } int load_module(void) { return ast_register_application(app, rxfax_exec, synopsis, descrip); } char *description(void) { return tdesc; } int usecount(void) { int res; STANDARD_USECOUNT(res); return res; } char *key() { return ASTERISK_GPL_KEY; }