add experimental mISDN channel driver (issue #4077)
[asterisk/asterisk.git] / channels / chan_misdn_config.c
1 /*
2  * Chan_Misdn -- Channel Driver for Asterisk
3  *
4  * Interface to Asterisk
5  *
6  * Copyright (C) 2005, Christian Richter
7  *
8  * Christian Richter <crich@beronet.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14
15 #include "chan_misdn_config.h"
16
17 #include <asterisk/config.h>
18 #include <asterisk/channel.h>
19 #include <asterisk/logger.h>
20 #include <asterisk/lock.h>
21 #include <asterisk/strings.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <asterisk/utils.h>
26 #define AST_LOAD_CFG ast_config_load
27 #define AST_DESTROY_CFG ast_config_destroy
28
29 #define DEF_ECHOCANCEL 128
30 #define DEF_ECHOTRAINING 1
31
32 struct msn_list {
33         char *msn;
34         struct msn_list *next;
35 };
36
37 struct port_config {
38         char *name;
39         int *rxgain;
40         int *txgain;
41         int *te_choose_channel;
42         char *context;
43         char *language;
44         char *callerid;
45         char *method;
46         int *dialplan; 
47         char *nationalprefix;
48         char *internationalprefix;
49         int *pres;
50         int *always_immediate;
51         int *immediate;
52         int *hold_allowed;
53         int *early_bconnect;
54         int *use_callingpres;
55         int *echocancel;
56         int *echocancelwhenbridged;
57         int *echotraining;
58         struct msn_list *msn_list;
59         ast_group_t *callgroup;         /* Call group */
60         ast_group_t *pickupgroup;       /* Pickup group */
61 };
62
63 struct general_config {
64         int *debug;
65         char *tracefile;
66         int *trace_calls;
67         char *trace_dir;
68         int *bridging;
69         int *stop_tone_after_first_digit;
70         int *append_digits2exten;
71         int *l1_info_ok;
72         int *clear_l3;
73         int *dynamic_crypt;
74         char *crypt_prefix;
75         char *crypt_keys;
76 };
77
78 /* array of port configs, default is at position 0. */
79 static struct port_config **port_cfg;
80 /* max number of available ports, is set on init */
81 static int max_ports;
82 /* general config */
83 static struct general_config *general_cfg;
84 /* storing the ptp flag separated to save memory */
85 static int *ptp;
86
87 static ast_mutex_t config_mutex; 
88
89
90 static inline void misdn_cfg_lock (void) {
91         ast_mutex_lock(&config_mutex);
92 }
93
94 static inline void misdn_cfg_unlock (void) {
95         ast_mutex_unlock(&config_mutex);
96 }
97
98 static void free_msn_list (struct msn_list* iter) {
99         if (iter->next)
100                 free_msn_list(iter->next);
101         if (iter->msn)
102                 free(iter->msn);
103         free(iter);
104 }
105
106 static void free_port_cfg (void) {
107
108         struct port_config **free_list = (struct port_config **)calloc(max_ports + 1, sizeof(struct port_config *));
109
110         int i, j;
111
112         for (i = 0; i < max_ports; i++) {
113                 if (port_cfg[i]) {
114                         for (j = 0; j < max_ports && free_list[j]; j++) {
115                                 if (free_list[j] && free_list[j] == port_cfg[i])
116                                         continue; /* already in list */
117                                 free_list[j] = port_cfg[i];
118                         }
119                 }
120         }
121
122 #define FREE_ELEM(elem) ({ \
123                 if (free_list[i]->elem) \
124                         free(free_list[i]->elem); \
125         })
126         
127         for (i = 0; i < max_ports; i++) {
128                 if (free_list[i]) {
129                         FREE_ELEM(name);
130                         FREE_ELEM(rxgain);
131                         FREE_ELEM(txgain);
132                         FREE_ELEM(te_choose_channel);
133                         FREE_ELEM(context);
134                         FREE_ELEM(language);
135                         FREE_ELEM(callerid);
136                         FREE_ELEM(method);
137                         FREE_ELEM(dialplan);
138                         FREE_ELEM(nationalprefix);
139                         FREE_ELEM(internationalprefix);
140                         FREE_ELEM(pres);
141                         FREE_ELEM(always_immediate);
142                         FREE_ELEM(immediate);
143                         FREE_ELEM(hold_allowed);
144                         FREE_ELEM(early_bconnect);
145                         FREE_ELEM(use_callingpres);
146                         FREE_ELEM(echocancel);
147                         FREE_ELEM(echocancelwhenbridged);
148                         FREE_ELEM(echotraining);
149                         if (free_list[i]->msn_list)
150                                 free_msn_list(free_list[i]->msn_list);
151                         FREE_ELEM(callgroup);
152                         FREE_ELEM(pickupgroup);
153                         free(free_list[i]);
154                 }
155         }
156         free(free_list);
157 }
158
159 static void free_general_cfg (void) {
160
161 #define FREE_GEN_ELEM(elem) ({ \
162                 if (general_cfg->elem) \
163                         free(general_cfg->elem); \
164         })
165         
166         FREE_GEN_ELEM(debug);
167         FREE_GEN_ELEM(tracefile);
168         FREE_GEN_ELEM(trace_calls);
169         FREE_GEN_ELEM(trace_dir);
170         FREE_GEN_ELEM(bridging);
171         FREE_GEN_ELEM(stop_tone_after_first_digit);
172         FREE_GEN_ELEM(append_digits2exten);
173         FREE_GEN_ELEM(l1_info_ok);
174         FREE_GEN_ELEM(clear_l3);
175         FREE_GEN_ELEM(dynamic_crypt);
176         FREE_GEN_ELEM(crypt_prefix);
177         FREE_GEN_ELEM(crypt_keys);
178 }
179
180 #define GET_PORTCFG_STRCPY(item) ({ \
181                 char *temp; \
182                 if (port_cfg[port] && port_cfg[port]->item) \
183                         temp = port_cfg[port]->item; \
184                 else \
185                         temp = port_cfg[0]->item; \
186                 if (!temp || !memccpy((void *)buf, (void *)temp, '\0', bufsize)) \
187                         memset(buf, 0, 1); \
188         })
189
190 #define GET_GENCFG_STRCPY(item) ({ \
191                 if (general_cfg && general_cfg->item) { \
192                         if (!memccpy((void *)buf, (void *)general_cfg->item, '\0', bufsize)) \
193                                 memset(buf, 0, 1); \
194                 } else \
195                         memset(buf, 0, 1); \
196         })
197
198 #define GET_PORTCFG_MEMCPY(item) ({ \
199                 typeof(port_cfg[0]->item) temp; \
200                 if (port_cfg[port] && port_cfg[port]->item) \
201                         temp = port_cfg[port]->item; \
202                 else \
203                         temp = port_cfg[0]->item; \
204                 if (temp) { \
205                         int l = sizeof(*temp); \
206                         if (l > bufsize) \
207                                 memset(buf, 0, bufsize); \
208                         else \
209                                 memcpy(buf, temp, l); \
210                 } else \
211                         memset(buf, 0, bufsize); \
212         })
213
214 #define GET_GENCFG_MEMCPY(item) ({ \
215                 if (general_cfg && general_cfg->item) { \
216                         typeof(general_cfg->item) temp = general_cfg->item; \
217                         int l = sizeof(*temp); \
218                         if (l > bufsize) \
219                                 memset(buf, 0, bufsize); \
220                         else \
221                                 memcpy(buf, temp, l); \
222                 } else \
223                         memset(buf, 0, bufsize); \
224         })
225
226 void misdn_cfg_get(int port, enum misdn_cfg_elements elem, void *buf, int bufsize) {
227         
228         if (!(elem > MISDN_GEN_FIRST) && !misdn_cfg_is_port_valid(port)) {
229                 memset(buf, 0, bufsize);
230                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Port number %d is not valid.\n", port);
231                 return;
232         }
233
234         misdn_cfg_lock();
235         
236         switch (elem) {
237                 
238                 /* port config elements */
239                         
240                 case MISDN_CFG_PTP:             if (sizeof(ptp[port]) <= bufsize)
241                                                         memcpy(buf, &ptp[port], sizeof(ptp[port]));
242                                                 else
243                                                         buf = 0; /* error, should not happen */
244                                                 break;
245                 case MISDN_CFG_GROUPNAME:       GET_PORTCFG_STRCPY(name);
246                                                 break;
247                 case MISDN_CFG_RXGAIN:          GET_PORTCFG_MEMCPY(rxgain);
248                                                 break;
249                 case MISDN_CFG_TXGAIN:          GET_PORTCFG_MEMCPY(txgain);
250                                                 break;
251                 case MISDN_CFG_TE_CHOOSE_CHANNEL:
252                                                 GET_PORTCFG_MEMCPY(te_choose_channel);
253                                                 break;
254                 case MISDN_CFG_CONTEXT:         GET_PORTCFG_STRCPY(context);
255                                                 break;
256                 case MISDN_CFG_LANGUAGE:        GET_PORTCFG_STRCPY(language);
257                                                 break;
258                 case MISDN_CFG_CALLERID:        GET_PORTCFG_STRCPY(callerid);
259                                                 break;
260                 case MISDN_CFG_METHOD:          GET_PORTCFG_STRCPY(method);
261                                                 break;
262                 case MISDN_CFG_DIALPLAN:        GET_PORTCFG_MEMCPY(dialplan);
263                                                 break;
264                 case MISDN_CFG_NATPREFIX:       GET_PORTCFG_STRCPY(nationalprefix);
265                                                 break;
266                 case MISDN_CFG_INTERNATPREFIX:
267                                                 GET_PORTCFG_STRCPY(internationalprefix);
268                                                 break;
269                 case MISDN_CFG_PRES:            GET_PORTCFG_MEMCPY(pres);
270                                                 break;
271                 case MISDN_CFG_ALWAYS_IMMEDIATE:
272                                                 GET_PORTCFG_MEMCPY(always_immediate);
273                                                 break;
274                 case MISDN_CFG_IMMEDIATE:       GET_PORTCFG_MEMCPY(immediate);
275                                                 break;
276                 case MISDN_CFG_HOLD_ALLOWED:
277                                                 GET_PORTCFG_MEMCPY(hold_allowed);
278                                                 break;
279                 case MISDN_CFG_EARLY_BCONNECT:
280                                                 GET_PORTCFG_MEMCPY(early_bconnect);
281                                                 break;
282                 case MISDN_CFG_USE_CALLINGPRES:
283                                                 GET_PORTCFG_MEMCPY(use_callingpres);
284                                                 break;
285                 case MISDN_CFG_ECHOCANCEL:
286                                                 GET_PORTCFG_MEMCPY(echocancel );
287                                                 break;
288                 case MISDN_CFG_ECHOCANCELWHENBRIDGED:
289                                                 GET_PORTCFG_MEMCPY(echocancelwhenbridged);
290                                                 break;
291                 case MISDN_CFG_ECHOTRAINING:
292                                                 GET_PORTCFG_MEMCPY(echotraining);
293                                                 break;
294                 case MISDN_CFG_CALLGROUP:       GET_PORTCFG_MEMCPY(callgroup);
295                                                 break;
296                 case MISDN_CFG_PICKUPGROUP:     GET_PORTCFG_MEMCPY(pickupgroup);
297                                                 break;
298                 
299                 /* general config elements */
300                         
301                 case MISDN_GEN_DEBUG:           GET_GENCFG_MEMCPY(debug);
302                                                 break;
303                 case MISDN_GEN_TRACEFILE:       GET_GENCFG_STRCPY(tracefile);
304                                                 break;
305                 case MISDN_GEN_TRACE_CALLS: GET_GENCFG_MEMCPY(trace_calls);
306                                                 break;
307                 case MISDN_GEN_TRACE_DIR:       GET_GENCFG_STRCPY(trace_dir);
308                                                 break;
309                 case MISDN_GEN_BRIDGING:        GET_GENCFG_MEMCPY(bridging);
310                                                 break;
311                 case MISDN_GEN_STOP_TONE:       GET_GENCFG_MEMCPY(stop_tone_after_first_digit);
312                                                 break;
313                 case MISDN_GEN_APPEND_DIGITS2EXTEN: 
314                                                 GET_GENCFG_MEMCPY(append_digits2exten);
315                                                 break;
316                 case MISDN_GEN_L1_INFO_OK:      GET_GENCFG_MEMCPY(l1_info_ok);
317                                                 break;
318                 case MISDN_GEN_CLEAR_L3:        GET_GENCFG_MEMCPY(clear_l3);
319                                                 break;
320                 case MISDN_GEN_DYNAMIC_CRYPT:   GET_GENCFG_MEMCPY(dynamic_crypt);
321                                                 break;
322                 case MISDN_GEN_CRYPT_PREFIX:    GET_GENCFG_STRCPY(crypt_prefix);
323                                                 break;
324                 case MISDN_GEN_CRYPT_KEYS:      GET_GENCFG_STRCPY(crypt_keys);
325                                                 break;
326                 default:                        memset(buf, 0, bufsize);
327         }
328         
329         misdn_cfg_unlock();
330 }
331
332 int misdn_cfg_is_msn_valid (int port, char* msn) {
333         
334         if (!misdn_cfg_is_port_valid(port)) {
335                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_is_msn_valid! Port number %d is not valid.\n", port);
336                 return 0;
337         }
338         
339         struct msn_list *iter;
340         
341         misdn_cfg_lock();
342         
343         if (port_cfg[port]->msn_list)
344                 iter = port_cfg[port]->msn_list;
345         else
346                 iter = port_cfg[0]->msn_list;
347         for (; iter; iter = iter->next) 
348                 if (*(iter->msn) == '*' || !strcasecmp(iter->msn, msn)) {
349                         misdn_cfg_unlock();
350                         return 1;
351                 }
352         
353         misdn_cfg_unlock();
354         
355         return 0;
356 }
357
358 int misdn_cfg_is_port_valid (int port) {
359         
360         misdn_cfg_lock();
361         
362         if (port < 1 || port > max_ports) {
363                 misdn_cfg_unlock();
364                 return 0;
365         }
366
367         int valid = (port_cfg[port] != NULL);
368
369         misdn_cfg_unlock();
370
371         return valid;
372 }
373
374 int misdn_cfg_is_group_method (char *group, enum misdn_cfg_method meth) {
375
376         int i, re = 0;
377         char *method = NULL;
378
379         misdn_cfg_lock();
380         
381         for (i = 0; i < max_ports; i++) {
382                 if (port_cfg[i]) {
383                         if (!strcasecmp(port_cfg[i]->name, group))
384                                 method = port_cfg[i]->method ? port_cfg[i]->method : port_cfg[0]->method;
385                 }
386         }
387
388         if (method) {
389                 switch (meth) {
390                 case METHOD_STANDARD:           re = !strcasecmp(method, "standard");
391                                                                         break;
392                 case METHOD_ROUND_ROBIN:        re = !strcasecmp(method, "round_robin");
393                                                                         break;
394                 }
395         }
396
397         misdn_cfg_unlock();
398
399         return re;
400 }
401
402 void misdn_cfg_get_ports_string (char *ports) {
403         *ports = 0;
404         char tmp[16];
405         int l;
406         
407         misdn_cfg_lock();
408
409         int i = 1;
410         for (; i <= max_ports; i++) {
411                 if (port_cfg[i]) {
412                         if (ptp[i])
413                                 sprintf(tmp, "%dptp,", i);
414                         else
415                                 sprintf(tmp, "%d,", i);
416                         strcat(ports, tmp);
417                 }
418         }
419         
420         misdn_cfg_unlock();
421
422         if ((l = strlen(ports)))
423                 ports[l-1] = 0;
424 }
425
426 #define GET_CFG_STRING(typestr, type) ({ \
427                 if (port_cfg[port] && port_cfg[port]->type) \
428                         snprintf(buf, bufsize, "%s " #typestr ": %s", begin, port_cfg[port]->type); \
429                 else \
430                         snprintf(buf, bufsize, "%s " #typestr ": %s", begin, port_cfg[0]->type); \
431         }) \
432
433 #define GET_GEN_STRING(typestr, type) ({ \
434                 snprintf(buf, bufsize, "%s " #typestr ": %s", begin, general_cfg->type ? general_cfg->type : "not set"); \
435         }) \
436
437 #define GET_CFG_INT(typestr, type) ({ \
438                 if (port_cfg[port] && port_cfg[port]->type) \
439                         snprintf(buf, bufsize, "%s " #typestr ": %d", begin, *port_cfg[port]->type); \
440                 else \
441                         snprintf(buf, bufsize, "%s " #typestr ": %d", begin, *port_cfg[0]->type); \
442         }) \
443
444 #define GET_GEN_INT(typestr, type) ({ \
445                 snprintf(buf, bufsize, "%s " #typestr ": %d", begin, general_cfg->type ? *general_cfg->type : 0); \
446         }) \
447
448 #define GET_CFG_BOOL(typestr, type, yes, no) ({ \
449                 int bool; \
450                 if (port_cfg[port] && port_cfg[port]->type) \
451                         bool = *port_cfg[port]->type; \
452                 else \
453                         bool = *port_cfg[0]->type; \
454                 snprintf(buf, bufsize, "%s " #typestr ": %s", begin, bool ? #yes : #no); \
455         }) \
456
457 #define GET_CFG_HYBRID(typestr, type, yes, no) ({ \
458                 int bool; \
459                 if (port_cfg[port] && port_cfg[port]->type) \
460                         bool = *port_cfg[port]->type; \
461                 else \
462                         bool = *port_cfg[0]->type; \
463                 if (bool == 1 || bool == 0) \
464                         snprintf(buf, bufsize, "%s " #typestr ": %s", begin, bool ? #yes : #no); \
465                 else \
466                         snprintf(buf, bufsize, "%s " #typestr ": %d", begin, bool); \
467         }) \
468
469 #define GET_GEN_BOOL(typestr, type, yes, no) ({ \
470                 snprintf(buf, bufsize, "%s " #typestr ": %s", begin, general_cfg->type ? (*general_cfg->type ? #yes : #no) : "not set"); \
471         }) \
472
473 #define GET_CFG_AST_GROUP_T(typestr, type) ({ \
474                 ast_group_t *tmp; \
475                 if (port_cfg[port] && port_cfg[port]->type) \
476                         tmp = port_cfg[port]->type; \
477                 else \
478                         tmp = port_cfg[0]->type; \
479                 if (tmp) { \
480                         char tmpbuf[256]; \
481                         snprintf(buf, bufsize, "%s " #typestr ": %s", begin, ast_print_group(tmpbuf, sizeof(tmpbuf), *tmp)); \
482                 } else \
483                         snprintf(buf, bufsize, "%s " #typestr ": %s", begin, "none"); \
484         }) \
485
486 void misdn_cfg_get_config_string(int port, enum misdn_cfg_elements elem, char* buf, int bufsize) {
487
488         if (!(elem > MISDN_GEN_FIRST) && !misdn_cfg_is_port_valid(port)) {
489                 *buf = 0;
490                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Port number %d is not valid.\n", port);
491                 return;
492         }
493         
494         char begin[] = " -> ";
495         
496         misdn_cfg_lock();
497         
498         switch (elem) {
499                 
500                 case MISDN_CFG_PTP:             snprintf(buf, bufsize, "%s PTP: %s", begin, ptp[port] ? "yes" : "no");
501                                                                         break;
502                 case MISDN_CFG_GROUPNAME:       GET_CFG_STRING(GROUPNAME, name);
503                                                                         break;
504                 case MISDN_CFG_RXGAIN:          GET_CFG_INT(RXGAIN, rxgain);
505                                                                         break;
506                 case MISDN_CFG_TXGAIN:          GET_CFG_INT(TXGAIN, txgain);
507                                                                         break;
508                 case MISDN_CFG_TE_CHOOSE_CHANNEL:
509                                                 GET_CFG_BOOL(TE_CHOOSE_CHANNEL, te_choose_channel, yes, no);
510                                                                         break;
511                 case MISDN_CFG_CONTEXT:         GET_CFG_STRING(CONTEXT, context);
512                                                                         break;
513                 case MISDN_CFG_LANGUAGE:        GET_CFG_STRING(LANGUAGE, language);
514                                                                         break;
515                 case MISDN_CFG_CALLERID:        GET_CFG_STRING(CALLERID, callerid);
516                                                                         break;
517                 case MISDN_CFG_METHOD:          GET_CFG_STRING(METHOD, method);
518                                                                         break;
519                 case MISDN_CFG_DIALPLAN:        GET_CFG_INT(DIALPLAN, dialplan);
520                                                                         break;
521                 case MISDN_CFG_NATPREFIX:       GET_CFG_STRING(NATIONALPREFIX, nationalprefix);
522                                                                         break;
523                 case MISDN_CFG_INTERNATPREFIX:
524                                                 GET_CFG_STRING(INTERNATIONALPREFIX, internationalprefix);
525                                                                         break;
526                 case MISDN_CFG_PRES:            GET_CFG_BOOL(PRESENTATION, pres, allowed, not_screened);
527                                                                         break;
528                 case MISDN_CFG_ALWAYS_IMMEDIATE:
529                                                 GET_CFG_BOOL(ALWAYS_IMMEDIATE, always_immediate, yes, no);
530                                                                         break;
531                 case MISDN_CFG_IMMEDIATE:       GET_CFG_BOOL(IMMEDIATE, immediate, yes, no);
532                                                                         break;
533                 case MISDN_CFG_HOLD_ALLOWED:
534                                                 GET_CFG_BOOL(HOLD_ALLOWED, hold_allowed, yes, no);
535                                                                         break;
536                 case MISDN_CFG_EARLY_BCONNECT:
537                                                 GET_CFG_BOOL(EARLY_BCONNECT, early_bconnect, yes, no);
538                                                                         break;
539                 case MISDN_CFG_USE_CALLINGPRES:
540                                                 GET_CFG_BOOL(USE_CALLINGPRES, use_callingpres, yes, no);
541                                                                         break;
542                 case MISDN_CFG_ECHOCANCEL:      GET_CFG_HYBRID(ECHOCANCEL, echocancel, yes, no);
543                                                                         break;
544                 case MISDN_CFG_ECHOCANCELWHENBRIDGED:
545                                                 GET_CFG_BOOL(ECHOCANCELWHENBRIDGED, echocancelwhenbridged, yes, no);
546                                                                         break;
547                 case MISDN_CFG_ECHOTRAINING:
548                                                 GET_CFG_HYBRID(ECHOTRAINING, echotraining, yes, no);
549                                                                         break;
550                 case MISDN_CFG_CALLGROUP:       GET_CFG_AST_GROUP_T(CALLINGGROUP, callgroup);
551                                                                         break;
552                 case MISDN_CFG_PICKUPGROUP:     GET_CFG_AST_GROUP_T(PICKUPGROUP, pickupgroup);
553                                                                         break;
554                 case MISDN_CFG_MSNS:            {
555                                                         char tmpbuffer[BUFFERSIZE];
556                                                         tmpbuffer[0] = 0;
557                                                         struct msn_list *iter;
558                                                         if (port_cfg[port]->msn_list)
559                                                                 iter = port_cfg[port]->msn_list;
560                                                         else
561                                                                 iter = port_cfg[0]->msn_list;
562                                                         if (iter) {
563                                                                 for (; iter; iter = iter->next)
564                                                                         sprintf(tmpbuffer, "%s%s, ", tmpbuffer, iter->msn);
565                                                                 tmpbuffer[strlen(tmpbuffer)-2] = 0;
566                                                         }
567                                                         snprintf(buf, bufsize, "%s MSNs: %s", begin, *tmpbuffer ? tmpbuffer : "none"); \
568                                                 }
569                                                 break;
570                 
571                 /* general config elements */
572                 
573                 case MISDN_GEN_DEBUG:           GET_GEN_INT(DEBUG_LEVEL, debug);
574                                                                         break;
575                 case MISDN_GEN_TRACEFILE:       GET_GEN_STRING(TRACEFILE, tracefile);
576                                                                         break;
577                 case MISDN_GEN_TRACE_CALLS: GET_GEN_BOOL(TRACE_CALLS, trace_calls, true, false);
578                                                                         break;
579                 case MISDN_GEN_TRACE_DIR:       GET_GEN_STRING(TRACE_DIR, trace_dir);
580                                                                         break;
581                 case MISDN_GEN_BRIDGING:        GET_GEN_BOOL(BRIDGING, bridging, yes, no);
582                                                                         break;
583                 case MISDN_GEN_STOP_TONE:       GET_GEN_BOOL(STOP_TONE_AFTER_FIRST_DIGIT, stop_tone_after_first_digit, yes, no);
584                                                                         break;
585                 case MISDN_GEN_APPEND_DIGITS2EXTEN: 
586                                                 GET_GEN_BOOL(APPEND_DIGITS2EXTEN, append_digits2exten, yes, no);
587                                                                         break;
588                 case MISDN_GEN_L1_INFO_OK:      GET_GEN_BOOL(L1_INFO_OK, l1_info_ok, yes, no);
589                                                                         break;
590                 case MISDN_GEN_CLEAR_L3:        GET_GEN_BOOL(CLEAR_L3, clear_l3, yes, no);
591                                                                         break;
592                 case MISDN_GEN_DYNAMIC_CRYPT:
593                                                 GET_GEN_BOOL(DYNAMIC_CRYPT,dynamic_crypt, yes, no);
594                                                                         break;
595                 case MISDN_GEN_CRYPT_PREFIX:
596                                                 GET_GEN_STRING(CRYPT_PREFIX, crypt_prefix);
597                                                                         break;
598                 case MISDN_GEN_CRYPT_KEYS:      GET_GEN_STRING(CRYPT_KEYS, crypt_keys);
599                                                                         break;
600                                                                         
601                 default:                        *buf = 0;
602                                                                         break;
603         }
604
605         misdn_cfg_unlock();
606 }
607
608 int misdn_cfg_get_next_port (int port) {
609         
610         misdn_cfg_lock();
611         
612         for (port++; port <= max_ports; port++) {
613                 if (port_cfg[port]) {
614                         misdn_cfg_unlock();
615                         return port;
616                 }
617         }
618         
619         misdn_cfg_unlock();
620         
621         return -1;
622 }
623
624 int misdn_cfg_get_next_port_spin (int port) {
625
626         int ret = misdn_cfg_get_next_port(port);
627
628         if (ret > 0)
629                 return ret;
630
631         return misdn_cfg_get_next_port(0);
632 }
633
634 #define PARSE_GEN_INT(item) ({ \
635                 if (!strcasecmp(v->name, #item)) { \
636                         int temp; \
637                         if (!sscanf(v->value, "%d", &temp)) { \
638                                 ast_log(LOG_WARNING, "Value \"%s\" for \"" #item "\" (generals section) invalid or out of range! Please edit your misdn.conf and then do a \"misdn reload\".\n", v->value); \
639                         } else { \
640                                 general_cfg->item = (int *)malloc(sizeof(int)); \
641                                 memcpy(general_cfg->item, &temp, sizeof(int)); \
642                         } \
643                         continue; \
644                 } \
645         }) \
646
647 #define PARSE_GEN_BOOL(item) ({ \
648                 if (!strcasecmp(v->name, #item)) { \
649                         general_cfg->item = (int *)malloc(sizeof(int)); \
650                         *(general_cfg->item) = ast_true(v->value)?1:0; \
651                         continue; \
652                 } \
653         })
654
655 #define PARSE_GEN_STR(item) ({ \
656                 if (!strcasecmp(v->name, #item)) { \
657                         int l = strlen(v->value); \
658                         if (l) { \
659                                 general_cfg->item = (char *)calloc(l+1, sizeof(char)); \
660                                 strncpy(general_cfg->item,v->value, l); \
661                         } \
662                         continue; \
663                 } \
664         })
665
666 static void build_general_config(struct ast_variable *v) {
667
668         if (!v) 
669                 return;
670
671         for (; v; v = v->next) {
672
673                 PARSE_GEN_INT(debug);
674                 PARSE_GEN_STR(tracefile);
675                 PARSE_GEN_BOOL(trace_calls);
676                 PARSE_GEN_STR(trace_dir);
677                 PARSE_GEN_BOOL(bridging);
678                 PARSE_GEN_BOOL(stop_tone_after_first_digit);
679                 PARSE_GEN_BOOL(append_digits2exten);
680                 PARSE_GEN_BOOL(l1_info_ok);
681                 PARSE_GEN_BOOL(clear_l3);
682                 PARSE_GEN_BOOL(dynamic_crypt);
683                 PARSE_GEN_STR(crypt_prefix);
684                 PARSE_GEN_STR(crypt_keys);
685                 
686         }
687 }
688
689 #define PARSE_CFG_HYBRID(item, def) ({ \
690                 if (!strcasecmp(v->name, #item)) { \
691                         new->item = (int *)malloc(sizeof(int)); \
692                         if (!sscanf(v->value, "%d", new->item)) { \
693                                 if (ast_true(v->value)) \
694                                         *new->item = def; \
695                                 else \
696                                         *new->item = 0; \
697                         } \
698                         continue; \
699                 } \
700         }) \
701
702 #define PARSE_CFG_INT(item) ({ \
703                 if (!strcasecmp(v->name, #item)) { \
704                         new->item = (int *)malloc(sizeof(int)); \
705                         if (!sscanf(v->value, "%d", new->item)) { \
706                                 ast_log(LOG_WARNING, "Value \"%s\" for \"" #item "\" of group \"%s\" invalid or out of range! Please edit your misdn.conf and then do a \"misdn reload\".\n", v->value, cat); \
707                                 free(new->item); \
708                                 new->item = NULL; \
709                         } \
710                         continue; \
711                 } \
712         }) \
713
714 #define PARSE_CFG_BOOL(item) ({ \
715                 if (!strcasecmp(v->name, #item)) { \
716                         new->item = (int *)malloc(sizeof(int)); \
717                         *(new->item) = ast_true(v->value)?1:0; \
718                         continue; \
719                 } \
720         })
721
722 #define PARSE_CFG_STR(item) ({ \
723                 if (!strcasecmp(v->name, #item)) { \
724                         int l = strlen(v->value); \
725                         if (l) { \
726                                 new->item = (char *)calloc(l+1, sizeof(char)); \
727                                 strncpy(new->item,v->value,l); \
728                         } \
729                         continue; \
730                 } \
731         })
732
733 static void build_port_config(struct ast_variable *v, char *cat) {
734         if (!v || !cat)
735                 return;
736
737         int cfg_for_ports[max_ports + 1];
738         int i = 0;
739         for (; i < (max_ports + 1); i++) {
740                 cfg_for_ports[i] = 0;
741         }
742         
743         /* we store the default config at position 0 */
744         if (!strcasecmp(cat, "default")) {
745                 cfg_for_ports[0] = 1;
746         }
747
748         struct port_config* new = (struct port_config *)calloc(1, sizeof(struct port_config));
749         
750         {
751                 int l = strlen(cat);
752                 new->name = (char *)calloc(l+1, sizeof(char));
753                 strncpy(new->name, cat, l);
754         }
755         
756         for (; v; v=v->next) {
757                 if (!strcasecmp(v->name, "ports")) {
758                         /* TODO check for value not beeing set, like PORTS= */
759                         char *iter;
760                         char *value = v->value;
761                         while ((iter = strchr(value, ',')) != NULL) {
762                                 *iter = 0;
763                                 /* strip spaces */
764                                 while (*value && *value == ' ') {
765                                         value++;
766                                 }
767                                 /* TODO check for char not 0-9 */
768
769                                 if (*value){
770                                         int p = atoi(value);
771                                         if (p <= max_ports && p > 0) {
772                                                 cfg_for_ports[p] = 1;
773                                                 if (strstr(value, "ptp"))
774                                                         ptp[p] = 1;
775                                         } else
776                                                 ast_log(LOG_WARNING, "Port value \"%s\" of group %s invalid or out of range! Please edit your misdn.conf and then do a \"misdn reload\".\n", value, cat);
777                                         value = ++iter;
778                                 }
779                         }
780                         /* the remaining or the only one */
781                         /* strip spaces */
782                         while (*value && *value == ' ') {
783                                 value++;
784                         }
785                         /* TODO check for char not 0-9 */
786                         if (*value) {
787                                 int p = atoi(value);
788                                 if (p <= max_ports && p > 0) {
789                                         cfg_for_ports[p] = 1;
790                                         if (strstr(value, "ptp"))
791                                                 ptp[p] = 1;
792                                 } else
793                                         ast_log(LOG_WARNING, "Port value \"%s\" of group %s invalid or out of range! Please edit your misdn.conf and then do a \"misdn reload\".\n", value, cat);
794                         }
795                         continue;
796                 }
797                 PARSE_CFG_STR(context);
798                 PARSE_CFG_INT(dialplan);
799                 PARSE_CFG_STR(nationalprefix);
800                 PARSE_CFG_STR(internationalprefix);
801                 PARSE_CFG_STR(language);
802                 if (!strcasecmp(v->name, "presentation")) {
803                         if (v->value && strlen(v->value)) {
804                                 new->pres = (int *)malloc(sizeof(int));
805                                 if (!strcasecmp(v->value, "allowed")) {
806                                         *(new->pres) = 1;
807                                 }
808                                 /* TODO: i assume if it is not "allowed", it is "not_screened" */
809                                 else {
810                                         *(new->pres) = 0;
811                                 }
812                         }
813                         continue;
814                 }
815                 PARSE_CFG_INT(rxgain);
816                 PARSE_CFG_INT(txgain);
817                 PARSE_CFG_BOOL(te_choose_channel);
818                 PARSE_CFG_BOOL(immediate);
819                 PARSE_CFG_BOOL(always_immediate);
820                 PARSE_CFG_BOOL(hold_allowed);
821                 PARSE_CFG_BOOL(early_bconnect);
822                 PARSE_CFG_BOOL(use_callingpres);
823                 PARSE_CFG_HYBRID(echocancel, DEF_ECHOCANCEL);
824                 PARSE_CFG_BOOL(echocancelwhenbridged);
825                 PARSE_CFG_HYBRID(echotraining, DEF_ECHOTRAINING);
826                 PARSE_CFG_STR(callerid);
827                 PARSE_CFG_STR(method);
828                 if (!strcasecmp(v->name, "msns")) {
829                         /* TODO check for value not beeing set, like msns= */
830                         char *iter;
831                         char *value = v->value;
832
833                         while ((iter = strchr(value, ',')) != NULL) {
834                                 *iter = 0;
835                                 /* strip spaces */
836                                 while (*value && *value == ' ') {
837                                         value++;
838                                 }
839                                 /* TODO check for char not 0-9 */
840                                 if (*value){
841                                         int l = strlen(value);
842                                         if (l) {
843                                                 struct msn_list *ml = (struct msn_list *)calloc(1, sizeof(struct msn_list));
844                                                 ml->msn = (char *)calloc(l+1, sizeof(char));
845                                                 strncpy(ml->msn,value,l);
846                                                 ml->next = new->msn_list;
847                                                 new->msn_list = ml;
848                                         }
849                                         value = ++iter;
850                                 }
851                         }
852                         /* the remaining or the only one */
853                         /* strip spaces */
854                         while (*value && *value == ' ') {
855                                 value++;
856                         }
857                         /* TODO check for char not 0-9 */
858                         if (*value) {
859                                 int l = strlen(value);
860                                 if (l) {
861                                         struct msn_list *ml = (struct msn_list *)calloc(1, sizeof(struct msn_list));
862                                         ml->msn = (char *)calloc(l+1, sizeof(char));
863                                         strncpy(ml->msn,value,l);
864                                         ml->next = new->msn_list;
865                                         new->msn_list = ml;
866                                 }
867                         }
868                         continue;
869                 }
870                 if (!strcasecmp(v->name, "callgroup")) {
871                         new->callgroup = (ast_group_t *)malloc(sizeof(ast_group_t));
872                         *(new->callgroup)=ast_get_group(v->value);
873                         continue;
874                 }
875                 if (!strcasecmp(v->name, "pickupgroup")) {
876                         new->pickupgroup = (ast_group_t *)malloc(sizeof(ast_group_t));
877                         *(new->pickupgroup)=ast_get_group(v->value);
878                         continue;
879                 }
880         }
881         /* store the new config in our array of port configs */
882         for (i = 0; i < (max_ports + 1); i++) {
883                 if (cfg_for_ports[i])
884                         port_cfg[i] = new;
885         }
886 }
887
888
889 static void fill_defaults (void) {
890         
891         /* general defaults */
892         if (!general_cfg->debug)
893                 general_cfg->debug = (int*)calloc(1, sizeof(int));
894         if (!general_cfg->trace_calls)
895                 general_cfg->trace_calls = (int*)calloc(1, sizeof(int));
896         if (!general_cfg->trace_dir) {
897                 general_cfg->trace_dir = (char *)malloc(10 * sizeof(char));
898                 sprintf(general_cfg->trace_dir, "/var/log/");
899         }
900         if (!general_cfg->bridging) {
901                 general_cfg->bridging = (int*)malloc(sizeof(int));
902                 *general_cfg->bridging = 1;
903         }
904         if (!general_cfg->stop_tone_after_first_digit) {
905                 general_cfg->stop_tone_after_first_digit = (int*)malloc(sizeof(int));
906                 *general_cfg->stop_tone_after_first_digit = 1;
907         }
908         if (!general_cfg->append_digits2exten) {
909                 general_cfg->append_digits2exten = (int*)malloc(sizeof(int));
910                 *general_cfg->append_digits2exten = 1;
911         }
912         if (!general_cfg->l1_info_ok) {
913                 general_cfg->l1_info_ok = (int*)malloc(sizeof(int));
914                 *general_cfg->l1_info_ok = 1;
915         }
916         if (!general_cfg->clear_l3)
917                 general_cfg->clear_l3 =(int*)calloc(1, sizeof(int));
918         if (!general_cfg->dynamic_crypt)
919                 general_cfg->dynamic_crypt = (int*)calloc(1, sizeof(int));
920
921         /* defaults for default port config */
922         if (!port_cfg[0])
923                 port_cfg[0] = (struct port_config*)calloc(1, sizeof(struct port_config));
924         if (!port_cfg[0]->name) {
925                 port_cfg[0]->name = (char *)malloc(8 * sizeof(char));
926                 sprintf(port_cfg[0]->name, "default");
927         }
928         if (!port_cfg[0]->rxgain)
929                 port_cfg[0]->rxgain = (int *)calloc(1, sizeof(int));
930         if (!port_cfg[0]->txgain)
931                 port_cfg[0]->txgain = (int *)calloc(1, sizeof(int));
932         if (!port_cfg[0]->te_choose_channel)
933                 port_cfg[0]->te_choose_channel = (int *)calloc(1, sizeof(int));
934         if (!port_cfg[0]->context) {
935                 port_cfg[0]->context = (char *)malloc(8 * sizeof(char));
936                 sprintf(port_cfg[0]->context, "default");
937         }
938         if (!port_cfg[0]->language) {
939                 port_cfg[0]->language = (char *)malloc(3 * sizeof(char));
940                 sprintf(port_cfg[0]->language, "en");
941         }
942         if (!port_cfg[0]->callerid)
943                 port_cfg[0]->callerid = (char *)calloc(1, sizeof(char));
944         if (!port_cfg[0]->method) {
945                 port_cfg[0]->method = (char *)malloc(9 * sizeof(char));
946                 sprintf(port_cfg[0]->method, "standard");
947         }
948         if (!port_cfg[0]->dialplan)
949                 port_cfg[0]->dialplan = (int *)calloc(1, sizeof(int));
950         if (!port_cfg[0]->nationalprefix) {
951                 port_cfg[0]->nationalprefix = (char *)malloc(2 * sizeof(char));
952                 sprintf(port_cfg[0]->nationalprefix, "0");
953         }
954         if (!port_cfg[0]->internationalprefix) {
955                 port_cfg[0]->internationalprefix = (char *)malloc(3 * sizeof(char));
956                 sprintf(port_cfg[0]->internationalprefix, "00");
957         }
958         if (!port_cfg[0]->pres) {
959                 port_cfg[0]->pres = (int *)malloc(sizeof(int));
960                 *port_cfg[0]->pres = 1;
961         }
962         if (!port_cfg[0]->always_immediate)
963                 port_cfg[0]->always_immediate = (int *)calloc(1, sizeof(int));
964         if (!port_cfg[0]->immediate)
965                 port_cfg[0]->immediate = (int *)calloc(1, sizeof(int));
966         if (!port_cfg[0]->hold_allowed)
967                 port_cfg[0]->hold_allowed = (int *)calloc(1, sizeof(int));
968         if (!port_cfg[0]->early_bconnect) {
969                 port_cfg[0]->early_bconnect = (int *)malloc(sizeof(int));
970                 *port_cfg[0]->early_bconnect = 1;
971         }
972         if (!port_cfg[0]->echocancel)
973                 port_cfg[0]->echocancel=(int *)calloc(1, sizeof(int));
974         if (!port_cfg[0]->echocancelwhenbridged)
975                 port_cfg[0]->echocancelwhenbridged=(int *)calloc(1, sizeof(int));
976         if (!port_cfg[0]->echotraining) {
977                 port_cfg[0]->echotraining=(int *)malloc(sizeof(int));
978                 *port_cfg[0]->echotraining = 1;
979         }
980         if (!port_cfg[0]->use_callingpres) {
981                 port_cfg[0]->use_callingpres = (int *)malloc(sizeof(int));
982                 *port_cfg[0]->use_callingpres = 1;
983         }
984         if (!port_cfg[0]->msn_list) {
985                 port_cfg[0]->msn_list = (struct msn_list *)malloc(sizeof(struct msn_list));
986                 port_cfg[0]->msn_list->next = NULL;
987                 port_cfg[0]->msn_list->msn = (char *)calloc(2, sizeof(char));
988                 *(port_cfg[0]->msn_list->msn) = '*';
989         }
990 }
991
992 void misdn_cfg_reload (void) {
993         misdn_cfg_init (0);
994 }
995
996 void misdn_cfg_destroy (void) {
997
998         misdn_cfg_lock();
999         
1000         free_port_cfg();
1001         free_general_cfg();
1002         
1003         free(port_cfg);
1004         free(general_cfg);
1005         free(ptp);
1006
1007         misdn_cfg_unlock();
1008         ast_mutex_destroy(&config_mutex);
1009 }
1010
1011 void misdn_cfg_init (int this_max_ports)
1012 {
1013         char config[]="misdn.conf";
1014         
1015         struct ast_config *cfg;
1016         cfg = AST_LOAD_CFG(config);
1017         if (!cfg) {
1018                 ast_log(LOG_WARNING,"no misdn.conf ?\n");
1019                 return;
1020         }
1021
1022         misdn_cfg_lock();
1023         
1024         if (this_max_ports) {
1025                 /* this is the first run */
1026                 max_ports = this_max_ports;
1027                 port_cfg = (struct port_config **)calloc(max_ports + 1, sizeof(struct port_config *));
1028                 general_cfg = (struct general_config*)calloc(1, sizeof(struct general_config));
1029                 ptp = (int *)calloc(max_ports + 1, sizeof(int));
1030         }
1031         else {
1032                 free_port_cfg();
1033                 free_general_cfg();
1034                 port_cfg = memset(port_cfg, 0, sizeof(struct port_config *) * (max_ports + 1));
1035                 general_cfg = memset(general_cfg, 0, sizeof(struct general_config));
1036                 ptp = memset(ptp, 0, sizeof(int) * (max_ports + 1));
1037         }
1038         
1039         char *cat;
1040         cat = ast_category_browse(cfg, NULL);
1041
1042         while(cat) {
1043                 struct ast_variable *v=ast_variable_browse(cfg,cat);
1044                 if (!strcasecmp(cat,"general")) {
1045                         build_general_config (v);
1046                 } else {
1047                         build_port_config (v, cat);
1048                 }
1049                 cat=ast_category_browse(cfg,cat);
1050         }
1051
1052         fill_defaults();
1053         
1054         misdn_cfg_unlock();
1055         
1056         AST_DESTROY_CFG(cfg);
1057 }