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