channels/chan_misdn.c
[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 "chan_misdn_config.h"
34
35 #include "asterisk/config.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/lock.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/strings.h"
40 #include "asterisk/utils.h"
41
42 #define NO_DEFAULT "<>"
43 #define NONE 0
44
45 #define GEN_CFG 1
46 #define PORT_CFG 2
47 #define NUM_GEN_ELEMENTS (sizeof(gen_spec) / sizeof(struct misdn_cfg_spec))
48 #define NUM_PORT_ELEMENTS (sizeof(port_spec) / sizeof(struct misdn_cfg_spec))
49
50 /*! Global jitterbuffer configuration - by default, jb is disabled */
51 static struct ast_jb_conf default_jbconf =
52 {
53         .flags = 0,
54         .max_size = -1,
55         .resync_threshold = -1,
56         .impl = "",
57 };
58
59 static struct ast_jb_conf global_jbconf;
60
61 enum misdn_cfg_type {
62         MISDN_CTYPE_STR,
63         MISDN_CTYPE_INT,
64         MISDN_CTYPE_BOOL,
65         MISDN_CTYPE_BOOLINT,
66         MISDN_CTYPE_MSNLIST,
67         MISDN_CTYPE_ASTGROUP
68 };
69
70 struct msn_list {
71         char *msn;
72         struct msn_list *next;
73 };
74
75 union misdn_cfg_pt {
76         char *str;
77         int *num;
78         struct msn_list *ml;
79         ast_group_t *grp;
80         void *any;
81 };
82
83 struct misdn_cfg_spec {
84         char name[BUFFERSIZE];
85         enum misdn_cfg_elements elem;
86         enum misdn_cfg_type type;
87         char def[BUFFERSIZE];
88         int boolint_def;
89         char desc[BUFFERSIZE];
90 };
91
92
93 static const char ports_description[] =
94         "Define your ports, e.g. 1,2 (depends on mISDN-driver loading order).";
95
96 static const struct misdn_cfg_spec port_spec[] = {
97         { "name", MISDN_CFG_GROUPNAME, MISDN_CTYPE_STR, "default", NONE,
98                 "Name of the portgroup." },
99         { "allowed_bearers", MISDN_CFG_ALLOWED_BEARERS, MISDN_CTYPE_STR, "all", NONE,
100                 "Here you can list which bearer capabilities should be allowed:\n"
101                 "\t  all                  - allow any bearer capability\n"
102                 "\t  speech               - allow speech\n"
103                 "\t  3_1khz               - allow 3.1KHz audio\n"
104                 "\t  digital_unrestricted - allow unrestricted digital\n"
105                 "\t  digital_restricted   - allow restricted digital\n"
106                 "\t  video                - allow video" },
107         { "rxgain", MISDN_CFG_RXGAIN, MISDN_CTYPE_INT, "0", NONE,
108                 "Set this between -8 and 8 to change the RX Gain." },
109         { "txgain", MISDN_CFG_TXGAIN, MISDN_CTYPE_INT, "0", NONE,
110                 "Set this between -8 and 8 to change the TX Gain." },
111         { "te_choose_channel", MISDN_CFG_TE_CHOOSE_CHANNEL, MISDN_CTYPE_BOOL, "no", NONE,
112                 "Some telcos especially in NL seem to need this set to yes,\n"
113                 "\talso in Switzerland this seems to be important." },
114         { "far_alerting", MISDN_CFG_FAR_ALERTING, MISDN_CTYPE_BOOL, "no", NONE,
115                 "If we should generate ringing for chan_sip and others." },
116         { "pmp_l1_check", MISDN_CFG_PMP_L1_CHECK, MISDN_CTYPE_BOOL, "no", NONE,
117                 "This option defines, if chan_misdn should check the L1 on a PMP\n"
118                 "\tbefore making a group call on it. The L1 may go down for PMP Ports\n"
119                 "\tso we might need this.\n"
120                 "\tBut be aware! a broken or plugged off cable might be used for a group call\n"
121                 "\tas well, since chan_misdn has no chance to distinguish if the L1 is down\n"
122                 "\tbecause of a lost Link or because the Provider shut it down..." },
123         { "block_on_alarm", MISDN_CFG_ALARM_BLOCK, MISDN_CTYPE_BOOL, "no", NONE ,
124           "Block this port if we have an alarm on it." },
125         { "hdlc", MISDN_CFG_HDLC, MISDN_CTYPE_BOOL, "no", NONE,
126                 "Set this to yes, if you want to bridge a mISDN data channel to\n"
127                 "\tanother channel type or to an application." },
128         { "context", MISDN_CFG_CONTEXT, MISDN_CTYPE_STR, "default", NONE,
129                 "Context to use for incoming calls." },
130         { "language", MISDN_CFG_LANGUAGE, MISDN_CTYPE_STR, "en", NONE,
131                 "Language." },
132         { "musicclass", MISDN_CFG_MUSICCLASS, MISDN_CTYPE_STR, "default", NONE,
133                 "Sets the musiconhold class." },
134         { "callerid", MISDN_CFG_CALLERID, MISDN_CTYPE_STR, "", NONE,
135                 "Sets the caller ID." },
136         { "method", MISDN_CFG_METHOD, MISDN_CTYPE_STR, "standard", NONE,
137                 "Sets the method to use for channel selection:\n"
138                 "\t  standard    - always choose the first free channel with the lowest number\n"
139                 "\t  round_robin - use the round robin algorithm to select a channel. use this\n"
140                 "\t                if you want to balance your load." },
141         { "dialplan", MISDN_CFG_DIALPLAN, MISDN_CTYPE_INT, "0", NONE,
142                 "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
143                 "\n"
144                 "\tThere are different types of the dialplan:\n"
145                 "\n"
146                 "\tdialplan -> outgoing Number\n"
147                 "\tlocaldialplan -> callerid\n"
148                 "\tcpndialplan -> connected party number\n"
149                 "\n"
150                 "\tdialplan options:\n"
151                 "\n"
152                 "\t0 - unknown\n"
153                 "\t1 - International\n"
154                 "\t2 - National\n"
155                 "\t4 - Subscriber\n"
156                 "\n"
157                 "\tThis setting is used for outgoing calls." },
158         { "localdialplan", MISDN_CFG_LOCALDIALPLAN, MISDN_CTYPE_INT, "0", NONE,
159                 "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
160                 "\n"
161                 "\tThere are different types of the dialplan:\n"
162                 "\n"
163                 "\tdialplan -> outgoing Number\n"
164                 "\tlocaldialplan -> callerid\n"
165                 "\tcpndialplan -> connected party number\n"
166                 "\n"
167                 "\tdialplan options:\n"
168                 "\n"
169                 "\t0 - unknown\n"
170                 "\t1 - International\n"
171                 "\t2 - National\n"
172                 "\t4 - Subscriber\n"
173                 "\n"
174                 "\tThis setting is used for outgoing calls." },
175         { "cpndialplan", MISDN_CFG_CPNDIALPLAN, MISDN_CTYPE_INT, "0", NONE,
176                 "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
177                 "\n"
178                 "\tThere are different types of the dialplan:\n"
179                 "\n"
180                 "\tdialplan -> outgoing Number\n"
181                 "\tlocaldialplan -> callerid\n"
182                 "\tcpndialplan -> connected party number\n"
183                 "\n"
184                 "\tdialplan options:\n"
185                 "\n"
186                 "\t0 - unknown\n"
187                 "\t1 - International\n"
188                 "\t2 - National\n"
189                 "\t4 - Subscriber\n"
190                 "\n"
191                 "\tThis setting is used for outgoing calls." },
192         { "nationalprefix", MISDN_CFG_NATPREFIX, MISDN_CTYPE_STR, "0", NONE,
193                 "Prefix for national, this is put before the\n"
194                 "\toad if an according dialplan is set by the other end." },
195         { "internationalprefix", MISDN_CFG_INTERNATPREFIX, MISDN_CTYPE_STR, "00", NONE,
196                 "Prefix for international, this is put before the\n"
197                 "\toad if an according dialplan is set by the other end." },
198         { "presentation", MISDN_CFG_PRES, MISDN_CTYPE_INT, "-1", NONE,
199                 "These (presentation and screen) are the exact isdn screening and presentation\n"
200                 "\tindicators.\n"
201                 "\tIf -1 is given for either value, the presentation indicators are used from\n"
202                 "\tAsterisk's CALLERPRES function.\n"
203                 "\n"
204                 "\tscreen=0, presentation=0 -> callerid presented\n"
205                 "\tscreen=1, presentation=1 -> callerid restricted (the remote end doesn't see it!)" },
206         { "screen", MISDN_CFG_SCREEN, MISDN_CTYPE_INT, "-1", NONE,
207                 "These (presentation and screen) are the exact isdn screening and presentation\n"
208                 "\tindicators.\n"
209                 "\tIf -1 is given for either value, the presentation indicators are used from\n"
210                 "\tAsterisk's CALLERPRES function.\n"
211                 "\n"
212                 "\tscreen=0, presentation=0 -> callerid presented\n"
213                 "\tscreen=1, presentation=1 -> callerid restricted (the remote end doesn't see it!)" },
214         { "always_immediate", MISDN_CFG_ALWAYS_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE,
215                 "Enable this to get into the s dialplan-extension.\n"
216                 "\tThere you can use DigitTimeout if you can't or don't want to use\n"
217                 "\tisdn overlap dial.\n"
218                 "\tNOTE: This will jump into the s extension for every exten!" },
219         { "nodialtone", MISDN_CFG_NODIALTONE, MISDN_CTYPE_BOOL, "no", NONE,
220                 "Enable this to prevent chan_misdn to generate the dialtone\n"
221                 "\tThis makes only sense together with the always_immediate=yes option\n"
222                 "\tto generate your own dialtone with Playtones or so."},
223         { "immediate", MISDN_CFG_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE,
224                 "Enable this if you want callers which called exactly the base\n"
225                 "\tnumber (so no extension is set) to jump into the s extension.\n"
226                 "\tIf the user dials something more, it jumps to the correct extension\n"
227                 "\tinstead." },
228         { "senddtmf", MISDN_CFG_SENDDTMF, MISDN_CTYPE_BOOL, "no", NONE,
229                 "Enable this if we should produce DTMF Tones ourselves." },
230         { "astdtmf", MISDN_CFG_ASTDTMF, MISDN_CTYPE_BOOL, "no", NONE,
231                 "Enable this if you want to use the Asterisk dtmf detector\n"
232                 "instead of the mISDN_dsp/hfcmulti one."
233                 },
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 echo cancellation with the given number of taps.\n"
245                 "\tBe aware: Move this setting only to outgoing portgroups!\n"
246                 "\tA value of zero turns echo cancellation 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."},
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 variable\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." },
334         { "msns", MISDN_CFG_MSNS, MISDN_CTYPE_MSNLIST, "*", NONE,
335                 "MSN's for TE ports, listen on those numbers on the above ports, and\n"
336                 "\tindicate the incoming calls to Asterisk.\n"
337                 "\tHere you can give a comma separated list, or simply an '*' for any msn." },
338 };
339
340 static const struct misdn_cfg_spec gen_spec[] = {
341         { "debug", MISDN_GEN_DEBUG, MISDN_CTYPE_INT, "0", NONE,
342                 "Sets the debugging flag:\n"
343                 "\t0 - No Debug\n"
344                 "\t1 - mISDN Messages and * - Messages, and * - State changes\n"
345                 "\t2 - Messages + Message specific Informations (e.g. bearer capability)\n"
346                 "\t3 - very Verbose, the above + lots of Driver specific infos\n"
347                 "\t4 - even more Verbose than 3" },
348 #ifndef MISDN_1_2
349         { "misdn_init", MISDN_GEN_MISDN_INIT, MISDN_CTYPE_STR, "/etc/misdn-init.conf", NONE,
350                 "Set the path to the misdn-init.conf (for nt_ptp mode checking)." },
351 #endif
352         { "tracefile", MISDN_GEN_TRACEFILE, MISDN_CTYPE_STR, "/var/log/asterisk/misdn.log", NONE,
353                 "Set the path to the massively growing trace file, if you want that." },
354         { "bridging", MISDN_GEN_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
355                 "Set this to yes if you want mISDN_dsp to bridge the calls in HW." },
356         { "stop_tone_after_first_digit", MISDN_GEN_STOP_TONE, MISDN_CTYPE_BOOL, "yes", NONE,
357                 "Stops dialtone after getting first digit on NT Port." },
358         { "append_digits2exten", MISDN_GEN_APPEND_DIGITS2EXTEN, MISDN_CTYPE_BOOL, "yes", NONE,
359                 "Whether to append overlapdialed Digits to Extension or not." },
360         { "dynamic_crypt", MISDN_GEN_DYNAMIC_CRYPT, MISDN_CTYPE_BOOL, "no", NONE,
361                 "Whether to look out for dynamic crypting attempts." },
362         { "crypt_prefix", MISDN_GEN_CRYPT_PREFIX, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
363                 "What is used for crypting Protocol." },
364         { "crypt_keys", MISDN_GEN_CRYPT_KEYS, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
365                 "Keys for cryption, you reference them in the dialplan\n"
366                 "\tLater also in dynamic encr." },
367         { "ntkeepcalls", MISDN_GEN_NTKEEPCALLS, MISDN_CTYPE_BOOL, "no", NONE, 
368                 "avoid dropping calls if the L2 goes down. some Nortel pbx\n" 
369                 "do put down the L2/L1 for some milliseconds even if there\n"
370                 "are running calls. with this option you can avoid dropping them" },
371         { "ntdebugflags", MISDN_GEN_NTDEBUGFLAGS, MISDN_CTYPE_INT, "0", NONE,
372                 "No description yet."},
373         { "ntdebugfile", MISDN_GEN_NTDEBUGFILE, MISDN_CTYPE_STR, "/var/log/misdn-nt.log", NONE,
374                 "No description yet." }
375 };
376
377
378 /* array of port configs, default is at position 0. */
379 static union misdn_cfg_pt **port_cfg;
380 /* max number of available ports, is set on init */
381 static int max_ports;
382 /* general config */
383 static union misdn_cfg_pt *general_cfg;
384 /* storing the ptp flag separated to save memory */
385 static int *ptp;
386 /* maps enum config elements to array positions */
387 static int *map;
388
389 static ast_mutex_t config_mutex; 
390
391 #define CLI_ERROR(name, value, section) ({ \
392         ast_log(LOG_WARNING, "misdn.conf: \"%s=%s\" (section: %s) invalid or out of range. " \
393                 "Please edit your misdn.conf and then do a \"misdn reload\".\n", name, value, section); \
394 })
395
396 static int _enum_array_map (void)
397 {
398         int i, j, ok;
399
400         for (i = MISDN_CFG_FIRST + 1; i < MISDN_CFG_LAST; ++i) {
401                 if (i == MISDN_CFG_PTP)
402                         continue;
403                 ok = 0;
404                 for (j = 0; j < NUM_PORT_ELEMENTS; ++j) {
405                         if (port_spec[j].elem == i) {
406                                 map[i] = j;
407                                 ok = 1;
408                                 break;
409                         }
410                 }
411                 if (!ok) {
412                         ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (port section) has no corresponding element in the config struct!\n", i);
413                         return -1;
414                 }
415         }
416         for (i = MISDN_GEN_FIRST + 1; i < MISDN_GEN_LAST; ++i) {
417                 ok = 0;
418                 for (j = 0; j < NUM_GEN_ELEMENTS; ++j) {
419                         if (gen_spec[j].elem == i) {
420                                 map[i] = j;
421                                 ok = 1;
422                                 break;
423                         }
424                 }
425                 if (!ok) {
426                         ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (general section) has no corresponding element in the config struct!\n", i);
427                         return -1;
428                 }
429         }
430         return 0;
431 }
432
433 static int get_cfg_position (const char *name, int type)
434 {
435         int i;
436
437         switch (type) {
438         case PORT_CFG:
439                 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
440                         if (!strcasecmp(name, port_spec[i].name))
441                                 return i;
442                 }
443                 break;
444         case GEN_CFG:
445                 for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
446                         if (!strcasecmp(name, gen_spec[i].name))
447                                 return i;
448                 }
449         }
450
451         return -1;
452 }
453
454 static inline void misdn_cfg_lock (void)
455 {
456         ast_mutex_lock(&config_mutex);
457 }
458
459 static inline void misdn_cfg_unlock (void)
460 {
461         ast_mutex_unlock(&config_mutex);
462 }
463
464 static void _free_msn_list (struct msn_list* iter)
465 {
466         if (iter->next)
467                 _free_msn_list(iter->next);
468         if (iter->msn)
469                 ast_free(iter->msn);
470         ast_free(iter);
471 }
472
473 static void _free_port_cfg (void)
474 {
475         int i, j;
476         int gn = map[MISDN_CFG_GROUPNAME];
477         union misdn_cfg_pt* free_list[max_ports + 2];
478         
479         memset(free_list, 0, sizeof(free_list));
480         free_list[0] = port_cfg[0];
481         for (i = 1; i <= max_ports; ++i) {
482                 if (port_cfg[i][gn].str) {
483                         /* we always have a groupname in the non-default case, so this is fine */
484                         for (j = 1; j <= max_ports; ++j) {
485                                 if (free_list[j] && free_list[j][gn].str == port_cfg[i][gn].str)
486                                         break;
487                                 else if (!free_list[j]) {
488                                         free_list[j] = port_cfg[i];
489                                         break;
490                                 }
491                         }
492                 }
493         }
494         for (j = 0; free_list[j]; ++j) {
495                 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
496                         if (free_list[j][i].any) {
497                                 if (port_spec[i].type == MISDN_CTYPE_MSNLIST)
498                                         _free_msn_list(free_list[j][i].ml);
499                                 else
500                                         ast_free(free_list[j][i].any);
501                         }
502                 }
503         }
504 }
505
506 static void _free_general_cfg (void)
507 {
508         int i;
509
510         for (i = 0; i < NUM_GEN_ELEMENTS; i++) 
511                 if (general_cfg[i].any)
512                         ast_free(general_cfg[i].any);
513 }
514
515 void misdn_cfg_get(int port, enum misdn_cfg_elements elem, void *buf, int bufsize)
516 {
517         int place;
518
519         if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
520                 memset(buf, 0, bufsize);
521                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Port number %d is not valid.\n", port);
522                 return;
523         }
524
525         misdn_cfg_lock();
526         if (elem == MISDN_CFG_PTP) {
527                 if (!memcpy(buf, &ptp[port], (bufsize > ptp[port]) ? sizeof(ptp[port]) : bufsize))
528                         memset(buf, 0, bufsize);
529         } else {
530                 if ((place = map[elem]) < 0) {
531                         memset(buf, 0, bufsize);
532                         ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Invalid element (%d) requested.\n", elem);
533                 } else {
534                         if (elem < MISDN_CFG_LAST) {
535                                 switch (port_spec[place].type) {
536                                 case MISDN_CTYPE_STR:
537                                         if (port_cfg[port][place].str) {
538                                                 ast_copy_string(buf, port_cfg[port][place].str, bufsize);
539                                         } else if (port_cfg[0][place].str) {
540                                                 ast_copy_string(buf, port_cfg[0][place].str, bufsize);
541                                         } else
542                                                 memset(buf, 0, bufsize);
543                                         break;
544                                 default:
545                                         if (port_cfg[port][place].any)
546                                                 memcpy(buf, port_cfg[port][place].any, bufsize);
547                                         else if (port_cfg[0][place].any)
548                                                 memcpy(buf, port_cfg[0][place].any, bufsize);
549                                         else
550                                                 memset(buf, 0, bufsize);
551                                 }
552                         } else {
553                                 switch (gen_spec[place].type) {
554                                 case MISDN_CTYPE_STR:
555                                         ast_copy_string(buf, S_OR(general_cfg[place].str, ""), bufsize);
556                                         break;
557                                 default:
558                                         if (general_cfg[place].any)
559                                                 memcpy(buf, general_cfg[place].any, bufsize);
560                                         else
561                                                 memset(buf, 0, bufsize);
562                                 }
563                         }
564                 }
565         }
566         misdn_cfg_unlock();
567 }
568
569 enum misdn_cfg_elements misdn_cfg_get_elem(char *name)
570 {
571         int pos;
572
573         /* here comes a hack to replace the (not existing) "name" element with the "ports" element */
574         if (!strcmp(name, "ports"))
575                 return MISDN_CFG_GROUPNAME;
576         if (!strcmp(name, "name"))
577                 return MISDN_CFG_FIRST;
578
579         pos = get_cfg_position(name, PORT_CFG);
580         if (pos >= 0)
581                 return port_spec[pos].elem;
582         
583         pos = get_cfg_position(name, GEN_CFG);
584         if (pos >= 0)
585                 return gen_spec[pos].elem;
586         
587         return MISDN_CFG_FIRST;
588 }
589
590 void misdn_cfg_get_name(enum misdn_cfg_elements elem, void *buf, int bufsize)
591 {
592         struct misdn_cfg_spec *spec = NULL;
593         int place = map[elem];
594
595         /* the ptp hack */
596         if (elem == MISDN_CFG_PTP) {
597                 memset(buf, 0, 1);
598                 return;
599         }
600         
601         /* here comes a hack to replace the (not existing) "name" element with the "ports" element */
602         if (elem == MISDN_CFG_GROUPNAME) {
603                 if (!snprintf(buf, bufsize, "ports"))
604                         memset(buf, 0, 1);
605                 return;
606         }
607
608         if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
609                 spec = (struct misdn_cfg_spec *)port_spec;
610         else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
611                 spec = (struct misdn_cfg_spec *)gen_spec;
612
613         ast_copy_string(buf, spec ? spec[place].name : "", bufsize);
614 }
615
616 void misdn_cfg_get_desc (enum misdn_cfg_elements elem, void *buf, int bufsize, void *buf_default, int bufsize_default)
617 {
618         int place = map[elem];
619         struct misdn_cfg_spec *spec = NULL;
620
621         /* here comes a hack to replace the (not existing) "name" element with the "ports" element */
622         if (elem == MISDN_CFG_GROUPNAME) {
623                 ast_copy_string(buf, ports_description, bufsize);
624                 if (buf_default && bufsize_default)
625                         memset(buf_default, 0, 1);
626                 return;
627         }
628
629         if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
630                 spec = (struct misdn_cfg_spec *)port_spec;
631         else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
632                 spec = (struct misdn_cfg_spec *)gen_spec;
633                 
634         if (!spec || !spec[place].desc)
635                 memset(buf, 0, 1);
636         else {
637                 ast_copy_string(buf, spec[place].desc, bufsize);
638                 if (buf_default && bufsize) {
639                         if (!strcmp(spec[place].def, NO_DEFAULT))
640                                 memset(buf_default, 0, 1);
641                         else
642                                 ast_copy_string(buf_default, spec[place].def, bufsize_default);
643                 }
644         }
645 }
646
647 int misdn_cfg_is_msn_valid (int port, char* msn)
648 {
649         int re = 0;
650         struct msn_list *iter;
651
652         if (!misdn_cfg_is_port_valid(port)) {
653                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_is_msn_valid! Port number %d is not valid.\n", port);
654                 return 0;
655         }
656
657         misdn_cfg_lock();
658         if (port_cfg[port][map[MISDN_CFG_MSNS]].ml)
659                 iter = port_cfg[port][map[MISDN_CFG_MSNS]].ml;
660         else
661                 iter = port_cfg[0][map[MISDN_CFG_MSNS]].ml;
662         for (; iter; iter = iter->next) 
663                 if (*(iter->msn) == '*' || ast_extension_match(iter->msn, msn)) {
664                         re = 1;
665                         break;
666                 }
667         misdn_cfg_unlock();
668
669         return re;
670 }
671
672 int misdn_cfg_is_port_valid (int port)
673 {
674         int gn = map[MISDN_CFG_GROUPNAME];
675
676         return (port >= 1 && port <= max_ports && port_cfg[port][gn].str);
677 }
678
679 int misdn_cfg_is_group_method (char *group, enum misdn_cfg_method meth)
680 {
681         int i, re = 0;
682         char *method ;
683
684         misdn_cfg_lock();
685
686         method = port_cfg[0][map[MISDN_CFG_METHOD]].str;
687
688         for (i = 1; i <= max_ports; i++) {
689                 if (port_cfg[i] && port_cfg[i][map[MISDN_CFG_GROUPNAME]].str) {
690                         if (!strcasecmp(port_cfg[i][map[MISDN_CFG_GROUPNAME]].str, group))
691                                 method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ? 
692                                                   port_cfg[i][map[MISDN_CFG_METHOD]].str : port_cfg[0][map[MISDN_CFG_METHOD]].str);
693                 }
694         }
695
696         if (method) {
697                 switch (meth) {
698                 case METHOD_STANDARD:           re = !strcasecmp(method, "standard");
699                                                                         break;
700                 case METHOD_ROUND_ROBIN:        re = !strcasecmp(method, "round_robin");
701                                                                         break;
702                 case METHOD_STANDARD_DEC:       re = !strcasecmp(method, "standard_dec");
703                                                                         break;
704                 }
705         }
706         misdn_cfg_unlock();
707
708         return re;
709 }
710
711 /*! 
712  * \brief Generate a comma separated list of all active ports
713  */
714 void misdn_cfg_get_ports_string (char *ports)
715 {
716         char tmp[16];
717         int l, i;
718         int gn = map[MISDN_CFG_GROUPNAME];
719
720         *ports = 0;
721
722         misdn_cfg_lock();
723         for (i = 1; i <= max_ports; i++) {
724                 if (port_cfg[i][gn].str) {
725                         if (ptp[i])
726                                 sprintf(tmp, "%dptp,", i);
727                         else
728                                 sprintf(tmp, "%d,", i);
729                         strcat(ports, tmp);
730                 }
731         }
732         misdn_cfg_unlock();
733
734         if ((l = strlen(ports))) {
735                 /* Strip trailing ',' */
736                 ports[l-1] = 0;
737         }
738 }
739
740 void misdn_cfg_get_config_string (int port, enum misdn_cfg_elements elem, char* buf, int bufsize)
741 {
742         int place;
743         char tempbuf[BUFFERSIZE] = "";
744         struct msn_list *iter;
745
746         if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
747                 *buf = 0;
748                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Port number %d is not valid.\n", port);
749                 return;
750         }
751
752         place = map[elem];
753
754         misdn_cfg_lock();
755         if (elem == MISDN_CFG_PTP) {
756                 snprintf(buf, bufsize, " -> ptp: %s", ptp[port] ? "yes" : "no");
757         }
758         else if (elem > MISDN_CFG_FIRST && elem < MISDN_CFG_LAST) {
759                 switch (port_spec[place].type) {
760                 case MISDN_CTYPE_INT:
761                 case MISDN_CTYPE_BOOLINT:
762                         if (port_cfg[port][place].num)
763                                 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[port][place].num);
764                         else if (port_cfg[0][place].num)
765                                 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[0][place].num);
766                         else
767                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
768                         break;
769                 case MISDN_CTYPE_BOOL:
770                         if (port_cfg[port][place].num)
771                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[port][place].num ? "yes" : "no");
772                         else if (port_cfg[0][place].num)
773                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[0][place].num ? "yes" : "no");
774                         else
775                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
776                         break;
777                 case MISDN_CTYPE_ASTGROUP:
778                         if (port_cfg[port][place].grp)
779                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, 
780                                                  ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[port][place].grp));
781                         else if (port_cfg[0][place].grp)
782                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, 
783                                                  ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[0][place].grp));
784                         else
785                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
786                         break;
787                 case MISDN_CTYPE_MSNLIST:
788                         if (port_cfg[port][place].ml)
789                                 iter = port_cfg[port][place].ml;
790                         else
791                                 iter = port_cfg[0][place].ml;
792                         if (iter) {
793                                 for (; iter; iter = iter->next)
794                                         sprintf(tempbuf, "%s%s, ", tempbuf, iter->msn);
795                                 tempbuf[strlen(tempbuf)-2] = 0;
796                         }
797                         snprintf(buf, bufsize, " -> msns: %s", *tempbuf ? tempbuf : "none");
798                         break;
799                 case MISDN_CTYPE_STR:
800                         if ( port_cfg[port][place].str) {
801                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[port][place].str);
802                         } else if (port_cfg[0][place].str) {
803                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[0][place].str);
804                         } else {
805                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
806                         }
807                         break;
808                 }
809         } else if (elem > MISDN_GEN_FIRST && elem < MISDN_GEN_LAST) {
810                 switch (gen_spec[place].type) {
811                 case MISDN_CTYPE_INT:
812                 case MISDN_CTYPE_BOOLINT:
813                         if (general_cfg[place].num)
814                                 snprintf(buf, bufsize, " -> %s: %d", gen_spec[place].name, *general_cfg[place].num);
815                         else
816                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
817                         break;
818                 case MISDN_CTYPE_BOOL:
819                         if (general_cfg[place].num)
820                                 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, *general_cfg[place].num ? "yes" : "no");
821                         else
822                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
823                         break;
824                 case MISDN_CTYPE_STR:
825                         if ( general_cfg[place].str) {
826                                 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, general_cfg[place].str);
827                         } else {
828                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
829                         }
830                         break;
831                 default:
832                         snprintf(buf, bufsize, " -> type of %s not handled yet", gen_spec[place].name);
833                         break;
834                 }
835         } else {
836                 *buf = 0;
837                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Invalid config element (%d) requested.\n", elem);
838         }
839         misdn_cfg_unlock();
840 }
841
842 int misdn_cfg_get_next_port (int port)
843 {
844         int p = -1;
845         int gn = map[MISDN_CFG_GROUPNAME];
846         
847         misdn_cfg_lock();
848         for (port++; port <= max_ports; port++) {
849                 if (port_cfg[port][gn].str) {
850                         p = port;
851                         break;
852                 }
853         }
854         misdn_cfg_unlock();
855
856         return p;
857 }
858
859 int misdn_cfg_get_next_port_spin (int port)
860 {
861         int p = misdn_cfg_get_next_port(port);
862         return (p > 0) ? p : misdn_cfg_get_next_port(0);
863 }
864
865 static int _parse (union misdn_cfg_pt *dest, const char *value, enum misdn_cfg_type type, int boolint_def)
866 {
867         int re = 0;
868         int len, tmp;
869         char *valtmp;
870         char *tmp2 = ast_strdupa(value);
871
872         switch (type) {
873         case MISDN_CTYPE_STR:
874                 if ((len = strlen(value))) {
875                         dest->str = ast_malloc((len + 1) * sizeof(char));
876                         strncpy(dest->str, value, len);
877                         dest->str[len] = 0;
878                 } else {
879                         dest->str = ast_malloc(sizeof(char));
880                         dest->str[0] = 0;
881                 }
882                 break;
883         case MISDN_CTYPE_INT:
884         {
885                 char *pat;
886                 if (strchr(value,'x')) 
887                         pat="%x";
888                 else
889                         pat="%d";
890                 if (sscanf(value, pat, &tmp)) {
891                         dest->num = ast_malloc(sizeof(int));
892                         memcpy(dest->num, &tmp, sizeof(int));
893                 } else
894                         re = -1;
895         }
896                 break;
897         case MISDN_CTYPE_BOOL:
898                 dest->num = ast_malloc(sizeof(int));
899                 *(dest->num) = (ast_true(value) ? 1 : 0);
900                 break;
901         case MISDN_CTYPE_BOOLINT:
902                 dest->num = ast_malloc(sizeof(int));
903                 if (sscanf(value, "%d", &tmp)) {
904                         memcpy(dest->num, &tmp, sizeof(int));
905                 } else {
906                         *(dest->num) = (ast_true(value) ? boolint_def : 0);
907                 }
908                 break;
909         case MISDN_CTYPE_MSNLIST:
910                 for (valtmp = strsep(&tmp2, ","); valtmp; valtmp = strsep(&tmp2, ",")) {
911                         if ((len = strlen(valtmp))) {
912                                 struct msn_list *ml = ast_malloc(sizeof(*ml));
913                                 ml->msn = ast_calloc(len+1, sizeof(char));
914                                 strncpy(ml->msn, valtmp, len);
915                                 ml->next = dest->ml;
916                                 dest->ml = ml;
917                         }
918                 }
919                 break;
920         case MISDN_CTYPE_ASTGROUP:
921                 dest->grp = ast_malloc(sizeof(ast_group_t));
922                 *(dest->grp) = ast_get_group(value);
923                 break;
924         }
925
926         return re;
927 }
928
929 static void _build_general_config (struct ast_variable *v)
930 {
931         int pos;
932
933         for (; v; v = v->next) {
934                 if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
935                         continue;
936                 if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) || 
937                         (_parse(&general_cfg[pos], v->value, gen_spec[pos].type, gen_spec[pos].boolint_def) < 0))
938                         CLI_ERROR(v->name, v->value, "general");
939         }
940 }
941
942 static void _build_port_config (struct ast_variable *v, char *cat)
943 {
944         int pos, i;
945         union misdn_cfg_pt cfg_tmp[NUM_PORT_ELEMENTS];
946         int cfg_for_ports[max_ports + 1];
947
948         if (!v || !cat)
949                 return;
950
951         memset(cfg_tmp, 0, sizeof(cfg_tmp));
952         memset(cfg_for_ports, 0, sizeof(cfg_for_ports));
953
954         if (!strcasecmp(cat, "default")) {
955                 cfg_for_ports[0] = 1;
956         }
957
958         if (((pos = get_cfg_position("name", PORT_CFG)) < 0) || 
959                 (_parse(&cfg_tmp[pos], cat, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) {
960                 CLI_ERROR(v->name, v->value, cat);
961                 return;
962         }
963
964         for (; v; v = v->next) {
965                 if (!strcasecmp(v->name, "ports")) {
966                         char *token, *tmp = ast_strdupa(v->value);
967                         char ptpbuf[BUFFERSIZE] = "";
968                         int start, end;
969                         for (token = strsep(&tmp, ","); token; token = strsep(&tmp, ","), *ptpbuf = 0) { 
970                                 if (!*token)
971                                         continue;
972                                 if (sscanf(token, "%d-%d%s", &start, &end, ptpbuf) >= 2) {
973                                         for (; start <= end; start++) {
974                                                 if (start <= max_ports && start > 0) {
975                                                         cfg_for_ports[start] = 1;
976                                                         ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
977                                                 } else
978                                                         CLI_ERROR(v->name, v->value, cat);
979                                         }
980                                 } else {
981                                         if (sscanf(token, "%d%s", &start, ptpbuf)) {
982                                                 if (start <= max_ports && start > 0) {
983                                                         cfg_for_ports[start] = 1;
984                                                         ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
985                                                 } else
986                                                         CLI_ERROR(v->name, v->value, cat);
987                                         } else
988                                                 CLI_ERROR(v->name, v->value, cat);
989                                 }
990                         }
991                 } else {
992                         if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) || 
993                                 (_parse(&cfg_tmp[pos], v->value, port_spec[pos].type, port_spec[pos].boolint_def) < 0))
994                                 CLI_ERROR(v->name, v->value, cat);
995                 }
996         }
997
998         for (i = 0; i < (max_ports + 1); ++i) {
999                 if (cfg_for_ports[i]) {
1000                         memcpy(port_cfg[i], cfg_tmp, sizeof(cfg_tmp));
1001                 }
1002         }
1003 }
1004
1005 void misdn_cfg_update_ptp (void)
1006 {
1007 #ifndef MISDN_1_2
1008         char misdn_init[BUFFERSIZE];
1009         char line[BUFFERSIZE];
1010         FILE *fp;
1011         char *tok, *p, *end;
1012         int port;
1013
1014         misdn_cfg_get(0, MISDN_GEN_MISDN_INIT, &misdn_init, sizeof(misdn_init));
1015
1016         if (!ast_strlen_zero(misdn_init)) {
1017                 fp = fopen(misdn_init, "r");
1018                 if (fp) {
1019                         while(fgets(line, sizeof(line), fp)) {
1020                                 if (!strncmp(line, "nt_ptp", 6)) {
1021                                         for (tok = strtok_r(line,",=", &p);
1022                                                  tok;
1023                                                  tok = strtok_r(NULL,",=", &p)) {
1024                                                 port = strtol(tok, &end, 10);
1025                                                 if (end != tok && misdn_cfg_is_port_valid(port)) {
1026                                                         misdn_cfg_lock();
1027                                                         ptp[port] = 1;
1028                                                         misdn_cfg_unlock();
1029                                                 }
1030                                         }
1031                                 }
1032                         }
1033                         fclose(fp);
1034                 } else {
1035                         ast_log(LOG_WARNING,"Couldn't open %s: %s\n", misdn_init, strerror(errno));
1036                 }
1037         }
1038 #else
1039         int i;
1040         int proto;
1041         char filename[128];
1042         FILE *fp;
1043
1044         for (i = 1; i <= max_ports; ++i) {
1045                 snprintf(filename, sizeof(filename), "/sys/class/mISDN-stacks/st-%08x/protocol", i << 8);
1046                 fp = fopen(filename, "r");
1047                 if (!fp) {
1048                         ast_log(LOG_WARNING, "Could not open %s: %s\n", filename, strerror(errno));
1049                         continue;
1050                 }
1051                 if (fscanf(fp, "0x%08x", &proto) != 1)
1052                         ast_log(LOG_WARNING, "Could not parse contents of %s!\n", filename);
1053                 else
1054                         ptp[i] = proto & 1<<5 ? 1 : 0;
1055                 fclose(fp);
1056         }
1057 #endif
1058 }
1059
1060 static void _fill_defaults (void)
1061 {
1062         int i;
1063
1064         for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
1065                 if (!port_cfg[0][i].any && strcasecmp(port_spec[i].def, NO_DEFAULT))
1066                         _parse(&(port_cfg[0][i]), (char *)port_spec[i].def, port_spec[i].type, port_spec[i].boolint_def);
1067         }
1068         for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
1069                 if (!general_cfg[i].any && strcasecmp(gen_spec[i].def, NO_DEFAULT))
1070                         _parse(&(general_cfg[i]), (char *)gen_spec[i].def, gen_spec[i].type, gen_spec[i].boolint_def);
1071         }
1072 }
1073
1074 void misdn_cfg_reload (void)
1075 {
1076         misdn_cfg_init(0, 1);
1077 }
1078
1079 void misdn_cfg_destroy (void)
1080 {
1081         misdn_cfg_lock();
1082
1083         _free_port_cfg();
1084         _free_general_cfg();
1085
1086         ast_free(port_cfg);
1087         ast_free(general_cfg);
1088         ast_free(ptp);
1089         ast_free(map);
1090
1091         misdn_cfg_unlock();
1092         ast_mutex_destroy(&config_mutex);
1093 }
1094
1095 int misdn_cfg_init(int this_max_ports, int reload)
1096 {
1097         char config[] = "misdn.conf";
1098         char *cat, *p;
1099         int i;
1100         struct ast_config *cfg;
1101         struct ast_variable *v;
1102         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1103
1104         if (!(cfg = ast_config_load2(config, "chan_misdn", config_flags))) {
1105                 ast_log(LOG_WARNING, "missing file: misdn.conf\n");
1106                 return -1;
1107         } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
1108                 return 0;
1109
1110         ast_mutex_init(&config_mutex);
1111
1112         /* Copy the default jb config over global_jbconf */
1113         memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
1114
1115         misdn_cfg_lock();
1116
1117         if (this_max_ports) {
1118                 /* this is the first run */
1119                 max_ports = this_max_ports;
1120                 map = ast_calloc(MISDN_GEN_LAST + 1, sizeof(int));
1121                 if (_enum_array_map())
1122                         return -1;
1123                 p = ast_calloc(1, (max_ports + 1) * sizeof(union misdn_cfg_pt *)
1124                                                    + (max_ports + 1) * NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt));
1125                 port_cfg = (union misdn_cfg_pt **)p;
1126                 p += (max_ports + 1) * sizeof(union misdn_cfg_pt *);
1127                 for (i = 0; i <= max_ports; ++i) {
1128                         port_cfg[i] = (union misdn_cfg_pt *)p;
1129                         p += NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt);
1130                 }
1131                 general_cfg = ast_calloc(1, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1132                 ptp = ast_calloc(max_ports + 1, sizeof(int));
1133         }
1134         else {
1135                 /* misdn reload */
1136                 _free_port_cfg();
1137                 _free_general_cfg();
1138                 memset(port_cfg[0], 0, NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt) * (max_ports + 1));
1139                 memset(general_cfg, 0, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1140                 memset(ptp, 0, sizeof(int) * (max_ports + 1));
1141         }
1142
1143         cat = ast_category_browse(cfg, NULL);
1144
1145         while(cat) {
1146                 v = ast_variable_browse(cfg, cat);
1147                 if (!strcasecmp(cat, "general")) {
1148                         _build_general_config(v);
1149                 } else {
1150                         _build_port_config(v, cat);
1151                 }
1152                 cat = ast_category_browse(cfg, cat);
1153         }
1154
1155         _fill_defaults();
1156
1157         misdn_cfg_unlock();
1158         ast_config_destroy(cfg);
1159
1160         return 0;
1161 }
1162
1163 struct ast_jb_conf *misdn_get_global_jbconf() {
1164         return &global_jbconf;
1165 }