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