f9450c07b2488758d15ecdc5827db2e6f0f40cdc
[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         { "msns", MISDN_CFG_MSNS, MISDN_CTYPE_MSNLIST, "*", NONE,
318                 "MSN's for TE ports, listen on those numbers on the above ports, and\n"
319                 "\tindicate the incoming calls to Asterisk.\n"
320                 "\tHere you can give a comma seperated list, or simply an '*' for any msn." },
321 };
322
323 static const struct misdn_cfg_spec gen_spec[] = {
324         { "debug", MISDN_GEN_DEBUG, MISDN_CTYPE_INT, "0", NONE,
325                 "Sets the debugging flag:\n"
326                 "\t0 - No Debug\n"
327                 "\t1 - mISDN Messages and * - Messages, and * - State changes\n"
328                 "\t2 - Messages + Message specific Informations (e.g. bearer capability)\n"
329                 "\t3 - very Verbose, the above + lots of Driver specific infos\n"
330                 "\t4 - even more Verbose than 3" },
331 #ifndef MISDN_1_2
332         { "misdn_init", MISDN_GEN_MISDN_INIT, MISDN_CTYPE_STR, "/etc/misdn-init.conf", NONE,
333                 "Set the path to the misdn-init.conf (for nt_ptp mode checking)." },
334 #endif
335         { "tracefile", MISDN_GEN_TRACEFILE, MISDN_CTYPE_STR, "/var/log/asterisk/misdn.log", NONE,
336                 "Set the path to the massively growing trace file, if you want that." },
337         { "bridging", MISDN_GEN_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
338                 "Set this to yes if you want mISDN_dsp to bridge the calls in HW." },
339         { "stop_tone_after_first_digit", MISDN_GEN_STOP_TONE, MISDN_CTYPE_BOOL, "yes", NONE,
340                 "Stops dialtone after getting first digit on NT Port." },
341         { "append_digits2exten", MISDN_GEN_APPEND_DIGITS2EXTEN, MISDN_CTYPE_BOOL, "yes", NONE,
342                 "Wether to append overlapdialed Digits to Extension or not." },
343         { "dynamic_crypt", MISDN_GEN_DYNAMIC_CRYPT, MISDN_CTYPE_BOOL, "no", NONE,
344                 "Wether to look out for dynamic crypting attempts." },
345         { "crypt_prefix", MISDN_GEN_CRYPT_PREFIX, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
346                 "What is used for crypting Protocol." },
347         { "crypt_keys", MISDN_GEN_CRYPT_KEYS, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
348                 "Keys for cryption, you reference them in the dialplan\n"
349                 "\tLater also in dynamic encr." },
350         { "ntdebugflags", MISDN_GEN_NTDEBUGFLAGS, MISDN_CTYPE_INT, "0", NONE,
351                 "No description yet."},
352         { "ntdebugfile", MISDN_GEN_NTDEBUGFILE, MISDN_CTYPE_STR, "/var/log/misdn-nt.log", NONE,
353                 "No description yet." }
354 };
355
356
357 /* array of port configs, default is at position 0. */
358 static union misdn_cfg_pt **port_cfg;
359 /* max number of available ports, is set on init */
360 static int max_ports;
361 /* general config */
362 static union misdn_cfg_pt *general_cfg;
363 /* storing the ptp flag separated to save memory */
364 static int *ptp;
365 /* maps enum config elements to array positions */
366 static int *map;
367
368 static ast_mutex_t config_mutex; 
369
370 #define CLI_ERROR(name, value, section) ({ \
371         ast_log(LOG_WARNING, "misdn.conf: \"%s=%s\" (section: %s) invalid or out of range. " \
372                 "Please edit your misdn.conf and then do a \"misdn reload\".\n", name, value, section); \
373 })
374
375 static int _enum_array_map (void)
376 {
377         int i, j, ok;
378
379         for (i = MISDN_CFG_FIRST + 1; i < MISDN_CFG_LAST; ++i) {
380                 if (i == MISDN_CFG_PTP)
381                         continue;
382                 ok = 0;
383                 for (j = 0; j < NUM_PORT_ELEMENTS; ++j) {
384                         if (port_spec[j].elem == i) {
385                                 map[i] = j;
386                                 ok = 1;
387                                 break;
388                         }
389                 }
390                 if (!ok) {
391                         ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (port section) has no corresponding element in the config struct!\n", i);
392                         return -1;
393                 }
394         }
395         for (i = MISDN_GEN_FIRST + 1; i < MISDN_GEN_LAST; ++i) {
396                 ok = 0;
397                 for (j = 0; j < NUM_GEN_ELEMENTS; ++j) {
398                         if (gen_spec[j].elem == i) {
399                                 map[i] = j;
400                                 ok = 1;
401                                 break;
402                         }
403                 }
404                 if (!ok) {
405                         ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (general section) has no corresponding element in the config struct!\n", i);
406                         return -1;
407                 }
408         }
409         return 0;
410 }
411
412 static int get_cfg_position (char *name, int type)
413 {
414         int i;
415
416         switch (type) {
417         case PORT_CFG:
418                 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
419                         if (!strcasecmp(name, port_spec[i].name))
420                                 return i;
421                 }
422                 break;
423         case GEN_CFG:
424                 for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
425                         if (!strcasecmp(name, gen_spec[i].name))
426                                 return i;
427                 }
428         }
429
430         return -1;
431 }
432
433 static inline void misdn_cfg_lock (void)
434 {
435         ast_mutex_lock(&config_mutex);
436 }
437
438 static inline void misdn_cfg_unlock (void)
439 {
440         ast_mutex_unlock(&config_mutex);
441 }
442
443 static void _free_msn_list (struct msn_list* iter)
444 {
445         if (iter->next)
446                 _free_msn_list(iter->next);
447         if (iter->msn)
448                 free(iter->msn);
449         free(iter);
450 }
451
452 static void _free_port_cfg (void)
453 {
454         int i, j;
455         int gn = map[MISDN_CFG_GROUPNAME];
456         union misdn_cfg_pt* free_list[max_ports + 2];
457         
458         memset(free_list, 0, sizeof(free_list));
459         free_list[0] = port_cfg[0];
460         for (i = 1; i <= max_ports; ++i) {
461                 if (port_cfg[i][gn].str) {
462                         /* we always have a groupname in the non-default case, so this is fine */
463                         for (j = 1; j <= max_ports; ++j) {
464                                 if (free_list[j] && free_list[j][gn].str == port_cfg[i][gn].str)
465                                         break;
466                                 else if (!free_list[j]) {
467                                         free_list[j] = port_cfg[i];
468                                         break;
469                                 }
470                         }
471                 }
472         }
473         for (j = 0; free_list[j]; ++j) {
474                 for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
475                         if (free_list[j][i].any) {
476                                 if (port_spec[i].type == MISDN_CTYPE_MSNLIST)
477                                         _free_msn_list(free_list[j][i].ml);
478                                 else
479                                         free(free_list[j][i].any);
480                         }
481                 }
482         }
483 }
484
485 static void _free_general_cfg (void)
486 {
487         int i;
488
489         for (i = 0; i < NUM_GEN_ELEMENTS; i++) 
490                 if (general_cfg[i].any)
491                         free(general_cfg[i].any);
492 }
493
494 void misdn_cfg_get (int port, enum misdn_cfg_elements elem, void *buf, int bufsize)
495 {
496         int place;
497
498         if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
499                 memset(buf, 0, bufsize);
500                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Port number %d is not valid.\n", port);
501                 return;
502         }
503
504         misdn_cfg_lock();
505         if (elem == MISDN_CFG_PTP) {
506                 if (!memcpy(buf, &ptp[port], (bufsize > ptp[port]) ? sizeof(ptp[port]) : bufsize))
507                         memset(buf, 0, bufsize);
508         } else {
509                 if ((place = map[elem]) < 0) {
510                         memset (buf, 0, bufsize);
511                         ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Invalid element (%d) requested.\n", elem);
512                 } else {
513                         if (elem < MISDN_CFG_LAST) {
514                                 switch (port_spec[place].type) {
515                                 case MISDN_CTYPE_STR:
516                                         if (port_cfg[port][place].str) {
517                                                 if (!memccpy(buf, port_cfg[port][place].str, 0, bufsize))
518                                                         memset(buf, 0, 1);
519                                         } else if (port_cfg[0][place].str) {
520                                                 if (!memccpy(buf, port_cfg[0][place].str, 0, bufsize))
521                                                         memset(buf, 0, 1);
522                                         }
523                                         break;
524                                 default:
525                                         if (port_cfg[port][place].any)
526                                                 memcpy(buf, port_cfg[port][place].any, bufsize);
527                                         else if (port_cfg[0][place].any)
528                                                 memcpy(buf, port_cfg[0][place].any, bufsize);
529                                         else
530                                                 memset(buf, 0, bufsize);
531                                 }
532                         } else {
533                                 switch (gen_spec[place].type) {
534                                 case MISDN_CTYPE_STR:
535                                         if (!general_cfg[place].str || !memccpy(buf, general_cfg[place].str, 0, bufsize))
536                                                 memset(buf, 0, 1);
537                                         break;
538                                 default:
539                                         if (general_cfg[place].any)
540                                                 memcpy(buf, general_cfg[place].any, bufsize);
541                                         else
542                                                 memset(buf, 0, bufsize);
543                                 }
544                         }
545                 }
546         }
547         misdn_cfg_unlock();
548 }
549
550 enum misdn_cfg_elements misdn_cfg_get_elem (char *name)
551 {
552         int pos;
553
554         /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
555         if (!strcmp(name, "ports"))
556                 return MISDN_CFG_GROUPNAME;
557         if (!strcmp(name, "name"))
558                 return MISDN_CFG_FIRST;
559
560         pos = get_cfg_position (name, PORT_CFG);
561         if (pos >= 0)
562                 return port_spec[pos].elem;
563         
564         pos = get_cfg_position (name, GEN_CFG);
565         if (pos >= 0)
566                 return gen_spec[pos].elem;
567         
568         return MISDN_CFG_FIRST;
569 }
570
571 void misdn_cfg_get_name (enum misdn_cfg_elements elem, void *buf, int bufsize)
572 {
573         struct misdn_cfg_spec *spec = NULL;
574         int place = map[elem];
575
576         /* the ptp hack */
577         if (elem == MISDN_CFG_PTP) {
578                 memset(buf, 0, 1);
579                 return;
580         }
581         
582         /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
583         if (elem == MISDN_CFG_GROUPNAME) {
584                 if (!snprintf(buf, bufsize, "ports"))
585                         memset(buf, 0, 1);
586                 return;
587         }
588         
589         if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
590                 spec = (struct misdn_cfg_spec *)port_spec;
591         else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
592                 spec = (struct misdn_cfg_spec *)gen_spec;
593
594         if (!spec || !memccpy(buf, spec[place].name, 0, bufsize))
595                 memset(buf, 0, 1);
596 }
597
598 void misdn_cfg_get_desc (enum misdn_cfg_elements elem, void *buf, int bufsize, void *buf_default, int bufsize_default)
599 {
600         int place = map[elem];
601         struct misdn_cfg_spec *spec = NULL;
602
603         /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
604         if (elem == MISDN_CFG_GROUPNAME) {
605                 if (!memccpy(buf, ports_description, 0, bufsize))
606                         memset(buf, 0, 1);
607                 if (buf_default && bufsize_default)
608                         memset(buf_default, 0, 1);
609                 return;
610         }
611
612         if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
613                 spec = (struct misdn_cfg_spec *)port_spec;
614         else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
615                 spec = (struct misdn_cfg_spec *)gen_spec;
616                 
617         if (!spec || !spec[place].desc)
618                 memset(buf, 0, 1);
619         else {
620                 if (!memccpy(buf, spec[place].desc, 0, bufsize))
621                         memset(buf, 0, 1);
622                 if (buf_default && bufsize) {
623                         if (!strcmp(spec[place].def, NO_DEFAULT))
624                                 memset(buf_default, 0, 1);
625                         else if (!memccpy(buf_default, spec[place].def, 0, bufsize_default))
626                                 memset(buf_default, 0, 1);
627                 }
628         }
629 }
630
631 int misdn_cfg_is_msn_valid (int port, char* msn)
632 {
633         int re = 0;
634         struct msn_list *iter;
635
636         if (!misdn_cfg_is_port_valid(port)) {
637                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_is_msn_valid! Port number %d is not valid.\n", port);
638                 return 0;
639         }
640
641         misdn_cfg_lock();
642         if (port_cfg[port][map[MISDN_CFG_MSNS]].ml)
643                 iter = port_cfg[port][map[MISDN_CFG_MSNS]].ml;
644         else
645                 iter = port_cfg[0][map[MISDN_CFG_MSNS]].ml;
646         for (; iter; iter = iter->next) 
647                 if (*(iter->msn) == '*' || ast_extension_match(iter->msn, msn)) {
648                         re = 1;
649                         break;
650                 }
651         misdn_cfg_unlock();
652
653         return re;
654 }
655
656 int misdn_cfg_is_port_valid (int port)
657 {
658         int gn = map[MISDN_CFG_GROUPNAME];
659
660         return (port >= 1 && port <= max_ports && port_cfg[port][gn].str);
661 }
662
663 int misdn_cfg_is_group_method (char *group, enum misdn_cfg_method meth)
664 {
665         int i, re = 0;
666         char *method ;
667
668         misdn_cfg_lock();
669
670         method = port_cfg[0][map[MISDN_CFG_METHOD]].str;
671
672         for (i = 1; i <= max_ports; i++) {
673                 if (port_cfg[i] && port_cfg[i][map[MISDN_CFG_GROUPNAME]].str) {
674                         if (!strcasecmp(port_cfg[i][map[MISDN_CFG_GROUPNAME]].str, group))
675                                 method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ? 
676                                                   port_cfg[i][map[MISDN_CFG_METHOD]].str : port_cfg[0][map[MISDN_CFG_METHOD]].str);
677                 }
678         }
679
680         if (method) {
681                 switch (meth) {
682                 case METHOD_STANDARD:           re = !strcasecmp(method, "standard");
683                                                                         break;
684                 case METHOD_ROUND_ROBIN:        re = !strcasecmp(method, "round_robin");
685                                                                         break;
686                 case METHOD_STANDARD_DEC:       re = !strcasecmp(method, "standard_dec");
687                                                                         break;
688                 }
689         }
690         misdn_cfg_unlock();
691
692         return re;
693 }
694
695 void misdn_cfg_get_ports_string (char *ports)
696 {
697         char tmp[16];
698         int l, i;
699         int gn = map[MISDN_CFG_GROUPNAME];
700
701         *ports = 0;
702
703         misdn_cfg_lock();
704         for (i = 1; i <= max_ports; i++) {
705                 if (port_cfg[i][gn].str) {
706                         if (ptp[i])
707                                 sprintf(tmp, "%dptp,", i);
708                         else
709                                 sprintf(tmp, "%d,", i);
710                         strcat(ports, tmp);
711                 }
712         }
713         misdn_cfg_unlock();
714
715         if ((l = strlen(ports)))
716                 ports[l-1] = 0;
717 }
718
719 void misdn_cfg_get_config_string (int port, enum misdn_cfg_elements elem, char* buf, int bufsize)
720 {
721         int place;
722         char tempbuf[BUFFERSIZE] = "";
723         struct msn_list *iter;
724
725         if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
726                 *buf = 0;
727                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Port number %d is not valid.\n", port);
728                 return;
729         }
730
731         place = map[elem];
732
733         misdn_cfg_lock();
734         if (elem == MISDN_CFG_PTP) {
735                 snprintf(buf, bufsize, " -> ptp: %s", ptp[port] ? "yes" : "no");
736         }
737         else if (elem > MISDN_CFG_FIRST && elem < MISDN_CFG_LAST) {
738                 switch (port_spec[place].type) {
739                 case MISDN_CTYPE_INT:
740                 case MISDN_CTYPE_BOOLINT:
741                         if (port_cfg[port][place].num)
742                                 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[port][place].num);
743                         else if (port_cfg[0][place].num)
744                                 snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[0][place].num);
745                         else
746                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
747                         break;
748                 case MISDN_CTYPE_BOOL:
749                         if (port_cfg[port][place].num)
750                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[port][place].num ? "yes" : "no");
751                         else if (port_cfg[0][place].num)
752                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[0][place].num ? "yes" : "no");
753                         else
754                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
755                         break;
756                 case MISDN_CTYPE_ASTGROUP:
757                         if (port_cfg[port][place].grp)
758                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, 
759                                                  ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[port][place].grp));
760                         else if (port_cfg[0][place].grp)
761                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, 
762                                                  ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[0][place].grp));
763                         else
764                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
765                         break;
766                 case MISDN_CTYPE_MSNLIST:
767                         if (port_cfg[port][place].ml)
768                                 iter = port_cfg[port][place].ml;
769                         else
770                                 iter = port_cfg[0][place].ml;
771                         if (iter) {
772                                 for (; iter; iter = iter->next)
773                                         sprintf(tempbuf, "%s%s, ", tempbuf, iter->msn);
774                                 tempbuf[strlen(tempbuf)-2] = 0;
775                         }
776                         snprintf(buf, bufsize, " -> msns: %s", *tempbuf ? tempbuf : "none");
777                         break;
778                 case MISDN_CTYPE_STR:
779                         if ( port_cfg[port][place].str) {
780                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[port][place].str);
781                         } else if (port_cfg[0][place].str) {
782                                 snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[0][place].str);
783                         } else {
784                                 snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
785                         }
786                         break;
787                 }
788         } else if (elem > MISDN_GEN_FIRST && elem < MISDN_GEN_LAST) {
789                 switch (gen_spec[place].type) {
790                 case MISDN_CTYPE_INT:
791                 case MISDN_CTYPE_BOOLINT:
792                         if (general_cfg[place].num)
793                                 snprintf(buf, bufsize, " -> %s: %d", gen_spec[place].name, *general_cfg[place].num);
794                         else
795                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
796                         break;
797                 case MISDN_CTYPE_BOOL:
798                         if (general_cfg[place].num)
799                                 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, *general_cfg[place].num ? "yes" : "no");
800                         else
801                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
802                         break;
803                 case MISDN_CTYPE_STR:
804                         if ( general_cfg[place].str) {
805                                 snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, general_cfg[place].str);
806                         } else {
807                                 snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
808                         }
809                         break;
810                 default:
811                         snprintf(buf, bufsize, " -> type of %s not handled yet", gen_spec[place].name);
812                         break;
813                 }
814         } else {
815                 *buf = 0;
816                 ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Invalid config element (%d) requested.\n", elem);
817         }
818         misdn_cfg_unlock();
819 }
820
821 int misdn_cfg_get_next_port (int port)
822 {
823         int p = -1;
824         int gn = map[MISDN_CFG_GROUPNAME];
825         
826         misdn_cfg_lock();
827         for (port++; port <= max_ports; port++) {
828                 if (port_cfg[port][gn].str) {
829                         p = port;
830                         break;
831                 }
832         }
833         misdn_cfg_unlock();
834
835         return p;
836 }
837
838 int misdn_cfg_get_next_port_spin (int port)
839 {
840         int p = misdn_cfg_get_next_port(port);
841         return (p > 0) ? p : misdn_cfg_get_next_port(0);
842 }
843
844 static int _parse (union misdn_cfg_pt *dest, char *value, enum misdn_cfg_type type, int boolint_def)
845 {
846         int re = 0;
847         int len, tmp;
848         char *valtmp;
849
850         switch (type) {
851         case MISDN_CTYPE_STR:
852                 if ((len = strlen(value))) {
853                         dest->str = (char *)malloc((len + 1) * sizeof(char));
854                         strncpy(dest->str, value, len);
855                         dest->str[len] = 0;
856                 } else {
857                         dest->str = (char *)malloc( sizeof(char));
858                         dest->str[0] = 0;
859                 }
860                 break;
861         case MISDN_CTYPE_INT:
862         {
863                 char *pat;
864                 if (strchr(value,'x')) 
865                         pat="%x";
866                 else
867                         pat="%d";
868                 if (sscanf(value, pat, &tmp)) {
869                         dest->num = (int *)malloc(sizeof(int));
870                         memcpy(dest->num, &tmp, sizeof(int));
871                 } else
872                         re = -1;
873         }
874                 break;
875         case MISDN_CTYPE_BOOL:
876                 dest->num = (int *)malloc(sizeof(int));
877                 *(dest->num) = (ast_true(value) ? 1 : 0);
878                 break;
879         case MISDN_CTYPE_BOOLINT:
880                 dest->num = (int *)malloc(sizeof(int));
881                 if (sscanf(value, "%d", &tmp)) {
882                         memcpy(dest->num, &tmp, sizeof(int));
883                 } else {
884                         *(dest->num) = (ast_true(value) ? boolint_def : 0);
885                 }
886                 break;
887         case MISDN_CTYPE_MSNLIST:
888                 for (valtmp = strsep(&value, ","); valtmp; valtmp = strsep(&value, ",")) {
889                         if ((len = strlen(valtmp))) {
890                                 struct msn_list *ml = (struct msn_list *)malloc(sizeof(struct msn_list));
891                                 ml->msn = (char *)calloc(len+1, sizeof(char));
892                                 strncpy(ml->msn, valtmp, len);
893                                 ml->next = dest->ml;
894                                 dest->ml = ml;
895                         }
896                 }
897                 break;
898         case MISDN_CTYPE_ASTGROUP:
899                 dest->grp = (ast_group_t *)malloc(sizeof(ast_group_t));
900                 *(dest->grp) = ast_get_group(value);
901                 break;
902         }
903
904         return re;
905 }
906
907 static void _build_general_config (struct ast_variable *v)
908 {
909         int pos;
910
911         for (; v; v = v->next) {
912                 if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) || 
913                         (_parse(&general_cfg[pos], v->value, gen_spec[pos].type, gen_spec[pos].boolint_def) < 0))
914                         CLI_ERROR(v->name, v->value, "general");
915         }
916 }
917
918 static void _build_port_config (struct ast_variable *v, char *cat)
919 {
920         int pos, i;
921         union misdn_cfg_pt cfg_tmp[NUM_PORT_ELEMENTS];
922         int cfg_for_ports[max_ports + 1];
923
924         if (!v || !cat)
925                 return;
926
927         memset(cfg_tmp, 0, sizeof(cfg_tmp));
928         memset(cfg_for_ports, 0, sizeof(cfg_for_ports));
929
930         if (!strcasecmp(cat, "default")) {
931                 cfg_for_ports[0] = 1;
932         }
933
934         if (((pos = get_cfg_position("name", PORT_CFG)) < 0) || 
935                 (_parse(&cfg_tmp[pos], cat, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) {
936                 CLI_ERROR(v->name, v->value, cat);
937                 return;
938         }
939
940         for (; v; v = v->next) {
941                 if (!strcasecmp(v->name, "ports")) {
942                         char *token;
943                         char ptpbuf[BUFFERSIZE] = "";
944                         int start, end;
945                         for (token = strsep(&v->value, ","); token; token = strsep(&v->value, ","), *ptpbuf = 0) { 
946                                 if (!*token)
947                                         continue;
948                                 if (sscanf(token, "%d-%d%s", &start, &end, ptpbuf) >= 2) {
949                                         for (; start <= end; start++) {
950                                                 if (start <= max_ports && start > 0) {
951                                                         cfg_for_ports[start] = 1;
952                                                         ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
953                                                 } else
954                                                         CLI_ERROR(v->name, v->value, cat);
955                                         }
956                                 } else {
957                                         if (sscanf(token, "%d%s", &start, ptpbuf)) {
958                                                 if (start <= max_ports && start > 0) {
959                                                         cfg_for_ports[start] = 1;
960                                                         ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
961                                                 } else
962                                                         CLI_ERROR(v->name, v->value, cat);
963                                         } else
964                                                 CLI_ERROR(v->name, v->value, cat);
965                                 }
966                         }
967                 } else {
968                         if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) || 
969                                 (_parse(&cfg_tmp[pos], v->value, port_spec[pos].type, port_spec[pos].boolint_def) < 0))
970                                 CLI_ERROR(v->name, v->value, cat);
971                 }
972         }
973
974         for (i = 0; i < (max_ports + 1); ++i) {
975                 if (cfg_for_ports[i]) {
976                         memcpy(port_cfg[i], cfg_tmp, sizeof(cfg_tmp));
977                 }
978         }
979 }
980
981 void misdn_cfg_update_ptp (void)
982 {
983 #ifndef MISDN_1_2
984         char misdn_init[BUFFERSIZE];
985         char line[BUFFERSIZE];
986         FILE *fp;
987         char *tok, *p, *end;
988         int port;
989
990         misdn_cfg_get(0, MISDN_GEN_MISDN_INIT, &misdn_init, sizeof(misdn_init));
991
992         if (misdn_init) {
993                 fp = fopen(misdn_init, "r");
994                 if (fp) {
995                         while(fgets(line, sizeof(line), fp)) {
996                                 if (!strncmp(line, "nt_ptp", 6)) {
997                                         for (tok = strtok_r(line,",=", &p);
998                                                  tok;
999                                                  tok = strtok_r(NULL,",=", &p)) {
1000                                                 port = strtol(tok, &end, 10);
1001                                                 if (end != tok && misdn_cfg_is_port_valid(port)) {
1002                                                         misdn_cfg_lock();
1003                                                         ptp[port] = 1;
1004                                                         misdn_cfg_unlock();
1005                                                 }
1006                                         }
1007                                 }
1008                         }
1009                         fclose(fp);
1010                 } else {
1011                         ast_log(LOG_WARNING,"Couldn't open %s: %s\n", misdn_init, strerror(errno));
1012                 }
1013         }
1014 #else
1015         int i;
1016         int proto;
1017         char filename[128];
1018         FILE *fp;
1019
1020         for (i = 1; i <= max_ports; ++i) {
1021                 snprintf(filename, sizeof(filename), "/sys/class/mISDN-stacks/st-%08x/protocol", i << 8);
1022                 fp = fopen(filename, "r");
1023                 if (!fp) {
1024                         ast_log(LOG_WARNING, "Could not open %s: %s\n", filename, strerror(errno));
1025                         continue;
1026                 }
1027                 if (fscanf(fp, "0x%08x", &proto) != 1)
1028                         ast_log(LOG_WARNING, "Could not parse contents of %s!\n", filename);
1029                 else
1030                         ptp[i] = proto & 1<<5 ? 1 : 0;
1031                 fclose(fp);
1032         }
1033 #endif
1034 }
1035
1036 static void _fill_defaults (void)
1037 {
1038         int i;
1039
1040         for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
1041                 if (!port_cfg[0][i].any && strcasecmp(port_spec[i].def, NO_DEFAULT))
1042                         _parse(&(port_cfg[0][i]), (char *)port_spec[i].def, port_spec[i].type, port_spec[i].boolint_def);
1043         }
1044         for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
1045                 if (!general_cfg[i].any && strcasecmp(gen_spec[i].def, NO_DEFAULT))
1046                         _parse(&(general_cfg[i]), (char *)gen_spec[i].def, gen_spec[i].type, gen_spec[i].boolint_def);
1047         }
1048 }
1049
1050 void misdn_cfg_reload (void)
1051 {
1052         misdn_cfg_init (0);
1053 }
1054
1055 void misdn_cfg_destroy (void)
1056 {
1057         misdn_cfg_lock();
1058
1059         _free_port_cfg();
1060         _free_general_cfg();
1061
1062         free(port_cfg);
1063         free(general_cfg);
1064         free(ptp);
1065         free(map);
1066
1067         misdn_cfg_unlock();
1068         ast_mutex_destroy(&config_mutex);
1069 }
1070
1071 int misdn_cfg_init (int this_max_ports)
1072 {
1073         char config[] = "misdn.conf";
1074         char *cat, *p;
1075         int i;
1076         struct ast_config *cfg;
1077         struct ast_variable *v;
1078
1079         if (!(cfg = AST_LOAD_CFG(config))) {
1080                 ast_log(LOG_WARNING, "missing file: misdn.conf\n");
1081                 return -1;
1082         }
1083
1084         ast_mutex_init(&config_mutex);
1085
1086         misdn_cfg_lock();
1087
1088         if (this_max_ports) {
1089                 /* this is the first run */
1090                 max_ports = this_max_ports;
1091                 map = (int *)calloc(MISDN_GEN_LAST + 1, sizeof(int));
1092                 if (_enum_array_map())
1093                         return -1;
1094                 p = (char *)calloc(1, (max_ports + 1) * sizeof(union misdn_cfg_pt *)
1095                                                    + (max_ports + 1) * NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt));
1096                 port_cfg = (union misdn_cfg_pt **)p;
1097                 p += (max_ports + 1) * sizeof(union misdn_cfg_pt *);
1098                 for (i = 0; i <= max_ports; ++i) {
1099                         port_cfg[i] = (union misdn_cfg_pt *)p;
1100                         p += NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt);
1101                 }
1102                 general_cfg = (union misdn_cfg_pt *)calloc(1, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1103                 ptp = (int *)calloc(max_ports + 1, sizeof(int));
1104         }
1105         else {
1106                 /* misdn reload */
1107                 _free_port_cfg();
1108                 _free_general_cfg();
1109                 memset(port_cfg[0], 0, NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt) * (max_ports + 1));
1110                 memset(general_cfg, 0, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
1111                 memset(ptp, 0, sizeof(int) * (max_ports + 1));
1112         }
1113
1114         cat = ast_category_browse(cfg, NULL);
1115
1116         while(cat) {
1117                 v = ast_variable_browse(cfg, cat);
1118                 if (!strcasecmp(cat, "general")) {
1119                         _build_general_config(v);
1120                 } else {
1121                         _build_port_config(v, cat);
1122                 }
1123                 cat = ast_category_browse(cfg, cat);
1124         }
1125
1126         _fill_defaults();
1127
1128         misdn_cfg_unlock();
1129         AST_DESTROY_CFG(cfg);
1130
1131         return 0;
1132 }
1133
1134