include "logger.h" and errno.h from asterisk.h - usage shows that they
[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 <errno.h>
34
35 #include "chan_misdn_config.h"
36
37 #include "asterisk/config.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/lock.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/strings.h"
42 #include "asterisk/utils.h"
43
44 #define AST_LOAD_CFG ast_config_load
45 #define AST_DESTROY_CFG ast_config_destroy
46
47 #define NO_DEFAULT "<>"
48 #define NONE 0
49
50 #define GEN_CFG 1
51 #define PORT_CFG 2
52 #define NUM_GEN_ELEMENTS (sizeof(gen_spec) / sizeof(struct misdn_cfg_spec))
53 #define NUM_PORT_ELEMENTS (sizeof(port_spec) / sizeof(struct misdn_cfg_spec))
54
55 /*! Global jitterbuffer configuration - by default, jb is disabled */
56 static struct ast_jb_conf default_jbconf =
57 {
58         .flags = 0,
59         .max_size = -1,
60         .resync_threshold = -1,
61         .impl = "",
62 };
63
64 static struct ast_jb_conf global_jbconf;
65
66 enum misdn_cfg_type {
67         MISDN_CTYPE_STR,
68         MISDN_CTYPE_INT,
69         MISDN_CTYPE_BOOL,
70         MISDN_CTYPE_BOOLINT,
71         MISDN_CTYPE_MSNLIST,
72         MISDN_CTYPE_ASTGROUP
73 };
74
75 struct msn_list {
76         char *msn;
77         struct msn_list *next;
78 };
79
80 union misdn_cfg_pt {
81         char *str;
82         int *num;
83         struct msn_list *ml;
84         ast_group_t *grp;
85         void *any;
86 };
87
88 struct misdn_cfg_spec {
89         char name[BUFFERSIZE];
90         enum misdn_cfg_elements elem;
91         enum misdn_cfg_type type;
92         char def[BUFFERSIZE];
93         int boolint_def;
94         char desc[BUFFERSIZE];
95 };
96
97
98 static const char ports_description[] =
99         "Define your ports, e.g. 1,2 (depends on mISDN-driver loading order).";
100
101 static const struct misdn_cfg_spec port_spec[] = {
102         { "name", MISDN_CFG_GROUPNAME, MISDN_CTYPE_STR, "default", NONE,
103                 "Name of the portgroup." },
104         { "allowed_bearers", MISDN_CFG_ALLOWED_BEARERS, MISDN_CTYPE_STR, "all", NONE,
105                 "Here you can define which bearers should be allowed." },
106         { "rxgain", MISDN_CFG_RXGAIN, MISDN_CTYPE_INT, "0", NONE,
107                 "Set this between -8 and 8 to change the RX Gain." },
108         { "txgain", MISDN_CFG_TXGAIN, MISDN_CTYPE_INT, "0", NONE,
109                 "Set this between -8 and 8 to change the TX Gain." },
110         { "te_choose_channel", MISDN_CFG_TE_CHOOSE_CHANNEL, MISDN_CTYPE_BOOL, "no", NONE,
111                 "Some telcos espacially in NL seem to need this set to yes,\n"
112                 "\talso in switzerland this seems to be important." },
113         { "far_alerting", MISDN_CFG_FAR_ALERTING, MISDN_CTYPE_BOOL, "no", NONE,
114                 "If we should generate ringing for chan_sip and others." },
115         { "pmp_l1_check", MISDN_CFG_PMP_L1_CHECK, MISDN_CTYPE_BOOL, "no", NONE,
116                 "This option defines, if chan_misdn should check the L1 on a PMP\n"
117                 "\tbefore makeing a group call on it. The L1 may go down for PMP Ports\n"
118                 "\tso we might need this.\n"
119                 "\tBut be aware! a broken or plugged off cable might be used for a group call\n"
120                 "\tas well, since chan_misdn has no chance to distinguish if the L1 is down\n"
121                 "\tbecause of a lost Link or because the Provider shut it down..." },
122         { "block_on_alarm", MISDN_CFG_ALARM_BLOCK, MISDN_CTYPE_BOOL, "no", NONE ,
123           "Block this port if we have an alarm on it."
124           "default: yes\n" },
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 both values, the presentation indicators are used from\n"
202                 "\tAsterisks SetCallerPres application.\n"
203                 "\n"
204                 "\tscreen=0, presentation=0 -> callerid presented not screened\n"
205                 "\tscreen=1, presentation=1 -> callerid presented but screened (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 both values, the presentation indicators are used from\n"
210                 "\tAsterisks SetCallerPres application.\n"
211                 "\n"
212                 "\tscreen=0, presentation=0 -> callerid presented not screened\n"
213                 "\tscreen=1, presentation=1 -> callerid presented but screened (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 echocancellation, with the given number of taps.\n"
245                 "\tBe aware, move this setting only to outgoing portgroups!\n"
246                 "\tA value of zero turns echocancellation off.\n"
247                 "\n"
248                 "\tPossible values are: 0,32,64,128,256,yes(=128),no(=0)" },
249 #ifdef MISDN_1_2
250         { "pipeline", MISDN_CFG_PIPELINE, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
251                 "Set the configuration string for the mISDN dsp pipeline.\n"
252                 "\n"
253                 "\tExample for enabling the mg2 echo cancellation module with deftaps\n"
254                 "\tset to 128:\n"
255                 "\t\tmg2ec(deftaps=128)" },
256 #endif
257 #ifdef WITH_BEROEC
258         { "bnechocancel", MISDN_CFG_BNECHOCANCEL, MISDN_CTYPE_BOOLINT, "yes", 64,
259                 "echotail in ms (1-200)\n"},
260         { "bnec_antihowl", MISDN_CFG_BNEC_ANTIHOWL, MISDN_CTYPE_INT, "0", NONE,
261                 "Use antihowl\n"},
262         { "bnec_nlp", MISDN_CFG_BNEC_NLP, MISDN_CTYPE_BOOL, "yes", NONE,
263                 "Nonlinear Processing (much faster adaption)"},
264         { "bnec_zerocoeff", MISDN_CFG_BNEC_ZEROCOEFF, MISDN_CTYPE_BOOL, "no", NONE,
265                 "ZeroCoeffeciens\n"},
266         { "bnec_tonedisabler", MISDN_CFG_BNEC_TD, MISDN_CTYPE_BOOL, "no", NONE,
267                 "Disable Tone\n"},
268         { "bnec_adaption", MISDN_CFG_BNEC_ADAPT, MISDN_CTYPE_INT, "1", NONE,
269                 "Adaption mode (0=no,1=full,2=fast)\n"},
270 #endif
271         { "need_more_infos", MISDN_CFG_NEED_MORE_INFOS, MISDN_CTYPE_BOOL, "0", NONE,
272                 "Send Setup_Acknowledge on incoming calls anyway (instead of PROCEEDING),\n"
273                 "\tthis requests additional Infos, so we can waitfordigits without much\n"
274                 "\tissues. This works only for PTP Ports" },
275         { "noautorespond_on_setup", MISDN_CFG_NOAUTORESPOND_ON_SETUP, MISDN_CTYPE_BOOL, "0", NONE,
276                 "Do not send SETUP_ACKNOWLEDGE or PROCEEDING automatically to the calling Party.\n"
277                 "Instead we directly jump into the dialplan. This might be useful for fast call\n"
278                 "rejection, or for some broken switches, that need hangup causes like busy in the.\n"
279                 "RELEASE_COMPLETE Message, instead of the DISCONNECT Message.\n"},
280         { "jitterbuffer", MISDN_CFG_JITTERBUFFER, MISDN_CTYPE_INT, "4000", NONE,
281                 "The jitterbuffer." },
282         { "jitterbuffer_upper_threshold", MISDN_CFG_JITTERBUFFER_UPPER_THRESHOLD, MISDN_CTYPE_INT, "0", NONE,
283                 "Change this threshold to enable dejitter functionality." },
284         { "callgroup", MISDN_CFG_CALLGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE,
285                 "Callgroup." },
286         { "pickupgroup", MISDN_CFG_PICKUPGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE,
287                 "Pickupgroup." },
288         { "max_incoming", MISDN_CFG_MAX_IN, MISDN_CTYPE_INT, "-1", NONE,
289                 "Defines the maximum amount of incoming calls per port for this group.\n"
290                 "\tCalls which exceed the maximum will be marked with the channel varible\n"
291                 "\tMAX_OVERFLOW. It will contain the amount of overflowed calls" },
292         { "max_outgoing", MISDN_CFG_MAX_OUT, MISDN_CTYPE_INT, "-1", NONE,
293                 "Defines the maximum amount of outgoing calls per port for this group\n"
294                 "\texceeding calls will be rejected" },
295
296         { "reject_cause", MISDN_CFG_REJECT_CAUSE, MISDN_CTYPE_INT, "21", NONE,
297                 "Defines the cause with which a 3. call is rejected on PTMP BRI."},
298         { "faxdetect", MISDN_CFG_FAXDETECT, MISDN_CTYPE_STR, "no", NONE,
299                 "Setup fax detection:\n"
300                 "\t    no        - no fax detection\n"
301                 "\t    incoming  - fax detection for incoming calls\n"
302                 "\t    outgoing  - fax detection for outgoing calls\n"
303                 "\t    both      - fax detection for incoming and outgoing calls\n"
304                 "\tAdd +nojump to your value (i.e. faxdetect=both+nojump) if you don't want to jump into the\n"
305                 "\tfax-extension but still want to detect the fax and prepare the channel for fax transfer." },
306         { "faxdetect_timeout", MISDN_CFG_FAXDETECT_TIMEOUT, MISDN_CTYPE_INT, "5", NONE,
307                 "Number of seconds the fax detection should do its job. After the given period of time,\n"
308                 "\twe assume that it's not a fax call and save some CPU time by turning off fax detection.\n"
309                 "\tSet this to 0 if you don't want a timeout (never stop detecting)." },
310         { "faxdetect_context", MISDN_CFG_FAXDETECT_CONTEXT, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
311                 "Context to jump into if we detect a fax. Don't set this if you want to stay in the current context." },
312         { "l1watcher_timeout", MISDN_CFG_L1_TIMEOUT, MISDN_CTYPE_BOOLINT, "0", 4,
313                 "Watches the layer 1. If the layer 1 is down, it tries to\n"
314                 "\tget it up. The timeout is given in seconds. with 0 as value it\n"
315                 "\tdoes not watch the l1 at all\n"
316                 "\n"
317                 "\tThis option is only read at loading time of chan_misdn, which\n"
318                 "\tmeans you need to unload and load chan_misdn to change the value,\n"
319                 "\tan Asterisk restart should do the trick." },
320         { "overlapdial", MISDN_CFG_OVERLAP_DIAL, MISDN_CTYPE_BOOLINT, "0", 4,
321                 "Enables overlap dial for the given amount of seconds.\n"
322                 "\tPossible values are positive integers or:\n"
323                 "\t   yes (= 4 seconds)\n"
324                 "\t   no  (= 0 seconds = disabled)" },
325         { "nttimeout", MISDN_CFG_NTTIMEOUT, MISDN_CTYPE_BOOL, "no", NONE ,
326                 "Set this to yes if you want calls disconnected in overlap mode\n"
327                 "\twhen a timeout happens." },
328         { "bridging", MISDN_CFG_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
329                 "Set this to yes/no, default is yes.\n"
330                 "This can be used to have bridging enabled in general and to\n"
331                 "disable it for specific ports. It makes sense to disable\n"
332                 "bridging on NT Port where you plan to use the HOLD/RETRIEVE\n"
333                 "features with ISDN phones.\n"
334                 },
335         { "msns", MISDN_CFG_MSNS, MISDN_CTYPE_MSNLIST, "*", NONE,
336                 "MSN's for TE ports, listen on those numbers on the above ports, and\n"
337                 "\tindicate the incoming calls to Asterisk.\n"
338                 "\tHere you can give a comma seperated list, or simply an '*' for any msn." },
339 };
340
341 static const struct misdn_cfg_spec gen_spec[] = {
342         { "debug", MISDN_GEN_DEBUG, MISDN_CTYPE_INT, "0", NONE,
343                 "Sets the debugging flag:\n"
344                 "\t0 - No Debug\n"
345                 "\t1 - mISDN Messages and * - Messages, and * - State changes\n"
346                 "\t2 - Messages + Message specific Informations (e.g. bearer capability)\n"
347                 "\t3 - very Verbose, the above + lots of Driver specific infos\n"
348                 "\t4 - even more Verbose than 3" },
349 #ifndef MISDN_1_2
350         { "misdn_init", MISDN_GEN_MISDN_INIT, MISDN_CTYPE_STR, "/etc/misdn-init.conf", NONE,
351                 "Set the path to the misdn-init.conf (for nt_ptp mode checking)." },
352 #endif
353         { "tracefile", MISDN_GEN_TRACEFILE, MISDN_CTYPE_STR, "/var/log/asterisk/misdn.log", NONE,
354                 "Set the path to the massively growing trace file, if you want that." },
355         { "bridging", MISDN_GEN_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
356                 "Set this to yes if you want mISDN_dsp to bridge the calls in HW." },
357         { "stop_tone_after_first_digit", MISDN_GEN_STOP_TONE, MISDN_CTYPE_BOOL, "yes", NONE,
358                 "Stops dialtone after getting first digit on NT Port." },
359         { "append_digits2exten", MISDN_GEN_APPEND_DIGITS2EXTEN, MISDN_CTYPE_BOOL, "yes", NONE,
360                 "Wether to append overlapdialed Digits to Extension or not." },
361         { "dynamic_crypt", MISDN_GEN_DYNAMIC_CRYPT, MISDN_CTYPE_BOOL, "no", NONE,
362                 "Wether to look out for dynamic crypting attempts." },
363         { "crypt_prefix", MISDN_GEN_CRYPT_PREFIX, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
364                 "What is used for crypting Protocol." },
365         { "crypt_keys", MISDN_GEN_CRYPT_KEYS, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
366                 "Keys for cryption, you reference them in the dialplan\n"
367                 "\tLater also in dynamic encr." },
368         { "ntkeepcalls", MISDN_GEN_NTKEEPCALLS, MISDN_CTYPE_BOOL, "no", NONE, 
369                 "avoid dropping calls if the L2 goes down. some nortel pbx\n" 
370                 "do put down the L2/L1 for some milliseconds even if there\n"
371                 "are running calls. with this option you can avoid dropping them\n" },
372         { "ntdebugflags", MISDN_GEN_NTDEBUGFLAGS, MISDN_CTYPE_INT, "0", NONE,
373                 "No description yet."},
374         { "ntdebugfile", MISDN_GEN_NTDEBUGFILE, MISDN_CTYPE_STR, "/var/log/misdn-nt.log", NONE,
375                 "No description yet." }
376 };
377
378
379 /* array of port configs, default is at position 0. */
380 static union misdn_cfg_pt **port_cfg;
381 /* max number of available ports, is set on init */
382 static int max_ports;
383 /* general config */
384 static union misdn_cfg_pt *general_cfg;
385 /* storing the ptp flag separated to save memory */
386 static int *ptp;
387 /* maps enum config elements to array positions */
388 static int *map;
389
390 static ast_mutex_t config_mutex; 
391
392 #define CLI_ERROR(name, value, section) ({ \
393         ast_log(LOG_WARNING, "misdn.conf: \"%s=%s\" (section: %s) invalid or out of range. " \
394                 "Please edit your misdn.conf and then do a \"misdn reload\".\n", name, value, section); \
395 })
396
397 static int _enum_array_map (void)
398 {
399         int i, j, ok;
400
401         for (i = MISDN_CFG_FIRST + 1; i < MISDN_CFG_LAST; ++i) {
402                 if (i == MISDN_CFG_PTP)
403                         continue;
404                 ok = 0;
405                 for (j = 0; j < NUM_PORT_ELEMENTS; ++j) {
406                         if (port_spec[j].elem == i) {
407                                 map[i] = j;
408                                 ok = 1;
409                                 break;
410                         }
411                 }
412                 if (!ok) {
413                         ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (port section) has no corresponding element in the config struct!\n", i);
414                         return -1;
415                 }
416         }
417         for (i = MISDN_GEN_FIRST + 1; i < MISDN_GEN_LAST; ++i) {
418                 ok = 0;
419                 for (j = 0; j < NUM_GEN_ELEMENTS; ++j) {
420                         if (gen_spec[j].elem == i) {
421                                 map[i] = j;
422                                 ok = 1;
423                                 break;
424                         }
425                 }
426                 if (!ok) {
427                         ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (general section) has no corresponding element in the config struct!\n", i);
428                         return -1;
429                 }
430         }
431         return 0;
432 }
433
434 static int get_cfg_position (const char *name, int type)
435 {
436         int i;
437
438         switch (type) {
439         case PORT_CFG:
440                 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
441                         if (!strcasecmp(name, port_spec[i].name))
442                                 return i;
443                 }
444                 break;
445         case GEN_CFG:
446                 for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
447                         if (!strcasecmp(name, gen_spec[i].name))
448                                 return i;
449                 }
450         }
451
452         return -1;
453 }
454
455 static inline void misdn_cfg_lock (void)
456 {
457         ast_mutex_lock(&config_mutex);
458 }
459
460 static inline void misdn_cfg_unlock (void)
461 {
462         ast_mutex_unlock(&config_mutex);
463 }
464
465 static void _free_msn_list (struct msn_list* iter)
466 {
467         if (iter->next)
468                 _free_msn_list(iter->next);
469         if (iter->msn)
470                 ast_free(iter->msn);
471         ast_free(iter);
472 }
473
474 static void _free_port_cfg (void)
475 {
476         int i, j;
477         int gn = map[MISDN_CFG_GROUPNAME];
478         union misdn_cfg_pt* free_list[max_ports + 2];
479         
480         memset(free_list, 0, sizeof(free_list));
481         free_list[0] = port_cfg[0];
482         for (i = 1; i <= max_ports; ++i) {
483                 if (port_cfg[i][gn].str) {
484                         /* we always have a groupname in the non-default case, so this is fine */
485                         for (j = 1; j <= max_ports; ++j) {
486                                 if (free_list[j] && free_list[j][gn].str == port_cfg[i][gn].str)
487                                         break;
488                                 else if (!free_list[j]) {
489                                         free_list[j] = port_cfg[i];
490                                         break;
491                                 }
492                         }
493                 }
494         }
495         for (j = 0; free_list[j]; ++j) {
496                 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
497                         if (free_list[j][i].any) {
498                                 if (port_spec[i].type == MISDN_CTYPE_MSNLIST)
499                                         _free_msn_list(free_list[j][i].ml);
500                                 else
501                                         ast_free(free_list[j][i].any);
502                         }
503                 }
504         }
505 }
506
507 static void _free_general_cfg (void)
508 {
509         int i;
510
511         for (i = 0; i < NUM_GEN_ELEMENTS; i++) 
512                 if (general_cfg[i].any)
513                         ast_free(general_cfg[i].any);
514 }
515
516 void misdn_cfg_get(int port, enum misdn_cfg_elements elem, void *buf, int bufsize)
517 {
518         int place;
519
520         if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
521                 memset(buf, 0, bufsize);
522                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Port number %d is not valid.\n", port);
523                 return;
524         }
525
526         misdn_cfg_lock();
527         if (elem == MISDN_CFG_PTP) {
528                 if (!memcpy(buf, &ptp[port], (bufsize > ptp[port]) ? sizeof(ptp[port]) : bufsize))
529                         memset(buf, 0, bufsize);
530         } else {
531                 if ((place = map[elem]) < 0) {
532                         memset(buf, 0, bufsize);
533                         ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Invalid element (%d) requested.\n", elem);
534                 } else {
535                         if (elem < MISDN_CFG_LAST) {
536                                 switch (port_spec[place].type) {
537                                 case MISDN_CTYPE_STR:
538                                         if (port_cfg[port][place].str) {
539                                                 ast_copy_string(buf, port_cfg[port][place].str, bufsize);
540                                         } else if (port_cfg[0][place].str) {
541                                                 ast_copy_string(buf, port_cfg[0][place].str, bufsize);
542                                         }
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" elemet 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" elemet 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" elemet 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 void misdn_cfg_get_ports_string (char *ports)
712 {
713         char tmp[16];
714         int l, i;
715         int gn = map[MISDN_CFG_GROUPNAME];
716
717         *ports = 0;
718
719         misdn_cfg_lock();
720         for (i = 1; i <= max_ports; i++) {
721                 if (port_cfg[i][gn].str) {
722                         if (ptp[i])
723                                 sprintf(tmp, "%dptp,", i);
724                         else
725                                 sprintf(tmp, "%d,", i);
726                         strcat(ports, tmp);
727                 }
728         }
729         misdn_cfg_unlock();
730
731         if ((l = strlen(ports)))
732                 ports[l-1] = 0;
733 }
734
735 void misdn_cfg_get_config_string (int port, enum misdn_cfg_elements elem, char* buf, int bufsize)
736 {
737         int place;
738         char tempbuf[BUFFERSIZE] = "";
739         struct msn_list *iter;
740
741         if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
742                 *buf = 0;
743                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Port number %d is not valid.\n", port);
744                 return;
745         }
746
747         place = map[elem];
748
749         misdn_cfg_lock();
750         if (elem == MISDN_CFG_PTP) {
751                 snprintf(buf, bufsize, " -> ptp: %s", ptp[port] ? "yes" : "no");
752         }
753         else if (elem > MISDN_CFG_FIRST && elem < MISDN_CFG_LAST) {
754                 switch (port_spec[place].type) {
755                 case MISDN_CTYPE_INT:
756                 case MISDN_CTYPE_BOOLINT:
757                         if (port_cfg[port][place].num)
758                                 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[port][place].num);
759                         else if (port_cfg[0][place].num)
760                                 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[0][place].num);
761                         else
762                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
763                         break;
764                 case MISDN_CTYPE_BOOL:
765                         if (port_cfg[port][place].num)
766                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[port][place].num ? "yes" : "no");
767                         else if (port_cfg[0][place].num)
768                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[0][place].num ? "yes" : "no");
769                         else
770                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
771                         break;
772                 case MISDN_CTYPE_ASTGROUP:
773                         if (port_cfg[port][place].grp)
774                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, 
775                                                  ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[port][place].grp));
776                         else if (port_cfg[0][place].grp)
777                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, 
778                                                  ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[0][place].grp));
779                         else
780                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
781                         break;
782                 case MISDN_CTYPE_MSNLIST:
783                         if (port_cfg[port][place].ml)
784                                 iter = port_cfg[port][place].ml;
785                         else
786                                 iter = port_cfg[0][place].ml;
787                         if (iter) {
788                                 for (; iter; iter = iter->next)
789                                         sprintf(tempbuf, "%s%s, ", tempbuf, iter->msn);
790                                 tempbuf[strlen(tempbuf)-2] = 0;
791                         }
792                         snprintf(buf, bufsize, " -> msns: %s", *tempbuf ? tempbuf : "none");
793                         break;
794                 case MISDN_CTYPE_STR:
795                         if ( port_cfg[port][place].str) {
796                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[port][place].str);
797                         } else if (port_cfg[0][place].str) {
798                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[0][place].str);
799                         } else {
800                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
801                         }
802                         break;
803                 }
804         } else if (elem > MISDN_GEN_FIRST && elem < MISDN_GEN_LAST) {
805                 switch (gen_spec[place].type) {
806                 case MISDN_CTYPE_INT:
807                 case MISDN_CTYPE_BOOLINT:
808                         if (general_cfg[place].num)
809                                 snprintf(buf, bufsize, " -> %s: %d", gen_spec[place].name, *general_cfg[place].num);
810                         else
811                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
812                         break;
813                 case MISDN_CTYPE_BOOL:
814                         if (general_cfg[place].num)
815                                 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, *general_cfg[place].num ? "yes" : "no");
816                         else
817                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
818                         break;
819                 case MISDN_CTYPE_STR:
820                         if ( general_cfg[place].str) {
821                                 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, general_cfg[place].str);
822                         } else {
823                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
824                         }
825                         break;
826                 default:
827                         snprintf(buf, bufsize, " -> type of %s not handled yet", gen_spec[place].name);
828                         break;
829                 }
830         } else {
831                 *buf = 0;
832                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Invalid config element (%d) requested.\n", elem);
833         }
834         misdn_cfg_unlock();
835 }
836
837 int misdn_cfg_get_next_port (int port)
838 {
839         int p = -1;
840         int gn = map[MISDN_CFG_GROUPNAME];
841         
842         misdn_cfg_lock();
843         for (port++; port <= max_ports; port++) {
844                 if (port_cfg[port][gn].str) {
845                         p = port;
846                         break;
847                 }
848         }
849         misdn_cfg_unlock();
850
851         return p;
852 }
853
854 int misdn_cfg_get_next_port_spin (int port)
855 {
856         int p = misdn_cfg_get_next_port(port);
857         return (p > 0) ? p : misdn_cfg_get_next_port(0);
858 }
859
860 static int _parse (union misdn_cfg_pt *dest, const char *value, enum misdn_cfg_type type, int boolint_def)
861 {
862         int re = 0;
863         int len, tmp;
864         char *valtmp;
865         char *tmp2 = ast_strdupa(value);
866
867         switch (type) {
868         case MISDN_CTYPE_STR:
869                 if ((len = strlen(value))) {
870                         dest->str = ast_malloc((len + 1) * sizeof(char));
871                         strncpy(dest->str, value, len);
872                         dest->str[len] = 0;
873                 } else {
874                         dest->str = ast_malloc(sizeof(char));
875                         dest->str[0] = 0;
876                 }
877                 break;
878         case MISDN_CTYPE_INT:
879         {
880                 char *pat;
881                 if (strchr(value,'x')) 
882                         pat="%x";
883                 else
884                         pat="%d";
885                 if (sscanf(value, pat, &tmp)) {
886                         dest->num = ast_malloc(sizeof(int));
887                         memcpy(dest->num, &tmp, sizeof(int));
888                 } else
889                         re = -1;
890         }
891                 break;
892         case MISDN_CTYPE_BOOL:
893                 dest->num = ast_malloc(sizeof(int));
894                 *(dest->num) = (ast_true(value) ? 1 : 0);
895                 break;
896         case MISDN_CTYPE_BOOLINT:
897                 dest->num = ast_malloc(sizeof(int));
898                 if (sscanf(value, "%d", &tmp)) {
899                         memcpy(dest->num, &tmp, sizeof(int));
900                 } else {
901                         *(dest->num) = (ast_true(value) ? boolint_def : 0);
902                 }
903                 break;
904         case MISDN_CTYPE_MSNLIST:
905                 for (valtmp = strsep(&tmp2, ","); valtmp; valtmp = strsep(&tmp2, ",")) {
906                         if ((len = strlen(valtmp))) {
907                                 struct msn_list *ml = ast_malloc(sizeof(*ml));
908                                 ml->msn = ast_calloc(len+1, sizeof(char));
909                                 strncpy(ml->msn, valtmp, len);
910                                 ml->next = dest->ml;
911                                 dest->ml = ml;
912                         }
913                 }
914                 break;
915         case MISDN_CTYPE_ASTGROUP:
916                 dest->grp = ast_malloc(sizeof(ast_group_t));
917                 *(dest->grp) = ast_get_group(value);
918                 break;
919         }
920
921         return re;
922 }
923
924 static void _build_general_config (struct ast_variable *v)
925 {
926         int pos;
927
928         for (; v; v = v->next) {
929                 if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
930                         continue;
931                 if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) || 
932                         (_parse(&general_cfg[pos], v->value, gen_spec[pos].type, gen_spec[pos].boolint_def) < 0))
933                         CLI_ERROR(v->name, v->value, "general");
934         }
935 }
936
937 static void _build_port_config (struct ast_variable *v, char *cat)
938 {
939         int pos, i;
940         union misdn_cfg_pt cfg_tmp[NUM_PORT_ELEMENTS];
941         int cfg_for_ports[max_ports + 1];
942
943         if (!v || !cat)
944                 return;
945
946         memset(cfg_tmp, 0, sizeof(cfg_tmp));
947         memset(cfg_for_ports, 0, sizeof(cfg_for_ports));
948
949         if (!strcasecmp(cat, "default")) {
950                 cfg_for_ports[0] = 1;
951         }
952
953         if (((pos = get_cfg_position("name", PORT_CFG)) < 0) || 
954                 (_parse(&cfg_tmp[pos], cat, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) {
955                 CLI_ERROR(v->name, v->value, cat);
956                 return;
957         }
958
959         for (; v; v = v->next) {
960                 if (!strcasecmp(v->name, "ports")) {
961                         char *token, *tmp = ast_strdupa(v->value);
962                         char ptpbuf[BUFFERSIZE] = "";
963                         int start, end;
964                         for (token = strsep(&tmp, ","); token; token = strsep(&tmp, ","), *ptpbuf = 0) { 
965                                 if (!*token)
966                                         continue;
967                                 if (sscanf(token, "%d-%d%s", &start, &end, ptpbuf) >= 2) {
968                                         for (; start <= end; start++) {
969                                                 if (start <= max_ports && start > 0) {
970                                                         cfg_for_ports[start] = 1;
971                                                         ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
972                                                 } else
973                                                         CLI_ERROR(v->name, v->value, cat);
974                                         }
975                                 } else {
976                                         if (sscanf(token, "%d%s", &start, ptpbuf)) {
977                                                 if (start <= max_ports && start > 0) {
978                                                         cfg_for_ports[start] = 1;
979                                                         ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
980                                                 } else
981                                                         CLI_ERROR(v->name, v->value, cat);
982                                         } else
983                                                 CLI_ERROR(v->name, v->value, cat);
984                                 }
985                         }
986                 } else {
987                         if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) || 
988                                 (_parse(&cfg_tmp[pos], v->value, port_spec[pos].type, port_spec[pos].boolint_def) < 0))
989                                 CLI_ERROR(v->name, v->value, cat);
990                 }
991         }
992
993         for (i = 0; i < (max_ports + 1); ++i) {
994                 if (cfg_for_ports[i]) {
995                         memcpy(port_cfg[i], cfg_tmp, sizeof(cfg_tmp));
996                 }
997         }
998 }
999
1000 void misdn_cfg_update_ptp (void)
1001 {
1002 #ifndef MISDN_1_2
1003         char misdn_init[BUFFERSIZE];
1004         char line[BUFFERSIZE];
1005         FILE *fp;
1006         char *tok, *p, *end;
1007         int port;
1008
1009         misdn_cfg_get(0, MISDN_GEN_MISDN_INIT, &misdn_init, sizeof(misdn_init));
1010
1011         if (!ast_strlen_zero(misdn_init)) {
1012                 fp = fopen(misdn_init, "r");
1013                 if (fp) {
1014                         while(fgets(line, sizeof(line), fp)) {
1015                                 if (!strncmp(line, "nt_ptp", 6)) {
1016                                         for (tok = strtok_r(line,",=", &p);
1017                                                  tok;
1018                                                  tok = strtok_r(NULL,",=", &p)) {
1019                                                 port = strtol(tok, &end, 10);
1020                                                 if (end != tok && misdn_cfg_is_port_valid(port)) {
1021                                                         misdn_cfg_lock();
1022                                                         ptp[port] = 1;
1023                                                         misdn_cfg_unlock();
1024                                                 }
1025                                         }
1026                                 }
1027                         }
1028                         fclose(fp);
1029                 } else {
1030                         ast_log(LOG_WARNING,"Couldn't open %s: %s\n", misdn_init, strerror(errno));
1031                 }
1032         }
1033 #else
1034         int i;
1035         int proto;
1036         char filename[128];
1037         FILE *fp;
1038
1039         for (i = 1; i <= max_ports; ++i) {
1040                 snprintf(filename, sizeof(filename), "/sys/class/mISDN-stacks/st-%08x/protocol", i << 8);
1041                 fp = fopen(filename, "r");
1042                 if (!fp) {
1043                         ast_log(LOG_WARNING, "Could not open %s: %s\n", filename, strerror(errno));
1044                         continue;
1045                 }
1046                 if (fscanf(fp, "0x%08x", &proto) != 1)
1047                         ast_log(LOG_WARNING, "Could not parse contents of %s!\n", filename);
1048                 else
1049                         ptp[i] = proto & 1<<5 ? 1 : 0;
1050                 fclose(fp);
1051         }
1052 #endif
1053 }
1054
1055 static void _fill_defaults (void)
1056 {
1057         int i;
1058
1059         for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
1060                 if (!port_cfg[0][i].any && strcasecmp(port_spec[i].def, NO_DEFAULT))
1061                         _parse(&(port_cfg[0][i]), (char *)port_spec[i].def, port_spec[i].type, port_spec[i].boolint_def);
1062         }
1063         for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
1064                 if (!general_cfg[i].any && strcasecmp(gen_spec[i].def, NO_DEFAULT))
1065                         _parse(&(general_cfg[i]), (char *)gen_spec[i].def, gen_spec[i].type, gen_spec[i].boolint_def);
1066         }
1067 }
1068
1069 void misdn_cfg_reload (void)
1070 {
1071         misdn_cfg_init(0, 1);
1072 }
1073
1074 void misdn_cfg_destroy (void)
1075 {
1076         misdn_cfg_lock();
1077
1078         _free_port_cfg();
1079         _free_general_cfg();
1080
1081         ast_free(port_cfg);
1082         ast_free(general_cfg);
1083         ast_free(ptp);
1084         ast_free(map);
1085
1086         misdn_cfg_unlock();
1087         ast_mutex_destroy(&config_mutex);
1088 }
1089
1090 int misdn_cfg_init(int this_max_ports, int reload)
1091 {
1092         char config[] = "misdn.conf";
1093         char *cat, *p;
1094         int i;
1095         struct ast_config *cfg;
1096         struct ast_variable *v;
1097         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1098
1099         if (!(cfg = AST_LOAD_CFG(config, config_flags))) {
1100                 ast_log(LOG_WARNING, "missing file: misdn.conf\n");
1101                 return -1;
1102         } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
1103                 return 0;
1104
1105         ast_mutex_init(&config_mutex);
1106
1107         /* Copy the default jb config over global_jbconf */
1108         memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
1109
1110         misdn_cfg_lock();
1111
1112         if (this_max_ports) {
1113                 /* this is the first run */
1114                 max_ports = this_max_ports;
1115                 map = ast_calloc(MISDN_GEN_LAST + 1, sizeof(int));
1116                 if (_enum_array_map())
1117                         return -1;
1118                 p = ast_calloc(1, (max_ports + 1) * sizeof(union misdn_cfg_pt *)
1119                                                    + (max_ports + 1) * NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt));
1120                 port_cfg = (union misdn_cfg_pt **)p;
1121                 p += (max_ports + 1) * sizeof(union misdn_cfg_pt *);
1122                 for (i = 0; i <= max_ports; ++i) {
1123                         port_cfg[i] = (union misdn_cfg_pt *)p;
1124                         p += NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt);
1125                 }
1126                 general_cfg = ast_calloc(1, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1127                 ptp = ast_calloc(max_ports + 1, sizeof(int));
1128         }
1129         else {
1130                 /* misdn reload */
1131                 _free_port_cfg();
1132                 _free_general_cfg();
1133                 memset(port_cfg[0], 0, NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt) * (max_ports + 1));
1134                 memset(general_cfg, 0, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1135                 memset(ptp, 0, sizeof(int) * (max_ports + 1));
1136         }
1137
1138         cat = ast_category_browse(cfg, NULL);
1139
1140         while(cat) {
1141                 v = ast_variable_browse(cfg, cat);
1142                 if (!strcasecmp(cat, "general")) {
1143                         _build_general_config(v);
1144                 } else {
1145                         _build_port_config(v, cat);
1146                 }
1147                 cat = ast_category_browse(cfg, cat);
1148         }
1149
1150         _fill_defaults();
1151
1152         misdn_cfg_unlock();
1153         AST_DESTROY_CFG(cfg);
1154
1155         return 0;
1156 }
1157
1158 struct ast_jb_conf *misdn_get_global_jbconf() {
1159         return &global_jbconf;
1160 }