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