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