Added keypad forwarding, keypad parameter, chan_lcr keypad option 'k'.
[lcr.git] / apppbx.cpp
index a64467e..9dee717 100644 (file)
@@ -136,7 +136,7 @@ void EndpointAppPBX::trace_header(const char *name, int direction)
        SCPY(msgtext, name);
 
        /* init trace with given values */
-       start_trace(0,
+       start_trace(-1,
                    NULL,
                    numberrize_callerinfo(e_callerinfo.id, e_callerinfo.ntype, options.national, options.international),
                    e_dialinginfo.id,
@@ -550,28 +550,36 @@ struct mISDNport *EndpointAppPBX::hunt_port(char *ifname, int *channel)
        struct select_channel *selchannel; 
        struct mISDNport *mISDNport;
        int index, i;
+       int there_is_an_external = 0;
 
        interface = interface_first;
 
        /* first find the given interface or, if not given, one with no extension */
        checknext:
-       if (!interface)
+       if (!interface) {
+               if (!there_is_an_external && !(ifname && ifname[0])) {
+                       trace_header("CHANNEL SELECTION (no external interface specified)", DIRECTION_NONE);
+                       add_trace("info", NULL, "Add 'external' parameter to interface.conf.");
+                       end_trace();
+               }
                return(NULL);
+       }
 
        /* check for given interface */
-       if (ifname) {
+       if (ifname && ifname[0]) {
                if (!strcasecmp(interface->name, ifname)) {
                        /* found explicit interface */
-                       trace_header("CHANNEL SELECTION (found interface)", DIRECTION_NONE);
+                       trace_header("CHANNEL SELECTION (found given interface)", DIRECTION_NONE);
                        add_trace("interface", NULL, "%s", ifname);
                        end_trace();
                        goto foundif;
                }
 
        } else {
-               if (!interface->extension) {
+               if (interface->external) {
+                       there_is_an_external = 1;
                        /* found non extension */
-                       trace_header("CHANNEL SELECTION (found non extension interface)", DIRECTION_NONE);
+                       trace_header("CHANNEL SELECTION (found external interface)", DIRECTION_NONE);
                        add_trace("interface", NULL, "%s", interface->name);
                        end_trace();
                        goto foundif;
@@ -640,92 +648,108 @@ foundif:
 
        /* check for channel form selection list */
        *channel = 0;
-       selchannel = ifport->out_channel;
-       while(selchannel) {
-               switch(selchannel->channel) {
-                       case CHANNEL_FREE: /* free channel */
-                       if (mISDNport->b_reserved >= mISDNport->b_num)
-                               break; /* all channel in use or reserverd */
-                       /* find channel */
-                       i = 0;
-                       while(i < mISDNport->b_num) {
-                               if (mISDNport->b_port[i] == NULL) {
-                                       *channel = i+1+(i>=15);
-                                       trace_header("CHANNEL SELECTION (selecting free channel)", DIRECTION_NONE);
-                                       add_trace("port", NULL, "%d", ifport->portnum);
-                                       add_trace("position", NULL, "%d", index);
-                                       add_trace("channel", NULL, "%d", *channel);
-                                       end_trace();
-                                       break;
-                               }
-                               i++;
-                       }
-                       if (*channel)
-                               break;
-                       trace_header("CHANNEL SELECTION (no channel is 'free')", DIRECTION_NONE);
+#ifdef WITH_SS5
+       if (mISDNport->ss5) {
+               class Pss5 *port;
+               port = ss5_hunt_line(mISDNport);
+               if (port) {
+                       *channel = port->p_m_b_channel;
+                       trace_header("CHANNEL SELECTION (selecting SS5 channel)", DIRECTION_NONE);
                        add_trace("port", NULL, "%d", ifport->portnum);
                        add_trace("position", NULL, "%d", index);
+                       add_trace("channel", NULL, "%d", *channel);
                        end_trace();
-                       break;
-
-                       case CHANNEL_ANY: /* don't ask for channel */
-                       if (mISDNport->b_reserved >= mISDNport->b_num) {
-                               trace_header("CHANNEL SELECTION (cannot ask for 'any' channel, all reserved)", DIRECTION_NONE);
+               }
+       } else
+#endif
+       {
+               selchannel = ifport->out_channel;
+               while(selchannel) {
+                       switch(selchannel->channel) {
+                               case CHANNEL_FREE: /* free channel */
+                               if (mISDNport->b_reserved >= mISDNport->b_num)
+                                       break; /* all channel in use or reserverd */
+                               /* find channel */
+                               i = 0;
+                               while(i < mISDNport->b_num) {
+                                       if (mISDNport->b_port[i] == NULL) {
+                                               *channel = i+1+(i>=15);
+                                               trace_header("CHANNEL SELECTION (selecting free channel)", DIRECTION_NONE);
+                                               add_trace("port", NULL, "%d", ifport->portnum);
+                                               add_trace("position", NULL, "%d", index);
+                                               add_trace("channel", NULL, "%d", *channel);
+                                               end_trace();
+                                               break;
+                                       }
+                                       i++;
+                               }
+                               if (*channel)
+                                       break;
+                               trace_header("CHANNEL SELECTION (no channel is 'free')", DIRECTION_NONE);
                                add_trace("port", NULL, "%d", ifport->portnum);
                                add_trace("position", NULL, "%d", index);
-                               add_trace("total", NULL, "%d", mISDNport->b_num);
-                               add_trace("reserved", NULL, "%d", mISDNport->b_reserved);
                                end_trace();
-                               break; /* all channel in use or reserverd */
-                       }
-                       trace_header("CHANNEL SELECTION (using 'any' channel)", DIRECTION_NONE);
-                       add_trace("port", NULL, "%d", ifport->portnum);
-                       add_trace("position", NULL, "%d", index);
-                       end_trace();
-                       *channel = CHANNEL_ANY;
-                       break;
-
-                       case CHANNEL_NO: /* call waiting */
-                       trace_header("CHANNEL SELECTION (using 'no' channel, call-waiting)", DIRECTION_NONE);
-                       add_trace("port", NULL, "%d", ifport->portnum);
-                       add_trace("position", NULL, "%d", index);
-                       end_trace();
-                       *channel = CHANNEL_NO;
-                       break;
+                               break;
 
-                       default:
-                       if (selchannel->channel<1 || selchannel->channel==16) {
-                               trace_header("CHANNEL SELECTION (channel out of range)", DIRECTION_NONE);
-                               add_trace("port", NULL, "%d", ifport->portnum);
-                               add_trace("position", NULL, "%d", index);
-                               add_trace("channel", NULL, "%d", selchannel->channel);
-                               end_trace();
-                               break; /* invalid channels */
-                       }
-                       i = selchannel->channel-1-(selchannel->channel>=17);
-                       if (i >= mISDNport->b_num) {
-                               trace_header("CHANNEL SELECTION (channel out of range)", DIRECTION_NONE);
+                               case CHANNEL_ANY: /* don't ask for channel */
+                               if (mISDNport->b_reserved >= mISDNport->b_num) {
+                                       trace_header("CHANNEL SELECTION (cannot ask for 'any' channel, all reserved)", DIRECTION_NONE);
+                                       add_trace("port", NULL, "%d", ifport->portnum);
+                                       add_trace("position", NULL, "%d", index);
+                                       add_trace("total", NULL, "%d", mISDNport->b_num);
+                                       add_trace("reserved", NULL, "%d", mISDNport->b_reserved);
+                                       end_trace();
+                                       break; /* all channel in use or reserverd */
+                               }
+                               trace_header("CHANNEL SELECTION (using 'any' channel)", DIRECTION_NONE);
                                add_trace("port", NULL, "%d", ifport->portnum);
                                add_trace("position", NULL, "%d", index);
-                               add_trace("channel", NULL, "%d", selchannel->channel);
-                               add_trace("channels", NULL, "%d", mISDNport->b_num);
                                end_trace();
-                               break; /* channel not in port */
-                       }
-                       if (mISDNport->b_port[i] == NULL) {
-                               *channel = selchannel->channel;
-                               trace_header("CHANNEL SELECTION (selecting given channel)", DIRECTION_NONE);
+                               *channel = CHANNEL_ANY;
+                               break;
+
+                               case CHANNEL_NO: /* call waiting */
+                               trace_header("CHANNEL SELECTION (using 'no' channel, call-waiting)", DIRECTION_NONE);
                                add_trace("port", NULL, "%d", ifport->portnum);
                                add_trace("position", NULL, "%d", index);
-                               add_trace("channel", NULL, "%d", *channel);
                                end_trace();
+                               *channel = CHANNEL_NO;
+                               break;
+
+                               default:
+                               if (selchannel->channel<1 || selchannel->channel==16) {
+                                       trace_header("CHANNEL SELECTION (channel out of range)", DIRECTION_NONE);
+                                       add_trace("port", NULL, "%d", ifport->portnum);
+                                       add_trace("position", NULL, "%d", index);
+                                       add_trace("channel", NULL, "%d", selchannel->channel);
+                                       end_trace();
+                                       break; /* invalid channels */
+                               }
+                               i = selchannel->channel-1-(selchannel->channel>=17);
+                               if (i >= mISDNport->b_num) {
+                                       trace_header("CHANNEL SELECTION (channel out of range)", DIRECTION_NONE);
+                                       add_trace("port", NULL, "%d", ifport->portnum);
+                                       add_trace("position", NULL, "%d", index);
+                                       add_trace("channel", NULL, "%d", selchannel->channel);
+                                       add_trace("channels", NULL, "%d", mISDNport->b_num);
+                                       end_trace();
+                                       break; /* channel not in port */
+                               }
+                               if (mISDNport->b_port[i] == NULL) {
+                                       *channel = selchannel->channel;
+                                       trace_header("CHANNEL SELECTION (selecting given channel)", DIRECTION_NONE);
+                                       add_trace("port", NULL, "%d", ifport->portnum);
+                                       add_trace("position", NULL, "%d", index);
+                                       add_trace("channel", NULL, "%d", *channel);
+                                       end_trace();
+                                       break;
+                               }
                                break;
                        }
-                       break;
+                       if (*channel)
+                               break; /* found channel */
+                       selchannel = selchannel->next;
                }
-               if (*channel)
-                       break; /* found channel */
-               selchannel = selchannel->next;
        }
 
        /* if channel was found, return mISDNport and channel */
@@ -913,6 +937,11 @@ void EndpointAppPBX::out_setup(void)
                        }
                        /* creating INTERNAL port */
                        SPRINT(portname, "%s-%d-out", mISDNport->ifport->interface->name, mISDNport->portnum);
+#ifdef WITH_SS5
+                       if (mISDNport->ss5)
+                               port = ss5_hunt_line(mISDNport);
+                       else
+#endif
                        if (!mISDNport->gsm)
                                port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode);
                        else
@@ -1026,6 +1055,11 @@ void EndpointAppPBX::out_setup(void)
                                if (mISDNport) {
                                        /* creating EXTERNAL port*/
                                        SPRINT(portname, "%s-%d-out", mISDNport->ifport->interface->name, mISDNport->portnum);
+#ifdef WITH_SS5
+                                       if (mISDNport->ss5)
+                                               port = ss5_hunt_line(mISDNport);
+                                       else
+#endif
                                                port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode);
                                        if (!port)
                                                FATAL("No memory for Port instance\n");
@@ -1094,9 +1128,12 @@ void EndpointAppPBX::out_setup(void)
 
                /* *********************** external call */
                default:
-               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) dialing external: '%s'\n", ea_endpoint->ep_serial, e_dialinginfo.id);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) dialing external: called='%s' keypad='%s'\n", ea_endpoint->ep_serial, e_dialinginfo.id, e_dialinginfo.keypad);
                /* call to extenal interfaces */
-               p = e_dialinginfo.id;
+               if (e_dialinginfo.keypad[0])
+                       p = e_dialinginfo.keypad;
+               else
+                       p = e_dialinginfo.id;
                do {
                        number[0] = '\0';
                        while(*p!=',' && *p!='\0')
@@ -1116,6 +1153,11 @@ void EndpointAppPBX::out_setup(void)
                        }
                        /* creating EXTERNAL port*/
                        SPRINT(portname, "%s-%d-out", mISDNport->ifport->interface->name, mISDNport->portnum);
+#ifdef WITH_SS5
+                       if (mISDNport->ss5)
+                               port = ss5_hunt_line(mISDNport);
+                       else
+#endif
                        if (!mISDNport->gsm)
                                port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode);
                        else
@@ -1129,7 +1171,10 @@ void EndpointAppPBX::out_setup(void)
                        earlyb = mISDNport->earlyb;
                        PDEBUG(DEBUG_EPOINT, "EPOINT(%d) created port %s\n", ea_endpoint->ep_serial, port->p_name);
                        memset(&dialinginfo, 0, sizeof(dialinginfo));
-                       SCPY(dialinginfo.id, number);
+                       if (e_dialinginfo.keypad[0])
+                               SCPY(dialinginfo.keypad, number);
+                       else
+                               SCPY(dialinginfo.id, number);
                        dialinginfo.itype = INFO_ITYPE_ISDN;
                        dialinginfo.ntype = e_dialinginfo.ntype;
                        portlist = ea_endpoint->portlist_new(port->p_serial, port->p_type, mISDNport->earlyb);
@@ -1141,7 +1186,6 @@ void EndpointAppPBX::out_setup(void)
 //printf("EXTERNAL caller=%s,id=%s,dial=%s\n", param.setup.networkid, param.setup.callerinfo.id, param.setup.dialinginfo.id);
                        message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_SETUP);
                        memcpy(&message->param.setup.dialinginfo, &dialinginfo, sizeof(struct dialing_info));
-                       SCPY(message->param.setup.dialinginfo.id, number);
                        memcpy(&message->param.setup.redirinfo, &e_redirinfo, sizeof(struct redir_info));
                        memcpy(&message->param.setup.callerinfo, &e_callerinfo, sizeof(struct caller_info));
                        memcpy(&message->param.setup.capainfo, &e_capainfo, sizeof(struct capa_info));
@@ -1565,7 +1609,7 @@ void EndpointAppPBX::port_information(struct port_list *portlist, int message_ty
        }
 
        /* keypad when connected */
-       if (e_state == EPOINT_STATE_CONNECT) {
+       if (e_state == EPOINT_STATE_CONNECT || e_state == EPOINT_STATE_IN_ALERTING) {
                if (e_ext.keypad || e_enablekeypad) {
                        PDEBUG(DEBUG_EPOINT, "EPOINT(%d) keypad information received during connect: %s.\n", ea_endpoint->ep_serial, param->information.id);
                        /* processing keypad function */
@@ -1639,7 +1683,7 @@ NOTE: vbox is now handled due to overlap state
 #endif
 
        /* check for *X# sequence */
-       if (e_state == EPOINT_STATE_CONNECT) {
+       if (e_state == EPOINT_STATE_CONNECT || e_state == EPOINT_STATE_IN_ALERTING) {
                if (e_dtmf_time+3 < now) {
                        /* the last digit was too far in the past to be a sequence */
                        if (param->dtmf == '*')
@@ -2758,6 +2802,9 @@ void EndpointAppPBX::join_alerting(struct port_list *portlist, int message_type,
                set_tone(portlist, "ringpbx");
        else
                set_tone(portlist, "ringing");
+
+       if (e_ext.number[0])
+               e_dtmf = 1; /* allow dtmf */
 }
 
 /* join MESSAGE_CONNECT */
@@ -3268,7 +3315,7 @@ void EndpointAppPBX::ea_message_join(unsigned int join_id, int message_type, uni
                join_notify(portlist, message_type, param);
                break;
 
-               /* JOIN wants keypad / dtml */
+               /* JOIN wants keypad / dtmf */
                case MESSAGE_ENABLEKEYPAD:
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') received keypad enable request.\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
                e_enablekeypad = 1;
@@ -3514,7 +3561,7 @@ void EndpointAppPBX::join_join(void)
        class Port *our_port, *other_port;
        class Pdss1 *our_pdss1, *other_pdss1;
 
-       /* are we a candidate to join a join */
+       /* are we a candidate to join a join? */
        our_join = find_join_id(ea_endpoint->ep_join_id);
        if (!our_join) {
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our join doesn't exist anymore.\n", ea_endpoint->ep_serial);
@@ -3575,14 +3622,14 @@ void EndpointAppPBX::join_join(void)
                other_eapp = other_eapp->next;
        }
        if (!other_eapp) {
-               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no other endpoint on same isdn interface with port on hold.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no other endpoint on same isdn terminal.\n", ea_endpoint->ep_serial);
                return;
        }
-       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) port on hold found.\n", ea_endpoint->ep_serial);
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) port with same terminal found.\n", ea_endpoint->ep_serial);
 
        /* if we have the same join */
        if (other_eapp->ea_endpoint->ep_join_id == ea_endpoint->ep_join_id) {
-               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we an the other have the same join.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we and the other have the same join.\n", ea_endpoint->ep_serial);
                return;
        }
        other_join = find_join_id(other_eapp->ea_endpoint->ep_join_id);
@@ -3802,6 +3849,8 @@ void EndpointAppPBX::logmessage(int message_type, union parameter *param, unsign
                }
                if (param->setup.dialinginfo.id[0])
                        add_trace("dialing", NULL, "%s", param->setup.dialinginfo.id);
+               if (param->setup.dialinginfo.keypad[0])
+                       add_trace("keypad", NULL, "%s", param->setup.dialinginfo.keypad);
                if (param->setup.dialinginfo.display[0])
                        add_trace("display", NULL, "%s", param->setup.dialinginfo.display);
                if (param->setup.dialinginfo.sending_complete)