* first bits of decoding facility information elements
[asterisk/asterisk.git] / channels / misdn_config.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  * 
4  * Copyright (C) 2005, Christian Richter
5  *
6  * Christian Richter <crich@beronet.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  *
18  */
19
20 /*!
21  * \file
22  *
23  * \brief chan_misdn configuration management
24  * \author Christian Richter <crich@beronet.com>
25  *
26  * \ingroup channel_drivers
27  */
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <errno.h>
37
38 #include "chan_misdn_config.h"
39
40 #include "asterisk/config.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/logger.h"
43 #include "asterisk/lock.h"
44 #include "asterisk/pbx.h"
45 #include "asterisk/strings.h"
46 #include "asterisk/utils.h"
47
48 #define AST_LOAD_CFG ast_config_load
49 #define AST_DESTROY_CFG ast_config_destroy
50
51 #define NO_DEFAULT "<>"
52 #define NONE 0
53
54 #define GEN_CFG 1
55 #define PORT_CFG 2
56 #define NUM_GEN_ELEMENTS (sizeof(gen_spec) / sizeof(struct misdn_cfg_spec))
57 #define NUM_PORT_ELEMENTS (sizeof(port_spec) / sizeof(struct misdn_cfg_spec))
58
59 enum misdn_cfg_type {
60         MISDN_CTYPE_STR,
61         MISDN_CTYPE_INT,
62         MISDN_CTYPE_BOOL,
63         MISDN_CTYPE_BOOLINT,
64         MISDN_CTYPE_MSNLIST,
65         MISDN_CTYPE_ASTGROUP
66 };
67
68 struct msn_list {
69         char *msn;
70         struct msn_list *next;
71 };
72
73 union misdn_cfg_pt {
74         char *str;
75         int *num;
76         struct msn_list *ml;
77         ast_group_t *grp;
78         void *any;
79 };
80
81 struct misdn_cfg_spec {
82         char name[BUFFERSIZE];
83         enum misdn_cfg_elements elem;
84         enum misdn_cfg_type type;
85         char def[BUFFERSIZE];
86         int boolint_def;
87         char desc[BUFFERSIZE];
88 };
89
90
91 static const char ports_description[] =
92         "Define your ports, e.g. 1,2 (depends on mISDN-driver loading order).";
93
94 static const struct misdn_cfg_spec port_spec[] = {
95         { "name", MISDN_CFG_GROUPNAME, MISDN_CTYPE_STR, "default", NONE,
96                 "Name of the portgroup." },
97         { "allowed_bearers", MISDN_CFG_ALLOWED_BEARERS, MISDN_CTYPE_STR, "all", NONE,
98                 "Here you can define which bearers should be allowed." },
99         { "rxgain", MISDN_CFG_RXGAIN, MISDN_CTYPE_INT, "0", NONE,
100                 "Set this between -8 and 8 to change the RX Gain." },
101         { "txgain", MISDN_CFG_TXGAIN, MISDN_CTYPE_INT, "0", NONE,
102                 "Set this between -8 and 8 to change the TX Gain." },
103         { "te_choose_channel", MISDN_CFG_TE_CHOOSE_CHANNEL, MISDN_CTYPE_BOOL, "no", NONE,
104                 "Some telcos espacially in NL seem to need this set to yes,\n"
105                 "\talso in switzerland this seems to be important." },
106         { "far_alerting", MISDN_CFG_FAR_ALERTING, MISDN_CTYPE_BOOL, "no", NONE,
107                 "If we should generate ringing for chan_sip and others." },
108         { "pmp_l1_check", MISDN_CFG_PMP_L1_CHECK, MISDN_CTYPE_BOOL, "yes", NONE,
109                 "This option defines, if chan_misdn should check the L1 on a PMP\n"
110                 "\tbefore makeing a group call on it. The L1 may go down for PMP Ports\n"
111                 "\tso we might need this.\n"
112                 "\tBut be aware! a broken or plugged off cable might be used for a group call\n"
113                 "\tas well, since chan_misdn has no chance to distinguish if the L1 is down\n"
114                 "\tbecause of a lost Link or because the Provider shut it down..." },
115         { "block_on_alarm", MISDN_CFG_ALARM_BLOCK, MISDN_CTYPE_BOOL, "yes", NONE ,
116           "Block this port if we have an alarm on it."
117           "default: yes\n" },
118         { "hdlc", MISDN_CFG_HDLC, MISDN_CTYPE_BOOL, "no", NONE,
119                 "Set this to yes, if you want to bridge a mISDN data channel to\n"
120                 "\tanother channel type or to an application." },
121         { "context", MISDN_CFG_CONTEXT, MISDN_CTYPE_STR, "default", NONE,
122                 "Context to use for incoming calls." },
123         { "language", MISDN_CFG_LANGUAGE, MISDN_CTYPE_STR, "en", NONE,
124                 "Language." },
125         { "musicclass", MISDN_CFG_MUSICCLASS, MISDN_CTYPE_STR, "default", NONE,
126                 "Sets the musiconhold class." },
127         { "callerid", MISDN_CFG_CALLERID, MISDN_CTYPE_STR, "", NONE,
128                 "Sets the caller ID." },
129         { "method", MISDN_CFG_METHOD, MISDN_CTYPE_STR, "standard", NONE,
130                 "Sets the method to use for channel selection:\n"
131                 "\t  standard    - always choose the first free channel with the lowest number\n"
132                 "\t  round_robin - use the round robin algorithm to select a channel. use this\n"
133                 "\t                if you want to balance your load." },
134         { "dialplan", MISDN_CFG_DIALPLAN, MISDN_CTYPE_INT, "0", NONE,
135                 "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
136                 "\n"
137                 "\tThere are different types of the dialplan:\n"
138                 "\n"
139                 "\tdialplan -> outgoing Number\n"
140                 "\tlocaldialplan -> callerid\n"
141                 "\tcpndialplan -> connected party number\n"
142                 "\n"
143                 "\tdialplan options:\n"
144                 "\n"
145                 "\t0 - unknown\n"
146                 "\t1 - International\n"
147                 "\t2 - National\n"
148                 "\t4 - Subscriber\n"
149                 "\n"
150                 "\tThis setting is used for outgoing calls." },
151         { "localdialplan", MISDN_CFG_LOCALDIALPLAN, MISDN_CTYPE_INT, "0", NONE,
152                 "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
153                 "\n"
154                 "\tThere are different types of the dialplan:\n"
155                 "\n"
156                 "\tdialplan -> outgoing Number\n"
157                 "\tlocaldialplan -> callerid\n"
158                 "\tcpndialplan -> connected party number\n"
159                 "\n"
160                 "\tdialplan options:\n"
161                 "\n"
162                 "\t0 - unknown\n"
163                 "\t1 - International\n"
164                 "\t2 - National\n"
165                 "\t4 - Subscriber\n"
166                 "\n"
167                 "\tThis setting is used for outgoing calls" },
168         { "cpndialplan", MISDN_CFG_CPNDIALPLAN, MISDN_CTYPE_INT, "0", NONE,
169                 "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
170                 "\n"
171                 "\tThere are different types of the dialplan:\n"
172                 "\n"
173                 "\tdialplan -> outgoing Number\n"
174                 "\tlocaldialplan -> callerid\n"
175                 "\tcpndialplan -> connected party number\n"
176                 "\n"
177                 "\tdialplan options:\n"
178                 "\n"
179                 "\t0 - unknown\n"
180                 "\t1 - International\n"
181                 "\t2 - National\n"
182                 "\t4 - Subscriber\n"
183                 "\n"
184                 "\tThis setting is used for outgoing calls." },
185         { "nationalprefix", MISDN_CFG_NATPREFIX, MISDN_CTYPE_STR, "0", NONE,
186                 "Prefix for national, this is put before the\n"
187                 "\toad if an according dialplan is set by the other end." },
188         { "internationalprefix", MISDN_CFG_INTERNATPREFIX, MISDN_CTYPE_STR, "00", NONE,
189                 "Prefix for international, this is put before the\n"
190                 "\toad if an according dialplan is set by the other end." },
191         { "presentation", MISDN_CFG_PRES, MISDN_CTYPE_INT, "-1", NONE,
192                 "These (presentation and screen) are the exact isdn screening and presentation\n"
193                 "\tindicators.\n"
194                 "\tIf -1 is given for both values, the presentation indicators are used from\n"
195                 "\tAsterisks SetCallerPres application.\n"
196                 "\n"
197                 "\tscreen=0, presentation=0 -> callerid presented not screened\n"
198                 "\tscreen=1, presentation=1 -> callerid presented but screened (the remote end doesn't see it!)" },
199         { "screen", MISDN_CFG_SCREEN, MISDN_CTYPE_INT, "-1", NONE,
200                 "These (presentation and screen) are the exact isdn screening and presentation\n"
201                 "\tindicators.\n"
202                 "\tIf -1 is given for both values, the presentation indicators are used from\n"
203                 "\tAsterisks SetCallerPres application.\n"
204                 "\n"
205                 "\tscreen=0, presentation=0 -> callerid presented not screened\n"
206                 "\tscreen=1, presentation=1 -> callerid presented but screened (the remote end doesn't see it!)" },
207         { "always_immediate", MISDN_CFG_ALWAYS_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE,
208                 "Enable this to get into the s dialplan-extension.\n"
209                 "\tThere you can use DigitTimeout if you can't or don't want to use\n"
210                 "\tisdn overlap dial.\n"
211                 "\tNOTE: This will jump into the s extension for every exten!" },
212         { "nodialtone", MISDN_CFG_NODIALTONE, MISDN_CTYPE_BOOL, "no", NONE,
213                 "Enable this to prevent chan_misdn to generate the dialtone\n"
214                 "\tThis makes only sense together with the always_immediate=yes option\n"
215                 "\tto generate your own dialtone with Playtones or so."},
216         { "immediate", MISDN_CFG_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE,
217                 "Enable this if you want callers which called exactly the base\n"
218                 "\tnumber (so no extension is set) to jump into the s extension.\n"
219                 "\tIf the user dials something more, it jumps to the correct extension\n"
220                 "\tinstead." },
221         { "senddtmf", MISDN_CFG_SENDDTMF, MISDN_CTYPE_BOOL, "no", NONE,
222                 "Enable this if we should produce DTMF Tones ourselves." },
223         { "hold_allowed", MISDN_CFG_HOLD_ALLOWED, MISDN_CTYPE_BOOL, "no", NONE,
224                 "Enable this to have support for hold and retrieve." },
225         { "early_bconnect", MISDN_CFG_EARLY_BCONNECT, MISDN_CTYPE_BOOL, "yes", NONE,
226                 "Disable this if you don't mind correct handling of Progress Indicators." },
227         { "incoming_early_audio", MISDN_CFG_INCOMING_EARLY_AUDIO, MISDN_CTYPE_BOOL, "no", NONE,
228                 "Turn this on if you like to send Tone Indications to a Incoming\n"
229                 "\tisdn channel on a TE Port. Rarely used, only if the Telco allows\n"
230                 "\tyou to send indications by yourself, normally the Telco sends the\n"
231                 "\tindications to the remote party." },
232         { "echocancel", MISDN_CFG_ECHOCANCEL, MISDN_CTYPE_BOOLINT, "0", 128,
233                 "This enables echocancellation, with the given number of taps.\n"
234                 "\tBe aware, move this setting only to outgoing portgroups!\n"
235                 "\tA value of zero turns echocancellation off.\n"
236                 "\n"
237                 "\tPossible values are: 0,32,64,128,256,yes(=128),no(=0)" },
238         { "echocancelwhenbridged", MISDN_CFG_ECHOCANCELWHENBRIDGED, MISDN_CTYPE_BOOL, "no", NONE,
239                 "This disables echocancellation when the call is bridged between\n"
240                 "\tmISDN channels" },
241 #ifdef WITH_BEROEC
242         { "bnechocancel", MISDN_CFG_BNECHOCANCEL, MISDN_CTYPE_BOOLINT, "yes", 64,
243                 "echotail in ms (1-200)\n"},
244         { "bnec_antihowl", MISDN_CFG_BNEC_ANTIHOWL, MISDN_CTYPE_INT, "0", NONE,
245                 "Use antihowl\n"},
246         { "bnec_nlp", MISDN_CFG_BNEC_NLP, MISDN_CTYPE_BOOL, "yes", NONE,
247                 "Nonlinear Processing (much faster adaption)"},
248         { "bnec_zerocoeff", MISDN_CFG_BNEC_ZEROCOEFF, MISDN_CTYPE_BOOL, "no", NONE,
249                 "ZeroCoeffeciens\n"},
250         { "bnec_tonedisabler", MISDN_CFG_BNEC_TD, MISDN_CTYPE_BOOL, "no", NONE,
251                 "Disable Tone\n"},
252         { "bnec_adaption", MISDN_CFG_BNEC_ADAPT, MISDN_CTYPE_INT, "1", NONE,
253                 "Adaption mode (0=no,1=full,2=fast)\n"},
254 #endif
255         { "need_more_infos", MISDN_CFG_NEED_MORE_INFOS, MISDN_CTYPE_BOOL, "0", NONE,
256                 "Send Setup_Acknowledge on incoming calls anyway (instead of PROCEEDING),\n"
257                 "\tthis requests additional Infos, so we can waitfordigits without much\n"
258                 "\tissues. This works only for PTP Ports" },
259         { "jitterbuffer", MISDN_CFG_JITTERBUFFER, MISDN_CTYPE_INT, "4000", NONE,
260                 "The jitterbuffer." },
261         { "jitterbuffer_upper_threshold", MISDN_CFG_JITTERBUFFER_UPPER_THRESHOLD, MISDN_CTYPE_INT, "0", NONE,
262                 "Change this threshold to enable dejitter functionality." },
263         { "callgroup", MISDN_CFG_CALLGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE,
264                 "Callgroup." },
265         { "pickupgroup", MISDN_CFG_PICKUPGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE,
266                 "Pickupgroup." },
267         { "max_incoming", MISDN_CFG_MAX_IN, MISDN_CTYPE_INT, "-1", NONE,
268                 "Defines the maximum amount of incoming calls per port for this group.\n"
269                 "\tCalls which exceed the maximum will be marked with the channel varible\n"
270                 "\tMAX_OVERFLOW. It will contain the amount of overflowed calls" },
271         { "max_outgoing", MISDN_CFG_MAX_OUT, MISDN_CTYPE_INT, "-1", NONE,
272                 "Defines the maximum amount of outgoing calls per port for this group\n"
273                 "\texceeding calls will be rejected" },
274         { "faxdetect", MISDN_CFG_FAXDETECT, MISDN_CTYPE_STR, "no", NONE,
275                 "Setup fax detection:\n"
276                 "\t    no        - no fax detection\n"
277                 "\t    incoming  - fax detection for incoming calls\n"
278                 "\t    outgoing  - fax detection for outgoing calls\n"
279                 "\t    both      - fax detection for incoming and outgoing calls\n"
280                 "\tAdd +nojump to your value (i.e. faxdetect=both+nojump) if you don't want to jump into the\n"
281                 "\tfax-extension but still want to detect the fax and prepare the channel for fax transfer." },
282         { "faxdetect_timeout", MISDN_CFG_FAXDETECT_TIMEOUT, MISDN_CTYPE_INT, "5", NONE,
283                 "Number of seconds the fax detection should do its job. After the given period of time,\n"
284                 "\twe assume that it's not a fax call and save some CPU time by turning off fax detection.\n"
285                 "\tSet this to 0 if you don't want a timeout (never stop detecting)." },
286         { "faxdetect_context", MISDN_CFG_FAXDETECT_CONTEXT, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
287                 "Context to jump into if we detect a fax. Don't set this if you want to stay in the current context." },
288         { "l1watcher_timeout", MISDN_CFG_L1_TIMEOUT, MISDN_CTYPE_BOOLINT, "0", 4,
289                 "Watches the layer 1. If the layer 1 is down, it tries to\n"
290                 "\tget it up. The timeout is given in seconds. with 0 as value it\n"
291                 "\tdoes not watch the l1 at all\n"
292                 "\n"
293                 "\tThis option is only read at loading time of chan_misdn, which\n"
294                 "\tmeans you need to unload and load chan_misdn to change the value,\n"
295                 "\tan Asterisk restart should do the trick." },
296         { "overlap_dial", MISDN_CFG_OVERLAP_DIAL, MISDN_CTYPE_BOOLINT, "0", 4,
297                 "Enables overlap dial for the given amount of seconds.\n"
298                 "\tPossible values are positive integers or:\n"
299                 "\t   yes (= 4 seconds)\n"
300                 "\t   no  (= 0 seconds = disabled)" },
301         { "msns", MISDN_CFG_MSNS, MISDN_CTYPE_MSNLIST, NO_DEFAULT, NONE,
302                 "MSN's for TE ports, listen on those numbers on the above ports, and\n"
303                 "\tindicate the incoming calls to Asterisk.\n"
304                 "\tHere you can give a comma seperated list, or simply an '*' for any msn." },
305 };
306
307 static const struct misdn_cfg_spec gen_spec[] = {
308         { "debug", MISDN_GEN_DEBUG, MISDN_CTYPE_INT, "0", NONE,
309                 "Sets the debugging flag:\n"
310                 "\t0 - No Debug\n"
311                 "\t1 - mISDN Messages and * - Messages, and * - State changes\n"
312                 "\t2 - Messages + Message specific Informations (e.g. bearer capability)\n"
313                 "\t3 - very Verbose, the above + lots of Driver specific infos\n"
314                 "\t4 - even more Verbose than 3" },
315         { "misdn_init", MISDN_GEN_MISDN_INIT, MISDN_CTYPE_STR, "/etc/misdn-init.conf", NONE,
316                 "Set the path to the misdn-init.conf (for nt_ptp mode checking)." },
317         { "tracefile", MISDN_GEN_TRACEFILE, MISDN_CTYPE_STR, "/var/log/asterisk/misdn.log", NONE,
318                 "Set the path to the massively growing trace file, if you want that." },
319         { "bridging", MISDN_GEN_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
320                 "Set this to yes if you want mISDN_dsp to bridge the calls in HW." },
321         { "stop_tone_after_first_digit", MISDN_GEN_STOP_TONE, MISDN_CTYPE_BOOL, "yes", NONE,
322                 "Stops dialtone after getting first digit on NT Port." },
323         { "append_digits2exten", MISDN_GEN_APPEND_DIGITS2EXTEN, MISDN_CTYPE_BOOL, "yes", NONE,
324                 "Wether to append overlapdialed Digits to Extension or not." },
325         { "dynamic_crypt", MISDN_GEN_DYNAMIC_CRYPT, MISDN_CTYPE_BOOL, "no", NONE,
326                 "Wether to look out for dynamic crypting attempts." },
327         { "crypt_prefix", MISDN_GEN_CRYPT_PREFIX, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
328                 "What is used for crypting Protocol." },
329         { "crypt_keys", MISDN_GEN_CRYPT_KEYS, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
330                 "Keys for cryption, you reference them in the dialplan\n"
331                 "\tLater also in dynamic encr." },
332         { "ntdebugflags", MISDN_GEN_NTDEBUGFLAGS, MISDN_CTYPE_INT, "0", NONE,
333                 "No description yet."},
334         { "ntdebugfile", MISDN_GEN_NTDEBUGFILE, MISDN_CTYPE_STR, "/var/log/misdn-nt.log", NONE,
335                 "No description yet." }
336 };
337
338
339 /* array of port configs, default is at position 0. */
340 static union misdn_cfg_pt **port_cfg;
341 /* max number of available ports, is set on init */
342 static int max_ports;
343 /* general config */
344 static union misdn_cfg_pt *general_cfg;
345 /* storing the ptp flag separated to save memory */
346 static int *ptp;
347 /* maps enum config elements to array positions */
348 static int *map;
349
350 static ast_mutex_t config_mutex; 
351
352 #define CLI_ERROR(name, value, section) ({ \
353         ast_log(LOG_WARNING, "misdn.conf: \"%s=%s\" (section: %s) invalid or out of range. " \
354                 "Please edit your misdn.conf and then do a \"misdn reload\".\n", name, value, section); \
355 })
356
357 static int _enum_array_map (void)
358 {
359         int i, j, ok;
360
361         for (i = MISDN_CFG_FIRST + 1; i < MISDN_CFG_LAST; ++i) {
362                 if (i == MISDN_CFG_PTP)
363                         continue;
364                 ok = 0;
365                 for (j = 0; j < NUM_PORT_ELEMENTS; ++j) {
366                         if (port_spec[j].elem == i) {
367                                 map[i] = j;
368                                 ok = 1;
369                                 break;
370                         }
371                 }
372                 if (!ok) {
373                         ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (port section) has no corresponding element in the config struct!\n", i);
374                         return -1;
375                 }
376         }
377         for (i = MISDN_GEN_FIRST + 1; i < MISDN_GEN_LAST; ++i) {
378                 ok = 0;
379                 for (j = 0; j < NUM_GEN_ELEMENTS; ++j) {
380                         if (gen_spec[j].elem == i) {
381                                 map[i] = j;
382                                 ok = 1;
383                                 break;
384                         }
385                 }
386                 if (!ok) {
387                         ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (general section) has no corresponding element in the config struct!\n", i);
388                         return -1;
389                 }
390         }
391         return 0;
392 }
393
394 static int get_cfg_position (char *name, int type)
395 {
396         int i;
397
398         switch (type) {
399         case PORT_CFG:
400                 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
401                         if (!strcasecmp(name, port_spec[i].name))
402                                 return i;
403                 }
404                 break;
405         case GEN_CFG:
406                 for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
407                         if (!strcasecmp(name, gen_spec[i].name))
408                                 return i;
409                 }
410         }
411
412         return -1;
413 }
414
415 static inline void misdn_cfg_lock (void)
416 {
417         ast_mutex_lock(&config_mutex);
418 }
419
420 static inline void misdn_cfg_unlock (void)
421 {
422         ast_mutex_unlock(&config_mutex);
423 }
424
425 static void _free_msn_list (struct msn_list* iter)
426 {
427         if (iter->next)
428                 _free_msn_list(iter->next);
429         if (iter->msn)
430                 free(iter->msn);
431         free(iter);
432 }
433
434 static void _free_port_cfg (void)
435 {
436         int i, j;
437         int gn = map[MISDN_CFG_GROUPNAME];
438         union misdn_cfg_pt* free_list[max_ports + 2];
439         
440         memset(free_list, 0, sizeof(free_list));
441         free_list[0] = port_cfg[0];
442         for (i = 1; i <= max_ports; ++i) {
443                 if (port_cfg[i][gn].str) {
444                         /* we always have a groupname in the non-default case, so this is fine */
445                         for (j = 1; j <= max_ports; ++j) {
446                                 if (free_list[j] && free_list[j][gn].str == port_cfg[i][gn].str)
447                                         break;
448                                 else if (!free_list[j]) {
449                                         free_list[j] = port_cfg[i];
450                                         break;
451                                 }
452                         }
453                 }
454         }
455         for (j = 0; free_list[j]; ++j) {
456                 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
457                         if (free_list[j][i].any) {
458                                 if (port_spec[i].type == MISDN_CTYPE_MSNLIST)
459                                         _free_msn_list(free_list[j][i].ml);
460                                 else
461                                         free(free_list[j][i].any);
462                         }
463                 }
464         }
465 }
466
467 static void _free_general_cfg (void)
468 {
469         int i;
470
471         for (i = 0; i < NUM_GEN_ELEMENTS; i++) 
472                 if (general_cfg[i].any)
473                         free(general_cfg[i].any);
474 }
475
476 void misdn_cfg_get (int port, enum misdn_cfg_elements elem, void *buf, int bufsize)
477 {
478         int place;
479
480         if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
481                 memset(buf, 0, bufsize);
482                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Port number %d is not valid.\n", port);
483                 return;
484         }
485
486         misdn_cfg_lock();
487         if (elem == MISDN_CFG_PTP) {
488                 if (!memcpy(buf, &ptp[port], (bufsize > ptp[port]) ? sizeof(ptp[port]) : bufsize))
489                         memset(buf, 0, bufsize);
490         } else {
491                 if ((place = map[elem]) < 0) {
492                         memset (buf, 0, bufsize);
493                         ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Invalid element (%d) requested.\n", elem);
494                 } else {
495                         if (elem < MISDN_CFG_LAST) {
496                                 switch (port_spec[place].type) {
497                                 case MISDN_CTYPE_STR:
498                                         if (port_cfg[port][place].str) {
499                                                 if (!memccpy(buf, port_cfg[port][place].str, 0, bufsize))
500                                                         memset(buf, 0, 1);
501                                         } else if (port_cfg[0][place].str) {
502                                                 if (!memccpy(buf, port_cfg[0][place].str, 0, bufsize))
503                                                         memset(buf, 0, 1);
504                                         }
505                                         break;
506                                 default:
507                                         if (port_cfg[port][place].any)
508                                                 memcpy(buf, port_cfg[port][place].any, bufsize);
509                                         else if (port_cfg[0][place].any)
510                                                 memcpy(buf, port_cfg[0][place].any, bufsize);
511                                         else
512                                                 memset(buf, 0, bufsize);
513                                 }
514                         } else {
515                                 switch (gen_spec[place].type) {
516                                 case MISDN_CTYPE_STR:
517                                         if (!general_cfg[place].str || !memccpy(buf, general_cfg[place].str, 0, bufsize))
518                                                 memset(buf, 0, 1);
519                                         break;
520                                 default:
521                                         if (general_cfg[place].any)
522                                                 memcpy(buf, general_cfg[place].any, bufsize);
523                                         else
524                                                 memset(buf, 0, bufsize);
525                                 }
526                         }
527                 }
528         }
529         misdn_cfg_unlock();
530 }
531
532 enum misdn_cfg_elements misdn_cfg_get_elem (char *name)
533 {
534         int pos;
535
536         /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
537         if (!strcmp(name, "ports"))
538                 return MISDN_CFG_GROUPNAME;
539         if (!strcmp(name, "name"))
540                 return MISDN_CFG_FIRST;
541
542         pos = get_cfg_position (name, PORT_CFG);
543         if (pos >= 0)
544                 return port_spec[pos].elem;
545         
546         pos = get_cfg_position (name, GEN_CFG);
547         if (pos >= 0)
548                 return gen_spec[pos].elem;
549         
550         return MISDN_CFG_FIRST;
551 }
552
553 void misdn_cfg_get_name (enum misdn_cfg_elements elem, void *buf, int bufsize)
554 {
555         struct misdn_cfg_spec *spec = NULL;
556         int place = map[elem];
557
558         /* the ptp hack */
559         if (elem == MISDN_CFG_PTP) {
560                 memset(buf, 0, 1);
561                 return;
562         }
563         
564         /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
565         if (elem == MISDN_CFG_GROUPNAME) {
566                 if (!snprintf(buf, bufsize, "ports"))
567                         memset(buf, 0, 1);
568                 return;
569         }
570         
571         if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
572                 spec = (struct misdn_cfg_spec *)port_spec;
573         else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
574                 spec = (struct misdn_cfg_spec *)gen_spec;
575
576         if (!spec || !memccpy(buf, spec[place].name, 0, bufsize))
577                 memset(buf, 0, 1);
578 }
579
580 void misdn_cfg_get_desc (enum misdn_cfg_elements elem, void *buf, int bufsize, void *buf_default, int bufsize_default)
581 {
582         int place = map[elem];
583         struct misdn_cfg_spec *spec = NULL;
584
585         /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
586         if (elem == MISDN_CFG_GROUPNAME) {
587                 if (!memccpy(buf, ports_description, 0, bufsize))
588                         memset(buf, 0, 1);
589                 if (buf_default && bufsize_default)
590                         memset(buf_default, 0, 1);
591                 return;
592         }
593
594         if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
595                 spec = (struct misdn_cfg_spec *)port_spec;
596         else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
597                 spec = (struct misdn_cfg_spec *)gen_spec;
598                 
599         if (!spec || !spec[place].desc)
600                 memset(buf, 0, 1);
601         else {
602                 if (!memccpy(buf, spec[place].desc, 0, bufsize))
603                         memset(buf, 0, 1);
604                 if (buf_default && bufsize) {
605                         if (!strcmp(spec[place].def, NO_DEFAULT))
606                                 memset(buf_default, 0, 1);
607                         else if (!memccpy(buf_default, spec[place].def, 0, bufsize_default))
608                                 memset(buf_default, 0, 1);
609                 }
610         }
611 }
612
613 int misdn_cfg_is_msn_valid (int port, char* msn)
614 {
615         int re = 0;
616         struct msn_list *iter;
617
618         if (!misdn_cfg_is_port_valid(port)) {
619                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_is_msn_valid! Port number %d is not valid.\n", port);
620                 return 0;
621         }
622
623         misdn_cfg_lock();
624         if (port_cfg[port][map[MISDN_CFG_MSNS]].ml)
625                 iter = port_cfg[port][map[MISDN_CFG_MSNS]].ml;
626         else
627                 iter = port_cfg[0][map[MISDN_CFG_MSNS]].ml;
628         for (; iter; iter = iter->next) 
629                 if (*(iter->msn) == '*' || ast_extension_match(iter->msn, msn)) {
630                         re = 1;
631                         break;
632                 }
633         misdn_cfg_unlock();
634
635         return re;
636 }
637
638 int misdn_cfg_is_port_valid (int port)
639 {
640         int gn = map[MISDN_CFG_GROUPNAME];
641
642         return (port >= 1 && port <= max_ports && port_cfg[port][gn].str);
643 }
644
645 int misdn_cfg_is_group_method (char *group, enum misdn_cfg_method meth)
646 {
647         int i, re = 0;
648         char *method = NULL;
649
650         misdn_cfg_lock();
651         for (i = 1; i <= max_ports; i++) {
652                 if (port_cfg[i] && port_cfg[i][map[MISDN_CFG_GROUPNAME]].str) {
653                         if (!strcasecmp(port_cfg[i][map[MISDN_CFG_GROUPNAME]].str, group))
654                                 method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ? 
655                                                   port_cfg[i][map[MISDN_CFG_METHOD]].str : port_cfg[0][map[MISDN_CFG_METHOD]].str);
656                 }
657         }
658         if (method) {
659                 switch (meth) {
660                 case METHOD_STANDARD:           re = !strcasecmp(method, "standard");
661                                                                         break;
662                 case METHOD_ROUND_ROBIN:        re = !strcasecmp(method, "round_robin");
663                                                                         break;
664                 }
665         }
666         misdn_cfg_unlock();
667
668         return re;
669 }
670
671 void misdn_cfg_get_ports_string (char *ports)
672 {
673         char tmp[16];
674         int l, i;
675         int gn = map[MISDN_CFG_GROUPNAME];
676
677         *ports = 0;
678
679         misdn_cfg_lock();
680         for (i = 1; i <= max_ports; i++) {
681                 if (port_cfg[i][gn].str) {
682                         if (ptp[i])
683                                 sprintf(tmp, "%dptp,", i);
684                         else
685                                 sprintf(tmp, "%d,", i);
686                         strcat(ports, tmp);
687                 }
688         }
689         misdn_cfg_unlock();
690
691         if ((l = strlen(ports)))
692                 ports[l-1] = 0;
693 }
694
695 void misdn_cfg_get_config_string (int port, enum misdn_cfg_elements elem, char* buf, int bufsize)
696 {
697         int place;
698         char tempbuf[BUFFERSIZE] = "";
699         struct msn_list *iter;
700
701         if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
702                 *buf = 0;
703                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Port number %d is not valid.\n", port);
704                 return;
705         }
706
707         place = map[elem];
708
709         misdn_cfg_lock();
710         if (elem == MISDN_CFG_PTP) {
711                 snprintf(buf, bufsize, " -> ptp: %s", ptp[port] ? "yes" : "no");
712         }
713         else if (elem > MISDN_CFG_FIRST && elem < MISDN_CFG_LAST) {
714                 switch (port_spec[place].type) {
715                 case MISDN_CTYPE_INT:
716                 case MISDN_CTYPE_BOOLINT:
717                         if (port_cfg[port][place].num)
718                                 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[port][place].num);
719                         else if (port_cfg[0][place].num)
720                                 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[0][place].num);
721                         else
722                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
723                         break;
724                 case MISDN_CTYPE_BOOL:
725                         if (port_cfg[port][place].num)
726                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[port][place].num ? "yes" : "no");
727                         else if (port_cfg[0][place].num)
728                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[0][place].num ? "yes" : "no");
729                         else
730                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
731                         break;
732                 case MISDN_CTYPE_ASTGROUP:
733                         if (port_cfg[port][place].grp)
734                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, 
735                                                  ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[port][place].grp));
736                         else if (port_cfg[0][place].grp)
737                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, 
738                                                  ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[0][place].grp));
739                         else
740                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
741                         break;
742                 case MISDN_CTYPE_MSNLIST:
743                         if (port_cfg[port][place].ml)
744                                 iter = port_cfg[port][place].ml;
745                         else
746                                 iter = port_cfg[0][place].ml;
747                         if (iter) {
748                                 for (; iter; iter = iter->next)
749                                         sprintf(tempbuf, "%s%s, ", tempbuf, iter->msn);
750                                 tempbuf[strlen(tempbuf)-2] = 0;
751                         }
752                         snprintf(buf, bufsize, " -> msns: %s", *tempbuf ? tempbuf : "none");
753                         break;
754                 case MISDN_CTYPE_STR:
755                         if ( port_cfg[port][place].str) {
756                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[port][place].str);
757                         } else if (port_cfg[0][place].str) {
758                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[0][place].str);
759                         } else {
760                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
761                         }
762                         break;
763                 }
764         } else if (elem > MISDN_GEN_FIRST && elem < MISDN_GEN_LAST) {
765                 switch (gen_spec[place].type) {
766                 case MISDN_CTYPE_INT:
767                 case MISDN_CTYPE_BOOLINT:
768                         if (general_cfg[place].num)
769                                 snprintf(buf, bufsize, " -> %s: %d", gen_spec[place].name, *general_cfg[place].num);
770                         else
771                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
772                         break;
773                 case MISDN_CTYPE_BOOL:
774                         if (general_cfg[place].num)
775                                 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, *general_cfg[place].num ? "yes" : "no");
776                         else
777                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
778                         break;
779                 case MISDN_CTYPE_STR:
780                         if ( general_cfg[place].str) {
781                                 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, general_cfg[place].str);
782                         } else {
783                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
784                         }
785                         break;
786                 default:
787                         snprintf(buf, bufsize, " -> type of %s not handled yet", gen_spec[place].name);
788                         break;
789                 }
790         } else {
791                 *buf = 0;
792                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Invalid config element (%d) requested.\n", elem);
793         }
794         misdn_cfg_unlock();
795 }
796
797 int misdn_cfg_get_next_port (int port)
798 {
799         int p = -1;
800         int gn = map[MISDN_CFG_GROUPNAME];
801         
802         misdn_cfg_lock();
803         for (port++; port <= max_ports; port++) {
804                 if (port_cfg[port][gn].str) {
805                         p = port;
806                         break;
807                 }
808         }
809         misdn_cfg_unlock();
810
811         return p;
812 }
813
814 int misdn_cfg_get_next_port_spin (int port)
815 {
816         int p = misdn_cfg_get_next_port(port);
817         return (p > 0) ? p : misdn_cfg_get_next_port(0);
818 }
819
820 static int _parse (union misdn_cfg_pt *dest, char *value, enum misdn_cfg_type type, int boolint_def)
821 {
822         int re = 0;
823         int len, tmp;
824         char *valtmp;
825
826         switch (type) {
827         case MISDN_CTYPE_STR:
828                 if ((len = strlen(value))) {
829                         dest->str = (char *)malloc((len + 1) * sizeof(char));
830                         strncpy(dest->str, value, len);
831                         dest->str[len] = 0;
832                 } else {
833                         dest->str = (char *)malloc( sizeof(char));
834                         dest->str[0] = 0;
835                 }
836                 break;
837         case MISDN_CTYPE_INT:
838         {
839                 char *pat;
840                 if (strchr(value,'x')) 
841                         pat="%x";
842                 else
843                         pat="%d";
844                 if (sscanf(value, pat, &tmp)) {
845                         dest->num = (int *)malloc(sizeof(int));
846                         memcpy(dest->num, &tmp, sizeof(int));
847                 } else
848                         re = -1;
849         }
850                 break;
851         case MISDN_CTYPE_BOOL:
852                 dest->num = (int *)malloc(sizeof(int));
853                 *(dest->num) = (ast_true(value) ? 1 : 0);
854                 break;
855         case MISDN_CTYPE_BOOLINT:
856                 dest->num = (int *)malloc(sizeof(int));
857                 if (sscanf(value, "%d", &tmp)) {
858                         memcpy(dest->num, &tmp, sizeof(int));
859                 } else {
860                         *(dest->num) = (ast_true(value) ? boolint_def : 0);
861                 }
862                 break;
863         case MISDN_CTYPE_MSNLIST:
864                 for (valtmp = strsep(&value, ","); valtmp; valtmp = strsep(&value, ",")) {
865                         if ((len = strlen(valtmp))) {
866                                 struct msn_list *ml = (struct msn_list *)malloc(sizeof(struct msn_list));
867                                 ml->msn = (char *)calloc(len+1, sizeof(char));
868                                 strncpy(ml->msn, valtmp, len);
869                                 ml->next = dest->ml;
870                                 dest->ml = ml;
871                         }
872                 }
873                 break;
874         case MISDN_CTYPE_ASTGROUP:
875                 dest->grp = (ast_group_t *)malloc(sizeof(ast_group_t));
876                 *(dest->grp) = ast_get_group(value);
877                 break;
878         }
879
880         return re;
881 }
882
883 static void _build_general_config (struct ast_variable *v)
884 {
885         int pos;
886
887         for (; v; v = v->next) {
888                 if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) || 
889                         (_parse(&general_cfg[pos], v->value, gen_spec[pos].type, gen_spec[pos].boolint_def) < 0))
890                         CLI_ERROR(v->name, v->value, "general");
891         }
892 }
893
894 static void _build_port_config (struct ast_variable *v, char *cat)
895 {
896         int pos, i;
897         union misdn_cfg_pt cfg_tmp[NUM_PORT_ELEMENTS];
898         int cfg_for_ports[max_ports + 1];
899
900         if (!v || !cat)
901                 return;
902
903         memset(cfg_tmp, 0, sizeof(cfg_tmp));
904         memset(cfg_for_ports, 0, sizeof(cfg_for_ports));
905
906         if (!strcasecmp(cat, "default")) {
907                 cfg_for_ports[0] = 1;
908         }
909
910         if (((pos = get_cfg_position("name", PORT_CFG)) < 0) || 
911                 (_parse(&cfg_tmp[pos], cat, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) {
912                 CLI_ERROR(v->name, v->value, cat);
913                 return;
914         }
915
916         for (; v; v = v->next) {
917                 if (!strcasecmp(v->name, "ports")) {
918                         char *token;
919                         char ptpbuf[BUFFERSIZE] = "";
920                         int start, end;
921                         for (token = strsep(&v->value, ","); token; token = strsep(&v->value, ","), *ptpbuf = 0) { 
922                                 if (!*token)
923                                         continue;
924                                 if (sscanf(token, "%d-%d%s", &start, &end, ptpbuf) >= 2) {
925                                         for (; start <= end; start++) {
926                                                 if (start <= max_ports && start > 0) {
927                                                         cfg_for_ports[start] = 1;
928                                                         ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
929                                                 } else
930                                                         CLI_ERROR(v->name, v->value, cat);
931                                         }
932                                 } else {
933                                         if (sscanf(token, "%d%s", &start, ptpbuf)) {
934                                                 if (start <= max_ports && start > 0) {
935                                                         cfg_for_ports[start] = 1;
936                                                         ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
937                                                 } else
938                                                         CLI_ERROR(v->name, v->value, cat);
939                                         } else
940                                                 CLI_ERROR(v->name, v->value, cat);
941                                 }
942                         }
943                 } else {
944                         if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) || 
945                                 (_parse(&cfg_tmp[pos], v->value, port_spec[pos].type, port_spec[pos].boolint_def) < 0))
946                                 CLI_ERROR(v->name, v->value, cat);
947                 }
948         }
949
950         for (i = 0; i < (max_ports + 1); ++i) {
951                 if (cfg_for_ports[i]) {
952                         memcpy(port_cfg[i], cfg_tmp, sizeof(cfg_tmp));
953                 }
954         }
955 }
956
957 void misdn_cfg_update_ptp (void)
958 {
959         char misdn_init[BUFFERSIZE];
960         char line[BUFFERSIZE];
961         FILE *fp;
962         char *tok, *p, *end;
963         int port;
964
965         misdn_cfg_get(0, MISDN_GEN_MISDN_INIT, &misdn_init, sizeof(misdn_init));
966
967         if (misdn_init) {
968                 fp = fopen(misdn_init, "r");
969                 if (fp) {
970                         while(fgets(line, sizeof(line), fp)) {
971                                 if (!strncmp(line, "nt_ptp", 6)) {
972                                         for (tok = strtok_r(line,",=", &p);
973                                                  tok;
974                                                  tok = strtok_r(NULL,",=", &p)) {
975                                                 port = strtol(tok, &end, 10);
976                                                 if (end != tok && misdn_cfg_is_port_valid(port)) {
977                                                         misdn_cfg_lock();
978                                                         ptp[port] = 1;
979                                                         misdn_cfg_unlock();
980                                                 }
981                                         }
982                                 }
983                         }
984                         fclose(fp);
985                 } else {
986                         ast_log(LOG_WARNING,"Couldn't open %s: %s\n", misdn_init, strerror(errno));
987                 }
988         }
989 }
990
991 static void _fill_defaults (void)
992 {
993         int i;
994
995         for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
996                 if (!port_cfg[0][i].any && strcasecmp(port_spec[i].def, NO_DEFAULT))
997                         _parse(&(port_cfg[0][i]), (char *)port_spec[i].def, port_spec[i].type, port_spec[i].boolint_def);
998         }
999         for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
1000                 if (!general_cfg[i].any && strcasecmp(gen_spec[i].def, NO_DEFAULT))
1001                         _parse(&(general_cfg[i]), (char *)gen_spec[i].def, gen_spec[i].type, gen_spec[i].boolint_def);
1002         }
1003 }
1004
1005 void misdn_cfg_reload (void)
1006 {
1007         misdn_cfg_init (0);
1008 }
1009
1010 void misdn_cfg_destroy (void)
1011 {
1012         misdn_cfg_lock();
1013
1014         _free_port_cfg();
1015         _free_general_cfg();
1016
1017         free(port_cfg);
1018         free(general_cfg);
1019         free(ptp);
1020         free(map);
1021
1022         misdn_cfg_unlock();
1023         ast_mutex_destroy(&config_mutex);
1024 }
1025
1026 int misdn_cfg_init (int this_max_ports)
1027 {
1028         char config[] = "misdn.conf";
1029         char *cat, *p;
1030         int i;
1031         struct ast_config *cfg;
1032         struct ast_variable *v;
1033
1034         if (!(cfg = AST_LOAD_CFG(config))) {
1035                 ast_log(LOG_WARNING, "missing file: misdn.conf\n");
1036                 return -1;
1037         }
1038
1039         ast_mutex_init(&config_mutex);
1040
1041         misdn_cfg_lock();
1042
1043         if (this_max_ports) {
1044                 /* this is the first run */
1045                 max_ports = this_max_ports;
1046                 map = (int *)calloc(MISDN_GEN_LAST + 1, sizeof(int));
1047                 if (_enum_array_map())
1048                         return -1;
1049                 p = (char *)calloc(1, (max_ports + 1) * sizeof(union misdn_cfg_pt *)
1050                                                    + (max_ports + 1) * NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt));
1051                 port_cfg = (union misdn_cfg_pt **)p;
1052                 p += (max_ports + 1) * sizeof(union misdn_cfg_pt *);
1053                 for (i = 0; i <= max_ports; ++i) {
1054                         port_cfg[i] = (union misdn_cfg_pt *)p;
1055                         p += NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt);
1056                 }
1057                 general_cfg = (union misdn_cfg_pt *)calloc(1, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1058                 ptp = (int *)calloc(max_ports + 1, sizeof(int));
1059         }
1060         else {
1061                 /* misdn reload */
1062                 _free_port_cfg();
1063                 _free_general_cfg();
1064                 memset(port_cfg[0], 0, NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt) * (max_ports + 1));
1065                 memset(general_cfg, 0, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1066                 memset(ptp, 0, sizeof(int) * (max_ports + 1));
1067         }
1068
1069         cat = ast_category_browse(cfg, NULL);
1070
1071         while(cat) {
1072                 v = ast_variable_browse(cfg, cat);
1073                 if (!strcasecmp(cat, "general")) {
1074                         _build_general_config(v);
1075                 } else {
1076                         _build_port_config(v, cat);
1077                 }
1078                 cat = ast_category_browse(cfg, cat);
1079         }
1080
1081         _fill_defaults();
1082
1083         misdn_cfg_unlock();
1084         AST_DESTROY_CFG(cfg);
1085
1086         return 0;
1087 }
1088
1089