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