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