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