pjsip: new endpoint's options to control Connected Line updates
[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 #include "chan_misdn_config.h"
36
37 #include "asterisk/config.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/lock.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/strings.h"
42 #include "asterisk/utils.h"
43
44 #define NO_DEFAULT "<>"
45 #define NONE 0
46
47 #define GEN_CFG 1
48 #define PORT_CFG 2
49 #define NUM_GEN_ELEMENTS (sizeof(gen_spec) / sizeof(struct misdn_cfg_spec))
50 #define NUM_PORT_ELEMENTS (sizeof(port_spec) / sizeof(struct misdn_cfg_spec))
51
52 /*! Global jitterbuffer configuration - by default, jb is disabled
53  *  \note Values shown here match the defaults shown in misdn.conf.sample */
54 static struct ast_jb_conf default_jbconf =
55 {
56         .flags = 0,
57         .max_size = 200,
58         .resync_threshold = 1000,
59         .impl = "fixed",
60         .target_extra = 40,
61 };
62
63 static struct ast_jb_conf global_jbconf;
64
65 enum misdn_cfg_type {
66         MISDN_CTYPE_STR,
67         MISDN_CTYPE_INT,
68         MISDN_CTYPE_BOOL,
69         MISDN_CTYPE_BOOLINT,
70         MISDN_CTYPE_MSNLIST,
71         MISDN_CTYPE_ASTGROUP,
72         MISDN_CTYPE_ASTNAMEDGROUP
73 };
74
75 struct msn_list {
76         char *msn;
77         struct msn_list *next;
78 };
79
80 union misdn_cfg_pt {
81         char *str;
82         int *num;
83         struct msn_list *ml;
84         ast_group_t *grp;
85         struct ast_namedgroups *namgrp;
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         { "namedcallgroup", MISDN_CFG_NAMEDCALLGROUP, MISDN_CTYPE_ASTNAMEDGROUP, NO_DEFAULT, NONE,
334                 "Named callgroup." },
335         { "namedpickupgroup", MISDN_CFG_NAMEDPICKUPGROUP, MISDN_CTYPE_ASTNAMEDGROUP, NO_DEFAULT, NONE,
336                 "Named pickupgroup." },
337         { "max_incoming", MISDN_CFG_MAX_IN, MISDN_CTYPE_INT, "-1", NONE,
338                 "Defines the maximum amount of incoming calls per port for this group.\n"
339                 "\tCalls which exceed the maximum will be marked with the channel variable\n"
340                 "\tMAX_OVERFLOW. It will contain the amount of overflowed calls" },
341         { "max_outgoing", MISDN_CFG_MAX_OUT, MISDN_CTYPE_INT, "-1", NONE,
342                 "Defines the maximum amount of outgoing calls per port for this group\n"
343                 "\texceeding calls will be rejected" },
344
345         { "reject_cause", MISDN_CFG_REJECT_CAUSE, MISDN_CTYPE_INT, "21", NONE,
346                 "Defines the cause with which a 3. call is rejected on PTMP BRI."},
347         { "faxdetect", MISDN_CFG_FAXDETECT, MISDN_CTYPE_STR, "no", NONE,
348                 "Setup fax detection:\n"
349                 "\t    no        - no fax detection\n"
350                 "\t    incoming  - fax detection for incoming calls\n"
351                 "\t    outgoing  - fax detection for outgoing calls\n"
352                 "\t    both      - fax detection for incoming and outgoing calls\n"
353                 "\tAdd +nojump to your value (i.e. faxdetect=both+nojump) if you don't want to jump into the\n"
354                 "\tfax-extension but still want to detect the fax and prepare the channel for fax transfer." },
355         { "faxdetect_timeout", MISDN_CFG_FAXDETECT_TIMEOUT, MISDN_CTYPE_INT, "5", NONE,
356                 "Number of seconds the fax detection should do its job. After the given period of time,\n"
357                 "\twe assume that it's not a fax call and save some CPU time by turning off fax detection.\n"
358                 "\tSet this to 0 if you don't want a timeout (never stop detecting)." },
359         { "faxdetect_context", MISDN_CFG_FAXDETECT_CONTEXT, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
360                 "Context to jump into if we detect a fax. Don't set this if you want to stay in the current context." },
361         { "l1watcher_timeout", MISDN_CFG_L1_TIMEOUT, MISDN_CTYPE_BOOLINT, "0", 4,
362                 "Monitors L1 of the port.  If L1 is down it tries\n"
363                 "\tto bring it up.  The polling timeout is given in seconds.\n"
364                 "\tSetting the value to 0 disables monitoring L1 of the port.\n"
365                 "\n"
366                 "\tThis option is only read at chan_misdn loading time.\n"
367                 "\tYou need to unload and load chan_misdn to change the\n"
368                 "\tvalue.  An asterisk restart will also do the trick." },
369         { "overlapdial", MISDN_CFG_OVERLAP_DIAL, MISDN_CTYPE_BOOLINT, "0", 4,
370                 "Enables overlap dial for the given amount of seconds.\n"
371                 "\tPossible values are positive integers or:\n"
372                 "\t   yes (= 4 seconds)\n"
373                 "\t   no  (= 0 seconds = disabled)" },
374         { "nttimeout", MISDN_CFG_NTTIMEOUT, MISDN_CTYPE_BOOL, "no", NONE ,
375                 "Set this to yes if you want calls disconnected in overlap mode\n"
376                 "\twhen a timeout happens." },
377         { "bridging", MISDN_CFG_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
378                 "Set this to yes/no, default is yes.\n"
379                 "This can be used to have bridging enabled in general and to\n"
380                 "disable it for specific ports. It makes sense to disable\n"
381                 "bridging on NT Port where you plan to use the HOLD/RETRIEVE\n"
382                 "features with ISDN phones." },
383         { "msns", MISDN_CFG_MSNS, MISDN_CTYPE_MSNLIST, "*", NONE,
384                 "MSN's for TE ports, listen on those numbers on the above ports, and\n"
385                 "\tindicate the incoming calls to Asterisk.\n"
386                 "\tHere you can give a comma separated list, or simply an '*' for any msn." },
387         { "cc_request_retention", MISDN_CFG_CC_REQUEST_RETENTION, MISDN_CTYPE_BOOL, "yes", NONE,
388                 "Enable/Disable call-completion request retention support (ptp)." },
389 };
390
391 static const struct misdn_cfg_spec gen_spec[] = {
392         { "debug", MISDN_GEN_DEBUG, MISDN_CTYPE_INT, "0", NONE,
393                 "Sets the debugging flag:\n"
394                 "\t0 - No Debug\n"
395                 "\t1 - mISDN Messages and * - Messages, and * - State changes\n"
396                 "\t2 - Messages + Message specific Informations (e.g. bearer capability)\n"
397                 "\t3 - very Verbose, the above + lots of Driver specific infos\n"
398                 "\t4 - even more Verbose than 3" },
399 #ifndef MISDN_1_2
400         { "misdn_init", MISDN_GEN_MISDN_INIT, MISDN_CTYPE_STR, "/etc/misdn-init.conf", NONE,
401                 "Set the path to the misdn-init.conf (for nt_ptp mode checking)." },
402 #endif
403         { "tracefile", MISDN_GEN_TRACEFILE, MISDN_CTYPE_STR, "/var/log/asterisk/misdn.log", NONE,
404                 "Set the path to the massively growing trace file, if you want that." },
405         { "bridging", MISDN_GEN_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
406                 "Set this to yes if you want mISDN_dsp to bridge the calls in HW." },
407         { "stop_tone_after_first_digit", MISDN_GEN_STOP_TONE, MISDN_CTYPE_BOOL, "yes", NONE,
408                 "Stops dialtone after getting first digit on NT Port." },
409         { "append_digits2exten", MISDN_GEN_APPEND_DIGITS2EXTEN, MISDN_CTYPE_BOOL, "yes", NONE,
410                 "Whether to append overlapdialed Digits to Extension or not." },
411         { "dynamic_crypt", MISDN_GEN_DYNAMIC_CRYPT, MISDN_CTYPE_BOOL, "no", NONE,
412                 "Whether to look out for dynamic crypting attempts." },
413         { "crypt_prefix", MISDN_GEN_CRYPT_PREFIX, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
414                 "What is used for crypting Protocol." },
415         { "crypt_keys", MISDN_GEN_CRYPT_KEYS, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
416                 "Keys for cryption, you reference them in the dialplan\n"
417                 "\tLater also in dynamic encr." },
418         { "ntkeepcalls", MISDN_GEN_NTKEEPCALLS, MISDN_CTYPE_BOOL, "no", NONE,
419                 "avoid dropping calls if the L2 goes down. some Nortel pbx\n"
420                 "do put down the L2/L1 for some milliseconds even if there\n"
421                 "are running calls. with this option you can avoid dropping them" },
422         { "ntdebugflags", MISDN_GEN_NTDEBUGFLAGS, MISDN_CTYPE_INT, "0", NONE,
423                 "No description yet."},
424         { "ntdebugfile", MISDN_GEN_NTDEBUGFILE, MISDN_CTYPE_STR, "/var/log/misdn-nt.log", NONE,
425                 "No description yet." }
426 };
427
428
429 /* array of port configs, default is at position 0. */
430 static union misdn_cfg_pt **port_cfg;
431 /* max number of available ports, is set on init */
432 static int max_ports;
433 /* general config */
434 static union misdn_cfg_pt *general_cfg;
435 /* storing the ptp flag separated to save memory */
436 static int *ptp;
437 /* maps enum config elements to array positions */
438 static int *map;
439
440 static ast_mutex_t config_mutex;
441
442 #define CLI_ERROR(name, value, section) ({ \
443         ast_log(LOG_WARNING, "misdn.conf: \"%s=%s\" (section: %s) invalid or out of range. " \
444                 "Please edit your misdn.conf and then do a \"misdn reload\".\n", name, value, section); \
445 })
446
447 static int _enum_array_map (void)
448 {
449         int i, j, ok;
450
451         for (i = MISDN_CFG_FIRST + 1; i < MISDN_CFG_LAST; ++i) {
452                 if (i == MISDN_CFG_PTP)
453                         continue;
454                 ok = 0;
455                 for (j = 0; j < NUM_PORT_ELEMENTS; ++j) {
456                         if (port_spec[j].elem == i) {
457                                 map[i] = j;
458                                 ok = 1;
459                                 break;
460                         }
461                 }
462                 if (!ok) {
463                         ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (port section) has no corresponding element in the config struct!\n", i);
464                         return -1;
465                 }
466         }
467         for (i = MISDN_GEN_FIRST + 1; i < MISDN_GEN_LAST; ++i) {
468                 ok = 0;
469                 for (j = 0; j < NUM_GEN_ELEMENTS; ++j) {
470                         if (gen_spec[j].elem == i) {
471                                 map[i] = j;
472                                 ok = 1;
473                                 break;
474                         }
475                 }
476                 if (!ok) {
477                         ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (general section) has no corresponding element in the config struct!\n", i);
478                         return -1;
479                 }
480         }
481         return 0;
482 }
483
484 static int get_cfg_position (const char *name, int type)
485 {
486         int i;
487
488         switch (type) {
489         case PORT_CFG:
490                 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
491                         if (!strcasecmp(name, port_spec[i].name))
492                                 return i;
493                 }
494                 break;
495         case GEN_CFG:
496                 for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
497                         if (!strcasecmp(name, gen_spec[i].name))
498                                 return i;
499                 }
500         }
501
502         return -1;
503 }
504
505 static inline void misdn_cfg_lock (void)
506 {
507         ast_mutex_lock(&config_mutex);
508 }
509
510 static inline void misdn_cfg_unlock (void)
511 {
512         ast_mutex_unlock(&config_mutex);
513 }
514
515 static void _free_msn_list (struct msn_list* iter)
516 {
517         if (iter->next)
518                 _free_msn_list(iter->next);
519         if (iter->msn)
520                 ast_free(iter->msn);
521         ast_free(iter);
522 }
523
524 static void _free_port_cfg (void)
525 {
526         int i, j;
527         int gn = map[MISDN_CFG_GROUPNAME];
528         union misdn_cfg_pt* free_list[max_ports + 2];
529
530         memset(free_list, 0, sizeof(free_list));
531         free_list[0] = port_cfg[0];
532         for (i = 1; i <= max_ports; ++i) {
533                 if (port_cfg[i][gn].str) {
534                         /* we always have a groupname in the non-default case, so this is fine */
535                         for (j = 1; j <= max_ports; ++j) {
536                                 if (free_list[j] && free_list[j][gn].str == port_cfg[i][gn].str)
537                                         break;
538                                 else if (!free_list[j]) {
539                                         free_list[j] = port_cfg[i];
540                                         break;
541                                 }
542                         }
543                 }
544         }
545         for (j = 0; free_list[j]; ++j) {
546                 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
547                         if (free_list[j][i].any) {
548                                 if (port_spec[i].type == MISDN_CTYPE_MSNLIST) {
549                                         _free_msn_list(free_list[j][i].ml);
550                                 } else if (port_spec[i].type == MISDN_CTYPE_ASTNAMEDGROUP) {
551                                         ast_unref_namedgroups(free_list[j][i].namgrp);
552                                 } else {
553                                         ast_free(free_list[j][i].any);
554                                 }
555                         }
556                 }
557         }
558 }
559
560 static void _free_general_cfg (void)
561 {
562         int i;
563
564         for (i = 0; i < NUM_GEN_ELEMENTS; i++)
565                 if (general_cfg[i].any)
566                         ast_free(general_cfg[i].any);
567 }
568
569 void misdn_cfg_get(int port, enum misdn_cfg_elements elem, void *buf, int bufsize)
570 {
571         int place;
572
573         if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
574                 memset(buf, 0, bufsize);
575                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Port number %d is not valid.\n", port);
576                 return;
577         }
578
579         misdn_cfg_lock();
580         if (elem == MISDN_CFG_PTP) {
581                 if (!memcpy(buf, &ptp[port], (bufsize > ptp[port]) ? sizeof(ptp[port]) : bufsize))
582                         memset(buf, 0, bufsize);
583         } else {
584                 if ((place = map[elem]) < 0) {
585                         memset(buf, 0, bufsize);
586                         ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Invalid element (%d) requested.\n", elem);
587                 } else {
588                         if (elem < MISDN_CFG_LAST) {
589                                 switch (port_spec[place].type) {
590                                 case MISDN_CTYPE_STR:
591                                         if (port_cfg[port][place].str) {
592                                                 ast_copy_string(buf, port_cfg[port][place].str, bufsize);
593                                         } else if (port_cfg[0][place].str) {
594                                                 ast_copy_string(buf, port_cfg[0][place].str, bufsize);
595                                         } else
596                                                 memset(buf, 0, bufsize);
597                                         break;
598                                 case MISDN_CTYPE_ASTNAMEDGROUP:
599                                         if (bufsize >= sizeof(struct ast_namedgroups *)) {
600                                                 if (port_cfg[port][place].namgrp) {
601                                                         *(struct ast_namedgroups **)buf = port_cfg[port][place].namgrp;
602                                                 } else if (port_cfg[0][place].namgrp) {
603                                                         *(struct ast_namedgroups **)buf = port_cfg[0][place].namgrp;
604                                                 } else {
605                                                         *(struct ast_namedgroups **)buf = NULL;
606                                                 }
607                                         }
608                                         break;
609                                 default:
610                                         if (port_cfg[port][place].any)
611                                                 memcpy(buf, port_cfg[port][place].any, bufsize);
612                                         else if (port_cfg[0][place].any)
613                                                 memcpy(buf, port_cfg[0][place].any, bufsize);
614                                         else
615                                                 memset(buf, 0, bufsize);
616                                 }
617                         } else {
618                                 switch (gen_spec[place].type) {
619                                 case MISDN_CTYPE_STR:
620                                         ast_copy_string(buf, S_OR(general_cfg[place].str, ""), bufsize);
621                                         break;
622                                 default:
623                                         if (general_cfg[place].any)
624                                                 memcpy(buf, general_cfg[place].any, bufsize);
625                                         else
626                                                 memset(buf, 0, bufsize);
627                                 }
628                         }
629                 }
630         }
631         misdn_cfg_unlock();
632 }
633
634 enum misdn_cfg_elements misdn_cfg_get_elem(const char *name)
635 {
636         int pos;
637
638         /* here comes a hack to replace the (not existing) "name" element with the "ports" element */
639         if (!strcmp(name, "ports"))
640                 return MISDN_CFG_GROUPNAME;
641         if (!strcmp(name, "name"))
642                 return MISDN_CFG_FIRST;
643
644         pos = get_cfg_position(name, PORT_CFG);
645         if (pos >= 0)
646                 return port_spec[pos].elem;
647
648         pos = get_cfg_position(name, GEN_CFG);
649         if (pos >= 0)
650                 return gen_spec[pos].elem;
651
652         return MISDN_CFG_FIRST;
653 }
654
655 void misdn_cfg_get_name(enum misdn_cfg_elements elem, void *buf, int bufsize)
656 {
657         struct misdn_cfg_spec *spec = NULL;
658         int place = map[elem];
659
660         /* the ptp hack */
661         if (elem == MISDN_CFG_PTP) {
662                 memset(buf, 0, 1);
663                 return;
664         }
665
666         /* here comes a hack to replace the (not existing) "name" element with the "ports" element */
667         if (elem == MISDN_CFG_GROUPNAME) {
668                 if (!snprintf(buf, bufsize, "ports"))
669                         memset(buf, 0, 1);
670                 return;
671         }
672
673         if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
674                 spec = (struct misdn_cfg_spec *)port_spec;
675         else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
676                 spec = (struct misdn_cfg_spec *)gen_spec;
677
678         ast_copy_string(buf, spec ? spec[place].name : "", bufsize);
679 }
680
681 void misdn_cfg_get_desc (enum misdn_cfg_elements elem, void *buf, int bufsize, void *buf_default, int bufsize_default)
682 {
683         int place = map[elem];
684         struct misdn_cfg_spec *spec = NULL;
685
686         /* here comes a hack to replace the (not existing) "name" element with the "ports" element */
687         if (elem == MISDN_CFG_GROUPNAME) {
688                 ast_copy_string(buf, ports_description, bufsize);
689                 if (buf_default && bufsize_default)
690                         memset(buf_default, 0, 1);
691                 return;
692         }
693
694         if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
695                 spec = (struct misdn_cfg_spec *)port_spec;
696         else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
697                 spec = (struct misdn_cfg_spec *)gen_spec;
698
699         if (!spec)
700                 memset(buf, 0, 1);
701         else {
702                 ast_copy_string(buf, spec[place].desc, bufsize);
703                 if (buf_default && bufsize) {
704                         if (!strcmp(spec[place].def, NO_DEFAULT))
705                                 memset(buf_default, 0, 1);
706                         else
707                                 ast_copy_string(buf_default, spec[place].def, bufsize_default);
708                 }
709         }
710 }
711
712 int misdn_cfg_is_msn_valid (int port, char* msn)
713 {
714         int re = 0;
715         struct msn_list *iter;
716
717         if (!misdn_cfg_is_port_valid(port)) {
718                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_is_msn_valid! Port number %d is not valid.\n", port);
719                 return 0;
720         }
721
722         misdn_cfg_lock();
723         if (port_cfg[port][map[MISDN_CFG_MSNS]].ml)
724                 iter = port_cfg[port][map[MISDN_CFG_MSNS]].ml;
725         else
726                 iter = port_cfg[0][map[MISDN_CFG_MSNS]].ml;
727         for (; iter; iter = iter->next)
728                 if (*(iter->msn) == '*' || ast_extension_match(iter->msn, msn)) {
729                         re = 1;
730                         break;
731                 }
732         misdn_cfg_unlock();
733
734         return re;
735 }
736
737 int misdn_cfg_is_port_valid (int port)
738 {
739         int gn = map[MISDN_CFG_GROUPNAME];
740
741         return (port >= 1 && port <= max_ports && port_cfg[port][gn].str);
742 }
743
744 int misdn_cfg_is_group_method (char *group, enum misdn_cfg_method meth)
745 {
746         int i, re = 0;
747         char *method ;
748
749         misdn_cfg_lock();
750
751         method = port_cfg[0][map[MISDN_CFG_METHOD]].str;
752
753         for (i = 1; i <= max_ports; i++) {
754                 if (port_cfg[i] && port_cfg[i][map[MISDN_CFG_GROUPNAME]].str) {
755                         if (!strcasecmp(port_cfg[i][map[MISDN_CFG_GROUPNAME]].str, group))
756                                 method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ?
757                                                   port_cfg[i][map[MISDN_CFG_METHOD]].str : port_cfg[0][map[MISDN_CFG_METHOD]].str);
758                 }
759         }
760
761         if (method) {
762                 switch (meth) {
763                 case METHOD_STANDARD:           re = !strcasecmp(method, "standard");
764                                                                         break;
765                 case METHOD_ROUND_ROBIN:        re = !strcasecmp(method, "round_robin");
766                                                                         break;
767                 case METHOD_STANDARD_DEC:       re = !strcasecmp(method, "standard_dec");
768                                                                         break;
769                 }
770         }
771         misdn_cfg_unlock();
772
773         return re;
774 }
775
776 /*!
777  * \brief Generate a comma separated list of all active ports
778  */
779 void misdn_cfg_get_ports_string (char *ports)
780 {
781         char tmp[16];
782         int l, i;
783         int gn = map[MISDN_CFG_GROUPNAME];
784
785         *ports = 0;
786
787         misdn_cfg_lock();
788         for (i = 1; i <= max_ports; i++) {
789                 if (port_cfg[i][gn].str) {
790                         if (ptp[i])
791                                 sprintf(tmp, "%dptp,", i);
792                         else
793                                 sprintf(tmp, "%d,", i);
794                         strcat(ports, tmp);
795                 }
796         }
797         misdn_cfg_unlock();
798
799         if ((l = strlen(ports))) {
800                 /* Strip trailing ',' */
801                 ports[l-1] = 0;
802         }
803 }
804
805 void misdn_cfg_get_config_string (int port, enum misdn_cfg_elements elem, char* buf, int bufsize)
806 {
807         int place;
808         char tempbuf[BUFFERSIZE] = "";
809         struct msn_list *iter;
810
811         if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
812                 *buf = 0;
813                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Port number %d is not valid.\n", port);
814                 return;
815         }
816
817         place = map[elem];
818
819         misdn_cfg_lock();
820         if (elem == MISDN_CFG_PTP) {
821                 snprintf(buf, bufsize, " -> ptp: %s", ptp[port] ? "yes" : "no");
822         }
823         else if (elem > MISDN_CFG_FIRST && elem < MISDN_CFG_LAST) {
824                 switch (port_spec[place].type) {
825                 case MISDN_CTYPE_INT:
826                 case MISDN_CTYPE_BOOLINT:
827                         if (port_cfg[port][place].num)
828                                 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[port][place].num);
829                         else if (port_cfg[0][place].num)
830                                 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[0][place].num);
831                         else
832                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
833                         break;
834                 case MISDN_CTYPE_BOOL:
835                         if (port_cfg[port][place].num)
836                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[port][place].num ? "yes" : "no");
837                         else if (port_cfg[0][place].num)
838                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[0][place].num ? "yes" : "no");
839                         else
840                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
841                         break;
842                 case MISDN_CTYPE_ASTGROUP:
843                         if (port_cfg[port][place].grp)
844                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name,
845                                                  ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[port][place].grp));
846                         else if (port_cfg[0][place].grp)
847                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name,
848                                                  ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[0][place].grp));
849                         else
850                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
851                         break;
852                 case MISDN_CTYPE_ASTNAMEDGROUP:
853                         if (port_cfg[port][place].namgrp) {
854                                 struct ast_str *tmp_str = ast_str_create(1024);
855                                 if (tmp_str) {
856                                         snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name,
857                                                         ast_print_namedgroups(&tmp_str, port_cfg[port][place].namgrp));
858                                         ast_free(tmp_str);
859                                 }
860                         } else if (port_cfg[0][place].namgrp) {
861                                 struct ast_str *tmp_str = ast_str_create(1024);
862                                 if (tmp_str) {
863                                         snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name,
864                                                         ast_print_namedgroups(&tmp_str, port_cfg[0][place].namgrp));
865                                         ast_free(tmp_str);
866                                 }
867                         } else {
868                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
869                         }
870                         break;
871                 case MISDN_CTYPE_MSNLIST:
872                         if (port_cfg[port][place].ml)
873                                 iter = port_cfg[port][place].ml;
874                         else
875                                 iter = port_cfg[0][place].ml;
876                         if (iter) {
877                                 for (; iter; iter = iter->next) {
878                                         strncat(tempbuf, iter->msn, sizeof(tempbuf) - strlen(tempbuf) - 1);
879                                 }
880                                 if (strlen(tempbuf) > 1) {
881                                         tempbuf[strlen(tempbuf)-2] = 0;
882                                 }
883                         }
884                         snprintf(buf, bufsize, " -> msns: %s", *tempbuf ? tempbuf : "none");
885                         break;
886                 case MISDN_CTYPE_STR:
887                         if ( port_cfg[port][place].str) {
888                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[port][place].str);
889                         } else if (port_cfg[0][place].str) {
890                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[0][place].str);
891                         } else {
892                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
893                         }
894                         break;
895                 }
896         } else if (elem > MISDN_GEN_FIRST && elem < MISDN_GEN_LAST) {
897                 switch (gen_spec[place].type) {
898                 case MISDN_CTYPE_INT:
899                 case MISDN_CTYPE_BOOLINT:
900                         if (general_cfg[place].num)
901                                 snprintf(buf, bufsize, " -> %s: %d", gen_spec[place].name, *general_cfg[place].num);
902                         else
903                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
904                         break;
905                 case MISDN_CTYPE_BOOL:
906                         if (general_cfg[place].num)
907                                 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, *general_cfg[place].num ? "yes" : "no");
908                         else
909                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
910                         break;
911                 case MISDN_CTYPE_STR:
912                         if ( general_cfg[place].str) {
913                                 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, general_cfg[place].str);
914                         } else {
915                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
916                         }
917                         break;
918                 default:
919                         snprintf(buf, bufsize, " -> type of %s not handled yet", gen_spec[place].name);
920                         break;
921                 }
922         } else {
923                 *buf = 0;
924                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Invalid config element (%d) requested.\n", elem);
925         }
926         misdn_cfg_unlock();
927 }
928
929 int misdn_cfg_get_next_port (int port)
930 {
931         int p = -1;
932         int gn = map[MISDN_CFG_GROUPNAME];
933
934         misdn_cfg_lock();
935         for (port++; port <= max_ports; port++) {
936                 if (port_cfg[port][gn].str) {
937                         p = port;
938                         break;
939                 }
940         }
941         misdn_cfg_unlock();
942
943         return p;
944 }
945
946 int misdn_cfg_get_next_port_spin (int port)
947 {
948         int p = misdn_cfg_get_next_port(port);
949         return (p > 0) ? p : misdn_cfg_get_next_port(0);
950 }
951
952 static int _parse (union misdn_cfg_pt *dest, const char *value, enum misdn_cfg_type type, int boolint_def)
953 {
954         int re = 0;
955         int len, tmp;
956         char *valtmp;
957         char *tmp2 = ast_strdupa(value);
958
959         switch (type) {
960         case MISDN_CTYPE_STR:
961                 if (dest->str) {
962                         ast_free(dest->str);
963                 }
964                 if ((len = strlen(value))) {
965                         dest->str = ast_malloc((len + 1) * sizeof(char));
966                         strncpy(dest->str, value, len);
967                         dest->str[len] = 0;
968                 } else {
969                         dest->str = ast_malloc(sizeof(char));
970                         dest->str[0] = 0;
971                 }
972                 break;
973         case MISDN_CTYPE_INT:
974         {
975                 int res;
976
977                 if (strchr(value,'x')) {
978                         res = sscanf(value, "%30x", &tmp);
979                 } else {
980                         res = sscanf(value, "%30d", &tmp);
981                 }
982                 if (res) {
983                         if (!dest->num) {
984                                 dest->num = ast_malloc(sizeof(int));
985                         }
986                         memcpy(dest->num, &tmp, sizeof(int));
987                 } else
988                         re = -1;
989         }
990                 break;
991         case MISDN_CTYPE_BOOL:
992                 if (!dest->num) {
993                         dest->num = ast_malloc(sizeof(int));
994                 }
995                 *(dest->num) = (ast_true(value) ? 1 : 0);
996                 break;
997         case MISDN_CTYPE_BOOLINT:
998                 if (!dest->num) {
999                         dest->num = ast_malloc(sizeof(int));
1000                 }
1001                 if (sscanf(value, "%30d", &tmp)) {
1002                         memcpy(dest->num, &tmp, sizeof(int));
1003                 } else {
1004                         *(dest->num) = (ast_true(value) ? boolint_def : 0);
1005                 }
1006                 break;
1007         case MISDN_CTYPE_MSNLIST:
1008                 for (valtmp = strsep(&tmp2, ","); valtmp; valtmp = strsep(&tmp2, ",")) {
1009                         if ((len = strlen(valtmp))) {
1010                                 struct msn_list *ml = ast_malloc(sizeof(*ml));
1011                                 ml->msn = ast_calloc(len+1, sizeof(char));
1012                                 strncpy(ml->msn, valtmp, len);
1013                                 ml->next = dest->ml;
1014                                 dest->ml = ml;
1015                         }
1016                 }
1017                 break;
1018         case MISDN_CTYPE_ASTGROUP:
1019                 if (!dest->grp) {
1020                         dest->grp = ast_malloc(sizeof(ast_group_t));
1021                 }
1022                 *(dest->grp) = ast_get_group(value);
1023                 break;
1024         case MISDN_CTYPE_ASTNAMEDGROUP:
1025                 dest->namgrp = ast_get_namedgroups(value);
1026                 break;
1027         }
1028
1029         return re;
1030 }
1031
1032 static void _build_general_config (struct ast_variable *v)
1033 {
1034         int pos;
1035
1036         for (; v; v = v->next) {
1037                 if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
1038                         continue;
1039                 if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) ||
1040                         (_parse(&general_cfg[pos], v->value, gen_spec[pos].type, gen_spec[pos].boolint_def) < 0))
1041                         CLI_ERROR(v->name, v->value, "general");
1042         }
1043 }
1044
1045 static void _build_port_config (struct ast_variable *v, char *cat)
1046 {
1047         int pos, i;
1048         union misdn_cfg_pt cfg_tmp[NUM_PORT_ELEMENTS];
1049         int cfg_for_ports[max_ports + 1];
1050
1051         if (!v || !cat)
1052                 return;
1053
1054         memset(cfg_tmp, 0, sizeof(cfg_tmp));
1055         memset(cfg_for_ports, 0, sizeof(cfg_for_ports));
1056
1057         if (!strcasecmp(cat, "default")) {
1058                 cfg_for_ports[0] = 1;
1059         }
1060
1061         if (((pos = get_cfg_position("name", PORT_CFG)) < 0) ||
1062                 (_parse(&cfg_tmp[pos], cat, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) {
1063                 CLI_ERROR(v->name, v->value, cat);
1064                 return;
1065         }
1066
1067         for (; v; v = v->next) {
1068                 if (!strcasecmp(v->name, "ports")) {
1069                         char *token, *tmp = ast_strdupa(v->value);
1070                         char ptpbuf[BUFFERSIZE] = "";
1071                         int start, end;
1072                         for (token = strsep(&tmp, ","); token; token = strsep(&tmp, ","), *ptpbuf = 0) {
1073                                 if (!*token)
1074                                         continue;
1075                                 if (sscanf(token, "%30d-%30d%511s", &start, &end, ptpbuf) >= 2) {
1076                                         for (; start <= end; start++) {
1077                                                 if (start <= max_ports && start > 0) {
1078                                                         cfg_for_ports[start] = 1;
1079                                                         ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
1080                                                 } else
1081                                                         CLI_ERROR(v->name, v->value, cat);
1082                                         }
1083                                 } else {
1084                                         if (sscanf(token, "%30d%511s", &start, ptpbuf)) {
1085                                                 if (start <= max_ports && start > 0) {
1086                                                         cfg_for_ports[start] = 1;
1087                                                         ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
1088                                                 } else
1089                                                         CLI_ERROR(v->name, v->value, cat);
1090                                         } else
1091                                                 CLI_ERROR(v->name, v->value, cat);
1092                                 }
1093                         }
1094                 } else {
1095                         if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) ||
1096                                 (_parse(&cfg_tmp[pos], v->value, port_spec[pos].type, port_spec[pos].boolint_def) < 0))
1097                                 CLI_ERROR(v->name, v->value, cat);
1098                 }
1099         }
1100
1101         for (i = 0; i < (max_ports + 1); ++i) {
1102                 if (i > 0 && cfg_for_ports[0]) {
1103                         /* default category, will populate the port_cfg with additional port
1104                         categories in subsequent calls to this function */
1105                         memset(cfg_tmp, 0, sizeof(cfg_tmp));
1106                 }
1107                 if (cfg_for_ports[i]) {
1108                         memcpy(port_cfg[i], cfg_tmp, sizeof(cfg_tmp));
1109                 }
1110         }
1111 }
1112
1113 void misdn_cfg_update_ptp (void)
1114 {
1115 #ifndef MISDN_1_2
1116         char misdn_init[BUFFERSIZE];
1117         char line[BUFFERSIZE];
1118         FILE *fp;
1119         char *tok, *p, *end;
1120         int port;
1121
1122         misdn_cfg_get(0, MISDN_GEN_MISDN_INIT, &misdn_init, sizeof(misdn_init));
1123
1124         if (!ast_strlen_zero(misdn_init)) {
1125                 fp = fopen(misdn_init, "r");
1126                 if (fp) {
1127                         while(fgets(line, sizeof(line), fp)) {
1128                                 if (!strncmp(line, "nt_ptp", 6)) {
1129                                         for (tok = strtok_r(line,",=", &p);
1130                                                  tok;
1131                                                  tok = strtok_r(NULL,",=", &p)) {
1132                                                 port = strtol(tok, &end, 10);
1133                                                 if (end != tok && misdn_cfg_is_port_valid(port)) {
1134                                                         misdn_cfg_lock();
1135                                                         ptp[port] = 1;
1136                                                         misdn_cfg_unlock();
1137                                                 }
1138                                         }
1139                                 }
1140                         }
1141                         fclose(fp);
1142                 } else {
1143                         ast_log(LOG_WARNING,"Couldn't open %s: %s\n", misdn_init, strerror(errno));
1144                 }
1145         }
1146 #else
1147         int i;
1148         int proto;
1149         char filename[128];
1150         FILE *fp;
1151
1152         for (i = 1; i <= max_ports; ++i) {
1153                 snprintf(filename, sizeof(filename), "/sys/class/mISDN-stacks/st-%08x/protocol", i << 8);
1154                 fp = fopen(filename, "r");
1155                 if (!fp) {
1156                         ast_log(LOG_WARNING, "Could not open %s: %s\n", filename, strerror(errno));
1157                         continue;
1158                 }
1159                 if (fscanf(fp, "0x%08x", &proto) != 1)
1160                         ast_log(LOG_WARNING, "Could not parse contents of %s!\n", filename);
1161                 else
1162                         ptp[i] = proto & 1<<5 ? 1 : 0;
1163                 fclose(fp);
1164         }
1165 #endif
1166 }
1167
1168 static void _fill_defaults (void)
1169 {
1170         int i;
1171
1172         for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
1173                 if (!port_cfg[0][i].any && strcasecmp(port_spec[i].def, NO_DEFAULT))
1174                         _parse(&(port_cfg[0][i]), (char *)port_spec[i].def, port_spec[i].type, port_spec[i].boolint_def);
1175         }
1176         for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
1177                 if (!general_cfg[i].any && strcasecmp(gen_spec[i].def, NO_DEFAULT))
1178                         _parse(&(general_cfg[i]), (char *)gen_spec[i].def, gen_spec[i].type, gen_spec[i].boolint_def);
1179         }
1180 }
1181
1182 void misdn_cfg_reload (void)
1183 {
1184         misdn_cfg_init(0, 1);
1185 }
1186
1187 void misdn_cfg_destroy (void)
1188 {
1189         misdn_cfg_lock();
1190
1191         _free_port_cfg();
1192         _free_general_cfg();
1193
1194         ast_free(port_cfg);
1195         ast_free(general_cfg);
1196         ast_free(ptp);
1197         ast_free(map);
1198
1199         misdn_cfg_unlock();
1200         ast_mutex_destroy(&config_mutex);
1201 }
1202
1203 int misdn_cfg_init(int this_max_ports, int reload)
1204 {
1205         char config[] = "misdn.conf";
1206         char *cat, *p;
1207         int i;
1208         struct ast_config *cfg;
1209         struct ast_variable *v;
1210         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1211
1212         if (!(cfg = ast_config_load2(config, "chan_misdn", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
1213                 ast_log(LOG_WARNING, "missing or invalid file: misdn.conf\n");
1214                 return -1;
1215         } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
1216                 return 0;
1217
1218         ast_mutex_init(&config_mutex);
1219
1220         /* Copy the default jb config over global_jbconf */
1221         memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
1222
1223         misdn_cfg_lock();
1224
1225         if (this_max_ports) {
1226                 /* this is the first run */
1227                 max_ports = this_max_ports;
1228                 map = ast_calloc(MISDN_GEN_LAST + 1, sizeof(int));
1229                 if (_enum_array_map())
1230                         return -1;
1231                 p = ast_calloc(1, (max_ports + 1) * sizeof(union misdn_cfg_pt *)
1232                                                    + (max_ports + 1) * NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt));
1233                 port_cfg = (union misdn_cfg_pt **)p;
1234                 p += (max_ports + 1) * sizeof(union misdn_cfg_pt *);
1235                 for (i = 0; i <= max_ports; ++i) {
1236                         port_cfg[i] = (union misdn_cfg_pt *)p;
1237                         p += NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt);
1238                 }
1239                 general_cfg = ast_calloc(1, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1240                 ptp = ast_calloc(max_ports + 1, sizeof(int));
1241         }
1242         else {
1243                 /* misdn reload */
1244                 _free_port_cfg();
1245                 _free_general_cfg();
1246                 memset(port_cfg[0], 0, NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt) * (max_ports + 1));
1247                 memset(general_cfg, 0, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1248                 memset(ptp, 0, sizeof(int) * (max_ports + 1));
1249         }
1250
1251         cat = ast_category_browse(cfg, NULL);
1252
1253         while(cat) {
1254                 v = ast_variable_browse(cfg, cat);
1255                 if (!strcasecmp(cat, "general")) {
1256                         _build_general_config(v);
1257                 } else {
1258                         _build_port_config(v, cat);
1259                 }
1260                 cat = ast_category_browse(cfg, cat);
1261         }
1262
1263         _fill_defaults();
1264
1265         misdn_cfg_unlock();
1266         ast_config_destroy(cfg);
1267
1268         return 0;
1269 }
1270
1271 struct ast_jb_conf *misdn_get_global_jbconf() {
1272         return &global_jbconf;
1273 }