+ 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, ast->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, ast->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 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");
+ ret = ast_pbx_start(ast);
+ if (ret < 0)
+ {
+ cause = (ret==-2)?34:27;
+ goto release;
+ }
+ call->pbx_started = 1;
+// if (call->state == CHAN_LCR_STATE_IN_DIALING)
+ ast_setstate(ast, AST_STATE_RINGING);
+// else
+// ast_setstate(ast, AST_STATE_RINGING);
+// return;
+}
+
+/*
+ * 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 */
+ ast = ast_channel_alloc(1, AST_STATE_RESERVED, NULL, NULL, "", NULL, "", 0, "%s/%d", lcr_type, ++glob_channel);
+ 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_name = strdup(numberrize_callerinfo(param->setup.callerinfo.id, param->setup.callerinfo.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)
+ 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)
+ 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)
+{
+ union parameter newparam;
+
+ CDEBUG(call, call->ast, "Incomming connect (answer) from LCR.\n");
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_CONNECT;
+ /* request bchannel */
+ if (!call->bchannel) {
+ CDEBUG(call, call->ast, "Requesting B-channel.\n");
+ memset(&newparam, 0, sizeof(union parameter));
+ newparam.bchannel.type = BCHANNEL_REQUEST;
+ send_message(MESSAGE_BCHANNEL, call->ref, &newparam);
+ }
+ /* copy connectinfo */
+ memcpy(&call->connectinfo, ¶m->connectinfo, sizeof(struct connect_info));
+ /* queue event to asterisk */
+ if (call->ast && call->pbx_started)
+ strncat(call->queue_string, "A", 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_and_import(call, call->cause, call->location);
+ call->ref = 0;
+ /* change to release state */
+ call->state = CHAN_LCR_STATE_RELEASE;
+ /* queue release asterisk */
+ if (ast)
+ {
+ ast->hangupcause = call->cause;
+ if (call->pbx_started)
+ strcpy(call->queue_string, "H"); // overwrite other indications
+ else {
+ ast_hangup(ast); // call will be destroyed here
+ }
+ }
+}
+
+/*
+ * incoming setup acknowledge 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)
+ {
+ ast->hangupcause = call->cause;
+ if (call->pbx_started)
+ 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");
+ strncat(ast->exten, param->information.id, AST_MAX_EXTENSION-1);
+ 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])
+ 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) {
+ CDEBUG(call, call->ast, "Call is connected, briding.\n");
+ bridge_message_if_bridged(call, message_type, param);
+ }
+}
+
+/*
+ * incoming information from LCR
+ */
+static void lcr_in_notify(struct chan_call *call, int message_type, union parameter *param)
+{
+ union parameter newparam;
+
+ CDEBUG(call, call->ast, "Incomming notify from LCR. (notify=%d)\n", param->notifyinfo.notify);
+
+ /* request bchannel, if call is resumed and we don't have it */
+ if (param->notifyinfo.notify == INFO_NOTIFY_USER_RESUMED && !call->bchannel && call->ref) {
+ CDEBUG(call, call->ast, "Reqesting bchannel at resume.\n");
+ memset(&newparam, 0, sizeof(union parameter));
+ newparam.bchannel.type = BCHANNEL_REQUEST;
+ send_message(MESSAGE_BCHANNEL, call->ref, &newparam);
+ }
+
+ 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);
+}
+
+/*
+ * 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;
+
+ CDEBUG(call, call->ast, "Recognised DTMF digit '%c'.\n", val);
+ digit[0] = val;
+ digit[1] = '\0';
+ 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 bchannel *bchannel;
+ struct chan_call *call;
+ union parameter newparam;
+
+ memset(&newparam, 0, sizeof(union parameter));
+
+ /* handle bchannel message*/
+ if (message_type == MESSAGE_BCHANNEL)
+ {
+ switch(param->bchannel.type)
+ {
+ case BCHANNEL_ASSIGN:
+ CDEBUG(NULL, NULL, "Received BCHANNEL_ASSIGN message. (handle=%08lx) for ref %d\n", param->bchannel.handle, ref);
+ if ((bchannel = find_bchannel_handle(param->bchannel.handle)))
+ {
+ CERROR(NULL, NULL, "bchannel handle %x already assigned.\n", (int)param->bchannel.handle);
+ return(-1);
+ }
+ /* create bchannel */
+ bchannel = alloc_bchannel(param->bchannel.handle);
+ if (!bchannel)
+ {
+ CERROR(NULL, NULL, "alloc bchannel handle %x failed.\n", (int)param->bchannel.handle);
+ return(-1);
+ }
+
+ /* configure channel */
+ bchannel->b_tx_gain = param->bchannel.tx_gain;
+ bchannel->b_rx_gain = param->bchannel.rx_gain;
+ strncpy(bchannel->b_pipeline, param->bchannel.pipeline, sizeof(bchannel->b_pipeline)-1);
+ if (param->bchannel.crypt_len && param->bchannel.crypt_len <= sizeof(bchannel->b_bf_key))
+ {
+ bchannel->b_bf_len = param->bchannel.crypt_len;
+ memcpy(bchannel->b_bf_key, param->bchannel.crypt, param->bchannel.crypt_len);
+ }
+ bchannel->b_txdata = 0;
+ bchannel->b_dtmf = 1;
+ bchannel->b_tx_dejitter = 1;
+
+ /* in case, ref is not set, this bchannel instance must
+ * be created until it is removed again by LCR */
+ /* link to call */
+ call = find_call_ref(ref);
+ if (call)
+ {
+ bchannel->call = call;
+ call->bchannel = bchannel;
+ if (call->dtmf)
+ bchannel_dtmf(bchannel, 1);
+ if (call->bf_len)
+ bchannel_blowfish(bchannel, call->bf_key, call->bf_len);
+ if (call->pipeline[0])
+ bchannel_pipeline(bchannel, call->pipeline);
+ if (call->rx_gain)
+ bchannel_gain(bchannel, call->rx_gain, 0);
+ if (call->tx_gain)
+ bchannel_gain(bchannel, call->tx_gain, 1);
+ if (call->bridge_id) {
+ CDEBUG(call, call->ast, "Join bchannel, because call is already bridged.\n");
+ bchannel_join(bchannel, call->bridge_id);
+ }
+ /* create only, if call exists, othewhise it bchannel is freed below... */
+ if (bchannel_create(bchannel, ((call->nodsp)?1:0) + ((call->hdlc)?2:0)))
+ bchannel_activate(bchannel, 1);
+ }
+ /* acknowledge */
+ newparam.bchannel.type = BCHANNEL_ASSIGN_ACK;
+ newparam.bchannel.handle = param->bchannel.handle;
+ send_message(MESSAGE_BCHANNEL, 0, &newparam);
+ /* if call has released before bchannel is assigned */
+ if (!call) {
+ newparam.bchannel.type = BCHANNEL_RELEASE;
+ newparam.bchannel.handle = param->bchannel.handle;
+ send_message(MESSAGE_BCHANNEL, 0, &newparam);
+ }
+
+ break;
+
+ case BCHANNEL_REMOVE:
+ CDEBUG(NULL, NULL, "Received BCHANNEL_REMOVE message. (handle=%08lx)\n", param->bchannel.handle);
+ if (!(bchannel = find_bchannel_handle(param->bchannel.handle)))
+ {
+ CERROR(NULL, NULL, "Bchannel handle %x not assigned.\n", (int)param->bchannel.handle);
+ return(-1);
+ }
+ /* unklink from call and destroy bchannel */
+ free_bchannel(bchannel);
+
+ /* acknowledge */
+ newparam.bchannel.type = BCHANNEL_REMOVE_ACK;
+ newparam.bchannel.handle = param->bchannel.handle;
+ send_message(MESSAGE_BCHANNEL, 0, &newparam);
+
+ break;
+
+ default:
+ CDEBUG(NULL, NULL, "Received unknown bchannel message %d.\n", param->bchannel.type);
+ }
+ return(0);
+ }
+
+ /* handle new ref */
+ if (message_type == MESSAGE_NEWREF)
+ {
+ if (param->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;
+ /* 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 */
+ CDEBUG(NULL, NULL, "No call found, that requests a ref.\n");
+ send_release_and_import(call, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL);
+ return(0);
+ }
+ /* store new ref */
+ call->ref = ref;
+ /* 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_and_import(call, call->cause, call->location);
+ else
+ send_release_and_import(call, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL);
+ /* free call */
+ free_call(call);
+ return(0);
+ }
+ }
+ 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
+ break;
+
+ case MESSAGE_NOPATTERN: // audio not available from LCR
+ break;
+
+ case MESSAGE_AUDIOPATH: // if remote audio connected or hold
+ call->audiopath = param->audiopath;
+ break;
+
+ default:
+ CDEBUG(call, call->ast, "Message %d from LCR unhandled.\n", message_type);
+ break;
+ }
+ return(0);
+}
+
+/*
+ * release all calls (due to broken socket)
+ */
+static void release_all_calls(void)
+{
+ struct chan_call *call;
+
+again:
+ call = call_first;
+ while(call) {
+ /* no ast, so we may directly free call */
+ if (!call->ast) {
+ CDEBUG(call, NULL, "Freeing call, because no Asterisk channel is linked.\n");
+ free_call(call);
+ goto again;
+ }
+ /* already in release process */
+ if (call->state == CHAN_LCR_STATE_RELEASE) {
+ call = call->next;
+ continue;
+ }
+ /* release or queue release */
+ call->ref = 0;
+ call->state = CHAN_LCR_STATE_RELEASE;
+ if (!call->pbx_started) {
+ CDEBUG(call, call->ast, "Releasing call, because no Asterisk channel is not started.\n");
+ ast_hangup(call->ast); // call will be destroyed here
+ goto again;
+ }
+ CDEBUG(call, call->ast, "Queue call release, because Asterisk channel is running.\n");
+ strcpy(call->queue_string, "H");
+ call = call->next;
+ }
+
+ /* release all bchannels */
+ while(bchannel_first)
+ free_bchannel(bchannel_first);
+}
+
+
+/* asterisk handler
+ * warning! not thread safe
+ * returns -1 for socket error, 0 for no work, 1 for work
+ */
+int handle_socket(void)
+{
+ int work = 0;
+ int len;
+ struct admin_list *admin;
+ struct admin_message msg;
+
+ /* read from socket */
+ len = read(lcr_sock, &msg, sizeof(msg));
+ if (len == 0)
+ {
+ CERROR(NULL, NULL, "Socket closed.\n");
+ return(-1); // socket closed
+ }
+ if (len > 0)
+ {
+ if (len != sizeof(msg))
+ {
+ CERROR(NULL, NULL, "Socket short read. (len %d)\n", len);
+ return(-1); // socket error
+ }
+ if (msg.message != ADMIN_MESSAGE)
+ {
+ CERROR(NULL, NULL, "Socket received illegal message %d.\n", msg.message);