Sat Apr 26 2014 22:01:38

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 /*** MODULEINFO
00030    <support_level>extended</support_level>
00031  ***/
00032 
00033 #include "asterisk.h"
00034 
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370831 $")
00036 
00037 #include "chan_misdn_config.h"
00038 
00039 #include "asterisk/config.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/lock.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/strings.h"
00044 #include "asterisk/utils.h"
00045 
00046 #define NO_DEFAULT "<>"
00047 #define NONE 0
00048 
00049 #define GEN_CFG 1
00050 #define PORT_CFG 2
00051 #define NUM_GEN_ELEMENTS (sizeof(gen_spec) / sizeof(struct misdn_cfg_spec))
00052 #define NUM_PORT_ELEMENTS (sizeof(port_spec) / sizeof(struct misdn_cfg_spec))
00053 
00054 /*! Global jitterbuffer configuration - by default, jb is disabled
00055  *  \note Values shown here match the defaults shown in misdn.conf.sample */
00056 static struct ast_jb_conf default_jbconf =
00057 {
00058    .flags = 0,
00059    .max_size = 200,
00060    .resync_threshold = 1000,
00061    .impl = "fixed",
00062    .target_extra = 40,
00063 };
00064 
00065 static struct ast_jb_conf global_jbconf;
00066 
00067 enum misdn_cfg_type {
00068    MISDN_CTYPE_STR,
00069    MISDN_CTYPE_INT,
00070    MISDN_CTYPE_BOOL,
00071    MISDN_CTYPE_BOOLINT,
00072    MISDN_CTYPE_MSNLIST,
00073    MISDN_CTYPE_ASTGROUP,
00074    MISDN_CTYPE_ASTNAMEDGROUP
00075 };
00076 
00077 struct msn_list {
00078    char *msn;
00079    struct msn_list *next;
00080 };
00081 
00082 union misdn_cfg_pt {
00083    char *str;
00084    int *num;
00085    struct msn_list *ml;
00086    ast_group_t *grp;
00087    struct ast_namedgroups *namgrp;
00088    void *any;
00089 };
00090 
00091 struct misdn_cfg_spec {
00092    char name[BUFFERSIZE];
00093    enum misdn_cfg_elements elem;
00094    enum misdn_cfg_type type;
00095    char def[BUFFERSIZE];
00096    int boolint_def;
00097    char desc[BUFFERSIZE];
00098 };
00099 
00100 
00101 static const char ports_description[] =
00102    "Define your ports, e.g. 1,2 (depends on mISDN-driver loading order).";
00103 
00104 static const struct misdn_cfg_spec port_spec[] = {
00105    { "name", MISDN_CFG_GROUPNAME, MISDN_CTYPE_STR, "default", NONE,
00106       "Name of the portgroup." },
00107    { "allowed_bearers", MISDN_CFG_ALLOWED_BEARERS, MISDN_CTYPE_STR, "all", NONE,
00108       "Here you can list which bearer capabilities should be allowed:\n"
00109       "\t  all                  - allow any bearer capability\n"
00110       "\t  speech               - allow speech\n"
00111       "\t  3_1khz               - allow 3.1KHz audio\n"
00112       "\t  digital_unrestricted - allow unrestricted digital\n"
00113       "\t  digital_restricted   - allow restricted digital\n"
00114       "\t  video                - allow video" },
00115    { "rxgain", MISDN_CFG_RXGAIN, MISDN_CTYPE_INT, "0", NONE,
00116       "Set this between -8 and 8 to change the RX Gain." },
00117    { "txgain", MISDN_CFG_TXGAIN, MISDN_CTYPE_INT, "0", NONE,
00118       "Set this between -8 and 8 to change the TX Gain." },
00119    { "te_choose_channel", MISDN_CFG_TE_CHOOSE_CHANNEL, MISDN_CTYPE_BOOL, "no", NONE,
00120       "Some telcos especially in NL seem to need this set to yes,\n"
00121       "\talso in Switzerland this seems to be important." },
00122    { "far_alerting", MISDN_CFG_FAR_ALERTING, MISDN_CTYPE_BOOL, "no", NONE,
00123       "If we should generate ringing for chan_sip and others." },
00124    { "pmp_l1_check", MISDN_CFG_PMP_L1_CHECK, MISDN_CTYPE_BOOL, "no", NONE,
00125       "This option defines, if chan_misdn should check the L1 on a PMP\n"
00126       "\tbefore making a group call on it. The L1 may go down for PMP Ports\n"
00127       "\tso we might need this.\n"
00128       "\tBut be aware! a broken or plugged off cable might be used for a group call\n"
00129       "\tas well, since chan_misdn has no chance to distinguish if the L1 is down\n"
00130       "\tbecause of a lost Link or because the Provider shut it down..." },
00131    { "block_on_alarm", MISDN_CFG_ALARM_BLOCK, MISDN_CTYPE_BOOL, "no", NONE ,
00132      "Block this port if we have an alarm on it." },
00133    { "hdlc", MISDN_CFG_HDLC, MISDN_CTYPE_BOOL, "no", NONE,
00134       "Set this to yes, if you want to bridge a mISDN data channel to\n"
00135       "\tanother channel type or to an application." },
00136    { "context", MISDN_CFG_CONTEXT, MISDN_CTYPE_STR, "default", NONE,
00137       "Context to use for incoming calls." },
00138    { "language", MISDN_CFG_LANGUAGE, MISDN_CTYPE_STR, "en", NONE,
00139       "Language." },
00140    { "musicclass", MISDN_CFG_MUSICCLASS, MISDN_CTYPE_STR, "default", NONE,
00141       "Sets the musiconhold class." },
00142    { "callerid", MISDN_CFG_CALLERID, MISDN_CTYPE_STR, "", NONE,
00143       "Set the outgoing caller id to the value." },
00144    { "incoming_cid_tag", MISDN_CFG_INCOMING_CALLERID_TAG, MISDN_CTYPE_STR, "", NONE,
00145       "Set the incoming caller id string tag to the value." },
00146    { "append_msn_to_cid_tag", MISDN_CFG_APPEND_MSN_TO_CALLERID_TAG, MISDN_CTYPE_BOOL, "no", NONE,
00147       "Automatically appends incoming or outgoing MSN to the incoming caller\n"
00148       "\tid string tag. An underscore '_' is used as delimiter. Incoming calls\n"
00149       "\twill have the dialed number appended, and outgoing calls will have the\n"
00150       "\tcaller number appended to the tag." },
00151    { "method", MISDN_CFG_METHOD, MISDN_CTYPE_STR, "standard", NONE,
00152       "Set the method to use for channel selection:\n"
00153       "\t  standard     - Use the first free channel starting from the lowest number.\n"
00154       "\t  standard_dec - Use the first free channel starting from the highest number.\n"
00155       "\t  round_robin  - Use the round robin algorithm to select a channel. Use this\n"
00156       "\t                 if you want to balance your load." },
00157    { "dialplan", MISDN_CFG_DIALPLAN, MISDN_CTYPE_INT, "0", NONE,
00158       "Dialplan means Type Of Number in ISDN Terms\n"
00159       "\tThere are different types of the dialplan:\n"
00160       "\n"
00161       "\tdialplan -> for outgoing call's dialed number\n"
00162       "\tlocaldialplan -> for outgoing call's callerid\n"
00163       "\t      (if -1 is set use the value from the asterisk channel)\n"
00164       "\tcpndialplan -> for incoming call's connected party number sent to caller\n"
00165       "\t      (if -1 is set use the value from the asterisk channel)\n"
00166       "\n"
00167       "\tdialplan options:\n"
00168       "\n"
00169       "\t0 - unknown\n"
00170       "\t1 - International\n"
00171       "\t2 - National\n"
00172       "\t4 - Subscriber" },
00173    { "localdialplan", MISDN_CFG_LOCALDIALPLAN, MISDN_CTYPE_INT, "0", NONE,
00174       "Dialplan means Type Of Number in ISDN Terms\n"
00175       "\tThere are different types of the dialplan:\n"
00176       "\n"
00177       "\tdialplan -> for outgoing call's dialed number\n"
00178       "\tlocaldialplan -> for outgoing call's callerid\n"
00179       "\t      (if -1 is set use the value from the asterisk channel)\n"
00180       "\tcpndialplan -> for incoming call's connected party number sent to caller\n"
00181       "\t      (if -1 is set use the value from the asterisk channel)\n"
00182       "\n"
00183       "\tdialplan options:\n"
00184       "\n"
00185       "\t0 - unknown\n"
00186       "\t1 - International\n"
00187       "\t2 - National\n"
00188       "\t4 - Subscriber" },
00189    { "cpndialplan", MISDN_CFG_CPNDIALPLAN, MISDN_CTYPE_INT, "0", NONE,
00190       "Dialplan means Type Of Number in ISDN Terms\n"
00191       "\tThere are different types of the dialplan:\n"
00192       "\n"
00193       "\tdialplan -> for outgoing call's dialed number\n"
00194       "\tlocaldialplan -> for outgoing call's callerid\n"
00195       "\t      (if -1 is set use the value from the asterisk channel)\n"
00196       "\tcpndialplan -> for incoming call's connected party number sent to caller\n"
00197       "\t      (if -1 is set use the value from the asterisk channel)\n"
00198       "\n"
00199       "\tdialplan options:\n"
00200       "\n"
00201       "\t0 - unknown\n"
00202       "\t1 - International\n"
00203       "\t2 - National\n"
00204       "\t4 - Subscriber" },
00205    { "unknownprefix", MISDN_CFG_TON_PREFIX_UNKNOWN, MISDN_CTYPE_STR, "", NONE,
00206       "Prefix for unknown numbers, this is put before an incoming number\n"
00207       "\tif its type-of-number is unknown." },
00208    { "internationalprefix", MISDN_CFG_TON_PREFIX_INTERNATIONAL, MISDN_CTYPE_STR, "00", NONE,
00209       "Prefix for international numbers, this is put before an incoming number\n"
00210       "\tif its type-of-number is international." },
00211    { "nationalprefix", MISDN_CFG_TON_PREFIX_NATIONAL, MISDN_CTYPE_STR, "0", NONE,
00212       "Prefix for national numbers, this is put before an incoming number\n"
00213       "\tif its type-of-number is national." },
00214    { "netspecificprefix", MISDN_CFG_TON_PREFIX_NETWORK_SPECIFIC, MISDN_CTYPE_STR, "", NONE,
00215       "Prefix for network-specific numbers, this is put before an incoming number\n"
00216       "\tif its type-of-number is network-specific." },
00217    { "subscriberprefix", MISDN_CFG_TON_PREFIX_SUBSCRIBER, MISDN_CTYPE_STR, "", NONE,
00218       "Prefix for subscriber numbers, this is put before an incoming number\n"
00219       "\tif its type-of-number is subscriber." },
00220    { "abbreviatedprefix", MISDN_CFG_TON_PREFIX_ABBREVIATED, MISDN_CTYPE_STR, "", NONE,
00221       "Prefix for abbreviated numbers, this is put before an incoming number\n"
00222       "\tif its type-of-number is abbreviated." },
00223    { "presentation", MISDN_CFG_PRES, MISDN_CTYPE_INT, "-1", NONE,
00224       "These (presentation and screen) are the exact isdn screening and presentation\n"
00225       "\tindicators.\n"
00226       "\tIf -1 is given for either value, the presentation indicators are used from\n"
00227       "\tAsterisk's CALLERPRES function.\n"
00228       "\n"
00229       "\tscreen=0, presentation=0 -> callerid presented\n"
00230       "\tscreen=1, presentation=1 -> callerid restricted (the remote end doesn't see it!)" },
00231    { "screen", MISDN_CFG_SCREEN, MISDN_CTYPE_INT, "-1", NONE,
00232       "These (presentation and screen) are the exact isdn screening and presentation\n"
00233       "\tindicators.\n"
00234       "\tIf -1 is given for either value, the presentation indicators are used from\n"
00235       "\tAsterisk's CALLERPRES function.\n"
00236       "\n"
00237       "\tscreen=0, presentation=0 -> callerid presented\n"
00238       "\tscreen=1, presentation=1 -> callerid restricted (the remote end doesn't see it!)" },
00239    { "outgoing_colp", MISDN_CFG_OUTGOING_COLP, MISDN_CTYPE_INT, "0", NONE,
00240       "Select what to do with outgoing COLP information on this port.\n"
00241       "\n"
00242       "\t0 - Send out COLP information unaltered.\n"
00243       "\t1 - Force COLP to restricted on all outgoing COLP information.\n"
00244       "\t2 - Do not send COLP information." },
00245    { "display_connected", MISDN_CFG_DISPLAY_CONNECTED, MISDN_CTYPE_INT, "0", NONE,
00246       "Put a display ie in the CONNECT message containing the following\n"
00247       "\tinformation if it is available (nt port only):\n"
00248       "\n"
00249       "\t0 - Do not put the connected line information in the display ie.\n"
00250       "\t1 - Put the available connected line name in the display ie.\n"
00251       "\t2 - Put the available connected line number in the display ie.\n"
00252       "\t3 - Put the available connected line name and number in the display ie." },
00253    { "display_setup", MISDN_CFG_DISPLAY_SETUP, MISDN_CTYPE_INT, "0", NONE,
00254       "Put a display ie in the SETUP message containing the following\n"
00255       "\tinformation if it is available (nt port only):\n"
00256       "\n"
00257       "\t0 - Do not put the caller information in the display ie.\n"
00258       "\t1 - Put the available caller name in the display ie.\n"
00259       "\t2 - Put the available caller number in the display ie.\n"
00260       "\t3 - Put the available caller name and number in the display ie." },
00261    { "always_immediate", MISDN_CFG_ALWAYS_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE,
00262       "Enable this to get into the s dialplan-extension.\n"
00263       "\tThere you can use DigitTimeout if you can't or don't want to use\n"
00264       "\tisdn overlap dial.\n"
00265       "\tNOTE: This will jump into the s extension for every exten!" },
00266    { "nodialtone", MISDN_CFG_NODIALTONE, MISDN_CTYPE_BOOL, "no", NONE,
00267       "Enable this to prevent chan_misdn to generate the dialtone\n"
00268       "\tThis makes only sense together with the always_immediate=yes option\n"
00269       "\tto generate your own dialtone with Playtones or so." },
00270    { "immediate", MISDN_CFG_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE,
00271       "Enable this if you want callers which called exactly the base\n"
00272       "\tnumber (so no extension is set) to jump into the s extension.\n"
00273       "\tIf the user dials something more, it jumps to the correct extension\n"
00274       "\tinstead." },
00275    { "senddtmf", MISDN_CFG_SENDDTMF, MISDN_CTYPE_BOOL, "no", NONE,
00276       "Enable this if we should produce DTMF Tones ourselves." },
00277    { "astdtmf", MISDN_CFG_ASTDTMF, MISDN_CTYPE_BOOL, "no", NONE,
00278       "Enable this if you want to use the Asterisk dtmf detector\n"
00279       "instead of the mISDN_dsp/hfcmulti one."
00280       },
00281    { "hold_allowed", MISDN_CFG_HOLD_ALLOWED, MISDN_CTYPE_BOOL, "no", NONE,
00282       "Enable this to have support for hold and retrieve." },
00283    { "early_bconnect", MISDN_CFG_EARLY_BCONNECT, MISDN_CTYPE_BOOL, "yes", NONE,
00284       "Disable this if you don't mind correct handling of Progress Indicators." },
00285    { "incoming_early_audio", MISDN_CFG_INCOMING_EARLY_AUDIO, MISDN_CTYPE_BOOL, "no", NONE,
00286       "Turn this on if you like to send Tone Indications to a Incoming\n"
00287       "\tisdn channel on a TE Port. Rarely used, only if the Telco allows\n"
00288       "\tyou to send indications by yourself, normally the Telco sends the\n"
00289       "\tindications to the remote party." },
00290    { "echocancel", MISDN_CFG_ECHOCANCEL, MISDN_CTYPE_BOOLINT, "0", 128,
00291       "This enables echo cancellation with the given number of taps.\n"
00292       "\tBe aware: Move this setting only to outgoing portgroups!\n"
00293       "\tA value of zero turns echo cancellation off.\n"
00294       "\n"
00295       "\tPossible values are: 0,32,64,128,256,yes(=128),no(=0)" },
00296 #ifdef MISDN_1_2
00297    { "pipeline", MISDN_CFG_PIPELINE, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
00298       "Set the configuration string for the mISDN dsp pipeline.\n"
00299       "\n"
00300       "\tExample for enabling the mg2 echo cancellation module with deftaps\n"
00301       "\tset to 128:\n"
00302       "\t\tmg2ec(deftaps=128)" },
00303 #endif
00304 #ifdef WITH_BEROEC
00305    { "bnechocancel", MISDN_CFG_BNECHOCANCEL, MISDN_CTYPE_BOOLINT, "yes", 64,
00306       "echotail in ms (1-200)" },
00307    { "bnec_antihowl", MISDN_CFG_BNEC_ANTIHOWL, MISDN_CTYPE_INT, "0", NONE,
00308       "Use antihowl" },
00309    { "bnec_nlp", MISDN_CFG_BNEC_NLP, MISDN_CTYPE_BOOL, "yes", NONE,
00310       "Nonlinear Processing (much faster adaption)" },
00311    { "bnec_zerocoeff", MISDN_CFG_BNEC_ZEROCOEFF, MISDN_CTYPE_BOOL, "no", NONE,
00312       "ZeroCoeffeciens" },
00313    { "bnec_tonedisabler", MISDN_CFG_BNEC_TD, MISDN_CTYPE_BOOL, "no", NONE,
00314       "Disable Tone" },
00315    { "bnec_adaption", MISDN_CFG_BNEC_ADAPT, MISDN_CTYPE_INT, "1", NONE,
00316       "Adaption mode (0=no,1=full,2=fast)" },
00317 #endif
00318    { "need_more_infos", MISDN_CFG_NEED_MORE_INFOS, MISDN_CTYPE_BOOL, "0", NONE,
00319       "Send Setup_Acknowledge on incoming calls anyway (instead of PROCEEDING),\n"
00320       "\tthis requests additional Infos, so we can waitfordigits without much\n"
00321       "\tissues. This works only for PTP Ports" },
00322    { "noautorespond_on_setup", MISDN_CFG_NOAUTORESPOND_ON_SETUP, MISDN_CTYPE_BOOL, "0", NONE,
00323       "Do not send SETUP_ACKNOWLEDGE or PROCEEDING automatically to the calling Party.\n"
00324       "Instead we directly jump into the dialplan. This might be useful for fast call\n"
00325       "rejection, or for some broken switches, that need hangup causes like busy in the.\n"
00326       "RELEASE_COMPLETE Message, instead of the DISCONNECT Message."},
00327    { "jitterbuffer", MISDN_CFG_JITTERBUFFER, MISDN_CTYPE_INT, "4000", NONE,
00328       "The jitterbuffer." },
00329    { "jitterbuffer_upper_threshold", MISDN_CFG_JITTERBUFFER_UPPER_THRESHOLD, MISDN_CTYPE_INT, "0", NONE,
00330       "Change this threshold to enable dejitter functionality." },
00331    { "callgroup", MISDN_CFG_CALLGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE,
00332       "Callgroup." },
00333    { "pickupgroup", MISDN_CFG_PICKUPGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE,
00334       "Pickupgroup." },
00335    { "namedcallgroup", MISDN_CFG_NAMEDCALLGROUP, MISDN_CTYPE_ASTNAMEDGROUP, NO_DEFAULT, NONE,
00336       "Named callgroup." },
00337    { "namedpickupgroup", MISDN_CFG_NAMEDPICKUPGROUP, MISDN_CTYPE_ASTNAMEDGROUP, NO_DEFAULT, NONE,
00338       "Named pickupgroup." },
00339    { "max_incoming", MISDN_CFG_MAX_IN, MISDN_CTYPE_INT, "-1", NONE,
00340       "Defines the maximum amount of incoming calls per port for this group.\n"
00341       "\tCalls which exceed the maximum will be marked with the channel variable\n"
00342       "\tMAX_OVERFLOW. It will contain the amount of overflowed calls" },
00343    { "max_outgoing", MISDN_CFG_MAX_OUT, MISDN_CTYPE_INT, "-1", NONE,
00344       "Defines the maximum amount of outgoing calls per port for this group\n"
00345       "\texceeding calls will be rejected" },
00346 
00347    { "reject_cause", MISDN_CFG_REJECT_CAUSE, MISDN_CTYPE_INT, "21", NONE,
00348       "Defines the cause with which a 3. call is rejected on PTMP BRI."},
00349    { "faxdetect", MISDN_CFG_FAXDETECT, MISDN_CTYPE_STR, "no", NONE,
00350       "Setup fax detection:\n"
00351       "\t    no        - no fax detection\n"
00352       "\t    incoming  - fax detection for incoming calls\n"
00353       "\t    outgoing  - fax detection for outgoing calls\n"
00354       "\t    both      - fax detection for incoming and outgoing calls\n"
00355       "\tAdd +nojump to your value (i.e. faxdetect=both+nojump) if you don't want to jump into the\n"
00356       "\tfax-extension but still want to detect the fax and prepare the channel for fax transfer." },
00357    { "faxdetect_timeout", MISDN_CFG_FAXDETECT_TIMEOUT, MISDN_CTYPE_INT, "5", NONE,
00358       "Number of seconds the fax detection should do its job. After the given period of time,\n"
00359       "\twe assume that it's not a fax call and save some CPU time by turning off fax detection.\n"
00360       "\tSet this to 0 if you don't want a timeout (never stop detecting)." },
00361    { "faxdetect_context", MISDN_CFG_FAXDETECT_CONTEXT, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
00362       "Context to jump into if we detect a fax. Don't set this if you want to stay in the current context." },
00363    { "l1watcher_timeout", MISDN_CFG_L1_TIMEOUT, MISDN_CTYPE_BOOLINT, "0", 4,
00364       "Monitors L1 of the port.  If L1 is down it tries\n"
00365       "\tto bring it up.  The polling timeout is given in seconds.\n"
00366       "\tSetting the value to 0 disables monitoring L1 of the port.\n"
00367       "\n"
00368       "\tThis option is only read at chan_misdn loading time.\n"
00369       "\tYou need to unload and load chan_misdn to change the\n"
00370       "\tvalue.  An asterisk restart will also do the trick." },
00371    { "overlapdial", MISDN_CFG_OVERLAP_DIAL, MISDN_CTYPE_BOOLINT, "0", 4,
00372       "Enables overlap dial for the given amount of seconds.\n"
00373       "\tPossible values are positive integers or:\n"
00374       "\t   yes (= 4 seconds)\n"
00375       "\t   no  (= 0 seconds = disabled)" },
00376    { "nttimeout", MISDN_CFG_NTTIMEOUT, MISDN_CTYPE_BOOL, "no", NONE ,
00377       "Set this to yes if you want calls disconnected in overlap mode\n"
00378       "\twhen a timeout happens." },
00379    { "bridging", MISDN_CFG_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
00380       "Set this to yes/no, default is yes.\n"
00381       "This can be used to have bridging enabled in general and to\n"
00382       "disable it for specific ports. It makes sense to disable\n"
00383       "bridging on NT Port where you plan to use the HOLD/RETRIEVE\n"
00384       "features with ISDN phones." },
00385    { "msns", MISDN_CFG_MSNS, MISDN_CTYPE_MSNLIST, "*", NONE,
00386       "MSN's for TE ports, listen on those numbers on the above ports, and\n"
00387       "\tindicate the incoming calls to Asterisk.\n"
00388       "\tHere you can give a comma separated list, or simply an '*' for any msn." },
00389    { "cc_request_retention", MISDN_CFG_CC_REQUEST_RETENTION, MISDN_CTYPE_BOOL, "yes", NONE,
00390       "Enable/Disable call-completion request retention support (ptp)." },
00391 };
00392 
00393 static const struct misdn_cfg_spec gen_spec[] = {
00394    { "debug", MISDN_GEN_DEBUG, MISDN_CTYPE_INT, "0", NONE,
00395       "Sets the debugging flag:\n"
00396       "\t0 - No Debug\n"
00397       "\t1 - mISDN Messages and * - Messages, and * - State changes\n"
00398       "\t2 - Messages + Message specific Informations (e.g. bearer capability)\n"
00399       "\t3 - very Verbose, the above + lots of Driver specific infos\n"
00400       "\t4 - even more Verbose than 3" },
00401 #ifndef MISDN_1_2
00402    { "misdn_init", MISDN_GEN_MISDN_INIT, MISDN_CTYPE_STR, "/etc/misdn-init.conf", NONE,
00403       "Set the path to the misdn-init.conf (for nt_ptp mode checking)." },
00404 #endif
00405    { "tracefile", MISDN_GEN_TRACEFILE, MISDN_CTYPE_STR, "/var/log/asterisk/misdn.log", NONE,
00406       "Set the path to the massively growing trace file, if you want that." },
00407    { "bridging", MISDN_GEN_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE,
00408       "Set this to yes if you want mISDN_dsp to bridge the calls in HW." },
00409    { "stop_tone_after_first_digit", MISDN_GEN_STOP_TONE, MISDN_CTYPE_BOOL, "yes", NONE,
00410       "Stops dialtone after getting first digit on NT Port." },
00411    { "append_digits2exten", MISDN_GEN_APPEND_DIGITS2EXTEN, MISDN_CTYPE_BOOL, "yes", NONE,
00412       "Whether to append overlapdialed Digits to Extension or not." },
00413    { "dynamic_crypt", MISDN_GEN_DYNAMIC_CRYPT, MISDN_CTYPE_BOOL, "no", NONE,
00414       "Whether to look out for dynamic crypting attempts." },
00415    { "crypt_prefix", MISDN_GEN_CRYPT_PREFIX, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
00416       "What is used for crypting Protocol." },
00417    { "crypt_keys", MISDN_GEN_CRYPT_KEYS, MISDN_CTYPE_STR, NO_DEFAULT, NONE,
00418       "Keys for cryption, you reference them in the dialplan\n"
00419       "\tLater also in dynamic encr." },
00420    { "ntkeepcalls", MISDN_GEN_NTKEEPCALLS, MISDN_CTYPE_BOOL, "no", NONE,
00421       "avoid dropping calls if the L2 goes down. some Nortel pbx\n"
00422       "do put down the L2/L1 for some milliseconds even if there\n"
00423       "are running calls. with this option you can avoid dropping them" },
00424    { "ntdebugflags", MISDN_GEN_NTDEBUGFLAGS, MISDN_CTYPE_INT, "0", NONE,
00425       "No description yet."},
00426    { "ntdebugfile", MISDN_GEN_NTDEBUGFILE, MISDN_CTYPE_STR, "/var/log/misdn-nt.log", NONE,
00427       "No description yet." }
00428 };
00429 
00430 
00431 /* array of port configs, default is at position 0. */
00432 static union misdn_cfg_pt **port_cfg;
00433 /* max number of available ports, is set on init */
00434 static int max_ports;
00435 /* general config */
00436 static union misdn_cfg_pt *general_cfg;
00437 /* storing the ptp flag separated to save memory */
00438 static int *ptp;
00439 /* maps enum config elements to array positions */
00440 static int *map;
00441 
00442 static ast_mutex_t config_mutex;
00443 
00444 #define CLI_ERROR(name, value, section) ({ \
00445    ast_log(LOG_WARNING, "misdn.conf: \"%s=%s\" (section: %s) invalid or out of range. " \
00446       "Please edit your misdn.conf and then do a \"misdn reload\".\n", name, value, section); \
00447 })
00448 
00449 static int _enum_array_map (void)
00450 {
00451    int i, j, ok;
00452 
00453    for (i = MISDN_CFG_FIRST + 1; i < MISDN_CFG_LAST; ++i) {
00454       if (i == MISDN_CFG_PTP)
00455          continue;
00456       ok = 0;
00457       for (j = 0; j < NUM_PORT_ELEMENTS; ++j) {
00458          if (port_spec[j].elem == i) {
00459             map[i] = j;
00460             ok = 1;
00461             break;
00462          }
00463       }
00464       if (!ok) {
00465          ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (port section) has no corresponding element in the config struct!\n", i);
00466          return -1;
00467       }
00468    }
00469    for (i = MISDN_GEN_FIRST + 1; i < MISDN_GEN_LAST; ++i) {
00470       ok = 0;
00471       for (j = 0; j < NUM_GEN_ELEMENTS; ++j) {
00472          if (gen_spec[j].elem == i) {
00473             map[i] = j;
00474             ok = 1;
00475             break;
00476          }
00477       }
00478       if (!ok) {
00479          ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (general section) has no corresponding element in the config struct!\n", i);
00480          return -1;
00481       }
00482    }
00483    return 0;
00484 }
00485 
00486 static int get_cfg_position (const char *name, int type)
00487 {
00488    int i;
00489 
00490    switch (type) {
00491    case PORT_CFG:
00492       for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
00493          if (!strcasecmp(name, port_spec[i].name))
00494             return i;
00495       }
00496       break;
00497    case GEN_CFG:
00498       for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
00499          if (!strcasecmp(name, gen_spec[i].name))
00500             return i;
00501       }
00502    }
00503 
00504    return -1;
00505 }
00506 
00507 static inline void misdn_cfg_lock (void)
00508 {
00509    ast_mutex_lock(&config_mutex);
00510 }
00511 
00512 static inline void misdn_cfg_unlock (void)
00513 {
00514    ast_mutex_unlock(&config_mutex);
00515 }
00516 
00517 static void _free_msn_list (struct msn_list* iter)
00518 {
00519    if (iter->next)
00520       _free_msn_list(iter->next);
00521    if (iter->msn)
00522       ast_free(iter->msn);
00523    ast_free(iter);
00524 }
00525 
00526 static void _free_port_cfg (void)
00527 {
00528    int i, j;
00529    int gn = map[MISDN_CFG_GROUPNAME];
00530    union misdn_cfg_pt* free_list[max_ports + 2];
00531 
00532    memset(free_list, 0, sizeof(free_list));
00533    free_list[0] = port_cfg[0];
00534    for (i = 1; i <= max_ports; ++i) {
00535       if (port_cfg[i][gn].str) {
00536          /* we always have a groupname in the non-default case, so this is fine */
00537          for (j = 1; j <= max_ports; ++j) {
00538             if (free_list[j] && free_list[j][gn].str == port_cfg[i][gn].str)
00539                break;
00540             else if (!free_list[j]) {
00541                free_list[j] = port_cfg[i];
00542                break;
00543             }
00544          }
00545       }
00546    }
00547    for (j = 0; free_list[j]; ++j) {
00548       for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
00549          if (free_list[j][i].any) {
00550             if (port_spec[i].type == MISDN_CTYPE_MSNLIST) {
00551                _free_msn_list(free_list[j][i].ml);
00552             } else if (port_spec[i].type == MISDN_CTYPE_ASTNAMEDGROUP) {
00553                ast_unref_namedgroups(free_list[j][i].namgrp);
00554             } else {
00555                ast_free(free_list[j][i].any);
00556             }
00557          }
00558       }
00559    }
00560 }
00561 
00562 static void _free_general_cfg (void)
00563 {
00564    int i;
00565 
00566    for (i = 0; i < NUM_GEN_ELEMENTS; i++)
00567       if (general_cfg[i].any)
00568          ast_free(general_cfg[i].any);
00569 }
00570 
00571 void misdn_cfg_get(int port, enum misdn_cfg_elements elem, void *buf, int bufsize)
00572 {
00573    int place;
00574 
00575    if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
00576       memset(buf, 0, bufsize);
00577       ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Port number %d is not valid.\n", port);
00578       return;
00579    }
00580 
00581    misdn_cfg_lock();
00582    if (elem == MISDN_CFG_PTP) {
00583       if (!memcpy(buf, &ptp[port], (bufsize > ptp[port]) ? sizeof(ptp[port]) : bufsize))
00584          memset(buf, 0, bufsize);
00585    } else {
00586       if ((place = map[elem]) < 0) {
00587          memset(buf, 0, bufsize);
00588          ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Invalid element (%d) requested.\n", elem);
00589       } else {
00590          if (elem < MISDN_CFG_LAST) {
00591             switch (port_spec[place].type) {
00592             case MISDN_CTYPE_STR:
00593                if (port_cfg[port][place].str) {
00594                   ast_copy_string(buf, port_cfg[port][place].str, bufsize);
00595                } else if (port_cfg[0][place].str) {
00596                   ast_copy_string(buf, port_cfg[0][place].str, bufsize);
00597                } else
00598                   memset(buf, 0, bufsize);
00599                break;
00600             case MISDN_CTYPE_ASTNAMEDGROUP:
00601                if (bufsize >= sizeof(struct ast_namedgroups *)) {
00602                   if (port_cfg[port][place].namgrp) {
00603                      *(struct ast_namedgroups **)buf = port_cfg[port][place].namgrp;
00604                   } else if (port_cfg[0][place].namgrp) {
00605                      *(struct ast_namedgroups **)buf = port_cfg[0][place].namgrp;
00606                   } else {
00607                      *(struct ast_namedgroups **)buf = NULL;
00608                   }
00609                }
00610                break;
00611             default:
00612                if (port_cfg[port][place].any)
00613                   memcpy(buf, port_cfg[port][place].any, bufsize);
00614                else if (port_cfg[0][place].any)
00615                   memcpy(buf, port_cfg[0][place].any, bufsize);
00616                else
00617                   memset(buf, 0, bufsize);
00618             }
00619          } else {
00620             switch (gen_spec[place].type) {
00621             case MISDN_CTYPE_STR:
00622                ast_copy_string(buf, S_OR(general_cfg[place].str, ""), bufsize);
00623                break;
00624             default:
00625                if (general_cfg[place].any)
00626                   memcpy(buf, general_cfg[place].any, bufsize);
00627                else
00628                   memset(buf, 0, bufsize);
00629             }
00630          }
00631       }
00632    }
00633    misdn_cfg_unlock();
00634 }
00635 
00636 enum misdn_cfg_elements misdn_cfg_get_elem(const char *name)
00637 {
00638    int pos;
00639 
00640    /* here comes a hack to replace the (not existing) "name" element with the "ports" element */
00641    if (!strcmp(name, "ports"))
00642       return MISDN_CFG_GROUPNAME;
00643    if (!strcmp(name, "name"))
00644       return MISDN_CFG_FIRST;
00645 
00646    pos = get_cfg_position(name, PORT_CFG);
00647    if (pos >= 0)
00648       return port_spec[pos].elem;
00649 
00650    pos = get_cfg_position(name, GEN_CFG);
00651    if (pos >= 0)
00652       return gen_spec[pos].elem;
00653 
00654    return MISDN_CFG_FIRST;
00655 }
00656 
00657 void misdn_cfg_get_name(enum misdn_cfg_elements elem, void *buf, int bufsize)
00658 {
00659    struct misdn_cfg_spec *spec = NULL;
00660    int place = map[elem];
00661 
00662    /* the ptp hack */
00663    if (elem == MISDN_CFG_PTP) {
00664       memset(buf, 0, 1);
00665       return;
00666    }
00667 
00668    /* here comes a hack to replace the (not existing) "name" element with the "ports" element */
00669    if (elem == MISDN_CFG_GROUPNAME) {
00670       if (!snprintf(buf, bufsize, "ports"))
00671          memset(buf, 0, 1);
00672       return;
00673    }
00674 
00675    if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
00676       spec = (struct misdn_cfg_spec *)port_spec;
00677    else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
00678       spec = (struct misdn_cfg_spec *)gen_spec;
00679 
00680    ast_copy_string(buf, spec ? spec[place].name : "", bufsize);
00681 }
00682 
00683 void misdn_cfg_get_desc (enum misdn_cfg_elements elem, void *buf, int bufsize, void *buf_default, int bufsize_default)
00684 {
00685    int place = map[elem];
00686    struct misdn_cfg_spec *spec = NULL;
00687 
00688    /* here comes a hack to replace the (not existing) "name" element with the "ports" element */
00689    if (elem == MISDN_CFG_GROUPNAME) {
00690       ast_copy_string(buf, ports_description, bufsize);
00691       if (buf_default && bufsize_default)
00692          memset(buf_default, 0, 1);
00693       return;
00694    }
00695 
00696    if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST))
00697       spec = (struct misdn_cfg_spec *)port_spec;
00698    else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST))
00699       spec = (struct misdn_cfg_spec *)gen_spec;
00700 
00701    if (!spec || !spec[place].desc)
00702       memset(buf, 0, 1);
00703    else {
00704       ast_copy_string(buf, spec[place].desc, bufsize);
00705       if (buf_default && bufsize) {
00706          if (!strcmp(spec[place].def, NO_DEFAULT))
00707             memset(buf_default, 0, 1);
00708          else
00709             ast_copy_string(buf_default, spec[place].def, bufsize_default);
00710       }
00711    }
00712 }
00713 
00714 int misdn_cfg_is_msn_valid (int port, char* msn)
00715 {
00716    int re = 0;
00717    struct msn_list *iter;
00718 
00719    if (!misdn_cfg_is_port_valid(port)) {
00720       ast_log(LOG_WARNING, "Invalid call to misdn_cfg_is_msn_valid! Port number %d is not valid.\n", port);
00721       return 0;
00722    }
00723 
00724    misdn_cfg_lock();
00725    if (port_cfg[port][map[MISDN_CFG_MSNS]].ml)
00726       iter = port_cfg[port][map[MISDN_CFG_MSNS]].ml;
00727    else
00728       iter = port_cfg[0][map[MISDN_CFG_MSNS]].ml;
00729    for (; iter; iter = iter->next)
00730       if (*(iter->msn) == '*' || ast_extension_match(iter->msn, msn)) {
00731          re = 1;
00732          break;
00733       }
00734    misdn_cfg_unlock();
00735 
00736    return re;
00737 }
00738 
00739 int misdn_cfg_is_port_valid (int port)
00740 {
00741    int gn = map[MISDN_CFG_GROUPNAME];
00742 
00743    return (port >= 1 && port <= max_ports && port_cfg[port][gn].str);
00744 }
00745 
00746 int misdn_cfg_is_group_method (char *group, enum misdn_cfg_method meth)
00747 {
00748    int i, re = 0;
00749    char *method ;
00750 
00751    misdn_cfg_lock();
00752 
00753    method = port_cfg[0][map[MISDN_CFG_METHOD]].str;
00754 
00755    for (i = 1; i <= max_ports; i++) {
00756       if (port_cfg[i] && port_cfg[i][map[MISDN_CFG_GROUPNAME]].str) {
00757          if (!strcasecmp(port_cfg[i][map[MISDN_CFG_GROUPNAME]].str, group))
00758             method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ?
00759                     port_cfg[i][map[MISDN_CFG_METHOD]].str : port_cfg[0][map[MISDN_CFG_METHOD]].str);
00760       }
00761    }
00762 
00763    if (method) {
00764       switch (meth) {
00765       case METHOD_STANDARD:      re = !strcasecmp(method, "standard");
00766                            break;
00767       case METHOD_ROUND_ROBIN:   re = !strcasecmp(method, "round_robin");
00768                            break;
00769       case METHOD_STANDARD_DEC:  re = !strcasecmp(method, "standard_dec");
00770                            break;
00771       }
00772    }
00773    misdn_cfg_unlock();
00774 
00775    return re;
00776 }
00777 
00778 /*!
00779  * \brief Generate a comma separated list of all active ports
00780  */
00781 void misdn_cfg_get_ports_string (char *ports)
00782 {
00783    char tmp[16];
00784    int l, i;
00785    int gn = map[MISDN_CFG_GROUPNAME];
00786 
00787    *ports = 0;
00788 
00789    misdn_cfg_lock();
00790    for (i = 1; i <= max_ports; i++) {
00791       if (port_cfg[i][gn].str) {
00792          if (ptp[i])
00793             sprintf(tmp, "%dptp,", i);
00794          else
00795             sprintf(tmp, "%d,", i);
00796          strcat(ports, tmp);
00797       }
00798    }
00799    misdn_cfg_unlock();
00800 
00801    if ((l = strlen(ports))) {
00802       /* Strip trailing ',' */
00803       ports[l-1] = 0;
00804    }
00805 }
00806 
00807 void misdn_cfg_get_config_string (int port, enum misdn_cfg_elements elem, char* buf, int bufsize)
00808 {
00809    int place;
00810    char tempbuf[BUFFERSIZE] = "";
00811    struct msn_list *iter;
00812 
00813    if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) {
00814       *buf = 0;
00815       ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Port number %d is not valid.\n", port);
00816       return;
00817    }
00818 
00819    place = map[elem];
00820 
00821    misdn_cfg_lock();
00822    if (elem == MISDN_CFG_PTP) {
00823       snprintf(buf, bufsize, " -> ptp: %s", ptp[port] ? "yes" : "no");
00824    }
00825    else if (elem > MISDN_CFG_FIRST && elem < MISDN_CFG_LAST) {
00826       switch (port_spec[place].type) {
00827       case MISDN_CTYPE_INT:
00828       case MISDN_CTYPE_BOOLINT:
00829          if (port_cfg[port][place].num)
00830             snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[port][place].num);
00831          else if (port_cfg[0][place].num)
00832             snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[0][place].num);
00833          else
00834             snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
00835          break;
00836       case MISDN_CTYPE_BOOL:
00837          if (port_cfg[port][place].num)
00838             snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[port][place].num ? "yes" : "no");
00839          else if (port_cfg[0][place].num)
00840             snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[0][place].num ? "yes" : "no");
00841          else
00842             snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
00843          break;
00844       case MISDN_CTYPE_ASTGROUP:
00845          if (port_cfg[port][place].grp)
00846             snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name,
00847                    ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[port][place].grp));
00848          else if (port_cfg[0][place].grp)
00849             snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name,
00850                    ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[0][place].grp));
00851          else
00852             snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
00853          break;
00854       case MISDN_CTYPE_ASTNAMEDGROUP:
00855          if (port_cfg[port][place].namgrp) {
00856             struct ast_str *tmp_str = ast_str_create(1024);
00857             if (tmp_str) {
00858                snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name,
00859                      ast_print_namedgroups(&tmp_str, port_cfg[port][place].namgrp));
00860                ast_free(tmp_str);
00861             }
00862          } else if (port_cfg[0][place].namgrp) {
00863             struct ast_str *tmp_str = ast_str_create(1024);
00864             if (tmp_str) {
00865                snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name,
00866                      ast_print_namedgroups(&tmp_str, port_cfg[0][place].namgrp));
00867                ast_free(tmp_str);
00868             }
00869          } else {
00870             snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
00871          }
00872          break;
00873       case MISDN_CTYPE_MSNLIST:
00874          if (port_cfg[port][place].ml)
00875             iter = port_cfg[port][place].ml;
00876          else
00877             iter = port_cfg[0][place].ml;
00878          if (iter) {
00879             for (; iter; iter = iter->next) {
00880                strncat(tempbuf, iter->msn, sizeof(tempbuf) - strlen(tempbuf) - 1);
00881             }
00882             if (strlen(tempbuf) > 1) {
00883                tempbuf[strlen(tempbuf)-2] = 0;
00884             }
00885          }
00886          snprintf(buf, bufsize, " -> msns: %s", *tempbuf ? tempbuf : "none");
00887          break;
00888       case MISDN_CTYPE_STR:
00889          if ( port_cfg[port][place].str) {
00890             snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[port][place].str);
00891          } else if (port_cfg[0][place].str) {
00892             snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[0][place].str);
00893          } else {
00894             snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
00895          }
00896          break;
00897       }
00898    } else if (elem > MISDN_GEN_FIRST && elem < MISDN_GEN_LAST) {
00899       switch (gen_spec[place].type) {
00900       case MISDN_CTYPE_INT:
00901       case MISDN_CTYPE_BOOLINT:
00902          if (general_cfg[place].num)
00903             snprintf(buf, bufsize, " -> %s: %d", gen_spec[place].name, *general_cfg[place].num);
00904          else
00905             snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
00906          break;
00907       case MISDN_CTYPE_BOOL:
00908          if (general_cfg[place].num)
00909             snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, *general_cfg[place].num ? "yes" : "no");
00910          else
00911             snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
00912          break;
00913       case MISDN_CTYPE_STR:
00914          if ( general_cfg[place].str) {
00915             snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, general_cfg[place].str);
00916          } else {
00917             snprintf(buf, bufsize, " -> %s:", gen_spec[place].name);
00918          }
00919          break;
00920       default:
00921          snprintf(buf, bufsize, " -> type of %s not handled yet", gen_spec[place].name);
00922          break;
00923       }
00924    } else {
00925       *buf = 0;
00926       ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Invalid config element (%d) requested.\n", elem);
00927    }
00928    misdn_cfg_unlock();
00929 }
00930 
00931 int misdn_cfg_get_next_port (int port)
00932 {
00933    int p = -1;
00934    int gn = map[MISDN_CFG_GROUPNAME];
00935 
00936    misdn_cfg_lock();
00937    for (port++; port <= max_ports; port++) {
00938       if (port_cfg[port][gn].str) {
00939          p = port;
00940          break;
00941       }
00942    }
00943    misdn_cfg_unlock();
00944 
00945    return p;
00946 }
00947 
00948 int misdn_cfg_get_next_port_spin (int port)
00949 {
00950    int p = misdn_cfg_get_next_port(port);
00951    return (p > 0) ? p : misdn_cfg_get_next_port(0);
00952 }
00953 
00954 static int _parse (union misdn_cfg_pt *dest, const char *value, enum misdn_cfg_type type, int boolint_def)
00955 {
00956    int re = 0;
00957    int len, tmp;
00958    char *valtmp;
00959    char *tmp2 = ast_strdupa(value);
00960 
00961    switch (type) {
00962    case MISDN_CTYPE_STR:
00963       if (dest->str) {
00964          ast_free(dest->str);
00965       }
00966       if ((len = strlen(value))) {
00967          dest->str = ast_malloc((len + 1) * sizeof(char));
00968          strncpy(dest->str, value, len);
00969          dest->str[len] = 0;
00970       } else {
00971          dest->str = ast_malloc(sizeof(char));
00972          dest->str[0] = 0;
00973       }
00974       break;
00975    case MISDN_CTYPE_INT:
00976    {
00977       int res;
00978 
00979       if (strchr(value,'x')) {
00980          res = sscanf(value, "%30x", &tmp);
00981       } else {
00982          res = sscanf(value, "%30d", &tmp);
00983       }
00984       if (res) {
00985          if (!dest->num) {
00986             dest->num = ast_malloc(sizeof(int));
00987          }
00988          memcpy(dest->num, &tmp, sizeof(int));
00989       } else
00990          re = -1;
00991    }
00992       break;
00993    case MISDN_CTYPE_BOOL:
00994       if (!dest->num) {
00995          dest->num = ast_malloc(sizeof(int));
00996       }
00997       *(dest->num) = (ast_true(value) ? 1 : 0);
00998       break;
00999    case MISDN_CTYPE_BOOLINT:
01000       if (!dest->num) {
01001          dest->num = ast_malloc(sizeof(int));
01002       }
01003       if (sscanf(value, "%30d", &tmp)) {
01004          memcpy(dest->num, &tmp, sizeof(int));
01005       } else {
01006          *(dest->num) = (ast_true(value) ? boolint_def : 0);
01007       }
01008       break;
01009    case MISDN_CTYPE_MSNLIST:
01010       for (valtmp = strsep(&tmp2, ","); valtmp; valtmp = strsep(&tmp2, ",")) {
01011          if ((len = strlen(valtmp))) {
01012             struct msn_list *ml = ast_malloc(sizeof(*ml));
01013             ml->msn = ast_calloc(len+1, sizeof(char));
01014             strncpy(ml->msn, valtmp, len);
01015             ml->next = dest->ml;
01016             dest->ml = ml;
01017          }
01018       }
01019       break;
01020    case MISDN_CTYPE_ASTGROUP:
01021       if (!dest->grp) {
01022          dest->grp = ast_malloc(sizeof(ast_group_t));
01023       }
01024       *(dest->grp) = ast_get_group(value);
01025       break;
01026    case MISDN_CTYPE_ASTNAMEDGROUP:
01027       dest->namgrp = ast_get_namedgroups(value);
01028       break;
01029    }
01030 
01031    return re;
01032 }
01033 
01034 static void _build_general_config (struct ast_variable *v)
01035 {
01036    int pos;
01037 
01038    for (; v; v = v->next) {
01039       if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
01040          continue;
01041       if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) ||
01042          (_parse(&general_cfg[pos], v->value, gen_spec[pos].type, gen_spec[pos].boolint_def) < 0))
01043          CLI_ERROR(v->name, v->value, "general");
01044    }
01045 }
01046 
01047 static void _build_port_config (struct ast_variable *v, char *cat)
01048 {
01049    int pos, i;
01050    union misdn_cfg_pt cfg_tmp[NUM_PORT_ELEMENTS];
01051    int cfg_for_ports[max_ports + 1];
01052 
01053    if (!v || !cat)
01054       return;
01055 
01056    memset(cfg_tmp, 0, sizeof(cfg_tmp));
01057    memset(cfg_for_ports, 0, sizeof(cfg_for_ports));
01058 
01059    if (!strcasecmp(cat, "default")) {
01060       cfg_for_ports[0] = 1;
01061    }
01062 
01063    if (((pos = get_cfg_position("name", PORT_CFG)) < 0) ||
01064       (_parse(&cfg_tmp[pos], cat, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) {
01065       CLI_ERROR(v->name, v->value, cat);
01066       return;
01067    }
01068 
01069    for (; v; v = v->next) {
01070       if (!strcasecmp(v->name, "ports")) {
01071          char *token, *tmp = ast_strdupa(v->value);
01072          char ptpbuf[BUFFERSIZE] = "";
01073          int start, end;
01074          for (token = strsep(&tmp, ","); token; token = strsep(&tmp, ","), *ptpbuf = 0) {
01075             if (!*token)
01076                continue;
01077             if (sscanf(token, "%30d-%30d%511s", &start, &end, ptpbuf) >= 2) {
01078                for (; start <= end; start++) {
01079                   if (start <= max_ports && start > 0) {
01080                      cfg_for_ports[start] = 1;
01081                      ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
01082                   } else
01083                      CLI_ERROR(v->name, v->value, cat);
01084                }
01085             } else {
01086                if (sscanf(token, "%30d%511s", &start, ptpbuf)) {
01087                   if (start <= max_ports && start > 0) {
01088                      cfg_for_ports[start] = 1;
01089                      ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0;
01090                   } else
01091                      CLI_ERROR(v->name, v->value, cat);
01092                } else
01093                   CLI_ERROR(v->name, v->value, cat);
01094             }
01095          }
01096       } else {
01097          if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) ||
01098             (_parse(&cfg_tmp[pos], v->value, port_spec[pos].type, port_spec[pos].boolint_def) < 0))
01099             CLI_ERROR(v->name, v->value, cat);
01100       }
01101    }
01102 
01103    for (i = 0; i < (max_ports + 1); ++i) {
01104       if (i > 0 && cfg_for_ports[0]) {
01105          /* default category, will populate the port_cfg with additional port
01106          categories in subsequent calls to this function */
01107          memset(cfg_tmp, 0, sizeof(cfg_tmp));
01108       }
01109       if (cfg_for_ports[i]) {
01110          memcpy(port_cfg[i], cfg_tmp, sizeof(cfg_tmp));
01111       }
01112    }
01113 }
01114 
01115 void misdn_cfg_update_ptp (void)
01116 {
01117 #ifndef MISDN_1_2
01118    char misdn_init[BUFFERSIZE];
01119    char line[BUFFERSIZE];
01120    FILE *fp;
01121    char *tok, *p, *end;
01122    int port;
01123 
01124    misdn_cfg_get(0, MISDN_GEN_MISDN_INIT, &misdn_init, sizeof(misdn_init));
01125 
01126    if (!ast_strlen_zero(misdn_init)) {
01127       fp = fopen(misdn_init, "r");
01128       if (fp) {
01129          while(fgets(line, sizeof(line), fp)) {
01130             if (!strncmp(line, "nt_ptp", 6)) {
01131                for (tok = strtok_r(line,",=", &p);
01132                    tok;
01133                    tok = strtok_r(NULL,",=", &p)) {
01134                   port = strtol(tok, &end, 10);
01135                   if (end != tok && misdn_cfg_is_port_valid(port)) {
01136                      misdn_cfg_lock();
01137                      ptp[port] = 1;
01138                      misdn_cfg_unlock();
01139                   }
01140                }
01141             }
01142          }
01143          fclose(fp);
01144       } else {
01145          ast_log(LOG_WARNING,"Couldn't open %s: %s\n", misdn_init, strerror(errno));
01146       }
01147    }
01148 #else
01149    int i;
01150    int proto;
01151    char filename[128];
01152    FILE *fp;
01153 
01154    for (i = 1; i <= max_ports; ++i) {
01155       snprintf(filename, sizeof(filename), "/sys/class/mISDN-stacks/st-%08x/protocol", i << 8);
01156       fp = fopen(filename, "r");
01157       if (!fp) {
01158          ast_log(LOG_WARNING, "Could not open %s: %s\n", filename, strerror(errno));
01159          continue;
01160       }
01161       if (fscanf(fp, "0x%08x", &proto) != 1)
01162          ast_log(LOG_WARNING, "Could not parse contents of %s!\n", filename);
01163       else
01164          ptp[i] = proto & 1<<5 ? 1 : 0;
01165       fclose(fp);
01166    }
01167 #endif
01168 }
01169 
01170 static void _fill_defaults (void)
01171 {
01172    int i;
01173 
01174    for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
01175       if (!port_cfg[0][i].any && strcasecmp(port_spec[i].def, NO_DEFAULT))
01176          _parse(&(port_cfg[0][i]), (char *)port_spec[i].def, port_spec[i].type, port_spec[i].boolint_def);
01177    }
01178    for (i = 0; i < NUM_GEN_ELEMENTS; ++i) {
01179       if (!general_cfg[i].any && strcasecmp(gen_spec[i].def, NO_DEFAULT))
01180          _parse(&(general_cfg[i]), (char *)gen_spec[i].def, gen_spec[i].type, gen_spec[i].boolint_def);
01181    }
01182 }
01183 
01184 void misdn_cfg_reload (void)
01185 {
01186    misdn_cfg_init(0, 1);
01187 }
01188 
01189 void misdn_cfg_destroy (void)
01190 {
01191    misdn_cfg_lock();
01192 
01193    _free_port_cfg();
01194    _free_general_cfg();
01195 
01196    ast_free(port_cfg);
01197    ast_free(general_cfg);
01198    ast_free(ptp);
01199    ast_free(map);
01200 
01201    misdn_cfg_unlock();
01202    ast_mutex_destroy(&config_mutex);
01203 }
01204 
01205 int misdn_cfg_init(int this_max_ports, int reload)
01206 {
01207    char config[] = "misdn.conf";
01208    char *cat, *p;
01209    int i;
01210    struct ast_config *cfg;
01211    struct ast_variable *v;
01212    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01213 
01214    if (!(cfg = ast_config_load2(config, "chan_misdn", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
01215       ast_log(LOG_WARNING, "missing or invalid file: misdn.conf\n");
01216       return -1;
01217    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
01218       return 0;
01219 
01220    ast_mutex_init(&config_mutex);
01221 
01222    /* Copy the default jb config over global_jbconf */
01223    memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
01224 
01225    misdn_cfg_lock();
01226 
01227    if (this_max_ports) {
01228       /* this is the first run */
01229       max_ports = this_max_ports;
01230       map = ast_calloc(MISDN_GEN_LAST + 1, sizeof(int));
01231       if (_enum_array_map())
01232          return -1;
01233       p = ast_calloc(1, (max_ports + 1) * sizeof(union misdn_cfg_pt *)
01234                      + (max_ports + 1) * NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt));
01235       port_cfg = (union misdn_cfg_pt **)p;
01236       p += (max_ports + 1) * sizeof(union misdn_cfg_pt *);
01237       for (i = 0; i <= max_ports; ++i) {
01238          port_cfg[i] = (union misdn_cfg_pt *)p;
01239          p += NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt);
01240       }
01241       general_cfg = ast_calloc(1, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
01242       ptp = ast_calloc(max_ports + 1, sizeof(int));
01243    }
01244    else {
01245       /* misdn reload */
01246       _free_port_cfg();
01247       _free_general_cfg();
01248       memset(port_cfg[0], 0, NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt) * (max_ports + 1));
01249       memset(general_cfg, 0, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS);
01250       memset(ptp, 0, sizeof(int) * (max_ports + 1));
01251    }
01252 
01253    cat = ast_category_browse(cfg, NULL);
01254 
01255    while(cat) {
01256       v = ast_variable_browse(cfg, cat);
01257       if (!strcasecmp(cat, "general")) {
01258          _build_general_config(v);
01259       } else {
01260          _build_port_config(v, cat);
01261       }
01262       cat = ast_category_browse(cfg, cat);
01263    }
01264 
01265    _fill_defaults();
01266 
01267    misdn_cfg_unlock();
01268    ast_config_destroy(cfg);
01269 
01270    return 0;
01271 }
01272 
01273 struct ast_jb_conf *misdn_get_global_jbconf() {
01274    return &global_jbconf;
01275 }