Andrew Yager
2005-Jan-22 20:43 UTC
[Asterisk-Users] chan_capi patch: app_capiFax modifications
Hi, Since Carl has kindly provided us with fax support for CAPI based cards, we have been using it with much success. Today I have modified app_capiFax so that it now supports a dynamic CSID. The following example uses the DNID created by chan_capi on an AVM Fritz! card. * Receive a fax with CAPI API. * Usage : capiAnswerFax2(path_output_file.SFF|stationID) * * This function can be called even after a regular Answer (voice mode), * the channel will be changed to Fax Mode. * * Example of use : * line number 123, play something, if a fax tone is detected, handle it * line number 124, answer directly in fax mode * * [incoming] * exten => 123,1,Answer() * exten => 123,2,BackGround(jpop) * exten => 124,1,Goto(handle_fax,s,1) * exten => fax,1,Goto(handle_fax,s,1) * * [handle_fax] * exten => s,1,capiAnswerFax2(/tmp/${UNIQUEID}|1234567890) // Answer fax with CSID 1234567890 * exten => s,2,Hangup() * exten => h,1,deadagi,fax.php // Run SFF2TIFF and mail it. * Please note that this can only be used with the capi fax patch available at http://www.mlkj.net/asterisk/chan_capi-0.3.5-patch.tar.bz2 . You will also need to modify the Makefile and add app_capiFax2.so to the object list. Andrew _________________________ Andrew Yager Real World Technology Solutions Real People, Real SolUtions (tm) ph: (02) 9563 4840 fax: (02) 9563 4848 mob: 0405 15 2568 http://www.rwts.com.au/ _________________________ -------------- next part -------------- /* * (CAPI*) * * Receive a fax with CAPI API. * Usage : capiAnswerFax2(path_output_file.SFF,stationID) * * This function can be called even after a regular Answer (voice mode), * the channel will be changed to Fax Mode. * * Example of use : * line number 123, play something, if a fax tone is detected, handle it * line number 124, answer directly in fax mode * * [incoming] * exten => 123,1,Answer() * exten => 123,2,BackGround(jpop) * exten => 124,1,Goto(handle_fax,s,1) * exten => fax,1,Goto(handle_fax,s,1) * * [handle_fax] * exten => s,1,capiAnswerFax(/tmp/${UNIQUEID},${DNID}) * exten => s,2,Hangup() * exten => h,1,deadagi,fax.php // Run SFF2TIFF and mail it. * * An implementation of Common ISDN API 2.0 for Asterisk * * This program is free software and may be modified and * distributed under the terms of the GNU Public License. */ #include <asterisk/file.h> #include <asterisk/logger.h> #include <asterisk/channel.h> #include <asterisk/channel_pvt.h> #include <asterisk/logger.h> #include <asterisk/options.h> #include <asterisk/pbx.h> #include <asterisk/module.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <pthread.h> #include <linux/capi.h> #include <capi20.h> #include "chan_capi_pvt.h" #include "chan_capi_app.h" /* FAX Resolutions */ #define FAX_STANDARD_RESOLUTION 0 #define FAX_HIGH_RESOLUTION 1 /* FAX Formats */ #define FAX_SFF_FORMAT 0 #define FAX_PLAIN_FORMAT 1 #define FAX_PCX_FORMAT 2 #define FAX_DCX_FORMAT 3 #define FAX_TIFF_FORMAT 4 #define FAX_ASCII_FORMAT 5 #define FAX_EXTENDED_ASCII_FORMAT 6 #define FAX_BINARY_FILE_TRANSFER_FORMAT 7 typedef struct fax3proto3 { unsigned char len; unsigned short resolution __attribute__ ((packed)); unsigned short format __attribute__ ((packed)); unsigned char Infos[100] __attribute__ ((packed)); } B3_PROTO_FAXG3; static char *tdesc = "(CAPI*) Receive Faxes."; static char *app = "capiAnswerFax2"; static char *synopsis = "Answer Fax with CAPI (Allow Station ID Setting)"; STANDARD_LOCAL_USER; LOCAL_USER_DECL; void SetupB3Config(B3_PROTO_FAXG3 *B3conf, int FAX_Format, char* stationID) { int len1; int len2; char *headLine = "CAPI FAXServer"; B3conf->resolution = FAX_HIGH_RESOLUTION; B3conf->format = (unsigned short)FAX_Format; len1 = strlen(stationID); B3conf->Infos[0] = (unsigned char)len1; strcpy((char *)&B3conf->Infos[1], stationID); len2 = strlen(headLine); B3conf->Infos[len1 + 1] = (unsigned char)len2; strcpy((char *)&B3conf->Infos[len1 + 2], headLine); B3conf->len = (unsigned char)(2 * sizeof(unsigned short) + len1 + len2 + 2); } static int capi_change_bchan_fax(struct ast_channel *c, char* stationID) { struct ast_capi_pvt *i = c->pvt->pvt; MESSAGE_EXCHANGE_ERROR error; _cmsg CMSG; B3_PROTO_FAXG3 B3conf; SetupB3Config(&B3conf, FAX_SFF_FORMAT, stationID); // Format ignored by eicon cards DISCONNECT_B3_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); DISCONNECT_B3_REQ_NCCI(&CMSG) = i->NCCI; if ((error = _capi_put_cmsg(&CMSG)) != 0) { ast_log(LOG_ERROR, "error sending DISCONNECT_B3_REQ NCCI=%#x\n",i->NCCI); } else { if (option_verbose > 5) { ast_verbose(VERBOSE_PREFIX_4 "sent DISCONNECT_B3_REQ NCCI=%#x\n",i->NCCI); } } // wait for the B3 layer to go down while (i->state != CAPI_STATE_CONNECTED) { usleep(10000); } SELECT_B_PROTOCOL_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); SELECT_B_PROTOCOL_REQ_PLCI(&CMSG) = i->PLCI; SELECT_B_PROTOCOL_REQ_B1PROTOCOL(&CMSG) = 4; SELECT_B_PROTOCOL_REQ_B2PROTOCOL(&CMSG) = 4; SELECT_B_PROTOCOL_REQ_B3PROTOCOL(&CMSG) = 4; SELECT_B_PROTOCOL_REQ_B1CONFIGURATION(&CMSG) = NULL; SELECT_B_PROTOCOL_REQ_B2CONFIGURATION(&CMSG) = NULL; SELECT_B_PROTOCOL_REQ_B3CONFIGURATION(&CMSG) = (_cstruct)&B3conf; if ((error = _capi_put_cmsg(&CMSG)) != 0) ast_log(LOG_WARNING, "capiAnswerFax CAPI SELECT_B_PROTOCOL put_cmsg error\n"); return 0; } static int capi_answer_fax(struct ast_channel *c, char* stationID) { struct ast_capi_pvt *i = c->pvt->pvt; MESSAGE_EXCHANGE_ERROR error; _cmsg CMSG; char buf[AST_MAX_EXTENSION]; char *dnid; B3_PROTO_FAXG3 B3conf; if (i->isdnmode && (strlen(i->incomingmsn)<strlen(i->dnid))) dnid = i->dnid + strlen(i->incomingmsn); else dnid = i->dnid; SetupB3Config(&B3conf, FAX_SFF_FORMAT, stationID); // Format ignored by eicon cards CONNECT_RESP_HEADER(&CMSG, ast_capi_ApplID, i->MessageNumber, 0); CONNECT_RESP_PLCI(&CMSG) = i->PLCI; CONNECT_RESP_REJECT(&CMSG) = 0; buf[0] = strlen(dnid)+2; buf[1] = 0x0; buf[2] = 0x80; strncpy(&buf[3],dnid,sizeof(buf)-4); CONNECT_RESP_CONNECTEDNUMBER(&CMSG) = buf; CONNECT_RESP_CONNECTEDSUBADDRESS(&CMSG) = NULL; CONNECT_RESP_LLC(&CMSG) = NULL; CONNECT_RESP_B1PROTOCOL(&CMSG) = 4; // T.30 modem for Group 3 fax CONNECT_RESP_B2PROTOCOL(&CMSG) = 4; // T.30 for Group 3 fax CONNECT_RESP_B3PROTOCOL(&CMSG) = 4; // T.30 for Group 3 fax CONNECT_RESP_B3CONFIGURATION(&CMSG) = (_cstruct)&B3conf; if (option_verbose > 3) ast_verbose(VERBOSE_PREFIX_3 "CAPI Answering in fax mode for MSN %s\n", dnid); if ((error = _capi_put_cmsg(&CMSG)) != 0) { ast_log(LOG_WARNING, "capiAnswerFax CAPI put_cmsg error\n"); return -1; } else { if (option_verbose > 5) { if (capidebug) ast_verbose(VERBOSE_PREFIX_4 "sent CONNECT_RESP PLCI = %#x DNID = %s\n",i->PLCI,i->dnid); } } i->state = CAPI_STATE_ANSWERING; i->doB3 = AST_CAPI_B3_DONT; i->outgoing = 0; i->earlyB3 = -1; return 0; } static int capianswerfax_exec(struct ast_channel *chan, void *data) { int res=0; struct localuser *u; char *vdata, *fname, *stationID; struct ast_capi_pvt *i = chan->pvt->pvt; if (!data) { /* no data implies no filename or anything is present */ ast_log(LOG_WARNING, "capiAnswerFax requires an argument (filename)\n"); return -1; } vdata = ast_strdupa(data); LOCAL_USER_ADD(u); fname = vdata; if (fname) { stationID = strchr(vdata, '|'); if (stationID) { *stationID='\0'; stationID++; } else stationID = "00000000"; } else stationID = "00000000"; if (strcasecmp("CAPI", chan->type) == 0) { i->fFax = fopen(vdata, "wb"); if(i->fFax == NULL) { ast_log(LOG_WARNING, "capiAnswerFax can't create the output file (%s)\n", strerror(errno)); res = -1; } else { if(i->state != CAPI_STATE_BCONNECTED) capi_answer_fax(chan, stationID); else capi_change_bchan_fax(chan, stationID); while (i->state != CAPI_STATE_DISCONNECTED) { sleep(1); } } } else { ast_log(LOG_WARNING, "capiFax only works on CAPI channels, check your extensions.conf!\n"); res = -1; } 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, capianswerfax_exec,synopsis,tdesc); } char *description(void) { return tdesc; } int usecount(void) { int res; STANDARD_USECOUNT(res); return res; } char *key() { return ASTERISK_GPL_KEY; }