QSIG:
- implemented support for decoding Call Transfer event - have clean up the code early Makes atm only debugging output of feature. - moved some code out of chan_capi.c
This commit is contained in:
parent
cae2b60cd4
commit
bb3bbe31d3
|
@ -81,16 +81,19 @@ The QSIG support includes:
|
|||
to see the name and number of his connected peer.
|
||||
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.
|
||||
|
||||
Future Targets:
|
||||
===============
|
||||
- check code for buffer overflows
|
||||
- Call Transfer inbound
|
||||
- complete Call Transfer inbound
|
||||
- Path Replacement inbound
|
||||
- Allow/Disallow Path Replacement within capi.conf
|
||||
- Call Rerouting feature [ECMA-174]
|
||||
- CCBS
|
||||
- AOC
|
||||
- Sent out LEG_INFO2 - maybe this would allow an sendtext (display instructions on the connected set)
|
||||
- Sent out LEG_INFO3 - maybe this would allow an sendtext (display instructions on the connected set)
|
||||
- ...
|
||||
|
||||
How to use:
|
||||
|
@ -111,3 +114,4 @@ ToDo List:
|
|||
==========
|
||||
|
||||
- Support for inbound routing
|
||||
- Enhance ASN1-97 Addressing Data Elements support - will save much code
|
||||
|
|
16
chan_capi.c
16
chan_capi.c
|
@ -3448,9 +3448,8 @@ static void capidev_handle_connect_indication(_cmsg *CMSG, unsigned int PLCI, un
|
|||
pbx_builtin_setvar_helper(i->owner, "SECONDCALLERID", buffer);
|
||||
*/
|
||||
|
||||
if (i->qsigfeat != QSIG_DISABLED) {
|
||||
cc_qsig_handle_capiind(CONNECT_IND_FACILITYDATAARRAY(CMSG), i);
|
||||
}
|
||||
/* Handle QSIG informations, if any */
|
||||
cc_qsig_handle_capiind(CONNECT_IND_FACILITYDATAARRAY(CMSG), i);
|
||||
|
||||
if ((i->isdnmode == CAPI_ISDNMODE_MSN) && (i->immediate)) {
|
||||
/* if we don't want to wait for SETUP/SENDING-COMPLETE in MSN mode */
|
||||
|
@ -4859,11 +4858,9 @@ int mkif(struct cc_capi_conf *conf)
|
|||
tmp->doDTMF = conf->softdtmf;
|
||||
tmp->capability = conf->capability;
|
||||
|
||||
tmp->qsigfeat = conf->qsigfeat;
|
||||
if (conf->qsigfeat) {
|
||||
cc_qsig_interface_init(conf, tmp);
|
||||
}
|
||||
|
||||
/* Initialize QSIG code */
|
||||
cc_qsig_interface_init(conf, tmp);
|
||||
|
||||
tmp->next = capi_iflist; /* prepend */
|
||||
capi_iflist = tmp;
|
||||
cc_verbose(2, 0, VERBOSE_PREFIX_3 "capi %c %s (%s:%s) contr=%d devs=%d EC=%d,opt=%d,tail=%d\n",
|
||||
|
@ -5734,8 +5731,7 @@ int unload_module(void)
|
|||
if (i->smoother)
|
||||
ast_smoother_free(i->smoother);
|
||||
|
||||
if (i->qsigfeat)
|
||||
pbx_capi_qsig_unload_module(i);
|
||||
pbx_capi_qsig_unload_module(i);
|
||||
|
||||
cc_mutex_destroy(&i->lock);
|
||||
ast_cond_destroy(&i->event_trigger);
|
||||
|
|
|
@ -95,6 +95,7 @@
|
|||
/* QSIG Operations += 1000 */
|
||||
#define CCQSIG__ECMA__NAMEPRES 1000 /* Setting an own constant for ECMA Operation/Namepresentation, others will follow */
|
||||
#define CCQSIG__ECMA__PRPROPOSE 1004 /* Path Replacement Propose */
|
||||
#define CCQSIG__ECMA__CTCOMPLETE 1012 /* Call Transfer Complete */
|
||||
#define CCQSIG__ECMA__LEGINFO2 1021 /* LEG INFORMATION2 */
|
||||
|
||||
|
||||
|
@ -195,5 +196,6 @@ extern void cc_qsig_encode_ecma_prpropose(unsigned char * buf, unsigned int *idx
|
|||
|
||||
extern void cc_qsig_encode_ecma_sscalltransfer(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, char *param);
|
||||
extern void cc_qsig_encode_ecma_calltransfer(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, char *param, int info);
|
||||
extern char *cc_qsig_decode_ecma_calltransfer(struct cc_qsig_invokedata *invoke, struct capi_pvt *i);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <asterisk/options.h>
|
||||
#include "chan_capi20.h"
|
||||
#include "chan_capi.h"
|
||||
#include "chan_capi_utils.h"
|
||||
#include "chan_capi_qsig.h"
|
||||
#include "chan_capi_qsig_asn197ade.h"
|
||||
|
||||
|
@ -48,12 +49,17 @@ 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\n", numtype); */
|
||||
switch (numtype){
|
||||
case 0:
|
||||
if (data[myidx++] > 0) /* length of this context data */
|
||||
if (data[myidx++] == ASN1_TC_CONTEXTSPEC)
|
||||
myidx += cc_qsig_asn197ade_get_numdigits(buf, buflen, &myidx, data) + 1;
|
||||
if (data[myidx] > 0) { /* length of this context data */
|
||||
if (data[myidx+1] == ASN1_TC_CONTEXTSPEC) {
|
||||
myidx += 2;
|
||||
myidx += cc_qsig_asn197ade_get_numdigits(buf, buflen, &myidx, data);
|
||||
} else {
|
||||
myidx += cc_qsig_asn197ade_get_numdigits(buf, buflen, &myidx, data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1: /* publicPartyNumber (E.164) not supported yet */
|
||||
return 0;
|
||||
|
@ -62,9 +68,14 @@ unsigned int cc_qsig_asn197ade_get_partynumber(char *buf, int buflen, int *idx,
|
|||
return 0;
|
||||
break;
|
||||
case 3:
|
||||
if (data[myidx++] > 0) /* length of this context data */
|
||||
if (data[myidx++] == ASN1_TC_CONTEXTSPEC)
|
||||
myidx += cc_qsig_asn197ade_get_numdigits(buf, buflen, &myidx, data) + 1;
|
||||
if (data[myidx++] > 0) { /* length of this context data */
|
||||
if (data[myidx+1] == ASN1_TC_CONTEXTSPEC) {
|
||||
myidx += 2;
|
||||
myidx += cc_qsig_asn197ade_get_numdigits(buf, buflen, &myidx, data);
|
||||
} else {
|
||||
myidx += cc_qsig_asn197ade_get_numdigits(buf, buflen, &myidx, data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
};
|
||||
return myidx - *idx;
|
||||
|
@ -84,7 +95,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\n", strsize);
|
||||
return strsize;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,11 @@
|
|||
|
||||
#define ASN197ADE_NUMDIGITS_STRSIZE 20
|
||||
|
||||
struct asn197ade_numberscreened {
|
||||
unsigned char *partyNumber;
|
||||
enum screeningInd { userProvidedNotScreened, userProvidedVerifiedAndPassed, userProvidedVerifiedAndFailed, networkProvided };
|
||||
};
|
||||
|
||||
extern unsigned int cc_qsig_asn197ade_get_partynumber(char *buf, int buflen, int *idx, unsigned char *data);
|
||||
extern unsigned int cc_qsig_asn197ade_get_numdigits(char *buf, int buflen, int *idx, unsigned char *data);
|
||||
|
||||
|
|
|
@ -483,6 +483,8 @@ static int ident_qsig_invoke(int invoketype)
|
|||
return CCQSIG__ECMA__NAMEPRES;
|
||||
case 4:
|
||||
return CCQSIG__ECMA__PRPROPOSE;
|
||||
case 12:
|
||||
return CCQSIG__ECMA__CTCOMPLETE;
|
||||
case 21:
|
||||
return CCQSIG__ECMA__LEGINFO2;
|
||||
default:
|
||||
|
@ -572,9 +574,19 @@ signed int cc_qsig_identifyinvoke(struct cc_qsig_invokedata *invoke, int protoco
|
|||
|
||||
}
|
||||
|
||||
static void pbx_capi_qsig_handle_ctc(struct cc_qsig_invokedata *invoke, struct capi_pvt *i)
|
||||
{
|
||||
char *destination = cc_qsig_decode_ecma_calltransfer(invoke, i);
|
||||
|
||||
if (destination) {
|
||||
free(destination);
|
||||
}
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Handle inbound INVOKEs
|
||||
*/
|
||||
unsigned int cc_qsig_handle_invokeoperation(int invokeident, struct cc_qsig_invokedata *invoke, struct capi_pvt *i)
|
||||
{
|
||||
|
@ -585,6 +597,9 @@ unsigned int cc_qsig_handle_invokeoperation(int invokeident, struct cc_qsig_invo
|
|||
case CCQSIG__ECMA__PRPROPOSE:
|
||||
cc_qsig_op_ecma_isdn_prpropose(invoke, i);
|
||||
break;
|
||||
case CCQSIG__ECMA__CTCOMPLETE:
|
||||
pbx_capi_qsig_handle_ctc(invoke, i);
|
||||
break;
|
||||
case CCQSIG__ECMA__LEGINFO2:
|
||||
cc_qsig_op_ecma_isdn_leginfo2(invoke, i);
|
||||
break;
|
||||
|
@ -647,6 +662,9 @@ unsigned int cc_qsig_handle_capiind(unsigned char *data, struct capi_pvt *i)
|
|||
int faclen = 0;
|
||||
int facidx = 2;
|
||||
|
||||
if (!i->qsigfeat)
|
||||
return 0;
|
||||
|
||||
if (!data) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -965,14 +983,14 @@ int pbx_capi_qsig_ct(struct ast_channel *c, char *param)
|
|||
cc_qsig_do_facility(fac, c, param, 12, 0);
|
||||
|
||||
capi_sendf(NULL, 0, CAPI_INFO_REQ, ii->PLCI, get_capi_MessageNumber(),
|
||||
"()(()()()s)",
|
||||
"()(()()()s())",
|
||||
fac
|
||||
);
|
||||
|
||||
cc_qsig_do_facility(fac, c, param, 12, 1);
|
||||
|
||||
capi_sendf(NULL, 0, CAPI_INFO_REQ, i->PLCI, get_capi_MessageNumber(),
|
||||
"()(()()()s)",
|
||||
"()(()()()s())",
|
||||
fac
|
||||
);
|
||||
|
||||
|
@ -980,7 +998,7 @@ int pbx_capi_qsig_ct(struct ast_channel *c, char *param)
|
|||
}
|
||||
|
||||
/*
|
||||
* Initiate a QSIG Call Transfer
|
||||
* mark an call
|
||||
*/
|
||||
int pbx_capi_qsig_callmark(struct ast_channel *c, char *param)
|
||||
{
|
||||
|
@ -1025,6 +1043,10 @@ static void send_feature_calltransfer(struct capi_pvt *i)
|
|||
*/
|
||||
void cc_qsig_interface_init(struct cc_capi_conf *conf, struct capi_pvt *tmp)
|
||||
{
|
||||
tmp->qsigfeat = conf->qsigfeat;
|
||||
if (!conf->qsigfeat)
|
||||
return;
|
||||
|
||||
tmp->qsig_data.calltransfer_active = 0;
|
||||
tmp->qsig_data.calltransfer = 0;
|
||||
tmp->qsig_data.calltransfer_onring = 0;
|
||||
|
@ -1088,6 +1110,8 @@ void interface_cleanup_qsig(struct capi_pvt *i)
|
|||
*/
|
||||
void pbx_capi_qsig_unload_module(struct capi_pvt *i)
|
||||
{
|
||||
if (!i->qsigfeat)
|
||||
return;
|
||||
ast_cond_destroy(&i->qsig_data.event_trigger);
|
||||
}
|
||||
|
||||
|
|
|
@ -144,12 +144,8 @@ int cc_qsig_encode_ecma_name_invoke(unsigned char * buf, unsigned int *idx, stru
|
|||
invoke->descr_type = -1; /* Let others do the work: qsig_add_invoke */
|
||||
invoke->type = 0; /* Invoke Operation Number, if OID it's the last byte*/
|
||||
|
||||
/* HACK: */
|
||||
if (nametype)
|
||||
invoke->type = 2;
|
||||
|
||||
if (namelen>0) {
|
||||
data[dataidx++] = 0x80; /* We send only simple Name, Namepresentation allowed */
|
||||
data[dataidx++] = 0x80 | (nametype % 4); /* We send only simple Name, Namepresentation allowed */
|
||||
data[dataidx++] = namelen;
|
||||
memcpy(&data[dataidx], namebuf, namelen);
|
||||
dataidx += namelen;
|
||||
|
@ -416,6 +412,131 @@ void cc_qsig_encode_ecma_calltransfer(unsigned char * buf, unsigned int *idx, st
|
|||
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode Operation: 1.3.12.9.12 ECMA/ISDN/CALLTRANSFER
|
||||
*
|
||||
* This function decodes the call transfer facility
|
||||
*
|
||||
* We create an invoke struct with the complete encoded invoke.
|
||||
*
|
||||
* parameters
|
||||
* buf is pointer to facility array, not used now
|
||||
* idx current idx in facility array, not used now
|
||||
* invoke struct, which contains encoded data from facility
|
||||
* i is pointer to capi channel
|
||||
* returns
|
||||
* transfer to destination number
|
||||
*/
|
||||
char *cc_qsig_decode_ecma_calltransfer(struct cc_qsig_invokedata *invoke, struct capi_pvt *i)
|
||||
{
|
||||
unsigned int datalength;
|
||||
unsigned int seqlength = 0;
|
||||
unsigned char *data = invoke->data;
|
||||
int myidx = 0;
|
||||
/* TODO: write more code */
|
||||
|
||||
char *ct_status_txt[] = { "ANSWERED", "ALERTING" };
|
||||
int ct_status = 0;
|
||||
char ct_target[ASN197ADE_NUMDIGITS_STRSIZE+1] = { 0 };
|
||||
char ct_name[ASN197NO_NAME_STRSIZE+1] = { "EMPTY" };
|
||||
int ct_enddesignation = 0;
|
||||
int ct_numberpresentation = 0;
|
||||
unsigned int namelength = 0;
|
||||
int temp = 0;
|
||||
|
||||
#define ct_err(x...) { cc_verbose(1, 1, VERBOSE_PREFIX_4 " * not Handling QSIG CALL TRANSFER - "x); return NULL; }
|
||||
|
||||
cc_verbose(1, 1, VERBOSE_PREFIX_4 "Handling QSIG CALL TRANSFER (id# %#x)\n", invoke->id);
|
||||
|
||||
if (data[myidx++] != (ASN1_SEQUENCE | ASN1_TC_UNIVERSAL | ASN1_TF_CONSTRUCTED)) { /* 0x30 */
|
||||
/* We do not handle this, because it should start with an sequence tag */
|
||||
ct_err("not a sequence\n");
|
||||
}
|
||||
|
||||
/* This facility is encoded as SEQUENCE */
|
||||
seqlength = data[myidx++];
|
||||
datalength = invoke->datalen;
|
||||
if (datalength < (seqlength+1)) {
|
||||
ct_err("buffer error\n");
|
||||
}
|
||||
|
||||
if (data[myidx++] == ASN1_ENUMERATED) {
|
||||
ct_enddesignation = cc_qsig_asn1_get_integer(data, &myidx);
|
||||
} else {
|
||||
ct_err("no endDesignation information.\n");
|
||||
}
|
||||
|
||||
if (data[myidx] == (ASN1_TC_CONTEXTSPEC | ASN1_TF_CONSTRUCTED | 0)) { /* Parameter 0: partyNumber - transferee number */
|
||||
myidx ++;
|
||||
|
||||
temp = cc_qsig_asn197ade_get_partynumber(ct_target, ASN197ADE_NUMDIGITS_STRSIZE+1, &myidx, data);
|
||||
|
||||
if (temp)
|
||||
myidx += temp + 1;
|
||||
|
||||
/* HACK: this has to be moved to _get_presentednumberscreened */
|
||||
if (data[myidx++] == ASN1_ENUMERATED) {
|
||||
ct_numberpresentation = cc_qsig_asn1_get_integer(data, &myidx);
|
||||
} else {
|
||||
ct_err("screeningIndicator is missing.\n");
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
myidx += 2; /* HACK: I've seen an IMPLICIT ade "numberNotAvailableDueToInterworking NULL" */
|
||||
}
|
||||
|
||||
/* TODO: remove this code snippet, when it's 100% working */
|
||||
/*
|
||||
if (data[myidx++] == (ASN1_TC_CONTEXTSPEC | ASN1_TF_CONSTRUCTED | 0)) { */ /* Parameter 0: partyNumber - transferee number */
|
||||
/* if (!data[myidx++]) /* length = 0 */
|
||||
/* ct_err("No destination received.\n");
|
||||
|
||||
if (data[myidx++] != ASN1_TC_CONTEXTSPEC)
|
||||
ct_err("No destination received.\n");
|
||||
|
||||
temp = cc_qsig_asn1_get_string((unsigned char*)&ct_target, sizeof(ct_target), &data[myidx]);
|
||||
|
||||
if (temp) {
|
||||
myidx += temp + 1;
|
||||
} else {
|
||||
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * not Handling QSIG CALL TRANSFER - partyNumber expected (%i)\n", myidx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (data[myidx++] == ASN1_ENUMERATED) {
|
||||
ct_numberpresentation = cc_qsig_asn1_get_integer(data, &myidx);
|
||||
} else {
|
||||
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * not Handling QSIG CALL TRANSFER - wrong encoded #2.\n");
|
||||
return NULL;
|
||||
}
|
||||
}*/
|
||||
|
||||
if (myidx < datalength) {
|
||||
if (data[myidx] == ASN1_TC_APPLICATION) {
|
||||
/* ignore special Application Parameters - have no info about content */
|
||||
myidx++;
|
||||
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * QSIG CALL TRANSFER - ignoring application data (%i bytes).\n", (int)data[myidx]);
|
||||
myidx += data[myidx] + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (myidx < datalength) {
|
||||
if (data[myidx] != ASN1_ENUMERATED) { /* Maybe we get an name (OPTIONAL) */
|
||||
myidx += cc_qsig_asn197no_get_name(ct_name, ASN197NO_NAME_STRSIZE+1, &namelength, &myidx, data );
|
||||
}
|
||||
}
|
||||
|
||||
if (myidx < datalength) {
|
||||
if (data[myidx++] == ASN1_ENUMERATED) { /* Call Status */
|
||||
ct_status = cc_qsig_asn1_get_integer(data, &myidx);
|
||||
}
|
||||
}
|
||||
|
||||
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Got QSIG CALL TRANSFER endDesignation: %i partyNumber: %s (ScreeningInd: %i), partyName: \"%s\", Call state: %s\n", ct_enddesignation, ct_target, ct_numberpresentation, ct_name, ct_status_txt[ct_status]);
|
||||
|
||||
return strdup(ct_target);
|
||||
#undef ct_err
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode Operation: 1.3.12.9.99 ECMA/ISDN/SINGLESTEPCALLTRANSFER
|
||||
|
|
Loading…
Reference in New Issue