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