socket api work
[lcr.git] / interface.c
1 /*****************************************************************************\
2 **                                                                           **
3 ** LCR                                                                       **
4 **                                                                           **
5 **---------------------------------------------------------------------------**
6 ** Copyright: Andreas Eversberg                                              **
7 **                                                                           **
8 ** reading interface.conf file and filling structure                         **
9 **                                                                           **
10 \*****************************************************************************/ 
11
12 #include "main.h"
13
14 struct interface *interface_first = NULL; /* first interface is current list */
15 struct interface *interface_newlist = NULL; /* first interface in new list */
16
17
18 /* set default out_channel */
19 void default_out_channel(struct interface_port *ifport)
20 {
21         struct select_channel *selchannel, **selchannelp;
22
23         selchannel = (struct select_channel *)MALLOC(sizeof(struct select_channel));
24         memuse++;
25         
26         if (ifport->mISDNport->ntmode)
27                 selchannel->channel = CHANNEL_FREE;
28         else
29                 selchannel->channel = CHANNEL_ANY;
30         
31         ifport->out_channel = selchannel;
32
33         /* additional channel selection for multipoint NT ports */
34         if (!ifport->mISDNport->ptp && ifport->mISDNport->ntmode)
35         {
36                 selchannelp = &(selchannel->next);
37                 selchannel = (struct select_channel *)MALLOC(sizeof(struct select_channel));
38                 memuse++;
39                 selchannel->channel = CHANNEL_NO; // call waiting
40                 *selchannelp = selchannel;
41         }
42 }
43
44
45 /* set default in_channel */
46 void default_in_channel(struct interface_port *ifport)
47 {
48         struct select_channel *selchannel;
49
50         selchannel = (struct select_channel *)MALLOC(sizeof(struct select_channel));
51         memuse++;
52         
53         selchannel->channel = CHANNEL_FREE;
54         
55         ifport->in_channel = selchannel;
56 }
57
58
59 /* parse string for a positive number */
60 static int get_number(char *value)
61 {
62         int val = 0;
63         char text[10];
64
65         val = atoi(value);
66         
67         SPRINT(text, "%d", val);
68
69         if (!strcmp(value, text))
70                 return(val);
71
72         return(-1);
73 }
74
75
76 /* remove element from buffer
77  * and return pointer to next element in buffer */
78 static char *get_seperated(char *buffer)
79 {
80         while(*buffer)
81         {
82                 if (*buffer==',' || *buffer<=32) /* seperate */
83                 {
84                         *buffer++ = '\0';
85                         while((*buffer>'\0' && *buffer<=32) || *buffer==',')
86                                 buffer++;
87                         return(buffer);
88                 }
89                 buffer++;
90         }
91         return(buffer);
92 }
93
94 /*
95  * parameter processing
96  */
97 static int inter_block(struct interface *interface, char *filename, int line, char *parameter, char *value)
98 {
99         struct interface_port *ifport;
100
101         /* port in chain ? */
102         if (!interface->ifport)
103         {
104                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
105                 return(-1);
106         }
107         /* goto end of chain */
108         ifport = interface->ifport;
109         while(ifport->next)
110                 ifport = ifport->next;
111         /* add value */
112         if (value[0])
113         {
114                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects no value.\n", filename, line, parameter);
115                 return(-1);
116         }
117         ifport->block = 1;
118         return(0);
119 }
120 static int inter_extension(struct interface *interface, char *filename, int line, char *parameter, char *value)
121 {
122         if (value[0])
123         {
124                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects no value.\n", filename, line, parameter);
125                 return(-1);
126         }
127         interface->extension = 1;
128         return(0);
129 }
130 static int inter_ptp(struct interface *interface, char *filename, int line, char *parameter, char *value)
131 {
132         struct interface_port *ifport;
133
134         /* port in chain ? */
135         if (!interface->ifport)
136         {
137                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
138                 return(-1);
139         }
140         if (interface->ifport->ptmp)
141         {
142                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' previously ptmp was given.\n", filename, line, parameter);
143                 return(-1);
144         }
145         /* goto end of chain */
146         ifport = interface->ifport;
147         while(ifport->next)
148                 ifport = ifport->next;
149         /* add value */
150         if (value[0])
151         {
152                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects no value.\n", filename, line, parameter);
153                 return(-1);
154         }
155         ifport->ptp = 1;
156         return(0);
157 }
158 static int inter_ptmp(struct interface *interface, char *filename, int line, char *parameter, char *value)
159 {
160         struct interface_port *ifport;
161
162         /* port in chain ? */
163         if (!interface->ifport)
164         {
165                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
166                 return(-1);
167         }
168         if (interface->ifport->ptp)
169         {
170                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' previously ptp was given.\n", filename, line, parameter);
171                 return(-1);
172         }
173         /* goto end of chain */
174         ifport = interface->ifport;
175         while(ifport->next)
176                 ifport = ifport->next;
177         /* add value */
178         if (value[0])
179         {
180                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects no value.\n", filename, line, parameter);
181                 return(-1);
182         }
183         ifport->ptmp = 1;
184         return(0);
185 }
186 static int inter_nt(struct interface *interface, char *filename, int line, char *parameter, char *value)
187 {
188 #ifdef SOCKET_MISDN
189         struct interface_port *ifport;
190
191         /* port in chain ? */
192         if (!interface->ifport)
193         {
194                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
195                 return(-1);
196         }
197         /* goto end of chain */
198         ifport = interface->ifport;
199         while(ifport->next)
200                 ifport = ifport->next;
201         /* add value */
202         if (value[0])
203         {
204                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects no value.\n", filename, line, parameter);
205                 return(-1);
206         }
207         ifport->nt = 1;
208 #endif
209         return(0);
210 }
211 static int inter_tones(struct interface *interface, char *filename, int line, char *parameter, char *value)
212 {
213         if (!strcasecmp(value, "yes"))
214         {
215                 interface->is_tones = IS_YES;
216         } else
217         if (!strcasecmp(value, "no"))
218         {
219                 interface->is_tones = IS_NO;
220         } else
221         {
222                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects value 'yes' or 'no'.\n", filename, line, parameter);
223                 return(-1);
224         }
225         return(0);
226 }
227 static int inter_earlyb(struct interface *interface, char *filename, int line, char *parameter, char *value)
228 {
229         if (!strcasecmp(value, "yes"))
230         {
231                 interface->is_earlyb = IS_YES;
232         } else
233         if (!strcasecmp(value, "no"))
234         {
235                 interface->is_earlyb = IS_NO;
236         } else
237         {
238                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects value 'yes' or 'no'.\n", filename, line, parameter);
239                 return(-1);
240         }
241         return(0);
242 }
243 static int inter_hunt(struct interface *interface, char *filename, int line, char *parameter, char *value)
244 {
245         if (!strcasecmp(value, "linear"))
246         {
247                 interface->hunt = HUNT_LINEAR;
248         } else
249         if (!strcasecmp(value, "roundrobin"))
250         {
251                 interface->hunt = HUNT_ROUNDROBIN;
252         } else
253         {
254                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects value 'linear' or 'roundrobin'.\n", filename, line, parameter);
255                 return(-1);
256         }
257         return(0);
258 }
259 static int inter_port(struct interface *interface, char *filename, int line, char *parameter, char *value)
260 {
261         struct interface_port *ifport, **ifportp;
262         struct interface *searchif;
263         int val;
264
265         val = get_number(value);
266         if (val == -1)
267         {
268                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects one numeric value.\n", filename, line, parameter);
269                 return(-1);
270         }
271         /* check for port already assigned */
272         searchif = interface_newlist;
273         while(searchif)
274         {
275                 ifport = searchif->ifport;
276                 while(ifport)
277                 {
278                         if (ifport->portnum == val)
279                         {
280                                 SPRINT(interface_error, "Error in %s (line %d): port '%d' already used above.\n", filename, line, val);
281                                 return(-1);
282                         }
283                         ifport = ifport->next;
284                 }
285                 searchif = searchif->next;
286         }
287         /* alloc port substructure */
288         ifport = (struct interface_port *)MALLOC(sizeof(struct interface_port));
289         memuse++;
290         ifport->interface = interface;
291         /* set value */
292         ifport->portnum = val;
293         /* tail port */
294         ifportp = &interface->ifport;
295         while(*ifportp)
296                 ifportp = &((*ifportp)->next);
297         *ifportp = ifport;
298         return(0);
299 }
300 static int inter_channel_out(struct interface *interface, char *filename, int line, char *parameter, char *value)
301 {
302         struct interface_port *ifport;
303         struct select_channel *selchannel, **selchannelp;
304         int val;
305         char *p, *el;
306
307         /* port in chain ? */
308         if (!interface->ifport)
309         {
310                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
311                 return(-1);
312         }
313         /* goto end of chain */
314         ifport = interface->ifport;
315         while(ifport->next)
316                 ifport = ifport->next;
317         p = value;
318         while(*p)
319         {
320                 el = p;
321                 p = get_seperated(p);
322                 if (!strcasecmp(el, "force"))
323                 {
324                         ifport->channel_force = 1;
325                         if (ifport->out_channel)
326                         {
327                                 SPRINT(interface_error, "Error in %s (line %d): value 'force' may only appear as first element in list.\n", filename, line);
328                                 return(-1);
329                         }
330                 } else
331                 if (!strcasecmp(el, "any"))
332                 {
333                         val = CHANNEL_ANY;
334                         goto selchannel;
335                 } else
336                 if (!strcasecmp(el, "free"))
337                 {
338                         val = CHANNEL_FREE;
339                         goto selchannel;
340                 } else
341                 if (!strcasecmp(el, "no"))
342                 {
343                         val = CHANNEL_NO;
344                         goto selchannel;
345                 } else
346                 {
347                         val = get_number(el);
348                         if (val == -1)
349                         {
350                                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects a comma seperated list of 'force', 'any', 'free', 'no' and any channel number.\n", filename, line, parameter);
351                                 return(-1);
352                         }
353
354                         if (val<1 || val==16 || val>126)
355                         {
356                                 SPRINT(interface_error, "Error in %s (line %d): channel '%d' out of range.\n", filename, line, val);
357                                 return(-1);
358                         }
359                         selchannel:
360                         /* add to select-channel list */
361                         selchannel = (struct select_channel *)MALLOC(sizeof(struct select_channel));
362                         memuse++;
363                         /* set value */
364                         selchannel->channel = val;
365                         /* tail port */
366                         selchannelp = &ifport->out_channel;
367                         while(*selchannelp)
368                                 selchannelp = &((*selchannelp)->next);
369                         *selchannelp = selchannel;
370                 }
371         }
372         return(0);
373 }
374 static int inter_channel_in(struct interface *interface, char *filename, int line, char *parameter, char *value)
375 {
376         struct interface_port *ifport;
377         struct select_channel *selchannel, **selchannelp;
378         int val;
379         char *p, *el;
380
381         /* port in chain ? */
382         if (!interface->ifport)
383         {
384                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
385                 return(-1);
386         }
387         /* goto end of chain */
388         ifport = interface->ifport;
389         while(ifport->next)
390                 ifport = ifport->next;
391         p = value;
392         while(*p)
393         {
394                 el = p;
395                 p = get_seperated(p);
396                 if (ifport->in_channel) if (ifport->in_channel->channel == CHANNEL_FREE)
397                 {
398                         SPRINT(interface_error, "Error in %s (line %d): parameter '%s' has values behind 'free' keyword. They has no effect.\n", filename, line, parameter);
399                                 return(-1);
400                 }
401                 if (!strcasecmp(el, "free"))
402                 {
403                         val = CHANNEL_FREE;
404                         goto selchannel;
405                 } else
406                 {
407                         val = get_number(el);
408                         if (val == -1)
409                         {
410                                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects a comma seperated list of channel numbers and 'free'.\n", filename, line, parameter);
411                                 return(-1);
412                         }
413
414                         if (val<1 || val==16 || val>126)
415                         {
416                                 SPRINT(interface_error, "Error in %s (line %d): channel '%d' out of range.\n", filename, line, val);
417                                 return(-1);
418                         }
419                         selchannel:
420                         /* add to select-channel list */
421                         selchannel = (struct select_channel *)MALLOC(sizeof(struct select_channel));
422                         memuse++;
423                         /* set value */
424                         selchannel->channel = val;
425                         /* tail port */
426                         selchannelp = &ifport->in_channel;
427                         while(*selchannelp)
428                                 selchannelp = &((*selchannelp)->next);
429                         *selchannelp = selchannel;
430                 }
431         }
432         return(0);
433 }
434 static int inter_timeouts(struct interface *interface, char *filename, int line, char *parameter, char *value)
435 {
436         struct interface_port *ifport;
437 //      struct select_channel *selchannel, **selchannelp;
438 //      int val;
439         char *p, *el;
440
441         /* port in chain ? */
442         if (!interface->ifport)
443         {
444                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
445                 return(-1);
446         }
447         /* goto end of chain */
448         ifport = interface->ifport;
449         while(ifport->next)
450                 ifport = ifport->next;
451         p = value;
452         if (!*p)
453         {
454                 nofive:
455                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects five timeout values.\n", filename, line, parameter);
456                 return(-1);
457         }
458         el = p;
459         p = get_seperated(p);
460         ifport->tout_setup = atoi(el);
461         if (!*p)
462                 goto nofive;
463         el = p;
464         p = get_seperated(p);
465         ifport->tout_dialing = atoi(el);
466         if (!*p)
467                 goto nofive;
468         el = p;
469         p = get_seperated(p);
470         ifport->tout_proceeding = atoi(el);
471         if (!*p)
472                 goto nofive;
473         el = p;
474         p = get_seperated(p);
475         ifport->tout_alerting = atoi(el);
476         if (!*p)
477                 goto nofive;
478         el = p;
479         p = get_seperated(p);
480         ifport->tout_disconnect = atoi(el);
481         return(0);
482 }
483 static int inter_msn(struct interface *interface, char *filename, int line, char *parameter, char *value)
484 {
485         struct interface_msn *ifmsn, **ifmsnp;
486         char *p, *el;
487
488         if (!value[0])
489         {
490                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects one MSN number or a list.\n", filename, line, parameter);
491                 return(-1);
492         }
493         if (interface->ifscreen_in)
494         {
495                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' not allowed with 'screen_in' parameter.\n", filename, line, parameter);
496                 return(-1);
497         }
498
499         /* process list */
500         p = value;
501         while(*p)
502         {
503                 el = p;
504                 p = get_seperated(p);
505                 /* add MSN to list */
506                 ifmsn = (struct interface_msn *)MALLOC(sizeof(struct interface_msn));
507                 memuse++;
508                 /* set value */
509                 SCPY(ifmsn->msn, el);
510                 /* tail port */
511                 ifmsnp = &interface->ifmsn;
512                 while(*ifmsnp)
513                         ifmsnp = &((*ifmsnp)->next);
514                 *ifmsnp = ifmsn;
515         }
516         return(0);
517 }
518 static int inter_screen(struct interface_screen **ifscreenp, struct interface *interface, char *filename, int line, char *parameter, char *value)
519 {
520         struct interface_screen *ifscreen;
521         char *p, *el;
522
523         if (!value[0])
524         {
525                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects old caller ID and new caller ID.\n", filename, line, parameter);
526                 return(-1);
527         }
528         /* add screen entry to list*/
529         ifscreen = (struct interface_screen *)MALLOC(sizeof(struct interface_screen));
530         memuse++;
531         ifscreen->match_type = -1; /* unchecked */
532         ifscreen->match_present = -1; /* unchecked */
533         ifscreen->result_type = -1; /* unchanged */
534         ifscreen->result_present = -1; /* unchanged */
535         /* tail port */
536         while(*ifscreenp)
537                 ifscreenp = &((*ifscreenp)->next);
538         *ifscreenp = ifscreen;
539 //      printf("interface=%s\n", interface->name);
540         /* get match */
541         p = value;
542         while(*p)
543         {
544                 el = p;
545                 p = get_seperated(p);
546                 if (!strcasecmp(el, "unknown"))
547                 {
548                         if (ifscreen->match_type != -1)
549                         {
550                                 typeerror:
551                                 SPRINT(interface_error, "Error in %s (line %d): number type already set earlier.\n", filename, line, parameter);
552                                 return(-1);
553                         }
554                         ifscreen->match_type = INFO_NTYPE_UNKNOWN;
555                 } else
556                 if (!strcasecmp(el, "subscriber"))
557                 {
558                         if (ifscreen->match_type != -1)
559                                 goto typeerror;
560                         ifscreen->match_type = INFO_NTYPE_SUBSCRIBER;
561                 } else
562                 if (!strcasecmp(el, "national"))
563                 {
564                         if (ifscreen->match_type != -1)
565                                 goto typeerror;
566                         ifscreen->match_type = INFO_NTYPE_NATIONAL;
567                 } else
568                 if (!strcasecmp(el, "international"))
569                 {
570                         if (ifscreen->match_type != -1)
571                                 goto typeerror;
572                         ifscreen->match_type = INFO_NTYPE_INTERNATIONAL;
573                 } else
574                 if (!strcasecmp(el, "allowed"))
575                 {
576                         if (ifscreen->match_present != -1)
577                         {
578                                 presenterror:
579                                 SPRINT(interface_error, "Error in %s (line %d): presentation type already set earlier.\n", filename, line);
580                                 return(-1);
581                         }
582                         ifscreen->match_present = INFO_PRESENT_ALLOWED;
583                 } else
584                 if (!strcasecmp(el, "restrict") || !strcasecmp(el, "restricted"))
585                 {
586                         if (ifscreen->match_present != -1)
587                                 goto presenterror;
588                         ifscreen->match_present = INFO_PRESENT_RESTRICTED;
589                 } else {
590                         SCPY(ifscreen->match, el);
591                         /* check for % at the end */
592                         if (strchr(el, '%'))
593                         {
594                                 if (strchr(el, '%') != el+strlen(el)-1)
595                                 {
596                                         SPRINT(interface_error, "Error in %s (line %d): %% joker found, but must at the end.\n", filename, line, parameter);
597                                         return(-1);
598                                 }
599                         }
600                         break;
601                 }
602         }
603         if (ifscreen->match[0] == '\0')
604         {
605                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects old caller ID.\n", filename, line, parameter);
606                 return(-1);
607         }
608         /* get result */
609         while(*p)
610         {
611                 el = p;
612                 p = get_seperated(p);
613                 if (!strcasecmp(el, "unknown"))
614                 {
615                         if (ifscreen->result_type != -1)
616                                 goto typeerror;
617                         ifscreen->result_type = INFO_NTYPE_UNKNOWN;
618                 } else
619                 if (!strcasecmp(el, "subscriber"))
620                 {
621                         if (ifscreen->result_type != -1)
622                                 goto typeerror;
623                         ifscreen->result_type = INFO_NTYPE_SUBSCRIBER;
624                 } else
625                 if (!strcasecmp(el, "national"))
626                 {
627                         if (ifscreen->result_type != -1)
628                                 goto typeerror;
629                         ifscreen->result_type = INFO_NTYPE_NATIONAL;
630                 } else
631                 if (!strcasecmp(el, "international"))
632                 {
633                         if (ifscreen->result_type != -1)
634                                 goto typeerror;
635                         ifscreen->result_type = INFO_NTYPE_INTERNATIONAL;
636                 } else
637                 if (!strcasecmp(el, "present") || !strcasecmp(el, "presented") || !strcasecmp(el, "allowed") || !strcasecmp(el, "allow"))
638                 {
639                         if (ifscreen->result_present != -1)
640                                 goto presenterror;
641                         ifscreen->result_present = INFO_PRESENT_ALLOWED;
642                 } else
643                 if (!strcasecmp(el, "restrict") || !strcasecmp(el, "restricted") || !strcasecmp(el, "deny") || !strcasecmp(el, "denied"))
644                 {
645                         if (ifscreen->result_present != -1)
646                                 goto presenterror;
647                         ifscreen->result_present = INFO_PRESENT_RESTRICTED;
648                 } else {
649                         SCPY(ifscreen->result, el);
650                         /* check for % at the end */
651                         if (strchr(el, '%'))
652                         {
653                                 if (strchr(el, '%') != el+strlen(el)-1)
654                                 {
655                                         SPRINT(interface_error, "Error in %s (line %d): %% joker found, but must at the end.\n", filename, line, parameter);
656                                         return(-1);
657                                 }
658                         }
659                         break;
660                 }
661         }
662         if (ifscreen->result[0] == '\0')
663         {
664                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects new caller ID.\n", filename, line, parameter);
665                 return(-1);
666         }
667         return(0);
668 }
669 static int inter_screen_in(struct interface *interface, char *filename, int line, char *parameter, char *value)
670 {
671         if (interface->ifmsn)
672         {
673                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' not allowed with 'msn' parameter.\n", filename, line, parameter);
674                 return(-1);
675         }
676
677         return(inter_screen(&interface->ifscreen_in, interface, filename, line, parameter, value));
678 }
679 static int inter_screen_out(struct interface *interface, char *filename, int line, char *parameter, char *value)
680 {
681         return(inter_screen(&interface->ifscreen_out, interface, filename, line, parameter, value));
682 }
683 static int inter_nodtmf(struct interface *interface, char *filename, int line, char *parameter, char *value)
684 {
685         struct interface_port *ifport;
686
687         /* port in chain ? */
688         if (!interface->ifport)
689         {
690                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
691                 return(-1);
692         }
693         /* goto end of chain */
694         ifport = interface->ifport;
695         while(ifport->next)
696                 ifport = ifport->next;
697         ifport->nodtmf = 1;
698         return(0);
699 }
700 static int inter_filter(struct interface *interface, char *filename, int line, char *parameter, char *value)
701 {
702         char *p, *q;
703
704         /* seperate parameter from filter */
705         p = value;
706         while(*p > 32)
707                 p++;
708         if (*p)
709         {
710                 *p++ = 0;
711                 while(*p > 0 && *p <= 32)
712                         p++;
713         }
714
715         if (!strcasecmp(value, "gain"))
716         {
717                 q = p;
718                 while(*q > 32)
719                         q++;
720                 if (*q)
721                 {
722                         *q++ = 0;
723                         while(*q > 0 && *q <= 32)
724                                 q++;
725                 }
726                 if (*p == 0 || *q == 0)
727                 {
728                         SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' expects two gain values.\n", filename, line, parameter, value);
729                         return(-1);
730                 }
731                 if (atoi(p)<-8 || atoi(p)>8 || atoi(q)<-8 || atoi(q)>8)
732                 {
733                         SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' gain values not in range. (-8...8)\n", filename, line, parameter, value);
734                         return(-1);
735                 }
736                 interface->tx_gain = atoi(p);
737                 interface->rx_gain = atoi(q);
738         } else
739         if (!strcasecmp(value, "pipeline"))
740         {
741                 if (*p == 0)
742                 {
743                         SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' expects pipeline string.\n", filename, line, parameter, value);
744                         return(-1);
745                 }
746                 SCPY(interface->pipeline, p);
747         } else
748         if (!strcasecmp(value, "blowfish"))
749         {
750                 unsigned char key[56];
751                 int l;
752                 
753                 if (!!strncmp(p, "0x", 2))
754                 {
755                         SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' expects blowfish key starting with '0x'.\n", filename, line, parameter, value);
756                         return(-1);
757                 }
758                 p += 2;
759                 l = 0; 
760                 while(*p)
761                 {
762                         if (l == 56)
763                         {
764                                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' key too long.\n", filename, line, parameter, value);
765                                 return(-1);
766                         }
767                         if (*p >= '0' && *p <= '9')
768                                 key[l] = (*p-'0')<<4;
769                         else if (*p >= 'a' && *p <= 'f')
770                                 key[l] = (*p-'a'+10)<<4;
771                         else if (*p >= 'A' && *p <= 'F')
772                                 key[l] = (*p-'A'+10)<<4;
773                         else
774                         {
775                                 digout:
776                                 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);
777                                 return(-1);
778                         }
779                         p++;
780                         if (*p == 0)
781                         {
782                                 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);
783                                 return(-1);
784                         }
785                         if (*p >= '0' && *p <= '9')
786                                 key[l] = (*p-'0')<<4;
787                         else if (*p >= 'a' && *p <= 'f')
788                                 key[l] = (*p-'a'+10)<<4;
789                         else if (*p >= 'A' && *p <= 'F')
790                                 key[l] = (*p-'A'+10)<<4;
791                         else
792                                 goto digout;
793                         p++;
794                         l++;
795                 }
796                 if (l < 4)
797                 {
798                         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);
799                         return(-1);
800                 }
801                 memcpy(interface->bf_key, key, l);
802                 interface->bf_len = l;
803         } else
804         {
805                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' has unknown filter '%s'.\n", filename, line, parameter, value);
806                 return(-1);
807         }
808         return(0);
809 }
810
811
812 /*
813  * structure of parameters
814  */
815 struct interface_param interface_param[] = {
816         { "extension", &inter_extension, "",
817         "If keyword is given, calls to interface are handled as internal extensions."},
818         {"tones", &inter_tones, "yes | no",
819         "Interface generates tones during call setup and release, or not.\nBy default only NT-mode ports generate tones."},
820
821         {"earlyb", &inter_earlyb, "yes | no",
822         "Interface receives and bridges tones during call setup and release, or not.\nBy default only TE-mode ports receive tones."},
823
824         {"hunt", &inter_hunt, "linear | roundrobin",
825         "Select the algorithm for selecting port with free channel."},
826
827         {"port", &inter_port, "<number>",
828         "Give exactly one port for this interface.\nTo give multiple ports, add more lines with port parameters."},
829
830         {"block", &inter_block, "",
831         "If keyword is given, calls on this interface are blocked.\n"
832         "This parameter must follow a 'port' parameter."},
833
834         {"ptp", &inter_ptp, "",
835         "The given port above is opened as point-to-point.\n"
836         "This is required on NT-mode ports that are multipoint by default.\n"
837         "This parameter must follow a 'port' parameter."},
838
839         {"ptmp", &inter_ptmp, "",
840         "The given port above is opened as point-to-multipoint.\n"
841         "This is required on PRI NT-mode ports that are point-to-point by default.\n"
842         "This parameter must follow a 'port' parameter."},
843         {"nt", &inter_nt, "",
844         "The given port above is opened in NT-mode.\n"
845 #ifdef SOCKET_MISDN
846         "This is required on interfaces that support both NT-mode and TE-mode.\n"
847 #else
848         "This parameter is only required for socket based mISDN driver.\n"
849 #endif
850         "This parameter must follow a 'port' parameter."},
851
852         {"channel-out", &inter_channel_out, "[force,][<number>][,...][,free][,any][,no]",
853         "Channel selection list for all outgoing calls to the interface.\n"
854         "A free channels is searched in order of appearance.\n"
855         "This parameter must follow a 'port' parameter.\n"
856         " force - Forces the selected port with no acceptable alternative (see DSS1).\n"
857         " <number>[,...] - List of channels to search.\n"
858         " free - Select any free channel\n"
859         " any - On outgoing calls, signal 'any channel acceptable'. (see DSS1)\n"
860         " no - Signal 'no channel available' aka 'call waiting'. (see DSS1)"},
861
862         {"channel-in", &inter_channel_in, "[<number>][,...][,free]",
863         "Channel selection list for all incoming calls from the interface.\n"
864         "A free channels is accepted if in the list.\n"
865         "If any channel was requested, the first free channel found is selected.\n"
866         "This parameter must follow a 'port' parameter.\n"
867         " <number>[,...] - List of channels to accept.\n"
868         " free - Accept any free channel"},
869
870         {"timeouts", &inter_timeouts, "<setup> <dialing> <proceeding> <alerting> <disconnect>",
871         "Timeout values for call states. They are both for incoming and outgoing states.\n"
872         "The default is 120 seconds for all states. Use 0 to disable.\n"
873         "This parameter must follow a 'port' parameter.\n"},
874
875         {"msn", &inter_msn, "<default MSN>,[<additional MSN>[,...]]",
876         "Incoming caller ID is checked against given MSN numbers.\n"
877         "If the caller ID is not found in this list, it is overwritten by the first MSN"},
878
879         {"screen-in", &inter_screen_in, "[options] <old caller ID>[%] [options] <new caller ID>[%]",
880         "Adds an entry for incoming calls to the caller ID screen list.\n"
881         "If the given 'old caller ID' matches, it is replaced by the 'new caller ID'\n"
882         "If '%' is given after old caller ID, it matches even if caller ID has\n"
883         "additional digits.\n"
884         "If '%' is given after mew caller ID, additinal digits of the 'old caller ID'\n"
885         "are added.\n"
886         "Options can be:\n"
887         " unknown | subsciber | national | international - Change caller ID type.\n"
888         " present | restrict - Change presentation of caller ID."},
889                 
890         {"screen-out", &inter_screen_out, "[options] <old caller ID>[%] [options] <new caller ID>[%]",
891         "Adds an entry for outgoing calls to the caller ID screen list.\n"
892         "See 'screen-in' for help."},
893
894         {"nodtmf", &inter_nodtmf, "",
895         "Disables DTMF detection for this interface.\n"
896         "This parameter must follow a 'port' parameter."},
897
898 #if 0
899         {"layer2keep", &inter_layer2keep, "yes | no",
900         "By default, layer 2 is establised and kept up on PTP interfaces.\n"
901         ".\n"
902         "This parameter must follow a 'port' parameter."},
903 #endif
904
905         {"filter", &inter_filter, "<filter> <parameters>",
906         "Adds/appends a filter. Filters are ordered in transmit direction.\n"
907         "gain <tx-volume> <rx-volume> - Changes volume (-8 .. 8)\n"
908         "pipeline <string> - Sets echo cancelation pipeline.\n"
909         "blowfish <key> - Adds encryption. Key must be 4-56 bytes (8-112 hex characters."},
910
911         {NULL, NULL, NULL, NULL}
912 };
913
914 /* read interfaces
915  *
916  * read settings from interface.conf
917  */
918 char interface_error[256];
919 struct interface *read_interfaces(void)
920 {
921         FILE                    *fp = NULL;
922         char                    filename[128];
923         char                    *p;
924         unsigned int            line, i;
925         char                    buffer[256];
926         struct interface        *interface = NULL, /* in case no interface */
927                                 **interfacep = &interface_newlist;
928         char                    parameter[128];
929         char                    value[256];
930         int                     expecting = 1; /* expecting new interface */
931         struct interface_param  *ifparam;
932
933         if (interface_newlist != NULL)
934                 FATAL("list is not empty.\n");
935         interface_error[0] = '\0';
936         SPRINT(filename, "%s/interface.conf", INSTALL_DATA);
937
938         if (!(fp = fopen(filename,"r")))
939         {
940                 SPRINT(interface_error, "Cannot open '%s'\n", filename);
941                 goto error;
942         }
943
944         line=0;
945         while((fgets(buffer,sizeof(buffer),fp)))
946         {
947                 buffer[sizeof(buffer)-1]=0;
948                 if (buffer[0]) buffer[strlen(buffer)-1]=0;
949                 p=buffer;
950                 line++;
951
952                 while(*p <= 32) /* skip spaces */
953                 {
954                         if (*p == 0)
955                                 break;
956                         p++;
957                 }
958                 if (*p==0 || *p=='#') /* ignore comments and empty line */
959                         continue;
960
961                 parameter[0]=0;
962                 value[0]=0;
963                 i=0; /* read parameter */
964                 while(*p > 32)
965                 {
966                         if (i+1 >= sizeof(parameter))
967                         {
968                                 SPRINT(interface_error, "Error in %s (line %d): parameter name too long.\n",filename,line);
969                                 goto error;
970                         }
971                         parameter[i+1] = '\0';
972                         parameter[i++] = *p++;
973                 }
974
975                 while(*p <= 32) /* skip spaces */
976                 {
977                         if (*p == 0)
978                                 break;
979                         p++;
980                 }
981
982                 if (*p!=0 && *p!='#') /* missing name */
983                 {
984                         i=0; /* read until end */
985                         while(*p!=0 && *p!='#')
986                         {
987                                 if (i+1 >= sizeof(value))
988                                 {
989                                         SPRINT(interface_error, "Error in %s (line %d): value too long.\n", filename, line);
990                                         goto error;
991                                 }
992                                 value[i+1] = '\0';
993                                 value[i++] = *p++;
994                         }
995
996                         /* remove trailing spaces from value */
997                         while(i)
998                         {
999                                 if (value[i-1]==0 || value[i-1]>32)
1000                                         break;
1001                                 value[i-1] = '\0';
1002                                 i--;
1003                         }
1004                 }
1005
1006                 /* check for interface name as first statement */
1007                 if (expecting && parameter[0]!='[')
1008                 {
1009                         SPRINT(interface_error, "Error in %s (line %d): expecting interface name inside [ and ], but got: '%s'.\n", filename, line, parameter);
1010                         goto error;
1011                 }
1012                 expecting = 0;
1013
1014                 /* check for new interface */
1015                 if (parameter[0] == '[')
1016                 {
1017                         if (parameter[strlen(parameter)-1] != ']')
1018                         {
1019                                 SPRINT(interface_error, "Error in %s (line %d): expecting interface name inside [ and ], but got: '%s'.\n", filename, line, parameter);
1020                                 goto error;
1021                         }
1022                         parameter[strlen(parameter)-1] = '\0';
1023
1024                         /* check if interface name already exists */
1025                         interface = interface_newlist;
1026                         while(interface)
1027                         {
1028                                 if (!strcasecmp(interface->name, parameter+1))
1029                                 {
1030                                         SPRINT(interface_error, "Error in %s (line %d): interface name '%s' already defined above.\n", filename, line, parameter+1);
1031                                         goto error;
1032                                 }
1033                                 interface = interface->next;
1034                         }
1035
1036                         /* append interface to new list */
1037                         interface = (struct interface *)MALLOC(sizeof(struct interface));
1038                         memuse++;
1039
1040                         /* name interface */
1041                         SCPY(interface->name, parameter+1);
1042
1043                         /* attach */
1044                         *interfacep = interface;
1045                         interfacep = &interface->next;
1046
1047                         continue;
1048                 }
1049
1050                 ifparam = interface_param;
1051                 while(ifparam->name)
1052                 {
1053                         if (!strcasecmp(parameter, ifparam->name))
1054                         {
1055                                 if (ifparam->func(interface, filename, line, parameter, value))
1056                                         goto error;
1057                                 break;
1058                         }
1059                         ifparam++;
1060                 }
1061                 if (ifparam->name)
1062                         continue;
1063
1064                 SPRINT(interface_error, "Error in %s (line %d): unknown parameter: '%s'.\n", filename, line, parameter);
1065                 goto error;
1066         }
1067
1068         if (fp) fclose(fp);
1069         return(interface_newlist);
1070 error:
1071         PERROR_RUNTIME("%s", interface_error);
1072         if (fp) fclose(fp);
1073         free_interfaces(interface_newlist);
1074         interface_newlist = NULL;
1075         return(NULL);
1076 }
1077
1078
1079 /*
1080  * freeing chain of interfaces
1081  */
1082 void free_interfaces(struct interface *interface)
1083 {
1084         void *temp;
1085         struct interface_port *ifport;
1086         struct select_channel *selchannel;
1087         struct interface_msn *ifmsn;
1088         struct interface_screen *ifscreen;
1089
1090         while(interface)
1091         {
1092                 ifport = interface->ifport;
1093                 while(ifport)
1094                 {
1095                         selchannel = ifport->in_channel;
1096                         while(selchannel)
1097                         {
1098                                 temp = selchannel;
1099                                 selchannel = selchannel->next;
1100                                 FREE(temp, sizeof(struct select_channel));
1101                                 memuse--;
1102                         }
1103                         selchannel = ifport->out_channel;
1104                         while(selchannel)
1105                         {
1106                                 temp = selchannel;
1107                                 selchannel = selchannel->next;
1108                                 FREE(temp, sizeof(struct select_channel));
1109                                 memuse--;
1110                         }
1111                         temp = ifport;
1112                         ifport = ifport->next;
1113                         FREE(temp, sizeof(struct interface_port));
1114                         memuse--;
1115                 }
1116                 ifmsn = interface->ifmsn;
1117                 while(ifmsn)
1118                 {
1119                         temp = ifmsn;
1120                         ifmsn = ifmsn->next;
1121                         FREE(temp, sizeof(struct interface_msn));
1122                         memuse--;
1123                 }
1124                 ifscreen = interface->ifscreen_in;
1125                 while(ifscreen)
1126                 {
1127                         temp = ifscreen;
1128                         ifscreen = ifscreen->next;
1129                         FREE(temp, sizeof(struct interface_screen));
1130                         memuse--;
1131                 }
1132                 ifscreen = interface->ifscreen_out;
1133                 while(ifscreen)
1134                 {
1135                         temp = ifscreen;
1136                         ifscreen = ifscreen->next;
1137                         FREE(temp, sizeof(struct interface_screen));
1138                         memuse--;
1139                 }
1140                 temp = interface;
1141                 interface = interface->next;
1142                 FREE(temp, sizeof(struct interface));
1143                 memuse--;
1144         }
1145 }
1146
1147 /*
1148  * defaults of ports if not specified by config
1149  */
1150 static void set_defaults(struct interface_port *ifport)
1151 {
1152         /* default channel selection list */
1153         if (!ifport->out_channel)
1154                 default_out_channel(ifport);
1155         if (!ifport->in_channel)
1156                 default_in_channel(ifport);
1157         /* default is_tones */
1158         if (ifport->interface->is_tones)
1159                 ifport->mISDNport->tones = (ifport->interface->is_tones==IS_YES);
1160         else
1161                 ifport->mISDNport->tones = (ifport->mISDNport->ntmode)?1:0;
1162         /* default is_earlyb */
1163         if (ifport->interface->is_earlyb)
1164                 ifport->mISDNport->earlyb = (ifport->interface->is_earlyb==IS_YES);
1165         else
1166                 ifport->mISDNport->earlyb = (ifport->mISDNport->ntmode)?0:1;
1167         /* set locally flag */
1168         if (ifport->interface->extension)
1169                 ifport->mISDNport->locally = 1;
1170         else
1171                 ifport->mISDNport->locally = 0;
1172 }
1173
1174
1175 /*
1176  * all links between mISDNport and interface are made
1177  * unused mISDNports are closed, new mISDNports are opened
1178  * also set default select_channel lists
1179  */
1180 void relink_interfaces(void)
1181 {
1182         struct mISDNport *mISDNport;
1183         struct interface *interface;
1184         struct interface_port *ifport;
1185
1186         /* unlink all mISDNports */
1187         mISDNport = mISDNport_first;
1188         while(mISDNport)
1189         {
1190                 mISDNport->ifport = NULL;
1191                 mISDNport = mISDNport->next;
1192         }
1193
1194         /* relink existing mISDNports */
1195         interface = interface_newlist;
1196         while(interface)
1197         {
1198                 ifport = interface->ifport;
1199                 while(ifport)
1200                 {
1201                         mISDNport = mISDNport_first;
1202                         while(mISDNport)
1203                         {
1204                                 if (mISDNport->portnum == ifport->portnum)
1205                                 {
1206                                         ifport->mISDNport = mISDNport;
1207                                         mISDNport->ifport = ifport;
1208                                         set_defaults(ifport);
1209                                 }
1210                                 mISDNport = mISDNport->next;
1211                         }
1212                         ifport = ifport->next;
1213                 }
1214                 interface = interface->next;
1215         }
1216
1217         /* close unused mISDNports */
1218         closeagain:
1219         mISDNport = mISDNport_first;
1220         while(mISDNport)
1221         {
1222                 if (mISDNport->ifport == NULL)
1223                 {
1224                         PDEBUG(DEBUG_ISDN, "Port %d is not used anymore and will be closed\n", mISDNport->portnum);
1225                         /* remove all port objects and destroy port */
1226                         mISDNport_close(mISDNport);
1227                         goto closeagain;
1228                 }
1229                 mISDNport = mISDNport->next;
1230         }
1231
1232         /* open and link new mISDNports */
1233         interface = interface_newlist;
1234         while(interface)
1235         {
1236                 ifport = interface->ifport;
1237                 while(ifport)
1238                 {
1239                         if (!ifport->mISDNport)
1240                         {
1241                                 load_port(ifport);
1242                         }
1243                         ifport = ifport->next;
1244                 }
1245                 interface = interface->next;
1246         }
1247
1248 }
1249
1250
1251 /*
1252  * load port
1253  */
1254 void load_port(struct interface_port *ifport)
1255 {
1256         struct mISDNport *mISDNport;
1257
1258         /* open new port */
1259         mISDNport = mISDNport_open(ifport->portnum, ifport->ptp, ifport->ptmp, ifport->nt, ifport->interface);
1260         if (mISDNport)
1261         {
1262                 /* link port */
1263                 ifport->mISDNport = mISDNport;
1264                 mISDNport->ifport = ifport;
1265                 set_defaults(ifport);
1266         } else
1267         {
1268                 ifport->block = 2; /* not available */
1269         }
1270 }
1271
1272 /*
1273  * give summary of interface syntax
1274  */
1275 void doc_interface(void)
1276 {
1277         struct interface_param *ifparam;
1278         
1279         printf("Syntax overview\n");
1280         printf("---------------\n\n");
1281
1282         printf("[<name>]\n");
1283         ifparam = interface_param;
1284         while(ifparam->name)
1285         {
1286                 printf("%s %s\n", ifparam->name, ifparam->usage);
1287                 ifparam++;
1288         }
1289
1290         ifparam = interface_param;
1291         while(ifparam->name)
1292         {
1293                 printf("\nParameter: %s %s\n", ifparam->name, ifparam->usage);
1294                 printf("%s\n", ifparam->help);
1295                 ifparam++;
1296         }
1297 }
1298
1299
1300 /* screen caller id
1301  * out==0: incoming caller id, out==1: outgoing caller id
1302  */
1303 void do_screen(int out, char *id, int idsize, int *type, int *present, struct interface *interface)
1304 {
1305         char                    *msn1;
1306         struct interface_msn    *ifmsn;
1307         struct interface_screen *ifscreen;
1308         char suffix[64];
1309
1310         /* screen incoming caller id */
1311         if (!out)
1312         {
1313                 /* check for MSN numbers, use first MSN if no match */
1314                 msn1 = NULL;
1315                 ifmsn = interface->ifmsn;
1316                 while(ifmsn)
1317                 {
1318                         if (!msn1)
1319                                 msn1 = ifmsn->msn;
1320                         if (!strcmp(ifmsn->msn, id))
1321                         {
1322                                 break;
1323                         }
1324                         ifmsn = ifmsn->next;
1325                 }
1326                 if (ifmsn)
1327                 {
1328                         start_trace(0, interface, numberrize_callerinfo(id, *type), NULL, DIRECTION_IN, 0, 0, "SCREEN (found in MSN list)");
1329                         add_trace("msn", NULL, "%s", id);
1330                         end_trace();
1331                 }
1332                 if (!ifmsn && msn1) // not in list, first msn given
1333                 {
1334                         start_trace(0, interface, numberrize_callerinfo(id, *type), NULL, DIRECTION_IN, 0, 0, "SCREEN (not found in MSN list)");
1335                         add_trace("msn", "given", "%s", id);
1336                         add_trace("msn", "used", "%s", msn1);
1337                         end_trace();
1338                         UNCPY(id, msn1, idsize);
1339                         id[idsize-1] = '\0';
1340                 }
1341         }
1342
1343         /* check screen list */
1344         if (out)
1345                 ifscreen = interface->ifscreen_out;
1346         else
1347                 ifscreen = interface->ifscreen_in;
1348         while (ifscreen)
1349         {
1350                 if (ifscreen->match_type==-1 || ifscreen->match_type==*type)
1351                 if (ifscreen->match_present==-1 || ifscreen->match_present==*present)
1352                 {
1353                         if (strchr(ifscreen->match,'%'))
1354                         {
1355                                 if (!strncmp(ifscreen->match, id, strchr(ifscreen->match,'%')-ifscreen->match))
1356                                         break;
1357                         } else
1358                         {
1359                                 if (!strcmp(ifscreen->match, id))
1360                                         break;
1361                         }
1362                 }
1363                 ifscreen = ifscreen->next;
1364         }
1365         if (ifscreen) // match
1366         {
1367                 start_trace(0, interface, numberrize_callerinfo(id, *type), NULL, out?DIRECTION_OUT:DIRECTION_IN, 0, 0, "SCREEN (found in screen list)");
1368                 switch(*type)
1369                 {
1370                         case INFO_NTYPE_UNKNOWN:
1371                         add_trace("given", "type", "unknown");
1372                         break;
1373                         case INFO_NTYPE_SUBSCRIBER:
1374                         add_trace("given", "type", "subscriber");
1375                         break;
1376                         case INFO_NTYPE_NATIONAL:
1377                         add_trace("given", "type", "national");
1378                         break;
1379                         case INFO_NTYPE_INTERNATIONAL:
1380                         add_trace("given", "type", "international");
1381                         break;
1382                 }
1383                 switch(*present)
1384                 {
1385                         case INFO_PRESENT_ALLOWED:
1386                         add_trace("given", "present", "allowed");
1387                         break;
1388                         case INFO_PRESENT_RESTRICTED:
1389                         add_trace("given", "present", "restricted");
1390                         break;
1391                         case INFO_PRESENT_NOTAVAIL:
1392                         add_trace("given", "present", "not available");
1393                         break;
1394                 }
1395                 add_trace("given", "id", "%s", id[0]?id:"<empty>");
1396                 if (ifscreen->result_type != -1)
1397                 {
1398                         *type = ifscreen->result_type;
1399                         switch(*type)
1400                         {
1401                                 case INFO_NTYPE_UNKNOWN:
1402                                 add_trace("used", "type", "unknown");
1403                                 break;
1404                                 case INFO_NTYPE_SUBSCRIBER:
1405                                 add_trace("used", "type", "subscriber");
1406                                 break;
1407                                 case INFO_NTYPE_NATIONAL:
1408                                 add_trace("used", "type", "national");
1409                                 break;
1410                                 case INFO_NTYPE_INTERNATIONAL:
1411                                 add_trace("used", "type", "international");
1412                                 break;
1413                         }
1414                 }
1415                 if (ifscreen->result_present != -1)
1416                 {
1417                         *present = ifscreen->result_present;
1418                         switch(*present)
1419                         {
1420                                 case INFO_PRESENT_ALLOWED:
1421                                 add_trace("used", "present", "allowed");
1422                                 break;
1423                                 case INFO_PRESENT_RESTRICTED:
1424                                 add_trace("used", "present", "restricted");
1425                                 break;
1426                                 case INFO_PRESENT_NOTAVAIL:
1427                                 add_trace("used", "present", "not available");
1428                                 break;
1429                         }
1430                 }
1431                 if (strchr(ifscreen->match,'%'))
1432                 {
1433                         SCPY(suffix, strchr(ifscreen->match,'%') - ifscreen->match + id);
1434                         UNCPY(id, ifscreen->result, idsize);
1435                         id[idsize-1] = '\0';
1436                         if (strchr(id,'%'))
1437                         {
1438                                 *strchr(id,'%') = '\0';
1439                                 UNCAT(id, suffix, idsize);
1440                                 id[idsize-1] = '\0';
1441                         }
1442                 } else
1443                 {
1444                         UNCPY(id, ifscreen->result, idsize);
1445                         id[idsize-1] = '\0';
1446                 }
1447                 add_trace("used", "id", "%s", id[0]?id:"<empty>");
1448                 end_trace();
1449         }
1450 }
1451