- Added QSIG patch from Mario Goegel.
This commit is contained in:
parent
92c3f4afc6
commit
6c19246b9d
2
Makefile
2
Makefile
|
@ -91,7 +91,7 @@ INSTALL=install
|
||||||
|
|
||||||
SHAREDOS=chan_capi.so
|
SHAREDOS=chan_capi.so
|
||||||
|
|
||||||
OBJECTS=chan_capi.o c20msg.o chan_capi_rtp.o
|
OBJECTS=chan_capi.o c20msg.o chan_capi_rtp.o chan_capi_qsig_core.o chan_capi_qsig_ecma.o
|
||||||
|
|
||||||
CFLAGS+=-Wno-missing-prototypes -Wno-missing-declarations
|
CFLAGS+=-Wno-missing-prototypes -Wno-missing-declarations
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
(CAPI*) chan_capi a Common ISDN API 2.0 implementation
|
||||||
|
for Asterisk/OpenPBX
|
||||||
|
|
||||||
|
QSIG Extension for chan_capi
|
||||||
|
|
||||||
|
Copyright (C) 2005-2007 Cytronics & Melware
|
||||||
|
Armin Schindler <armin@melware.de>
|
||||||
|
|
||||||
|
Copyright (C) 2006-2007 Mario Goegel
|
||||||
|
Mario Goegel <m.goegel@gmx.de>
|
||||||
|
|
||||||
|
This program is free software and may be modified and distributed under
|
||||||
|
the terms of the GNU Public License. There is _NO_ warranty for this!
|
||||||
|
|
||||||
|
Thanks go to the debuggers, bugfixers and contributors :)
|
||||||
|
===========================================================================
|
||||||
|
None yet - you will be welcome here :-)
|
||||||
|
|
||||||
|
(...and all the others that have been forgotten...) :-)
|
||||||
|
|
||||||
|
No support for Asterisk 1.0.x any more, you need at least
|
||||||
|
Asterisk 1.2.x
|
||||||
|
|
||||||
|
What is Q.SIG
|
||||||
|
=============
|
||||||
|
|
||||||
|
Q.SIG is an protocoll extension for ISDN.
|
||||||
|
It is mainly used on connecting PBXs of different PBX vendors, which allows
|
||||||
|
better interoperability.
|
||||||
|
As example there can be a name of an extension transferred between different
|
||||||
|
PBXs, which is with standard ISDN not possible.
|
||||||
|
|
||||||
|
These extensions will be transmitted as encoded facility information elements.
|
||||||
|
To use Q.SIG with asterisk, you'll need a card like Eicon DIVA Server
|
||||||
|
(BRI like PRI), which supports QSIG. Maybe others do also work, let me now.
|
||||||
|
|
||||||
|
The QSIG support includes:
|
||||||
|
==========================
|
||||||
|
- Name presentation on Call SETUP incoming like outgoing
|
||||||
|
|
||||||
|
Future Targets:
|
||||||
|
===============
|
||||||
|
- better name handling - there are still some case which are wrongly handled
|
||||||
|
- check code for buffer overflows
|
||||||
|
- Path Replacement
|
||||||
|
- CCBS
|
||||||
|
- AOC
|
||||||
|
- ...
|
||||||
|
|
||||||
|
How to use:
|
||||||
|
===========
|
||||||
|
|
||||||
|
simply enable Q.SIG with following line in your capi.conf interface:
|
||||||
|
|
||||||
|
qsig=on
|
||||||
|
|
||||||
|
Take care that you enable this only for interfaces, where the other end
|
||||||
|
understands the Q.SIG protocoll. If not, then these switches may reject the
|
||||||
|
entire call, because of wrong facility contents.
|
||||||
|
|
||||||
|
Later this will change to qsig=off or qsig=1..x where we can support some
|
||||||
|
pbx manufacturer specific operations.
|
||||||
|
|
|
@ -67,4 +67,5 @@ devices=2 ;number of concurrent calls (b-channels) on this controller
|
||||||
;jb..... ;with Asterisk 1.4 you can configure jitterbuffer,
|
;jb..... ;with Asterisk 1.4 you can configure jitterbuffer,
|
||||||
;see Asterisk documentation for all jb* setting available.
|
;see Asterisk documentation for all jb* setting available.
|
||||||
;mohinterpret=default ;Asterisk 1.4: default music on hold class when placed on hold.
|
;mohinterpret=default ;Asterisk 1.4: default music on hold class when placed on hold.
|
||||||
|
;qsig=on ;enable use of Q.SIG extensions.
|
||||||
|
|
||||||
|
|
22
chan_capi.c
22
chan_capi.c
|
@ -89,6 +89,7 @@ OPENPBX_FILE_VERSION("$HeadURL$", "$Revision$")
|
||||||
#include "chan_capi20.h"
|
#include "chan_capi20.h"
|
||||||
#include "chan_capi.h"
|
#include "chan_capi.h"
|
||||||
#include "chan_capi_rtp.h"
|
#include "chan_capi_rtp.h"
|
||||||
|
#include "chan_capi_qsig.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef PBX_IS_OPBX
|
#ifdef PBX_IS_OPBX
|
||||||
|
@ -1454,6 +1455,7 @@ static int pbx_capi_call(struct ast_channel *c, char *idest, int timeout)
|
||||||
char *dsa = NULL;
|
char *dsa = NULL;
|
||||||
char callingsubaddress[AST_MAX_EXTENSION];
|
char callingsubaddress[AST_MAX_EXTENSION];
|
||||||
char calledsubaddress[AST_MAX_EXTENSION];
|
char calledsubaddress[AST_MAX_EXTENSION];
|
||||||
|
int doqsig;
|
||||||
|
|
||||||
_cmsg CMSG;
|
_cmsg CMSG;
|
||||||
MESSAGE_EXCHANGE_ERROR error;
|
MESSAGE_EXCHANGE_ERROR error;
|
||||||
|
@ -1465,6 +1467,7 @@ static int pbx_capi_call(struct ast_channel *c, char *idest, int timeout)
|
||||||
i->doB3 = CAPI_B3_DONT;
|
i->doB3 = CAPI_B3_DONT;
|
||||||
i->doOverlap = 0;
|
i->doOverlap = 0;
|
||||||
memset(i->overlapdigits, 0, sizeof(i->overlapdigits));
|
memset(i->overlapdigits, 0, sizeof(i->overlapdigits));
|
||||||
|
doqsig = i->doqsig;
|
||||||
|
|
||||||
/* parse the parameters */
|
/* parse the parameters */
|
||||||
while ((param) && (*param)) {
|
while ((param) && (*param)) {
|
||||||
|
@ -1489,6 +1492,11 @@ static int pbx_capi_call(struct ast_channel *c, char *idest, int timeout)
|
||||||
cc_log(LOG_WARNING, "Default CID already set in '%s'\n", idest);
|
cc_log(LOG_WARNING, "Default CID already set in '%s'\n", idest);
|
||||||
use_defaultcid = 1;
|
use_defaultcid = 1;
|
||||||
break;
|
break;
|
||||||
|
case 'q': /* disable QSIG */
|
||||||
|
cc_verbose(4, 0, VERBOSE_PREFIX_4 "%s: QSIG extensions for this call disabled\n",
|
||||||
|
i->vname);
|
||||||
|
doqsig = 0;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
cc_log(LOG_WARNING, "Unknown parameter '%c' in '%s', ignoring.\n",
|
cc_log(LOG_WARNING, "Unknown parameter '%c' in '%s', ignoring.\n",
|
||||||
*param, idest);
|
*param, idest);
|
||||||
|
@ -1577,6 +1585,12 @@ static int pbx_capi_call(struct ast_channel *c, char *idest, int timeout)
|
||||||
bchaninfo[2] = 0x0;
|
bchaninfo[2] = 0x0;
|
||||||
CONNECT_REQ_BCHANNELINFORMATION(&CMSG) = (_cstruct)bchaninfo; /* 0 */
|
CONNECT_REQ_BCHANNELINFORMATION(&CMSG) = (_cstruct)bchaninfo; /* 0 */
|
||||||
|
|
||||||
|
if (doqsig) {
|
||||||
|
char *facilityarray = alloca(CAPI_MAX_FACILITYDATAARRAY_SIZE);
|
||||||
|
cc_qsig_add_call_setup_data(facilityarray, i);
|
||||||
|
CONNECT_REQ_FACILITYDATAARRAY(&CMSG) = facilityarray;
|
||||||
|
}
|
||||||
|
|
||||||
cc_mutex_lock(&i->lock);
|
cc_mutex_lock(&i->lock);
|
||||||
|
|
||||||
i->outgoing = 1;
|
i->outgoing = 1;
|
||||||
|
@ -3914,6 +3928,11 @@ static void capidev_handle_connect_indication(_cmsg *CMSG, unsigned int PLCI, un
|
||||||
pbx_builtin_setvar_helper(i->owner, "ANI2", buffer);
|
pbx_builtin_setvar_helper(i->owner, "ANI2", buffer);
|
||||||
pbx_builtin_setvar_helper(i->owner, "SECONDCALLERID", buffer);
|
pbx_builtin_setvar_helper(i->owner, "SECONDCALLERID", buffer);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if (i->qsigfeat == QSIG_ENABLED) {
|
||||||
|
cc_qsig_handle_capiind(CONNECT_IND_FACILITYDATAARRAY(CMSG), i);
|
||||||
|
}
|
||||||
|
|
||||||
if ((i->isdnmode == CAPI_ISDNMODE_MSN) && (i->immediate)) {
|
if ((i->isdnmode == CAPI_ISDNMODE_MSN) && (i->immediate)) {
|
||||||
/* if we don't want to wait for SETUP/SENDING-COMPLETE in MSN mode */
|
/* if we don't want to wait for SETUP/SENDING-COMPLETE in MSN mode */
|
||||||
start_pbx_on_match(i, PLCI, HEADER_MSGNUM(CMSG));
|
start_pbx_on_match(i, PLCI, HEADER_MSGNUM(CMSG));
|
||||||
|
@ -5197,6 +5216,8 @@ int mkif(struct cc_capi_conf *conf)
|
||||||
tmp->doDTMF = conf->softdtmf;
|
tmp->doDTMF = conf->softdtmf;
|
||||||
tmp->capability = conf->capability;
|
tmp->capability = conf->capability;
|
||||||
|
|
||||||
|
tmp->qsigfeat = conf->qsigfeat;
|
||||||
|
|
||||||
tmp->next = iflist; /* prepend */
|
tmp->next = iflist; /* prepend */
|
||||||
iflist = tmp;
|
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",
|
cc_verbose(2, 0, VERBOSE_PREFIX_3 "capi %c %s (%s:%s) contr=%d devs=%d EC=%d,opt=%d,tail=%d\n",
|
||||||
|
@ -5797,6 +5818,7 @@ static int conf_interface(struct cc_capi_conf *conf, struct ast_variable *v)
|
||||||
CONF_TRUE(conf->es, "echosquelch", 1)
|
CONF_TRUE(conf->es, "echosquelch", 1)
|
||||||
CONF_TRUE(conf->bridge, "bridge", 1)
|
CONF_TRUE(conf->bridge, "bridge", 1)
|
||||||
CONF_TRUE(conf->ntmode, "ntmode", 1)
|
CONF_TRUE(conf->ntmode, "ntmode", 1)
|
||||||
|
CONF_TRUE(conf->qsigfeat, "qsig", 1)
|
||||||
if (!strcasecmp(v->name, "callgroup")) {
|
if (!strcasecmp(v->name, "callgroup")) {
|
||||||
conf->callgroup = ast_get_group(v->value);
|
conf->callgroup = ast_get_group(v->value);
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
|
|
||||||
#define RTP_HEADER_SIZE 12
|
#define RTP_HEADER_SIZE 12
|
||||||
|
|
||||||
|
#define CAPI_MAX_FACILITYDATAARRAY_SIZE 300
|
||||||
|
|
||||||
#ifndef CONNECT_RESP_GLOBALCONFIGURATION
|
#ifndef CONNECT_RESP_GLOBALCONFIGURATION
|
||||||
#define CC_HAVE_NO_GLOBALCONFIGURATION
|
#define CC_HAVE_NO_GLOBALCONFIGURATION
|
||||||
#warning If you dont update your libcapi20, some fax features are not available
|
#warning If you dont update your libcapi20, some fax features are not available
|
||||||
|
@ -374,6 +376,9 @@ struct capi_pvt {
|
||||||
int codec;
|
int codec;
|
||||||
unsigned int timestamp;
|
unsigned int timestamp;
|
||||||
|
|
||||||
|
/* Q.SIG features */
|
||||||
|
int qsigfeat;
|
||||||
|
|
||||||
/*! Next channel in list */
|
/*! Next channel in list */
|
||||||
struct capi_pvt *next;
|
struct capi_pvt *next;
|
||||||
};
|
};
|
||||||
|
@ -415,6 +420,7 @@ struct cc_capi_conf {
|
||||||
int es;
|
int es;
|
||||||
int bridge;
|
int bridge;
|
||||||
int amaflags;
|
int amaflags;
|
||||||
|
int qsigfeat;
|
||||||
unsigned int faxsetting;
|
unsigned int faxsetting;
|
||||||
ast_group_t callgroup;
|
ast_group_t callgroup;
|
||||||
ast_group_t pickupgroup;
|
ast_group_t pickupgroup;
|
||||||
|
@ -463,6 +469,7 @@ struct cc_capi_controller {
|
||||||
#define CAPI_ETSI_IE_CAUSE 0x08
|
#define CAPI_ETSI_IE_CAUSE 0x08
|
||||||
#define CAPI_ETSI_IE_PROGRESS_INDICATOR 0x1e
|
#define CAPI_ETSI_IE_PROGRESS_INDICATOR 0x1e
|
||||||
#define CAPI_ETSI_IE_CALLED_PARTY_NUMBER 0x70
|
#define CAPI_ETSI_IE_CALLED_PARTY_NUMBER 0x70
|
||||||
|
#define CAPI_ETSI_IE_FACILITY 0x1c
|
||||||
|
|
||||||
/* ETIS 300 102-1 message types */
|
/* ETIS 300 102-1 message types */
|
||||||
#define CAPI_ETSI_ALERTING 0x01
|
#define CAPI_ETSI_ALERTING 0x01
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
* (QSIG)
|
||||||
|
*
|
||||||
|
* Implementation of QSIG extensions for CHAN_CAPI
|
||||||
|
*
|
||||||
|
* Copyright 2006-2007 (c) Mario Goegel
|
||||||
|
*
|
||||||
|
* Mario Goegel <m.goegel@gmx.de>
|
||||||
|
*
|
||||||
|
* This program is free software and may be modified and
|
||||||
|
* distributed under the terms of the GNU Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PBX_QSIG_H
|
||||||
|
#define PBX_QSIG_H
|
||||||
|
|
||||||
|
#define QSIG_DISABLED 0x00
|
||||||
|
#define QSIG_ENABLED 0x01
|
||||||
|
|
||||||
|
#define QSIG_TYPE_DEFAULT 0x00 /* use only common features */
|
||||||
|
#define QSIG_TYPE_ALCATEL 0x01 /* use additional Alcatel features */
|
||||||
|
#define QSIG_TYPE_HICOM 0x02 /* use additional Hicom features */
|
||||||
|
|
||||||
|
#define Q932_PROTOCOL_ROSE 0x11 /* X.219 & X.229 */
|
||||||
|
#define Q932_PROTOCOL_CMIP 0x12 /* Q.941 */
|
||||||
|
#define Q932_PROTOCOL_ACSE 0x13 /* X.217 & X.227 */
|
||||||
|
#define Q932_PROTOCOL_GAT 0x16
|
||||||
|
#define Q932_PROTOCOL_EXTENSIONS 0x1F
|
||||||
|
|
||||||
|
#define COMP_TYPE_INVOKE 0xa1 /* Invoke component */
|
||||||
|
#define COMP_TYPE_DISCR_SS 0x91 /* Supplementary service descriptor - ROSE PROTOCOL */
|
||||||
|
#define COMP_TYPE_NFE 0xaa /* Network Facility Extensions (ECMA-165) */
|
||||||
|
#define COMP_TYPE_APDU_INTERP 0x8b /* APDU Interpration Type (0 DISCARD, 1 CLEARCALL-IF-UNKNOWN, 2 REJECT-APDU) */
|
||||||
|
#define COMP_TYPE_RETURN_RESULT 0xA2
|
||||||
|
#define COMP_TYPE_RETURN_ERROR 0xA3
|
||||||
|
#define COMP_TYPE_REJECT 0xA4
|
||||||
|
|
||||||
|
#define APDUINTERPRETATION_IGNORE 0x00
|
||||||
|
#define APDUINTERPRETATION_CLEARCALL 0x01
|
||||||
|
#define APDUINTERPRETATION_REJECT 0x02
|
||||||
|
|
||||||
|
/* ASN.1 Identifier Octet - Data types */
|
||||||
|
#define ASN1_TYPE_MASK 0x1f
|
||||||
|
#define ASN1_BOOLEAN 0x01
|
||||||
|
#define ASN1_INTEGER 0x02
|
||||||
|
#define ASN1_BITSTRING 0x03
|
||||||
|
#define ASN1_OCTETSTRING 0x04
|
||||||
|
#define ASN1_NULL 0x05
|
||||||
|
#define ASN1_OBJECTIDENTIFIER 0x06
|
||||||
|
#define ASN1_OBJECTDESCRIPTOR 0x07
|
||||||
|
#define ASN1_EXTERN 0x08
|
||||||
|
#define ASN1_REAL 0x09
|
||||||
|
#define ASN1_ENUMERATED 0x0a
|
||||||
|
#define ASN1_EMBEDDEDPDV 0x0b
|
||||||
|
#define ASN1_UTF8STRING 0x0c
|
||||||
|
#define ASN1_RELATIVEOBJECTID 0x0d
|
||||||
|
/* 0x0e & 0x0f are reserved for future ASN.1 editions */
|
||||||
|
#define ASN1_SEQUENCE 0x10
|
||||||
|
#define ASN1_SET 0x11
|
||||||
|
#define ASN1_NUMERICSTRING 0x12
|
||||||
|
#define ASN1_PRINTABLESTRING 0x13
|
||||||
|
#define ASN1_TELETEXSTRING 0x14
|
||||||
|
#define ASN1_IA5STRING 0x16
|
||||||
|
#define ASN1_UTCTIME 0x17
|
||||||
|
#define ASN1_GENERALIZEDTIME 0x18
|
||||||
|
|
||||||
|
|
||||||
|
#define CNIP_CALLINGNAME 0x00 /* Name-Types defined in ECMA-164 */
|
||||||
|
#define CNIP_CALLEDNAME 0x01
|
||||||
|
#define CNIP_CONNECTEDNAME 0x02
|
||||||
|
#define CNIP_BUSYNAME 0x03
|
||||||
|
|
||||||
|
|
||||||
|
#define CNIP_NAMEPRESALLOW 0x80
|
||||||
|
#define CNIP_NAMEPRESRESTR 0xA0
|
||||||
|
#define CNIP_NAMEPRESUNAVAIL 0xC0
|
||||||
|
|
||||||
|
#define CNIP_NAMEUSERPROVIDED 0x00 /* Name is User-provided, unvalidated */
|
||||||
|
#define CNIP_NAMEUSERPROVIDEDV 0x01 /* Name is User-provided and validated */
|
||||||
|
|
||||||
|
#define CCQSIG__ECMA__NAMEPRES 1000 /* Setting an own constant for ECMA Operation/Namepresentation, others will follow */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* INVOKE Data struct, contains data for further operations
|
||||||
|
*/
|
||||||
|
struct cc_qsig_invokedata {
|
||||||
|
int len; /* invoke length */
|
||||||
|
int offset; /* where does the invoke start in facility array */
|
||||||
|
int id; /* id from sent Invoke Number */
|
||||||
|
int apdu_interpr; /* What To Do with unknown Operation? */
|
||||||
|
int descr_type; /* component descriptor is of ASN.1 Datatype (0x02 Integer, 0x06 Object Identifier) */
|
||||||
|
int type; /* when component is Integer */
|
||||||
|
int oid_len;
|
||||||
|
unsigned char oid_bin[20]; /* when component is Object Identifier then save here the binary oid */
|
||||||
|
int datalen; /* invoke struct len */
|
||||||
|
unsigned char data[255]; /* invoke */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NFE Entity Address - contains destination informations for following INVOKE's
|
||||||
|
*/
|
||||||
|
struct cc_qsig_entityaddr { /* In case of AnyPINX */
|
||||||
|
int partynum; /* private,public,etc a5=private */
|
||||||
|
int ton; /* Type of Number */
|
||||||
|
unsigned char *num; /* EntityNumber */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Network Facility Extensions struct - to which pbx does the INVOKE belong to
|
||||||
|
*/
|
||||||
|
struct cc_qsig_nfe {
|
||||||
|
int src_entity; /* Call is coming from PBX (End|Any) */
|
||||||
|
int dst_entity; /* Call destination is */
|
||||||
|
struct cc_qsig_entityaddr src_addr; /* additional infos (PBX identifier) */
|
||||||
|
struct cc_qsig_entityaddr dst_addr; /* same here for destination */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************** QSIG Core Functions */
|
||||||
|
|
||||||
|
/* Create an default QSIG Facility Array */
|
||||||
|
extern int cc_qsig_build_facility_struct(unsigned char * buf, unsigned int *idx, int apdu_interpr, struct cc_qsig_nfe *nfe);
|
||||||
|
|
||||||
|
extern int cc_qsig_add_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke);
|
||||||
|
|
||||||
|
/* Returns an String from ASN.1 Encoded String */
|
||||||
|
extern unsigned int cc_qsig_asn1_get_string(unsigned char *buf, int buflen, unsigned char *data);
|
||||||
|
|
||||||
|
/* Returns an Integer from ASN.1 Encoded Integer */
|
||||||
|
extern unsigned int cc_qsig_asn1_get_integer(unsigned char *data, int *idx);
|
||||||
|
|
||||||
|
/* Returns an Human Readable OID from ASN.1 Encoded OID */
|
||||||
|
extern unsigned char cc_qsig_asn1_get_oid(unsigned char *data, int *idx);
|
||||||
|
|
||||||
|
|
||||||
|
/* Check if OID is ECMA-ISDN (1.3.12.9.*) */
|
||||||
|
extern signed int cc_qsig_asn1_check_ecma_isdn_oid(unsigned char *data, int len);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Valid QSIG-Facility?
|
||||||
|
* Returns 0 if not
|
||||||
|
*/
|
||||||
|
extern unsigned int cc_qsig_check_facility(unsigned char *data, int *idx, int *apduval);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Is this an INVOKE component?
|
||||||
|
* when not return -1, set idx to next byte (length of component?)
|
||||||
|
* *** check idx in this case, that we are not out of range - maybe we got an unknown component then
|
||||||
|
* when it is an invoke, return invoke length and set idx to first byte of component
|
||||||
|
*/
|
||||||
|
extern signed int cc_qsig_check_invoke(unsigned char *data, int *idx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get Invoke ID
|
||||||
|
* returns current index
|
||||||
|
* idx points to next byte in array
|
||||||
|
*/
|
||||||
|
extern signed int cc_qsig_get_invokeid(unsigned char *data, int *idx, struct cc_qsig_invokedata *invoke);
|
||||||
|
|
||||||
|
/* fill the Invoke struct with all the invoke data */
|
||||||
|
extern signed int cc_qsig_fill_invokestruct(unsigned char *data, int *idx, struct cc_qsig_invokedata *invoke, int apduval);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handles incoming Facilities on CAPI Indications
|
||||||
|
*/
|
||||||
|
extern unsigned int cc_qsig_handle_capiind(unsigned char *data, struct capi_pvt *i);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handles outgoing Facilies on Call SETUP
|
||||||
|
*/
|
||||||
|
extern unsigned int cc_qsig_add_call_setup_data(unsigned char *data, struct capi_pvt *i);
|
||||||
|
|
||||||
|
/* Identify an INVOKE and return our own Ident Integer (CCQSIG__*) */
|
||||||
|
extern signed int cc_qsig_identifyinvoke(struct cc_qsig_invokedata *invoke);
|
||||||
|
|
||||||
|
extern unsigned int cc_qsig_handle_invokeoperation(int invokeident, struct cc_qsig_invokedata *invoke, struct capi_pvt *i);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ECMA QSIG Functions */
|
||||||
|
|
||||||
|
/* Handle Operation: 1.3.12.9.0-3 ECMA/ISDN/NAMEPRESENTATION */
|
||||||
|
extern void cc_qsig_op_ecma_isdn_namepres(struct cc_qsig_invokedata *invoke, struct capi_pvt *i);
|
||||||
|
|
||||||
|
extern int cc_qsig_encode_ecma_name_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,474 @@
|
||||||
|
/*
|
||||||
|
* (CAPI*)
|
||||||
|
*
|
||||||
|
* An implementation of Common ISDN API 2.0 for
|
||||||
|
* Asterisk / OpenPBX.org
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005-2007 Cytronics & Melware
|
||||||
|
* Copyright (C) 2007 Mario Goegel
|
||||||
|
*
|
||||||
|
* Armin Schindler <armin@melware.de>
|
||||||
|
* Mario Goegel <m.goegel@gmx.de>
|
||||||
|
*
|
||||||
|
* This program is free software and may be modified and
|
||||||
|
* distributed under the terms of the GNU Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <asterisk/channel.h>
|
||||||
|
#include <asterisk/options.h>
|
||||||
|
#include "chan_capi20.h"
|
||||||
|
#include "chan_capi.h"
|
||||||
|
#include "chan_capi_qsig.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Returns an String from ASN.1 Encoded String
|
||||||
|
unsigned int cc_qsig_asn1_add_string(unsigned char *buf, int *idx, char *data, int datalen)
|
||||||
|
{
|
||||||
|
int myidx=*idx;
|
||||||
|
|
||||||
|
if ((1 + datalen + (*idx) ) > sizeof(*buf)) {
|
||||||
|
// String exceeds buffer size
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[myidx++] = datalen;
|
||||||
|
memcpy(&buf[myidx], data, datalen);
|
||||||
|
myidx += 1 + datalen;
|
||||||
|
|
||||||
|
*idx = myidx;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns an String from ASN.1 Encoded String
|
||||||
|
unsigned int cc_qsig_asn1_get_string(unsigned char *buf, int buflen, unsigned char *data)
|
||||||
|
{
|
||||||
|
int strsize;
|
||||||
|
int myidx=0;
|
||||||
|
|
||||||
|
strsize = data[myidx++];
|
||||||
|
if (strsize > buflen)
|
||||||
|
strsize = buflen - 1;
|
||||||
|
memcpy(buf, &data[myidx], strsize);
|
||||||
|
// cc_verbose(1, 1, VERBOSE_PREFIX_4 " get_string length %i\n", strsize);
|
||||||
|
return strsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int cc_qsig_asn1_add_integer(unsigned char *buf, int *idx, int value)
|
||||||
|
{
|
||||||
|
int myidx = *idx;
|
||||||
|
int intlen = 1;
|
||||||
|
|
||||||
|
if ((unsigned int)value > (unsigned int)0xFFFF)
|
||||||
|
return -1; // no support at the moment
|
||||||
|
|
||||||
|
if (value > 255)
|
||||||
|
intlen++; // we need 2 bytes
|
||||||
|
|
||||||
|
buf[myidx++] = ASN1_INTEGER;
|
||||||
|
buf[myidx++] = intlen;
|
||||||
|
if (intlen > 1) {
|
||||||
|
buf[myidx++] = (unsigned char)(value >> 8);
|
||||||
|
buf[myidx++] = (unsigned char)(value - 0xff00);
|
||||||
|
} else {
|
||||||
|
buf[myidx++] = (unsigned char)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
*idx = myidx;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns an Integer from ASN.1 Encoded Integer
|
||||||
|
unsigned int cc_qsig_asn1_get_integer(unsigned char *data, int *idx)
|
||||||
|
{ // TODO: not conform with negative Integers
|
||||||
|
int myidx = *idx;
|
||||||
|
int intlen;
|
||||||
|
int temp;
|
||||||
|
|
||||||
|
intlen = data[myidx++];
|
||||||
|
if ((intlen < 1) || (intlen > 2)) { // i don't know if there's a bigger Integer as 16bit -> read specs
|
||||||
|
cc_verbose(1, 1, VERBOSE_PREFIX_3 "ASN1Decode: Size of ASN.1 Integer not supported: %i\n", intlen);
|
||||||
|
*idx = myidx + intlen;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp = (char)data[myidx++];
|
||||||
|
if (intlen == 2) {
|
||||||
|
temp=(temp << 8) + data[myidx++];
|
||||||
|
}
|
||||||
|
|
||||||
|
*idx = myidx;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns an Human Readable OID from ASN.1 Encoded OID
|
||||||
|
unsigned char cc_qsig_asn1_get_oid(unsigned char *data, int *idx)
|
||||||
|
{
|
||||||
|
// TODO: Add code
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check if OID is ECMA-ISDN (1.3.12.9.*)
|
||||||
|
signed int cc_qsig_asn1_check_ecma_isdn_oid(unsigned char *data, int len)
|
||||||
|
{
|
||||||
|
// 1.3 .12 .9
|
||||||
|
if ((data[0] == 0x2B) && (data[1] == 0x0C) && (data[2] == 0x09))
|
||||||
|
return 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This function simply updates the length informations of the facility struct
|
||||||
|
void cc_qsig_update_facility_length(unsigned char * buf, unsigned int idx)
|
||||||
|
{
|
||||||
|
buf[0] = idx;
|
||||||
|
buf[2] = idx-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Invoke Struct
|
||||||
|
int cc_qsig_build_facility_struct(unsigned char * buf, unsigned int *idx, int apdu_interpr, struct cc_qsig_nfe *nfe)
|
||||||
|
{
|
||||||
|
int myidx = 1; // we start with Index 1 - Byte 0 is Length of Facilitydataarray
|
||||||
|
|
||||||
|
buf[myidx++] = 0x1c;
|
||||||
|
buf[myidx++] = 0; // Byte 2 length of Facilitydataarray
|
||||||
|
buf[myidx++] = COMP_TYPE_DISCR_SS; // QSIG Facility
|
||||||
|
// TODO: Outsource following struct to an separate function
|
||||||
|
buf[myidx++] = COMP_TYPE_NFE; // Network Facility Extension
|
||||||
|
buf[myidx++] = 6; // NFE Size hardcoded - not good
|
||||||
|
buf[myidx++] = 0x80; // Source Entity
|
||||||
|
buf[myidx++] = 0x01;
|
||||||
|
buf[myidx++] = 0x00; // End PINX hardcoded
|
||||||
|
buf[myidx++] = 0x82; // Dest. Entity
|
||||||
|
buf[myidx++] = 0x01;
|
||||||
|
buf[myidx++] = 0x00; // End PINX hardcoded
|
||||||
|
buf[myidx++] = COMP_TYPE_APDU_INTERP; // How to interpret this APDU
|
||||||
|
buf[myidx++] = 0x01; // Length
|
||||||
|
buf[myidx++] = apdu_interpr;
|
||||||
|
// Here will follow now the Invoke
|
||||||
|
*idx = myidx;
|
||||||
|
cc_qsig_update_facility_length(buf, myidx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Add invoke to buf
|
||||||
|
int cc_qsig_add_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke)
|
||||||
|
{
|
||||||
|
int myidx = *idx;
|
||||||
|
int invlenidx;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
buf[myidx++] = COMP_TYPE_INVOKE;
|
||||||
|
invlenidx = myidx; // save the Invoke length index for later
|
||||||
|
buf[myidx++] = 0;
|
||||||
|
|
||||||
|
result = cc_qsig_asn1_add_integer(buf, &myidx, invoke->id);
|
||||||
|
if (result) {
|
||||||
|
cc_log(LOG_ERROR, "QSIG: Cannot add invoke, identifier is not encoded!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (invoke->descr_type) {
|
||||||
|
case ASN1_INTEGER:
|
||||||
|
result = cc_qsig_asn1_add_integer(buf, &myidx, invoke->type);
|
||||||
|
if (result) {
|
||||||
|
cc_log(LOG_ERROR, "QSIG: Cannot add invoke, identifier is not encoded!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ASN1_OBJECTIDENTIFIER:
|
||||||
|
if ((invoke->oid_len < 1) || (invoke->oid_len > 20)) {
|
||||||
|
cc_log(LOG_ERROR, "QSIG: Cannot add invoke, OID is too big!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
buf[myidx++] = ASN1_OBJECTIDENTIFIER;
|
||||||
|
buf[myidx++] = invoke->oid_len;
|
||||||
|
memcpy(&buf[myidx], invoke->oid_bin, invoke->oid_len);
|
||||||
|
myidx += invoke->oid_len;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unknown Invoke Type, not encoded (%i)\n", invoke->descr_type);
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invoke->datalen > 0) { // may be no error, if there's no data
|
||||||
|
memcpy(&buf[myidx], invoke->data, invoke->datalen);
|
||||||
|
myidx += invoke->datalen;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[invlenidx] = myidx-1;
|
||||||
|
cc_qsig_update_facility_length(buf, myidx - 1);
|
||||||
|
*idx = myidx;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Valid QSIG-Facility?
|
||||||
|
// Returns 0 if not
|
||||||
|
unsigned int cc_qsig_check_facility(unsigned char *data, int *idx, int *apduval)
|
||||||
|
{
|
||||||
|
int myidx = *idx;
|
||||||
|
|
||||||
|
// First byte after Facility Length
|
||||||
|
if (data[myidx] == (unsigned char)(0x80 | Q932_PROTOCOL_ROSE)) {
|
||||||
|
myidx++;
|
||||||
|
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: ROSE Supplementary Services\n");
|
||||||
|
if (data[myidx++] == (unsigned char)COMP_TYPE_NFE) {
|
||||||
|
// Todo: Check Entities?
|
||||||
|
myidx = myidx + data[myidx] + 1;
|
||||||
|
// cc_verbose(1, 1, VERBOSE_PREFIX_3 "CONNECT_IND (idc #1 %i)\n",idx);
|
||||||
|
if ((data[myidx++] == (unsigned char)COMP_TYPE_APDU_INTERP)) {
|
||||||
|
myidx = myidx + data[myidx];
|
||||||
|
*apduval = data[myidx];
|
||||||
|
// ToDo: implement real reject or clear call ?
|
||||||
|
*idx = ++myidx;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is this an INVOKE component?
|
||||||
|
// when not return -1, set idx to next byte (length of component?)
|
||||||
|
// *** check idx in this case, that we are not out of range - maybe we got an unknown component then
|
||||||
|
// when it is an invoke, return invoke length and set idx to first byte of component
|
||||||
|
signed int cc_qsig_check_invoke(unsigned char *data, int *idx)
|
||||||
|
{
|
||||||
|
int myidx = *idx;
|
||||||
|
|
||||||
|
if (data[myidx] == (unsigned char)COMP_TYPE_INVOKE) {
|
||||||
|
// is an INVOKE_IDENT
|
||||||
|
*idx = myidx + 1; // Set index to first byte of component
|
||||||
|
// cc_verbose(1, 1, VERBOSE_PREFIX_4 "CONNECT_IND (Invoke Length %i)\n", data[myidx+1]);
|
||||||
|
return data[myidx + 1]; // return component length
|
||||||
|
}
|
||||||
|
*idx = ++myidx;
|
||||||
|
return -1; // what to do now? got no Invoke
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Invoke ID
|
||||||
|
// returns current index
|
||||||
|
// idx points to next byte in array
|
||||||
|
signed int cc_qsig_get_invokeid(unsigned char *data, int *idx, struct cc_qsig_invokedata *invoke)
|
||||||
|
{
|
||||||
|
int myidx;
|
||||||
|
int invidtype = 0;
|
||||||
|
int invlen = 0;
|
||||||
|
int invoffset;
|
||||||
|
int temp = 0;
|
||||||
|
|
||||||
|
myidx = *idx;
|
||||||
|
invoffset = myidx;
|
||||||
|
invlen = data[myidx++];
|
||||||
|
if (invlen > 0) {
|
||||||
|
invoke->len = invlen; // set Length of Invoke struct
|
||||||
|
invoke->offset = invoffset; // offset in Facility Array, where the Invoke Data starts
|
||||||
|
invidtype = data[myidx++]; // Get INVOKE Id Type
|
||||||
|
if (invidtype != ASN1_INTEGER) {
|
||||||
|
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unknown Invoke Identifier Type 0x%#x\n", invidtype);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*idx = myidx;
|
||||||
|
temp = cc_qsig_asn1_get_integer(data, idx);
|
||||||
|
invoke->id = temp;
|
||||||
|
// cc_verbose(1, 1, VERBOSE_PREFIX_3 "CONNECT_IND (Invoke Identifier %#x)\n", temp);
|
||||||
|
// *idx=myidx; // Set by cc_qsig_asn1get_integer
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill the Invoke struct with all the invoke data
|
||||||
|
signed int cc_qsig_fill_invokestruct(unsigned char *data, int *idx, struct cc_qsig_invokedata *invoke, int apduval)
|
||||||
|
{
|
||||||
|
int myidx = *idx;
|
||||||
|
int invoptyp;
|
||||||
|
int temp;
|
||||||
|
int temp2;
|
||||||
|
int datalen;
|
||||||
|
|
||||||
|
invoptyp = data[myidx++]; // Invoke Operation Type 0x02=INTEGER, 0x06=OID
|
||||||
|
switch (invoptyp) {
|
||||||
|
case ASN1_INTEGER:
|
||||||
|
invoke->apdu_interpr = apduval;
|
||||||
|
temp = cc_qsig_asn1_get_integer(data, idx);
|
||||||
|
invoke->descr_type = ASN1_INTEGER;
|
||||||
|
invoke->type = temp;
|
||||||
|
myidx++; // component length
|
||||||
|
//datalen=data[myidx++]; // maybe correct, better we calculate the datalength
|
||||||
|
temp2 = (invoke->len) + (invoke->offset) + 1; // Array End = Invoke Length + Invoke Offset +1
|
||||||
|
datalen = temp2 - myidx;
|
||||||
|
|
||||||
|
if (datalen > 255) {
|
||||||
|
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unsupported INVOKE Operation Size (max 255 Bytes): %i\n", datalen);
|
||||||
|
datalen = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
invoke->datalen = datalen;
|
||||||
|
memcpy(invoke->data, &data[myidx], datalen); // copy data of Invoke Operation
|
||||||
|
myidx = myidx + datalen; // points to next INVOKE component, if there's any
|
||||||
|
*idx = myidx;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASN1_OBJECTIDENTIFIER:
|
||||||
|
invoke->apdu_interpr = apduval;
|
||||||
|
invoke->descr_type = ASN1_OBJECTIDENTIFIER;
|
||||||
|
temp = data[myidx++]; // Length of OID
|
||||||
|
if (temp > 20) {
|
||||||
|
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unsupported INVOKE Operation OID Size (max 20 Bytes): %i\n", temp);
|
||||||
|
temp = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Maybe we decode the OID here and be verbose - have to write cc_qsig_asn1get_oid
|
||||||
|
|
||||||
|
// cc_verbose(1, 1, VERBOSE_PREFIX_3 "CONNECT_IND (OID, Length %i)\n", temp);
|
||||||
|
invoke->oid_len = temp;
|
||||||
|
memcpy(invoke->oid_bin, &data[myidx], temp); // Copy OID to separate array
|
||||||
|
myidx = myidx + temp; // Set index to next information
|
||||||
|
|
||||||
|
temp2 = (invoke->len) + (invoke->offset) + 1; // Array End = Invoke Length + Invoke Offset +1
|
||||||
|
datalen = temp2 - myidx;
|
||||||
|
|
||||||
|
if (datalen > 255) {
|
||||||
|
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unsupported INVOKE Operation Size (max 255 Bytes): %i\n", datalen);
|
||||||
|
datalen = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cc_verbose(1, 1, VERBOSE_PREFIX_3 "CONNECT_IND (OID, Datalength %i)\n",datalen);
|
||||||
|
invoke->datalen = datalen;
|
||||||
|
memcpy(invoke->data, &data[myidx], datalen); // copy data of Invoke Operation
|
||||||
|
myidx = myidx + datalen; // points to next INVOKE component, if there's any
|
||||||
|
*idx = myidx;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unknown INVOKE Operation Type: %i\n", invoptyp);
|
||||||
|
|
||||||
|
temp2 = (invoke->len) + (invoke->offset) + 1; // Array End = Invoke Length + Invoke Offset +1
|
||||||
|
datalen = temp2 - myidx;
|
||||||
|
|
||||||
|
if (datalen > 255) {
|
||||||
|
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unsupported INVOKE Operation Size (max 255 Bytes): %i\n", datalen);
|
||||||
|
datalen = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
*idx = datalen; // Set index to next INVOKE, if there's any
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0; // No problems
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identify an INVOKE and return our own Ident Integer (CCQSIG__*)
|
||||||
|
|
||||||
|
signed int cc_qsig_identifyinvoke(struct cc_qsig_invokedata *invoke)
|
||||||
|
{
|
||||||
|
int invokedescrtype = 0;
|
||||||
|
int datalen;
|
||||||
|
|
||||||
|
// cc_verbose(1, 1, VERBOSE_PREFIX_4 "CONNECT_IND (Ident Invoke %i)\n", invoke->descr_type);
|
||||||
|
|
||||||
|
switch (invoke->descr_type) {
|
||||||
|
case ASN1_INTEGER:
|
||||||
|
invokedescrtype = 1;
|
||||||
|
break;
|
||||||
|
case ASN1_OBJECTIDENTIFIER:
|
||||||
|
invokedescrtype = 2;
|
||||||
|
datalen = invoke->oid_len;
|
||||||
|
if ((datalen) == 4) {
|
||||||
|
if (!cc_qsig_asn1_check_ecma_isdn_oid(invoke->oid_bin, datalen)) {
|
||||||
|
switch (invoke->oid_bin[3]) {
|
||||||
|
case 0: // ECMA QSIG Name Presentation
|
||||||
|
return CCQSIG__ECMA__NAMEPRES;
|
||||||
|
default: // Unknown Operation
|
||||||
|
cc_verbose(1, 1, VERBOSE_PREFIX_4 "QSIG: Unhandled ECMA-ISDN QSIG INVOKE (%i)\n", invoke->oid_bin[3]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unidentified INVOKE OP\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int cc_qsig_handle_invokeoperation(int invokeident, struct cc_qsig_invokedata *invoke, struct capi_pvt *i)
|
||||||
|
{
|
||||||
|
switch (invokeident) {
|
||||||
|
case CCQSIG__ECMA__NAMEPRES:
|
||||||
|
cc_qsig_op_ecma_isdn_namepres(invoke, i);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handles incoming Indications from CAPI
|
||||||
|
*/
|
||||||
|
unsigned int cc_qsig_handle_capiind(unsigned char *data, struct capi_pvt *i)
|
||||||
|
{
|
||||||
|
int faclen;
|
||||||
|
int facidx = 2;
|
||||||
|
int action_unkn_apdu; // What to do with unknown Invoke-APDUs (0=Ignore, 1=clear call, 2=reject APDU)
|
||||||
|
|
||||||
|
int invoke_len; // Length of Invoke APDU
|
||||||
|
unsigned int invoke_op; // Invoke Operation ID
|
||||||
|
struct cc_qsig_invokedata invoke;
|
||||||
|
int invoketmp1;
|
||||||
|
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
faclen=data[facidx];
|
||||||
|
// cc_verbose(1, 1, VERBOSE_PREFIX_3 "CONNECT_IND (Got Facility IE, Length=%#x)\n", faclen);
|
||||||
|
facidx++;
|
||||||
|
if (cc_qsig_check_facility(data, &facidx, &action_unkn_apdu)) {
|
||||||
|
// cc_verbose(1, 1, VERBOSE_PREFIX_3 "CONNECT_IND ROSE Supplementary Services (APDU Interpretation: %i)\n", action_unkn_apdu);
|
||||||
|
while ((facidx-1)<faclen) {
|
||||||
|
invoke_len=cc_qsig_check_invoke(data, &facidx);
|
||||||
|
if (invoke_len>0) {
|
||||||
|
if (cc_qsig_get_invokeid(data, &facidx, &invoke)==0) {
|
||||||
|
invoketmp1=cc_qsig_fill_invokestruct(data, &facidx, &invoke, action_unkn_apdu);
|
||||||
|
invoke_op=cc_qsig_identifyinvoke(&invoke);
|
||||||
|
cc_qsig_handle_invokeoperation(invoke_op, &invoke, i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Not an Invoke
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handles outgoing Facilies on Call SETUP
|
||||||
|
*/
|
||||||
|
unsigned int cc_qsig_add_call_setup_data(unsigned char *data, struct capi_pvt *i)
|
||||||
|
{
|
||||||
|
/* TODO: Check buffers */
|
||||||
|
struct cc_qsig_invokedata invoke;
|
||||||
|
struct cc_qsig_nfe nfe;
|
||||||
|
unsigned int dataidx;
|
||||||
|
|
||||||
|
cc_qsig_build_facility_struct(data, &dataidx, APDUINTERPRETATION_IGNORE, &nfe);
|
||||||
|
cc_qsig_encode_ecma_name_invoke(data, &dataidx, &invoke, i);
|
||||||
|
cc_qsig_add_invoke(data, &dataidx, &invoke);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* (CAPI*)
|
||||||
|
*
|
||||||
|
* An implementation of Common ISDN API 2.0 for
|
||||||
|
* Asterisk / OpenPBX.org
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005-2007 Cytronics & Melware
|
||||||
|
* Copyright (C) 2007 Mario Goegel
|
||||||
|
*
|
||||||
|
* Armin Schindler <armin@melware.de>
|
||||||
|
* Mario Goegel <m.goegel@gmx.de>
|
||||||
|
*
|
||||||
|
* This program is free software and may be modified and
|
||||||
|
* distributed under the terms of the GNU Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <asterisk/channel.h>
|
||||||
|
#include <asterisk/options.h>
|
||||||
|
#include "chan_capi20.h"
|
||||||
|
#include "chan_capi.h"
|
||||||
|
#include "chan_capi_qsig.h"
|
||||||
|
|
||||||
|
/* Handle Operation: 1.3.12.9.0-3 ECMA/ISDN/NAMEPRESENTATION */
|
||||||
|
void cc_qsig_op_ecma_isdn_namepres(struct cc_qsig_invokedata *invoke, struct capi_pvt *i)
|
||||||
|
{
|
||||||
|
char callername[50]; /* ECMA defines max length to 50 */
|
||||||
|
unsigned int namelength = 0;
|
||||||
|
unsigned int namesetlength = 0;
|
||||||
|
unsigned int charset = 1;
|
||||||
|
unsigned int namepres;
|
||||||
|
unsigned int nametype;
|
||||||
|
unsigned int datalength;
|
||||||
|
int myidx = 0;
|
||||||
|
|
||||||
|
cc_verbose(1, 1, VERBOSE_PREFIX_4 "Handling Name Operation (id# %#x)\n", invoke->id);
|
||||||
|
|
||||||
|
datalength = invoke->datalen;
|
||||||
|
namepres = (invoke->data[myidx] & 0xF0); /* Name Presentation or Restriction */
|
||||||
|
nametype = (invoke->data[myidx++] & 0x0F); /* Type of Name-Struct */
|
||||||
|
|
||||||
|
switch (nametype) {
|
||||||
|
case 0: /* Simple Name */
|
||||||
|
case 2: /* [LENGTH] [STRING] */
|
||||||
|
namelength = cc_qsig_asn1_get_string((unsigned char *)callername, sizeof(callername), &invoke->data[myidx]);
|
||||||
|
callername[namelength] = 0;
|
||||||
|
break;
|
||||||
|
case 1: /* Nameset */
|
||||||
|
case 3: /* [LENGTH] [BIT-STRING] [LENGTH] [STRING] [INTEGER] [LENGTH] [VALUE] */
|
||||||
|
namesetlength = invoke->data[myidx++];
|
||||||
|
if (invoke->data[myidx++] == ASN1_OCTETSTRING) {
|
||||||
|
/* should be so */
|
||||||
|
namelength = cc_qsig_asn1_get_string((unsigned char *)callername, sizeof(callername), &invoke->data[myidx]);
|
||||||
|
callername[namelength] = 0;
|
||||||
|
myidx += invoke->data[myidx-1]; /* is this safe? */
|
||||||
|
} else {
|
||||||
|
cc_verbose(1, 1, VERBOSE_PREFIX_4 " Namestruct not ECMA conform (String expected)\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (invoke->data[myidx++] == ASN1_INTEGER) {
|
||||||
|
charset=cc_qsig_asn1_get_integer(invoke->data, &myidx);
|
||||||
|
} else {
|
||||||
|
cc_verbose(1, 1, VERBOSE_PREFIX_4 " Namestruct not ECMA conform (Integer expected)\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4: /* Name not available */
|
||||||
|
break;
|
||||||
|
case 7: /* Namepres. restricted NULL - don't understand ECMA-164, Page 5 */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (namelength > 0) {
|
||||||
|
/* TODO: Maybe we do some charset conversions */
|
||||||
|
i->owner->cid.cid_name = strdup(callername);
|
||||||
|
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " callers name length: %i, \"%s\"\n", namelength, callername); */
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int cc_qsig_encode_ecma_name_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i)
|
||||||
|
{
|
||||||
|
const unsigned char oid[] = {0x2b,0x0c,0x09,0x00}; /* 1.3.12.9.0 */
|
||||||
|
int oid_len = sizeof(oid);
|
||||||
|
unsigned char namebuf[50];
|
||||||
|
unsigned char data[255];
|
||||||
|
int dataidx = 0;
|
||||||
|
int namelen = 0;
|
||||||
|
/*TODO: write something */
|
||||||
|
|
||||||
|
namelen = strlen(i->owner->cid.cid_name);
|
||||||
|
|
||||||
|
if (namelen < 1) { /* There's no name available, try to take Interface-Name */
|
||||||
|
if (strlen(i->name) >= 1) {
|
||||||
|
if (namelen > 50)
|
||||||
|
namelen = 50;
|
||||||
|
namelen = strlen(i->name);
|
||||||
|
memcpy(namebuf, i->name, namelen);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (namelen > 50)
|
||||||
|
namelen = 50;
|
||||||
|
memcpy(namebuf, i->owner->cid.cid_name, namelen);
|
||||||
|
}
|
||||||
|
|
||||||
|
invoke->id = 1;
|
||||||
|
invoke->descr_type = ASN1_OBJECTIDENTIFIER;
|
||||||
|
invoke->oid_len = oid_len;
|
||||||
|
memcpy(invoke->oid_bin, oid, oid_len);
|
||||||
|
|
||||||
|
if (namelen>0) {
|
||||||
|
data[dataidx++] = 0x80; /* We send only simple Name, Namepresentation allowed */
|
||||||
|
data[dataidx++] = namelen;
|
||||||
|
memcpy(&data[dataidx], namebuf, namelen);
|
||||||
|
dataidx += namelen;
|
||||||
|
} else {
|
||||||
|
data[dataidx++] = 0x84; /* Name not available */
|
||||||
|
data[dataidx++] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
invoke->datalen = dataidx;
|
||||||
|
memcpy(invoke->data, data, dataidx);
|
||||||
|
|
||||||
|
/* qsig_add_invoke(buf, idx, invoke); */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue