1208 lines
40 KiB
C
Executable File
1208 lines
40 KiB
C
Executable File
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
|
|
#include "cpr_stdio.h"
|
|
#include "cpr_stdlib.h"
|
|
#include "cpr_string.h"
|
|
#include "xml_defs.h"
|
|
#include "logger.h"
|
|
#include "logmsg.h"
|
|
#include "util_parse.h"
|
|
#include "debug.h"
|
|
#include "phone_types.h"
|
|
#include "regmgrapi.h"
|
|
|
|
|
|
#include "upgrade.h"
|
|
#include "dialplan.h"
|
|
|
|
extern char DirectoryBuffer[DIALPLAN_MAX_SIZE];
|
|
|
|
/*
|
|
* Tones with Bellcore- are Bellcore defined tones.
|
|
* Tones with Cisco- are our tones.
|
|
* The order of these names MUST match the order of
|
|
* the vcm_tones_t type in vcm.h
|
|
*/
|
|
const char *tone_names[] = {
|
|
"Bellcore-Inside",
|
|
"Bellcore-Outside",
|
|
"Bellcore-Busy",
|
|
"Bellcore-Alerting",
|
|
"Bellcore-BusyVerify",
|
|
"Bellcore-Stutter",
|
|
"Bellcore-MsgWaiting",
|
|
"Bellcore-Reorder",
|
|
"Bellcore-CallWaiting",
|
|
"Bellcore-Cw2",
|
|
"Bellcore-Cw3",
|
|
"Bellcore-Cw4",
|
|
"Bellcore-Hold",
|
|
"Bellcore-Confirmation",
|
|
"Bellcore-Permanent",
|
|
"Bellcore-Reminder",
|
|
"Bellcore-None",
|
|
"Cisco-ZipZip",
|
|
"Cisco-Zip",
|
|
"Cisco-BeepBonk"
|
|
};
|
|
|
|
static struct DialTemplate *basetemplate;
|
|
char DialTemplateFile[MAX_TEMPLATE_LENGTH];
|
|
char g_dp_version_stamp[MAX_DP_VERSION_STAMP_LEN];
|
|
|
|
/*
|
|
* Function: addbytes()
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Description:
|
|
*
|
|
* Returns: None
|
|
*/
|
|
static void
|
|
addbytes (char **output, int *outlen, const char *input, int inlen)
|
|
{
|
|
char *target = *output;
|
|
|
|
if (inlen == -1) {
|
|
inlen = strlen(input);
|
|
}
|
|
if (inlen >= *outlen) {
|
|
inlen = *outlen - 1;
|
|
}
|
|
memcpy(target, input, inlen);
|
|
target += inlen;
|
|
*outlen += inlen;
|
|
*output = target;
|
|
/*
|
|
* Null terminate the result
|
|
*/
|
|
*target = '\0';
|
|
}
|
|
|
|
/*
|
|
* Function: poundDialingEnabled()
|
|
*
|
|
* Parameters: None
|
|
*
|
|
* Description: Determines if '#' is treated as a "dial now" character
|
|
*
|
|
* Returns: TRUE if # is treated as end of dial signal
|
|
* FALSE if # is treated as a dialed digit
|
|
*/
|
|
static boolean
|
|
poundDialingEnabled (void)
|
|
{
|
|
if (sip_regmgr_get_cc_mode(1) == REG_MODE_NON_CCM) {
|
|
/*
|
|
* Operating in Peer-to-Peer SIP mode, allow # dialing
|
|
*/
|
|
return (TRUE);
|
|
} else {
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: isDialedDigit()
|
|
*
|
|
* Parameters: input - single char
|
|
*
|
|
* Description: Determine if the char is 0-9 or +, *
|
|
* # is matched here unless it is set to
|
|
* be used as "dial immediately"
|
|
*
|
|
* Returns: DialMatchAction
|
|
*/
|
|
boolean
|
|
isDialedDigit (char input)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
if (!isdigit(input)) {
|
|
if ((input == '*') || (input == '+') || ((input == '#') && (!poundDialingEnabled()))) {
|
|
result = TRUE;
|
|
}
|
|
} else {
|
|
result = TRUE;
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* Function: MatchLineNumber()
|
|
*
|
|
* Parameters: templateLine - line number specified in Dial Plan
|
|
* line - line number to match
|
|
*
|
|
* Description: The template line numbers are initialized to zero.
|
|
* Zero means that all lines match. If the Line parm
|
|
* is specified in the Dial Plan template, then its
|
|
* value must match the specified line.
|
|
*^M
|
|
* Returns: boolean
|
|
*/
|
|
static boolean
|
|
MatchLineNumber (const line_t templateLine, const line_t line)
|
|
{
|
|
/* Zero in the template matches any line. */
|
|
return (boolean) ((templateLine == line) || (templateLine == 0));
|
|
}
|
|
|
|
/*
|
|
* Function: MatchDialTemplate()
|
|
*
|
|
* Parameters: pattern - pattern string to match
|
|
* line - line number to match
|
|
* (May be 0 to match all lines)
|
|
* timeout - returned dial timeout in seconds
|
|
* (May be NULL to not get a timeout)
|
|
* rewrite - buffer to hold rewritten string
|
|
* (May be NULL for no rewrite)
|
|
* rewritelen - Bytes available in the buffer to write to
|
|
* routemode - pointer to location to hold route mode returned
|
|
* (May be NULL to not get a routemode)
|
|
* tone - pointer to location to hold tone returned
|
|
*
|
|
* Description: Find the best template to match a pattern
|
|
*
|
|
* Returns: DialMatchAction
|
|
*/
|
|
DialMatchAction
|
|
MatchDialTemplate (const char *pattern,
|
|
const line_t line,
|
|
int *timeout,
|
|
char *rewrite,
|
|
int rewritelen,
|
|
RouteMode *pRouteMode,
|
|
vcm_tones_t *pTone)
|
|
{
|
|
DialMatchAction result = DIAL_NOMATCH;
|
|
struct DialTemplate *ptempl = basetemplate;
|
|
struct DialTemplate *pbestmatch = NULL;
|
|
boolean bestmatch_dialnow = FALSE;
|
|
int best_comma_count = 0;
|
|
DialMatchAction partialmatch_type = DIAL_NOMATCH;
|
|
boolean partialmatch = FALSE;
|
|
|
|
int matchlen = 0;
|
|
int partialmatchlen = 0;
|
|
int givedialtone = 0;
|
|
int comma_counter = 0;
|
|
|
|
/*
|
|
* We need to provide a default rewrite string in case we do not have any template matches.
|
|
* This happens when there is no template match (such as no * pattern) and when they have
|
|
* no dial plan file at all
|
|
*/
|
|
if (rewrite != NULL) {
|
|
char *output = rewrite;
|
|
int room = rewritelen;
|
|
|
|
addbytes(&output, &room, pattern, -1);
|
|
}
|
|
|
|
/*
|
|
* If the dialplan is empty, check to see if a # is in the pattern
|
|
* and return DIAL_IMMEDIATELY. If not go ahead and return
|
|
* DIAL_NOMATCH since there will be no template match in
|
|
* an empty dialplan.
|
|
*/
|
|
if (ptempl == NULL) {
|
|
if (strchr(pattern, '#') && (poundDialingEnabled())) {
|
|
return DIAL_IMMEDIATELY;
|
|
} else {
|
|
return DIAL_NOMATCH;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Iterate through all the templates. Skip the this template if it's not
|
|
* for this line or all lines.
|
|
*/
|
|
while (ptempl != NULL) {
|
|
if (MatchLineNumber(ptempl->line, line)) {
|
|
char *pinput = (char *) pattern;
|
|
char *pmatch = ptempl->pattern;
|
|
int thismatchlen = 0;
|
|
DialMatchAction thismatch = DIAL_FULLMATCH;
|
|
char *subs[MAX_SUBTITUTIONS];
|
|
int subslen[MAX_SUBTITUTIONS];
|
|
int subscount = -1;
|
|
boolean dialnow = FALSE;
|
|
|
|
while (*pinput) {
|
|
int idx;
|
|
|
|
/* Since the code below combines multiple ,
|
|
* in a row into "one" , only increment
|
|
* comma counter once instead of once
|
|
* for each comma combined.
|
|
*/
|
|
if (pmatch[0] == ',') {
|
|
comma_counter++;
|
|
}
|
|
|
|
/*
|
|
* Skip over any dial tone characters
|
|
*/
|
|
while (pmatch[0] == ',') {
|
|
pmatch++;
|
|
}
|
|
/*
|
|
* If this is a pattern character, we need to capture the digits for the
|
|
* substitution strings
|
|
*/
|
|
if (((pmatch[0] == '.') && isDialedDigit(pinput[0])) ||
|
|
(pmatch[0] == '*')) {
|
|
/*
|
|
* Get the next index in the substitution array (if any)
|
|
* Note that if they have more pattern sections in the pattern string
|
|
* the last one will get the counts for the characters. So for example
|
|
* with the MAX_SUBSTITUTIONS of 5 and a pattern of
|
|
* 1.2.3.4.5.6.7.
|
|
* and an input string of
|
|
* 1a2b3c4d5e6f7g
|
|
* the arrays of substitions would come out as follows:
|
|
* %0 = 1a2b3c4d5e6f7g (as expected)
|
|
* %1 = a (as expected)
|
|
* %2 = b (as expected)
|
|
* %3 = c (as expected)
|
|
* %4 = d (as expected)
|
|
* %5 = e6f NOT what they really wanted, but predictable from the algorithm
|
|
*/
|
|
if (subscount < (MAX_SUBTITUTIONS - 1)) {
|
|
subscount++;
|
|
subs[subscount] = pinput;
|
|
subslen[subscount] = 1;
|
|
}
|
|
if (pmatch[0] == '.') {
|
|
thismatch = DIAL_FULLPATTERN;
|
|
/*
|
|
* . in the pattern will match anything but doesn't contribute
|
|
* to our matching length for finding the best template
|
|
*/
|
|
while (isdigit(pinput[1]) && (pmatch[1] == '.')) {
|
|
pinput++;
|
|
pmatch++;
|
|
subslen[subscount]++;
|
|
}
|
|
} else {
|
|
thismatch = DIAL_WILDPATTERN;
|
|
/*
|
|
* '*' is a wild card to match 1 or more characters
|
|
* Do a hungry first match
|
|
*
|
|
* Note: If match is currently pointing to an escape character,
|
|
* we need to go past it to match the actual character.
|
|
*/
|
|
if (pmatch[1] == DIAL_ESCAPE) {
|
|
idx = 2;
|
|
} else {
|
|
idx = 1;
|
|
}
|
|
|
|
/*
|
|
* The '*' will not match the '#' character since its' default use
|
|
* causes the phone to "dial immediately"
|
|
*/
|
|
if ((pinput[0] == '#') && (poundDialingEnabled())) {
|
|
dialnow = TRUE;
|
|
} else {
|
|
while ((pinput[1] != '\0') &&
|
|
(pinput[1] != pmatch[idx])) {
|
|
/*
|
|
* If '#' is found and pound dialing is enabled, break out of the loop.
|
|
*/
|
|
if ((pinput[1] == '#') &&
|
|
(poundDialingEnabled())) {
|
|
break;
|
|
}
|
|
pinput++;
|
|
subslen[subscount]++;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Any other character must match exactly
|
|
*/
|
|
} else {
|
|
/*
|
|
* Look for the Escape character '\' and remove it
|
|
* Right now, the only character that needs to be escaped is '*'
|
|
* Note that we treat \ at the end of a line as a non-special
|
|
* character
|
|
*/
|
|
if ((pmatch[0] == DIAL_ESCAPE) && (pmatch[1] != '\0')) {
|
|
pmatch++;
|
|
}
|
|
|
|
if (pmatch[0] != pinput[0]) {
|
|
/*
|
|
* We found a '#' that doesn't match the current match template
|
|
* This means that the '#" should be interpreted as the dial
|
|
* termination character.
|
|
*/
|
|
if ((pinput[0] == '#') && (poundDialingEnabled())) {
|
|
dialnow = TRUE;
|
|
break;
|
|
}
|
|
/*
|
|
* No match, so abandon with no pattern to select
|
|
*/
|
|
thismatchlen = -1;
|
|
thismatch = DIAL_NOMATCH;
|
|
break;
|
|
|
|
} else {
|
|
/*
|
|
* We matched one character, count it for the overall template match
|
|
*/
|
|
thismatchlen++;
|
|
}
|
|
}
|
|
pmatch++;
|
|
pinput++;
|
|
}
|
|
|
|
/*
|
|
* *pinput = NULL means we matched everything that
|
|
* was dialed in this template
|
|
* Partial never matches * rules
|
|
* Since 97. should have precendence over 9..
|
|
* also check matchlen.
|
|
* Since fullmatches (exact digits) have precendence
|
|
* over fullpattern (.) check matchtype
|
|
*/
|
|
if ((*pinput == NUL) || (dialnow)) {
|
|
if ((thismatchlen > partialmatchlen) ||
|
|
((thismatchlen == partialmatchlen) &&
|
|
(thismatch > partialmatch_type))) {
|
|
partialmatch_type = thismatch;
|
|
partialmatchlen = thismatchlen;
|
|
pbestmatch = ptempl;
|
|
partialmatch = TRUE;
|
|
bestmatch_dialnow = dialnow;
|
|
best_comma_count = comma_counter;
|
|
result = DIAL_NOMATCH;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we exhausted the match string, then the template is a perfect match
|
|
* However, we don't want to take this as the best template unless it matched
|
|
* more digits than any other pattern. For example if we have a pattern of
|
|
* 9011*
|
|
* 9.11
|
|
* We would want 9011 to match against the first one even though it is not complete
|
|
*
|
|
* We also have to be careful that a pattern such as
|
|
* *
|
|
* does not beat something like
|
|
* 9.......
|
|
* when you have 94694210
|
|
*/
|
|
if (pmatch[0] == '\0') {
|
|
/*
|
|
* If this pattern is better, we want to adopt it
|
|
*/
|
|
if ((thismatchlen > matchlen) ||
|
|
((thismatchlen == matchlen) && (thismatch > result)) ||
|
|
((thismatch == DIAL_WILDPATTERN) &&
|
|
((result == DIAL_NOMATCH) && (partialmatch == FALSE)))) {
|
|
/*
|
|
* this is a better match than what we found before
|
|
*/
|
|
pbestmatch = ptempl;
|
|
bestmatch_dialnow = dialnow;
|
|
matchlen = thismatchlen;
|
|
result = thismatch;
|
|
/*
|
|
* Generate a rewrite string
|
|
*
|
|
* <TEMPLATE MATCH="31." Timeout="0" User="Phone" Rewrite="11111111%s"/>
|
|
* 310 -> 111111110
|
|
* <TEMPLATE MATCH="32.." Timeout="0" User="Phone" Rewrite="122222232.."/>
|
|
* 3255 -> 12222223255
|
|
* <TEMPLATE MATCH="33..." Timeout="0" User="Phone" Rewrite="13333333%1"/>
|
|
* <TEMPLATE MATCH="34..1.." Timeout="0" User="Phone" Rewrite="14444%155%2"/>
|
|
* <TEMPLATE MATCH="34..2.." Timeout="0" User="Phone" Rewrite="1444%s"/>
|
|
* <TEMPLATE MATCH="34..3.." Timeout="0" User="Phone" Rewrite="11..11.."/>
|
|
*/
|
|
if (rewrite != NULL) {
|
|
int dotindex = -1;
|
|
int dotsleft = 0;
|
|
char *output = rewrite;
|
|
int room = rewritelen;
|
|
char *prewrite = pbestmatch->rewrite;
|
|
|
|
if ((prewrite == NULL) || (prewrite[0] == '\0')) {
|
|
/*
|
|
* Null or empty patterns produce the input as the result
|
|
*/
|
|
addbytes(&output, &room, pattern, -1);
|
|
} else {
|
|
while (prewrite[0] != '\0') {
|
|
if (prewrite[0] == '.') {
|
|
/*
|
|
* For a dot, we copy over the single previously matched character
|
|
*/
|
|
while ((dotsleft == 0) &&
|
|
(dotindex < subscount)) {
|
|
dotindex++;
|
|
dotsleft = subslen[dotindex];
|
|
}
|
|
if (dotsleft > 0) {
|
|
addbytes(&output, &room,
|
|
subs[dotindex] +
|
|
subslen[dotindex] - dotsleft,
|
|
1);
|
|
dotsleft--;
|
|
}
|
|
} else if (prewrite[0] == '%') {
|
|
int idx = prewrite[1] - '1';
|
|
|
|
prewrite++; // Consume the %
|
|
if ((prewrite[0] == 's') ||
|
|
(prewrite[0] == '0')) {
|
|
/*
|
|
* %0 or %s means copy the entire input string
|
|
*/
|
|
addbytes(&output, &room, pattern, -1);
|
|
} else if ((idx >= 0) &&
|
|
(idx <= subscount)) {
|
|
/*
|
|
* %n where n is withing the range of substitution patterns copies over
|
|
* all of the characters that matched that group
|
|
*/
|
|
addbytes(&output, &room, subs[idx],
|
|
subslen[idx]);
|
|
} else if (prewrite[0]) {
|
|
/*
|
|
* %anything copies over anything. This is how you would get a
|
|
* % in the rewrite string
|
|
*/
|
|
addbytes(&output, &room, prewrite, 1);
|
|
}
|
|
} else {
|
|
/*
|
|
* Just a normal character, just copy it over
|
|
*/
|
|
addbytes(&output, &room, prewrite, 1);
|
|
}
|
|
/*
|
|
* Consume the character we used (if any)
|
|
*/
|
|
if (prewrite[0]) {
|
|
prewrite++;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Put on the usermode (if specified)
|
|
*/
|
|
switch (pbestmatch->userMode) {
|
|
case UserPhone:
|
|
if (*(output - 1) == '>') {
|
|
--room;
|
|
--output;
|
|
addbytes(&output, &room, ";user=phone>", -1);
|
|
} else {
|
|
addbytes(&output, &room, ";user=phone", -1);
|
|
}
|
|
break;
|
|
case UserIP:
|
|
if (*(output - 1) == '>') {
|
|
--room;
|
|
--output;
|
|
addbytes(&output, &room, ";user=ip>", -1);
|
|
} else {
|
|
addbytes(&output, &room, ";user=ip", -1);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else if (pmatch[0] == ',') {
|
|
givedialtone = 1;
|
|
}
|
|
|
|
/*
|
|
* Even if this pattern was not taken as a full match, remember the longest length
|
|
*/
|
|
if (thismatchlen > matchlen) {
|
|
matchlen = thismatchlen;
|
|
}
|
|
}
|
|
/*
|
|
* Try the next template
|
|
*/
|
|
ptempl = ptempl->next;
|
|
comma_counter = 0;
|
|
}
|
|
/*
|
|
* Did we get any templates at all?
|
|
*/
|
|
switch (result) {
|
|
case DIAL_FULLPATTERN:
|
|
case DIAL_FULLMATCH:
|
|
givedialtone = 0;
|
|
/* FALLTHROUGH */
|
|
case DIAL_WILDPATTERN:
|
|
if (timeout != NULL) {
|
|
*timeout = pbestmatch->timeout;
|
|
}
|
|
if (pRouteMode != NULL) {
|
|
*pRouteMode = pbestmatch->routeMode;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* If we have received a partial match, set the timeout
|
|
* to be the default timeout (i.e. interdigit timeout)
|
|
* to allow for additional digit collection.
|
|
*/
|
|
if (partialmatch) {
|
|
if ((timeout != NULL) && (*timeout == 0)) {
|
|
*timeout = DIAL_TIMEOUT;
|
|
}
|
|
} else if ((*(pattern + strlen(pattern) - 1) == '#') &&
|
|
(poundDialingEnabled())) {
|
|
/*
|
|
* No template was matched, however if the dialplan
|
|
* does not have the default '*' rule and the pattern
|
|
* dialed did not match any other template this code
|
|
* would be hit. Therefore check for a # to see if
|
|
* we need to set the dial immediately flag.
|
|
*/
|
|
result = DIAL_IMMEDIATELY;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If the bestmatch template says to dialnow, do it.
|
|
*/
|
|
if (bestmatch_dialnow) {
|
|
/*
|
|
* If there is partial match, and dialled pattern has "#",
|
|
* then do not Dial immediately. Give user a chance to edit
|
|
* the mistake if any.
|
|
*/
|
|
if (!((poundDialingEnabled()) && (strchr(pattern, '#')) &&
|
|
partialmatch)) {
|
|
result = DIAL_IMMEDIATELY;
|
|
if (timeout != NULL) {
|
|
*timeout = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (givedialtone) {
|
|
/*
|
|
* Give user dial-tone. The tone given
|
|
* is based on the rule. Default tone
|
|
* is Outside-Dial-Tone.
|
|
*/
|
|
if (pTone != NULL) {
|
|
*pTone = VCM_DEFAULT_TONE;
|
|
if (pbestmatch != NULL) {
|
|
if (best_comma_count < pbestmatch->tones_defined) {
|
|
*pTone = pbestmatch->tone[best_comma_count];
|
|
}
|
|
}
|
|
}
|
|
result = DIAL_GIVETONE;
|
|
}
|
|
|
|
return result; // Nothing special to match against
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: InitDialPlan()
|
|
*
|
|
* Parameters: wipe - should we wipe the current dialplan out before we start,
|
|
* or leave it in place for safety's sake
|
|
*
|
|
* Description: Reads the Dial Plan from memory and submits a TFTP
|
|
* request for the Dial Plan file.
|
|
*
|
|
* Returns: None
|
|
*/
|
|
void
|
|
InitDialPlan (boolean wipe)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function: FreeDialTemplates()
|
|
*
|
|
* Parameters: None
|
|
*
|
|
* Description: Frees the Dial Templates from memory
|
|
*
|
|
* Returns: None
|
|
*/
|
|
void
|
|
FreeDialTemplates (void)
|
|
{
|
|
struct DialTemplate *pnext;
|
|
|
|
while (basetemplate != NULL) {
|
|
pnext = basetemplate->next;
|
|
cpr_free(basetemplate);
|
|
basetemplate = pnext;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: AddDialTemplate()
|
|
*
|
|
* Parameters: pattern -
|
|
* line -
|
|
* timeout -
|
|
* userMode -
|
|
* rewrite -
|
|
* routeMode -
|
|
* tone - array of tones to play when , is hit in the rule
|
|
* tones_defined - # of tones this rule actually defined
|
|
*
|
|
* Description: Add a dial template to the known list of templates
|
|
*
|
|
* Returns: None
|
|
*/
|
|
static void
|
|
AddDialTemplate (const char *pattern, const line_t line,
|
|
int timeout, UserMode userMode,
|
|
const char *rewrite, RouteMode routeMode,
|
|
vcm_tones_t tone[MAX_TONES], int tones_defined)
|
|
{
|
|
struct DialTemplate *pnewtemplate;
|
|
int patternlen = strlen(pattern);
|
|
int rewritelen = strlen(rewrite);
|
|
int counter;
|
|
|
|
pnewtemplate = (struct DialTemplate *)
|
|
cpr_malloc(sizeof(struct DialTemplate) + patternlen + rewritelen +
|
|
2);
|
|
if (pnewtemplate != NULL) {
|
|
pnewtemplate->next = NULL;
|
|
pnewtemplate->pattern = (char *) (pnewtemplate + 1);
|
|
sstrncpy(pnewtemplate->pattern, (char *) pattern, patternlen + 1);
|
|
pnewtemplate->rewrite = pnewtemplate->pattern + patternlen + 1;
|
|
sstrncpy(pnewtemplate->rewrite, (char *) rewrite, rewritelen + 1);
|
|
pnewtemplate->line = line;
|
|
pnewtemplate->timeout = timeout;
|
|
pnewtemplate->userMode = userMode;
|
|
pnewtemplate->routeMode = routeMode;
|
|
pnewtemplate->tones_defined = tones_defined;
|
|
for (counter = 0; counter < MAX_TONES; counter++) {
|
|
pnewtemplate->tone[counter] = tone[counter];
|
|
}
|
|
|
|
/*
|
|
* Now add it to the end of all the templates
|
|
*/
|
|
if (basetemplate == NULL) {
|
|
basetemplate = pnewtemplate;
|
|
} else {
|
|
struct DialTemplate *base = basetemplate;
|
|
|
|
while (base->next != NULL) {
|
|
base = base->next;
|
|
}
|
|
base->next = pnewtemplate;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: show_dialplan_cmd
|
|
*
|
|
* Parameters: standard args
|
|
*
|
|
* Description: Display the current dialplan (if any)
|
|
*
|
|
* Returns:
|
|
*
|
|
*/
|
|
int32_t
|
|
show_dialplan_cmd (int32_t argc, const char *argv[])
|
|
{
|
|
struct DialTemplate *pTemp;
|
|
char umode[32], rmode[32];
|
|
char line_str[32];
|
|
uint32_t idx = 1;
|
|
int32_t counter = 0;
|
|
|
|
debugif_printf("Dialplan is....\n");
|
|
debugif_printf("Dialplan version: %s\n", g_dp_version_stamp);
|
|
|
|
pTemp = basetemplate;
|
|
if (basetemplate == NULL) {
|
|
debugif_printf("EMPTY\n");
|
|
return 0;
|
|
}
|
|
while (pTemp != NULL) {
|
|
switch (pTemp->routeMode) {
|
|
case RouteEmergency:
|
|
sstrncpy(rmode, "Emergency", sizeof(rmode));
|
|
break;
|
|
case RouteFQDN:
|
|
sstrncpy(rmode, "FQDN", sizeof(rmode));
|
|
break;
|
|
default:
|
|
sstrncpy(rmode, "Default", sizeof(rmode));
|
|
break;
|
|
}
|
|
|
|
switch (pTemp->userMode) {
|
|
case UserPhone:
|
|
sstrncpy(umode, "Phone", sizeof(umode));
|
|
break;
|
|
case UserIP:
|
|
sstrncpy(umode, "IP", sizeof(umode));
|
|
break;
|
|
default:
|
|
sstrncpy(umode, "Unspecified", sizeof(umode));
|
|
break;
|
|
}
|
|
|
|
if (pTemp->line == 0) {
|
|
sprintf(line_str, "All");
|
|
} else {
|
|
sprintf(line_str, "%d", pTemp->line);
|
|
}
|
|
debugif_printf("%02d. Pattern: %s Rewrite: %s Line: %s\n"
|
|
" Timeout: %04d UserMode: %s RouteMode: %s\n",
|
|
idx, pTemp->pattern, pTemp->rewrite, line_str,
|
|
pTemp->timeout, umode, rmode);
|
|
for (counter = 0; counter < pTemp->tones_defined; counter++) {
|
|
debugif_printf(" Tone %d: %s\n", counter + 1,
|
|
tone_names[(int) (pTemp->tone[counter])]);
|
|
}
|
|
pTemp = pTemp->next;
|
|
idx++;
|
|
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: ParseDialEntry()
|
|
*
|
|
* Parameters: parseptr - pointer to bytes to be parsed
|
|
*
|
|
* Description: Parse the contents of a TEMPLATE tag and add the data to
|
|
* the dial template lists.
|
|
* The keywords parsed are:
|
|
* MATCH="string"
|
|
* Line="number"
|
|
* Timeout="number"
|
|
* User="Phone" | "IP"
|
|
* Rewrite="string"
|
|
* All other keywords are silently ignored
|
|
* The format of the TEMPLATE can be one ofthe following:
|
|
* 1. <TEMPLATE KEYWORD1="value1" ... KEYWORDN="valueN" />
|
|
* 2. <TEMPLATE KEYWORD1="value1" ... KEYWORDN="valueN"></TEMPLATE>
|
|
*
|
|
* Returns: Int - The Number of errors encountered
|
|
*/
|
|
static int
|
|
ParseDialEntry (char **parseptr)
|
|
{
|
|
char dialtemplate[MAX_TEMPLATE_LENGTH];
|
|
char rewrite[MAX_TEMPLATE_LENGTH];
|
|
int timeout = DIAL_TIMEOUT; /* Default to DIAL_TIMEOUT if none specified */
|
|
unsigned char line = 0; /* Default to 0 if not specified. Zero matches all lines. */
|
|
int counter = 0;
|
|
int tone_counter = 0;
|
|
UserMode usermode = UserUnspec;
|
|
RouteMode routeMode = RouteDefault;
|
|
vcm_tones_t tone[MAX_TONES];
|
|
ParseDialState state = STATE_ANY;
|
|
long strtol_result;
|
|
char *strtol_end;
|
|
|
|
dialtemplate[0] = '\0';
|
|
rewrite[0] = '\0';
|
|
// Set all tones to default. Rule may override.
|
|
for (counter = 0; counter < MAX_TONES; counter++) {
|
|
tone[counter] = VCM_DEFAULT_TONE;
|
|
}
|
|
|
|
for (;;) {
|
|
char buffer[64];
|
|
XMLToken tok;
|
|
|
|
tok = parse_xml_tokens(parseptr, buffer, sizeof(buffer));
|
|
switch (tok) {
|
|
case TOK_KEYWORD:
|
|
if (state != STATE_ANY) {
|
|
if ((cpr_strcasecmp(buffer, "TEMPLATE") == 0) &&
|
|
(state == STATE_END_TAG_STARTED)) {
|
|
state = STATE_END_TAG_FOUND;
|
|
} else {
|
|
return 1;
|
|
}
|
|
} else if (cpr_strcasecmp(buffer, "MATCH") == 0) {
|
|
state = STATE_GOT_MATCH;
|
|
} else if (cpr_strcasecmp(buffer, "LINE") == 0) {
|
|
state = STATE_GOT_LINE;
|
|
} else if (cpr_strcasecmp(buffer, "TIMEOUT") == 0) {
|
|
state = STATE_GOT_TIMEOUT;
|
|
} else if (cpr_strcasecmp(buffer, "USER") == 0) {
|
|
state = STATE_GOT_USER;
|
|
} else if (cpr_strcasecmp(buffer, "REWRITE") == 0) {
|
|
state = STATE_GOT_REWRITE;
|
|
} else if (cpr_strcasecmp(buffer, "ROUTE") == 0) {
|
|
state = STATE_GOT_ROUTE;
|
|
} else if (cpr_strcasecmp(buffer, "TONE") == 0) {
|
|
state = STATE_GOT_TONE;
|
|
} else {
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case TOK_EQ:
|
|
switch (state) {
|
|
case STATE_GOT_MATCH:
|
|
state = STATE_GOT_MATCH_EQ;
|
|
break;
|
|
case STATE_GOT_LINE:
|
|
state = STATE_GOT_LINE_EQ;
|
|
break;
|
|
case STATE_GOT_TIMEOUT:
|
|
state = STATE_GOT_TIMEOUT_EQ;
|
|
break;
|
|
case STATE_GOT_USER:
|
|
state = STATE_GOT_USER_EQ;
|
|
break;
|
|
case STATE_GOT_REWRITE:
|
|
state = STATE_GOT_REWRITE_EQ;
|
|
break;
|
|
case STATE_GOT_ROUTE:
|
|
state = STATE_GOT_ROUTE_EQ;
|
|
break;
|
|
case STATE_GOT_TONE:
|
|
state = STATE_GOT_TONE_EQ;
|
|
break;
|
|
|
|
default:
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case TOK_STR:
|
|
switch (state) {
|
|
case STATE_GOT_MATCH_EQ:
|
|
sstrncpy(dialtemplate, buffer, sizeof(dialtemplate));
|
|
break;
|
|
|
|
case STATE_GOT_LINE_EQ:
|
|
errno = 0;
|
|
strtol_result = strtol(buffer, &strtol_end, 10);
|
|
|
|
if (errno || buffer == strtol_end || strtol_result < 0 || strtol_result > UCHAR_MAX) {
|
|
return 1;
|
|
}
|
|
|
|
line = (unsigned char) strtol_result;
|
|
break;
|
|
|
|
case STATE_GOT_TIMEOUT_EQ:
|
|
errno = 0;
|
|
strtol_result = strtol(buffer, &strtol_end, 10);
|
|
|
|
if (errno || buffer == strtol_end || strtol_result < 0 || strtol_result > INT_MAX) {
|
|
return 1;
|
|
}
|
|
|
|
timeout = (int) strtol_result;
|
|
|
|
break;
|
|
|
|
case STATE_GOT_USER_EQ:
|
|
if (cpr_strcasecmp(buffer, "PHONE") == 0) {
|
|
usermode = UserPhone;
|
|
} else if (cpr_strcasecmp(buffer, "IP") == 0) {
|
|
usermode = UserIP;
|
|
} else {
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case STATE_GOT_ROUTE_EQ:
|
|
if (cpr_strcasecmp(buffer, "DEFAULT") == 0) {
|
|
routeMode = RouteDefault;
|
|
} else if (cpr_strcasecmp(buffer, "EMERGENCY") == 0) {
|
|
routeMode = RouteEmergency;
|
|
} else if (cpr_strcasecmp(buffer, "FQDN") == 0) {
|
|
routeMode = RouteFQDN;
|
|
} else {
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case STATE_GOT_TONE_EQ:
|
|
if (tone_counter < MAX_TONES) {
|
|
/* Tone = "" check */
|
|
if (*buffer == '\000') {
|
|
tone[tone_counter] = VCM_DEFAULT_TONE;
|
|
} else {
|
|
for (counter = 0; counter < VCM_MAX_DIALTONE; counter++) {
|
|
if (cpr_strcasecmp(buffer, tone_names[counter]) ==
|
|
0) {
|
|
tone[tone_counter] = (vcm_tones_t) counter;
|
|
break;
|
|
}
|
|
}
|
|
// Tone string not matched
|
|
if (counter == VCM_MAX_DIALTONE) {
|
|
return 1;
|
|
}
|
|
}
|
|
tone_counter++;
|
|
// Too many tones defined in the rule
|
|
} else {
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case STATE_GOT_REWRITE_EQ:
|
|
sstrncpy(rewrite, buffer, sizeof(rewrite));
|
|
break;
|
|
|
|
default:
|
|
return 1;
|
|
}
|
|
state = STATE_ANY;
|
|
break;
|
|
|
|
case TOK_EMPTYBRACKET: /* "/>" */
|
|
AddDialTemplate(dialtemplate, line, timeout, usermode, rewrite,
|
|
routeMode, tone, tone_counter);
|
|
if (state == STATE_ANY) {
|
|
return 0;
|
|
}
|
|
/*
|
|
* There was a parsing error, so just ignore it
|
|
*/
|
|
return 1;
|
|
|
|
case TOK_RBRACKET: /* ">" */
|
|
if (state == STATE_ANY) {
|
|
state = STATE_START_TAG_COMPLETED;
|
|
} else if (state == STATE_END_TAG_FOUND) {
|
|
AddDialTemplate(dialtemplate, line, timeout, usermode, rewrite,
|
|
routeMode, tone, tone_counter);
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case TOK_ENDLBRACKET: /* "</" */
|
|
if (state == STATE_START_TAG_COMPLETED) {
|
|
state = STATE_END_TAG_STARTED;
|
|
} else {
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
/*
|
|
* Problem parsing. Let them know
|
|
*/
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: ParseDialVersion()
|
|
*
|
|
* Parameters: parseptr - pointer to bytes to be parsed
|
|
*
|
|
* Description: Parse the contents of a versionStamp tag and store it
|
|
* The format of the versionStamp is as follows:
|
|
* <versionStamp>value up to 64 chars</versionStamp>
|
|
*
|
|
* Returns: 0 - if the version stamp is successfully parsed.
|
|
* 1 - if the version stamp parsing fails.
|
|
*/
|
|
static int
|
|
ParseDialVersion (char **parseptr)
|
|
{
|
|
ParseDialState state = STATE_ANY;
|
|
char version_stamp[MAX_DP_VERSION_STAMP_LEN] = { 0 };
|
|
int len;
|
|
|
|
for (;;) {
|
|
char buffer[MAX_DP_VERSION_STAMP_LEN];
|
|
XMLToken tok;
|
|
|
|
tok = parse_xml_tokens(parseptr, buffer, sizeof(buffer));
|
|
switch (tok) {
|
|
case TOK_KEYWORD:
|
|
switch (state) {
|
|
case STATE_START_TAG_COMPLETED:
|
|
/*
|
|
* the keyword is version stamp value here. copy it temporarily.
|
|
*/
|
|
memcpy(version_stamp, buffer, MAX_DP_VERSION_STAMP_LEN);
|
|
break;
|
|
case STATE_END_TAG_STARTED:
|
|
if (cpr_strcasecmp(buffer, "versionStamp") != 0) {
|
|
return 1;
|
|
}
|
|
state = STATE_END_TAG_FOUND;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case TOK_RBRACKET: /* ">" */
|
|
if (state == STATE_ANY) {
|
|
state = STATE_START_TAG_COMPLETED;
|
|
} else if (state == STATE_END_TAG_FOUND) {
|
|
/* strip of the warping curly braces if they exist */
|
|
len = strlen(version_stamp);
|
|
if (len <= 2)
|
|
{
|
|
CCAPP_ERROR("ParseDialVersion(): Version length [%d] is way too small", len);
|
|
return (1);
|
|
}
|
|
|
|
memset(g_dp_version_stamp, 0, MAX_DP_VERSION_STAMP_LEN);
|
|
if ((version_stamp[0] == '{')
|
|
&& (version_stamp[len - 1] == '}')) {
|
|
memcpy(g_dp_version_stamp, (version_stamp + 1), (len - 2));
|
|
} else {
|
|
memcpy(g_dp_version_stamp, version_stamp, len);
|
|
}
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case TOK_ENDLBRACKET: /* "</" */
|
|
if (state == STATE_START_TAG_COMPLETED) {
|
|
state = STATE_END_TAG_STARTED;
|
|
} else {
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* Problem parsing. Let them know
|
|
*/
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: ParseDialTemplate()
|
|
*
|
|
* Parameters: parseptr - pointer to bytes to be parsed
|
|
*
|
|
* Description: Parse the contents of a dial template XML file
|
|
* All Start and end tags are ignored
|
|
* The empty element <TEMPLATE is parsed by ParseDialEntry
|
|
*
|
|
* Returns: false if parse fails else true
|
|
*/
|
|
boolean
|
|
ParseDialTemplate (char *parseptr)
|
|
{
|
|
char buffer[MAX_TEMPLATE_LENGTH];
|
|
XMLToken tok;
|
|
int LookKey;
|
|
int LookEndKey;
|
|
int errors = 0;
|
|
int insideDialPlan = 0;
|
|
|
|
LookKey = 0;
|
|
LookEndKey = 0;
|
|
FreeDialTemplates();
|
|
/*
|
|
* reset the version stamp so that dialplans that do not contain versionStamp
|
|
* tag will not keep the previous versionstamp.
|
|
*/
|
|
g_dp_version_stamp[0] = 0;
|
|
|
|
|
|
if (parseptr == NULL) {
|
|
debugif_printf("ParseDialTempate(): parseptr=NULL Returning.\n");
|
|
return (FALSE);
|
|
}
|
|
|
|
while ((tok =
|
|
parse_xml_tokens(&parseptr, buffer, sizeof(buffer))) != TOK_EOF) {
|
|
if (LookEndKey) {
|
|
if (tok == TOK_RBRACKET) {
|
|
LookEndKey = 0;
|
|
} else if ((tok == TOK_KEYWORD)
|
|
&& !cpr_strcasecmp(buffer, "DIALTEMPLATE")) {
|
|
insideDialPlan = 0;
|
|
}
|
|
} else if (tok == TOK_LBRACKET) {
|
|
LookKey = 1;
|
|
} else if ((LookKey != 0) && (tok == TOK_KEYWORD)
|
|
&& !cpr_strcasecmp(buffer, "DIALTEMPLATE")) {
|
|
insideDialPlan = 1;
|
|
} else if ((LookKey != 0) && (tok == TOK_KEYWORD)
|
|
&& !strcmp(buffer, "TEMPLATE")) {
|
|
if (insideDialPlan) {
|
|
errors += ParseDialEntry(&parseptr);
|
|
} else {
|
|
errors++;
|
|
}
|
|
} else if ((LookKey != 0) && (tok == TOK_KEYWORD)
|
|
&& !cpr_strcasecmp(buffer, "versionStamp")) {
|
|
if (insideDialPlan) {
|
|
/* <versionStamp> tag found. parse it */
|
|
errors += ParseDialVersion(&parseptr);
|
|
} else {
|
|
errors++;
|
|
}
|
|
} else if (tok == TOK_ENDLBRACKET) {
|
|
LookEndKey = 1;
|
|
} else {
|
|
LookKey = 0;
|
|
}
|
|
}
|
|
/*
|
|
* If we had any parse errors, put them into the log
|
|
*/
|
|
log_clear(LOG_CFG_PARSE_DIAL);
|
|
if (errors != 0) {
|
|
log_msg(LOG_CFG_PARSE_DIAL, errors, DialTemplateFile);
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|