summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
Diffstat (limited to 'main')
-rw-r--r--main/ast_expr2f.c26
-rw-r--r--main/pbx.c402
-rw-r--r--main/strings.c11
3 files changed, 395 insertions, 44 deletions
diff --git a/main/ast_expr2f.c b/main/ast_expr2f.c
index 7c8d2f824..15dbb5c2e 100644
--- a/main/ast_expr2f.c
+++ b/main/ast_expr2f.c
@@ -2424,6 +2424,32 @@ int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan)
return return_value;
}
+#if !defined(STANDALONE)
+int ast_str_expr(struct ast_str **str, ssize_t maxlen, struct ast_channel *chan, char *expr)
+{
+ struct parse_io io = { .string = expr, .chan = chan };
+
+ ast_yylex_init(&io.scanner);
+ ast_yy_scan_string(expr, io.scanner);
+ ast_yyparse ((void *) &io);
+ ast_yylex_destroy(io.scanner);
+
+ if (!io.val) {
+ ast_str_set(str, maxlen, "0");
+ } else {
+ if (io.val->type == AST_EXPR_number) {
+ int res_length;
+ ast_str_set(str, maxlen, FP___PRINTF, io.val->u.i);
+ } else if (io.val->u.s) {
+ ast_str_set(str, maxlen, "%s", io.val->u.s);
+ free(io.val->u.s);
+ }
+ free(io.val);
+ }
+ return ast_str_strlen(*str);
+}
+#endif
+
char extra_error_message[4095];
int extra_error_message_supplied = 0;
diff --git a/main/pbx.c b/main/pbx.c
index e3e936ec1..65c803568 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -2837,6 +2837,54 @@ static char *substring(const char *value, int offset, int length, char *workspac
return ret;
}
+static const char *ast_str_substring(struct ast_str *value, int offset, int length)
+{
+ int lr; /* length of the input string after the copy */
+
+ lr = ast_str_strlen(value); /* compute length after copy, so we never go out of the workspace */
+
+ /* Quick check if no need to do anything */
+ if (offset == 0 && length >= lr) /* take the whole string */
+ return ast_str_buffer(value);
+
+ if (offset < 0) { /* translate negative offset into positive ones */
+ offset = lr + offset;
+ if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
+ offset = 0;
+ }
+
+ /* too large offset result in empty string so we know what to return */
+ if (offset >= lr) {
+ ast_str_reset(value);
+ return ast_str_buffer(value);
+ }
+
+ if (offset > 0) {
+ /* Go ahead and chop off the beginning */
+ memcpy(ast_str_buffer(value), ast_str_buffer(value) + offset, ast_str_strlen(value) - offset + 1);
+ lr -= offset;
+ }
+
+ if (length >= 0 && length < lr) { /* truncate if necessary */
+ char *tmp = ast_str_buffer(value);
+ tmp[length] = '\0';
+ ast_str_update(value);
+ } else if (length < 0) {
+ if (lr > -length) { /* After we remove from the front and from the rear, is there anything left? */
+ char *tmp = ast_str_buffer(value);
+ tmp[lr + length] = '\0';
+ ast_str_update(value);
+ } else {
+ ast_str_reset(value);
+ }
+ } else {
+ /* Nothing to do, but update the buffer length */
+ ast_str_update(value);
+ }
+
+ return ast_str_buffer(value);
+}
+
/*! \brief Support for Asterisk built-in variables in the dialplan
\note See also
@@ -2845,8 +2893,20 @@ static char *substring(const char *value, int offset, int length, char *workspac
*/
void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
{
+ struct ast_str *str = ast_str_create(16);
+ const char *cret;
+
+ cret = ast_str_retrieve_variable(&str, 0, c, headp, var);
+ ast_copy_string(workspace, ast_str_buffer(str), workspacelen);
+ *ret = cret ? workspace : NULL;
+ ast_free(str);
+}
+
+const char *ast_str_retrieve_variable(struct ast_str **str, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *var)
+{
const char not_found = '\0';
char *tmpvar;
+ const char *ret;
const char *s; /* the result */
int offset, length;
int i, need_substring;
@@ -2885,47 +2945,48 @@ void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, c
if (!strncmp(var, "CALL", 4)) {
if (!strncmp(var + 4, "ING", 3)) {
if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */
- snprintf(workspace, workspacelen, "%d", c->cid.cid_pres);
- s = workspace;
+ ast_str_set(str, maxlen, "%d", c->cid.cid_pres);
+ s = ast_str_buffer(*str);
} else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */
- snprintf(workspace, workspacelen, "%d", c->cid.cid_ani2);
- s = workspace;
+ ast_str_set(str, maxlen, "%d", c->cid.cid_ani2);
+ s = ast_str_buffer(*str);
} else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */
- snprintf(workspace, workspacelen, "%d", c->cid.cid_ton);
- s = workspace;
+ ast_str_set(str, maxlen, "%d", c->cid.cid_ton);
+ s = ast_str_buffer(*str);
} else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */
- snprintf(workspace, workspacelen, "%d", c->cid.cid_tns);
- s = workspace;
+ ast_str_set(str, maxlen, "%d", c->cid.cid_tns);
+ s = ast_str_buffer(*str);
}
}
} else if (!strcmp(var, "HINT")) {
- s = ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten) ? workspace : NULL;
+ s = ast_str_get_hint(str, maxlen, NULL, 0, c, c->context, c->exten) ? ast_str_buffer(*str) : NULL;
} else if (!strcmp(var, "HINTNAME")) {
- s = ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten) ? workspace : NULL;
+ s = ast_str_get_hint(NULL, 0, str, maxlen, c, c->context, c->exten) ? ast_str_buffer(*str) : NULL;
} else if (!strcmp(var, "EXTEN")) {
s = c->exten;
} else if (!strcmp(var, "CONTEXT")) {
s = c->context;
} else if (!strcmp(var, "PRIORITY")) {
- snprintf(workspace, workspacelen, "%d", c->priority);
- s = workspace;
+ ast_str_set(str, maxlen, "%d", c->priority);
+ s = ast_str_buffer(*str);
} else if (!strcmp(var, "CHANNEL")) {
s = c->name;
} else if (!strcmp(var, "UNIQUEID")) {
s = c->uniqueid;
} else if (!strcmp(var, "HANGUPCAUSE")) {
- snprintf(workspace, workspacelen, "%d", c->hangupcause);
- s = workspace;
+ ast_str_set(str, maxlen, "%d", c->hangupcause);
+ s = ast_str_buffer(*str);
}
}
if (s == &not_found) { /* look for more */
if (!strcmp(var, "EPOCH")) {
- snprintf(workspace, workspacelen, "%u",(int)time(NULL));
- s = workspace;
+ ast_str_set(str, maxlen, "%u", (int) time(NULL));
+ s = ast_str_buffer(*str);
} else if (!strcmp(var, "SYSTEMNAME")) {
s = ast_config_AST_SYSTEM_NAME;
} else if (!strcmp(var, "ENTITYID")) {
- ast_eid_to_str(workspace, workspacelen, &ast_eid_default);
+ char workspace[20];
+ ast_eid_to_str(workspace, sizeof(workspace), &ast_eid_default);
s = workspace;
}
}
@@ -2945,18 +3006,25 @@ void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, c
if (places[i] == &globals)
ast_rwlock_unlock(&globalslock);
}
- if (s == &not_found || s == NULL)
- *ret = NULL;
- else {
- if (s != workspace)
- ast_copy_string(workspace, s, workspacelen);
- *ret = workspace;
- if (need_substring)
- *ret = substring(*ret, offset, length, workspace, workspacelen);
+ if (s == &not_found || s == NULL) {
+ ast_debug(5, "Result of '%s' is NULL\n", var);
+ ret = NULL;
+ } else {
+ ast_debug(5, "Result of '%s' is '%s'\n", var, s);
+ if (s != ast_str_buffer(*str)) {
+ ast_str_set(str, maxlen, "%s", s);
+ }
+ ret = ast_str_buffer(*str);
+ if (need_substring) {
+ ret = ast_str_substring(*str, offset, length);
+ ast_debug(2, "Final result of '%s' is '%s'\n", var, ret);
+ }
}
- if (c)
+ if (c) {
ast_channel_unlock(c);
+ }
+ return ret;
}
static void exception_store_free(void *data)
@@ -3334,19 +3402,78 @@ int ast_func_read(struct ast_channel *chan, const char *function, char *workspac
char *copy = ast_strdupa(function);
char *args = func_args(copy);
struct ast_custom_function *acfptr = ast_custom_function_find(copy);
+ int res;
+ struct ast_module_user *u = NULL;
- if (acfptr == NULL)
+ if (acfptr == NULL) {
ast_log(LOG_ERROR, "Function %s not registered\n", copy);
- else if (!acfptr->read)
+ } else if (!acfptr->read && !acfptr->read2) {
ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
- else {
- int res;
- struct ast_module_user *u = NULL;
- if (acfptr->mod)
+ } else if (acfptr->read) {
+ if (acfptr->mod) {
u = __ast_module_user_add(acfptr->mod, chan);
+ }
res = acfptr->read(chan, copy, args, workspace, len);
- if (acfptr->mod && u)
+ if (acfptr->mod && u) {
__ast_module_user_remove(acfptr->mod, u);
+ }
+ return res;
+ } else {
+ struct ast_str *str = ast_str_create(16);
+ if (acfptr->mod) {
+ u = __ast_module_user_add(acfptr->mod, chan);
+ }
+ res = acfptr->read2(chan, copy, args, &str, 0);
+ if (acfptr->mod && u) {
+ __ast_module_user_remove(acfptr->mod, u);
+ }
+ ast_copy_string(workspace, ast_str_buffer(str), len > ast_str_size(str) ? ast_str_size(str) : len);
+ ast_free(str);
+ return res;
+ }
+ return -1;
+}
+
+int ast_func_read2(struct ast_channel *chan, const char *function, struct ast_str **str, ssize_t maxlen)
+{
+ char *copy = ast_strdupa(function);
+ char *args = func_args(copy);
+ struct ast_custom_function *acfptr = ast_custom_function_find(copy);
+ int res;
+ struct ast_module_user *u = NULL;
+
+ if (acfptr == NULL) {
+ ast_log(LOG_ERROR, "Function %s not registered\n", copy);
+ } else if (!acfptr->read && !acfptr->read2) {
+ ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
+ } else {
+ if (acfptr->mod) {
+ u = __ast_module_user_add(acfptr->mod, chan);
+ }
+ if (acfptr->read2) {
+ /* ast_str enabled */
+ ast_str_reset(*str);
+ res = acfptr->read2(chan, copy, args, str, maxlen);
+ } else {
+ /* Legacy function pointer, allocate buffer for result */
+ int maxsize = ast_str_size(*str);
+ if (maxlen > -1) {
+ if (maxlen == 0) {
+ if (acfptr->read_max) {
+ maxsize = acfptr->read_max;
+ } else {
+ maxsize = VAR_BUF_SIZE;
+ }
+ } else {
+ maxsize = maxlen;
+ }
+ ast_str_make_space(str, maxsize);
+ }
+ res = acfptr->read(chan, copy, args, ast_str_buffer(*str), maxsize);
+ }
+ if (acfptr->mod && u) {
+ __ast_module_user_remove(acfptr->mod, u);
+ }
return res;
}
return -1;
@@ -3376,6 +3503,194 @@ int ast_func_write(struct ast_channel *chan, const char *function, const char *v
return -1;
}
+void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *templ, size_t *used)
+{
+ /* Substitutes variables into buf, based on string templ */
+ char *cp4 = NULL;
+ const char *tmp, *whereweare;
+ int orig_size = 0;
+ int offset, offset2, isfunction;
+ const char *nextvar, *nextexp, *nextthing;
+ const char *vars, *vare;
+ char *finalvars;
+ int pos, brackets, needsub, len;
+ struct ast_str *substr1 = ast_str_create(16), *substr2 = NULL, *substr3 = ast_str_create(16);
+
+ ast_str_reset(*buf);
+ whereweare = tmp = templ;
+ while (!ast_strlen_zero(whereweare)) {
+ /* Assume we're copying the whole remaining string */
+ pos = strlen(whereweare);
+ nextvar = NULL;
+ nextexp = NULL;
+ nextthing = strchr(whereweare, '$');
+ if (nextthing) {
+ switch (nextthing[1]) {
+ case '{':
+ nextvar = nextthing;
+ pos = nextvar - whereweare;
+ break;
+ case '[':
+ nextexp = nextthing;
+ pos = nextexp - whereweare;
+ break;
+ default:
+ pos = 1;
+ }
+ }
+
+ if (pos) {
+ /* Copy that many bytes */
+ ast_str_append_substr(buf, maxlen, whereweare, pos);
+
+ templ += pos;
+ whereweare += pos;
+ }
+
+ if (nextvar) {
+ /* We have a variable. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextvar + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while (brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ } else if (vare[0] == '{') {
+ brackets++;
+ } else if (vare[0] == '}') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '['))
+ needsub++;
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");
+ len = vare - vars - 1;
+
+ /* Skip totally over variable string */
+ whereweare += (len + 3);
+
+ /* Store variable name (and truncate) */
+ ast_str_set_substr(&substr1, 0, vars, len);
+ ast_debug(5, "Evaluating '%s' (from '%s' len %d)\n", ast_str_buffer(substr1), vars, len);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ size_t used;
+ if (!substr2) {
+ substr2 = ast_str_create(16);
+ }
+
+ ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), &used);
+ finalvars = ast_str_buffer(substr2);
+ } else {
+ finalvars = ast_str_buffer(substr1);
+ }
+
+ parse_variable_name(finalvars, &offset, &offset2, &isfunction);
+ if (isfunction) {
+ /* Evaluate function */
+ if (c || !headp) {
+ cp4 = ast_func_read2(c, finalvars, &substr3, 0) ? NULL : ast_str_buffer(substr3);
+ } else {
+ struct varshead old;
+ struct ast_channel *bogus = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/%p", vars);
+ if (bogus) {
+ memcpy(&old, &bogus->varshead, sizeof(old));
+ memcpy(&bogus->varshead, headp, sizeof(bogus->varshead));
+ cp4 = ast_func_read2(c, finalvars, &substr3, 0) ? NULL : ast_str_buffer(substr3);
+ /* Don't deallocate the varshead that was passed in */
+ memcpy(&bogus->varshead, &old, sizeof(bogus->varshead));
+ ast_channel_free(bogus);
+ } else {
+ ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
+ }
+ }
+ ast_debug(2, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
+ } else {
+ /* Retrieve variable value */
+ ast_str_retrieve_variable(&substr3, 0, c, headp, finalvars);
+ cp4 = ast_str_buffer(substr3);
+ }
+ if (cp4) {
+ ast_str_substring(substr3, offset, offset2);
+ ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));
+ }
+ } else if (nextexp) {
+ /* We have an expression. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextexp + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while (brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '[')) {
+ needsub++;
+ brackets++;
+ vare++;
+ } else if (vare[0] == '[') {
+ brackets++;
+ } else if (vare[0] == ']') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ vare++;
+ }
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");
+ len = vare - vars - 1;
+
+ /* Skip totally over expression */
+ whereweare += (len + 3);
+
+ /* Store variable name (and truncate) */
+ ast_str_set_substr(&substr1, 0, vars, len);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ size_t used;
+ if (!substr2) {
+ substr2 = ast_str_create(16);
+ }
+
+ ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), &used);
+ finalvars = ast_str_buffer(substr2);
+ } else {
+ finalvars = ast_str_buffer(substr1);
+ }
+
+ if (ast_str_expr(&substr3, 0, c, finalvars)) {
+ ast_debug(2, "Expression result is '%s'\n", ast_str_buffer(substr3));
+ }
+ ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));
+ }
+ }
+ *used = ast_str_strlen(*buf) - orig_size;
+ ast_free(substr1);
+ ast_free(substr2);
+ ast_free(substr3);
+}
+
+void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
+{
+ size_t used;
+ ast_str_substitute_variables_full(buf, maxlen, chan, NULL, templ, &used);
+}
+
+void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)
+{
+ size_t used;
+ ast_str_substitute_variables_full(buf, maxlen, NULL, headp, templ, &used);
+}
+
void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count, size_t *used)
{
/* Substitutes variables into cp2, based on string cp1, cp2 NO LONGER NEEDS TO BE ZEROED OUT!!!! */
@@ -4108,6 +4423,27 @@ int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_
return 0;
}
+/*! \brief Get hint for channel */
+int ast_str_get_hint(struct ast_str **hint, ssize_t hintsize, struct ast_str **name, ssize_t namesize, struct ast_channel *c, const char *context, const char *exten)
+{
+ struct ast_exten *e = ast_hint_extension(c, context, exten);
+
+ if (!e) {
+ return 0;
+ }
+
+ if (hint) {
+ ast_str_set(hint, hintsize, "%s", ast_get_extension_app(e));
+ }
+ if (name) {
+ const char *tmp = ast_get_extension_app_data(e);
+ if (tmp) {
+ ast_str_set(name, namesize, "%s", tmp);
+ }
+ }
+ return -1;
+}
+
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
{
return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCH, 0, 0);
diff --git a/main/strings.c b/main/strings.c
index 2e4b749c9..0cc360c4e 100644
--- a/main/strings.c
+++ b/main/strings.c
@@ -109,17 +109,6 @@ int __ast_str_helper(struct ast_str **buf, size_t max_len,
return res;
}
-void ast_str_substitute_variables(struct ast_str **buf, size_t maxlen, struct ast_channel *chan, const char *template)
-{
- int first = 1;
- do {
- ast_str_make_space(buf, maxlen ? maxlen :
- (first ? strlen(template) * 2 : (*buf)->__AST_STR_LEN * 2));
- pbx_substitute_variables_helper_full(chan, NULL, template, (*buf)->__AST_STR_STR, (*buf)->__AST_STR_LEN - 1, &((*buf)->__AST_STR_USED));
- first = 0;
- } while (maxlen == 0 && (*buf)->__AST_STR_LEN - 5 < (*buf)->__AST_STR_USED);
-}
-
char *__ast_str_helper2(struct ast_str **buf, size_t maxlen, const char *src, size_t maxsrc, int append, int escapecommas)
{
int dynamic = 0;