9f6a3b767e14a125e0d71e275f55026bf7732c0c
[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 = NULL;
662
663         misdn_cfg_lock();
664         for (i = 1; i <= max_ports; i++) {
665                 if (port_cfg[i] && port_cfg[i][map[MISDN_CFG_GROUPNAME]].str) {
666                         if (!strcasecmp(port_cfg[i][map[MISDN_CFG_GROUPNAME]].str, group))
667                                 method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ? 
668                                                   port_cfg[i][map[MISDN_CFG_METHOD]].str : port_cfg[0][map[MISDN_CFG_METHOD]].str);
669                 }
670         }
671         if (method) {
672                 switch (meth) {
673                 case METHOD_STANDARD:           re = !strcasecmp(method, "standard");
674                                                                         break;
675                 case METHOD_ROUND_ROBIN:        re = !strcasecmp(method, "round_robin");
676                                                                         break;
677                 }
678         }
679         misdn_cfg_unlock();
680
681         return re;
682 }
683
684 void misdn_cfg_get_ports_string (char *ports)
685 {
686         char tmp[16];
687         int l, i;
688         int gn = map[MISDN_CFG_GROUPNAME];
689
690         *ports = 0;
691
692         misdn_cfg_lock();
693         for (i = 1; i <= max_ports; i++) {
694                 if (port_cfg[i][gn].str) {
695                         if (ptp[i])
696                                 sprintf(tmp, "%dptp,", i);
697                         else
698                                 sprintf(tmp, "%d,", i);
699                         strcat(ports, tmp);
700                 }
701         }
702         misdn_cfg_unlock();
703
704         if ((l = strlen(ports)))
705                 ports[l-1] = 0;
706 }
707
708 void misdn_cfg_get_config_string (int port, enum misdn_cfg_elements elem, char* buf, int bufsize)
709 {
710         int place;
711         char tempbuf[BUFFERSIZE] = "";
712         struct msn_list *iter;
713
714         if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
715                 *buf = 0;
716                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Port number %d is not valid.\n", port);
717                 return;
718         }
719
720         place = map[elem];
721
722         misdn_cfg_lock();
723         if (elem == MISDN_CFG_PTP) {
724                 snprintf(buf, bufsize, " -> ptp: %s", ptp[port] ? "yes" : "no");
725         }
726         else if (elem > MISDN_CFG_FIRST && elem < MISDN_CFG_LAST) {
727                 switch (port_spec[place].type) {
728                 case MISDN_CTYPE_INT:
729                 case MISDN_CTYPE_BOOLINT:
730                         if (port_cfg[port][place].num)
731                                 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[port][place].num);
732                         else if (port_cfg[0][place].num)
733                                 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[0][place].num);
734                         else
735                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
736                         break;
737                 case MISDN_CTYPE_BOOL:
738                         if (port_cfg[port][place].num)
739                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[port][place].num ? "yes" : "no");
740                         else if (port_cfg[0][place].num)
741                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[0][place].num ? "yes" : "no");
742                         else
743                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
744                         break;
745                 case MISDN_CTYPE_ASTGROUP:
746                         if (port_cfg[port][place].grp)
747                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, 
748                                                  ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[port][place].grp));
749                         else if (port_cfg[0][place].grp)
750                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, 
751                                                  ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[0][place].grp));
752                         else
753                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
754                         break;
755                 case MISDN_CTYPE_MSNLIST:
756                         if (port_cfg[port][place].ml)
757                                 iter = port_cfg[port][place].ml;
758                         else
759                                 iter = port_cfg[0][place].ml;
760                         if (iter) {
761                                 for (; iter; iter = iter->next)
762                                         sprintf(tempbuf, "%s%s, ", tempbuf, iter->msn);
763                                 tempbuf[strlen(tempbuf)-2] = 0;
764                         }
765                         snprintf(buf, bufsize, " -> msns: %s", *tempbuf ? tempbuf : "none");
766                         break;
767                 case MISDN_CTYPE_STR:
768                         if ( port_cfg[port][place].str) {
769                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[port][place].str);
770                         } else if (port_cfg[0][place].str) {
771                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[0][place].str);
772                         } else {
773                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
774                         }
775                         break;
776                 }
777         } else if (elem > MISDN_GEN_FIRST && elem < MISDN_GEN_LAST) {
778                 switch (gen_spec[place].type) {
779                 case MISDN_CTYPE_INT:
780                 case MISDN_CTYPE_BOOLINT:
781                         if (general_cfg[place].num)
782                                 snprintf(buf, bufsize, " -> %s: %d", gen_spec[place].name, *general_cfg[place].num);
783                         else
784                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
785                         break;
786                 case MISDN_CTYPE_BOOL:
787                         if (general_cfg[place].num)
788                                 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, *general_cfg[place].num ? "yes" : "no");
789                         else
790                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
791                         break;
792                 case MISDN_CTYPE_STR:
793                         if ( general_cfg[place].str) {
794                                 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, general_cfg[place].str);
795                         } else {
796                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
797                         }
798                         break;
799                 default:
800                         snprintf(buf, bufsize, " -> type of %s not handled yet", gen_spec[place].name);
801                         break;
802                 }
803         } else {
804                 *buf = 0;
805                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Invalid config element (%d) requested.\n", elem);
806         }
807         misdn_cfg_unlock();
808 }
809
810 int misdn_cfg_get_next_port (int port)
811 {
812         int p = -1;
813         int gn = map[MISDN_CFG_GROUPNAME];
814         
815         misdn_cfg_lock();
816         for (port++; port <= max_ports; port++) {
817                 if (port_cfg[port][gn].str) {
818                         p = port;
819                         break;
820                 }
821         }
822         misdn_cfg_unlock();
823
824         return p;
825 }
826
827 int misdn_cfg_get_next_port_spin (int port)
828 {
829         int p = misdn_cfg_get_next_port(port);
830         return (p > 0) ? p : misdn_cfg_get_next_port(0);
831 }
832
833 static int _parse (union misdn_cfg_pt *dest, char *value, enum misdn_cfg_type type, int boolint_def)
834 {
835         int re = 0;
836         int len, tmp;
837         char *valtmp;
838
839         switch (type) {
840         case MISDN_CTYPE_STR:
841                 if ((len = strlen(value))) {
842                         dest->str = (char *)malloc((len + 1) * sizeof(char));
843                         strncpy(dest->str, value, len);
844                         dest->str[len] = 0;
845                 } else {
846                         dest->str = (char *)malloc( sizeof(char));
847                         dest->str[0] = 0;
848                 }
849                 break;
850         case MISDN_CTYPE_INT:
851         {
852                 char *pat;
853                 if (strchr(value,'x')) 
854                         pat="%x";
855                 else
856                         pat="%d";
857                 if (sscanf(value, pat, &tmp)) {
858                         dest->num = (int *)malloc(sizeof(int));
859                         memcpy(dest->num, &tmp, sizeof(int));
860                 } else
861                         re = -1;
862         }
863                 break;
864         case MISDN_CTYPE_BOOL:
865                 dest->num = (int *)malloc(sizeof(int));
866                 *(dest->num) = (ast_true(value) ? 1 : 0);
867                 break;
868         case MISDN_CTYPE_BOOLINT:
869                 dest->num = (int *)malloc(sizeof(int));
870                 if (sscanf(value, "%d", &tmp)) {
871                         memcpy(dest->num, &tmp, sizeof(int));
872                 } else {
873                         *(dest->num) = (ast_true(value) ? boolint_def : 0);
874                 }
875                 break;
876         case MISDN_CTYPE_MSNLIST:
877                 for (valtmp = strsep(&value, ","); valtmp; valtmp = strsep(&value, ",")) {
878                         if ((len = strlen(valtmp))) {
879                                 struct msn_list *ml = (struct msn_list *)malloc(sizeof(struct msn_list));
880                                 ml->msn = (char *)calloc(len+1, sizeof(char));
881                                 strncpy(ml->msn, valtmp, len);
882                                 ml->next = dest->ml;
883                                 dest->ml = ml;
884                         }
885                 }
886                 break;
887         case MISDN_CTYPE_ASTGROUP:
888                 dest->grp = (ast_group_t *)malloc(sizeof(ast_group_t));
889                 *(dest->grp) = ast_get_group(value);
890                 break;
891         }
892
893         return re;
894 }
895
896 static void _build_general_config (struct ast_variable *v)
897 {
898         int pos;
899
900         for (; v; v = v->next) {
901                 if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) || 
902                         (_parse(&general_cfg[pos], v->value, gen_spec[pos].type, gen_spec[pos].boolint_def) < 0))
903                         CLI_ERROR(v->name, v->value, "general");
904         }
905 }
906
907 static void _build_port_config (struct ast_variable *v, char *cat)
908 {
909         int pos, i;
910         union misdn_cfg_pt cfg_tmp[NUM_PORT_ELEMENTS];
911         int cfg_for_ports[max_ports + 1];
912
913         if (!v || !cat)
914                 return;
915
916         memset(cfg_tmp, 0, sizeof(cfg_tmp));
917         memset(cfg_for_ports, 0, sizeof(cfg_for_ports));
918
919         if (!strcasecmp(cat, "default")) {
920                 cfg_for_ports[0] = 1;
921         }
922
923         if (((pos = get_cfg_position("name", PORT_CFG)) < 0) || 
924                 (_parse(&cfg_tmp[pos], cat, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) {
925                 CLI_ERROR(v->name, v->value, cat);
926                 return;
927         }
928
929         for (; v; v = v->next) {
930                 if (!strcasecmp(v->name, "ports")) {
931                         char *token;
932                         char ptpbuf[BUFFERSIZE] = "";
933                         int start, end;
934                         for (token = strsep(&v->value, ","); token; token = strsep(&v->value, ","), *ptpbuf = 0) { 
935                                 if (!*token)
936                                         continue;
937                                 if (sscanf(token, "%d-%d%s", &start, &end, ptpbuf) >= 2) {
938                                         for (; start <= end; start++) {
939                                                 if (start <= max_ports && start > 0) {
940                                                         cfg_for_ports[start] = 1;
941                                                         ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
942                                                 } else
943                                                         CLI_ERROR(v->name, v->value, cat);
944                                         }
945                                 } else {
946                                         if (sscanf(token, "%d%s", &start, ptpbuf)) {
947                                                 if (start <= max_ports && start > 0) {
948                                                         cfg_for_ports[start] = 1;
949                                                         ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
950                                                 } else
951                                                         CLI_ERROR(v->name, v->value, cat);
952                                         } else
953                                                 CLI_ERROR(v->name, v->value, cat);
954                                 }
955                         }
956                 } else {
957                         if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) || 
958                                 (_parse(&cfg_tmp[pos], v->value, port_spec[pos].type, port_spec[pos].boolint_def) < 0))
959                                 CLI_ERROR(v->name, v->value, cat);
960                 }
961         }
962
963         for (i = 0; i < (max_ports + 1); ++i) {
964                 if (cfg_for_ports[i]) {
965                         memcpy(port_cfg[i], cfg_tmp, sizeof(cfg_tmp));
966                 }
967         }
968 }
969
970 void misdn_cfg_update_ptp (void)
971 {
972 #ifndef MISDN_1_2
973         char misdn_init[BUFFERSIZE];
974         char line[BUFFERSIZE];
975         FILE *fp;
976         char *tok, *p, *end;
977         int port;
978
979         misdn_cfg_get(0, MISDN_GEN_MISDN_INIT, &misdn_init, sizeof(misdn_init));
980
981         if (misdn_init) {
982                 fp = fopen(misdn_init, "r");
983                 if (fp) {
984                         while(fgets(line, sizeof(line), fp)) {
985                                 if (!strncmp(line, "nt_ptp", 6)) {
986                                         for (tok = strtok_r(line,",=", &p);
987                                                  tok;
988                                                  tok = strtok_r(NULL,",=", &p)) {
989                                                 port = strtol(tok, &end, 10);
990                                                 if (end != tok && misdn_cfg_is_port_valid(port)) {
991                                                         misdn_cfg_lock();
992                                                         ptp[port] = 1;
993                                                         misdn_cfg_unlock();
994                                                 }
995                                         }
996                                 }
997                         }
998                         fclose(fp);
999                 } else {
1000                         ast_log(LOG_WARNING,"Couldn't open %s: %s\n", misdn_init, strerror(errno));
1001                 }
1002         }
1003 #else
1004         int i;
1005         int proto;
1006         char filename[128];
1007         FILE *fp;
1008
1009         for (i = 1; i <= max_ports; ++i) {
1010                 snprintf(filename, sizeof(filename), "/sys/class/mISDN-stacks/st-%08x/protocol", i << 8);
1011                 fp = fopen(filename, "r");
1012                 if (!fp) {
1013                         ast_log(LOG_WARNING, "Could not open %s: %s\n", filename, strerror(errno));
1014                         continue;
1015                 }
1016                 if (fscanf(fp, "0x%08x", &proto) != 1)
1017                         ast_log(LOG_WARNING, "Could not parse contents of %s!\n", filename);
1018                 else
1019                         ptp[i] = proto & 1<<5 ? 1 : 0;
1020                 fclose(fp);
1021         }
1022 #endif
1023 }
1024
1025 static void _fill_defaults (void)
1026 {
1027         int i;
1028
1029         for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
1030                 if (!port_cfg[0][i].any && strcasecmp(port_spec[i].def, NO_DEFAULT))
1031                         _parse(&(port_cfg[0][i]), (char *)port_spec[i].def, port_spec[i].type, port_spec[i].boolint_def);
1032         }
1033         for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
1034                 if (!general_cfg[i].any && strcasecmp(gen_spec[i].def, NO_DEFAULT))
1035                         _parse(&(general_cfg[i]), (char *)gen_spec[i].def, gen_spec[i].type, gen_spec[i].boolint_def);
1036         }
1037 }
1038
1039 void misdn_cfg_reload (void)
1040 {
1041         misdn_cfg_init (0);
1042 }
1043
1044 void misdn_cfg_destroy (void)
1045 {
1046         misdn_cfg_lock();
1047
1048         _free_port_cfg();
1049         _free_general_cfg();
1050
1051         free(port_cfg);
1052         free(general_cfg);
1053         free(ptp);
1054         free(map);
1055
1056         misdn_cfg_unlock();
1057         ast_mutex_destroy(&config_mutex);
1058 }
1059
1060 int misdn_cfg_init (int this_max_ports)
1061 {
1062         char config[] = "misdn.conf";
1063         char *cat, *p;
1064         int i;
1065         struct ast_config *cfg;
1066         struct ast_variable *v;
1067
1068         if (!(cfg = AST_LOAD_CFG(config))) {
1069                 ast_log(LOG_WARNING, "missing file: misdn.conf\n");
1070                 return -1;
1071         }
1072
1073         ast_mutex_init(&config_mutex);
1074
1075         misdn_cfg_lock();
1076
1077         if (this_max_ports) {
1078                 /* this is the first run */
1079                 max_ports = this_max_ports;
1080                 map = (int *)calloc(MISDN_GEN_LAST + 1, sizeof(int));
1081                 if (_enum_array_map())
1082                         return -1;
1083                 p = (char *)calloc(1, (max_ports + 1) * sizeof(union misdn_cfg_pt *)
1084                                                    + (max_ports + 1) * NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt));
1085                 port_cfg = (union misdn_cfg_pt **)p;
1086                 p += (max_ports + 1) * sizeof(union misdn_cfg_pt *);
1087                 for (i = 0; i <= max_ports; ++i) {
1088                         port_cfg[i] = (union misdn_cfg_pt *)p;
1089                         p += NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt);
1090                 }
1091                 general_cfg = (union misdn_cfg_pt *)calloc(1, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1092                 ptp = (int *)calloc(max_ports + 1, sizeof(int));
1093         }
1094         else {
1095                 /* misdn reload */
1096                 _free_port_cfg();
1097                 _free_general_cfg();
1098                 memset(port_cfg[0], 0, NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt) * (max_ports + 1));
1099                 memset(general_cfg, 0, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1100                 memset(ptp, 0, sizeof(int) * (max_ports + 1));
1101         }
1102
1103         cat = ast_category_browse(cfg, NULL);
1104
1105         while(cat) {
1106                 v = ast_variable_browse(cfg, cat);
1107                 if (!strcasecmp(cat, "general")) {
1108                         _build_general_config(v);
1109                 } else {
1110                         _build_port_config(v, cat);
1111                 }
1112                 cat = ast_category_browse(cfg, cat);
1113         }
1114
1115         _fill_defaults();
1116
1117         misdn_cfg_unlock();
1118         AST_DESTROY_CFG(cfg);
1119
1120         return 0;
1121 }
1122
1123