Make local copy of arguments to parse. (issue #8362 reported by homesick)
[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
275         { "reject_cause", MISDN_CFG_REJECT_CAUSE, MISDN_CTYPE_INT, "21", NONE,
276                 "Defines the cause with which a 3. call is rejected on PTMP BRI."},
277         { "faxdetect", MISDN_CFG_FAXDETECT, MISDN_CTYPE_STR, "no", NONE,
278                 "Setup fax detection:\n"
279                 "\t    no        - no fax detection\n"
280                 "\t    incoming  - fax detection for incoming calls\n"
281                 "\t    outgoing  - fax detection for outgoing calls\n"
282                 "\t    both      - fax detection for incoming and outgoing calls\n"
283                 "\tAdd +nojump to your value (i.e. faxdetect=both+nojump) if you don't want to jump into the\n"
284                 "\tfax-extension but still want to detect the fax and prepare the channel for fax transfer." },
285         { "faxdetect_timeout", MISDN_CFG_FAXDETECT_TIMEOUT, MISDN_CTYPE_INT, "5", NONE,
286                 "Number of seconds the fax detection should do its job. After the given period of time,\n"
287                 "\twe assume that it's not a fax call and save some CPU time by turning off fax detection.\n"
288                 "\tSet this to 0 if you don't want a timeout (never stop detecting)." },
289         { "faxdetect_context", MISDN_CFG_FAXDETECT_CONTEXT, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
290                 "Context to jump into if we detect a fax. Don't set this if you want to stay in the current context." },
291         { "l1watcher_timeout", MISDN_CFG_L1_TIMEOUT, MISDN_CTYPE_BOOLINT, "0", 4,
292                 "Watches the layer 1. If the layer 1 is down, it tries to\n"
293                 "\tget it up. The timeout is given in seconds. with 0 as value it\n"
294                 "\tdoes not watch the l1 at all\n"
295                 "\n"
296                 "\tThis option is only read at loading time of chan_misdn, which\n"
297                 "\tmeans you need to unload and load chan_misdn to change the value,\n"
298                 "\tan Asterisk restart should do the trick." },
299         { "overlapdial", MISDN_CFG_OVERLAP_DIAL, MISDN_CTYPE_BOOLINT, "0", 4,
300                 "Enables overlap dial for the given amount of seconds.\n"
301                 "\tPossible values are positive integers or:\n"
302                 "\t   yes (= 4 seconds)\n"
303                 "\t   no  (= 0 seconds = disabled)" },
304         { "nttimeout", MISDN_CFG_NTTIMEOUT, MISDN_CTYPE_BOOL, "no", NONE ,
305                 "Set this to yes if you want calls disconnected in overlap mode\n"
306                 "\twhen a timeout happens." },
307         { "msns", MISDN_CFG_MSNS, MISDN_CTYPE_MSNLIST, NO_DEFAULT, NONE,
308                 "MSN's for TE ports, listen on those numbers on the above ports, and\n"
309                 "\tindicate the incoming calls to Asterisk.\n"
310                 "\tHere you can give a comma seperated list, or simply an '*' for any msn." },
311
312 };
313
314 static const struct misdn_cfg_spec gen_spec[] = {
315         { "debug", MISDN_GEN_DEBUG, MISDN_CTYPE_INT, "0", NONE,
316                 "Sets the debugging flag:\n"
317                 "\t0 - No Debug\n"
318                 "\t1 - mISDN Messages and * - Messages, and * - State changes\n"
319                 "\t2 - Messages + Message specific Informations (e.g. bearer capability)\n"
320                 "\t3 - very Verbose, the above + lots of Driver specific infos\n"
321                 "\t4 - even more Verbose than 3" },
322         { "misdn_init", MISDN_GEN_MISDN_INIT, MISDN_CTYPE_STR, "/etc/misdn-init.conf", NONE,
323                 "Set the path to the misdn-init.conf (for nt_ptp mode checking)." },
324         { "tracefile", MISDN_GEN_TRACEFILE, MISDN_CTYPE_STR, "/var/log/asterisk/misdn.log", NONE,
325                 "Set the path to the massively growing trace file, if you want that." },
326         { "bridging", MISDN_GEN_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
327                 "Set this to yes if you want mISDN_dsp to bridge the calls in HW." },
328         { "stop_tone_after_first_digit", MISDN_GEN_STOP_TONE, MISDN_CTYPE_BOOL, "yes", NONE,
329                 "Stops dialtone after getting first digit on NT Port." },
330         { "append_digits2exten", MISDN_GEN_APPEND_DIGITS2EXTEN, MISDN_CTYPE_BOOL, "yes", NONE,
331                 "Wether to append overlapdialed Digits to Extension or not." },
332         { "dynamic_crypt", MISDN_GEN_DYNAMIC_CRYPT, MISDN_CTYPE_BOOL, "no", NONE,
333                 "Wether to look out for dynamic crypting attempts." },
334         { "crypt_prefix", MISDN_GEN_CRYPT_PREFIX, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
335                 "What is used for crypting Protocol." },
336         { "crypt_keys", MISDN_GEN_CRYPT_KEYS, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
337                 "Keys for cryption, you reference them in the dialplan\n"
338                 "\tLater also in dynamic encr." },
339         { "ntdebugflags", MISDN_GEN_NTDEBUGFLAGS, MISDN_CTYPE_INT, "0", NONE,
340                 "No description yet."},
341         { "ntdebugfile", MISDN_GEN_NTDEBUGFILE, MISDN_CTYPE_STR, "/var/log/misdn-nt.log", NONE,
342                 "No description yet." }
343 };
344
345
346 /* array of port configs, default is at position 0. */
347 static union misdn_cfg_pt **port_cfg;
348 /* max number of available ports, is set on init */
349 static int max_ports;
350 /* general config */
351 static union misdn_cfg_pt *general_cfg;
352 /* storing the ptp flag separated to save memory */
353 static int *ptp;
354 /* maps enum config elements to array positions */
355 static int *map;
356
357 static ast_mutex_t config_mutex; 
358
359 #define CLI_ERROR(name, value, section) ({ \
360         ast_log(LOG_WARNING, "misdn.conf: \"%s=%s\" (section: %s) invalid or out of range. " \
361                 "Please edit your misdn.conf and then do a \"misdn reload\".\n", name, value, section); \
362 })
363
364 static int _enum_array_map (void)
365 {
366         int i, j, ok;
367
368         for (i = MISDN_CFG_FIRST + 1; i < MISDN_CFG_LAST; ++i) {
369                 if (i == MISDN_CFG_PTP)
370                         continue;
371                 ok = 0;
372                 for (j = 0; j < NUM_PORT_ELEMENTS; ++j) {
373                         if (port_spec[j].elem == i) {
374                                 map[i] = j;
375                                 ok = 1;
376                                 break;
377                         }
378                 }
379                 if (!ok) {
380                         ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (port section) has no corresponding element in the config struct!\n", i);
381                         return -1;
382                 }
383         }
384         for (i = MISDN_GEN_FIRST + 1; i < MISDN_GEN_LAST; ++i) {
385                 ok = 0;
386                 for (j = 0; j < NUM_GEN_ELEMENTS; ++j) {
387                         if (gen_spec[j].elem == i) {
388                                 map[i] = j;
389                                 ok = 1;
390                                 break;
391                         }
392                 }
393                 if (!ok) {
394                         ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (general section) has no corresponding element in the config struct!\n", i);
395                         return -1;
396                 }
397         }
398         return 0;
399 }
400
401 static int get_cfg_position (char *name, int type)
402 {
403         int i;
404
405         switch (type) {
406         case PORT_CFG:
407                 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
408                         if (!strcasecmp(name, port_spec[i].name))
409                                 return i;
410                 }
411                 break;
412         case GEN_CFG:
413                 for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
414                         if (!strcasecmp(name, gen_spec[i].name))
415                                 return i;
416                 }
417         }
418
419         return -1;
420 }
421
422 static inline void misdn_cfg_lock (void)
423 {
424         ast_mutex_lock(&config_mutex);
425 }
426
427 static inline void misdn_cfg_unlock (void)
428 {
429         ast_mutex_unlock(&config_mutex);
430 }
431
432 static void _free_msn_list (struct msn_list* iter)
433 {
434         if (iter->next)
435                 _free_msn_list(iter->next);
436         if (iter->msn)
437                 free(iter->msn);
438         free(iter);
439 }
440
441 static void _free_port_cfg (void)
442 {
443         int i, j;
444         int gn = map[MISDN_CFG_GROUPNAME];
445         union misdn_cfg_pt* free_list[max_ports + 2];
446         
447         memset(free_list, 0, sizeof(free_list));
448         free_list[0] = port_cfg[0];
449         for (i = 1; i <= max_ports; ++i) {
450                 if (port_cfg[i][gn].str) {
451                         /* we always have a groupname in the non-default case, so this is fine */
452                         for (j = 1; j <= max_ports; ++j) {
453                                 if (free_list[j] && free_list[j][gn].str == port_cfg[i][gn].str)
454                                         break;
455                                 else if (!free_list[j]) {
456                                         free_list[j] = port_cfg[i];
457                                         break;
458                                 }
459                         }
460                 }
461         }
462         for (j = 0; free_list[j]; ++j) {
463                 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
464                         if (free_list[j][i].any) {
465                                 if (port_spec[i].type == MISDN_CTYPE_MSNLIST)
466                                         _free_msn_list(free_list[j][i].ml);
467                                 else
468                                         free(free_list[j][i].any);
469                         }
470                 }
471         }
472 }
473
474 static void _free_general_cfg (void)
475 {
476         int i;
477
478         for (i = 0; i < NUM_GEN_ELEMENTS; i++) 
479                 if (general_cfg[i].any)
480                         free(general_cfg[i].any);
481 }
482
483 void misdn_cfg_get (int port, enum misdn_cfg_elements elem, void *buf, int bufsize)
484 {
485         int place;
486
487         if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
488                 memset(buf, 0, bufsize);
489                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Port number %d is not valid.\n", port);
490                 return;
491         }
492
493         misdn_cfg_lock();
494         if (elem == MISDN_CFG_PTP) {
495                 if (!memcpy(buf, &ptp[port], (bufsize > ptp[port]) ? sizeof(ptp[port]) : bufsize))
496                         memset(buf, 0, bufsize);
497         } else {
498                 if ((place = map[elem]) < 0) {
499                         memset (buf, 0, bufsize);
500                         ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Invalid element (%d) requested.\n", elem);
501                 } else {
502                         if (elem < MISDN_CFG_LAST) {
503                                 switch (port_spec[place].type) {
504                                 case MISDN_CTYPE_STR:
505                                         if (port_cfg[port][place].str) {
506                                                 if (!memccpy(buf, port_cfg[port][place].str, 0, bufsize))
507                                                         memset(buf, 0, 1);
508                                         } else if (port_cfg[0][place].str) {
509                                                 if (!memccpy(buf, port_cfg[0][place].str, 0, bufsize))
510                                                         memset(buf, 0, 1);
511                                         }
512                                         break;
513                                 default:
514                                         if (port_cfg[port][place].any)
515                                                 memcpy(buf, port_cfg[port][place].any, bufsize);
516                                         else if (port_cfg[0][place].any)
517                                                 memcpy(buf, port_cfg[0][place].any, bufsize);
518                                         else
519                                                 memset(buf, 0, bufsize);
520                                 }
521                         } else {
522                                 switch (gen_spec[place].type) {
523                                 case MISDN_CTYPE_STR:
524                                         if (!general_cfg[place].str || !memccpy(buf, general_cfg[place].str, 0, bufsize))
525                                                 memset(buf, 0, 1);
526                                         break;
527                                 default:
528                                         if (general_cfg[place].any)
529                                                 memcpy(buf, general_cfg[place].any, bufsize);
530                                         else
531                                                 memset(buf, 0, bufsize);
532                                 }
533                         }
534                 }
535         }
536         misdn_cfg_unlock();
537 }
538
539 enum misdn_cfg_elements misdn_cfg_get_elem (char *name)
540 {
541         int pos;
542
543         /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
544         if (!strcmp(name, "ports"))
545                 return MISDN_CFG_GROUPNAME;
546         if (!strcmp(name, "name"))
547                 return MISDN_CFG_FIRST;
548
549         pos = get_cfg_position (name, PORT_CFG);
550         if (pos >= 0)
551                 return port_spec[pos].elem;
552         
553         pos = get_cfg_position (name, GEN_CFG);
554         if (pos >= 0)
555                 return gen_spec[pos].elem;
556         
557         return MISDN_CFG_FIRST;
558 }
559
560 void misdn_cfg_get_name (enum misdn_cfg_elements elem, void *buf, int bufsize)
561 {
562         struct misdn_cfg_spec *spec = NULL;
563         int place = map[elem];
564
565         /* the ptp hack */
566         if (elem == MISDN_CFG_PTP) {
567                 memset(buf, 0, 1);
568                 return;
569         }
570         
571         /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
572         if (elem == MISDN_CFG_GROUPNAME) {
573                 if (!snprintf(buf, bufsize, "ports"))
574                         memset(buf, 0, 1);
575                 return;
576         }
577         
578         if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
579                 spec = (struct misdn_cfg_spec *)port_spec;
580         else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
581                 spec = (struct misdn_cfg_spec *)gen_spec;
582
583         if (!spec || !memccpy(buf, spec[place].name, 0, bufsize))
584                 memset(buf, 0, 1);
585 }
586
587 void misdn_cfg_get_desc (enum misdn_cfg_elements elem, void *buf, int bufsize, void *buf_default, int bufsize_default)
588 {
589         int place = map[elem];
590         struct misdn_cfg_spec *spec = NULL;
591
592         /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
593         if (elem == MISDN_CFG_GROUPNAME) {
594                 if (!memccpy(buf, ports_description, 0, bufsize))
595                         memset(buf, 0, 1);
596                 if (buf_default && bufsize_default)
597                         memset(buf_default, 0, 1);
598                 return;
599         }
600
601         if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
602                 spec = (struct misdn_cfg_spec *)port_spec;
603         else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
604                 spec = (struct misdn_cfg_spec *)gen_spec;
605                 
606         if (!spec || !spec[place].desc)
607                 memset(buf, 0, 1);
608         else {
609                 if (!memccpy(buf, spec[place].desc, 0, bufsize))
610                         memset(buf, 0, 1);
611                 if (buf_default && bufsize) {
612                         if (!strcmp(spec[place].def, NO_DEFAULT))
613                                 memset(buf_default, 0, 1);
614                         else if (!memccpy(buf_default, spec[place].def, 0, bufsize_default))
615                                 memset(buf_default, 0, 1);
616                 }
617         }
618 }
619
620 int misdn_cfg_is_msn_valid (int port, char* msn)
621 {
622         int re = 0;
623         struct msn_list *iter;
624
625         if (!misdn_cfg_is_port_valid(port)) {
626                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_is_msn_valid! Port number %d is not valid.\n", port);
627                 return 0;
628         }
629
630         misdn_cfg_lock();
631         if (port_cfg[port][map[MISDN_CFG_MSNS]].ml)
632                 iter = port_cfg[port][map[MISDN_CFG_MSNS]].ml;
633         else
634                 iter = port_cfg[0][map[MISDN_CFG_MSNS]].ml;
635         for (; iter; iter = iter->next) 
636                 if (*(iter->msn) == '*' || ast_extension_match(iter->msn, msn)) {
637                         re = 1;
638                         break;
639                 }
640         misdn_cfg_unlock();
641
642         return re;
643 }
644
645 int misdn_cfg_is_port_valid (int port)
646 {
647         int gn = map[MISDN_CFG_GROUPNAME];
648
649         return (port >= 1 && port <= max_ports && port_cfg[port][gn].str);
650 }
651
652 int misdn_cfg_is_group_method (char *group, enum misdn_cfg_method meth)
653 {
654         int i, re = 0;
655         char *method = NULL;
656
657         misdn_cfg_lock();
658         for (i = 1; i <= max_ports; i++) {
659                 if (port_cfg[i] && port_cfg[i][map[MISDN_CFG_GROUPNAME]].str) {
660                         if (!strcasecmp(port_cfg[i][map[MISDN_CFG_GROUPNAME]].str, group))
661                                 method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ? 
662                                                   port_cfg[i][map[MISDN_CFG_METHOD]].str : port_cfg[0][map[MISDN_CFG_METHOD]].str);
663                 }
664         }
665         if (method) {
666                 switch (meth) {
667                 case METHOD_STANDARD:           re = !strcasecmp(method, "standard");
668                                                                         break;
669                 case METHOD_ROUND_ROBIN:        re = !strcasecmp(method, "round_robin");
670                                                                         break;
671                 }
672         }
673         misdn_cfg_unlock();
674
675         return re;
676 }
677
678 void misdn_cfg_get_ports_string (char *ports)
679 {
680         char tmp[16];
681         int l, i;
682         int gn = map[MISDN_CFG_GROUPNAME];
683
684         *ports = 0;
685
686         misdn_cfg_lock();
687         for (i = 1; i <= max_ports; i++) {
688                 if (port_cfg[i][gn].str) {
689                         if (ptp[i])
690                                 sprintf(tmp, "%dptp,", i);
691                         else
692                                 sprintf(tmp, "%d,", i);
693                         strcat(ports, tmp);
694                 }
695         }
696         misdn_cfg_unlock();
697
698         if ((l = strlen(ports)))
699                 ports[l-1] = 0;
700 }
701
702 void misdn_cfg_get_config_string (int port, enum misdn_cfg_elements elem, char* buf, int bufsize)
703 {
704         int place;
705         char tempbuf[BUFFERSIZE] = "";
706         struct msn_list *iter;
707
708         if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
709                 *buf = 0;
710                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Port number %d is not valid.\n", port);
711                 return;
712         }
713
714         place = map[elem];
715
716         misdn_cfg_lock();
717         if (elem == MISDN_CFG_PTP) {
718                 snprintf(buf, bufsize, " -> ptp: %s", ptp[port] ? "yes" : "no");
719         }
720         else if (elem > MISDN_CFG_FIRST && elem < MISDN_CFG_LAST) {
721                 switch (port_spec[place].type) {
722                 case MISDN_CTYPE_INT:
723                 case MISDN_CTYPE_BOOLINT:
724                         if (port_cfg[port][place].num)
725                                 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[port][place].num);
726                         else if (port_cfg[0][place].num)
727                                 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[0][place].num);
728                         else
729                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
730                         break;
731                 case MISDN_CTYPE_BOOL:
732                         if (port_cfg[port][place].num)
733                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[port][place].num ? "yes" : "no");
734                         else if (port_cfg[0][place].num)
735                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[0][place].num ? "yes" : "no");
736                         else
737                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
738                         break;
739                 case MISDN_CTYPE_ASTGROUP:
740                         if (port_cfg[port][place].grp)
741                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, 
742                                                  ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[port][place].grp));
743                         else if (port_cfg[0][place].grp)
744                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, 
745                                                  ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[0][place].grp));
746                         else
747                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
748                         break;
749                 case MISDN_CTYPE_MSNLIST:
750                         if (port_cfg[port][place].ml)
751                                 iter = port_cfg[port][place].ml;
752                         else
753                                 iter = port_cfg[0][place].ml;
754                         if (iter) {
755                                 for (; iter; iter = iter->next)
756                                         sprintf(tempbuf, "%s%s, ", tempbuf, iter->msn);
757                                 tempbuf[strlen(tempbuf)-2] = 0;
758                         }
759                         snprintf(buf, bufsize, " -> msns: %s", *tempbuf ? tempbuf : "none");
760                         break;
761                 case MISDN_CTYPE_STR:
762                         if ( port_cfg[port][place].str) {
763                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[port][place].str);
764                         } else if (port_cfg[0][place].str) {
765                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[0][place].str);
766                         } else {
767                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
768                         }
769                         break;
770                 }
771         } else if (elem > MISDN_GEN_FIRST && elem < MISDN_GEN_LAST) {
772                 switch (gen_spec[place].type) {
773                 case MISDN_CTYPE_INT:
774                 case MISDN_CTYPE_BOOLINT:
775                         if (general_cfg[place].num)
776                                 snprintf(buf, bufsize, " -> %s: %d", gen_spec[place].name, *general_cfg[place].num);
777                         else
778                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
779                         break;
780                 case MISDN_CTYPE_BOOL:
781                         if (general_cfg[place].num)
782                                 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, *general_cfg[place].num ? "yes" : "no");
783                         else
784                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
785                         break;
786                 case MISDN_CTYPE_STR:
787                         if ( general_cfg[place].str) {
788                                 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, general_cfg[place].str);
789                         } else {
790                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
791                         }
792                         break;
793                 default:
794                         snprintf(buf, bufsize, " -> type of %s not handled yet", gen_spec[place].name);
795                         break;
796                 }
797         } else {
798                 *buf = 0;
799                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Invalid config element (%d) requested.\n", elem);
800         }
801         misdn_cfg_unlock();
802 }
803
804 int misdn_cfg_get_next_port (int port)
805 {
806         int p = -1;
807         int gn = map[MISDN_CFG_GROUPNAME];
808         
809         misdn_cfg_lock();
810         for (port++; port <= max_ports; port++) {
811                 if (port_cfg[port][gn].str) {
812                         p = port;
813                         break;
814                 }
815         }
816         misdn_cfg_unlock();
817
818         return p;
819 }
820
821 int misdn_cfg_get_next_port_spin (int port)
822 {
823         int p = misdn_cfg_get_next_port(port);
824         return (p > 0) ? p : misdn_cfg_get_next_port(0);
825 }
826
827 static int _parse (union misdn_cfg_pt *dest, char *value, enum misdn_cfg_type type, int boolint_def)
828 {
829         int re = 0;
830         int len, tmp;
831         char *valtmp;
832
833         switch (type) {
834         case MISDN_CTYPE_STR:
835                 if ((len = strlen(value))) {
836                         dest->str = (char *)malloc((len + 1) * sizeof(char));
837                         strncpy(dest->str, value, len);
838                         dest->str[len] = 0;
839                 } else {
840                         dest->str = (char *)malloc( sizeof(char));
841                         dest->str[0] = 0;
842                 }
843                 break;
844         case MISDN_CTYPE_INT:
845         {
846                 char *pat;
847                 if (strchr(value,'x')) 
848                         pat="%x";
849                 else
850                         pat="%d";
851                 if (sscanf(value, pat, &tmp)) {
852                         dest->num = (int *)malloc(sizeof(int));
853                         memcpy(dest->num, &tmp, sizeof(int));
854                 } else
855                         re = -1;
856         }
857                 break;
858         case MISDN_CTYPE_BOOL:
859                 dest->num = (int *)malloc(sizeof(int));
860                 *(dest->num) = (ast_true(value) ? 1 : 0);
861                 break;
862         case MISDN_CTYPE_BOOLINT:
863                 dest->num = (int *)malloc(sizeof(int));
864                 if (sscanf(value, "%d", &tmp)) {
865                         memcpy(dest->num, &tmp, sizeof(int));
866                 } else {
867                         *(dest->num) = (ast_true(value) ? boolint_def : 0);
868                 }
869                 break;
870         case MISDN_CTYPE_MSNLIST:
871                 for (valtmp = strsep(&value, ","); valtmp; valtmp = strsep(&value, ",")) {
872                         if ((len = strlen(valtmp))) {
873                                 struct msn_list *ml = (struct msn_list *)malloc(sizeof(struct msn_list));
874                                 ml->msn = (char *)calloc(len+1, sizeof(char));
875                                 strncpy(ml->msn, valtmp, len);
876                                 ml->next = dest->ml;
877                                 dest->ml = ml;
878                         }
879                 }
880                 break;
881         case MISDN_CTYPE_ASTGROUP:
882                 dest->grp = (ast_group_t *)malloc(sizeof(ast_group_t));
883                 *(dest->grp) = ast_get_group(value);
884                 break;
885         }
886
887         return re;
888 }
889
890 static void _build_general_config (struct ast_variable *v)
891 {
892         int pos;
893
894         for (; v; v = v->next) {
895                 if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) || 
896                         (_parse(&general_cfg[pos], v->value, gen_spec[pos].type, gen_spec[pos].boolint_def) < 0))
897                         CLI_ERROR(v->name, v->value, "general");
898         }
899 }
900
901 static void _build_port_config (struct ast_variable *v, char *cat)
902 {
903         int pos, i;
904         union misdn_cfg_pt cfg_tmp[NUM_PORT_ELEMENTS];
905         int cfg_for_ports[max_ports + 1];
906
907         if (!v || !cat)
908                 return;
909
910         memset(cfg_tmp, 0, sizeof(cfg_tmp));
911         memset(cfg_for_ports, 0, sizeof(cfg_for_ports));
912
913         if (!strcasecmp(cat, "default")) {
914                 cfg_for_ports[0] = 1;
915         }
916
917         if (((pos = get_cfg_position("name", PORT_CFG)) < 0) || 
918                 (_parse(&cfg_tmp[pos], cat, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) {
919                 CLI_ERROR(v->name, v->value, cat);
920                 return;
921         }
922
923         for (; v; v = v->next) {
924                 if (!strcasecmp(v->name, "ports")) {
925                         char *token;
926                         char ptpbuf[BUFFERSIZE] = "";
927                         int start, end;
928                         for (token = strsep(&v->value, ","); token; token = strsep(&v->value, ","), *ptpbuf = 0) { 
929                                 if (!*token)
930                                         continue;
931                                 if (sscanf(token, "%d-%d%s", &start, &end, ptpbuf) >= 2) {
932                                         for (; start <= end; start++) {
933                                                 if (start <= max_ports && start > 0) {
934                                                         cfg_for_ports[start] = 1;
935                                                         ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
936                                                 } else
937                                                         CLI_ERROR(v->name, v->value, cat);
938                                         }
939                                 } else {
940                                         if (sscanf(token, "%d%s", &start, ptpbuf)) {
941                                                 if (start <= max_ports && start > 0) {
942                                                         cfg_for_ports[start] = 1;
943                                                         ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
944                                                 } else
945                                                         CLI_ERROR(v->name, v->value, cat);
946                                         } else
947                                                 CLI_ERROR(v->name, v->value, cat);
948                                 }
949                         }
950                 } else {
951                         if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) || 
952                                 (_parse(&cfg_tmp[pos], v->value, port_spec[pos].type, port_spec[pos].boolint_def) < 0))
953                                 CLI_ERROR(v->name, v->value, cat);
954                 }
955         }
956
957         for (i = 0; i < (max_ports + 1); ++i) {
958                 if (cfg_for_ports[i]) {
959                         memcpy(port_cfg[i], cfg_tmp, sizeof(cfg_tmp));
960                 }
961         }
962 }
963
964 void misdn_cfg_update_ptp (void)
965 {
966         char misdn_init[BUFFERSIZE];
967         char line[BUFFERSIZE];
968         FILE *fp;
969         char *tok, *p, *end;
970         int port;
971
972         misdn_cfg_get(0, MISDN_GEN_MISDN_INIT, &misdn_init, sizeof(misdn_init));
973
974         if (misdn_init) {
975                 fp = fopen(misdn_init, "r");
976                 if (fp) {
977                         while(fgets(line, sizeof(line), fp)) {
978                                 if (!strncmp(line, "nt_ptp", 6)) {
979                                         for (tok = strtok_r(line,",=", &p);
980                                                  tok;
981                                                  tok = strtok_r(NULL,",=", &p)) {
982                                                 port = strtol(tok, &end, 10);
983                                                 if (end != tok && misdn_cfg_is_port_valid(port)) {
984                                                         misdn_cfg_lock();
985                                                         ptp[port] = 1;
986                                                         misdn_cfg_unlock();
987                                                 }
988                                         }
989                                 }
990                         }
991                         fclose(fp);
992                 } else {
993                         ast_log(LOG_WARNING,"Couldn't open %s: %s\n", misdn_init, strerror(errno));
994                 }
995         }
996 }
997
998 static void _fill_defaults (void)
999 {
1000         int i;
1001
1002         for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
1003                 if (!port_cfg[0][i].any && strcasecmp(port_spec[i].def, NO_DEFAULT))
1004                         _parse(&(port_cfg[0][i]), (char *)port_spec[i].def, port_spec[i].type, port_spec[i].boolint_def);
1005         }
1006         for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
1007                 if (!general_cfg[i].any && strcasecmp(gen_spec[i].def, NO_DEFAULT))
1008                         _parse(&(general_cfg[i]), (char *)gen_spec[i].def, gen_spec[i].type, gen_spec[i].boolint_def);
1009         }
1010 }
1011
1012 void misdn_cfg_reload (void)
1013 {
1014         misdn_cfg_init (0);
1015 }
1016
1017 void misdn_cfg_destroy (void)
1018 {
1019         misdn_cfg_lock();
1020
1021         _free_port_cfg();
1022         _free_general_cfg();
1023
1024         free(port_cfg);
1025         free(general_cfg);
1026         free(ptp);
1027         free(map);
1028
1029         misdn_cfg_unlock();
1030         ast_mutex_destroy(&config_mutex);
1031 }
1032
1033 int misdn_cfg_init (int this_max_ports)
1034 {
1035         char config[] = "misdn.conf";
1036         char *cat, *p;
1037         int i;
1038         struct ast_config *cfg;
1039         struct ast_variable *v;
1040
1041         if (!(cfg = AST_LOAD_CFG(config))) {
1042                 ast_log(LOG_WARNING, "missing file: misdn.conf\n");
1043                 return -1;
1044         }
1045
1046         ast_mutex_init(&config_mutex);
1047
1048         misdn_cfg_lock();
1049
1050         if (this_max_ports) {
1051                 /* this is the first run */
1052                 max_ports = this_max_ports;
1053                 map = (int *)calloc(MISDN_GEN_LAST + 1, sizeof(int));
1054                 if (_enum_array_map())
1055                         return -1;
1056                 p = (char *)calloc(1, (max_ports + 1) * sizeof(union misdn_cfg_pt *)
1057                                                    + (max_ports + 1) * NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt));
1058                 port_cfg = (union misdn_cfg_pt **)p;
1059                 p += (max_ports + 1) * sizeof(union misdn_cfg_pt *);
1060                 for (i = 0; i <= max_ports; ++i) {
1061                         port_cfg[i] = (union misdn_cfg_pt *)p;
1062                         p += NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt);
1063                 }
1064                 general_cfg = (union misdn_cfg_pt *)calloc(1, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1065                 ptp = (int *)calloc(max_ports + 1, sizeof(int));
1066         }
1067         else {
1068                 /* misdn reload */
1069                 _free_port_cfg();
1070                 _free_general_cfg();
1071                 memset(port_cfg[0], 0, NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt) * (max_ports + 1));
1072                 memset(general_cfg, 0, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1073                 memset(ptp, 0, sizeof(int) * (max_ports + 1));
1074         }
1075
1076         cat = ast_category_browse(cfg, NULL);
1077
1078         while(cat) {
1079                 v = ast_variable_browse(cfg, cat);
1080                 if (!strcasecmp(cat, "general")) {
1081                         _build_general_config(v);
1082                 } else {
1083                         _build_port_config(v, cat);
1084                 }
1085                 cat = ast_category_browse(cfg, cat);
1086         }
1087
1088         _fill_defaults();
1089
1090         misdn_cfg_unlock();
1091         AST_DESTROY_CFG(cfg);
1092
1093         return 0;
1094 }
1095
1096