Added layer1 hold feature. Requires new mISDN and mISDNuser package from git.
[lcr.git] / interface.c
index 1a17842..28ad88c 100644 (file)
@@ -9,9 +9,6 @@
 **                                                                           **
 \*****************************************************************************/ 
 
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
 #include "main.h"
 
 struct interface *interface_first = NULL; /* first interface is current list */
@@ -158,6 +155,7 @@ static int inter_ptp(struct interface *interface, char *filename, int line, char
        ifport->ptp = 1;
        return(0);
 }
+#if 0
 static int inter_ptmp(struct interface *interface, char *filename, int line, char *parameter, char *value)
 {
        struct interface_port *ifport;
@@ -186,6 +184,53 @@ static int inter_ptmp(struct interface *interface, char *filename, int line, cha
        ifport->ptmp = 1;
        return(0);
 }
+#endif
+static int inter_nt(struct interface *interface, char *filename, int line, char *parameter, char *value)
+{
+       struct interface_port *ifport;
+
+       /* port in chain ? */
+       if (!interface->ifport)
+       {
+               SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
+               return(-1);
+       }
+       /* goto end of chain */
+       ifport = interface->ifport;
+       while(ifport->next)
+               ifport = ifport->next;
+       /* add value */
+       if (value[0])
+       {
+               SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects no value.\n", filename, line, parameter);
+               return(-1);
+       }
+       ifport->nt = 1;
+       return(0);
+}
+static int inter_tespecial(struct interface *interface, char *filename, int line, char *parameter, char *value)
+{
+       struct interface_port *ifport;
+
+       /* port in chain ? */
+       if (!interface->ifport)
+       {
+               SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
+               return(-1);
+       }
+       /* goto end of chain */
+       ifport = interface->ifport;
+       while(ifport->next)
+               ifport = ifport->next;
+       /* add value */
+       if (value[0])
+       {
+               SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects no value.\n", filename, line, parameter);
+               return(-1);
+       }
+       ifport->tespecial = 1;
+       return(0);
+}
 static int inter_tones(struct interface *interface, char *filename, int line, char *parameter, char *value)
 {
        if (!strcasecmp(value, "yes"))
@@ -236,6 +281,11 @@ static int inter_hunt(struct interface *interface, char *filename, int line, cha
 }
 static int inter_port(struct interface *interface, char *filename, int line, char *parameter, char *value)
 {
+       SPRINT(interface_error, "Error in %s (line %d): parameter '%s' is outdated.\nPlease use 'portnum' and decrease port number by 1! Ports are counted from 0 now.\n", filename, line, parameter);
+       return(-1);
+}
+static int inter_portnum(struct interface *interface, char *filename, int line, char *parameter, char *value)
+{
        struct interface_port *ifport, **ifportp;
        struct interface *searchif;
        int val;
@@ -275,6 +325,97 @@ static int inter_port(struct interface *interface, char *filename, int line, cha
        *ifportp = ifport;
        return(0);
 }
+static int inter_portname(struct interface *interface, char *filename, int line, char *parameter, char *value)
+{
+       struct interface_port *ifport, **ifportp;
+       struct interface *searchif;
+
+       /* check for port already assigned */
+       searchif = interface_newlist;
+       while(searchif)
+       {
+               ifport = searchif->ifport;
+               while(ifport)
+               {
+                       if (!strcasecmp(ifport->portname, value))
+                       {
+                               SPRINT(interface_error, "Error in %s (line %d): port '%s' already used above.\n", filename, line, value);
+                               return(-1);
+                       }
+                       ifport = ifport->next;
+               }
+               searchif = searchif->next;
+       }
+       /* alloc port substructure */
+       ifport = (struct interface_port *)MALLOC(sizeof(struct interface_port));
+       memuse++;
+       ifport->interface = interface;
+       /* set value */
+       ifport->portnum = -1; // disable until resolved
+       SCPY(ifport->portname, value);
+       /* tail port */
+       ifportp = &interface->ifport;
+       while(*ifportp)
+               ifportp = &((*ifportp)->next);
+       *ifportp = ifport;
+       return(0);
+}
+static int inter_l1hold(struct interface *interface, char *filename, int line, char *parameter, char *value)
+{
+       struct interface_port *ifport;
+
+       /* port in chain ? */
+       if (!interface->ifport)
+       {
+               SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
+               return(-1);
+       }
+       /* goto end of chain */
+       ifport = interface->ifport;
+       while(ifport->next)
+               ifport = ifport->next;
+       if (!strcmp(value, "yes"))
+       {
+               ifport->l1hold = 1;
+       } else
+       if (!strcmp(value, "no"))
+       {
+               ifport->l1hold = 0;
+       } else
+       {
+               SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expecting parameter 'yes' or 'no'.\n", filename, line, parameter);
+               return(-1);
+       }
+       return(0);
+}
+static int inter_l2hold(struct interface *interface, char *filename, int line, char *parameter, char *value)
+{
+       struct interface_port *ifport;
+
+       /* port in chain ? */
+       if (!interface->ifport)
+       {
+               SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
+               return(-1);
+       }
+       /* goto end of chain */
+       ifport = interface->ifport;
+       while(ifport->next)
+               ifport = ifport->next;
+       if (!strcmp(value, "yes"))
+       {
+               ifport->l2hold = 1;
+       } else
+       if (!strcmp(value, "no"))
+       {
+               ifport->l2hold = -1;
+       } else
+       {
+               SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expecting parameter 'yes' or 'no'.\n", filename, line, parameter);
+               return(-1);
+       }
+       return(0);
+}
 static int inter_channel_out(struct interface *interface, char *filename, int line, char *parameter, char *value)
 {
        struct interface_port *ifport;
@@ -409,6 +550,55 @@ static int inter_channel_in(struct interface *interface, char *filename, int lin
        }
        return(0);
 }
+static int inter_timeouts(struct interface *interface, char *filename, int line, char *parameter, char *value)
+{
+       struct interface_port *ifport;
+//     struct select_channel *selchannel, **selchannelp;
+//     int val;
+       char *p, *el;
+
+       /* port in chain ? */
+       if (!interface->ifport)
+       {
+               SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
+               return(-1);
+       }
+       /* goto end of chain */
+       ifport = interface->ifport;
+       while(ifport->next)
+               ifport = ifport->next;
+       p = value;
+       if (!*p)
+       {
+               nofive:
+               SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects five timeout values.\n", filename, line, parameter);
+               return(-1);
+       }
+       el = p;
+       p = get_seperated(p);
+       ifport->tout_setup = atoi(el);
+       if (!*p)
+               goto nofive;
+       el = p;
+       p = get_seperated(p);
+       ifport->tout_dialing = atoi(el);
+       if (!*p)
+               goto nofive;
+       el = p;
+       p = get_seperated(p);
+       ifport->tout_proceeding = atoi(el);
+       if (!*p)
+               goto nofive;
+       el = p;
+       p = get_seperated(p);
+       ifport->tout_alerting = atoi(el);
+       if (!*p)
+               goto nofive;
+       el = p;
+       p = get_seperated(p);
+       ifport->tout_disconnect = atoi(el);
+       return(0);
+}
 static int inter_msn(struct interface *interface, char *filename, int line, char *parameter, char *value)
 {
        struct interface_msn *ifmsn, **ifmsnp;
@@ -454,9 +644,6 @@ static int inter_screen(struct interface_screen **ifscreenp, struct interface *i
                SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects old caller ID and new caller ID.\n", filename, line, parameter);
                return(-1);
        }
-       p = value;
-       el = p;
-       p = get_seperated(p);
        /* add screen entry to list*/
        ifscreen = (struct interface_screen *)MALLOC(sizeof(struct interface_screen));
        memuse++;
@@ -468,7 +655,9 @@ static int inter_screen(struct interface_screen **ifscreenp, struct interface *i
        while(*ifscreenp)
                ifscreenp = &((*ifscreenp)->next);
        *ifscreenp = ifscreen;
+//     printf("interface=%s\n", interface->name);
        /* get match */
+       p = value;
        while(*p)
        {
                el = p;
@@ -511,7 +700,7 @@ static int inter_screen(struct interface_screen **ifscreenp, struct interface *i
                        }
                        ifscreen->match_present = INFO_PRESENT_ALLOWED;
                } else
-               if (!strcasecmp(el, "restricted"))
+               if (!strcasecmp(el, "restrict") || !strcasecmp(el, "restricted"))
                {
                        if (ifscreen->match_present != -1)
                                goto presenterror;
@@ -564,13 +753,13 @@ static int inter_screen(struct interface_screen **ifscreenp, struct interface *i
                                goto typeerror;
                        ifscreen->result_type = INFO_NTYPE_INTERNATIONAL;
                } else
-               if (!strcasecmp(el, "allowed"))
+               if (!strcasecmp(el, "present") || !strcasecmp(el, "presented") || !strcasecmp(el, "allowed") || !strcasecmp(el, "allow"))
                {
                        if (ifscreen->result_present != -1)
                                goto presenterror;
                        ifscreen->result_present = INFO_PRESENT_ALLOWED;
                } else
-               if (!strcasecmp(el, "restricted"))
+               if (!strcasecmp(el, "restrict") || !strcasecmp(el, "restricted") || !strcasecmp(el, "deny") || !strcasecmp(el, "denied"))
                {
                        if (ifscreen->result_present != -1)
                                goto presenterror;
@@ -627,13 +816,133 @@ static int inter_nodtmf(struct interface *interface, char *filename, int line, c
        ifport->nodtmf = 1;
        return(0);
 }
-#warning filter to be done
-#if 0
 static int inter_filter(struct interface *interface, char *filename, int line, char *parameter, char *value)
 {
+       char *p, *q;
+
+       /* seperate parameter from filter */
+       p = value;
+       while(*p > 32)
+               p++;
+       if (*p)
+       {
+               *p++ = 0;
+               while(*p > 0 && *p <= 32)
+                       p++;
+       }
+
+       if (!strcasecmp(value, "gain"))
+       {
+               q = p;
+               while(*q > 32)
+                       q++;
+               if (*q)
+               {
+                       *q++ = 0;
+                       while(*q > 0 && *q <= 32)
+                               q++;
+               }
+               if (*p == 0 || *q == 0)
+               {
+                       SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' expects two gain values.\n", filename, line, parameter, value);
+                       return(-1);
+               }
+               if (atoi(p)<-8 || atoi(p)>8 || atoi(q)<-8 || atoi(q)>8)
+               {
+                       SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' gain values not in range. (-8...8)\n", filename, line, parameter, value);
+                       return(-1);
+               }
+               interface->tx_gain = atoi(p);
+               interface->rx_gain = atoi(q);
+       } else
+       if (!strcasecmp(value, "pipeline"))
+       {
+               if (*p == 0)
+               {
+                       SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' expects pipeline string.\n", filename, line, parameter, value);
+                       return(-1);
+               }
+               SCPY(interface->pipeline, p);
+       } else
+       if (!strcasecmp(value, "blowfish"))
+       {
+               unsigned char key[56];
+               int l;
+               
+               if (!!strncmp(p, "0x", 2))
+               {
+                       SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' expects blowfish key starting with '0x'.\n", filename, line, parameter, value);
+                       return(-1);
+               }
+               p += 2;
+               l = 0; 
+               while(*p)
+               {
+                       if (l == 56)
+                       {
+                               SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' key too long.\n", filename, line, parameter, value);
+                               return(-1);
+                       }
+                       if (*p >= '0' && *p <= '9')
+                               key[l] = (*p-'0')<<4;
+                       else if (*p >= 'a' && *p <= 'f')
+                               key[l] = (*p-'a'+10)<<4;
+                       else if (*p >= 'A' && *p <= 'F')
+                               key[l] = (*p-'A'+10)<<4;
+                       else
+                       {
+                               digout:
+                               SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' key has digits out of range. (0...9, a...f)\n", filename, line, parameter, value);
+                               return(-1);
+                       }
+                       p++;
+                       if (*p == 0)
+                       {
+                               SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' key must end on an 8 bit boundary (two character boundary).\n", filename, line, parameter, value);
+                               return(-1);
+                       }
+                       if (*p >= '0' && *p <= '9')
+                               key[l] = (*p-'0')<<4;
+                       else if (*p >= 'a' && *p <= 'f')
+                               key[l] = (*p-'a'+10)<<4;
+                       else if (*p >= 'A' && *p <= 'F')
+                               key[l] = (*p-'A'+10)<<4;
+                       else
+                               goto digout;
+                       p++;
+                       l++;
+               }
+               if (l < 4)
+               {
+                       SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' key must be at least 4 bytes (8 characters).\n", filename, line, parameter, value);
+                       return(-1);
+               }
+               memcpy(interface->bf_key, key, l);
+               interface->bf_len = l;
+       } else
+       {
+               SPRINT(interface_error, "Error in %s (line %d): parameter '%s' has unknown filter '%s'.\n", filename, line, parameter, value);
+               return(-1);
+       }
+       return(0);
+}
+static int inter_dialmax(struct interface *interface, char *filename, int line, char *parameter, char *value)
+{
+       struct interface_port *ifport;
+
+       /* port in chain ? */
+       if (!interface->ifport)
+       {
+               SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
+               return(-1);
+       }
+       /* goto end of chain */
+       ifport = interface->ifport;
+       while(ifport->next)
+               ifport = ifport->next;
+       ifport->dialmax = atoi(value);
        return(0);
 }
-#endif
 
 
 /*
@@ -643,16 +952,20 @@ struct interface_param interface_param[] = {
        { "extension", &inter_extension, "",
        "If keyword is given, calls to interface are handled as internal extensions."},
        {"tones", &inter_tones, "yes | no",
-       "Interface generates tones during call setup and release, or not.\nBy default only NT-mode interfaces generate tones."},
+       "Interface generates tones during call setup and release, or not.\nBy default only NT-mode ports generate tones."},
 
        {"earlyb", &inter_earlyb, "yes | no",
-       "Interface receives and bridges tones during call setup and release, or not.\nBy default only TE-mode interfaces receive tones."},
+       "Interface receives and bridges tones during call setup and release, or not.\nBy default only TE-mode ports receive tones."},
 
        {"hunt", &inter_hunt, "linear | roundrobin",
        "Select the algorithm for selecting port with free channel."},
 
        {"port", &inter_port, "<number>",
+       ""},
+       {"portnum", &inter_portnum, "<number>",
        "Give exactly one port for this interface.\nTo give multiple ports, add more lines with port parameters."},
+       {"portname", &inter_portname, "<name>",
+       "Same as 'portnum', but the name is given instead.\nUse 'isdninfo' to list all available ports and names."},
 
        {"block", &inter_block, "",
        "If keyword is given, calls on this interface are blocked.\n"
@@ -663,12 +976,37 @@ struct interface_param interface_param[] = {
        "This is required on NT-mode ports that are multipoint by default.\n"
        "This parameter must follow a 'port' parameter."},
 
+#if 0
        {"ptmp", &inter_ptmp, "",
        "The given port above is opened as point-to-multipoint.\n"
        "This is required on PRI NT-mode ports that are point-to-point by default.\n"
        "This parameter must follow a 'port' parameter."},
+#endif
+
+       {"nt", &inter_nt, "",
+       "The given port above is opened in NT-mode.\n"
+       "This is required on interfaces that support both NT-mode and TE-mode.\n"
+       "This parameter must follow a 'port' parameter."},
 
-       {"channel_out", &inter_channel_out, "[force,][<number>][,...][,free][,any][,no]",
+       {"te-special", &inter_tespecial, "",
+       "The given port uses a modified TE-mode.\n"
+       "All information elements that are allowed Network->User will then be\n"
+       "transmitted User->Network also. This is usefull to pass all informations\n"
+       "between two interconnected LCRs, like 'redirected number' or 'display'.\n"
+       "Note that this is not compliant with ISDN protocol.\n"
+       "This parameter must follow a 'port' parameter."},
+
+       {"layer1hold", &inter_l1hold, "yes | no",
+       "The given port will not release layer 1 after layer 2 is down.\n"
+       "It is required to keep layer 1 of telephones up, to solve activation problems.\n"
+       "This parameter must follow a 'port' parameter."},
+
+       {"layer2hold", &inter_l2hold, "yes | no",
+       "The given port will continuously try to establish layer 2 link and hold it.\n"
+       "It is required for PTP links in most cases, therefore it is default.\n"
+       "This parameter must follow a 'port' parameter."},
+
+       {"channel-out", &inter_channel_out, "[force,][<number>][,...][,free][,any][,no]",
        "Channel selection list for all outgoing calls to the interface.\n"
        "A free channels is searched in order of appearance.\n"
        "This parameter must follow a 'port' parameter.\n"
@@ -678,30 +1016,35 @@ struct interface_param interface_param[] = {
        " any - On outgoing calls, signal 'any channel acceptable'. (see DSS1)\n"
        " no - Signal 'no channel available' aka 'call waiting'. (see DSS1)"},
 
-       {"channel_in", &inter_channel_in, "[force,][<number>][,...][,free][,any][,no]",
-       "Channel selection list for all incomming calls from the interface.\n"
+       {"channel-in", &inter_channel_in, "[<number>][,...][,free]",
+       "Channel selection list for all incoming calls from the interface.\n"
        "A free channels is accepted if in the list.\n"
-       "If no channel was requested, the first free channel found is selected.\n"
+       "If any channel was requested, the first free channel found is selected.\n"
        "This parameter must follow a 'port' parameter.\n"
        " <number>[,...] - List of channels to accept.\n"
        " free - Accept any free channel"},
 
+       {"timeouts", &inter_timeouts, "<setup> <dialing> <proceeding> <alerting> <disconnect>",
+       "Timeout values for call states. They are both for incoming and outgoing states.\n"
+       "The default is 120 seconds for all states. Use 0 to disable.\n"
+       "This parameter must follow a 'port' parameter.\n"},
+
        {"msn", &inter_msn, "<default MSN>,[<additional MSN>[,...]]",
-       "Incomming caller ID is checked against given MSN numbers.\n"
+       "Incoming caller ID is checked against given MSN numbers.\n"
        "If the caller ID is not found in this list, it is overwritten by the first MSN"},
 
-       {"screen-in", &inter_screen_in, "[options] <old caller ID>[%%] [options] <new caller ID>[%%]",
-       "Adds an entry for incomming calls to the caller ID screen list.\n"
+       {"screen-in", &inter_screen_in, "[options] <old caller ID>[%] [options] <new caller ID>[%]",
+       "Adds an entry for incoming calls to the caller ID screen list.\n"
        "If the given 'old caller ID' matches, it is replaced by the 'new caller ID'\n"
-       "If '%%' is given after old caller ID, it matches even if caller ID has\n"
+       "If '%' is given after old caller ID, it matches even if caller ID has\n"
        "additional digits.\n"
-       "If '%%' is given after mew caller ID, additinal digits of the 'old caller ID'\n"
+       "If '%' is given after mew caller ID, additinal digits of the 'old caller ID'\n"
        "are added.\n"
        "Options can be:\n"
        " unknown | subsciber | national | international - Change caller ID type.\n"
        " present | restrict - Change presentation of caller ID."},
                
-       {"screen-out", &inter_screen_out, "<old caller ID> <new caller ID> [options]",
+       {"screen-out", &inter_screen_out, "[options] <old caller ID>[%] [options] <new caller ID>[%]",
        "Adds an entry for outgoing calls to the caller ID screen list.\n"
        "See 'screen-in' for help."},
 
@@ -709,13 +1052,14 @@ struct interface_param interface_param[] = {
        "Disables DTMF detection for this interface.\n"
        "This parameter must follow a 'port' parameter."},
 
-#if 0
-#warning todo: filter, also in the PmISDN object
-       {"filter", &inter_filter, "<filter> [parameters]",
+       {"filter", &inter_filter, "<filter> <parameters>",
        "Adds/appends a filter. Filters are ordered in transmit direction.\n"
        "gain <tx-volume> <rx-volume> - Changes volume (-8 .. 8)\n"
+       "pipeline <string> - Sets echo cancelation pipeline.\n"
        "blowfish <key> - Adds encryption. Key must be 4-56 bytes (8-112 hex characters."},
-#endif
+
+       {"dialmax", &inter_dialmax, "<digits>",
+       "Limits the number of digits in setup/information message."},
 
        {NULL, NULL, NULL, NULL}
 };
@@ -742,7 +1086,7 @@ struct interface *read_interfaces(void)
        if (interface_newlist != NULL)
                FATAL("list is not empty.\n");
        interface_error[0] = '\0';
-       SPRINT(filename, "%s/interface.conf", INSTALL_DATA);
+       SPRINT(filename, "%s/interface.conf", CONFIG_DATA);
 
        if (!(fp = fopen(filename,"r")))
        {
@@ -895,7 +1239,6 @@ void free_interfaces(struct interface *interface)
        struct select_channel *selchannel;
        struct interface_msn *ifmsn;
        struct interface_screen *ifscreen;
-       struct interface_filter *iffilter;
 
        while(interface)
        {
@@ -947,14 +1290,6 @@ void free_interfaces(struct interface *interface)
                        FREE(temp, sizeof(struct interface_screen));
                        memuse--;
                }
-               iffilter = interface->iffilter;
-               while(iffilter)
-               {
-                       temp = iffilter;
-                       iffilter = iffilter->next;
-                       FREE(temp, sizeof(struct interface_filter));
-                       memuse--;
-               }
                temp = interface;
                interface = interface->next;
                FREE(temp, sizeof(struct interface));
@@ -1019,8 +1354,11 @@ void relink_interfaces(void)
                        mISDNport = mISDNport_first;
                        while(mISDNport)
                        {
+                               if (!strcmp(mISDNport->name, ifport->portname))
+                                       ifport->portnum = mISDNport->portnum; /* same name, so we use same number */
                                if (mISDNport->portnum == ifport->portnum)
                                {
+                                       PDEBUG(DEBUG_ISDN, "Port %d:%s relinking!\n", mISDNport->portnum);
                                        ifport->mISDNport = mISDNport;
                                        mISDNport->ifport = ifport;
                                        set_defaults(ifport);
@@ -1074,12 +1412,16 @@ void load_port(struct interface_port *ifport)
        struct mISDNport *mISDNport;
 
        /* open new port */
-       mISDNport = mISDNport_open(ifport->portnum, ifport->ptp, ifport->ptmp, ifport->interface);
+       mISDNport = mISDNport_open(ifport->portnum, ifport->portname, ifport->ptp, ifport->nt, ifport->tespecial, ifport->l1hold, ifport->l2hold, ifport->interface);
        if (mISDNport)
        {
                /* link port */
                ifport->mISDNport = mISDNport;
                mISDNport->ifport = ifport;
+               /* set number and name */
+               ifport->portnum = mISDNport->portnum;
+               SCPY(ifport->portname, mISDNport->name);
+               /* set defaults */
                set_defaults(ifport);
        } else
        {
@@ -1101,16 +1443,173 @@ void doc_interface(void)
        ifparam = interface_param;
        while(ifparam->name)
        {
-               printf("%s %s\n", ifparam->name, ifparam->usage);
+               if (ifparam->name[0])
+                       printf("%s %s\n", ifparam->name, ifparam->usage);
                ifparam++;
        }
 
        ifparam = interface_param;
        while(ifparam->name)
        {
-               printf("\nParameter: %s %s\n", ifparam->name, ifparam->usage);
-               printf("%s\n", ifparam->help);
+               if (ifparam->name[0])
+               {
+                       printf("\nParameter: %s %s\n", ifparam->name, ifparam->usage);
+                       printf("%s\n", ifparam->help);
+               }
                ifparam++;
        }
 }
 
+
+/* screen caller id
+ * out==0: incoming caller id, out==1: outgoing caller id
+ */
+void do_screen(int out, char *id, int idsize, int *type, int *present, struct interface *interface)
+{
+       char                    *msn1;
+       struct interface_msn    *ifmsn;
+       struct interface_screen *ifscreen;
+       char suffix[64];
+
+       /* screen incoming caller id */
+       if (!out)
+       {
+               /* check for MSN numbers, use first MSN if no match */
+               msn1 = NULL;
+               ifmsn = interface->ifmsn;
+               while(ifmsn)
+               {
+                       if (!msn1)
+                               msn1 = ifmsn->msn;
+                       if (!strcmp(ifmsn->msn, id))
+                       {
+                               break;
+                       }
+                       ifmsn = ifmsn->next;
+               }
+               if (ifmsn)
+               {
+                       start_trace(-1, interface, numberrize_callerinfo(id, *type, options.national, options.international), NULL, DIRECTION_IN, 0, 0, "SCREEN (found in MSN list)");
+                       add_trace("msn", NULL, "%s", id);
+                       end_trace();
+               }
+               if (!ifmsn && msn1) // not in list, first msn given
+               {
+                       start_trace(-1, interface, numberrize_callerinfo(id, *type, options.national, options.international), NULL, DIRECTION_IN, 0, 0, "SCREEN (not found in MSN list)");
+                       add_trace("msn", "given", "%s", id);
+                       add_trace("msn", "used", "%s", msn1);
+                       end_trace();
+                       UNCPY(id, msn1, idsize);
+                       id[idsize-1] = '\0';
+               }
+       }
+
+       /* check screen list */
+       if (out)
+               ifscreen = interface->ifscreen_out;
+       else
+               ifscreen = interface->ifscreen_in;
+       while (ifscreen)
+       {
+               if (ifscreen->match_type==-1 || ifscreen->match_type==*type)
+               if (ifscreen->match_present==-1 || ifscreen->match_present==*present)
+               {
+                       if (strchr(ifscreen->match,'%'))
+                       {
+                               if (!strncmp(ifscreen->match, id, strchr(ifscreen->match,'%')-ifscreen->match))
+                                       break;
+                       } else
+                       {
+                               if (!strcmp(ifscreen->match, id))
+                                       break;
+                       }
+               }
+               ifscreen = ifscreen->next;
+       }
+       if (ifscreen) // match
+       {
+               start_trace(-1, interface, numberrize_callerinfo(id, *type, options.national, options.international), NULL, out?DIRECTION_OUT:DIRECTION_IN, 0, 0, "SCREEN (found in screen list)");
+               switch(*type)
+               {
+                       case INFO_NTYPE_UNKNOWN:
+                       add_trace("given", "type", "unknown");
+                       break;
+                       case INFO_NTYPE_SUBSCRIBER:
+                       add_trace("given", "type", "subscriber");
+                       break;
+                       case INFO_NTYPE_NATIONAL:
+                       add_trace("given", "type", "national");
+                       break;
+                       case INFO_NTYPE_INTERNATIONAL:
+                       add_trace("given", "type", "international");
+                       break;
+               }
+               switch(*present)
+               {
+                       case INFO_PRESENT_ALLOWED:
+                       add_trace("given", "present", "allowed");
+                       break;
+                       case INFO_PRESENT_RESTRICTED:
+                       add_trace("given", "present", "restricted");
+                       break;
+                       case INFO_PRESENT_NOTAVAIL:
+                       add_trace("given", "present", "not available");
+                       break;
+               }
+               add_trace("given", "id", "%s", id[0]?id:"<empty>");
+               if (ifscreen->result_type != -1)
+               {
+                       *type = ifscreen->result_type;
+                       switch(*type)
+                       {
+                               case INFO_NTYPE_UNKNOWN:
+                               add_trace("used", "type", "unknown");
+                               break;
+                               case INFO_NTYPE_SUBSCRIBER:
+                               add_trace("used", "type", "subscriber");
+                               break;
+                               case INFO_NTYPE_NATIONAL:
+                               add_trace("used", "type", "national");
+                               break;
+                               case INFO_NTYPE_INTERNATIONAL:
+                               add_trace("used", "type", "international");
+                               break;
+                       }
+               }
+               if (ifscreen->result_present != -1)
+               {
+                       *present = ifscreen->result_present;
+                       switch(*present)
+                       {
+                               case INFO_PRESENT_ALLOWED:
+                               add_trace("used", "present", "allowed");
+                               break;
+                               case INFO_PRESENT_RESTRICTED:
+                               add_trace("used", "present", "restricted");
+                               break;
+                               case INFO_PRESENT_NOTAVAIL:
+                               add_trace("used", "present", "not available");
+                               break;
+                       }
+               }
+               if (strchr(ifscreen->match,'%'))
+               {
+                       SCPY(suffix, strchr(ifscreen->match,'%') - ifscreen->match + id);
+                       UNCPY(id, ifscreen->result, idsize);
+                       id[idsize-1] = '\0';
+                       if (strchr(id,'%'))
+                       {
+                               *strchr(id,'%') = '\0';
+                               UNCAT(id, suffix, idsize);
+                               id[idsize-1] = '\0';
+                       }
+               } else
+               {
+                       UNCPY(id, ifscreen->result, idsize);
+                       id[idsize-1] = '\0';
+               }
+               add_trace("used", "id", "%s", id[0]?id:"<empty>");
+               end_trace();
+       }
+}
+