Receive a fax via CAPI is now done with capicommand(receivefax|...)

V0_6
MelwareDE 2005-08-14 12:32:18 +00:00
parent 8f8b5bd30c
commit 743e63fbf7
5 changed files with 211 additions and 299 deletions

View File

@ -43,7 +43,7 @@ CC=gcc
INSTALL=install
SHAREDOS=chan_capi.so app_capiHOLD.so app_capiRETRIEVE.so \
app_capiECT.so app_capiMCID.so app_capiNoES.so app_capiFax.so
app_capiECT.so app_capiMCID.so app_capiNoES.so
CFLAGS+=-Wno-missing-prototypes -Wno-missing-declarations

28
README
View File

@ -60,7 +60,7 @@ This chan_capi version includes:
- Compiles with different Asterisk versions (automatic build configuration)
- Added app_capiNoES for disabling the primitive echo suppressor, use this
before you start recording voicemail or your files may get choppy
- Added app_capiFax to receive faxes over CAPI (see below)
- receive faxes over CAPI (see below)
- Fixes for BSD (Jan Stocker)
- Support 'type of number'.
@ -102,7 +102,13 @@ Call Deflection:
Forwards an unanswered call to another number.
Example:
exten => s,1,capicommand(deflect|12345678)
Fax receive:
Receive a fax using CAPI.
Example:
exten => s,1,capicommand(receivefax|/tmp/${UNIQUEID}|+49 6137 555123|Asterisk)
(more see below)
Helper applications
===================
@ -176,14 +182,17 @@ Example context for incoming calls on MSN 12345678:
exten => 12345678,1,Dial(SIP/phone1)
exten => 12345678,2,Hangup
Short HOWTO of 'capiAnswerFax':
===============================
Short HOWTO of capicommand(receivefax|<filename>[|<stationid>|<headline>]):
==========================================================================
For those of you who have a CAPI card with an on-board DSP (like some Eicon and
DIVA Server), this patch allows you to receive faxes.
If you want to answer a channel in fax mode, use capiAnswerFax() instead of
Answer()
If you want to answer a channel in fax mode, use capicommand(receivefax|...)
instead of Answer()
If you use Answer(), you will be in voice mode. If the hardware DSP detects
fax tone, you can switch from voice to fax mode by calling capiAnswerFax().
fax tone, you can switch from voice to fax mode by calling capicommand(receivefax|...).
The parameter <filename> is mandatory and the parameters <stationid> and
<headline> are optional.
Example of use :
line number 123, play something, if a fax tone is detected, handle it
@ -196,11 +205,12 @@ exten => 124,1,Goto(handle_fax,s,1)
exten => fax,1,Goto(handle_fax,s,1)
[handle_fax]
exten => s,1,capiAnswerFax(/tmp/${UNIQUEID})
exten => s,1,capicommand(receivefax|/tmp/${UNIQUEID}[|<stationid>|<headline>])
exten => s,2,Hangup()
exten => h,1,deadagi,fax.php // Run sfftobmp and mail it.
The output of capiAnswerFax is a SFF file. Use sfftobmp to convert it.
The output of capicommand(receivefax|...) is a SFF file.
Use sfftobmp to convert it.
With a DIVA Server, following features are provided:
- fax up to 33600
- high resolution

View File

@ -1,275 +0,0 @@
/*
* (CAPI*)
*
* Receive a fax with CAPI API.
* Usage : capiAnswerFax(path_output_file.SFF)
*
* 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})
* exten => s,2,Hangup()
* exten => h,1,deadagi,fax.php // Run SFF2TIFF and mail it.
*
* This program is free software and may be modified and
* distributed under the terms of the GNU Public License.
*
* (c) 2004,2005 by Carl Sempla, Cedrik Hans, Frank Sautter
*
*/
#include "config.h"
#include <asterisk/file.h>
#include <asterisk/logger.h>
#include <asterisk/channel.h>
#ifndef CC_AST_HAVE_TECH_PVT
#include <asterisk/channel_pvt.h>
#endif
#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 <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 = "capiAnswerFax";
static char *synopsis = "Answer Fax with CAPI";
STANDARD_LOCAL_USER;
LOCAL_USER_DECL;
void SetupB3Config(B3_PROTO_FAXG3 *B3conf, int FAX_Format)
{
int len1;
int len2;
char *stationID = "00000000";
char *headLine = "CAPI FAXServer";
B3conf->resolution = 0;
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)
{
struct ast_capi_pvt *i = CC_AST_CHANNEL_PVT(c);
_cmsg CMSG;
B3_PROTO_FAXG3 B3conf;
SetupB3Config(&B3conf, FAX_SFF_FORMAT); /* Format ignored by eicon cards */
DISCONNECT_B3_REQ_HEADER(&CMSG, ast_capi_ApplID, get_ast_capi_MessageNumber(), 0);
DISCONNECT_B3_REQ_NCCI(&CMSG) = i->NCCI;
_capi_put_cmsg(&CMSG);
/* wait for the B3 layer to go down */
while (i->state != CAPI_STATE_CONNECTED) {
usleep(10000);
}
/* TODO: if state != BCONNECTED */
SELECT_B_PROTOCOL_REQ_HEADER(&CMSG, ast_capi_ApplID, get_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;
_capi_put_cmsg(&CMSG);
return 0;
}
static int capi_answer_fax(struct ast_channel *c)
{
struct ast_capi_pvt *i = CC_AST_CHANNEL_PVT(c);
MESSAGE_EXCHANGE_ERROR error;
_cmsg CMSG;
char buf[AST_CAPI_MAX_STRING];
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); /* 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;
cc_ast_verbose(3, 0, VERBOSE_PREFIX_3 "CAPI Answering in fax mode for MSN %s\n", dnid);
if ((error = _capi_put_cmsg(&CMSG)) != 0) {
return -1;
}
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;
struct ast_capi_pvt *i = CC_AST_CHANNEL_PVT(chan);
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);
if (!strcasecmp("CAPI", chan->type) == 0) {
ast_log(LOG_WARNING, "capiFax only works on CAPI channels, check your extensions.conf!\n");
LOCAL_USER_REMOVE(u);
return -1;
}
i->fFax = fopen(vdata, "wb");
if (i->fFax == NULL) {
ast_log(LOG_WARNING, "capiAnswerFax: can't create the output file (%s)\n", strerror(errno));
LOCAL_USER_REMOVE(u);
return -1;
}
i->FaxState = 1;
if (i->state != CAPI_STATE_BCONNECTED) {
capi_answer_fax(chan);
} else {
capi_change_bchan_fax(chan);
}
while (i->FaxState) {
sleep(1);
}
/* if the file has zero length */
if (ftell(i->fFax) == 0L)
res = -1;
cc_ast_verbose(2, 1, VERBOSE_PREFIX_3 "Closing fax file...\n");
fclose(i->fFax);
i->fFax = NULL;
switch (i->reason) {
case 0x3490:
case 0x349f:
res = (i->reasonb3 == 0) ? 0 : -1;
break;
default:
res = -1;
}
if (res != 0) {
cc_ast_verbose(2, 0,
VERBOSE_PREFIX_1 "capiAnswerFax: fax receive failed.\n");
unlink(vdata);
} else {
cc_ast_verbose(2, 0,
VERBOSE_PREFIX_1 "capiAnswerFax: fax received.\n");
}
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;
}

View File

@ -559,7 +559,6 @@ static void interface_cleanup(struct ast_capi_pvt *i)
i->cause = 0;
i->faxhandled = 0;
i->FaxState = 0;
i->PLCI = 0;
i->NCCI = 0;
@ -857,9 +856,9 @@ int capi_call(struct ast_channel *c, char *idest, int timeout)
}
/*
* Asterisk tells us to answer a call
* answer a capi call
*/
static int capi_answer(struct ast_channel *c)
static int capi_send_answer(struct ast_channel *c, int *bprot, _cstruct b3conf)
{
struct ast_capi_pvt *i = CC_AST_CHANNEL_PVT(c);
_cmsg CMSG;
@ -874,9 +873,7 @@ static int capi_answer(struct ast_channel *c)
dnid = i->dnid;
}
i->fFax = NULL;
i->faxhandled = 0;
i->FaxState = 0;
memset(&CMSG, 0, sizeof(CMSG));
CONNECT_RESP_HEADER(&CMSG, ast_capi_ApplID, i->MessageNumber, 0);
CONNECT_RESP_PLCI(&CMSG) = i->PLCI;
@ -886,11 +883,10 @@ static int capi_answer(struct ast_channel *c)
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) = 1;
CONNECT_RESP_B2PROTOCOL(&CMSG) = 1;
CONNECT_RESP_B3PROTOCOL(&CMSG) = 0;
CONNECT_RESP_B1PROTOCOL(&CMSG) = bprot[0];
CONNECT_RESP_B2PROTOCOL(&CMSG) = bprot[1];
CONNECT_RESP_B3PROTOCOL(&CMSG) = bprot[2];
CONNECT_RESP_B3CONFIGURATION(&CMSG) = b3conf;
cc_ast_verbose(3, 0, VERBOSE_PREFIX_3 "%s: Answering for %s\n",
i->name, dnid);
@ -907,6 +903,16 @@ static int capi_answer(struct ast_channel *c)
return 0;
}
/*
* Asterisk tells us to answer a call
*/
static int capi_answer(struct ast_channel *c)
{
int bprot[3] = { 1, 1, 0 };
return capi_send_answer(c, bprot, NULL);
}
/*
* Asterisk tells us to read for a channel
*/
@ -1401,6 +1407,143 @@ struct ast_channel *capi_request(char *type, int format, void *data)
return NULL;
}
/*
* fill out fax conf struct
*/
static void setup_b3_fax_config(B3_PROTO_FAXG3 *b3conf, int fax_format, char *stationid, char *headline)
{
int len1;
int len2;
cc_ast_verbose(3, 1, VERBOSE_PREFIX_3 "Setup fax b3conf fmt=%d, stationid='%s' headline='%s'\n",
fax_format, stationid, headline);
b3conf->resolution = 0;
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);
}
/*
* change b protocol to fax
*/
static void capi_change_bchan_fax(struct ast_channel *c, B3_PROTO_FAXG3 *b3conf)
{
struct ast_capi_pvt *i = CC_AST_CHANNEL_PVT(c);
_cmsg CMSG;
if (i->state == CAPI_STATE_BCONNECTED) {
int waitcount = 200;
DISCONNECT_B3_REQ_HEADER(&CMSG, ast_capi_ApplID, get_ast_capi_MessageNumber(), 0);
DISCONNECT_B3_REQ_NCCI(&CMSG) = i->NCCI;
_capi_put_cmsg(&CMSG);
/* wait for the B3 layer to go down */
while ((waitcount > 0) && (i->state == CAPI_STATE_BCONNECTED)) {
usleep(10000);
waitcount--;
}
if (i->state != CAPI_STATE_CONNECTED) {
ast_log(LOG_WARNING, "capi receivefax disconnect b3 wait error\n");
}
}
SELECT_B_PROTOCOL_REQ_HEADER(&CMSG, ast_capi_ApplID, get_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;
_capi_put_cmsg(&CMSG);
return;
}
/*
* capicommand 'receivefax'
*/
static int capi_receive_fax(struct ast_channel *c, char *data)
{
struct ast_capi_pvt *i = CC_AST_CHANNEL_PVT(c);
int res = 0;
char *filename, *stationid, *headline;
int bprot[3] = { 4, 4, 4 };
B3_PROTO_FAXG3 b3conf;
if (!data) { /* no data implies no filename or anything is present */
ast_log(LOG_WARNING, "capi receivefax requires a filename\n");
return -1;
}
filename = strsep(&data, "|");
stationid = strsep(&data, "|");
headline = data;
if (!stationid)
stationid = emptyid;
if (!headline)
headline = emptyid;
if ((i->fFax = fopen(filename, "wb")) == NULL) {
ast_log(LOG_WARNING, "can't create fax output file (%s)\n", strerror(errno));
return -1;
}
i->FaxState = 1;
setup_b3_fax_config(&b3conf, FAX_SFF_FORMAT, stationid, headline);
switch (i->state) {
case CAPI_STATE_ALERTING:
case CAPI_STATE_DID:
case CAPI_STATE_INCALL:
capi_send_answer(c, bprot, (_cstruct)&b3conf);
break;
case CAPI_STATE_CONNECTED:
case CAPI_STATE_BCONNECTED:
capi_change_bchan_fax(c, &b3conf);
break;
default:
i->FaxState = 0;
ast_log(LOG_WARNING, "capi receive fax in wrong state (%d)\n",
i->state);
return -1;
}
while (i->FaxState == 1) {
usleep(10000);
}
res = i->FaxState;
i->FaxState = 0;
/* if the file has zero length */
if (ftell(i->fFax) == 0L) {
res = -1;
}
cc_ast_verbose(2, 1, VERBOSE_PREFIX_3 "Closing fax file...\n");
fclose(i->fFax);
i->fFax = NULL;
if (res != 0) {
cc_ast_verbose(2, 0,
VERBOSE_PREFIX_1 "capi receivefax: fax receive failed reason=0x%04x reasonB3=0x%04x\n",
i->reason, i->reasonb3);
unlink(filename);
} else {
cc_ast_verbose(2, 0,
VERBOSE_PREFIX_1 "capi receivefax: fax receive successful.\n");
}
return res;
}
/*
* Fax guard tone -- Handle and return NULL
*/
@ -1769,7 +1912,6 @@ static void handle_info_disconnect(_cmsg *CMSG, unsigned int PLCI, unsigned int
i->name);
if (i->FaxState) {
/* in capiFax */
i->FaxState = 0;
return;
}
pipe_cause_control(i, 0);
@ -2233,7 +2375,7 @@ static void capi_handle_connect_b3_active_indication(_cmsg *CMSG, unsigned int P
}
if (i->FaxState) {
cc_ast_verbose(6, 0, VERBOSE_PREFIX_3 "%s: Fax connection, no EC/DTMF\n",
cc_ast_verbose(3, 1, VERBOSE_PREFIX_3 "%s: Fax connection, no EC/DTMF\n",
i->name);
} else {
capi_echo_canceller(i->owner, EC_FUNCTION_ENABLE);
@ -2328,6 +2470,18 @@ static void capi_handle_disconnect_indication(_cmsg *CMSG, unsigned int PLCI, un
i->reason = DISCONNECT_IND_REASON(CMSG);
if (i->FaxState == 1) {
/* in capiFax */
switch (i->reason) {
case 0x3490:
case 0x349f:
i->FaxState = (i->reasonb3 == 0) ? 0 : -1;
break;
default:
i->FaxState = -1;
}
}
if (PLCI == i->onholdPLCI) {
/* the caller onhold hung up (or ECTed away) */
interface_cleanup(i);
@ -2900,7 +3054,7 @@ static int capicommand_exec(struct ast_channel *chan, void *data)
int res = 0;
struct localuser *u;
char *s;
char *stringp = NULL;
char *stringp;
char *command, *params;
if (!data) {
@ -2923,6 +3077,8 @@ static int capicommand_exec(struct ast_channel *chan, void *data)
res = capi_set_earlyb3(chan, params);
} else if (!strcasecmp(command, "deflect")) {
res = capi_call_deflect(chan, params);
} else if (!strcasecmp(command, "receivefax")) {
res = capi_receive_fax(chan, params);
} else {
res = -1;
ast_log(LOG_WARNING, "Unknown command '%s' for capiCommand\n",

View File

@ -64,6 +64,27 @@ static inline unsigned short read_capi_word(void *m)
static ast_mutex_t mutex = AST_MUTEX_INITIALIZER
#endif
/* 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
/* Fax struct */
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;
/* duration in ms for sending and detecting dtmfs */
#define AST_CAPI_DTMF_DURATION 0x40