/* * An implementation of Common ISDN API 2.0 for Asterisk * * Copyright (C) 2005-2008 Cytronics & Melware * Copyright (C) 2007 Mario Goegel * * Armin Schindler * Mario Goegel * * This program is free software and may be modified and * distributed under the terms of the GNU Public License. */ #include #include #include #include "chan_capi_platform.h" #include "chan_capi20.h" #include "chan_capi.h" #include "chan_capi_utils.h" #include "chan_capi_qsig.h" #include "chan_capi_qsig_ecma.h" #include "chan_capi_qsig_asn197ade.h" #include "chan_capi_qsig_asn197no.h" AST_MUTEX_DEFINE_STATIC(qsig_verbose_lock); /* * helper for _verbose with different verbose settings */ void cc_qsig_verbose(int c_d, char *text, ...) { char line[4096]; va_list ap; va_start(ap, text); vsnprintf(line, sizeof(line), text, ap); va_end(ap); #if 0 { FILE *fp; if ((fp = fopen("/tmp/cclog", "a")) != NULL) { fprintf(fp, "%s", line); fclose(fp); } } #endif if (option_verbose > 4) { if ((!c_d) || ((c_d) && (capiqsigdebug))) { cc_mutex_lock(&qsig_verbose_lock); cc_pbx_verbose("%s", line); cc_mutex_unlock(&qsig_verbose_lock); } } } /*! * \brief Encodes an ASN.1 string with type of string * * \param asn1_type type of string like ASN1_OCTETSTRING * \param data pointer to target buffer * \param len size of target buffer * \param max_len of the string data * \param src source pointer for string * \param src_len string length * \return data length */ int cc_qsig_asn1_add_string2(unsigned char asn1_type, void *data, int len, int max_len, void *src, int src_len) { struct rose_component *comp = NULL; if (len < 2 + src_len) return -1; if (max_len && (src_len > max_len)) src_len = max_len; comp = (struct rose_component *)data; comp->type = asn1_type; comp->len = src_len; memcpy(comp->data, src, src_len); return 2 + src_len; } /*! * \brief Encodes an ASN.1 string * * \param buf destination pointer for string * \param idx index points to string position in buffer * \param data string * \param datalen string length * \return always zero */ 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); buf[strsize] = 0; /* don't increase strsize after closing zero - string ends at strsize ! */ return strsize; } /* * Encode ASN.1 Integer */ 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_qsig_verbose( 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_oid2str(unsigned char *data, int size) { unsigned char buf[1024]; char numbuf[10]; unsigned char *s; int len, i; unsigned long n; s = buf; if (size < 3) { cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "OID2STR: Object identifier too small (%i).\n", size); return NULL; } #define N(n) \ snprintf(numbuf, sizeof numbuf, "%lu", (unsigned long)n); \ len = strlen(numbuf); \ memcpy(s, numbuf, len); \ s += len; N(data[0] / 40) *s++ = '.'; N(data[0] % 40) n = 0; for (i = 1; i < size; i++) { n = n << 7 | (data[i] & 0x7f); if ((data[i] & 0x80) == 0) { *s++ = '.'; N(n) n = 0; } } *s++ = 0; s = buf; return (unsigned char *) ast_strdup((char*)s); } /* * 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 protocolvar, int apdu_interpr, struct cc_qsig_nfe *nfe) { int myidx = *idx; /* we start with Index 1 - Byte 0 is Length of Facilitydataarray */ if (!myidx) myidx++; buf[myidx++] = 0x1c; buf[myidx++] = 0; /* Byte 2 length of Facilitydataarray */ buf[myidx++] = 0x80 | protocolvar; /* 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, struct capi_pvt *i) { unsigned char oid1[] = {0x2b,0x0c,0x09,0x00}; 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; } if (invoke->descr_type == -1) { switch (i->qsigfeat) { case QSIG_TYPE_ALCATEL_ECMA: invoke->descr_type = ASN1_OBJECTIDENTIFIER; /* Set ECMA/ETSI OID */ oid1[3] = (unsigned char)invoke->type; invoke->oid_len = sizeof(oid1); memcpy(invoke->oid_bin, oid1, sizeof(oid1)); break; case QSIG_TYPE_HICOM_ECMAV2: invoke->descr_type = ASN1_INTEGER; /* Leave type as it is */ break; default: /* INVOKE is not encoded */ break; } } 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, type 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_qsig_verbose( 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 - invlenidx - 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 protocol) { int myidx = *idx; char *APDU_STR[] = {"IGNORE", "REJECT CALL", "CLEAR CALL"}; /* First byte after Facility Length */ if (data[myidx] != (unsigned char)(0x80 | protocol)) { cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: received protocol 0x%#x not configured!\n", (data[myidx] ^= 0x80)); return 0; } myidx++; cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: Supplementary Services\n"); if (data[myidx] == (unsigned char)COMP_TYPE_NFE) { myidx++; /* TODO: Check Entities? */ myidx += data[myidx] + 1; *idx = myidx; cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: Facility has NFE struct\n"); } 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; cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: Facility has APDU - What to do if INVOKE is unknown: %s\n", APDU_STR[*apduval]); } return 1; } /* * 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 */ *idx = myidx; /* Set index to length byte of component */ return data[myidx + 1]; /* return component length */ } *idx += data[myidx]; /* we can end here, if it is an Invoke Result or Error */ 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_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: Unknown Invoke Identifier Type 0x%#x\n", invidtype); return -1; } temp = cc_qsig_asn1_get_integer(data, &myidx); invoke->id = temp; *idx = myidx; } 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, &myidx); invoke->descr_type = ASN1_INTEGER; invoke->type = temp; temp2 = (invoke->len) + (invoke->offset) + 1; /* Array End = Invoke Length + Invoke Offset +1 */ datalen = temp2 - myidx; if (datalen > 255) { cc_qsig_verbose( 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 += 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_qsig_verbose( 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 */ invoke->oid_len = temp; memcpy(invoke->oid_bin, &data[myidx], temp); /* Copy OID to separate array */ myidx = myidx + temp; /* Set index to next information */ if (temp == 4) { /* even if we have an OID, set the numeric invoke type */ invoke->type = (int) invoke->oid_bin[3]; } else { invoke->type = -1; } temp2 = (invoke->len) + (invoke->offset) + 1; /* Array End = Invoke Length + Invoke Offset +1 */ datalen = temp2 - myidx; if (datalen > 255) { cc_qsig_verbose( 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 += datalen; /* points to next INVOKE component, if there's any */ *idx = myidx; break; default: cc_qsig_verbose( 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_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: Unsupported INVOKE Operation Size (max 255 Bytes): %i\n", datalen); datalen = 255; } *idx = myidx + datalen; /* Set index to next INVOKE, if there's any */ return -1; break; } return 0; /* No problems */ } static int ident_qsig_invoke(int invoketype) { switch (invoketype) { case 0: case 1: case 2: case 3: return CCQSIG__ECMA__NAMEPRES; case 4: return CCQSIG__ECMA__PRPROPOSE; case 12: return CCQSIG__ECMA__CTCOMPLETE; case 21: return CCQSIG__ECMA__LEGINFO2; default: cc_qsig_verbose( 1, VERBOSE_PREFIX_4 "QSIG: Unhandled QSIG INVOKE (%i)\n", invoketype); return -1; } } /* * Identify an INVOKE and return our own Ident Integer (CCQSIG__*) */ signed int cc_qsig_identifyinvoke(struct cc_qsig_invokedata *invoke, int protocol) { int invokedescrtype = 0; int datalen; /* cc_qsig_verbose( 1, VERBOSE_PREFIX_4 "CONNECT_IND (Ident Invoke %i)\n", invoke->descr_type); */ switch (protocol) { case QSIG_TYPE_ALCATEL_ECMA: switch (invoke->descr_type) { case ASN1_INTEGER: invokedescrtype = 1; cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: INVOKE OP (%i)\n", invoke->type); return ident_qsig_invoke(invoke->type); break; case ASN1_OBJECTIDENTIFIER: invokedescrtype = 2; datalen = invoke->oid_len; { unsigned char *oidstr = NULL; oidstr = cc_qsig_asn1_oid2str(invoke->oid_bin, invoke->oid_len); if (oidstr) { cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: INVOKE OP (%s)\n", oidstr); ast_free(oidstr); } else { cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: INVOKE OP (unknown - OID not displayable)\n"); } } if ((datalen) == 4) { if (!cc_qsig_asn1_check_ecma_isdn_oid(invoke->oid_bin, datalen)) { return ident_qsig_invoke( (int)invoke->oid_bin[3]); } } return -1; break; default: cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: Unidentified INVOKE OP\n"); break; } break; case QSIG_TYPE_HICOM_ECMAV2: switch (invoke->descr_type) { case ASN1_INTEGER: invokedescrtype = 1; cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: INVOKE OP (%i)\n", invoke->type); return ident_qsig_invoke(invoke->type); break; case ASN1_OBJECTIDENTIFIER: invokedescrtype = 2; datalen = invoke->oid_len; { unsigned char *oidstr = NULL; oidstr = cc_qsig_asn1_oid2str(invoke->oid_bin, invoke->oid_len); if (oidstr) { cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: INVOKE OP (%s)\n", oidstr); ast_free(oidstr); } else { cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: INVOKE OP (unknown - OID not displayable)\n"); } } if ((datalen) == 4) { if (!cc_qsig_asn1_check_ecma_isdn_oid(invoke->oid_bin, datalen)) { return ident_qsig_invoke( (int)invoke->oid_bin[3]); } } break; default: cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: Unidentified INVOKE OP\n"); break; } break; default: break; } return -1; } /* * find the interface (pvt) the PLCI belongs to */ static struct capi_pvt *capi_find_interface_bynumber(char * num) { struct capi_pvt *i; if (!num) return NULL; for (i = capi_iflist; i; i = i->next) { if (strcmp(i->cid, num) == 0 || strcmp(i->dnid, num) == 0) return i; } #if 0 cc_mutex_lock(&nullif_lock); for (i = nulliflist; i; i = i->next) { if (strcmp(i->cid, num) == 0 || strcmp(i->dnid, num) == 0) break; } cc_mutex_unlock(&nullif_lock); #endif return i; } static void pbx_capi_qsig_handle_ctc(struct cc_qsig_invokedata *invoke, struct capi_pvt *i) { struct cc_qsig_ctcomplete ctc; struct capi_pvt *ii; #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 == 0)) { ii = capi_find_interface_bynumber(ctc.redirectionNumber.partyNumber); if (ii) { char *prpn = i->qsig_data.if_pr_propose_pn; cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: Call Transfer partner channel for %s found at channel %s, bridging possible.\n", ctc.redirectionNumber.partyNumber, ii->vname); if (!(strlen(prpn))) { cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: Path Replacement not configured, bridging not available!\n"); } else { unsigned char fac[CAPI_MAX_FACILITYDATAARRAY_SIZE]; cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: Trying to bridge with Path Replacement number %s...\n", prpn); switch (ii->state) { case CAPI_STATE_ALERTING: cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: peer is in state ALERTING, PATH REPLACE follows after CONNECT...\n"); ii->qsig_data.pr_propose_cid = ast_strdup("123"); /* HACK: need an dynamic ID */ ii->qsig_data.pr_propose_pn = ast_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_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: peer is CONNECTED...\n"); i->qsig_data.pr_propose_cid = ast_strdup("123"); /* HACK: need an dynamic ID */ i->qsig_data.pr_propose_pn = ast_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_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: peer's state is %i, which is not handled yet...\n", ii->state); break; } } CLEAR_CTC } } CLEAR_CTC return ; } /* * Handle inbound INVOKEs */ 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; 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; default: break; } return 0; } /** * Handles incoming facilities * * @internal * @param data facility buffer * @param i points to capip_pvt struct * @param idx index in facility buffer * @param faclen facility length * @param protocoltype Q932_PROTOCOL_ROSE | Q932_PROTOCOL_EXTENSIONS * @return zero */ static int qsig_handle_q932facility(unsigned char *data, struct capi_pvt *i, int *idx, int faclen, int protocoltype) { int facidx = *idx; int action_unkn_apdu; /* What to do with unknown Invoke-APDUs (0=Ignore, 1=clear call, 2=reject APDU) */ struct cc_qsig_invokedata invoke; int invoke_len; int invoketmp1; unsigned int invoke_op = 0; /* Invoke Operation ID */ if (cc_qsig_check_facility(data, &facidx, &action_unkn_apdu, protocoltype)) { while ((facidx) < faclen) { cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "Checking INVOKE at index %i (of %i)\n", facidx, 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, i->qsigfeat); if (invoke_op < 0) { cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "Invoke not identified!\n"); } cc_qsig_handle_invokeoperation(invoke_op, &invoke, i); } } else { /* Not an Invoke */ } } } else { /* kill endlessloop */ facidx += faclen; } *idx = facidx; return 0; } /* * Handles incoming Indications from CAPI */ unsigned int cc_qsig_handle_capiind(unsigned char *data, struct capi_pvt *i) { int faclen0 = 0; int faclen = 0; int facidx = 2; if (!i->qsigfeat) return 0; if (!data) { return 0; } faclen0 = data[facidx-2]; /* Length of facility array - there may be more facilities encoded in this struct */ faclen = data[facidx++]; faclen += facidx; while (facidx < faclen0) { cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "Checking Facility at index %i\n", facidx); switch (i->qsigfeat) { case QSIG_TYPE_ALCATEL_ECMA: qsig_handle_q932facility(data, i, &facidx, faclen, Q932_PROTOCOL_ROSE); break; case QSIG_TYPE_HICOM_ECMAV2: qsig_handle_q932facility(data, i, &facidx, faclen, Q932_PROTOCOL_EXTENSIONS); break; default: cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "Unknown QSIG protocol configured (%i)\n", i->qsigfeat); break; } if (facidx < faclen0) { /* there may follow a new facility */ if (data[facidx] == 0x1c) { cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "Found another facility at index %i\n", facidx); facidx++; faclen = data[facidx++]; faclen += facidx; } else { cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "More data found in facility at index %i, but this is not an facility (%#x)\n", facidx, data[facidx]); facidx++; /* don't start an endlessloop */ } } } cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "Facility done at index %i from %i\n", facidx, faclen); return 1; } /* * Handles incoming Facility Indications from CAPI */ unsigned int cc_qsig_handle_capi_facilityind(unsigned char *data, struct capi_pvt *i) { int faclen = 0; int facidx = 0; if (!data) { return 0; } faclen = data[facidx++]; /* cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "CONNECT_IND (Got Facility IE, Length=%#x)\n", faclen); */ while (facidx < faclen) { cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "Checking Facility at index %i\n", facidx); switch (i->qsigfeat) { case QSIG_TYPE_ALCATEL_ECMA: qsig_handle_q932facility(data, i, &facidx, faclen, Q932_PROTOCOL_ROSE); break; case QSIG_TYPE_HICOM_ECMAV2: qsig_handle_q932facility(data, i, &facidx, faclen, Q932_PROTOCOL_EXTENSIONS); break; default: cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "Unknown QSIG protocol configured (%i)\n", i->qsigfeat); /* kill endlessloop */ facidx += faclen; break; } } cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "Facility done at index %i from %i\n", facidx, faclen); return 1; } static int identify_qsig_setup_callfeature(char *param) { char *p = param; switch (*p) { case 't': cc_qsig_verbose( 1, "Call Transfer"); p++; if (*p == 'r') { cc_qsig_verbose( 1, " on ALERT"); return 2; } else { return 1; } default: cc_qsig_verbose( 1, "unknown (%c)\n", *p); break; } return 0; } /* * Handles outgoing Facilies on Call SETUP */ unsigned int cc_qsig_add_call_setup_data(unsigned char *data, struct capi_pvt *i, struct ast_channel *c) { /* TODO: Check buffers */ struct cc_qsig_invokedata invoke; struct cc_qsig_nfe nfe; unsigned int dataidx = 0; int protocolvar = 0; const unsigned char xprogress[] = {0x1e,0x02,0xa0,0x90}; char *p = NULL; char *pp = NULL; int add_externalinfo = 0; data[0] = 0; /* Initialize array length */ if ((p = (char *)pbx_builtin_getvar_helper(c, "QSIG_SETUP"))) { /* some special dial parameters */ /* parse the parameters */ while ((p) && (*p)) { switch (*p) { case 'X': /* add PROGRESS INDICATOR for external calls*/ cc_qsig_verbose( 1, VERBOSE_PREFIX_4 "Sending QSIG external PROGRESS IE.\n"); add_externalinfo = 1; pp = strsep (&p, "/"); pp = NULL; break; case 'C': cc_qsig_verbose( 1, VERBOSE_PREFIX_4 "QSIG Call Feature requested: "); p++; switch(identify_qsig_setup_callfeature(p)) { case 1: /* Call transfer */ p++; pp = strsep(&p, "/"); if (!pp) { cc_log(LOG_WARNING, "QSIG Call Feature needs plci as parameter!\n"); } else { i->qsig_data.calltransfer = 1; i->qsig_data.partner_plci = atoi(pp); /* set the other channel as partner to me */ { struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci); if (ii) ii->qsig_data.partner_plci = i->PLCI; } cc_qsig_verbose( 1, " for plci %#x\n", i->qsig_data.partner_plci); } break; case 2: /* Call transfer on ring */ p += 2; pp = strsep(&p, "/"); if (!pp) { cc_log(LOG_WARNING, "QSIG Call Feature needs plci as parameter!\n"); } else { i->qsig_data.calltransfer_onring = 1; i->qsig_data.partner_plci = atoi(pp); /* set the other channel as partner to me */ { struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci); if (ii) ii->qsig_data.partner_plci = i->PLCI; } cc_qsig_verbose( 1, " for plci %#x\n", i->qsig_data.partner_plci); } break; default: pp = strsep(&p, "/"); break; } pp = NULL; break; default: cc_log(LOG_WARNING, "Unknown parameter '%c' in QSIG_SETUP, ignoring.\n", *p); p++; } } } switch (i->qsigfeat) { case QSIG_TYPE_ALCATEL_ECMA: protocolvar = Q932_PROTOCOL_ROSE; break; case QSIG_TYPE_HICOM_ECMAV2: protocolvar = Q932_PROTOCOL_EXTENSIONS; break; default: cc_log(LOG_WARNING, " Unknown QSIG variant configured.\n"); return 0; break; } cc_qsig_build_facility_struct(data, &dataidx, protocolvar, APDUINTERPRETATION_IGNORE, &nfe); cc_qsig_encode_ecma_name_invoke(data, &dataidx, &invoke, i, 0, pbx_capi_get_connectedname (i->owner, "")); cc_qsig_add_invoke(data, &dataidx, &invoke, i); if (add_externalinfo) { /* add PROGRESS INDICATOR for external calls*/ int progress_size = sizeof(xprogress); memcpy(&data[dataidx], xprogress, progress_size); data[0] += progress_size; dataidx += progress_size; } return 0; } /* * Handles outgoing Facilies on Call Answer */ unsigned int cc_qsig_add_call_answer_data(unsigned char *data, struct capi_pvt *i, struct ast_channel *c) { struct cc_qsig_invokedata invoke; struct cc_qsig_nfe nfe; unsigned int dataidx = 0; int protocolvar = 0; const char *connectedname; data[0] = 0; if (!i->qsigfeat) return 0; if (!(connectedname = pbx_builtin_getvar_helper(c, "CONNECTEDNAME"))) return 0; if (!strlen(connectedname)) return 0; switch (i->qsigfeat) { case QSIG_TYPE_ALCATEL_ECMA: protocolvar = Q932_PROTOCOL_ROSE; break; case QSIG_TYPE_HICOM_ECMAV2: protocolvar = Q932_PROTOCOL_EXTENSIONS; break; default: cc_log(LOG_WARNING, " Unknown QSIG variant configured.\n"); return 0; break; } cc_qsig_build_facility_struct(data, &dataidx, protocolvar, APDUINTERPRETATION_IGNORE, &nfe); cc_qsig_encode_ecma_name_invoke(data, &dataidx, &invoke, i, 2, (char *)connectedname); cc_qsig_add_invoke(data, &dataidx, &invoke, i); return 1; } /* * Handles outgoing Facilies on Call Alert */ unsigned int cc_qsig_add_call_alert_data(unsigned char *data, struct capi_pvt *i, struct ast_channel *c) { struct cc_qsig_invokedata invoke; struct cc_qsig_nfe nfe; unsigned int dataidx = 0; int protocolvar = 0; const char *connectedname; data[0] = 0; if (!i->qsigfeat) return 0; if (!(connectedname = pbx_builtin_getvar_helper(c, "CALLEDNAME"))) return 0; if (!strlen(connectedname)) return 0; switch (i->qsigfeat) { case QSIG_TYPE_ALCATEL_ECMA: protocolvar = Q932_PROTOCOL_ROSE; break; case QSIG_TYPE_HICOM_ECMAV2: protocolvar = Q932_PROTOCOL_EXTENSIONS; break; default: cc_log(LOG_WARNING, " Unknown QSIG variant configured.\n"); return 0; break; } cc_qsig_build_facility_struct(data, &dataidx, protocolvar, APDUINTERPRETATION_IGNORE, &nfe); cc_qsig_encode_ecma_name_invoke(data, &dataidx, &invoke, i, 1, (char *)connectedname); cc_qsig_add_invoke(data, &dataidx, &invoke, i); return 1; } /* * Handles outgoing Facilies on capicommand */ unsigned int cc_qsig_do_facility(unsigned char *fac, struct ast_channel *c, char *param, unsigned int factype, int info1) { struct cc_qsig_invokedata invoke; struct cc_qsig_nfe nfe; struct capi_pvt *i = CC_CHANNEL_PVT(c); /* struct capi_pvt *ii = NULL; */ unsigned int facidx = 0; int protocolvar = 0; switch (i->qsigfeat) { case QSIG_TYPE_ALCATEL_ECMA: protocolvar = Q932_PROTOCOL_ROSE; break; case QSIG_TYPE_HICOM_ECMAV2: protocolvar = Q932_PROTOCOL_EXTENSIONS; break; default: cc_log(LOG_WARNING, " Unknown QSIG variant configured.\n"); return 0; break; } cc_qsig_build_facility_struct(fac, &facidx, protocolvar, APDUINTERPRETATION_IGNORE, &nfe); switch (factype) { case 4: /* ECMA-xxx pathReplacementPropose */ cc_qsig_encode_ecma_prpropose(fac, &facidx, &invoke, i, param); cc_qsig_add_invoke(fac, &facidx, &invoke, i); break; case 12: /* ECMA-178 callTransfer */ cc_qsig_encode_ecma_calltransfer(fac, &facidx, &invoke, i, param, info1); cc_qsig_add_invoke(fac, &facidx, &invoke, i); break; case 99: /* ECMA-300 simpleCallTransfer */ cc_qsig_encode_ecma_sscalltransfer(fac, &facidx, &invoke, i, param); cc_qsig_add_invoke(fac, &facidx, &invoke, i); break; default: break; } return 0; } /* * capicommand getplci - needed for Call Transfer */ int pbx_capi_qsig_getplci(struct ast_channel *c, char *param) { struct capi_pvt *i = CC_CHANNEL_PVT(c); char buffer[10]; snprintf(buffer, sizeof(buffer)-1, "%d", i->PLCI); cc_qsig_verbose( 1, VERBOSE_PREFIX_4 "QSIG_GETPLCI: %s\n", buffer); pbx_builtin_setvar_helper(c, "QSIG_PLCI", buffer); return 0; } /* * Initiate a QSIG Single Step Call Transfer */ int pbx_capi_qsig_ssct(struct ast_channel *c, char *param) { unsigned char fac[CAPI_MAX_FACILITYDATAARRAY_SIZE]; struct capi_pvt *i = CC_CHANNEL_PVT(c); if (!param) { /* no data implies no Calling Number and Destination Number */ cc_log(LOG_WARNING, CC_MESSAGE_NAME " qsig_ssct requires source number and destination number\n"); return -1; } cc_qsig_do_facility(fac, c, param, 99, 0); capi_sendf(NULL, 0, CAPI_INFO_REQ, i->PLCI, get_capi_MessageNumber(), "()(()()()s)", fac ); return 0; } /* * Initiate a QSIG Call Transfer */ int pbx_capi_qsig_ct(struct ast_channel *c, char *param) { unsigned char fac[CAPI_MAX_FACILITYDATAARRAY_SIZE]; struct capi_pvt *i = CC_CHANNEL_PVT(c); struct capi_pvt *ii = NULL; unsigned int callmark; char *marker; if (!param) { /* no data implies no Calling Number and Destination Number */ cc_log(LOG_WARNING, CC_MESSAGE_NAME " qsig_ct requires call marker, source number, destination number and await_connect info\n"); return -1; } marker = strsep(¶m, COMMANDSEPARATOR); callmark = atoi(marker); cc_qsig_verbose( 1, VERBOSE_PREFIX_4 " * QSIG_CT: using call marker %i(%s)\n", callmark, marker); for (ii = capi_iflist; ii; ii = ii->next) { if (ii->qsig_data.callmark == callmark) break; } if (!ii) { cc_log(LOG_WARNING, CC_MESSAGE_NAME " qsig_ct call marker not found!\n"); return -1; } cc_qsig_do_facility(fac, c, param, 12, 1); capi_sendf(NULL, 0, CAPI_INFO_REQ, i->PLCI, get_capi_MessageNumber(), "()(()()()s())", 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; } /* * mark an call */ int pbx_capi_qsig_callmark(struct ast_channel *c, char *param) { struct capi_pvt *i = CC_CHANNEL_PVT(c); if (!param) { /* no data implies no Calling Number and Destination Number */ cc_log(LOG_WARNING, "capi qsig_callmark requires an call identifier\n"); return -1; } i->qsig_data.callmark = atoi(param); return 0; } static void send_feature_calltransfer(struct capi_pvt *i) { unsigned char *fac = alloca(CAPI_MAX_FACILITYDATAARRAY_SIZE); struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci); /* needed for Path Replacement */ ii->qsig_data.partner_plci = i->PLCI; if (ii) { 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); } } /* * init QSIG data on new channel - will be called by mkif */ 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; tmp->qsig_data.callmark = 0; tmp->qsig_data.dnameid = NULL; /* Path Replacement */ 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; tmp->qsig_data.waitevent = 0; 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_TRUE(conf->divaqsig, "divaqsig", 1) 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 */ static void qsig_cleanup_channel(struct capi_pvt *i) { i->qsig_data.callmark = 0; i->qsig_data.partner_ch = NULL; i->qsig_data.calltransfer_active = 0; 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) { ast_free(i->qsig_data.pr_propose_cid); i->qsig_data.pr_propose_cid = NULL; } if (i->qsig_data.pr_propose_pn) { ast_free(i->qsig_data.pr_propose_pn); i->qsig_data.pr_propose_pn = NULL; } if (i->qsig_data.dnameid) { ast_free(i->qsig_data.dnameid); i->qsig_data.dnameid = NULL; } } /* * cleanup QSIG stuff on interface */ void interface_cleanup_qsig(struct capi_pvt *i) { if (i->qsigfeat) { cc_qsig_verbose( 1, VERBOSE_PREFIX_4 "QSIG: cleanup channel\n"); qsig_cleanup_channel(i); } } /* * cleanup QSIG stuff on module unload */ void pbx_capi_qsig_unload_module(struct capi_pvt *i) { if (!i->qsigfeat) return; ast_cond_destroy(&i->qsig_data.event_trigger); } /* * wait for B3 up */ int pbx_capi_qsig_wait_for_prpropose(struct capi_pvt *i) { struct timespec abstime; int ret = 1; cc_mutex_lock(&i->lock); if (!(i->qsig_data.pr_propose_sentback)) { i->qsig_data.waitevent = CAPI_QSIG_WAITEVENT_PRPROPOSE; abstime.tv_sec = time(NULL) + CCQSIG_TIMER_WAIT_PRPROPOSE; /* PR PROPOSE TIMER */ abstime.tv_nsec = 0; cc_qsig_verbose( 1, "%s: wait for PATH REPLACEMENT.\n", i->vname); if (ast_cond_timedwait(&i->qsig_data.event_trigger, &i->lock, &abstime) != 0) { cc_log(LOG_WARNING, "%s: timed out waiting for PATH REPLACEMENT.\n", i->vname); ret = 0; } else { cc_qsig_verbose( 1, "%s: cond signal received for PATH REPLACEMENT.\n", i->vname); } } cc_mutex_unlock(&i->lock); return ret; } /* * check special conditions, wake waiting threads and send outstanding commands * for the given interface */ static void pbx_capi_qsig_post_handling(struct capi_pvt *i) { if ((i->qsig_data.waitevent == CAPI_QSIG_WAITEVENT_PRPROPOSE) && (i->qsig_data.pr_propose_sentback == 1)) { i->qsig_data.waitevent = 0; ast_cond_signal(&i->qsig_data.event_trigger); cc_qsig_verbose( 1, "%s: found and signal for PATH REPLACEMENT state.\n", i->vname); return; } } /* * handle a bridge attempt - maybe we're allowed to make a path replacement */ int pbx_capi_qsig_bridge(struct capi_pvt *i0,struct capi_pvt *i1) { if (i1->qsig_data.pr_propose_sentback) { return 2; /* Path Replacement already sent out - call will be cleared in short */ } i1->qsig_data.partner_plci = i0->PLCI; send_feature_calltransfer(i1); if (pbx_capi_qsig_wait_for_prpropose(i1)) return 1; /* Path Replacement successful */ /* No Path Replacement - allow line interconnect */ return 0; } int pbx_capi_qsig_sendtext(struct ast_channel *c, const char *text) { struct capi_pvt *i = CC_CHANNEL_PVT(c); #if 0 /* suppress compiler warnings */ unsigned char *data = alloca(CAPI_MAX_FACILITYDATAARRAY_SIZE); unsigned int dataidx = 0; struct cc_qsig_invokedata invoke; struct cc_qsig_nfe nfe; #endif int protocolvar = 0; if (!i->qsigfeat) return 0; if (!strlen(text)) return 0; switch (i->qsigfeat) { case QSIG_TYPE_ALCATEL_ECMA: protocolvar = Q932_PROTOCOL_ROSE; break; case QSIG_TYPE_HICOM_ECMAV2: protocolvar = Q932_PROTOCOL_EXTENSIONS; break; default: cc_log(LOG_WARNING, " Unknown QSIG variant configured.\n"); return 0; break; } /* TODO: implement something - QSIG_LEG_INFO3 doesn't work here */ return 0; } /* * CAPI INFO_IND (QSIG part) */ void pbx_capi_qsig_handle_info_indication(_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt *i) { if (!i->qsigfeat) /* Run only, if QSIG enabled */ return; switch(INFO_IND_INFONUMBER(CMSG)) { case 0x0008: /* Cause */ break; case 0x0014: /* Call State */ break; case 0x0018: /* Channel Identification */ break; case 0x001c: /* Facility Q.932 */ { unsigned int qsiginvoke; 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 &! i->qsig_data.pr_propose_doinboundbridge) { struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci); if (ii) { if (ii->state == CAPI_STATE_CONNECTED) { /* second line is connected, we can proceed */ unsigned char fac[CAPI_MAX_FACILITYDATAARRAY_SIZE]; cc_qsig_do_facility(fac, i->owner, NULL, 4, 0); capi_sendf(NULL, 0, CAPI_INFO_REQ, ii->PLCI, get_capi_MessageNumber(), "()(()()()s)", fac ); i->qsig_data.pr_propose_active = 1; ii->qsig_data.pr_propose_sentback = 1; } else { /* Path Replacement has to be sent back after Connect on second line */ ii->qsig_data.pr_propose_sendback = 1; ii->qsig_data.pr_propose_cid = ast_strdup(i->qsig_data.pr_propose_cid); ii->qsig_data.pr_propose_pn = ast_strdup(i->qsig_data.pr_propose_pn); ii->qsig_data.pr_propose_active = 1; } } else cc_qsig_verbose( 1, VERBOSE_PREFIX_4 " * QSIG_PATHREPLACEMENT_PROPOSE: no partner channel found (%#x)\n", i->qsig_data.partner_plci); ast_free(i->qsig_data.pr_propose_cid); i->qsig_data.pr_propose_cid = NULL; ast_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_qsig_verbose( 1, VERBOSE_PREFIX_4 " * QSIG_PATHREPLACEMENT_PROPOSE: trying to complete bridge...\n"); chanx = ast_bridged_channel(i->owner); ast_channel_masquerade(ii->owner, chanx); } ast_free(i->qsig_data.pr_propose_cid); i->qsig_data.pr_propose_cid = NULL; ast_free(i->qsig_data.pr_propose_pn); i->qsig_data.pr_propose_pn = NULL; } } break; case 0x001e: /* Progress Indicator */ break; case 0x0027: /* Notification Indicator */ break; case 0x0028: /* DSP */ break; case 0x0029: /* Date/Time */ break; case 0x0070: /* Called Party Number */ break; case 0x0074: /* Redirecting Number */ break; case 0x0076: /* Redirection Number */ break; case 0x00a1: /* Sending Complete */ break; case 0x4000: /* CHARGE in UNITS */ break; case 0x4001: /* CHARGE in CURRENCY */ break; case 0x8001: /* ALERTING */ /* TODO: some checks, if there's any work here */ if (i->qsig_data.calltransfer_onring) { i->qsig_data.calltransfer_onring = 0; send_feature_calltransfer(i); } break; case 0x8002: /* CALL PROCEEDING */ break; case 0x8003: /* PROGRESS */ break; case 0x8005: /* SETUP */ break; case 0x8007: /* CONNECT */ if (i->qsig_data.calltransfer) { i->qsig_data.calltransfer = 0; send_feature_calltransfer(i); } { /* handle prior received Path Replacement */ if ((i->qsig_data.pr_propose_cid && i->qsig_data.pr_propose_pn) && i->qsig_data.pr_propose_sendback) { 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; ast_free(i->qsig_data.pr_propose_cid); i->qsig_data.pr_propose_cid = NULL; ast_free(i->qsig_data.pr_propose_pn); i->qsig_data.pr_propose_pn = NULL; i->qsig_data.pr_propose_sentback = 1; } } break; case 0x800d: /* SETUP ACK */ break; case 0x800f: /* CONNECT ACK */ { 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; ast_free(i->qsig_data.pr_propose_cid); i->qsig_data.pr_propose_cid = NULL; ast_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); break; case 0x804d: /* RELEASE */ break; case 0x805a: /* RELEASE COMPLETE */ qsig_cleanup_channel(i); break; case 0x8062: /* FACILITY */ break; case 0x806e: /* NOTIFY */ break; case 0x807b: /* INFORMATION */ break; case 0x807d: /* STATUS */ break; default: break; } pbx_capi_qsig_post_handling(i); return; }