Sat Nov 1 06:28:34 2008

Asterisk developer's documentation


misdn_config.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  * 
00004  * Copyright (C) 2005, Christian Richter
00005  *
00006  * Christian Richter <crich@beronet.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  *
00018  */
00019 
00020 /*!
00021  * \file
00022  *
00023  * \brief chan_misdn configuration management
00024  * \author Christian Richter <crich@beronet.com>
00025  *
00026  * \ingroup channel_drivers
00027  */
00028 
00029 #include "asterisk.h"
00030 
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 145293 $")
00032 
00033 #include <stdlib.h>
00034 #include <stdio.h>
00035 #include <string.h>
00036 #include <errno.h>
00037 
00038 #include "chan_misdn_config.h"
00039 
00040 #include "asterisk/config.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/logger.h"
00043 #include "asterisk/lock.h"
00044 #include "asterisk/pbx.h"
00045 #include "asterisk/strings.h"
00046 #include "asterisk/utils.h"
00047 
00048 #define AST_LOAD_CFG ast_config_load
00049 #define AST_DESTROY_CFG ast_config_destroy
00050 
00051 #define NO_DEFAULT "<>"
00052 #define NONE 0
00053 
00054 #define GEN_CFG 1
00055 #define PORT_CFG 2
00056 #define NUM_GEN_ELEMENTS (sizeof(gen_spec) / sizeof(struct misdn_cfg_spec))
00057 #define NUM_PORT_ELEMENTS (sizeof(port_spec) / sizeof(struct misdn_cfg_spec))
00058 
00059 enum misdn_cfg_type {
00060    MISDN_CTYPE_STR,
00061    MISDN_CTYPE_INT,
00062    MISDN_CTYPE_BOOL,
00063    MISDN_CTYPE_BOOLINT,
00064    MISDN_CTYPE_MSNLIST,
00065    MISDN_CTYPE_ASTGROUP
00066 };
00067 
00068 struct msn_list {
00069    char *msn;
00070    struct msn_list *next;
00071 };
00072 
00073 union misdn_cfg_pt {
00074    char *str;
00075    int *num;
00076    struct msn_list *ml;
00077    ast_group_t *grp;
00078    void *any;
00079 };
00080 
00081 struct misdn_cfg_spec {
00082    char name[BUFFERSIZE];
00083    enum misdn_cfg_elements elem;
00084    enum misdn_cfg_type type;
00085    char def[BUFFERSIZE];
00086    int boolint_def;
00087    char desc[BUFFERSIZE];
00088 };
00089 
00090 
00091 static const char ports_description[] =
00092    "Define your ports, e.g. 1,2 (depends on mISDN-driver loading order).";
00093 
00094 static const struct misdn_cfg_spec port_spec[] = {
00095    { "name", MISDN_CFG_GROUPNAME, MISDN_CTYPE_STR, "default", NONE,
00096       "Name of the portgroup." },
00097    { "allowed_bearers", MISDN_CFG_ALLOWED_BEARERS, MISDN_CTYPE_STR, "all", NONE,
00098       "Here you can list which bearer capabilities should be allowed:\n"
00099       "\t  all                  - allow any bearer capability\n"
00100       "\t  speech               - allow speech\n"
00101       "\t  3_1khz               - allow 3.1KHz audio\n"
00102       "\t  digital_unrestricted - allow unrestricted digital\n"
00103       "\t  digital_restricted   - allow restricted digital\n"
00104       "\t  video                - allow video" },
00105    { "rxgain", MISDN_CFG_RXGAIN, MISDN_CTYPE_INT, "0", NONE,
00106       "Set this between -8 and 8 to change the RX Gain." },
00107    { "txgain", MISDN_CFG_TXGAIN, MISDN_CTYPE_INT, "0", NONE,
00108       "Set this between -8 and 8 to change the TX Gain." },
00109    { "te_choose_channel", MISDN_CFG_TE_CHOOSE_CHANNEL, MISDN_CTYPE_BOOL, "no", NONE,
00110       "Some telcos especially in NL seem to need this set to yes,\n"
00111       "\talso in Switzerland this seems to be important." },
00112    { "far_alerting", MISDN_CFG_FAR_ALERTING, MISDN_CTYPE_BOOL, "no", NONE,
00113       "If we should generate ringing for chan_sip and others." },
00114    { "pmp_l1_check", MISDN_CFG_PMP_L1_CHECK, MISDN_CTYPE_BOOL, "no", NONE,
00115       "This option defines, if chan_misdn should check the L1 on a PMP\n"
00116       "\tbefore making a group call on it. The L1 may go down for PMP Ports\n"
00117       "\tso we might need this.\n"
00118       "\tBut be aware! a broken or plugged off cable might be used for a group call\n"
00119       "\tas well, since chan_misdn has no chance to distinguish if the L1 is down\n"
00120       "\tbecause of a lost Link or because the Provider shut it down..." },
00121    { "block_on_alarm", MISDN_CFG_ALARM_BLOCK, MISDN_CTYPE_BOOL, "no", NONE ,
00122      "Block this port if we have an alarm on it." },
00123    { "hdlc", MISDN_CFG_HDLC, MISDN_CTYPE_BOOL, "no", NONE,
00124       "Set this to yes, if you want to bridge a mISDN data channel to\n"
00125       "\tanother channel type or to an application." },
00126    { "context", MISDN_CFG_CONTEXT, MISDN_CTYPE_STR, "default", NONE,
00127       "Context to use for incoming calls." },
00128    { "language", MISDN_CFG_LANGUAGE, MISDN_CTYPE_STR, "en", NONE,
00129       "Language." },
00130    { "musicclass", MISDN_CFG_MUSICCLASS, MISDN_CTYPE_STR, "default", NONE,
00131       "Sets the musiconhold class." },
00132    { "callerid", MISDN_CFG_CALLERID, MISDN_CTYPE_STR, "", NONE,
00133       "Sets the caller ID." },
00134    { "method", MISDN_CFG_METHOD, MISDN_CTYPE_STR, "standard", NONE,
00135       "Sets the method to use for channel selection:\n"
00136       "\t  standard    - always choose the first free channel with the lowest number\n"
00137       "\t  round_robin - use the round robin algorithm to select a channel. use this\n"
00138       "\t                if you want to balance your load." },
00139    { "dialplan", MISDN_CFG_DIALPLAN, MISDN_CTYPE_INT, "0", NONE,
00140       "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
00141       "\n"
00142       "\tThere are different types of the dialplan:\n"
00143       "\n"
00144       "\tdialplan -> outgoing Number\n"
00145       "\tlocaldialplan -> callerid\n"
00146       "\tcpndialplan -> connected party number\n"
00147       "\n"
00148       "\tdialplan options:\n"
00149       "\n"
00150       "\t0 - unknown\n"
00151       "\t1 - International\n"
00152       "\t2 - National\n"
00153       "\t4 - Subscriber\n"
00154       "\n"
00155       "\tThis setting is used for outgoing calls." },
00156    { "localdialplan", MISDN_CFG_LOCALDIALPLAN, MISDN_CTYPE_INT, "0", NONE,
00157       "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
00158       "\n"
00159       "\tThere are different types of the dialplan:\n"
00160       "\n"
00161       "\tdialplan -> outgoing Number\n"
00162       "\tlocaldialplan -> callerid\n"
00163       "\tcpndialplan -> connected party number\n"
00164       "\n"
00165       "\tdialplan options:\n"
00166       "\n"
00167       "\t0 - unknown\n"
00168       "\t1 - International\n"
00169       "\t2 - National\n"
00170       "\t4 - Subscriber\n"
00171       "\n"
00172       "\tThis setting is used for outgoing calls." },
00173    { "cpndialplan", MISDN_CFG_CPNDIALPLAN, MISDN_CTYPE_INT, "0", NONE,
00174       "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
00175       "\n"
00176       "\tThere are different types of the dialplan:\n"
00177       "\n"
00178       "\tdialplan -> outgoing Number\n"
00179       "\tlocaldialplan -> callerid\n"
00180       "\tcpndialplan -> connected party number\n"
00181       "\n"
00182       "\tdialplan options:\n"
00183       "\n"
00184       "\t0 - unknown\n"
00185       "\t1 - International\n"
00186       "\t2 - National\n"
00187       "\t4 - Subscriber\n"
00188       "\n"
00189       "\tThis setting is used for outgoing calls." },
00190    { "nationalprefix", MISDN_CFG_NATPREFIX, MISDN_CTYPE_STR, "0", NONE,
00191       "Prefix for national, this is put before the\n"
00192       "\toad if an according dialplan is set by the other end." },
00193    { "internationalprefix", MISDN_CFG_INTERNATPREFIX, MISDN_CTYPE_STR, "00", NONE,
00194       "Prefix for international, this is put before the\n"
00195       "\toad if an according dialplan is set by the other end." },
00196    { "presentation", MISDN_CFG_PRES, MISDN_CTYPE_INT, "-1", NONE,
00197       "These (presentation and screen) are the exact isdn screening and presentation\n"
00198       "\tindicators.\n"
00199       "\tIf -1 is given for either value, the presentation indicators are used from\n"
00200       "\tAsterisk's SetCallerPres application.\n"
00201       "\n"
00202       "\tscreen=0, presentation=0 -> callerid presented\n"
00203       "\tscreen=1, presentation=1 -> callerid restricted (the remote end doesn't see it!)" },
00204    { "screen", MISDN_CFG_SCREEN, MISDN_CTYPE_INT, "-1", NONE,
00205       "These (presentation and screen) are the exact isdn screening and presentation\n"
00206       "\tindicators.\n"
00207       "\tIf -1 is given for either value, the presentation indicators are used from\n"
00208       "\tAsterisk's SetCallerPres application.\n"
00209       "\n"
00210       "\tscreen=0, presentation=0 -> callerid presented\n"
00211       "\tscreen=1, presentation=1 -> callerid restricted (the remote end doesn't see it!)" },
00212    { "always_immediate", MISDN_CFG_ALWAYS_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE,
00213       "Enable this to get into the s dialplan-extension.\n"
00214       "\tThere you can use DigitTimeout if you can't or don't want to use\n"
00215       "\tisdn overlap dial.\n"
00216       "\tNOTE: This will jump into the s extension for every exten!" },
00217    { "nodialtone", MISDN_CFG_NODIALTONE, MISDN_CTYPE_BOOL, "no", NONE,
00218       "Enable this to prevent chan_misdn to generate the dialtone\n"
00219       "\tThis makes only sense together with the always_immediate=yes option\n"
00220       "\tto generate your own dialtone with Playtones or so."},
00221    { "immediate", MISDN_CFG_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE,
00222       "Enable this if you want callers which called exactly the base\n"
00223       "\tnumber (so no extension is set) to jump into the s extension.\n"
00224       "\tIf the user dials something more, it jumps to the correct extension\n"
00225       "\tinstead." },
00226    { "senddtmf", MISDN_CFG_SENDDTMF, MISDN_CTYPE_BOOL, "no", NONE,
00227       "Enable this if we should produce DTMF Tones ourselves." },
00228    { "astdtmf", MISDN_CFG_ASTDTMF, MISDN_CTYPE_BOOL, "no", NONE,
00229       "Enable this if you want to use the Asterisk dtmf detector\n"
00230       "instead of the mISDN_dsp/hfcmulti one."
00231       },
00232    { "hold_allowed", MISDN_CFG_HOLD_ALLOWED, MISDN_CTYPE_BOOL, "no", NONE,
00233       "Enable this to have support for hold and retrieve." },
00234    { "early_bconnect", MISDN_CFG_EARLY_BCONNECT, MISDN_CTYPE_BOOL, "yes", NONE,
00235       "Disable this if you don't mind correct handling of Progress Indicators." },
00236    { "incoming_early_audio", MISDN_CFG_INCOMING_EARLY_AUDIO, MISDN_CTYPE_BOOL, "no", NONE,
00237       "Turn this on if you like to send Tone Indications to a Incoming\n"
00238       "\tisdn channel on a TE Port. Rarely used, only if the Telco allows\n"
00239       "\tyou to send indications by yourself, normally the Telco sends the\n"
00240       "\tindications to the remote party." },
00241    { "echocancel", MISDN_CFG_ECHOCANCEL, MISDN_CTYPE_BOOLINT, "0", 128,
00242       "This enables echo cancellation with the given number of taps.\n"
00243       "\tBe aware: Move this setting only to outgoing portgroups!\n"
00244       "\tA value of zero turns echo cancellation off.\n"
00245       "\n"
00246       "\tPossible values are: 0,32,64,128,256,yes(=128),no(=0)" },
00247 #ifdef MISDN_1_2
00248    { "pipeline", MISDN_CFG_PIPELINE, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
00249       "Set the configuration string for the mISDN dsp pipeline.\n"
00250       "\n"
00251       "\tExample for enabling the mg2 echo cancellation module with deftaps\n"
00252       "\tset to 128:\n"
00253       "\t\tmg2ec(deftaps=128)" },
00254 #endif
00255 #ifdef WITH_BEROEC
00256    { "bnechocancel", MISDN_CFG_BNECHOCANCEL, MISDN_CTYPE_BOOLINT, "yes", 64,
00257       "echotail in ms (1-200)\n"},
00258    { "bnec_antihowl", MISDN_CFG_BNEC_ANTIHOWL, MISDN_CTYPE_INT, "0", NONE,
00259       "Use antihowl\n"},
00260    { "bnec_nlp", MISDN_CFG_BNEC_NLP, MISDN_CTYPE_BOOL, "yes", NONE,
00261       "Nonlinear Processing (much faster adaption)"},
00262    { "bnec_zerocoeff", MISDN_CFG_BNEC_ZEROCOEFF, MISDN_CTYPE_BOOL, "no", NONE,
00263       "ZeroCoeffeciens\n"},
00264    { "bnec_tonedisabler", MISDN_CFG_BNEC_TD, MISDN_CTYPE_BOOL, "no", NONE,
00265       "Disable Tone\n"},
00266    { "bnec_adaption", MISDN_CFG_BNEC_ADAPT, MISDN_CTYPE_INT, "1", NONE,
00267       "Adaption mode (0=no,1=full,2=fast)\n"},
00268 #endif
00269    { "need_more_infos", MISDN_CFG_NEED_MORE_INFOS, MISDN_CTYPE_BOOL, "0", NONE,
00270       "Send Setup_Acknowledge on incoming calls anyway (instead of PROCEEDING),\n"
00271       "\tthis requests additional Infos, so we can waitfordigits without much\n"
00272       "\tissues. This works only for PTP Ports" },
00273    { "noautorespond_on_setup", MISDN_CFG_NOAUTORESPOND_ON_SETUP, MISDN_CTYPE_BOOL, "0", NONE,
00274       "Do not send SETUP_ACKNOWLEDGE or PROCEEDING automatically to the calling Party.\n"
00275       "Instead we directly jump into the dialplan. This might be useful for fast call\n"
00276       "rejection, or for some broken switches, that need hangup causes like busy in the.\n"
00277       "RELEASE_COMPLETE Message, instead of the DISCONNECT Message."},
00278    { "jitterbuffer", MISDN_CFG_JITTERBUFFER, MISDN_CTYPE_INT, "4000", NONE,
00279       "The jitterbuffer." },
00280    { "jitterbuffer_upper_threshold", MISDN_CFG_JITTERBUFFER_UPPER_THRESHOLD, MISDN_CTYPE_INT, "0", NONE,
00281       "Change this threshold to enable dejitter functionality." },
00282    { "callgroup", MISDN_CFG_CALLGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE,
00283       "Callgroup." },
00284    { "pickupgroup", MISDN_CFG_PICKUPGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE,
00285       "Pickupgroup." },
00286    { "max_incoming", MISDN_CFG_MAX_IN, MISDN_CTYPE_INT, "-1", NONE,
00287       "Defines the maximum amount of incoming calls per port for this group.\n"
00288       "\tCalls which exceed the maximum will be marked with the channel variable\n"
00289       "\tMAX_OVERFLOW. It will contain the amount of overflowed calls" },
00290    { "max_outgoing", MISDN_CFG_MAX_OUT, MISDN_CTYPE_INT, "-1", NONE,
00291       "Defines the maximum amount of outgoing calls per port for this group\n"
00292       "\texceeding calls will be rejected" },
00293 
00294    { "reject_cause", MISDN_CFG_REJECT_CAUSE, MISDN_CTYPE_INT, "21", NONE,
00295       "Defines the cause with which a 3. call is rejected on PTMP BRI."},
00296    { "faxdetect", MISDN_CFG_FAXDETECT, MISDN_CTYPE_STR, "no", NONE,
00297       "Setup fax detection:\n"
00298       "\t    no        - no fax detection\n"
00299       "\t    incoming  - fax detection for incoming calls\n"
00300       "\t    outgoing  - fax detection for outgoing calls\n"
00301       "\t    both      - fax detection for incoming and outgoing calls\n"
00302       "\tAdd +nojump to your value (i.e. faxdetect=both+nojump) if you don't want to jump into the\n"
00303       "\tfax-extension but still want to detect the fax and prepare the channel for fax transfer." },
00304    { "faxdetect_timeout", MISDN_CFG_FAXDETECT_TIMEOUT, MISDN_CTYPE_INT, "5", NONE,
00305       "Number of seconds the fax detection should do its job. After the given period of time,\n"
00306       "\twe assume that it's not a fax call and save some CPU time by turning off fax detection.\n"
00307       "\tSet this to 0 if you don't want a timeout (never stop detecting)." },
00308    { "faxdetect_context", MISDN_CFG_FAXDETECT_CONTEXT, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
00309       "Context to jump into if we detect a fax. Don't set this if you want to stay in the current context." },
00310    { "l1watcher_timeout", MISDN_CFG_L1_TIMEOUT, MISDN_CTYPE_BOOLINT, "0", 4,
00311       "Watches the layer 1. If the layer 1 is down, it tries to\n"
00312       "\tget it up. The timeout is given in seconds. with 0 as value it\n"
00313       "\tdoes not watch the l1 at all\n"
00314       "\n"
00315       "\tThis option is only read at loading time of chan_misdn, which\n"
00316       "\tmeans you need to unload and load chan_misdn to change the value,\n"
00317       "\tan Asterisk restart should do the trick." },
00318    { "overlapdial", MISDN_CFG_OVERLAP_DIAL, MISDN_CTYPE_BOOLINT, "0", 4,
00319       "Enables overlap dial for the given amount of seconds.\n"
00320       "\tPossible values are positive integers or:\n"
00321       "\t   yes (= 4 seconds)\n"
00322       "\t   no  (= 0 seconds = disabled)" },
00323    { "nttimeout", MISDN_CFG_NTTIMEOUT, MISDN_CTYPE_BOOL, "no", NONE ,
00324       "Set this to yes if you want calls disconnected in overlap mode" 
00325       "when a timeout happens."},
00326    { "bridging", MISDN_CFG_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
00327       "Set this to yes/no, default is yes.\n"
00328       "This can be used to have bridging enabled in general and to\n"
00329       "disable it for specific ports. It makes sense to disable\n"
00330       "bridging on NT Port where you plan to use the HOLD/RETRIEVE\n"
00331       "features with ISDN phones." },
00332    { "msns", MISDN_CFG_MSNS, MISDN_CTYPE_MSNLIST, "*", NONE,
00333       "MSN's for TE ports, listen on those numbers on the above ports, and\n"
00334       "\tindicate the incoming calls to Asterisk.\n"
00335       "\tHere you can give a comma separated list, or simply an '*' for any msn." },
00336 };
00337 
00338 static const struct misdn_cfg_spec gen_spec[] = {
00339    { "debug", MISDN_GEN_DEBUG, MISDN_CTYPE_INT, "0", NONE,
00340       "Sets the debugging flag:\n"
00341       "\t0 - No Debug\n"
00342       "\t1 - mISDN Messages and * - Messages, and * - State changes\n"
00343       "\t2 - Messages + Message specific Informations (e.g. bearer capability)\n"
00344       "\t3 - very Verbose, the above + lots of Driver specific infos\n"
00345       "\t4 - even more Verbose than 3" },
00346 #ifndef MISDN_1_2
00347    { "misdn_init", MISDN_GEN_MISDN_INIT, MISDN_CTYPE_STR, "/etc/misdn-init.conf", NONE,
00348       "Set the path to the misdn-init.conf (for nt_ptp mode checking)." },
00349 #endif
00350    { "tracefile", MISDN_GEN_TRACEFILE, MISDN_CTYPE_STR, "/var/log/asterisk/misdn.log", NONE,
00351       "Set the path to the massively growing trace file, if you want that." },
00352    { "bridging", MISDN_GEN_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
00353       "Set this to yes if you want mISDN_dsp to bridge the calls in HW." },
00354    { "stop_tone_after_first_digit", MISDN_GEN_STOP_TONE, MISDN_CTYPE_BOOL, "yes", NONE,
00355       "Stops dialtone after getting first digit on NT Port." },
00356    { "append_digits2exten", MISDN_GEN_APPEND_DIGITS2EXTEN, MISDN_CTYPE_BOOL, "yes", NONE,
00357       "Whether to append overlapdialed Digits to Extension or not." },
00358    { "dynamic_crypt", MISDN_GEN_DYNAMIC_CRYPT, MISDN_CTYPE_BOOL, "no", NONE,
00359       "Whether to look out for dynamic crypting attempts." },
00360    { "crypt_prefix", MISDN_GEN_CRYPT_PREFIX, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
00361       "What is used for crypting Protocol." },
00362    { "crypt_keys", MISDN_GEN_CRYPT_KEYS, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
00363       "Keys for cryption, you reference them in the dialplan\n"
00364       "\tLater also in dynamic encr." },
00365    { "ntkeepcalls", MISDN_GEN_NTKEEPCALLS, MISDN_CTYPE_BOOL, "no", NONE, 
00366       "avoid dropping calls if the L2 goes down. some Nortel pbx\n" 
00367       "do put down the L2/L1 for some milliseconds even if there\n"
00368       "are running calls. with this option you can avoid dropping them" },
00369    { "ntdebugflags", MISDN_GEN_NTDEBUGFLAGS, MISDN_CTYPE_INT, "0", NONE,
00370       "No description yet."},
00371    { "ntdebugfile", MISDN_GEN_NTDEBUGFILE, MISDN_CTYPE_STR, "/var/log/misdn-nt.log", NONE,
00372       "No description yet." }
00373 };
00374 
00375 
00376 /* array of port configs, default is at position 0. */
00377 static union misdn_cfg_pt **port_cfg;
00378 /* max number of available ports, is set on init */
00379 static int max_ports;
00380 /* general config */
00381 static union misdn_cfg_pt *general_cfg;
00382 /* storing the ptp flag separated to save memory */
00383 static int *ptp;
00384 /* maps enum config elements to array positions */
00385 static int *map;
00386 
00387 static ast_mutex_t config_mutex; 
00388 
00389 #define CLI_ERROR(name, value, section) ({ \
00390    ast_log(LOG_WARNING, "misdn.conf: \"%s=%s\" (section: %s) invalid or out of range. " \
00391       "Please edit your misdn.conf and then do a \"misdn reload\".\n", name, value, section); \
00392 })
00393 
00394 static int _enum_array_map (void)
00395 {
00396    int i, j, ok;
00397 
00398    for (i = MISDN_CFG_FIRST + 1; i < MISDN_CFG_LAST; ++i) {
00399       if (i == MISDN_CFG_PTP)
00400          continue;
00401       ok = 0;
00402       for (j = 0; j < NUM_PORT_ELEMENTS; ++j) {
00403          if (port_spec[j].elem == i) {
00404             map[i] = j;
00405             ok = 1;
00406             break;
00407          }
00408       }
00409       if (!ok) {
00410          ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (port section) has no corresponding element in the config struct!\n", i);
00411          return -1;
00412       }
00413    }
00414    for (i = MISDN_GEN_FIRST + 1; i < MISDN_GEN_LAST; ++i) {
00415       ok = 0;
00416       for (j = 0; j < NUM_GEN_ELEMENTS; ++j) {
00417          if (gen_spec[j].elem == i) {
00418             map[i] = j;
00419             ok = 1;
00420             break;
00421          }
00422       }
00423       if (!ok) {
00424          ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (general section) has no corresponding element in the config struct!\n", i);
00425          return -1;
00426       }
00427    }
00428    return 0;
00429 }
00430 
00431 static int get_cfg_position (char *name, int type)
00432 {
00433    int i;
00434 
00435    switch (type) {
00436    case PORT_CFG:
00437       for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
00438          if (!strcasecmp(name, port_spec[i].name))
00439             return i;
00440       }
00441       break;
00442    case GEN_CFG:
00443       for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
00444          if (!strcasecmp(name, gen_spec[i].name))
00445             return i;
00446       }
00447    }
00448 
00449    return -1;
00450 }
00451 
00452 static inline void misdn_cfg_lock (void)
00453 {
00454    ast_mutex_lock(&config_mutex);
00455 }
00456 
00457 static inline void misdn_cfg_unlock (void)
00458 {
00459    ast_mutex_unlock(&config_mutex);
00460 }
00461 
00462 static void _free_msn_list (struct msn_list* iter)
00463 {
00464    if (iter->next)
00465       _free_msn_list(iter->next);
00466    if (iter->msn)
00467       free(iter->msn);
00468    free(iter);
00469 }
00470 
00471 static void _free_port_cfg (void)
00472 {
00473    int i, j;
00474    int gn = map[MISDN_CFG_GROUPNAME];
00475    union misdn_cfg_pt* free_list[max_ports + 2];
00476    
00477    memset(free_list, 0, sizeof(free_list));
00478    free_list[0] = port_cfg[0];
00479    for (i = 1; i <= max_ports; ++i) {
00480       if (port_cfg[i][gn].str) {
00481          /* we always have a groupname in the non-default case, so this is fine */
00482          for (j = 1; j <= max_ports; ++j) {
00483             if (free_list[j] && free_list[j][gn].str == port_cfg[i][gn].str)
00484                break;
00485             else if (!free_list[j]) {
00486                free_list[j] = port_cfg[i];
00487                break;
00488             }
00489          }
00490       }
00491    }
00492    for (j = 0; free_list[j]; ++j) {
00493       for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
00494          if (free_list[j][i].any) {
00495             if (port_spec[i].type == MISDN_CTYPE_MSNLIST)
00496                _free_msn_list(free_list[j][i].ml);
00497             else
00498                free(free_list[j][i].any);
00499          }
00500       }
00501    }
00502 }
00503 
00504 static void _free_general_cfg (void)
00505 {
00506    int i;
00507 
00508    for (i = 0; i < NUM_GEN_ELEMENTS; i++) 
00509       if (general_cfg[i].any)
00510          free(general_cfg[i].any);
00511 }
00512 
00513 void misdn_cfg_get (int port, enum misdn_cfg_elements elem, void *buf, int bufsize)
00514 {
00515    int place;
00516 
00517    if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
00518       memset(buf, 0, bufsize);
00519       ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Port number %d is not valid.\n", port);
00520       return;
00521    }
00522 
00523    misdn_cfg_lock();
00524    if (elem == MISDN_CFG_PTP) {
00525       if (!memcpy(buf, &ptp[port], (bufsize > ptp[port]) ? sizeof(ptp[port]) : bufsize))
00526          memset(buf, 0, bufsize);
00527    } else {
00528       if ((place = map[elem]) < 0) {
00529          memset (buf, 0, bufsize);
00530          ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Invalid element (%d) requested.\n", elem);
00531       } else {
00532          if (elem < MISDN_CFG_LAST) {
00533             switch (port_spec[place].type) {
00534             case MISDN_CTYPE_STR:
00535                if (port_cfg[port][place].str) {
00536                   if (!memccpy(buf, port_cfg[port][place].str, 0, bufsize))
00537                      memset(buf, 0, 1);
00538                } else if (port_cfg[0][place].str) {
00539                   if (!memccpy(buf, port_cfg[0][place].str, 0, bufsize))
00540                      memset(buf, 0, 1);
00541                } else
00542                   memset(buf, 0, bufsize);
00543                break;
00544             default:
00545                if (port_cfg[port][place].any)
00546                   memcpy(buf, port_cfg[port][place].any, bufsize);
00547                else if (port_cfg[0][place].any)
00548                   memcpy(buf, port_cfg[0][place].any, bufsize);
00549                else
00550                   memset(buf, 0, bufsize);
00551             }
00552          } else {
00553             switch (gen_spec[place].type) {
00554             case MISDN_CTYPE_STR:
00555                if (!general_cfg[place].str || !memccpy(buf, general_cfg[place].str, 0, bufsize))
00556                   memset(buf, 0, 1);
00557                break;
00558             default:
00559                if (general_cfg[place].any)
00560                   memcpy(buf, general_cfg[place].any, bufsize);
00561                else
00562                   memset(buf, 0, bufsize);
00563             }
00564          }
00565       }
00566    }
00567    misdn_cfg_unlock();
00568 }
00569 
00570 enum misdn_cfg_elements misdn_cfg_get_elem (char *name)
00571 {
00572    int pos;
00573 
00574    /* here comes a hack to replace the (not existing) "name" element with the "ports" element */
00575    if (!strcmp(name, "ports"))
00576       return MISDN_CFG_GROUPNAME;
00577    if (!strcmp(name, "name"))
00578       return MISDN_CFG_FIRST;
00579 
00580    pos = get_cfg_position (name, PORT_CFG);
00581    if (pos >= 0)
00582       return port_spec[pos].elem;
00583    
00584    pos = get_cfg_position (name, GEN_CFG);
00585    if (pos >= 0)
00586       return gen_spec[pos].elem;
00587    
00588    return MISDN_CFG_FIRST;
00589 }
00590 
00591 void misdn_cfg_get_name (enum misdn_cfg_elements elem, void *buf, int bufsize)
00592 {
00593    struct misdn_cfg_spec *spec = NULL;
00594    int place = map[elem];
00595 
00596    /* the ptp hack */
00597    if (elem == MISDN_CFG_PTP) {
00598       memset(buf, 0, 1);
00599       return;
00600    }
00601    
00602    /* here comes a hack to replace the (not existing) "name" element with the "ports" element */
00603    if (elem == MISDN_CFG_GROUPNAME) {
00604       if (!snprintf(buf, bufsize, "ports"))
00605          memset(buf, 0, 1);
00606       return;
00607    }
00608    
00609    if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
00610       spec = (struct misdn_cfg_spec *)port_spec;
00611    else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
00612       spec = (struct misdn_cfg_spec *)gen_spec;
00613 
00614    if (!spec || !memccpy(buf, spec[place].name, 0, bufsize))
00615       memset(buf, 0, 1);
00616 }
00617 
00618 void misdn_cfg_get_desc (enum misdn_cfg_elements elem, void *buf, int bufsize, void *buf_default, int bufsize_default)
00619 {
00620    int place = map[elem];
00621    struct misdn_cfg_spec *spec = NULL;
00622 
00623    /* here comes a hack to replace the (not existing) "name" element with the "ports" element */
00624    if (elem == MISDN_CFG_GROUPNAME) {
00625       if (!memccpy(buf, ports_description, 0, bufsize))
00626          memset(buf, 0, 1);
00627       if (buf_default && bufsize_default)
00628          memset(buf_default, 0, 1);
00629       return;
00630    }
00631 
00632    if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
00633       spec = (struct misdn_cfg_spec *)port_spec;
00634    else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
00635       spec = (struct misdn_cfg_spec *)gen_spec;
00636       
00637    if (!spec || !spec[place].desc)
00638       memset(buf, 0, 1);
00639    else {
00640       if (!memccpy(buf, spec[place].desc, 0, bufsize))
00641          memset(buf, 0, 1);
00642       if (buf_default && bufsize) {
00643          if (!strcmp(spec[place].def, NO_DEFAULT))
00644             memset(buf_default, 0, 1);
00645          else if (!memccpy(buf_default, spec[place].def, 0, bufsize_default))
00646             memset(buf_default, 0, 1);
00647       }
00648    }
00649 }
00650 
00651 int misdn_cfg_is_msn_valid (int port, char* msn)
00652 {
00653    int re = 0;
00654    struct msn_list *iter;
00655 
00656    if (!misdn_cfg_is_port_valid(port)) {
00657       ast_log(LOG_WARNING, "Invalid call to misdn_cfg_is_msn_valid! Port number %d is not valid.\n", port);
00658       return 0;
00659    }
00660 
00661    misdn_cfg_lock();
00662    if (port_cfg[port][map[MISDN_CFG_MSNS]].ml)
00663       iter = port_cfg[port][map[MISDN_CFG_MSNS]].ml;
00664    else
00665       iter = port_cfg[0][map[MISDN_CFG_MSNS]].ml;
00666    for (; iter; iter = iter->next) 
00667       if (*(iter->msn) == '*' || ast_extension_match(iter->msn, msn)) {
00668          re = 1;
00669          break;
00670       }
00671    misdn_cfg_unlock();
00672 
00673    return re;
00674 }
00675 
00676 int misdn_cfg_is_port_valid (int port)
00677 {
00678    int gn = map[MISDN_CFG_GROUPNAME];
00679 
00680    return (port >= 1 && port <= max_ports && port_cfg[port][gn].str);
00681 }
00682 
00683 int misdn_cfg_is_group_method (char *group, enum misdn_cfg_method meth)
00684 {
00685    int i, re = 0;
00686    char *method ;
00687 
00688    misdn_cfg_lock();
00689 
00690    method = port_cfg[0][map[MISDN_CFG_METHOD]].str;
00691 
00692    for (i = 1; i <= max_ports; i++) {
00693       if (port_cfg[i] && port_cfg[i][map[MISDN_CFG_GROUPNAME]].str) {
00694          if (!strcasecmp(port_cfg[i][map[MISDN_CFG_GROUPNAME]].str, group))
00695             method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ? 
00696                     port_cfg[i][map[MISDN_CFG_METHOD]].str : port_cfg[0][map[MISDN_CFG_METHOD]].str);
00697       }
00698    }
00699 
00700    if (method) {
00701       switch (meth) {
00702       case METHOD_STANDARD:      re = !strcasecmp(method, "standard");
00703                            break;
00704       case METHOD_ROUND_ROBIN:   re = !strcasecmp(method, "round_robin");
00705                            break;
00706       case METHOD_STANDARD_DEC:  re = !strcasecmp(method, "standard_dec");
00707                            break;
00708       }
00709    }
00710    misdn_cfg_unlock();
00711 
00712    return re;
00713 }
00714 
00715 /*! 
00716  * \brief Generate a comma separated list of all active ports
00717  */
00718 void misdn_cfg_get_ports_string (char *ports)
00719 {
00720    char tmp[16];
00721    int l, i;
00722    int gn = map[MISDN_CFG_GROUPNAME];
00723 
00724    *ports = 0;
00725 
00726    misdn_cfg_lock();
00727    for (i = 1; i <= max_ports; i++) {
00728       if (port_cfg[i][gn].str) {
00729          if (ptp[i])
00730             sprintf(tmp, "%dptp,", i);
00731          else
00732             sprintf(tmp, "%d,", i);
00733          strcat(ports, tmp);
00734       }
00735    }
00736    misdn_cfg_unlock();
00737 
00738    if ((l = strlen(ports))) {
00739       /* Strip trailing ',' */
00740       ports[l-1] = 0;
00741    }
00742 }
00743 
00744 void misdn_cfg_get_config_string (int port, enum misdn_cfg_elements elem, char* buf, int bufsize)
00745 {
00746    int place;
00747    char tempbuf[BUFFERSIZE] = "";
00748    struct msn_list *iter;
00749 
00750    if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
00751       *buf = 0;
00752       ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Port number %d is not valid.\n", port);
00753       return;
00754    }
00755 
00756    place = map[elem];
00757 
00758    misdn_cfg_lock();
00759    if (elem == MISDN_CFG_PTP) {
00760       snprintf(buf, bufsize, " -> ptp: %s", ptp[port] ? "yes" : "no");
00761    }
00762    else if (elem > MISDN_CFG_FIRST && elem < MISDN_CFG_LAST) {
00763       switch (port_spec[place].type) {
00764       case MISDN_CTYPE_INT:
00765       case MISDN_CTYPE_BOOLINT:
00766          if (port_cfg[port][place].num)
00767             snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[port][place].num);
00768          else if (port_cfg[0][place].num)
00769             snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[0][place].num);
00770          else
00771             snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
00772          break;
00773       case MISDN_CTYPE_BOOL:
00774          if (port_cfg[port][place].num)
00775             snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[port][place].num ? "yes" : "no");
00776          else if (port_cfg[0][place].num)
00777             snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[0][place].num ? "yes" : "no");
00778          else
00779             snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
00780          break;
00781       case MISDN_CTYPE_ASTGROUP:
00782          if (port_cfg[port][place].grp)
00783             snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, 
00784                    ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[port][place].grp));
00785          else if (port_cfg[0][place].grp)
00786             snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, 
00787                    ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[0][place].grp));
00788          else
00789             snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
00790          break;
00791       case MISDN_CTYPE_MSNLIST:
00792          if (port_cfg[port][place].ml)
00793             iter = port_cfg[port][place].ml;
00794          else
00795             iter = port_cfg[0][place].ml;
00796          if (iter) {
00797             for (; iter; iter = iter->next)
00798                sprintf(tempbuf, "%s%s, ", tempbuf, iter->msn);
00799             tempbuf[strlen(tempbuf)-2] = 0;
00800          }
00801          snprintf(buf, bufsize, " -> msns: %s", *tempbuf ? tempbuf : "none");
00802          break;
00803       case MISDN_CTYPE_STR:
00804          if ( port_cfg[port][place].str) {
00805             snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[port][place].str);
00806          } else if (port_cfg[0][place].str) {
00807             snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[0][place].str);
00808          } else {
00809             snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
00810          }
00811          break;
00812       }
00813    } else if (elem > MISDN_GEN_FIRST && elem < MISDN_GEN_LAST) {
00814       switch (