+ * send release message to LCR and import bchannel if exported
+ */
+static void send_release_and_import(struct chan_call *call, int cause, int location)
+{
+ union parameter newparam;
+
+ /* importing channel */
+ if (call->bchannel) {
+ memset(&newparam, 0, sizeof(union parameter));
+ newparam.bchannel.type = BCHANNEL_RELEASE;
+ newparam.bchannel.handle = call->bchannel->handle;
+ send_message(MESSAGE_BCHANNEL, call->ref, &newparam);
+ }
+ /* sending release */
+ memset(&newparam, 0, sizeof(union parameter));
+ newparam.disconnectinfo.cause = cause;
+ newparam.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
+ send_message(MESSAGE_RELEASE, call->ref, &newparam);
+}
+
+/*
+ * check if extension matches and start asterisk
+ * if it can match, proceed
+ * if not, release
+ */
+static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int complete)
+{
+ int cause, ret;
+ union parameter newparam;
+ char *exten = ast->exten;
+ if (!*exten)
+ exten = "s";
+
+ CDEBUG(call, ast, "Try to start pbx. (exten=%s context=%s complete=%s)\n", exten, ast->context, complete?"yes":"no");
+
+ if (complete) {
+ /* if not match */
+ 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);
+ cause = 1;
+ goto release;
+ }
+ 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);
+ 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 (ast_canmatch_extension(ast, ast->context, exten, 1, call->oad)) {
+ /* 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 (ast_exists_extension(ast, ast->context, exten, 1, call->oad)) {
+ CDEBUG(call, ast, "Extensions matches.\n");
+ goto start;
+ }
+
+ /* if can match */
+ CDEBUG(call, ast, "Extensions may match, if more digits are dialed.\n");
+ return;
+ }
+
+ if (!*ast->exten) {
+ /* 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_and_import(call, cause, LOCATION_PRIVATE_LOCAL);
+ call->ref = 0;
+ /* release asterisk */
+ ast->hangupcause = call->cause;
+ /* 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), "LCR/%s-%04x",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;
+
+ 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
+ ast = ast_channel_alloc(1, AST_STATE_RESERVED, NULL, NULL, "", NULL, "", 0, "%s/%d", lcr_type, ++glob_channel);
+ #endif
+
+ if (!ast) {
+ /* release */
+ CERROR(call, NULL, "Failed to create Asterisk channel - releasing.\n");
+ send_release_and_import(call, CAUSE_RESSOURCEUNAVAIL, LOCATION_PRIVATE_LOCAL);
+ /* remove call */
+ free_call(call);
+ return;
+ }
+ /* link together */
+ call->ast = ast;
+ ast->tech_pvt = call;
+ ast->tech = &lcr_tech;
+ ast->fds[0] = call->pipe[0];
+
+ /* fill setup information */
+ if (param->setup.dialinginfo.id)
+ strncpy(ast->exten, param->setup.dialinginfo.id, AST_MAX_EXTENSION-1);
+ if (param->setup.context[0])
+ strncpy(ast->context, param->setup.context, AST_MAX_CONTEXT-1);
+ else
+ strncpy(ast->context, param->setup.callerinfo.interface, AST_MAX_CONTEXT-1);
+ if (param->setup.callerinfo.id[0])
+ ast->cid.cid_num = strdup(param->setup.callerinfo.id);
+ 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;
+ }
+ ast->transfercapability = param->setup.capainfo.bearer_capa;
+ /* 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 */
+ ast->nativeformats = (options.law=='a')?AST_FORMAT_ALAW:AST_FORMAT_ULAW;
+ ast->readformat = ast->rawreadformat = ast->nativeformats;
+ ast->writeformat = ast->rawwriteformat = ast->nativeformats;
+ ast->priority = 1;
+ ast->hangupcause = 0;
+
+ /* 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;
+ 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;
+ write(wake_pipe[1], &byte, 1);
+ }
+ strncat(call->queue_string, "R", sizeof(call->queue_string)-1);
+ }
+}
+
+/*
+ * incoming connect from LCR