- more work on addressing data elements
 - completed CallTransfer handling with inbound bridging
 - first outbound Path Replacement support for inbound bridging
    Code isn't first class yet. There may be some errors still there.
This commit is contained in:
MelwareDE 2007-07-05 19:48:19 +00:00
parent 850dfb503c
commit 9ae64305da
8 changed files with 176 additions and 58 deletions

View File

@ -82,14 +82,12 @@ The QSIG support includes:
This should be configurable in the next release.
- decoding of incoming Call Transfer feature
Does nothing useful yet, only some debug output. It's now a small step to implement inbound Path Replacement.
Enables inbound Path Replacement. If received, an automatic Path Replacement with asterisk internal bridging will be fired.
Future Targets:
===============
- check code for buffer overflows
- complete Call Transfer inbound
- Path Replacement inbound
- Allow/Disallow Path Replacement within capi.conf
- complete path replacement features
- Call Rerouting feature [ECMA-174]
- CCBS
- AOC
@ -113,5 +111,6 @@ Set qsig to one of the following values, which corresponds to your configuration
ToDo List:
==========
- Support for inbound routing
- Support for inbound rerouting
- Enhance ASN1-97 Addressing Data Elements support - will save much code
- Allow/Disallow Path Replacement within capi.conf - partially done

View File

@ -67,5 +67,5 @@ devices=2 ;number of concurrent calls (b-channels) on this controller
;jb..... ;with Asterisk 1.4 you can configure jitterbuffer,
;see Asterisk documentation for all jb* setting available.
;mohinterpret=default ;Asterisk 1.4: default music on hold class when placed on hold.
;qsig=on ;enable use of Q.SIG extensions.
;qsig=1 ;enable use of Q.SIG extensions. ECMA Variant
;qsig_prnum=1234 ;enable inbound bridging - this should be an QSIG-network-wide unique number

View File

@ -5474,7 +5474,6 @@ static int conf_interface(struct cc_capi_conf *conf, struct ast_variable *v)
CONF_TRUE(conf->es, "echosquelch", 1)
CONF_TRUE(conf->bridge, "bridge", 1)
CONF_TRUE(conf->ntmode, "ntmode", 1)
CONF_INTEGER(conf->qsigfeat, "qsig")
if (!strcasecmp(v->name, "callgroup")) {
conf->callgroup = ast_get_group(v->value);
continue;
@ -5575,6 +5574,7 @@ static int conf_interface(struct cc_capi_conf *conf, struct ast_variable *v)
if (!strcasecmp(v->name, "disallow")) {
ast_parse_allow_disallow(&conf->prefs, &conf->capability, v->value, 0);
}
cc_pbx_qsig_conf_interface_value(conf, v);
}
#undef CONF_STRING
#undef CONF_INTEGER

View File

@ -268,9 +268,12 @@ struct cc_qsig_data {
int pr_propose_sendback; /* send back an prior received PR PROPOSE on Connect */
int pr_propose_sentback; /* set to 1 after sending an PR PROPOSE */
int pr_propose_active;
int pr_propose_doinboundbridge; /* We have to to bridge a call back to asterisk */
char *pr_propose_cid; /* Call identity */
char *pr_propose_pn; /* Party Number */
char if_pr_propose_pn[AST_MAX_EXTENSION]; /* configured interface Party Number */
/* Partner Channel - needed for many features */
struct capi_pvt *partner_ch;
unsigned int partner_plci;
@ -457,6 +460,10 @@ struct cc_capi_profile {
unsigned int manufacturer[5];
} __attribute__((__packed__));
struct cc_capi_qsig_conf {
char if_pr_propose_pn[AST_MAX_EXTENSION];
};
struct cc_capi_conf {
char name[CAPI_MAX_STRING];
char language[MAX_LANGUAGE];
@ -481,6 +488,7 @@ struct cc_capi_conf {
int bridge;
int amaflags;
int qsigfeat;
struct cc_capi_qsig_conf qsigconf;
unsigned int faxsetting;
ast_group_t callgroup;
ast_group_t pickupgroup;

View File

@ -101,6 +101,9 @@
#define CCQSIG_TIMER_WAIT_PRPROPOSE 1 /* Wait x seconds */
#define free_null(x) { free(x); x = NULL; }
/* Common QSIG structs */
/*
@ -179,6 +182,7 @@ extern int pbx_capi_qsig_bridge(struct capi_pvt *i0, struct capi_pvt *i1);
extern void cc_qsig_interface_init(struct cc_capi_conf *conf, struct capi_pvt *tmp);
extern void cc_pbx_qsig_conf_interface_value(struct cc_capi_conf *conf, struct ast_variable *v);
extern void interface_cleanup_qsig(struct capi_pvt *i);
extern void pbx_capi_qsig_unload_module(struct capi_pvt *i);
extern void pbx_capi_qsig_handle_info_indication(_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt *i);

View File

@ -50,7 +50,7 @@ unsigned int cc_qsig_asn197ade_get_partynumber(char *buf, int buflen, int *idx,
numtype = (data[myidx++] & 0x0F); /* defines type of Number: numDigits, publicPartyNum, nsapEncNum, dataNumDigits */
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " * num type %i\n", numtype); */
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " * num type %i,%i\n", numtype, myidx); */
switch (numtype){
case 0:
if (data[myidx] > 0) { /* length of this context data */
@ -79,6 +79,7 @@ unsigned int cc_qsig_asn197ade_get_partynumber(char *buf, int buflen, int *idx,
}
break;
};
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " * num type %i,%i\n", numtype, myidx); */
return myidx - *idx;
}
@ -96,7 +97,7 @@ unsigned int cc_qsig_asn197ade_get_numdigits(char *buf, int buflen, int *idx, un
memcpy(buf, &data[myidx], strsize);
buf[strsize] = 0;
// cc_verbose(1, 1, VERBOSE_PREFIX_4 " * string length %i\n", strsize);
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " * string length %i,%i\n", strsize, *idx); */
return strsize;
}
@ -137,11 +138,12 @@ unsigned int cc_qsig_asn197ade_get_pns(unsigned char *data, int *idx, struct asn
numtype = (data[myidx++] & 0x0F); /* defines type of Number */
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " * num type %i\n", numtype); */
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " * num type %i,%i\n", numtype, myidx); */
switch (numtype){
case 0:
/* myidx points now to length */
res = cc_qsig_asn197ade_get_partynumber(buf, buflen, &myidx, data);
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " * res %i\n", numtype); */
if (!res)
return 0;
@ -151,7 +153,7 @@ unsigned int cc_qsig_asn197ade_get_pns(unsigned char *data, int *idx, struct asn
}
/* get screening indicator */
if (data[myidx] == ASN1_ENUMERATED) {
if (data[myidx] == ASN1_ENUMERATED) { /* HACK: this is not safe - check length of this parameter */
myidx++;
ns->screeningInd = cc_qsig_asn1_get_integer(data, &myidx);
}
@ -175,7 +177,7 @@ unsigned int cc_qsig_asn197ade_get_pns(unsigned char *data, int *idx, struct asn
}
/* get screening indicator */
if (data[myidx] == ASN1_ENUMERATED) {
if (data[myidx] == ASN1_ENUMERATED) { /* HACK: this is not safe - check length of this parameter */
myidx++;
ns->screeningInd = cc_qsig_asn1_get_integer(data, &myidx);
}

View File

@ -609,28 +609,66 @@ static void pbx_capi_qsig_handle_ctc(struct cc_qsig_invokedata *invoke, struct c
struct cc_qsig_ctcomplete ctc;
struct capi_pvt *ii;
#define CLEAR_CTC { if (ctc.redirectionNumber.partyNumber) free(ctc.redirectionNumber.partyNumber);\
if (ctc.basicCallInfoElements) free(ctc.basicCallInfoElements); \
if (ctc.redirectionName) free(ctc.redirectionName); \
if (ctc.argumentExtension) free(ctc.argumentExtension); }
#define CLEAR_CTC { if (ctc.redirectionNumber.partyNumber) free_null(ctc.redirectionNumber.partyNumber);\
if (ctc.basicCallInfoElements) free_null(ctc.basicCallInfoElements); \
if (ctc.redirectionName) free_null(ctc.redirectionName); \
if (ctc.argumentExtension) free_null(ctc.argumentExtension); }
int res = cc_qsig_decode_ecma_calltransfer(invoke, i, &ctc);
if (!res)
return;
if (ctc.redirectionNumber.partyNumber && (ctc.endDesignation == 2)) { /* TODO: activate this - there is a problem with channel_masquerade */
if (ctc.redirectionNumber.partyNumber && (ctc.endDesignation == 0)) {
ii = capi_find_interface_bynumber(ctc.redirectionNumber.partyNumber);
if (ii) {
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Call Transfer partner channel for %s found at channel %s\n", ctc.redirectionNumber.partyNumber, ii->vname);
char *prpn = i->qsig_data.if_pr_propose_pn;
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Call Transfer partner channel for %s found at channel %s, bridging possible.\n", ctc.redirectionNumber.partyNumber, ii->vname);
CLEAR_CTC
if (!(strlen(prpn))) {
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Path Replacement not configured, bridging not available!\n");
} else {
unsigned char fac[CAPI_MAX_FACILITYDATAARRAY_SIZE];
ast_channel_masquerade(i->owner, ii->owner);
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Trying to bridge with Path Replacement number %s...\n", prpn);
switch (ii->state) {
case CAPI_STATE_ALERTING:
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: peer is in state ALERTING, PATH REPLACE follows after CONNECT...\n");
ii->qsig_data.pr_propose_cid = strdup("123"); /* HACK: need an dynamic ID */
ii->qsig_data.pr_propose_pn = strdup(i->qsig_data.if_pr_propose_pn);
ii->qsig_data.pr_propose_doinboundbridge = 1;
i->qsig_data.pr_propose_doinboundbridge = 1;
i->qsig_data.partner_plci = ii->PLCI;
break;
case CAPI_STATE_CONNECTED:
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: peer is CONNECTED...\n");
i->qsig_data.pr_propose_cid = strdup("123"); /* HACK: need an dynamic ID */
i->qsig_data.pr_propose_pn = strdup(i->qsig_data.if_pr_propose_pn);
ii->qsig_data.pr_propose_doinboundbridge = 1;
ii->qsig_data.partner_plci = i->PLCI;
cc_qsig_do_facility(fac, i->owner, NULL, 4, 0);
capi_sendf(NULL, 0, CAPI_INFO_REQ, i->PLCI, get_capi_MessageNumber(),
"()(()()()s)", fac );
i->qsig_data.pr_propose_cid = NULL;
i->qsig_data.pr_propose_pn = NULL;
break;
default:
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: peer's state is %i, which is not handled yet...\n", ii->state);
break;
}
}
CLEAR_CTC
}
}
CLEAR_CTC
CLEAR_CTC
return ;
}
@ -1030,13 +1068,6 @@ int pbx_capi_qsig_ct(struct ast_channel *c, char *param)
return -1;
}
cc_qsig_do_facility(fac, c, param, 12, 0);
capi_sendf(NULL, 0, CAPI_INFO_REQ, ii->PLCI, get_capi_MessageNumber(),
"()(()()()s())",
fac
);
cc_qsig_do_facility(fac, c, param, 12, 1);
capi_sendf(NULL, 0, CAPI_INFO_REQ, i->PLCI, get_capi_MessageNumber(),
@ -1044,6 +1075,14 @@ int pbx_capi_qsig_ct(struct ast_channel *c, char *param)
fac
);
cc_qsig_do_facility(fac, c, param, 12, 0);
capi_sendf(NULL, 0, CAPI_INFO_REQ, ii->PLCI, get_capi_MessageNumber(),
"()(()()()s())",
fac
);
return 0;
}
@ -1075,13 +1114,13 @@ static void send_feature_calltransfer(struct capi_pvt *i)
ii->qsig_data.partner_plci = i->PLCI;
if (ii) {
cc_qsig_do_facility(fac, ii->owner, NULL, 12, 0);
capi_sendf(NULL, 0, CAPI_INFO_REQ, ii->PLCI, get_capi_MessageNumber(), "()(()()()s())", fac);
cc_qsig_do_facility(fac, i->owner, NULL, 12, 1);
capi_sendf(NULL, 0, CAPI_INFO_REQ, i->PLCI, get_capi_MessageNumber(), "()(()()()s())", fac);
cc_qsig_do_facility(fac, ii->owner, NULL, 12, 0);
capi_sendf(NULL, 0, CAPI_INFO_REQ, ii->PLCI, get_capi_MessageNumber(), "()(()()()s())", fac);
} else {
cc_log(LOG_WARNING, "Call Transfer failed - second channel not found (PLCI %#x)!\n", i->qsig_data.partner_plci);
}
@ -1107,9 +1146,12 @@ void cc_qsig_interface_init(struct cc_capi_conf *conf, struct capi_pvt *tmp)
tmp->qsig_data.pr_propose_active = 0;
tmp->qsig_data.pr_propose_sendback = 0; /* send back an prior received PR PROPOSE on Connect */
tmp->qsig_data.pr_propose_sentback = 0;
tmp->qsig_data.pr_propose_doinboundbridge = 0;
tmp->qsig_data.pr_propose_cid = NULL; /* Call identity */
tmp->qsig_data.pr_propose_pn = NULL; /* Party Number */
cc_copy_string(tmp->qsig_data.if_pr_propose_pn, conf->qsigconf.if_pr_propose_pn, sizeof(tmp->qsig_data.if_pr_propose_pn));
/* Partner Channel - needed for many features */
tmp->qsig_data.partner_ch = NULL;
tmp->qsig_data.partner_plci = 0;
@ -1118,6 +1160,34 @@ void cc_qsig_interface_init(struct cc_capi_conf *conf, struct capi_pvt *tmp)
ast_cond_init(&tmp->qsig_data.event_trigger, NULL);
}
/*
* build the qsig-interface according to configs
*/
void cc_pbx_qsig_conf_interface_value(struct cc_capi_conf *conf, struct ast_variable *v)
{
#define CONF_STRING(var, token) \
if (!strcasecmp(v->name, token)) { \
cc_copy_string(var, v->value, sizeof(var)); \
}
#define CONF_INTEGER(var, token) \
if (!strcasecmp(v->name, token)) { \
var = atoi(v->value); \
}
#define CONF_TRUE(var, token, val) \
if (!strcasecmp(v->name, token)) { \
if (ast_true(v->value)) \
var = val; \
}
CONF_INTEGER(conf->qsigfeat, "qsig")
CONF_STRING(conf->qsigconf.if_pr_propose_pn, "qsig_prnum")
#undef CONF_STRING
#undef CONF_INTEGER
#undef CONF_TRUE
}
/*
* cleanup QSIG stuff on every (end of) call per interface
*/
@ -1129,6 +1199,7 @@ static void qsig_cleanup_channel(struct capi_pvt *i)
i->qsig_data.calltransfer_onring = 0;
i->qsig_data.pr_propose_active = 0;
i->qsig_data.pr_propose_sentback = 0;
i->qsig_data.pr_propose_doinboundbridge = 0;
if (i->qsig_data.pr_propose_cid) {
free(i->qsig_data.pr_propose_cid);
i->qsig_data.pr_propose_cid = NULL;
@ -1252,7 +1323,7 @@ void pbx_capi_qsig_handle_info_indication(_cmsg *CMSG, unsigned int PLCI, unsign
qsiginvoke = cc_qsig_handle_capi_facilityind( (unsigned char*) INFO_IND_INFOELEMENT(CMSG), i);
/* got an Path Replacement */
if ((i->qsig_data.pr_propose_cid && i->qsig_data.pr_propose_pn) &! i->qsig_data.pr_propose_sendback) {
if ((i->qsig_data.pr_propose_cid && i->qsig_data.pr_propose_pn) &! i->qsig_data.pr_propose_sendback &! i->qsig_data.pr_propose_doinboundbridge) {
struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci);
if (ii) {
@ -1283,7 +1354,22 @@ void pbx_capi_qsig_handle_info_indication(_cmsg *CMSG, unsigned int PLCI, unsign
free(i->qsig_data.pr_propose_pn);
i->qsig_data.pr_propose_pn = NULL;
}
if ((i->qsig_data.pr_propose_cid && i->qsig_data.pr_propose_pn) && i->qsig_data.pr_propose_doinboundbridge) {
struct ast_channel *chanx;
struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci);
if (ii) {
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * QSIG_PATHREPLACEMENT_PROPOSE: trying to complete bridge...\n");
chanx = ast_bridged_channel(i->owner);
ast_channel_masquerade(ii->owner, chanx);
}
free(i->qsig_data.pr_propose_cid);
i->qsig_data.pr_propose_cid = NULL;
free(i->qsig_data.pr_propose_pn);
i->qsig_data.pr_propose_pn = NULL;
}
}
break;
@ -1338,9 +1424,9 @@ void pbx_capi_qsig_handle_info_indication(_cmsg *CMSG, unsigned int PLCI, unsign
);
i->qsig_data.pr_propose_sendback = 0;
free(i->qsig_data.pr_propose_cid);
free(i->qsig_data.pr_propose_cid);
i->qsig_data.pr_propose_cid = NULL;
free(i->qsig_data.pr_propose_pn);
free(i->qsig_data.pr_propose_pn);
i->qsig_data.pr_propose_pn = NULL;
i->qsig_data.pr_propose_sentback = 1;
@ -1355,6 +1441,27 @@ void pbx_capi_qsig_handle_info_indication(_cmsg *CMSG, unsigned int PLCI, unsign
unsigned int qsiginvoke;
qsiginvoke = cc_qsig_handle_capi_facilityind( (unsigned char*) INFO_IND_INFOELEMENT(CMSG), i);
}
{
/* handle outbound Path Replacement */
if ((i->qsig_data.pr_propose_cid && i->qsig_data.pr_propose_pn) && i->qsig_data.pr_propose_doinboundbridge) {
unsigned char fac[CAPI_MAX_FACILITYDATAARRAY_SIZE];
cc_qsig_do_facility(fac, i->owner, NULL, 4, 0);
capi_sendf(NULL, 0, CAPI_INFO_REQ, i->PLCI, get_capi_MessageNumber(),
"()(()()()s)", fac );
i->qsig_data.pr_propose_sendback = 0;
free(i->qsig_data.pr_propose_cid);
i->qsig_data.pr_propose_cid = NULL;
free(i->qsig_data.pr_propose_pn);
i->qsig_data.pr_propose_pn = NULL;
i->qsig_data.pr_propose_sentback = 1;
i->qsig_data.pr_propose_doinboundbridge = 0;
}
}
break;
case 0x8045: /* DISCONNECT */
qsig_cleanup_channel(i);

View File

@ -190,6 +190,7 @@ void cc_qsig_op_ecma_isdn_leginfo2(struct cc_qsig_invokedata *invoke, struct cap
char tempstr[5];
char divertNum[ASN197ADE_NUMDIGITS_STRSIZE+1];
char origCalledNum[ASN197ADE_NUMDIGITS_STRSIZE+1];
struct asn197ade_numberscreened divertPNS, origPNS;
char divertName[ASN197NO_NAME_STRSIZE+1];
char origCalledName[ASN197NO_NAME_STRSIZE+1];
unsigned int temp = 0;
@ -233,32 +234,29 @@ void cc_qsig_op_ecma_isdn_leginfo2(struct cc_qsig_invokedata *invoke, struct cap
orgDivReason = cc_qsig_asn1_get_integer(invoke->data, &myidx);
break;
case 1:
temp = cc_qsig_asn197ade_get_partynumber(divertNum, ASN197ADE_NUMDIGITS_STRSIZE, &myidx, invoke->data);
if (temp) {
myidx += temp;
}
temp = invoke->data[myidx++]; /* keep the length of this info - maybe we don't get all data now */
cc_qsig_asn197ade_get_pns(invoke->data, &myidx, &divertPNS);
myidx += temp;
break;
case 2:
temp = cc_qsig_asn197ade_get_partynumber(origCalledNum, ASN197ADE_NUMDIGITS_STRSIZE, &myidx, invoke->data);
if (temp) {
myidx += temp;
}
temp = invoke->data[myidx++]; /* keep the length of this info - maybe we don't get all data now */
cc_qsig_asn197ade_get_pns(invoke->data, &myidx, &origPNS);
myidx += temp;
break;
case 3:
/* Redirecting Name */
myidx++;
temp = cc_qsig_asn197no_get_name(divertName, ASN197NO_NAME_STRSIZE, &temp2, &myidx, invoke->data);
if (temp) {
myidx += temp;
}
temp = invoke->data[myidx++]; /* keep the length of this info - maybe we don't get all data now */
cc_qsig_asn197no_get_name(divertName, ASN197NO_NAME_STRSIZE, &temp2, &myidx, invoke->data);
myidx += temp;
break;
case 4:
/* origCalled Name */
myidx++;
temp = cc_qsig_asn197no_get_name(origCalledName, ASN197NO_NAME_STRSIZE, &temp2, &myidx, invoke->data);
if (temp) {
myidx += temp;
}
temp = invoke->data[myidx++]; /* keep the length of this info - maybe we don't get all data now */
cc_qsig_asn197no_get_name(origCalledName, ASN197NO_NAME_STRSIZE, &temp2, &myidx, invoke->data);
myidx += temp;
break;
default:
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * unknown parameter %i\n", parameter);
break;
}
}
@ -270,12 +268,12 @@ void cc_qsig_op_ecma_isdn_leginfo2(struct cc_qsig_invokedata *invoke, struct cap
snprintf(tempstr, 5, "%i", divCount);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_DIVCOUNT", tempstr);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_DIVNUM", divertNum);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_ODIVNUM", origCalledNum);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_DIVNUM", divertPNS.partyNumber);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_ODIVNUM", origPNS.partyNumber);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_DIVNAME", divertName);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_ODIVNAME", origCalledName);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Got QSIG_LEG_INFO2: %i(%i), %ix %s->%s, %s->%s\n", divReason, orgDivReason, divCount, origCalledNum, divertNum, origCalledName, divertName);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Got QSIG_LEG_INFO2: %i(%i), %ix %s->%s, %s->%s\n", divReason, orgDivReason, divCount, origPNS.partyNumber, divertPNS.partyNumber, origCalledName, divertName);
return;