+ int cause, ret;
+ union parameter newparam;
+#if ASTERISK_VERSION_NUM < 110000
+ char *exten = ast->exten;
+#else
+ char s_exten[AST_MAX_EXTENSION];
+ char *exten=s_exten;
+
+ strncpy(exten, ast_channel_exten(ast), AST_MAX_EXTENSION-1);
+#endif
+
+if (!*exten)
+ exten = "s";
+
+#if ASTERISK_VERSION_NUM < 110000
+ CDEBUG(call, ast, "Try to start pbx. (exten=%s context=%s complete=%s)\n", exten, ast->context, complete?"yes":"no");
+#else
+ CDEBUG(call, ast, "Try to start pbx. (exten=%s context=%s complete=%s)\n", exten, ast_channel_context(ast), complete?"yes":"no");
+#endif
+
+ if (complete) {
+ /* if not match */
+#if ASTERISK_VERSION_NUM < 110000
+ if (!ast_canmatch_extension(ast, ast->context, exten, 1, call->oad)) {
+CDEBUG(call, ast, "Got 'sending complete', but extension '%s' will not match at context '%s' - releasing.\n", exten, ast->context);
+#else
+ if (!ast_canmatch_extension(ast, ast_channel_context(ast), exten, 1, call->oad)) {
+CDEBUG(call, ast, "Got 'sending complete', but extension '%s' will not match at context '%s' - releasing.\n", exten, ast_channel_context(ast));
+#endif
+ cause = 1;
+ goto release;
+ }
+#if ASTERISK_VERSION_NUM < 110000
+ if (!ast_exists_extension(ast, ast->context, exten, 1, call->oad)) {
+ CDEBUG(call, ast, "Got 'sending complete', but extension '%s' would match at context '%s', if more digits would be dialed - releasing.\n", exten, ast->context);
+#else
+ if (!ast_exists_extension(ast, ast_channel_context(ast), exten, 1, call->oad)) {
+ CDEBUG(call, ast, "Got 'sending complete', but extension '%s' would match at context '%s', if more digits would be dialed - releasing.\n", exten, ast_channel_context(ast));
+#endif
+ cause = 28;
+ goto release;
+ }
+ CDEBUG(call, ast, "Got 'sending complete', extensions matches.\n");
+ /* send setup acknowledge to lcr */
+ memset(&newparam, 0, sizeof(union parameter));
+ send_message(MESSAGE_PROCEEDING, call->ref, &newparam);
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_IN_PROCEEDING;
+
+ goto start;
+ }
+
+#if ASTERISK_VERSION_NUM < 110000
+ if (ast_canmatch_extension(ast, ast->context, exten, 1, call->oad)) {
+#else
+ if (ast_canmatch_extension(ast, ast_channel_context(ast), exten, 1, call->oad)) {
+#endif
+ /* send setup acknowledge to lcr */
+ if (call->state != CHAN_LCR_STATE_IN_DIALING) {
+ memset(&newparam, 0, sizeof(union parameter));
+ send_message(MESSAGE_OVERLAP, call->ref, &newparam);
+ }
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_IN_DIALING;
+
+ /* if match, start pbx */
+#if ASTERISK_VERSION_NUM < 110000
+ if (ast_exists_extension(ast, ast->context, exten, 1, call->oad)) {
+#else
+ if (ast_exists_extension(ast, ast_channel_context(ast), exten, 1, call->oad)) {
+#endif
+ CDEBUG(call, ast, "Extensions matches.\n");
+ goto start;
+ }
+
+ /* send setup acknowledge to lcr */
+ if (call->state != CHAN_LCR_STATE_IN_DIALING) {
+ memset(&newparam, 0, sizeof(union parameter));
+ send_message(MESSAGE_OVERLAP, call->ref, &newparam);
+ }
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_IN_DIALING;
+
+ /* if can match */
+ CDEBUG(call, ast, "Extensions may match, if more digits are dialed.\n");
+ return;
+ }
+
+#if ASTERISK_VERSION_NUM < 110000
+ if (!*ast->exten) {
+#else
+ if (!*ast_channel_exten(ast)) {
+#endif
+ /* send setup acknowledge to lcr */
+ if (call->state != CHAN_LCR_STATE_IN_DIALING) {
+ memset(&newparam, 0, sizeof(union parameter));
+ send_message(MESSAGE_OVERLAP, call->ref, &newparam);
+ }
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_IN_DIALING;
+
+ /* if can match */
+ CDEBUG(call, ast, "There is no 's' extension (and we tried to match it implicitly). Extensions may match, if more digits are dialed.\n");
+ return;
+ }
+
+ /* if not match */
+ cause = 1;
+ release:
+ /* release lcr */
+ CDEBUG(call, ast, "Releasing due to extension missmatch.\n");
+ send_release(call, cause, LOCATION_PRIVATE_LOCAL);
+ call->ref = 0;
+ /* release asterisk */
+#if ASTERISK_VERSION_NUM < 110000
+ ast->hangupcause = call->cause;
+#else
+ ast_channel_hangupcause_set(ast, call->cause);
+#endif
+ /* change to release state */
+ call->state = CHAN_LCR_STATE_RELEASE;
+ ast_hangup(ast); // call will be destroyed here
+ return;
+
+ start:
+ /* send setup to asterisk */
+ CDEBUG(call, ast, "Starting call to Asterisk due to matching extension.\n");
+
+ #ifdef LCR_FOR_CALLWEAVER
+ ast->type = "LCR";
+ snprintf(ast->name, sizeof(ast->name), "%s/%s-%04x",lcr_type ,ast->cid.cid_num, ast_random() & 0xffff);
+ #endif
+
+ ret = ast_pbx_start(ast);
+ if (ret < 0) {
+ cause = (ret==-2)?34:27;
+ goto release;
+ }
+ call->pbx_started = 1;
+ ast_setstate(ast, AST_STATE_RING);
+}
+
+/*
+ * incoming setup from LCR
+ */
+static void lcr_in_setup(struct chan_call *call, int message_type, union parameter *param)
+{
+ struct ast_channel *ast;
+#ifdef AST_1_8_OR_HIGHER
+ struct ast_party_redirecting *ast_redir;
+ struct ast_party_caller *ast_caller;
+#else
+ struct ast_callerid *ast_caller;
+#endif
+#if ASTERISK_VERSION_NUM >= 110000
+ struct ast_party_redirecting s_ast_redir;
+ struct ast_party_caller s_ast_caller;
+ ast_party_redirecting_init(&s_ast_redir);
+ ast_party_caller_init(&s_ast_caller);
+#endif
+ CDEBUG(call, NULL, "Incomming setup from LCR. (callerid %s, dialing %s)\n", param->setup.callerinfo.id, param->setup.dialinginfo.id);
+
+ /* create asterisk channel instrance */
+
+ #ifdef LCR_FOR_CALLWEAVER
+ ast = ast_channel_alloc(1);
+ #endif
+
+ #ifdef LCR_FOR_ASTERISK
+#ifdef AST_1_8_OR_HIGHER
+ ast = ast_channel_alloc(1, AST_STATE_RESERVED, NULL, NULL, "", NULL, "", "", 0, "%s/%d", lcr_type, ++glob_channel);
+#else
+ ast = ast_channel_alloc(1, AST_STATE_RESERVED, NULL, NULL, "", NULL, "", 0, "%s/%d", lcr_type, ++glob_channel);
+#endif
+ #endif
+
+#if ASTERISK_VERSION_NUM < 110000
+#ifdef AST_1_8_OR_HIGHER
+ ast_redir = &ast->redirecting;
+ ast_caller = &ast->caller;
+#else
+ ast_caller = &ast->cid;
+#endif
+#else
+ ast_redir = &s_ast_redir;
+ ast_caller = &s_ast_caller;
+#endif
+
+ if (!ast) {
+ /* release */
+ CERROR(call, NULL, "Failed to create Asterisk channel - releasing.\n");
+ send_release(call, CAUSE_RESSOURCEUNAVAIL, LOCATION_PRIVATE_LOCAL);
+ /* remove call */
+ free_call(call);
+ return;
+ }
+ /* link together */
+ call->ast = ast;
+#if ASTERISK_VERSION_NUM < 110000
+ ast->tech_pvt = call;
+ ast->tech = &lcr_tech;
+ ast->fds[0] = call->pipe[0];
+#else
+ ast_channel_tech_pvt_set(ast, call);
+ ast_channel_tech_set(ast, &lcr_tech);
+ ast_channel_set_fd(ast, 0, call->pipe[0]);
+#endif
+
+ /* fill setup information */
+ if (param->setup.dialinginfo.id)
+#if ASTERISK_VERSION_NUM < 110000
+ strncpy(ast->exten, param->setup.dialinginfo.id, AST_MAX_EXTENSION-1);
+ if (param->setup.dialinginfo.context[0])
+ strncpy(ast->context, param->setup.dialinginfo.context, AST_MAX_CONTEXT-1);
+ else
+ strncpy(ast->context, param->setup.callerinfo.interface, AST_MAX_CONTEXT-1);
+#else
+ ast_channel_exten_set(ast, param->setup.dialinginfo.id);
+ if (param->setup.dialinginfo.context[0])
+ ast_channel_context_set(ast, param->setup.dialinginfo.context);
+ else
+ ast_channel_context_set(ast, param->setup.callerinfo.interface);
+#endif
+
+
+#ifdef AST_1_8_OR_HIGHER
+ if (param->setup.callerinfo.id[0]) {
+ ast_caller->id.number.valid = 1;
+ ast_caller->id.number.str = strdup(param->setup.callerinfo.id);
+ if (!param->setup.callerinfo.id[0]) {
+ ast_caller->id.number.presentation = AST_PRES_RESTRICTED;
+ ast_caller->id.number.plan = (0 << 4) | 1;
+ }
+ switch (param->setup.callerinfo.present) {
+ case INFO_PRESENT_ALLOWED:
+ ast_caller->id.number.presentation = AST_PRES_ALLOWED;
+ break;
+ case INFO_PRESENT_RESTRICTED:
+ ast_caller->id.number.presentation = AST_PRES_RESTRICTED;
+ break;
+ default:
+ ast_caller->id.number.presentation = AST_PRES_UNAVAILABLE;
+ }
+ switch (param->setup.callerinfo.screen) {
+ case INFO_SCREEN_USER:
+ ast_caller->id.number.presentation |= AST_PRES_USER_NUMBER_UNSCREENED;
+ break;
+ case INFO_SCREEN_USER_VERIFIED_PASSED:
+ ast_caller->id.number.presentation |= AST_PRES_USER_NUMBER_PASSED_SCREEN;
+ break;
+ case INFO_SCREEN_USER_VERIFIED_FAILED:
+ ast_caller->id.number.presentation |= AST_PRES_USER_NUMBER_FAILED_SCREEN;
+ break;
+ default:
+ ast_caller->id.number.presentation |= AST_PRES_NETWORK_NUMBER;
+ }
+ switch (param->setup.callerinfo.ntype) {
+ case INFO_NTYPE_SUBSCRIBER:
+ ast_caller->id.number.plan = (4 << 4) | 1;
+ break;
+ case INFO_NTYPE_NATIONAL:
+ ast_caller->id.number.plan = (2 << 4) | 1;
+ break;
+ case INFO_NTYPE_INTERNATIONAL:
+ ast_caller->id.number.plan = (1 << 4) | 1;
+ break;
+ default:
+ ast_caller->id.number.plan = (0 << 4) | 1;
+ }
+ }
+ if (param->setup.callerinfo.id2[0]) {
+ ast_caller->ani.number.valid = 1;
+ ast_caller->ani.number.str = strdup(param->setup.callerinfo.id2);
+ switch (param->setup.callerinfo.present2) {
+ case INFO_PRESENT_ALLOWED:
+ ast_caller->ani.number.presentation = AST_PRES_ALLOWED;
+ break;
+ case INFO_PRESENT_RESTRICTED:
+ ast_caller->ani.number.presentation = AST_PRES_RESTRICTED;
+ break;
+ default:
+ ast_caller->ani.number.presentation = AST_PRES_UNAVAILABLE;
+ }
+ switch (param->setup.callerinfo.screen2) {
+ case INFO_SCREEN_USER:
+ ast_caller->ani.number.presentation |= AST_PRES_USER_NUMBER_UNSCREENED;
+ break;
+ case INFO_SCREEN_USER_VERIFIED_PASSED:
+ ast_caller->ani.number.presentation |= AST_PRES_USER_NUMBER_PASSED_SCREEN;
+ break;
+ case INFO_SCREEN_USER_VERIFIED_FAILED:
+ ast_caller->ani.number.presentation |= AST_PRES_USER_NUMBER_FAILED_SCREEN;
+ break;
+ default:
+ ast_caller->ani.number.presentation |= AST_PRES_NETWORK_NUMBER;
+ }
+ switch (param->setup.callerinfo.ntype2) {
+ case INFO_NTYPE_SUBSCRIBER:
+ ast_caller->ani.number.plan = (4 << 4) | 1;
+ break;
+ case INFO_NTYPE_NATIONAL:
+ ast_caller->ani.number.plan = (2 << 4) | 1;
+ break;
+ case INFO_NTYPE_INTERNATIONAL:
+ ast_caller->ani.number.plan = (1 << 4) | 1;
+ break;
+ default:
+ ast_caller->ani.number.plan = (0 << 4) | 1;
+ }
+ }
+ if (param->setup.callerinfo.name[0]) {
+ ast_caller->id.name.valid = 1;
+ ast_caller->id.name.str = strdup(param->setup.callerinfo.name);
+ }
+#if ASTERISK_VERSION_NUM >= 110000
+ ast_channel_caller_set(ast, ast_caller);
+#endif
+ if (param->setup.redirinfo.id[0]) {
+ ast_redir->from.number.valid = 1;
+ ast_redir->from.number.str = strdup(param->setup.redirinfo.id);
+ switch (param->setup.redirinfo.present) {
+ case INFO_PRESENT_ALLOWED:
+ ast_redir->from.number.presentation = AST_PRES_ALLOWED;
+ break;
+ case INFO_PRESENT_RESTRICTED:
+ ast_redir->from.number.presentation = AST_PRES_RESTRICTED;
+ break;
+ default:
+ ast_redir->from.number.presentation = AST_PRES_UNAVAILABLE;
+ }
+ switch (param->setup.redirinfo.screen) {
+ case INFO_SCREEN_USER:
+ ast_redir->from.number.presentation |= AST_PRES_USER_NUMBER_UNSCREENED;
+ break;
+ case INFO_SCREEN_USER_VERIFIED_PASSED:
+ ast_redir->from.number.presentation |= AST_PRES_USER_NUMBER_PASSED_SCREEN;
+ break;
+ case INFO_SCREEN_USER_VERIFIED_FAILED:
+ ast_redir->from.number.presentation |= AST_PRES_USER_NUMBER_FAILED_SCREEN;
+ break;
+ default:
+ ast_redir->from.number.presentation |= AST_PRES_NETWORK_NUMBER;
+ }
+ switch (param->setup.redirinfo.ntype) {
+ case INFO_NTYPE_SUBSCRIBER:
+ ast_redir->from.number.plan = (4 << 4) | 1;
+ break;
+ case INFO_NTYPE_NATIONAL:
+ ast_redir->from.number.plan = (2 << 4) | 1;
+ break;
+ case INFO_NTYPE_INTERNATIONAL:
+ ast_redir->from.number.plan = (1 << 4) | 1;
+ break;
+ default:
+ ast_redir->from.number.plan = (0 << 4) | 1;
+ }
+#if ASTERISK_VERSION_NUM >= 110000
+ ast_channel_redirecting_set(ast, ast_redir);
+#endif
+ }
+#else
+ memset(&ast->cid, 0, sizeof(ast->cid));
+ if (param->setup.callerinfo.id[0])
+ ast->cid.cid_num = strdup(param->setup.callerinfo.id);
+ if (param->setup.callerinfo.id2[0])
+ ast->cid.cid_ani = strdup(param->setup.callerinfo.id2);
+ if (param->setup.callerinfo.name[0])
+ ast->cid.cid_name = strdup(param->setup.callerinfo.name);
+ if (param->setup.redirinfo.id[0])
+ ast->cid.cid_rdnis = strdup(numberrize_callerinfo(param->setup.redirinfo.id, param->setup.redirinfo.ntype, options.national, options.international));
+ switch (param->setup.callerinfo.present) {
+ case INFO_PRESENT_ALLOWED:
+ ast->cid.cid_pres = AST_PRES_ALLOWED;
+ break;
+ case INFO_PRESENT_RESTRICTED:
+ ast->cid.cid_pres = AST_PRES_RESTRICTED;
+ break;
+ default:
+ ast->cid.cid_pres = AST_PRES_UNAVAILABLE;
+ }
+ switch (param->setup.callerinfo.ntype) {
+ case INFO_NTYPE_SUBSCRIBER:
+ ast->cid.cid_ton = 4;
+ break;
+ case INFO_NTYPE_NATIONAL:
+ ast->cid.cid_ton = 2;
+ break;
+ case INFO_NTYPE_INTERNATIONAL:
+ ast->cid.cid_ton = 1;
+ break;
+ default:
+ ast->cid.cid_ton = 0;
+ }
+#endif
+
+#if ASTERISK_VERSION_NUM < 110000
+ ast->transfercapability = param->setup.capainfo.bearer_capa;
+#else
+ ast_channel_transfercapability_set(ast, param->setup.capainfo.bearer_capa);
+#endif
+ /* enable hdlc if transcap is data */
+ if (param->setup.capainfo.source_mode == B_MODE_HDLC)
+ call->hdlc = 1;
+ strncpy(call->oad, numberrize_callerinfo(param->setup.callerinfo.id, param->setup.callerinfo.ntype, options.national, options.international), sizeof(call->oad)-1);
+
+ /* configure channel */
+#if ASTERISK_VERSION_NUM < 100000
+ ast->nativeformats = (options.law=='a')?AST_FORMAT_ALAW:AST_FORMAT_ULAW;
+ ast->readformat = ast->rawreadformat = ast->nativeformats;
+ ast->writeformat = ast->rawwriteformat = ast->nativeformats;
+#else
+#if ASTERISK_VERSION_NUM < 110000
+ ast_format_set(&ast->rawwriteformat ,(options.law=='a')?AST_FORMAT_ALAW:AST_FORMAT_ULAW , 0);
+ ast_format_copy(&ast->rawreadformat, &ast->rawwriteformat);
+ ast_format_cap_set(ast->nativeformats, &ast->rawwriteformat);
+ ast_set_write_format(ast, &ast->rawwriteformat);
+ ast_set_read_format(ast, &ast->rawreadformat);
+#else
+ ast_format_set(ast_channel_rawwriteformat(ast) ,(options.law=='a')?AST_FORMAT_ALAW:AST_FORMAT_ULAW , 0);
+ ast_format_copy(ast_channel_rawreadformat(ast), ast_channel_rawwriteformat(ast));
+ ast_format_cap_set(ast_channel_nativeformats(ast), ast_channel_rawwriteformat(ast));
+ ast_set_write_format(ast, ast_channel_rawwriteformat(ast));
+ ast_set_read_format(ast, ast_channel_rawreadformat(ast));
+#endif
+#endif
+#if ASTERISK_VERSION_NUM < 110000
+ ast->priority = 1;
+ ast->hangupcause = 0;
+#else
+ ast_channel_priority_set(ast, 1);
+ ast_channel_hangupcause_set(ast, 0);
+#endif
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_IN_SETUP;
+
+ if (!call->pbx_started)
+ lcr_start_pbx(call, ast, param->setup.dialinginfo.sending_complete);
+}
+
+/*
+ * incoming setup acknowledge from LCR
+ */
+static void lcr_in_overlap(struct chan_call *call, int message_type, union parameter *param)
+{
+ if (!call->ast) return;
+
+ CDEBUG(call, call->ast, "Incomming setup acknowledge from LCR.\n");
+
+ /* send pending digits in dialque */
+ if (call->dialque[0])
+ send_dialque_to_lcr(call);
+ /* change to overlap state */
+ call->state = CHAN_LCR_STATE_OUT_DIALING;
+}
+
+/*
+ * incoming proceeding from LCR
+ */
+static void lcr_in_proceeding(struct chan_call *call, int message_type, union parameter *param)
+{
+ CDEBUG(call, call->ast, "Incomming proceeding from LCR.\n");
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_OUT_PROCEEDING;
+ /* queue event for asterisk */
+ if (call->ast && call->pbx_started) {
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ int rc;
+ rc = write(wake_pipe[1], &byte, 1);
+ }
+ strncat(call->queue_string, "P", sizeof(call->queue_string)-1);
+ }
+
+}
+
+/*
+ * incoming alerting from LCR
+ */
+static void lcr_in_alerting(struct chan_call *call, int message_type, union parameter *param)
+{
+ CDEBUG(call, call->ast, "Incomming alerting from LCR.\n");
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_OUT_ALERTING;
+ /* queue event to asterisk */
+ if (call->ast && call->pbx_started) {
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ int rc;
+ rc = write(wake_pipe[1], &byte, 1);
+ }
+ strncat(call->queue_string, "R", sizeof(call->queue_string)-1);
+ }
+}
+
+/*
+ * incoming connect from LCR
+ */
+static void lcr_in_connect(struct chan_call *call, int message_type, union parameter *param)
+{
+ CDEBUG(call, call->ast, "Incomming connect (answer) from LCR.\n");
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_CONNECT;
+ /* copy connectinfo */
+ memcpy(&call->connectinfo, ¶m->connectinfo, sizeof(struct connect_info));
+ /* queue event to asterisk */
+ if (call->ast && call->pbx_started) {
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ int rc;
+ rc = write(wake_pipe[1], &byte, 1);
+ }
+ strncat(call->queue_string, "N", sizeof(call->queue_string)-1);
+ }
+}
+
+/*
+ * incoming disconnect from LCR
+ */
+static void lcr_in_disconnect(struct chan_call *call, int message_type, union parameter *param)
+{
+ struct ast_channel *ast = call->ast;
+
+ CDEBUG(call, call->ast, "Incomming disconnect from LCR. (cause=%d)\n", param->disconnectinfo.cause);
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_IN_DISCONNECT;
+ /* save cause */
+ call->cause = param->disconnectinfo.cause;
+ call->location = param->disconnectinfo.location;
+ /* if bridge, forward disconnect and return */
+#ifdef TODO
+ feature flag
+ if (call->bridge_call) {
+ CDEBUG(call, call->ast, "Only signal disconnect via bridge.\n");
+ bridge_message_if_bridged(call, message_type, param);
+ return;
+ }
+#endif
+ /* release lcr with same cause */
+ send_release(call, call->cause, call->location);
+ call->ref = 0;
+ /* change to release state */
+ call->state = CHAN_LCR_STATE_RELEASE;
+ /* queue release asterisk */
+ if (ast) {
+#if ASTERISK_VERSION_NUM < 110000
+ ast->hangupcause = call->cause;
+#else
+ ast_channel_hangupcause_set(ast, call->cause);
+#endif
+ if (call->pbx_started) {
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ int rc;
+ rc = write(wake_pipe[1], &byte, 1);
+ }
+ strcpy(call->queue_string, "H"); // overwrite other indications
+ } else {
+ ast_hangup(ast); // call will be destroyed here
+ }
+ }
+}
+
+/*
+ * incoming release from LCR
+ */
+static void lcr_in_release(struct chan_call *call, int message_type, union parameter *param)
+{
+ struct ast_channel *ast = call->ast;
+
+ CDEBUG(call, call->ast, "Incomming release from LCR, releasing ref. (cause=%d)\n", param->disconnectinfo.cause);
+
+ /* release ref */
+ call->ref = 0;
+ /* change to release state */
+ call->state = CHAN_LCR_STATE_RELEASE;
+ /* copy release info */
+ if (!call->cause) {
+ call->cause = param->disconnectinfo.cause;
+ call->location = param->disconnectinfo.location;
+ }
+ /* if we have an asterisk instance, queue hangup, else we are done */
+ if (ast) {
+#if ASTERISK_VERSION_NUM < 110000
+ ast->hangupcause = call->cause;
+#else
+ ast_channel_hangupcause_set(ast, call->cause);
+#endif
+ if (call->pbx_started) {
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ int rc;
+ rc = write(wake_pipe[1], &byte, 1);
+ }
+ strcpy(call->queue_string, "H");
+ } else {
+ ast_hangup(ast); // call will be destroyed here
+ }
+ } else {
+ free_call(call);
+ }
+
+}
+
+/*
+ * incoming information from LCR
+ */
+static void lcr_in_information(struct chan_call *call, int message_type, union parameter *param)
+{
+ struct ast_channel *ast = call->ast;
+
+ CDEBUG(call, call->ast, "Incoming information from LCR. (dialing=%s)\n", param->information.id);
+
+ if (!ast) return;
+
+ /* pbx not started */
+ if (!call->pbx_started) {
+ CDEBUG(call, call->ast, "Asterisk not started, adding digits to number.\n");
+#if ASTERISK_VERSION_NUM < 110000
+ strncat(ast->exten, param->information.id, AST_MAX_EXTENSION-1);
+#else
+ char *tmp[AST_MAX_EXTENSION];
+ snprintf(tmp,AST_MAX_EXTENSION,"%s%s",ast_channel_exten(ast),param->information.id);
+ ast_channel_exten_set(ast, tmp);
+#endif
+ lcr_start_pbx(call, ast, param->information.sending_complete);
+ return;
+ }
+
+ /* change dailing state after setup */
+ if (call->state == CHAN_LCR_STATE_IN_SETUP) {
+ CDEBUG(call, call->ast, "Changing from SETUP to DIALING state.\n");
+ call->state = CHAN_LCR_STATE_IN_DIALING;
+// ast_setstate(ast, AST_STATE_DIALING);
+ }
+
+ /* queue digits */
+ if (call->state == CHAN_LCR_STATE_IN_DIALING && param->information.id[0]) {
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ int rc;
+ rc = write(wake_pipe[1], &byte, 1);
+ }
+ strncat(call->queue_string, param->information.id, sizeof(call->queue_string)-1);
+ }
+
+ /* use bridge to forware message not supported by asterisk */
+ if (call->state == CHAN_LCR_STATE_CONNECT) {
+ if (call->bridge_call) {
+ CDEBUG(call, call->ast, "Call is connected, bridging.\n");
+ bridge_message_if_bridged(call, message_type, param);
+ } else {
+ if (call->dsp_dtmf) {
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ int rc;
+ rc = write(wake_pipe[1], &byte, 1);
+ }
+ strncat(call->queue_string, param->information.id, sizeof(call->queue_string)-1);
+ } else
+ CDEBUG(call, call->ast, "LCR's DTMF detection is disabled.\n");
+ }
+ }
+}
+
+/*
+ * incoming information from LCR
+ */
+static void lcr_in_notify(struct chan_call *call, int message_type, union parameter *param)
+{
+ CDEBUG(call, call->ast, "Incomming notify from LCR. (notify=%d)\n", param->notifyinfo.notify);
+
+ if (!call->ast) return;
+
+ /* use bridge to forware message not supported by asterisk */
+ bridge_message_if_bridged(call, message_type, param);
+}
+
+/*
+ * incoming information from LCR
+ */
+static void lcr_in_facility(struct chan_call *call, int message_type, union parameter *param)
+{
+ CDEBUG(call, call->ast, "Incomming facility from LCR.\n");
+
+ if (!call->ast) return;
+
+ /* use bridge to forware message not supported by asterisk */
+ bridge_message_if_bridged(call, message_type, param);
+}
+
+/*
+ * incoming pattern from LCR
+ */
+static void lcr_in_pattern(struct chan_call *call, int message_type, union parameter *param)
+{
+ union parameter newparam;
+
+ CDEBUG(call, call->ast, "Incomming pattern indication from LCR.\n");
+
+ if (!call->ast) return;
+
+ /* pattern are indicated only once */
+ if (call->has_pattern)
+ return;
+ call->has_pattern = 1;
+
+ /* request bchannel */
+ CDEBUG(call, call->ast, "Requesting audio path (ref=%d)\n", call->ref);
+ memset(&newparam, 0, sizeof(union parameter));
+ send_message(MESSAGE_AUDIOPATH, call->ref, &newparam);
+
+ /* queue PROGRESS, because tones are available */
+ if (call->ast && call->pbx_started) {
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ int rc;
+ rc = write(wake_pipe[1], &byte, 1);
+ }
+ strncat(call->queue_string, "T", sizeof(call->queue_string)-1);
+ }
+}
+
+/*
+ * got dtmf from bchannel (locked state)
+ */
+void lcr_in_dtmf(struct chan_call *call, int val)
+{
+ struct ast_channel *ast = call->ast;
+ char digit[2];
+
+ if (!ast)
+ return;
+ if (!call->pbx_started)
+ return;
+
+ if (!call->dsp_dtmf) {
+ CDEBUG(call, call->ast, "Recognised DTMF digit '%c' by LCR, but ignoring. (disabled by option)\n", val);
+ return;
+ }
+
+ CDEBUG(call, call->ast, "Recognised DTMF digit '%c'.\n", val);
+ digit[0] = val;
+ digit[1] = '\0';
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ int rc;
+ rc = write(wake_pipe[1], &byte, 1);
+ }
+ strncat(call->queue_string, digit, sizeof(call->queue_string)-1);
+}
+
+/*
+ * message received from LCR
+ */
+int receive_message(int message_type, unsigned int ref, union parameter *param)
+{
+ struct chan_call *call;
+ union parameter newparam;
+ int rc = 0;
+
+ memset(&newparam, 0, sizeof(union parameter));
+
+ /* handle new ref */
+ if (message_type == MESSAGE_NEWREF) {
+ if (param->newref.direction) {
+ /* new ref from lcr */
+ CDEBUG(NULL, NULL, "Received new ref by LCR, due to incomming call. (ref=%ld)\n", ref);
+ if (!ref || find_call_ref(ref)) {
+ CERROR(NULL, NULL, "Illegal new ref %ld received.\n", ref);
+ return -1;
+ }
+ /* allocate new call instance */
+ call = alloc_call();
+ /* new state */
+ call->state = CHAN_LCR_STATE_IN_PREPARE;
+ /* set ref */
+ call->ref = ref;
+ call->ref_was_assigned = 1;
+ /* wait for setup (or release from asterisk) */
+ } else {
+ /* new ref, as requested from this remote application */
+ CDEBUG(NULL, NULL, "Received new ref by LCR, as requested from chan_lcr. (ref=%ld)\n", ref);
+ call = find_call_ref(0);
+ if (!call) {
+ /* send release, if ref does not exist */
+ CERROR(NULL, NULL, "No call found, that requests a ref.\n");
+ return 0;
+ }
+ /* store new ref */
+ call->ref = ref;
+ call->ref_was_assigned = 1;
+ /* send pending setup info */
+ if (call->state == CHAN_LCR_STATE_OUT_PREPARE)
+ send_setup_to_lcr(call);
+ /* release if asterisk has signed off */
+ else if (call->state == CHAN_LCR_STATE_RELEASE) {
+ /* send release */
+ if (call->cause)
+ send_release(call, call->cause, call->location);
+ else
+ send_release(call, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL);
+ /* free call */
+ free_call(call);
+ return 0;
+ }
+ }
+ send_message(MESSAGE_ENABLEKEYPAD, call->ref, &newparam);
+ return 0;
+ }
+
+ /* check ref */
+ if (!ref) {
+ CERROR(NULL, NULL, "Received message %d without ref.\n", message_type);
+ return -1;
+ }
+ call = find_call_ref(ref);
+ if (!call) {
+ /* ignore ref that is not used (anymore) */
+ CDEBUG(NULL, NULL, "Message %d from LCR ignored, because no call instance found.\n", message_type);
+ return 0;
+ }
+
+ /* handle messages */
+ switch(message_type) {
+ case MESSAGE_SETUP:
+ lcr_in_setup(call, message_type, param);
+ break;
+
+ case MESSAGE_OVERLAP:
+ lcr_in_overlap(call, message_type, param);
+ break;
+
+ case MESSAGE_PROCEEDING:
+ lcr_in_proceeding(call, message_type, param);
+ break;
+
+ case MESSAGE_ALERTING:
+ lcr_in_alerting(call, message_type, param);
+ break;
+
+ case MESSAGE_CONNECT:
+ lcr_in_connect(call, message_type, param);
+ break;
+
+ case MESSAGE_DISCONNECT:
+ lcr_in_disconnect(call, message_type, param);
+ break;
+
+ case MESSAGE_RELEASE:
+ lcr_in_release(call, message_type, param);
+ break;
+
+ case MESSAGE_INFORMATION:
+ lcr_in_information(call, message_type, param);
+ break;
+
+ case MESSAGE_NOTIFY:
+ lcr_in_notify(call, message_type, param);
+ break;
+
+ case MESSAGE_FACILITY:
+ lcr_in_facility(call, message_type, param);
+ break;
+
+ case MESSAGE_PATTERN: // audio available from LCR
+ if (!call->has_pattern)
+ lcr_in_pattern(call, message_type, param);
+ break;
+
+ case MESSAGE_NOPATTERN: // audio not available from LCR
+ break;
+
+ case MESSAGE_AUDIOPATH: // if remote audio connected or hold
+ call->audiopath = param->audiopath;
+ break;
+
+ case MESSAGE_TRAFFIC: // if remote audio connected or hold
+ {
+ unsigned char *p = param->traffic.data;
+ int i, len = param->traffic.len;
+ for (i = 0; i < len; i++, p++)
+ *p = flip_bits[*p];
+ }
+ rc = write(call->pipe[1], param->traffic.data, param->traffic.len);
+ break;
+
+ case MESSAGE_DTMF:
+ lcr_in_dtmf(call, param->dtmf);
+ break;
+
+ default:
+ CDEBUG(call, call->ast, "Message %d from LCR unhandled.\n", message_type);
+ break;
+ }
+ return rc;
+}