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