Merge str_substitution branch.

This branch adds additional methods to dialplan functions, whereby the result
buffers are now dynamic buffers, which can be expanded to the size of any
result.  No longer are variable substitutions limited to 4095 bytes of data.
In addition, the common case of needing buffers much smaller than that will
enable substitution to only take up the amount of memory actually needed.
The existing variable substitution routines are still available, but users
of those API calls should transition to using the dynamic-buffer APIs.
Reviewboard: http://reviewboard.digium.com/r/174/


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@191140 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Tilghman Lesher
2009-04-29 18:53:01 +00:00
parent 0ea83eab48
commit a866a75900
38 changed files with 13291 additions and 13141 deletions

View File

@@ -132,56 +132,61 @@ static char *app_execif = "ExecIf";
static int exec_exec(struct ast_channel *chan, void *data)
{
int res = 0;
char *s, *appname, *endargs, args[MAXRESULT];
char *s, *appname, *endargs;
struct ast_app *app;
struct ast_str *args = NULL;
if (ast_strlen_zero(data))
return 0;
s = ast_strdupa(data);
args[0] = 0;
appname = strsep(&s, "(");
if (s) {
endargs = strrchr(s, ')');
if (endargs)
*endargs = '\0';
pbx_substitute_variables_helper(chan, s, args, MAXRESULT - 1);
if ((args = ast_str_create(16))) {
ast_str_substitute_variables(&args, 0, chan, s);
}
}
if (appname) {
app = pbx_findapp(appname);
if (app) {
res = pbx_exec(chan, app, args);
res = pbx_exec(chan, app, args ? ast_str_buffer(args) : NULL);
} else {
ast_log(LOG_WARNING, "Could not find application (%s)\n", appname);
res = -1;
}
}
ast_free(args);
return res;
}
static int tryexec_exec(struct ast_channel *chan, void *data)
{
int res = 0;
char *s, *appname, *endargs, args[MAXRESULT];
char *s, *appname, *endargs;
struct ast_app *app;
struct ast_str *args = NULL;
if (ast_strlen_zero(data))
return 0;
s = ast_strdupa(data);
args[0] = 0;
appname = strsep(&s, "(");
if (s) {
endargs = strrchr(s, ')');
if (endargs)
*endargs = '\0';
pbx_substitute_variables_helper(chan, s, args, MAXRESULT - 1);
if ((args = ast_str_create(16))) {
ast_str_substitute_variables(&args, 0, chan, s);
}
}
if (appname) {
app = pbx_findapp(appname);
if (app) {
res = pbx_exec(chan, app, args);
res = pbx_exec(chan, app, args ? ast_str_buffer(args) : NULL);
pbx_builtin_setvar_helper(chan, "TRYSTATUS", res ? "FAILED" : "SUCCESS");
} else {
ast_log(LOG_WARNING, "Could not find application (%s)\n", appname);
@@ -189,6 +194,7 @@ static int tryexec_exec(struct ast_channel *chan, void *data)
}
}
ast_free(args);
return 0;
}

View File

@@ -231,6 +231,7 @@ static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
int offset, depth = 0, maxdepth = 7;
int setmacrocontext=0;
int autoloopflag, inhangup = 0;
struct ast_str *tmp_subst = NULL;
char *save_macro_exten;
char *save_macro_context;
@@ -281,7 +282,6 @@ static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
return 0;
}
snprintf(depthc, sizeof(depthc), "%d", depth + 1);
pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
tmp = ast_strdupa(data);
rest = tmp;
@@ -312,6 +312,10 @@ static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
ast_autoservice_stop(chan);
}
if (!(tmp_subst = ast_str_create(16))) {
return -1;
}
/* Save old info */
oldpriority = chan->priority;
ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
@@ -337,6 +341,8 @@ static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
/* Setup environment for new run */
chan->exten[0] = 's';
chan->exten[1] = '\0';
@@ -415,8 +421,9 @@ static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
gosub_level++;
ast_debug(1, "Incrementing gosub_level\n");
} else if (!strcasecmp(runningapp, "GOSUBIF")) {
char tmp2[1024], *cond, *app_arg, *app2 = tmp2;
pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
char *cond, *app_arg, *app2;
ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
app2 = ast_str_buffer(tmp_subst);
cond = strsep(&app2, "?");
app_arg = strsep(&app2, ":");
if (pbx_checkcondition(cond)) {
@@ -438,19 +445,24 @@ static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
ast_debug(1, "Decrementing gosub_level\n");
} else if (!strncasecmp(runningapp, "EXEC", 4)) {
/* Must evaluate args to find actual app */
char tmp2[1024], *tmp3 = NULL;
pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
char *tmp2, *tmp3 = NULL;
ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
tmp2 = ast_str_buffer(tmp_subst);
if (!strcasecmp(runningapp, "EXECIF")) {
tmp3 = strchr(tmp2, '|');
if (tmp3)
if (tmp3) {
*tmp3++ = '\0';
if (!pbx_checkcondition(tmp2))
}
if (!pbx_checkcondition(tmp2)) {
tmp3 = NULL;
} else
}
} else {
tmp3 = tmp2;
}
if (tmp3)
if (tmp3) {
ast_debug(1, "Last app: %s\n", tmp3);
}
if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
gosub_level++;
@@ -547,6 +559,7 @@ static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
}
}
ast_channel_unlock(chan);
ast_free(tmp_subst);
return res;
}

View File

@@ -483,7 +483,8 @@ AST_APP_OPTIONS(minivm_accmess_options, {
AST_APP_OPTION('n', OPT_NAME_GREETING),
});
/*! \brief Structure for linked list of Mini-Voicemail users: \ref minivm_accounts */
/*!\internal
* \brief Structure for linked list of Mini-Voicemail users: \ref minivm_accounts */
struct minivm_account {
char username[AST_MAX_CONTEXT]; /*!< Mailbox username */
char domain[AST_MAX_CONTEXT]; /*!< Voicemail domain */
@@ -508,12 +509,14 @@ struct minivm_account {
AST_LIST_ENTRY(minivm_account) list;
};
/*! \brief The list of e-mail accounts */
/*!\internal
* \brief The list of e-mail accounts */
static AST_LIST_HEAD_STATIC(minivm_accounts, minivm_account);
/*! \brief Linked list of e-mail templates in various languages
These are used as templates for e-mails, pager messages and jabber messages
\ref message_templates
/*!\internal
* \brief Linked list of e-mail templates in various languages
* These are used as templates for e-mails, pager messages and jabber messages
* \ref message_templates
*/
struct minivm_template {
char name[80]; /*!< Template name */
@@ -588,11 +591,11 @@ static char default_vmformat[80];
static struct ast_flags globalflags = {0}; /*!< Global voicemail flags */
static int global_saydurationminfo;
static char global_charset[32]; /*!< Global charset in messages */
static double global_volgain; /*!< Volume gain for voicmemail via e-mail */
/*! \brief Default dateformat, can be overridden in configuration file */
/*!\internal
* \brief Default dateformat, can be overridden in configuration file */
#define DEFAULT_DATEFORMAT "%A, %B %d, %Y at %r"
#define DEFAULT_CHARSET "ISO-8859-1"
@@ -603,7 +606,8 @@ static int create_vmaccount(char *name, struct ast_variable *var, int realtime);
static struct minivm_account *find_user_realtime(const char *domain, const char *username);
static char *handle_minivm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
/*! \brief Create message template */
/*!\internal
* \brief Create message template */
static struct minivm_template *message_template_create(const char *name)
{
struct minivm_template *template;
@@ -622,7 +626,8 @@ static struct minivm_template *message_template_create(const char *name)
return template;
}
/*! \brief Release memory allocated by message template */
/*!\internal
* \brief Release memory allocated by message template */
static void message_template_free(struct minivm_template *template)
{
if (template->body)
@@ -631,7 +636,8 @@ static void message_template_free(struct minivm_template *template)
ast_free (template);
}
/*! \brief Build message template from configuration */
/*!\internal
* \brief Build message template from configuration */
static int message_template_build(const char *name, struct ast_variable *var)
{
struct minivm_template *template;
@@ -693,7 +699,8 @@ static int message_template_build(const char *name, struct ast_variable *var)
return error;
}
/*! \brief Find named template */
/*!\internal
* \brief Find named template */
static struct minivm_template *message_template_find(const char *name)
{
struct minivm_template *this, *res = NULL;
@@ -714,18 +721,21 @@ static struct minivm_template *message_template_find(const char *name)
}
/*! \brief Clear list of templates */
/*!\internal
* \brief Clear list of templates */
static void message_destroy_list(void)
{
struct minivm_template *this;
AST_LIST_LOCK(&message_templates);
while ((this = AST_LIST_REMOVE_HEAD(&message_templates, list)))
while ((this = AST_LIST_REMOVE_HEAD(&message_templates, list))) {
message_template_free(this);
}
AST_LIST_UNLOCK(&message_templates);
}
/*! \brief read buffer from file (base64 conversion) */
/*!\internal
* \brief read buffer from file (base64 conversion) */
static int b64_inbuf(struct b64_baseio *bio, FILE *fi)
{
int l;
@@ -747,7 +757,8 @@ static int b64_inbuf(struct b64_baseio *bio, FILE *fi)
return 1;
}
/*! \brief read character from file to buffer (base64 conversion) */
/*!\internal
* \brief read character from file to buffer (base64 conversion) */
static int b64_inchar(struct b64_baseio *bio, FILE *fi)
{
if (bio->iocp >= bio->iolen) {
@@ -758,7 +769,8 @@ static int b64_inchar(struct b64_baseio *bio, FILE *fi)
return bio->iobuf[bio->iocp++];
}
/*! \brief write buffer to file (base64 conversion) */
/*!\internal
* \brief write buffer to file (base64 conversion) */
static int b64_ochar(struct b64_baseio *bio, int c, FILE *so)
{
if (bio->linelength >= B64_BASELINELEN) {
@@ -776,7 +788,8 @@ static int b64_ochar(struct b64_baseio *bio, int c, FILE *so)
return 1;
}
/*! \brief Encode file to base64 encoding for email attachment (base64 conversion) */
/*!\internal
* \brief Encode file to base64 encoding for email attachment (base64 conversion) */
static int base_encode(char *filename, FILE *so)
{
unsigned char dtable[B64_BASEMAXINLINE];
@@ -859,7 +872,8 @@ static int get_date(char *s, int len)
}
/*! \brief Free user structure - if it's allocated */
/*!\internal
* \brief Free user structure - if it's allocated */
static void free_user(struct minivm_account *vmu)
{
if (vmu->chanvars)
@@ -869,8 +883,9 @@ static void free_user(struct minivm_account *vmu)
/*! \brief Prepare for voicemail template by adding channel variables
to the channel
/*!\internal
* \brief Prepare for voicemail template by adding channel variables
* to the channel
*/
static void prep_email_sub_vars(struct ast_channel *channel, const struct minivm_account *vmu, const char *cidnum, const char *cidname, const char *dur, const char *date, const char *counter)
{
@@ -899,7 +914,8 @@ static void prep_email_sub_vars(struct ast_channel *channel, const struct minivm
pbx_builtin_setvar_helper(channel, "MVM_COUNTER", counter);
}
/*! \brief Set default values for Mini-Voicemail users */
/*!\internal
* \brief Set default values for Mini-Voicemail users */
static void populate_defaults(struct minivm_account *vmu)
{
ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
@@ -907,26 +923,8 @@ static void populate_defaults(struct minivm_account *vmu)
vmu->volgain = global_volgain;
}
/*! \brief Fix quote of mail headers for non-ascii characters */
static char *mailheader_quote(const char *from, char *to, size_t len)
{
char *ptr = to;
*ptr++ = '"';
for (; ptr < to + len - 1; from++) {
if (*from == '"')
*ptr++ = '\\';
else if (*from == '\0')
break;
*ptr++ = *from;
}
if (ptr < to + len - 1)
*ptr++ = '"';
*ptr = '\0';
return to;
}
/*! \brief Allocate new vm user and set default values */
/*!\internal
* \brief Allocate new vm user and set default values */
static struct minivm_account *mvm_user_alloc(void)
{
struct minivm_account *new;
@@ -940,7 +938,8 @@ static struct minivm_account *mvm_user_alloc(void)
}
/*! \brief Clear list of users */
/*!\internal
* \brief Clear list of users */
static void vmaccounts_destroy_list(void)
{
struct minivm_account *this;
@@ -951,7 +950,8 @@ static void vmaccounts_destroy_list(void)
}
/*! \brief Find user from static memory object list */
/*!\internal
* \brief Find user from static memory object list */
static struct minivm_account *find_account(const char *domain, const char *username, int createtemp)
{
struct minivm_account *vmu = NULL, *cur;
@@ -992,8 +992,9 @@ static struct minivm_account *find_account(const char *domain, const char *usern
return vmu;
}
/*! \brief Find user in realtime storage
Returns pointer to minivm_account structure
/*!\internal
* \brief Find user in realtime storage
* \return pointer to minivm_account structure
*/
static struct minivm_account *find_user_realtime(const char *domain, const char *username)
{
@@ -1023,7 +1024,102 @@ static struct minivm_account *find_user_realtime(const char *domain, const char
return retval;
}
/*! \brief Send voicemail with audio file as an attachment */
/*!\internal
* \brief Check if the string would need encoding within the MIME standard, to
* avoid confusing certain mail software that expects messages to be 7-bit
* clean.
*/
static int check_mime(const char *str)
{
for (; *str; str++) {
if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
return 1;
}
}
return 0;
}
/*!\internal
* \brief Encode a string according to the MIME rules for encoding strings
* that are not 7-bit clean or contain control characters.
*
* Additionally, if the encoded string would exceed the MIME limit of 76
* characters per line, then the encoding will be broken up into multiple
* sections, separated by a space character, in order to facilitate
* breaking up the associated header across multiple lines.
*
* \param end An expandable buffer for holding the result
* \param maxlen \see ast_str
* \param charset Character set in which the result should be encoded
* \param start A string to be encoded
* \param preamble The length of the first line already used for this string,
* to ensure that each line maintains a maximum length of 76 chars.
* \param postamble the length of any additional characters appended to the
* line, used to ensure proper field wrapping.
* \return The encoded string.
*/
static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *charset, const char *start, size_t preamble, size_t postamble)
{
struct ast_str *tmp = ast_str_alloca(80);
int first_section = 1;
*end = '\0';
ast_str_reset(*end);
ast_str_set(&tmp, -1, "=?%s?Q?", charset);
for (; *start; start++) {
int need_encoding = 0;
if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
need_encoding = 1;
}
if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
(first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
(!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
(!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
/* Start new line */
ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
ast_str_set(&tmp, -1, "=?%s?Q?", charset);
first_section = 0;
}
if (need_encoding && *start == ' ') {
ast_str_append(&tmp, -1, "_");
} else if (need_encoding) {
ast_str_append(&tmp, -1, "=%hhX", *start);
} else {
ast_str_append(&tmp, -1, "%c", *start);
}
}
ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
return ast_str_buffer(*end);
}
/*!\internal
* \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
* \param from The string to work with.
* \param buf The destination buffer to write the modified quoted string.
* \param maxlen Always zero. \see ast_str
*
* \return The destination string with quotes wrapped on it (the to field).
*/
static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
{
const char *ptr;
/* We're only ever passing 0 to maxlen, so short output isn't possible */
ast_str_set(buf, maxlen, "\"");
for (ptr = from; *ptr; ptr++) {
if (*ptr == '"' || *ptr == '\\') {
ast_str_append(buf, maxlen, "\\%c", *ptr);
} else {
ast_str_append(buf, maxlen, "%c", *ptr);
}
}
ast_str_append(buf, maxlen, "\"");
return ast_str_buffer(*buf);
}
/*!\internal
* \brief Send voicemail with audio file as an attachment */
static int sendmail(struct minivm_template *template, struct minivm_account *vmu, char *cidnum, char *cidname, const char *filename, char *format, int duration, int attach_user_voicemail, enum mvm_messagetype type, const char *counter)
{
FILE *p = NULL;
@@ -1039,14 +1135,18 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
struct timeval now;
struct ast_tm tm;
struct minivm_zone *the_zone = NULL;
int len_passdata;
struct ast_channel *ast;
char *finalfilename;
char *passdata = NULL;
char *passdata2 = NULL;
struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
char *fromaddress;
char *fromemail;
if (!str1 || !str2) {
ast_free(str1);
ast_free(str2);
return -1;
}
if (type == MVM_MESSAGE_EMAIL) {
if (vmu && !ast_strlen_zero(vmu->email)) {
ast_copy_string(email, vmu->email, sizeof(email));
@@ -1160,51 +1260,71 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
if (ast_strlen_zero(fromaddress)) {
fprintf(p, "From: Asterisk PBX <%s>\n", who);
} else {
/* Allocate a buffer big enough for variable substitution */
int vmlen = strlen(fromaddress) * 3 + 200;
ast_debug(4, "Fromaddress template: %s\n", fromaddress);
if ((passdata = alloca(vmlen))) {
pbx_substitute_variables_helper(ast, fromaddress, passdata, vmlen);
len_passdata = strlen(passdata) * 2 + 3;
passdata2 = alloca(len_passdata);
fprintf(p, "From: %s <%s>\n", mailheader_quote(passdata, passdata2, len_passdata), who);
ast_str_substitute_variables(&str1, 0, ast, fromaddress);
if (check_mime(ast_str_buffer(str1))) {
int first_line = 1;
char *ptr;
ast_str_encode_mime(&str2, 0, template->charset, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
*ptr = '\0';
fprintf(p, "%s %s\n", first_line ? "From:" : "", ast_str_buffer(str2));
first_line = 0;
/* Substring is smaller, so this will never grow */
ast_str_set(&str2, 0, "%s", ptr + 1);
}
fprintf(p, "%s %s <%s>\n", first_line ? "From:" : "", ast_str_buffer(str2), who);
} else {
ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
fclose(p);
return -1;
fprintf(p, "From: %s <%s>\n", ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
}
}
ast_debug(4, "Fromstring now: %s\n", ast_strlen_zero(passdata) ? "-default-" : passdata);
fprintf(p, "Message-ID: <Asterisk-%d-%s-%d-%s>\n", (unsigned int)ast_random(), vmu->username, (int)getpid(), who);
len_passdata = strlen(vmu->fullname) * 2 + 3;
passdata2 = alloca(len_passdata);
if (!ast_strlen_zero(vmu->email))
fprintf(p, "To: %s <%s>\n", mailheader_quote(vmu->fullname, passdata2, len_passdata), vmu->email);
else
fprintf(p, "To: %s <%s@%s>\n", mailheader_quote(vmu->fullname, passdata2, len_passdata), vmu->username, vmu->domain);
if (!ast_strlen_zero(template->subject)) {
char *pass_data;
int vmlen = strlen(template->subject) * 3 + 200;
if ((pass_data = alloca(vmlen))) {
pbx_substitute_variables_helper(ast, template->subject, pass_data, vmlen);
fprintf(p, "Subject: %s\n", pass_data);
if (ast_strlen_zero(vmu->email)) {
snprintf(email, sizeof(email), "%s@%s", vmu->username, vmu->domain);
} else {
ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
fclose(p);
return -1;
ast_copy_string(email, vmu->email, sizeof(email));
}
ast_debug(4, "Subject now: %s\n", pass_data);
if (check_mime(vmu->fullname)) {
int first_line = 1;
char *ptr;
ast_str_encode_mime(&str2, 0, template->charset, vmu->fullname, strlen("To: "), strlen(email) + 3);
while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
*ptr = '\0';
fprintf(p, "%s %s\n", first_line ? "To:" : "", ast_str_buffer(str2));
first_line = 0;
/* Substring is smaller, so this will never grow */
ast_str_set(&str2, 0, "%s", ptr + 1);
}
fprintf(p, "%s %s <%s>\n", first_line ? "To:" : "", ast_str_buffer(str2), email);
} else {
fprintf(p, "To: %s <%s>\n", ast_str_quote(&str2, 0, vmu->fullname), email);
}
if (!ast_strlen_zero(template->subject)) {
ast_str_substitute_variables(&str1, 0, ast, template->subject);
if (check_mime(ast_str_buffer(str1))) {
int first_line = 1;
char *ptr;
ast_str_encode_mime(&str2, 0, template->charset, ast_str_buffer(str1), strlen("Subject: "), 0);
while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
*ptr = '\0';
fprintf(p, "%s %s\n", first_line ? "Subject:" : "", ast_str_buffer(str2));
first_line = 0;
/* Substring is smaller, so this will never grow */
ast_str_set(&str2, 0, "%s", ptr + 1);
}
fprintf(p, "%s %s\n", first_line ? "Subject:" : "", ast_str_buffer(str2));
} else {
fprintf(p, "Subject: %s\n", ast_str_buffer(str1));
}
} else {
fprintf(p, "Subject: New message in mailbox %s@%s\n", vmu->username, vmu->domain);
ast_debug(1, "Using default subject for this email \n");
}
if (option_debug > 2)
fprintf(p, "X-Asterisk-debug: template %s user account %s@%s\n", template->name, vmu->username, vmu->domain);
fprintf(p, "MIME-Version: 1.0\n");
@@ -1215,19 +1335,13 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
fprintf(p, "--%s\n", bound);
fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", global_charset);
fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", template->charset);
if (!ast_strlen_zero(template->body)) {
char *pass_data;
int vmlen = strlen(template->body)*3 + 200;
if ((pass_data = alloca(vmlen))) {
pbx_substitute_variables_helper(ast, template->body, pass_data, vmlen);
ast_debug(3, "Message now: %s\n-----\n", pass_data);
fprintf(p, "%s\n", pass_data);
} else
ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
ast_str_substitute_variables(&str1, 0, ast, template->body);
ast_debug(3, "Message now: %s\n-----\n", ast_str_buffer(str1));
fprintf(p, "%s\n", ast_str_buffer(str1));
} else {
fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message \n"
"in mailbox %s from %s, on %s so you might\n"
"want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
dur, vmu->username, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
@@ -1257,16 +1371,20 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
if (ast) {
ast = ast_channel_release(ast);
}
ast_free(str1);
ast_free(str2);
return 0;
}
/*! \brief Create directory based on components */
/*!\internal
* \brief Create directory based on components */
static int make_dir(char *dest, int len, const char *domain, const char *username, const char *folder)
{
return snprintf(dest, len, "%s%s/%s%s%s", MVM_SPOOL_DIR, domain, username, ast_strlen_zero(folder) ? "" : "/", folder ? folder : "");
}
/*! \brief Checks if directory exists. Does not create directory, but builds string in dest
/*!\internal
* \brief Checks if directory exists. Does not create directory, but builds string in dest
* \param dest String. base directory.
* \param len Int. Length base directory string.
* \param domain String. Ignored if is null or empty string.
@@ -1284,7 +1402,8 @@ static int check_dirpath(char *dest, int len, char *domain, char *username, char
return TRUE;
}
/*! \brief basically mkdir -p $dest/$domain/$username/$folder
/*!\internal
* \brief basically mkdir -p $dest/$domain/$username/$folder
* \param dest String. base directory.
* \param len Length of directory string
* \param domain String. Ignored if is null or empty string.
@@ -1305,7 +1424,8 @@ static int create_dirpath(char *dest, int len, char *domain, char *username, cha
}
/*! \brief Play intro message before recording voicemail
/*!\internal
* \brief Play intro message before recording voicemail
*/
static int invent_message(struct ast_channel *chan, char *domain, char *username, int busy, char *ecodes)
{
@@ -1362,7 +1482,8 @@ static int invent_message(struct ast_channel *chan, char *domain, char *username
return res;
}
/*! \brief Delete media files and attribute file */
/*!\internal
* \brief Delete media files and attribute file */
static int vm_delete(char *file)
{
int res;
@@ -1375,7 +1496,8 @@ static int vm_delete(char *file)
}
/*! \brief Record voicemail message & let caller review or re-record it, or set options if applicable */
/*!\internal
* \brief Record voicemail message & let caller review or re-record it, or set options if applicable */
static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
int outsidecaller, struct minivm_account *vmu, int *duration, const char *unlockdir,
signed char record_gain)
@@ -1524,7 +1646,8 @@ static void run_externnotify(struct ast_channel *chan, struct minivm_account *vm
ast_safe_system(arguments);
}
/*! \brief Send message to voicemail account owner */
/*!\internal
* \brief Send message to voicemail account owner */
static int notify_new_message(struct ast_channel *chan, const char *templatename, struct minivm_account *vmu, const char *filename, long duration, const char *format, char *cidnum, char *cidname)
{
char *stringp;
@@ -1537,9 +1660,10 @@ static int notify_new_message(struct ast_channel *chan, const char *templatename
if (!ast_strlen_zero(vmu->attachfmt)) {
if (strstr(format, vmu->attachfmt)) {
format = vmu->attachfmt;
} else
} else {
ast_log(LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'. Falling back to default format for '%s@%s'.\n", vmu->attachfmt, format, vmu->username, vmu->domain);
}
}
etemplate = message_template_find(vmu->etemplate);
if (!etemplate)
@@ -1595,13 +1719,15 @@ static int notify_new_message(struct ast_channel *chan, const char *templatename
run_externnotify(chan, vmu); /* Run external notification */
if (etemplate->locale)
if (etemplate->locale) {
setlocale(LC_TIME, oldlocale); /* Rest to old locale */
}
return res;
}
/*! \brief Record voicemail message, store into file prepared for sending e-mail */
/*!\internal
* \brief Record voicemail message, store into file prepared for sending e-mail */
static int leave_voicemail(struct ast_channel *chan, char *username, struct leave_vm_options *options)
{
char tmptxtfile[PATH_MAX];
@@ -1664,7 +1790,6 @@ static int leave_voicemail(struct ast_channel *chan, char *username, struct leav
snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
/* XXX This file needs to be in temp directory */
txtdes = mkstemp(tmptxtfile);
if (txtdes < 0) {
@@ -1751,12 +1876,14 @@ static int leave_voicemail(struct ast_channel *chan, char *username, struct leav
}
global_stats.lastreceived = ast_tvnow();
global_stats.receivedmessages++;
// /* Go ahead and delete audio files from system, they're not needed any more */
// if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
// ast_filedelete(tmptxtfile, NULL);
// /* Even not being used at the moment, it's better to convert ast_log to ast_debug anyway */
// ast_debug(2, "-_-_- Deleted audio file after notification :: %s \n", tmptxtfile);
// }
#if 0
/* Go ahead and delete audio files from system, they're not needed any more */
if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
ast_filedelete(tmptxtfile, NULL);
/* Even not being used at the moment, it's better to convert ast_log to ast_debug anyway */
ast_debug(2, "-_-_- Deleted audio file after notification :: %s \n", tmptxtfile);
}
#endif
if (res > 0)
res = 0;
@@ -1768,7 +1895,8 @@ static int leave_voicemail(struct ast_channel *chan, char *username, struct leav
return res;
}
/*! \brief Queue a message waiting event */
/*!\internal
* \brief Queue a message waiting event */
static void queue_mwi_event(const char *mbx, const char *ctx, int urgent, int new, int old)
{
struct ast_event *event;
@@ -1792,7 +1920,8 @@ static void queue_mwi_event(const char *mbx, const char *ctx, int urgent, int ne
ast_event_queue_and_cache(event);
}
/*! \brief Send MWI using interal Asterisk event subsystem */
/*!\internal
* \brief Send MWI using interal Asterisk event subsystem */
static int minivm_mwi_exec(struct ast_channel *chan, void *data)
{
int argc;
@@ -1833,7 +1962,8 @@ static int minivm_mwi_exec(struct ast_channel *chan, void *data)
}
/*! \brief Notify voicemail account owners - either generic template or user specific */
/*!\internal
* \brief Notify voicemail account owners - either generic template or user specific */
static int minivm_notify_exec(struct ast_channel *chan, void *data)
{
int argc;
@@ -1912,7 +2042,8 @@ static int minivm_notify_exec(struct ast_channel *chan, void *data)
}
/*! \brief Dialplan function to record voicemail */
/*!\internal
* \brief Dialplan function to record voicemail */
static int minivm_record_exec(struct ast_channel *chan, void *data)
{
int res = 0;
@@ -1968,7 +2099,8 @@ static int minivm_record_exec(struct ast_channel *chan, void *data)
return res;
}
/*! \brief Play voicemail prompts - either generic or user specific */
/*!\internal
* \brief Play voicemail prompts - either generic or user specific */
static int minivm_greet_exec(struct ast_channel *chan, void *data)
{
struct leave_vm_options leave_options = { 0, '\0'};
@@ -2154,7 +2286,8 @@ static int minivm_greet_exec(struct ast_channel *chan, void *data)
}
/*! \brief Dialplan application to delete voicemail */
/*!\internal
* \brief Dialplan application to delete voicemail */
static int minivm_delete_exec(struct ast_channel *chan, void *data)
{
int res = 0;
@@ -2609,7 +2742,6 @@ static int load_config(int reload)
ast_copy_string(default_vmformat, "wav", sizeof(default_vmformat));
ast_set2_flag((&globalflags), FALSE, MVM_REVIEW);
ast_set2_flag((&globalflags), FALSE, MVM_OPERATOR);
strcpy(global_charset, "ISO-8859-1");
/* Reset statistics */
memset(&global_stats, 0, sizeof(global_stats));
global_stats.reset = ast_tvnow();

View File

@@ -3843,9 +3843,10 @@ static int base_encode(char *filename, FILE *so)
return 1;
}
static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category, const char *flag)
static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, const char *category, const char *flag)
{
char callerid[256];
char num[12];
char fromdir[256], fromfile[256];
struct ast_config *msg_cfg;
const char *origcallerid, *origtime;
@@ -3856,8 +3857,8 @@ static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu
/* Prepare variables for substitution in email body and subject */
pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
snprintf(passdata, passdatasize, "%d", msgnum);
pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
snprintf(num, sizeof(num), "%d", msgnum);
pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
@@ -3901,30 +3902,32 @@ static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu
/*!
* \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
* \param from The string to work with.
* \param to The string to write the modified quoted string. This buffer should be sufficiently larger than the from string, so as to allow it to be expanded by the surrounding quotes and escaping of internal quotes.
* \param buf The buffer into which to write the modified quoted string.
* \param maxlen Always zero, but see \see ast_str
*
* \return The destination string with quotes wrapped on it (the to field).
*/
static char *quote(const char *from, char *to, size_t len)
static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
{
char *ptr = to;
*ptr++ = '"';
for (; ptr < to + len - 1; from++) {
if (*from == '"')
*ptr++ = '\\';
else if (*from == '\0')
break;
*ptr++ = *from;
const char *ptr;
/* We're only ever passing 0 to maxlen, so short output isn't possible */
ast_str_set(buf, maxlen, "\"");
for (ptr = from; *ptr; ptr++) {
if (*ptr == '"' || *ptr == '\\') {
ast_str_append(buf, maxlen, "\\%c", *ptr);
} else {
ast_str_append(buf, maxlen, "%c", *ptr);
}
if (ptr < to + len - 1)
*ptr++ = '"';
*ptr = '\0';
return to;
}
ast_str_append(buf, maxlen, "\"");
return ast_str_buffer(*buf);
}
/*! \brief
* fill in *tm for current time according to the proper timezone, if any.
* Return tm so it can be used as a function argument.
* \return tm so it can be used as a function argument.
*/
static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
{
@@ -3967,46 +3970,47 @@ static int check_mime(const char *str)
* sections, separated by a space character, in order to facilitate
* breaking up the associated header across multiple lines.
*
* \param start A string to be encoded
* \param end An expandable buffer for holding the result
* \param maxlen Always zero, but see \see ast_str
* \param start A string to be encoded
* \param preamble The length of the first line already used for this string,
* to ensure that each line maintains a maximum length of 76 chars.
* \param postamble the length of any additional characters appended to the
* line, used to ensure proper field wrapping.
* \retval The encoded string.
*/
static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
{
char tmp[80];
struct ast_str *tmp = ast_str_alloca(80);
int first_section = 1;
size_t endlen = 0, tmplen = 0;
*end = '\0';
tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
ast_str_reset(*end);
ast_str_set(&tmp, -1, "=?%s?Q?", charset);
for (; *start; start++) {
int need_encoding = 0;
if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
need_encoding = 1;
}
if ((first_section && need_encoding && preamble + tmplen > 70) ||
(first_section && !need_encoding && preamble + tmplen > 72) ||
(!first_section && need_encoding && tmplen > 70) ||
(!first_section && !need_encoding && tmplen > 72)) {
if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
(first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
(!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
(!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
/* Start new line */
endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
ast_str_set(&tmp, -1, "=?%s?Q?", charset);
first_section = 0;
}
if (need_encoding && *start == ' ') {
tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
ast_str_append(&tmp, -1, "_");
} else if (need_encoding) {
tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
ast_str_append(&tmp, -1, "=%hhX", *start);
} else {
tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
ast_str_append(&tmp, -1, "%c", *start);
}
}
snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
return end;
ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
return ast_str_buffer(*end);
}
/*!
@@ -4038,28 +4042,21 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
char dur[256];
struct ast_tm tm;
char enc_cidnum[256] = "", enc_cidname[256] = "";
char *passdata = NULL, *passdata2;
size_t len_passdata = 0, len_passdata2, tmplen;
struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
char *greeting_attachment;
char filename[256];
if (!str1 || !str2) {
ast_free(str1);
ast_free(str2);
return;
}
#ifdef IMAP_STORAGE
#define ENDL "\r\n"
#else
#define ENDL "\n"
#endif
/* One alloca for multiple fields */
len_passdata2 = strlen(vmu->fullname);
if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
len_passdata2 = tmplen;
}
if ((tmplen = strlen(fromstring)) > len_passdata2) {
len_passdata2 = tmplen;
}
len_passdata2 = len_passdata2 * 3 + 200;
passdata2 = alloca(len_passdata2);
if (cidnum) {
strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum));
}
@@ -4068,14 +4065,16 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
}
gethostname(host, sizeof(host) - 1);
if (strchr(srcemail, '@'))
if (strchr(srcemail, '@')) {
ast_copy_string(who, srcemail, sizeof(who));
else
} else {
snprintf(who, sizeof(who), "%s@%s", srcemail, host);
}
greeting_attachment = strrchr(ast_strdupa(attach), '/');
if (greeting_attachment)
if (greeting_attachment) {
*greeting_attachment++ = '\0';
}
snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
@@ -4088,23 +4087,22 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
struct ast_channel *ast;
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
char *ptr;
memset(passdata2, 0, len_passdata2);
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag);
pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
len_passdata = strlen(passdata2) * 3 + 300;
passdata = alloca(len_passdata);
if (check_mime(passdata2)) {
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
ast_str_substitute_variables(&str1, 0, ast, fromstring);
if (check_mime(ast_str_buffer(str1))) {
int first_line = 1;
encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
while ((ptr = strchr(passdata, ' '))) {
ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
*ptr = '\0';
fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
first_line = 0;
passdata = ptr + 1;
/* Substring is smaller, so this will never grow */
ast_str_set(&str2, 0, "%s", ptr + 1);
}
fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
} else {
fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
}
ast = ast_channel_release(ast);
} else {
@@ -4117,44 +4115,39 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
if (check_mime(vmu->fullname)) {
int first_line = 1;
char *ptr;
encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
while ((ptr = strchr(passdata2, ' '))) {
ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
*ptr = '\0';
fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
first_line = 0;
passdata2 = ptr + 1;
/* Substring is smaller, so this will never grow */
ast_str_set(&str2, 0, "%s", ptr + 1);
}
fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
} else {
fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
}
if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
struct ast_channel *ast;
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
int vmlen = strlen(e_subj) * 3 + 200;
/* Only allocate more space if the previous was not large enough */
if (vmlen > len_passdata) {
passdata = alloca(vmlen);
len_passdata = vmlen;
}
memset(passdata, 0, len_passdata);
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category, flag);
pbx_substitute_variables_helper(ast, e_subj, passdata, len_passdata);
if (check_mime(passdata)) {
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
ast_str_substitute_variables(&str1, 0, ast, e_subj);
if (check_mime(ast_str_buffer(str1))) {
int first_line = 1;
char *ptr;
encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
while ((ptr = strchr(passdata2, ' '))) {
ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
*ptr = '\0';
fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
first_line = 0;
passdata2 = ptr + 1;
/* Substring is smaller, so this will never grow */
ast_str_set(&str2, 0, "%s", ptr + 1);
}
fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
} else {
fprintf(p, "Subject: %s" ENDL, passdata);
fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
}
ast = ast_channel_release(ast);
} else {
@@ -4222,16 +4215,13 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
struct ast_channel *ast;
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
char *passdata;
int vmlen = strlen(e_body) * 3 + 200;
passdata = alloca(vmlen);
memset(passdata, 0, vmlen);
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
pbx_substitute_variables_helper(ast, e_body, passdata, vmlen);
fprintf(p, "%s" ENDL, passdata);
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
ast_str_substitute_variables(&str1, 0, ast, e_body);
fprintf(p, "%s" ENDL, ast_str_buffer(str1));
ast = ast_channel_release(ast);
} else
} else {
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
}
} else if (msgnum > -1) {
if (strcmp(vmu->mailbox, mailbox)) {
/* Forwarded type */
@@ -4296,6 +4286,8 @@ plain_message:
add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
}
}
ast_free(str1);
ast_free(str2);
}
static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum)
@@ -4349,7 +4341,6 @@ static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format,
}
return 0;
}
#undef ENDL
static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, const char *flag)
{
@@ -4381,6 +4372,7 @@ static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *c
static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category, const char *flag)
{
char enc_cidnum[256], enc_cidname[256];
char date[256];
char host[MAXHOSTNAMELEN] = "";
char who[256];
@@ -4389,49 +4381,106 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
char tmp2[PATH_MAX];
struct ast_tm tm;
FILE *p;
struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
if (!str1 || !str2) {
ast_free(str1);
ast_free(str2);
return -1;
}
if (cidnum) {
strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum));
}
if (cidname) {
strip_control(cidname, enc_cidname, sizeof(enc_cidname));
}
if ((p = vm_mkftemp(tmp)) == NULL) {
ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
ast_free(str1);
ast_free(str2);
return -1;
}
gethostname(host, sizeof(host)-1);
if (strchr(srcemail, '@'))
if (strchr(srcemail, '@')) {
ast_copy_string(who, srcemail, sizeof(who));
else
} else {
snprintf(who, sizeof(who), "%s@%s", srcemail, host);
}
snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
fprintf(p, "Date: %s\n", date);
if (*pagerfromstring) {
if (!ast_strlen_zero(pagerfromstring)) {
struct ast_channel *ast;
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
char *passdata;
int vmlen = strlen(fromstring)*3 + 200;
passdata = alloca(vmlen);
memset(passdata, 0, vmlen);
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
fprintf(p, "From: %s <%s>\n", passdata, who);
char *ptr;
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
if (check_mime(ast_str_buffer(str1))) {
int first_line = 1;
ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
*ptr = '\0';
fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
first_line = 0;
/* Substring is smaller, so this will never grow */
ast_str_set(&str2, 0, "%s", ptr + 1);
}
fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
} else {
fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
}
ast = ast_channel_release(ast);
} else
} else {
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
} else
fprintf(p, "From: Asterisk PBX <%s>\n", who);
fprintf(p, "To: %s\n", pager);
if (pagersubject) {
}
} else {
fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
}
if (check_mime(vmu->fullname)) {
int first_line = 1;
char *ptr;
ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
*ptr = '\0';
fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
first_line = 0;
/* Substring is smaller, so this will never grow */
ast_str_set(&str2, 0, "%s", ptr + 1);
}
fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
} else {
fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
}
if (!ast_strlen_zero(pagersubject)) {
struct ast_channel *ast;
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
char *passdata;
int vmlen = strlen(pagersubject) * 3 + 200;
passdata = alloca(vmlen);
memset(passdata, 0, vmlen);
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
fprintf(p, "Subject: %s\n\n", passdata);
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
ast_str_substitute_variables(&str1, 0, ast, pagersubject);
if (check_mime(ast_str_buffer(str1))) {
int first_line = 1;
char *ptr;
ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
*ptr = '\0';
fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
first_line = 0;
/* Substring is smaller, so this will never grow */
ast_str_set(&str2, 0, "%s", ptr + 1);
}
fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
} else {
fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
}
ast = ast_channel_release(ast);
} else
} else {
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
}
} else {
if (ast_strlen_zero(flag)) {
fprintf(p, "Subject: New VM\n\n");
@@ -4444,26 +4493,27 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
if (pagerbody) {
struct ast_channel *ast;
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
char *passdata;
int vmlen = strlen(pagerbody) * 3 + 200;
passdata = alloca(vmlen);
memset(passdata, 0, vmlen);
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
fprintf(p, "%s\n", passdata);
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
ast_str_substitute_variables(&str1, 0, ast, pagerbody);
fprintf(p, "%s" ENDL, ast_str_buffer(str1));
ast = ast_channel_release(ast);
} else
} else {
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
}
} else {
fprintf(p, "New %s long %s msg in box %s\n"
"from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
}
fclose(p);
snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
ast_safe_system(tmp2);
ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
ast_free(str1);
ast_free(str2);
return 0;
}
#undef ENDL
/*!
* \brief Gets the current date and time, as formatted string.
@@ -5025,7 +5075,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
char fmt[80];
char *context;
char ecodes[17] = "#";
char tmp[1024] = "";
struct ast_str *tmp = ast_str_create(16);
char *tmpptr;
struct ast_vm_user *vmu;
struct ast_vm_user svm;
@@ -5034,9 +5084,13 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
const char *alldtmf = "0123456789ABCD*#";
char flag[80];
ast_copy_string(tmp, ext, sizeof(tmp));
ext = tmp;
if ((context = strchr(tmp, '@'))) {
if (!tmp) {
return -1;
}
ast_str_set(&tmp, 0, "%s", ext);
ext = ast_str_buffer(tmp);
if ((context = strchr(ext, '@'))) {
*context++ = '\0';
tmpptr = strchr(context, '&');
} else {

View File

@@ -44,6 +44,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/threadstorage.h"
#include "asterisk/strings.h"
#define CUSTOM_LOG_DIR "/cdr_custom"
@@ -52,6 +54,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
AST_MUTEX_DEFINE_STATIC(lock);
AST_MUTEX_DEFINE_STATIC(mf_lock);
AST_THREADSTORAGE(custom_buf);
static char *name = "cdr-custom";
static char master[PATH_MAX];
@@ -110,35 +114,37 @@ static int load_config(int reload)
static int custom_log(struct ast_cdr *cdr)
{
FILE *mf = NULL;
/* Make sure we have a big enough buf */
char buf[2048];
struct ast_channel dummy;
struct ast_str *str;
/* Abort if no master file is specified */
if (ast_strlen_zero(master))
if (ast_strlen_zero(master)) {
return 0;
}
/* Batching saves memory management here. Otherwise, it's the same as doing an allocation and free each time. */
if (!(str = ast_str_thread_get(&custom_buf, 16))) {
return -1;
}
ast_str_reset(str);
/* Quite possibly the first use of a static struct ast_channel, we need it so the var funcs will work */
memset(&dummy, 0, sizeof(dummy));
dummy.cdr = cdr;
pbx_substitute_variables_helper(&dummy, format, buf, sizeof(buf) - 1);
ast_str_substitute_variables(&str, 0, &dummy, format);
/* because of the absolutely unconditional need for the
highest reliability possible in writing billing records,
we open write and close the log file each time */
ast_mutex_lock(&mf_lock);
mf = fopen(master, "a");
if (mf) {
fputs(buf, mf);
if ((mf = fopen(master, "a"))) {
fputs(ast_str_buffer(str), mf);
fflush(mf); /* be particularly anal here */
fclose(mf);
mf = NULL;
ast_mutex_unlock(&mf_lock);
} else {
ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", master, strerror(errno));
ast_mutex_unlock(&mf_lock);
}
ast_mutex_unlock(&mf_lock);
return 0;
}

23594
configure vendored

File diff suppressed because it is too large Load Diff

View File

@@ -31,6 +31,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/aes.h"
#include "asterisk/strings.h"
#define AES_BLOCK_SIZE 16
@@ -71,15 +72,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
static int aes_helper(struct ast_channel *chan, const char *cmd, char *data,
char *buf, size_t len)
char *buf, struct ast_str **str, ssize_t maxlen)
{
unsigned char curblock[AES_BLOCK_SIZE] = { 0, };
char *tmp;
char *tmp = NULL;
char *tmpP;
int data_len, encrypt;
int keylen, len, tmplen, elen = 0;
ast_aes_encrypt_key ecx; /* AES 128 Encryption context */
ast_aes_decrypt_key dcx;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(key);
AST_APP_ARG(data);
@@ -92,22 +93,37 @@ static int aes_helper(struct ast_channel *chan, const char *cmd, char *data,
return -1;
}
if (strlen(args.key) != AES_BLOCK_SIZE) { /* key must be of 16 characters in length, 128 bits */
ast_log(LOG_WARNING, "Syntax: %s(<key>,<data>) - <key> parameter must be exactly 16 characters!\n", cmd);
if ((keylen = strlen(args.key)) != AES_BLOCK_SIZE) { /* key must be of 16 characters in length, 128 bits */
ast_log(LOG_WARNING, "Syntax: %s(<key>,<data>) - <key> parameter must be exactly 16 characters%s!\n", cmd, keylen < 16 ? " - padding" : "");
return -1;
}
ast_aes_encrypt_key((unsigned char *) args.key, &ecx); /* encryption: plaintext -> encryptedtext -> base64 */
ast_aes_decrypt_key((unsigned char *) args.key, &dcx); /* decryption: base64 -> encryptedtext -> plaintext */
tmp = ast_calloc(1, len); /* requires a tmp buffer for the base64 decode */
tmpP = tmp;
if (buf) {
len = maxlen;
} else if (maxlen == -1) {
len = ast_str_size(*str);
} else if (maxlen > 0) {
len = maxlen;
} else {
len = INT_MAX;
}
ast_debug(3, "len=%d\n", len);
encrypt = strcmp("AES_DECRYPT", cmd); /* -1 if encrypting, 0 if decrypting */
/* Round up the buffer to an even multiple of 16, plus 1 */
tmplen = (strlen(args.data) / 16 + 1) * 16 + 1;
tmp = ast_calloc(1, tmplen);
tmpP = tmp;
if (encrypt) { /* if decrypting first decode src to base64 */
ast_copy_string(tmp, args.data, len);
/* encryption: plaintext -> encryptedtext -> base64 */
ast_aes_encrypt_key((unsigned char *) args.key, &ecx);
strcpy(tmp, args.data);
data_len = strlen(tmp);
} else {
data_len = ast_base64decode((unsigned char *) tmp, args.data, len);
/* decryption: base64 -> encryptedtext -> plaintext */
ast_aes_decrypt_key((unsigned char *) args.key, &dcx);
data_len = ast_base64decode((unsigned char *) tmp, args.data, tmplen);
}
if (data_len >= len) { /* make sure to not go over buffer len */
@@ -116,8 +132,11 @@ static int aes_helper(struct ast_channel *chan, const char *cmd, char *data,
}
while (data_len > 0) {
/* Tricky operation. We first copy the data into curblock, then
* the data is encrypted or decrypted and put back into the original
* buffer. */
memset(curblock, 0, AES_BLOCK_SIZE);
memcpy(curblock, tmpP, (data_len < AES_BLOCK_SIZE) ? data_len : AES_BLOCK_SIZE);
memcpy(curblock, tmpP, AES_BLOCK_SIZE);
if (encrypt) {
ast_aes_encrypt(curblock, (unsigned char *) tmpP, &ecx);
} else {
@@ -125,26 +144,53 @@ static int aes_helper(struct ast_channel *chan, const char *cmd, char *data,
}
tmpP += AES_BLOCK_SIZE;
data_len -= AES_BLOCK_SIZE;
elen += AES_BLOCK_SIZE;
}
if (encrypt) { /* if encrypting encode result to base64 */
ast_base64encode(buf, (unsigned char *) tmp, strlen(tmp), len);
if (buf) {
ast_base64encode(buf, (unsigned char *) tmp, elen, len);
} else {
if (maxlen >= 0) {
ast_str_make_space(str, maxlen ? maxlen : elen * 4 / 3 + 2);
}
ast_base64encode(ast_str_buffer(*str), (unsigned char *) tmp, elen, ast_str_size(*str));
ast_str_update(*str);
}
} else {
if (buf) {
memcpy(buf, tmp, len);
} else {
ast_str_set(str, maxlen, "%s", tmp);
}
}
ast_free(tmp);
return 0;
}
static int aes_buf_helper(struct ast_channel *chan, const char *cmd, char *data,
char *buf, size_t maxlen)
{
return aes_helper(chan, cmd, data, buf, NULL, maxlen);
}
static int aes_str_helper(struct ast_channel *chan, const char *cmd, char *data,
struct ast_str **buf, ssize_t maxlen)
{
return aes_helper(chan, cmd, data, NULL, buf, maxlen);
}
static struct ast_custom_function aes_encrypt_function = {
.name = "AES_ENCRYPT",
.read = aes_helper,
.read = aes_buf_helper,
.read2 = aes_str_helper,
};
static struct ast_custom_function aes_decrypt_function = {
.name = "AES_DECRYPT",
.read = aes_helper,
.read = aes_buf_helper,
.read2 = aes_str_helper,
};
static int unload_module(void)

View File

@@ -29,6 +29,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/module.h"
#include "asterisk/pbx.h" /* function register/unregister */
#include "asterisk/utils.h"
#include "asterisk/strings.h"
/*** DOCUMENTATION
<function name="BASE64_ENCODE" language="en_US">
@@ -59,40 +60,61 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</function>
***/
static int base64_encode(struct ast_channel *chan, const char *cmd, char *data,
char *buf, size_t len)
static int base64_helper(struct ast_channel *chan, const char *cmd, char *data,
char *buf, struct ast_str **str, ssize_t len)
{
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "Syntax: BASE64_ENCODE(<data>) - missing argument!\n");
ast_log(LOG_WARNING, "Syntax: %s(<data>) - missing argument!\n", cmd);
return -1;
}
if (cmd[7] == 'E') {
if (buf) {
ast_base64encode(buf, (unsigned char *) data, strlen(data), len);
} else {
if (len >= 0) {
ast_str_make_space(str, len ? len : ast_str_strlen(*str) + strlen(data) * 4 / 3 + 2);
}
ast_base64encode(ast_str_buffer(*str) + ast_str_strlen(*str), (unsigned char *) data, strlen(data), ast_str_size(*str) - ast_str_strlen(*str));
ast_str_update(*str);
}
} else {
if (buf) {
ast_base64decode((unsigned char *) buf, data, len);
} else {
if (len >= 0) {
ast_str_make_space(str, len ? len : ast_str_strlen(*str) + strlen(data) * 3 / 4 + 2);
}
ast_base64decode((unsigned char *) ast_str_buffer(*str) + ast_str_strlen(*str), data, ast_str_size(*str) - ast_str_strlen(*str));
ast_str_update(*str);
}
}
return 0;
}
static int base64_decode(struct ast_channel *chan, const char *cmd, char *data,
static int base64_buf_helper(struct ast_channel *chan, const char *cmd, char *data,
char *buf, size_t len)
{
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "Syntax: BASE64_DECODE(<base_64 string>) - missing argument!\n");
return -1;
return base64_helper(chan, cmd, data, buf, NULL, len);
}
ast_base64decode((unsigned char *) buf, data, len);
return 0;
static int base64_str_helper(struct ast_channel *chan, const char *cmd, char *data,
struct ast_str **buf, ssize_t len)
{
return base64_helper(chan, cmd, data, NULL, buf, len);
}
static struct ast_custom_function base64_encode_function = {
.name = "BASE64_ENCODE",
.read = base64_encode,
.read = base64_buf_helper,
.read2 = base64_str_helper,
};
static struct ast_custom_function base64_decode_function = {
.name = "BASE64_DECODE",
.read = base64_decode,
.read = base64_buf_helper,
.read2 = base64_str_helper,
};
static int unload_module(void)

View File

@@ -70,9 +70,26 @@ static int blacklist_read(struct ast_channel *chan, const char *cmd, char *data,
return 0;
}
static int blacklist_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
{
/* 2 bytes is a single integer, plus terminating null */
if (ast_str_size(*str) - ast_str_strlen(*str) < 2) {
if (len > ast_str_size(*str) || len == 0) {
ast_str_make_space(str, len ? len : ast_str_strlen(*str) + 2);
}
}
if (ast_str_size(*str) - ast_str_strlen(*str) >= 2) {
int res = blacklist_read(chan, cmd, data, ast_str_buffer(*str) + ast_str_strlen(*str), 2);
ast_str_update(*str);
return res;
}
return -1;
}
static struct ast_custom_function blacklist_function = {
.name = "BLACKLIST",
.read = blacklist_read,
.read2 = blacklist_read2,
};
static int unload_module(void)

View File

@@ -274,12 +274,14 @@ static int callerid_write(struct ast_channel *chan, const char *cmd, char *data,
static struct ast_custom_function callerid_function = {
.name = "CALLERID",
.read = callerid_read,
.read_max = 256,
.write = callerid_write,
};
static struct ast_custom_function callerpres_function = {
.name = "CALLERPRES",
.read = callerpres_read,
.read_max = 50,
.write = callerpres_write,
};

View File

@@ -276,7 +276,7 @@ yuck:
return 0;
}
static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int acf_curlopt_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len)
{
struct ast_datastore *store;
struct global_curl_info *list[2] = { &global_curl_info, NULL };
@@ -303,38 +303,78 @@ static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *dat
AST_LIST_TRAVERSE(list[i], cur, list) {
if (cur->key == key) {
if (ot == OT_BOOLEAN || ot == OT_INTEGER) {
if (buf) {
snprintf(buf, len, "%ld", (long) cur->value);
} else {
ast_str_set(bufstr, len, "%ld", (long) cur->value);
}
} else if (ot == OT_INTEGER_MS) {
if ((long) cur->value % 1000 == 0) {
if (buf) {
snprintf(buf, len, "%ld", (long)cur->value / 1000);
} else {
ast_str_set(bufstr, len, "%ld", (long) cur->value / 1000);
}
} else {
if (buf) {
snprintf(buf, len, "%.3f", (double) ((long) cur->value) / 1000.0);
} else {
ast_str_set(bufstr, len, "%.3f", (double) ((long) cur->value) / 1000.0);
}
}
} else if (ot == OT_STRING) {
ast_debug(1, "Found entry %p, with key %d and value %p\n", cur, cur->key, cur->value);
if (buf) {
ast_copy_string(buf, cur->value, len);
} else {
ast_str_set(bufstr, 0, "%s", (char *) cur->value);
}
} else if (key == CURLOPT_PROXYTYPE) {
if (0) {
#if CURLVERSION_ATLEAST(7,15,2)
} else if ((long)cur->value == CURLPROXY_SOCKS4) {
if (buf) {
ast_copy_string(buf, "socks4", len);
} else {
ast_str_set(bufstr, 0, "socks4");
}
#endif
#if CURLVERSION_ATLEAST(7,18,0)
} else if ((long)cur->value == CURLPROXY_SOCKS4A) {
if (buf) {
ast_copy_string(buf, "socks4a", len);
} else {
ast_str_set(bufstr, 0, "socks4a");
}
#endif
} else if ((long)cur->value == CURLPROXY_SOCKS5) {
if (buf) {
ast_copy_string(buf, "socks5", len);
} else {
ast_str_set(bufstr, 0, "socks5");
}
#if CURLVERSION_ATLEAST(7,18,0)
} else if ((long)cur->value == CURLPROXY_SOCKS5_HOSTNAME) {
if (buf) {
ast_copy_string(buf, "socks5hostname", len);
} else {
ast_str_set(bufstr, 0, "socks5hostname");
}
#endif
#if CURLVERSION_ATLEAST(7,10,0)
} else if ((long)cur->value == CURLPROXY_HTTP) {
if (buf) {
ast_copy_string(buf, "http", len);
} else {
ast_str_set(bufstr, 0, "http");
}
#endif
} else {
if (buf) {
ast_copy_string(buf, "unknown", len);
} else {
ast_str_set(bufstr, 0, "unknown");
}
}
}
break;
@@ -349,6 +389,16 @@ static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *dat
return cur ? 0 : -1;
}
static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
return acf_curlopt_helper(chan, cmd, data, buf, NULL, len);
}
static int acf_curlopt_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
{
return acf_curlopt_helper(chan, cmd, data, NULL, buf, len);
}
static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
{
register int realsize = size * nmemb;
@@ -391,7 +441,7 @@ static void curl_instance_cleanup(void *data)
AST_THREADSTORAGE_CUSTOM(curl_instance, curl_instance_init, curl_instance_cleanup);
static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len)
static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info, char *buf, struct ast_str **input_str, ssize_t len)
{
struct ast_str *str = ast_str_create(16);
int ret = -1;
@@ -405,7 +455,9 @@ static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info,
int hashcompat = 0;
AST_LIST_HEAD(global_curl_info, curl_settings) *list = NULL;
if (buf) {
*buf = '\0';
}
if (ast_strlen_zero(info)) {
ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
@@ -483,11 +535,19 @@ static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info,
rowcount++;
}
pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
if (buf) {
ast_copy_string(buf, ast_str_buffer(values), len);
} else {
ast_str_set(input_str, len, "%s", ast_str_buffer(values));
}
ast_free(fields);
ast_free(values);
} else {
if (buf) {
ast_copy_string(buf, ast_str_buffer(str), len);
} else {
ast_str_set(input_str, len, "%s", ast_str_buffer(str));
}
}
ret = 0;
}
@@ -499,6 +559,16 @@ static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info,
return ret;
}
static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len)
{
return acf_curl_helper(chan, cmd, info, buf, NULL, len);
}
static int acf_curl2_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
{
return acf_curl_helper(chan, cmd, info, NULL, buf, len);
}
struct ast_custom_function acf_curl = {
.name = "CURL",
.synopsis = "Retrieves the contents of a URL",
@@ -507,6 +577,7 @@ struct ast_custom_function acf_curl = {
" url - URL to retrieve\n"
" post-data - Optional data to send as a POST (GET is default action)\n",
.read = acf_curl_exec,
.read2 = acf_curl2_exec,
};
struct ast_custom_function acf_curlopt = {
@@ -532,6 +603,7 @@ struct ast_custom_function acf_curlopt = {
" hashcompat - Result data will be compatible for use with HASH()\n"
"",
.read = acf_curlopt_read,
.read2 = acf_curlopt_read2,
.write = acf_curlopt_write,
};

View File

@@ -77,9 +77,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</function>
***/
/* Maximum length of any variable */
#define MAXRESULT 1024
struct sortable_keys {
char *key;
float value;
@@ -151,7 +148,7 @@ static int sort_internal(struct ast_channel *chan, char *data, char *buffer, siz
return 0;
}
static int cut_internal(struct ast_channel *chan, char *data, char *buffer, size_t buflen)
static int cut_internal(struct ast_channel *chan, char *data, struct ast_str **buf, ssize_t buflen)
{
char *parse;
size_t delim_consumed;
@@ -160,8 +157,7 @@ static int cut_internal(struct ast_channel *chan, char *data, char *buffer, size
AST_APP_ARG(delimiter);
AST_APP_ARG(field);
);
*buffer = '\0';
struct ast_str *str = ast_str_create(16);
parse = ast_strdupa(data);
@@ -169,31 +165,30 @@ static int cut_internal(struct ast_channel *chan, char *data, char *buffer, size
/* Check and parse arguments */
if (args.argc < 3) {
ast_free(str);
return ERROR_NOARG;
} else {
char d, ds[2] = "";
char ds[2] = "";
char *tmp = alloca(strlen(args.varname) + 4);
char varvalue[MAXRESULT], *tmp2=varvalue;
if (tmp) {
snprintf(tmp, strlen(args.varname) + 4, "${%s}", args.varname);
} else {
ast_free(str);
return ERROR_NOMEM;
}
if (ast_get_encoded_char(args.delimiter, ds, &delim_consumed))
ast_copy_string(ds, "-", sizeof(ds));
/* String form of the delimiter, for use with strsep(3) */
d = *ds;
ast_str_substitute_variables(&str, 0, chan, tmp);
pbx_substitute_variables_helper(chan, tmp, tmp2, MAXRESULT - 1);
if (tmp2) {
if (ast_str_strlen(str)) {
int curfieldnum = 1;
char *tmp2 = ast_str_buffer(str);
while (tmp2 != NULL && args.field != NULL) {
char *nextgroup = strsep(&(args.field), "&");
int num1 = 0, num2 = MAXRESULT;
int num1 = 0, num2 = INT_MAX;
char trashchar;
if (sscanf(nextgroup, "%d-%d", &num1, &num2) == 2) {
@@ -203,18 +198,19 @@ static int cut_internal(struct ast_channel *chan, char *data, char *buffer, size
num1 = 0;
} else if ((sscanf(nextgroup, "%d%c", &num1, &trashchar) == 2) && (trashchar == '-')) {
/* range with start */
num2 = MAXRESULT;
num2 = INT_MAX;
} else if (sscanf(nextgroup, "%d", &num1) == 1) {
/* single number */
num2 = num1;
} else {
ast_free(str);
return ERROR_USAGE;
}
/* Get to start, if any */
if (num1 > 0) {
while (tmp2 != (char *)NULL + 1 && curfieldnum < num1) {
tmp2 = strchr(tmp2, d) + 1;
while (tmp2 != NULL && curfieldnum < num1) {
tmp2 = strchr(tmp2 + 1, ds[0]);
curfieldnum++;
}
}
@@ -223,25 +219,16 @@ static int cut_internal(struct ast_channel *chan, char *data, char *buffer, size
if ((num1 > 0) && (curfieldnum > num1))
ast_log(LOG_WARNING, "We're already past the field you wanted?\n");
/* Re-null tmp2 if we added 1 to NULL */
if (tmp2 == (char *)NULL + 1)
tmp2 = NULL;
/* Output fields until we either run out of fields or num2 is reached */
while (tmp2 != NULL && curfieldnum <= num2) {
char *tmp3 = strsep(&tmp2, ds);
int curlen = strlen(buffer);
if (curlen)
snprintf(buffer + curlen, buflen - curlen, "%c%s", d, tmp3);
else
snprintf(buffer, buflen, "%s", tmp3);
ast_str_append(buf, buflen, "%s%s", ast_str_strlen(*buf) ? ds : "", tmp3);
curfieldnum++;
}
}
}
}
ast_free(str);
return 0;
}
@@ -267,6 +254,32 @@ static int acf_sort_exec(struct ast_channel *chan, const char *cmd, char *data,
}
static int acf_cut_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
int ret = -1;
struct ast_str *str = ast_str_create(16);
switch (cut_internal(chan, data, &str, len)) {
case ERROR_NOARG:
ast_log(LOG_ERROR, "Syntax: CUT(<varname>,<char-delim>,<range-spec>) - missing argument!\n");
break;
case ERROR_NOMEM:
ast_log(LOG_ERROR, "Out of memory\n");
break;
case ERROR_USAGE:
ast_log(LOG_ERROR, "Usage: CUT(<varname>,<char-delim>,<range-spec>)\n");
break;
case 0:
ret = 0;
ast_copy_string(buf, ast_str_buffer(str), len);
break;
default:
ast_log(LOG_ERROR, "Unknown internal error\n");
}
ast_free(str);
return ret;
}
static int acf_cut_exec2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
{
int ret = -1;
@@ -298,6 +311,7 @@ struct ast_custom_function acf_sort = {
struct ast_custom_function acf_cut = {
.name = "CUT",
.read = acf_cut_exec,
.read2 = acf_cut_exec2,
};
static int unload_module(void)

View File

@@ -201,6 +201,7 @@ static int function_db_exists(struct ast_channel *chan, const char *cmd,
static struct ast_custom_function db_exists_function = {
.name = "DB_EXISTS",
.read = function_db_exists,
.read_max = 2,
};
static int function_db_delete(struct ast_channel *chan, const char *cmd,

View File

@@ -105,6 +105,7 @@ static int isexten_function_read(struct ast_channel *chan, const char *cmd, char
static struct ast_custom_function isexten_function = {
.name = "DIALPLAN_EXISTS",
.read = isexten_function_read,
.read_max = 2,
};
static int unload_module(void)

View File

@@ -230,7 +230,8 @@ static struct ast_custom_function env_function = {
static struct ast_custom_function stat_function = {
.name = "STAT",
.read = stat_read
.read = stat_read,
.read_max = 12,
};
static struct ast_custom_function file_function = {

View File

@@ -122,6 +122,7 @@ static int extstate_read(struct ast_channel *chan, const char *cmd, char *data,
static struct ast_custom_function extstate_function = {
.name = "EXTENSION_STATE",
.read = extstate_read,
.read_max = 12,
};
static int unload_module(void)

View File

@@ -134,6 +134,7 @@ static int group_count_function_read(struct ast_channel *chan, const char *cmd,
static struct ast_custom_function group_count_function = {
.name = "GROUP_COUNT",
.read = group_count_function_read,
.read_max = 12,
};
static int group_match_count_function_read(struct ast_channel *chan,
@@ -159,6 +160,7 @@ static int group_match_count_function_read(struct ast_channel *chan,
static struct ast_custom_function group_match_count_function = {
.name = "GROUP_MATCH_COUNT",
.read = group_match_count_function_read,
.read_max = 12,
.write = NULL,
};

View File

@@ -324,16 +324,19 @@ static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, c
static struct ast_custom_function lock_function = {
.name = "LOCK",
.read = lock_read,
.read_max = 2,
};
static struct ast_custom_function trylock_function = {
.name = "TRYLOCK",
.read = trylock_read,
.read_max = 2,
};
static struct ast_custom_function unlock_function = {
.name = "UNLOCK",
.read = unlock_read,
.read_max = 2,
};
static int unload_module(void)

View File

@@ -223,25 +223,40 @@ static int set(struct ast_channel *chan, const char *cmd, char *data, char *buf,
return 0;
}
static int acf_import(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int set2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
{
if (len > -1) {
ast_str_make_space(str, len == 0 ? strlen(data) : len);
}
return set(chan, cmd, data, ast_str_buffer(*str), ast_str_size(*str));
}
static int import_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **str, ssize_t len)
{
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(channel);
AST_APP_ARG(varname);
);
AST_STANDARD_APP_ARGS(args, data);
buf[0] = '\0';
if (buf) {
*buf = '\0';
}
if (!ast_strlen_zero(args.varname)) {
struct ast_channel *chan2;
if ((chan2 = ast_channel_get_by_name(args.channel))) {
char *s = alloca(strlen(args.varname) + 4);
if (s) {
sprintf(s, "${%s}", args.varname);
ast_channel_lock(chan2);
if (buf) {
pbx_substitute_variables_helper(chan2, s, buf, len);
} else {
ast_str_substitute_variables(str, len, chan2, s);
}
ast_channel_unlock(chan2);
}
chan2 = ast_channel_unref(chan2);
}
}
@@ -249,19 +264,32 @@ static int acf_import(struct ast_channel *chan, const char *cmd, char *data, cha
return 0;
}
static int import_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
return import_helper(chan, cmd, data, buf, NULL, len);
}
static int import_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
{
return import_helper(chan, cmd, data, NULL, str, len);
}
static struct ast_custom_function isnull_function = {
.name = "ISNULL",
.read = isnull,
.read_max = 2,
};
static struct ast_custom_function set_function = {
.name = "SET",
.read = set,
.read2 = set2,
};
static struct ast_custom_function exists_function = {
.name = "EXISTS",
.read = exists,
.read_max = 2,
};
static struct ast_custom_function if_function = {
@@ -276,7 +304,8 @@ static struct ast_custom_function if_time_function = {
static struct ast_custom_function import_function = {
.name = "IMPORT",
.read = acf_import,
.read = import_read,
.read2 = import_read2,
};
static int unload_module(void)

View File

@@ -64,6 +64,7 @@ static int md5(struct ast_channel *chan, const char *cmd, char *data,
static struct ast_custom_function md5_function = {
.name = "MD5",
.read = md5,
.read_max = 33,
};
static int unload_module(void)

View File

@@ -65,6 +65,7 @@ static int ifmodule_read(struct ast_channel *chan, const char *cmd, char *data,
static struct ast_custom_function ifmodule_function = {
.name = "IFMODULE",
.read = ifmodule_read,
.read_max = 2,
};

View File

@@ -87,6 +87,7 @@ static int acf_rand_exec(struct ast_channel *chan, const char *cmd,
static struct ast_custom_function acf_rand = {
.name = "RAND",
.read = acf_rand_exec,
.read_max = 12,
};
static int unload_module(void)

View File

@@ -74,6 +74,7 @@ static int sha1(struct ast_channel *chan, const char *cmd, char *data,
static struct ast_custom_function sha1_function = {
.name = "SHA1",
.read = sha1,
.read_max = 42,
};
static int unload_module(void)

View File

@@ -337,13 +337,15 @@ static int speex_read(struct ast_channel *chan, const char *cmd, char *data, cha
static struct ast_custom_function agc_function = {
.name = "AGC",
.write = speex_write,
.read = speex_read
.read = speex_read,
.read_max = 22,
};
static struct ast_custom_function denoise_function = {
.name = "DENOISE",
.write = speex_write,
.read = speex_read
.read = speex_read,
.read_max = 22,
};
static int unload_module(void)

View File

@@ -343,10 +343,11 @@ AST_THREADSTORAGE(result_buf);
</function>
***/
static int function_fieldqty(struct ast_channel *chan, const char *cmd,
char *parse, char *buf, size_t len)
static int function_fieldqty_helper(struct ast_channel *chan, const char *cmd,
char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
{
char *varsubst, varval[8192], *varval2 = varval;
char *varsubst;
struct ast_str *str = ast_str_create(16);
int fieldcount = 0;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(varname);
@@ -355,6 +356,10 @@ static int function_fieldqty(struct ast_channel *chan, const char *cmd,
char delim[2] = "";
size_t delim_used;
if (!str) {
return -1;
}
AST_STANDARD_APP_ARGS(args, parse);
if (args.delim) {
ast_get_encoded_char(args.delim, delim, &delim_used);
@@ -362,27 +367,47 @@ static int function_fieldqty(struct ast_channel *chan, const char *cmd,
varsubst = alloca(strlen(args.varname) + 4);
sprintf(varsubst, "${%s}", args.varname);
pbx_substitute_variables_helper(chan, varsubst, varval, sizeof(varval) - 1);
if (ast_strlen_zero(varval2))
ast_str_substitute_variables(&str, 0, chan, varsubst);
if (ast_str_strlen(str) == 0) {
fieldcount = 0;
else {
while (strsep(&varval2, delim))
} else {
char *varval = ast_str_buffer(str);
while (strsep(&varval, delim)) {
fieldcount++;
}
}
} else {
fieldcount = 1;
}
if (sbuf) {
ast_str_set(sbuf, len, "%d", fieldcount);
} else {
snprintf(buf, len, "%d", fieldcount);
}
ast_free(str);
return 0;
}
static int function_fieldqty(struct ast_channel *chan, const char *cmd,
char *parse, char *buf, size_t len)
{
return function_fieldqty_helper(chan, cmd, parse, buf, NULL, len);
}
static int function_fieldqty_str(struct ast_channel *chan, const char *cmd,
char *parse, struct ast_str **buf, ssize_t len)
{
return function_fieldqty_helper(chan, cmd, parse, NULL, buf, len);
}
static struct ast_custom_function fieldqty_function = {
.name = "FIELDQTY",
.read = function_fieldqty,
.read2 = function_fieldqty_str,
};
static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, struct ast_str **bufstr, ssize_t len)
{
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(listname);
@@ -392,11 +417,18 @@ static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, ch
const char *orig_list, *ptr;
const char *begin, *cur, *next;
int dlen, flen, first = 1;
struct ast_str *result = ast_str_thread_get(&result_buf, 16);
struct ast_str *result, **result_ptr = &result;
char *delim;
AST_STANDARD_APP_ARGS(args, parse);
if (buf) {
result = ast_str_thread_get(&result_buf, 16);
} else {
/* Place the result directly into the output buffer */
result_ptr = bufstr;
}
if (args.argc < 3) {
ast_log(LOG_ERROR, "Usage: LISTFILTER(<listname>,<delimiter>,<fieldvalue>)\n");
return -1;
@@ -416,7 +448,11 @@ static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, ch
/* If the string isn't there, just copy out the string and be done with it. */
if (!(ptr = strstr(orig_list, args.fieldvalue))) {
if (buf) {
ast_copy_string(buf, orig_list, len);
} else {
ast_str_set(result_ptr, len, "%s", orig_list);
}
if (chan) {
ast_channel_unlock(chan);
}
@@ -436,7 +472,9 @@ static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, ch
ast_str_reset(result);
/* Enough space for any result */
ast_str_make_space(&result, strlen(orig_list) + 1);
if (len > -1) {
ast_str_make_space(result_ptr, len ? len : strlen(orig_list) + 1);
}
begin = orig_list;
next = strstr(begin, delim);
@@ -456,10 +494,10 @@ static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, ch
} else {
/* Copy field to output */
if (!first) {
ast_str_append(&result, 0, "%s", delim);
ast_str_append(result_ptr, len, "%s", delim);
}
ast_str_append_substr(&result, 0, begin, cur - begin + 1);
ast_str_append_substr(result_ptr, len, begin, cur - begin + 1);
first = 0;
begin = cur + dlen;
}
@@ -468,14 +506,27 @@ static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, ch
ast_channel_unlock(chan);
}
if (buf) {
ast_copy_string(buf, ast_str_buffer(result), len);
}
return 0;
}
static int listfilter_read(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
{
return listfilter(chan, cmd, parse, buf, NULL, len);
}
static int listfilter_read2(struct ast_channel *chan, const char *cmd, char *parse, struct ast_str **buf, ssize_t len)
{
return listfilter(chan, cmd, parse, NULL, buf, len);
}
static struct ast_custom_function listfilter_function = {
.name = "LISTFILTER",
.read = listfilter,
.read = listfilter_read,
.read2 = listfilter_read2,
};
static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
@@ -679,16 +730,15 @@ static int array(struct ast_channel *chan, const char *cmd, char *var,
static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
struct ast_var_t *newvar;
int plen;
char prefix[80];
snprintf(prefix, sizeof(prefix), HASH_PREFIX, data);
plen = strlen(prefix);
struct ast_str *prefix = ast_str_alloca(80);
ast_str_set(&prefix, -1, HASH_PREFIX, data);
memset(buf, 0, len);
AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
if (strncasecmp(prefix, ast_var_name(newvar), plen) == 0) {
if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
/* Copy everything after the prefix */
strncat(buf, ast_var_name(newvar) + plen, len - strlen(buf) - 1);
strncat(buf, ast_var_name(newvar) + ast_str_strlen(prefix), len - strlen(buf) - 1);
/* Trim the trailing ~ */
buf[strlen(buf) - 1] = ',';
}
@@ -698,6 +748,29 @@ static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data,
return 0;
}
static int hashkeys_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
{
struct ast_var_t *newvar;
struct ast_str *prefix = ast_str_alloca(80);
char *tmp;
ast_str_set(&prefix, -1, HASH_PREFIX, data);
AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
/* Copy everything after the prefix */
ast_str_append(buf, len, "%s", ast_var_name(newvar) + ast_str_strlen(prefix));
/* Trim the trailing ~ */
tmp = ast_str_buffer(*buf);
tmp[ast_str_strlen(*buf) - 1] = ',';
}
}
/* Trim the trailing comma */
tmp = ast_str_buffer(*buf);
tmp[ast_str_strlen(*buf) - 1] = '\0';
return 0;
}
static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
{
char varname[256];
@@ -773,6 +846,7 @@ static struct ast_custom_function hash_function = {
static struct ast_custom_function hashkeys_function = {
.name = "HASHKEYS",
.read = hashkeys_read,
.read2 = hashkeys_read2,
};
static struct ast_custom_function array_function = {
@@ -824,6 +898,7 @@ static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf,
static struct ast_custom_function len_function = {
.name = "LEN",
.read = len,
.read_max = 12,
};
static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
@@ -915,9 +990,23 @@ static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
return 0;
}
static int function_eval2(struct ast_channel *chan, const char *cmd, char *data,
struct ast_str **buf, ssize_t buflen)
{
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
return -1;
}
ast_str_substitute_variables(buf, buflen, chan, data);
return 0;
}
static struct ast_custom_function eval_function = {
.name = "EVAL",
.read = function_eval,
.read2 = function_eval2,
};
static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
@@ -969,9 +1058,24 @@ static int string_toupper(struct ast_channel *chan, const char *cmd, char *data,
return 0;
}
static int string_toupper2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
{
char *bufptr, *dataptr = data;
if (buflen > -1) {
ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
}
bufptr = ast_str_buffer(*buf);
while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = toupper(*dataptr++)));
ast_str_update(*buf);
return 0;
}
static struct ast_custom_function toupper_function = {
.name = "TOUPPER",
.read = string_toupper,
.read2 = string_toupper2,
};
static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
@@ -983,9 +1087,24 @@ static int string_tolower(struct ast_channel *chan, const char *cmd, char *data,
return 0;
}
static int string_tolower2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
{
char *bufptr, *dataptr = data;
if (buflen > -1) {
ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
}
bufptr = ast_str_buffer(*buf);
while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = tolower(*dataptr++)));
ast_str_update(*buf);
return 0;
}
static struct ast_custom_function tolower_function = {
.name = "TOLOWER",
.read = string_tolower,
.read2 = string_tolower2,
};
static int array_remove(struct ast_channel *chan, const char *cmd, char *var, char *buf, size_t len, int beginning)

View File

@@ -86,6 +86,7 @@ static struct ast_custom_function sysinfo_function = {
.synopsis = "Returns system information specified by parameter.",
.syntax = "SYSINFO(<parameter>)",
.read = sysinfo_helper,
.read_max = 22,
.desc =
"Returns information from a given parameter\n"
" Options:\n"

View File

@@ -191,6 +191,7 @@ static int timeout_write(struct ast_channel *chan, const char *cmd, char *data,
static struct ast_custom_function timeout_function = {
.name = "TIMEOUT",
.read = timeout_read,
.read_max = 22,
.write = timeout_write,
};

View File

@@ -97,6 +97,7 @@ static int acf_vmcount_exec(struct ast_channel *chan, const char *cmd, char *arg
struct ast_custom_function acf_vmcount = {
.name = "VMCOUNT",
.read = acf_vmcount_exec,
.read_max = 12,
};
static int unload_module(void)

View File

@@ -31,8 +31,24 @@
extern "C" {
#endif
/*!\brief Evaluate the given expression
* \param expr An expression
* \param buf Result buffer
* \param length Size of the result buffer, in bytes
* \param chan Channel to use for evaluating included dialplan functions, if any
* \return Length of the result string, in bytes
*/
int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan);
/*!\brief Evaluate the given expression
* \param str Dynamic result buffer
* \param maxlen <0 if the size of the buffer should remain constant, >0 if the size of the buffer should expand to that many bytes, maximum, or 0 for unlimited expansion of the result buffer
* \param chan Channel to use for evaluating included dialplan functions, if any
* \param expr An expression
* \return Length of the result string, in bytes
*/
int ast_str_expr(struct ast_str **str, ssize_t maxlen, struct ast_channel *chan, char *expr);
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif

View File

@@ -1196,6 +1196,9 @@
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if the C compiler supports function prototypes. */
#undef PROTOTYPES
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
#undef PTHREAD_CREATE_JOINABLE
@@ -1212,6 +1215,11 @@
/* Define to the type of arg 5 for `select'. */
#undef SELECT_TYPE_ARG5
/* Define to 1 if the `setvbuf' function takes the buffering type as its
second argument and the buffer pointer as the third, as on System V before
release 3. */
#undef SETVBUF_REVERSED
/* The size of `int', as computed by sizeof. */
#undef SIZEOF_INT
@@ -1232,30 +1240,20 @@
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
#undef TM_IN_SYS_TIME
/* Enable extensions on AIX 3, Interix. */
/* Define to 1 if on AIX 3.
System headers sometimes define this.
We just want to avoid a redefinition error message. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
#undef _LARGEFILE_SOURCE
@@ -1273,6 +1271,20 @@
/* Define to 1 if you need to in order for `stat' and other things to work. */
#undef _POSIX_SOURCE
/* Enable extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Define like PROTOTYPES; this can be used by system headers. */
#undef __PROTOTYPES
/* Define to empty if `const' does not conform to ANSI C. */
#undef const

View File

@@ -90,8 +90,20 @@ struct ast_custom_function {
AST_STRING_FIELD(seealso); /*!< See also */
);
enum ast_doc_src docsrc; /*!< Where the documentation come from */
int (*read)(struct ast_channel *, const char *, char *, char *, size_t); /*!< Read function, if read is supported */
int (*write)(struct ast_channel *, const char *, char *, const char *); /*!< Write function, if write is supported */
/*! Read function, if read is supported */
int (*read)(struct ast_channel *, const char *, char *, char *, size_t);
/*! Read function, if read is supported. Note: only one of read or read2
* needs to be implemented. In new code, read2 should be implemented as
* the way forward, but they should return identical results, within the
* constraints of buffer size, if both are implemented. That is, if the
* read function is handed a 16-byte buffer, and the result is 17 bytes
* long, then the first 15 bytes (remember NULL terminator) should be
* the same for both the read and the read2 methods. */
int (*read2)(struct ast_channel *, const char *, char *, struct ast_str **, ssize_t);
/*! If no read2 function is provided, what maximum size? */
size_t read_max;
/*! Write function, if write is supported */
int (*write)(struct ast_channel *, const char *, char *, const char *);
struct ast_module *mod; /*!< Module this custom function belongs to */
AST_RWLIST_ENTRY(ast_custom_function) acflist;
};
@@ -422,6 +434,24 @@ int ast_extension_state_del(int id, ast_state_cb_type callback);
int ast_get_hint(char *hint, int hintsize, char *name, int namesize,
struct ast_channel *c, const char *context, const char *exten);
/*!
* \brief If an extension hint exists, return non-zero
*
* \param hint buffer for hint
* \param hintsize Maximum size of hint buffer (<0 to prevent growth, >0 to limit growth to that number of bytes, or 0 for unlimited growth)
* \param name buffer for name portion of hint
* \param namesize Maximum size of name buffer (<0 to prevent growth, >0 to limit growth to that number of bytes, or 0 for unlimited growth)
* \param c Channel from which to return the hint. This is only important when the hint or name contains an expression to be expanded.
* \param context which context to look in
* \param exten which extension to search for
*
* \return If an extension within the given context with the priority PRIORITY_HINT
* is found, a non zero value will be returned.
* Otherwise, 0 is returned.
*/
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);
/*!
* \brief Determine whether an extension exists
*
@@ -960,7 +990,45 @@ int pbx_builtin_raise_exception(struct ast_channel *chan, void *data);
void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count);
void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count);
void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int cp2_size, size_t *used);
void ast_str_substitute_variables(struct ast_str **buf, size_t maxlen, struct ast_channel *chan, const char *templ);
/*! @} */
/*! @} */
/*! @name Substitution routines, using dynamic string buffers */
/*!
* \param buf Result will be placed in this buffer.
* \param maxlen -1 if the buffer should not grow, 0 if the buffer may grow to any size, and >0 if the buffer should grow only to that number of bytes.
* \param chan Channel variables from which to extract values, and channel to pass to any dialplan functions.
* \param headp If no channel is specified, a channel list from which to extract variable values
* \param var Variable name to retrieve.
*/
const char *ast_str_retrieve_variable(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, struct varshead *headp, const char *var);
/*!
* \param buf Result will be placed in this buffer.
* \param maxlen -1 if the buffer should not grow, 0 if the buffer may grow to any size, and >0 if the buffer should grow only to that number of bytes.
* \param chan Channel variables from which to extract values, and channel to pass to any dialplan functions.
* \param templ Variable template to expand.
*/
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ);
/*!
* \param buf Result will be placed in this buffer.
* \param maxlen -1 if the buffer should not grow, 0 if the buffer may grow to any size, and >0 if the buffer should grow only to that number of bytes.
* \param headp If no channel is specified, a channel list from which to extract variable values
* \param templ Variable template to expand.
*/
void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ);
/*!
* \param buf Result will be placed in this buffer.
* \param maxlen -1 if the buffer should not grow, 0 if the buffer may grow to any size, and >0 if the buffer should grow only to that number of bytes.
* \param c Channel variables from which to extract values, and channel to pass to any dialplan functions.
* \param headp If no channel is specified, a channel list from which to extract variable values
* \param templ Variable template to expand.
* \param used Number of bytes read from the template.
*/
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);
/*! @} */
int ast_extension_patmatch(const char *pattern, const char *data);
@@ -1049,6 +1117,21 @@ int ast_processed_calls(void);
*/
int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len);
/*!
* \brief executes a read operation on a function
*
* \param chan Channel to execute on
* \param function Data containing the function call string (will be modified)
* \param str A dynamic string buffer into which to place the result.
* \param maxlen <0 if the dynamic buffer should not grow; >0 if the dynamic buffer should be limited to that number of bytes; 0 if the dynamic buffer has no upper limit
*
* This application executes a function in read mode on a given channel.
*
* \retval 0 success
* \retval non-zero failure
*/
int ast_func_read2(struct ast_channel *chan, const char *function, struct ast_str **str, ssize_t maxlen);
/*!
* \brief executes a write operation on a function
*

View File

@@ -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;

View File

@@ -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
@@ -2844,9 +2892,21 @@ static char *substring(const char *value, int offset, int length, char *workspac
- \ref AstCauses The HANGUPCAUSE variable
*/
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,19 +3006,26 @@ 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);
if (acfptr == NULL)
ast_log(LOG_ERROR, "Function %s not registered\n", copy);
else if (!acfptr->read)
ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
else {
int res;
struct ast_module_user *u = NULL;
if (acfptr->mod)
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->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);

View File

@@ -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;

View File

@@ -1828,7 +1828,6 @@ static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char
static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv)
{
char tmp[4096];
struct ast_channel *chan2 = NULL;
if (argc != 4 && argc != 5) {
@@ -1842,8 +1841,14 @@ static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc,
}
if (chan2) {
pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1);
ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", tmp);
struct ast_str *str = ast_str_create(16);
if (!str) {
ast_agi_send(agi->fd, chan, "200 result=0\n");
return RESULT_SUCCESS;
}
ast_str_substitute_variables(&str, 0, chan2, argv[3]);
ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", ast_str_buffer(str));
ast_free(str);
} else {
ast_agi_send(agi->fd, chan, "200 result=0\n");
}

View File

@@ -43,6 +43,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/utils.h"
#include "asterisk/threadstorage.h"
AST_THREADSTORAGE(query_buf);
AST_THREADSTORAGE(result_buf);
/*!
* \brief Execute a curl query and return ast_variable list
@@ -55,25 +59,24 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
*/
static struct ast_variable *realtime_curl(const char *url, const char *unused, va_list ap)
{
struct ast_str *query;
char buf1[200], buf2[200];
struct ast_str *query, *buffer;
char buf1[256], buf2[256];
const char *newparam, *newval;
char *stringp, *pair, *key;
int i;
struct ast_variable *var = NULL, *prev = NULL;
const int EncodeSpecialChars = 1, bufsize = 64000;
char *buffer;
const int EncodeSpecialChars = 1;
if (!ast_custom_function_find("CURL")) {
ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
return NULL;
}
if (!(query = ast_str_create(1000)))
if (!(query = ast_str_thread_get(&query_buf, 16))) {
return NULL;
}
if (!(buffer = ast_malloc(bufsize))) {
ast_free(query);
if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
return NULL;
}
@@ -88,31 +91,33 @@ static struct ast_variable *realtime_curl(const char *url, const char *unused, v
va_end(ap);
ast_str_append(&query, 0, ")}");
pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, bufsize);
ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
/* Remove any trailing newline characters */
if ((stringp = strchr(buffer, '\r')) || (stringp = strchr(buffer, '\n')))
if ((stringp = strchr(ast_str_buffer(buffer), '\r')) || (stringp = strchr(ast_str_buffer(buffer), '\n'))) {
*stringp = '\0';
}
stringp = buffer;
stringp = ast_str_buffer(buffer);
while ((pair = strsep(&stringp, "&"))) {
key = strsep(&pair, "=");
ast_uri_decode(key);
if (pair)
if (pair) {
ast_uri_decode(pair);
}
if (!ast_strlen_zero(key)) {
if (prev) {
prev->next = ast_variable_new(key, S_OR(pair, ""), "");
if (prev->next)
if (prev->next) {
prev = prev->next;
} else
}
} else {
prev = var = ast_variable_new(key, S_OR(pair, ""), "");
}
}
}
ast_free(buffer);
ast_free(query);
return var;
}
@@ -127,27 +132,26 @@ static struct ast_variable *realtime_curl(const char *url, const char *unused, v
*/
static struct ast_config *realtime_multi_curl(const char *url, const char *unused, va_list ap)
{
struct ast_str *query;
char buf1[200], buf2[200];
struct ast_str *query, *buffer;
char buf1[256], buf2[256];
const char *newparam, *newval;
char *stringp, *line, *pair, *key, *initfield = NULL;
int i;
const int EncodeSpecialChars = 1, bufsize = 256000;
const int EncodeSpecialChars = 1;
struct ast_variable *var = NULL;
struct ast_config *cfg = NULL;
struct ast_category *cat = NULL;
char *buffer;
if (!ast_custom_function_find("CURL")) {
ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
return NULL;
}
if (!(query = ast_str_create(1000)))
if (!(query = ast_str_thread_get(&query_buf, 16))) {
return NULL;
}
if (!(buffer = ast_malloc(bufsize))) {
ast_free(query);
if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
return NULL;
}
@@ -170,28 +174,33 @@ static struct ast_config *realtime_multi_curl(const char *url, const char *unuse
ast_str_append(&query, 0, ")}");
/* Do the CURL query */
pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, bufsize);
ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
if (!(cfg = ast_config_new()))
goto exit_multi;
if (!(cfg = ast_config_new())) {
return NULL;
}
/* Line oriented output */
stringp = buffer;
stringp = ast_str_buffer(buffer);
while ((line = strsep(&stringp, "\r\n"))) {
if (ast_strlen_zero(line))
if (ast_strlen_zero(line)) {
continue;
}
if (!(cat = ast_category_new("", "", 99999)))
if (!(cat = ast_category_new("", "", 99999))) {
continue;
}
while ((pair = strsep(&line, "&"))) {
key = strsep(&pair, "=");
ast_uri_decode(key);
if (pair)
if (pair) {
ast_uri_decode(pair);
}
if (!strcasecmp(key, initfield) && pair)
if (!strcasecmp(key, initfield) && pair) {
ast_category_rename(cat, pair);
}
if (!ast_strlen_zero(key)) {
var = ast_variable_new(key, S_OR(pair, ""), "");
@@ -201,9 +210,6 @@ static struct ast_config *realtime_multi_curl(const char *url, const char *unuse
ast_category_append(cfg, cat);
}
exit_multi:
ast_free(buffer);
ast_free(query);
return cfg;
}
@@ -224,24 +230,23 @@ exit_multi:
*/
static int update_curl(const char *url, const char *unused, const char *keyfield, const char *lookup, va_list ap)
{
struct ast_str *query;
char buf1[200], buf2[200];
struct ast_str *query, *buffer;
char buf1[256], buf2[256];
const char *newparam, *newval;
char *stringp;
int i, rowcount = -1;
const int EncodeSpecialChars = 1, bufsize = 100;
char *buffer;
const int EncodeSpecialChars = 1;
if (!ast_custom_function_find("CURL")) {
ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
return -1;
}
if (!(query = ast_str_create(1000)))
if (!(query = ast_str_thread_get(&query_buf, 16))) {
return -1;
}
if (!(buffer = ast_malloc(bufsize))) {
ast_free(query);
if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
return -1;
}
@@ -258,43 +263,40 @@ static int update_curl(const char *url, const char *unused, const char *keyfield
va_end(ap);
ast_str_append(&query, 0, ")}");
pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, bufsize);
ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
/* Line oriented output */
stringp = buffer;
while (*stringp <= ' ')
stringp = ast_str_buffer(buffer);
while (*stringp <= ' ') {
stringp++;
}
sscanf(stringp, "%d", &rowcount);
ast_free(buffer);
ast_free(query);
if (rowcount >= 0)
if (rowcount >= 0) {
return (int)rowcount;
}
return -1;
}
static int update2_curl(const char *url, const char *unused, va_list ap)
{
struct ast_str *query;
struct ast_str *query, *buffer;
char buf1[200], buf2[200];
const char *newparam, *newval;
char *stringp;
int rowcount = -1, lookup = 1, first = 1;
const int EncodeSpecialChars = 1, bufsize = 100;
char *buffer;
const int EncodeSpecialChars = 1;
if (!ast_custom_function_find("CURL")) {
ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
return -1;
}
if (!(query = ast_str_create(1000)))
if (!(query = ast_str_thread_get(&query_buf, 1000)))
return -1;
if (!(buffer = ast_malloc(bufsize))) {
ast_free(query);
if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
return -1;
}
@@ -316,24 +318,27 @@ static int update2_curl(const char *url, const char *unused, va_list ap)
ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
ast_str_append(&query, 0, "%s%s=%s", first ? "" : "&", buf1, buf2);
first = 0;
}
va_end(ap);
ast_str_append(&query, 0, ")}");
/* TODO: Make proxies work */
pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, bufsize);
/* Proxies work, by setting CURLOPT options in the [globals] section of
* extensions.conf. Unfortunately, this means preloading pbx_config.so
* so that they have an opportunity to be set prior to startup realtime
* queries. */
ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
/* Line oriented output */
stringp = buffer;
while (*stringp <= ' ')
stringp = ast_str_buffer(buffer);
while (*stringp <= ' ') {
stringp++;
}
sscanf(stringp, "%d", &rowcount);
ast_free(buffer);
ast_free(query);
if (rowcount >= 0)
if (rowcount >= 0) {
return (int)rowcount;
}
return -1;
}
@@ -353,24 +358,23 @@ static int update2_curl(const char *url, const char *unused, va_list ap)
*/
static int store_curl(const char *url, const char *unused, va_list ap)
{
struct ast_str *query;
char buf1[200], buf2[200];
struct ast_str *query, *buffer;
char buf1[256], buf2[256];
const char *newparam, *newval;
char *stringp;
int i, rowcount = -1;
const int EncodeSpecialChars = 1, bufsize = 100;
char *buffer;
const int EncodeSpecialChars = 1;
if (!ast_custom_function_find("CURL")) {
ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
return -1;
}
if (!(query = ast_str_create(1000)))
if (!(query = ast_str_thread_get(&query_buf, 1000))) {
return -1;
}
if (!(buffer = ast_malloc(bufsize))) {
ast_free(query);
if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
return -1;
}
@@ -385,18 +389,17 @@ static int store_curl(const char *url, const char *unused, va_list ap)
va_end(ap);
ast_str_append(&query, 0, ")}");
pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, bufsize);
ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
stringp = buffer;
while (*stringp <= ' ')
stringp = ast_str_buffer(buffer);
while (*stringp <= ' ') {
stringp++;
}
sscanf(stringp, "%d", &rowcount);
ast_free(buffer);
ast_free(query);
if (rowcount >= 0)
return (int)rowcount;
if (rowcount >= 0) {
return rowcount;
}
return -1;
}
@@ -418,24 +421,23 @@ static int store_curl(const char *url, const char *unused, va_list ap)
*/
static int destroy_curl(const char *url, const char *unused, const char *keyfield, const char *lookup, va_list ap)
{
struct ast_str *query;
struct ast_str *query, *buffer;
char buf1[200], buf2[200];
const char *newparam, *newval;
char *stringp;
int i, rowcount = -1;
const int EncodeSpecialChars = 1, bufsize = 100;
char *buffer;
const int EncodeSpecialChars = 1;
if (!ast_custom_function_find("CURL")) {
ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
return -1;
}
if (!(query = ast_str_create(1000)))
if (!(query = ast_str_thread_get(&query_buf, 1000))) {
return -1;
}
if (!(buffer = ast_malloc(bufsize))) {
ast_free(query);
if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
return -1;
}
@@ -452,27 +454,26 @@ static int destroy_curl(const char *url, const char *unused, const char *keyfiel
va_end(ap);
ast_str_append(&query, 0, ")}");
pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, bufsize);
ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
/* Line oriented output */
stringp = buffer;
while (*stringp <= ' ')
stringp = ast_str_buffer(buffer);
while (*stringp <= ' ') {
stringp++;
}
sscanf(stringp, "%d", &rowcount);
ast_free(buffer);
ast_free(query);
if (rowcount >= 0)
if (rowcount >= 0) {
return (int)rowcount;
}
return -1;
}
static int require_curl(const char *url, const char *unused, va_list ap)
{
struct ast_str *query;
char *elm, field[256], buffer[128];
struct ast_str *query, *buffer;
char *elm, field[256];
int type, size;
const int EncodeSpecialChars = 1;
@@ -481,7 +482,11 @@ static int require_curl(const char *url, const char *unused, va_list ap)
return -1;
}
if (!(query = ast_str_create(100))) {
if (!(query = ast_str_thread_get(&query_buf, 100))) {
return -1;
}
if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
return -1;
}
@@ -511,19 +516,19 @@ static int require_curl(const char *url, const char *unused, va_list ap)
va_end(ap);
ast_str_append(&query, 0, ")}");
pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, sizeof(buffer));
return atoi(buffer);
ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
return atoi(ast_str_buffer(buffer));
}
static struct ast_config *config_curl(const char *url, const char *unused, const char *file, struct ast_config *cfg, struct ast_flags flags, const char *sugg_incl, const char *who_asked)
{
struct ast_str *query;
struct ast_str *query, *buffer;
char buf1[200];
char *stringp, *line, *pair, *key;
const int EncodeSpecialChars = 1, bufsize = 256000;
const int EncodeSpecialChars = 1;
int last_cat_metric = -1, cat_metric = -1;
struct ast_category *cat = NULL;
char *buffer, *cur_cat = "";
char *cur_cat = "";
char *category = "", *var_name = "", *var_val = "";
struct ast_flags loader_flags = { 0 };
@@ -532,11 +537,11 @@ static struct ast_config *config_curl(const char *url, const char *unused, const
return NULL;
}
if (!(query = ast_str_create(1000)))
if (!(query = ast_str_thread_get(&query_buf, 100))) {
return NULL;
}
if (!(buffer = ast_malloc(bufsize))) {
ast_free(query);
if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
return NULL;
}
@@ -544,31 +549,34 @@ static struct ast_config *config_curl(const char *url, const char *unused, const
ast_str_set(&query, 0, "${CURL(%s/static?file=%s)}", url, buf1);
/* Do the CURL query */
pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, bufsize);
ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
/* Line oriented output */
stringp = buffer;
stringp = ast_str_buffer(buffer);
cat = ast_config_get_current_category(cfg);
while ((line = strsep(&stringp, "\r\n"))) {
if (ast_strlen_zero(line))
if (ast_strlen_zero(line)) {
continue;
}
while ((pair = strsep(&line, "&"))) {
key = strsep(&pair, "=");
ast_uri_decode(key);
if (pair)
if (pair) {
ast_uri_decode(pair);
}
if (!strcasecmp(key, "category"))
if (!strcasecmp(key, "category")) {
category = S_OR(pair, "");
else if (!strcasecmp(key, "var_name"))
} else if (!strcasecmp(key, "var_name")) {
var_name = S_OR(pair, "");
else if (!strcasecmp(key, "var_val"))
} else if (!strcasecmp(key, "var_val")) {
var_val = S_OR(pair, "");
else if (!strcasecmp(key, "cat_metric"))
} else if (!strcasecmp(key, "cat_metric")) {
cat_metric = pair ? atoi(pair) : 0;
}
}
if (!strcmp(var_name, "#include")) {
if (!ast_config_internal_load(var_val, cfg, loader_flags, "", who_asked))
@@ -585,8 +593,6 @@ static struct ast_config *config_curl(const char *url, const char *unused, const
ast_variable_append(cat, ast_variable_new(var_name, var_val, ""));
}
ast_free(buffer);
ast_free(query);
return cfg;
}

View File

@@ -429,8 +429,7 @@ static int phoneprov_callback(struct ast_tcptls_session_instance *ser, const str
route = unref_route(route);
return 0;
} else { /* Dynamic file */
int bufsize;
char *tmp;
struct ast_str *tmp;
len = load_file(path, &file);
if (len < 0) {
@@ -446,12 +445,7 @@ static int phoneprov_callback(struct ast_tcptls_session_instance *ser, const str
goto out500;
}
/* XXX This is a hack -- maybe sum length of all variables in route->user->headp and add that? */
bufsize = len + VAR_BUF_SIZE;
/* malloc() instead of alloca() here, just in case the file is bigger than
* we have enough stack space for. */
if (!(tmp = ast_calloc(1, bufsize))) {
if (!(tmp = ast_str_create(len))) {
if (file) {
ast_free(file);
}
@@ -480,7 +474,7 @@ static int phoneprov_callback(struct ast_tcptls_session_instance *ser, const str
}
}
pbx_substitute_variables_varshead(AST_LIST_FIRST(&route->user->extensions)->headp, file, tmp, bufsize);
ast_str_substitute_variables_varshead(&tmp, 0, AST_LIST_FIRST(&route->user->extensions)->headp, file);
if (file) {
ast_free(file);
@@ -496,7 +490,7 @@ static int phoneprov_callback(struct ast_tcptls_session_instance *ser, const str
}
goto out500;
}
ast_str_append(&result, 0, "%s", tmp);
ast_str_append(&result, 0, "%s", ast_str_buffer(tmp));
ast_http_send(ser, method, 200, NULL, http_header, result, 0, 0);
if (tmp) {
@@ -830,17 +824,24 @@ static struct user *build_user(const char *mac, struct phone_profile *profile)
static int add_user_extension(struct user *user, struct extension *exten)
{
struct ast_var_t *var;
struct ast_str *str = ast_str_create(16);
if (!str) {
return -1;
}
/* Append profile variables here, and substitute variables on profile
* setvars, so that we can use user specific variables in them */
AST_LIST_TRAVERSE(user->profile->headp, var, entries) {
char expand_buf[VAR_BUF_SIZE] = {0,};
struct ast_var_t *var2;
pbx_substitute_variables_varshead(exten->headp, var->value, expand_buf, sizeof(expand_buf));
if ((var2 = ast_var_assign(var->name, expand_buf)))
ast_str_substitute_variables_varshead(&str, 0, exten->headp, var->value);
if ((var2 = ast_var_assign(var->name, ast_str_buffer(str)))) {
AST_LIST_INSERT_TAIL(exten->headp, var2, entries);
}
}
ast_free(str);
if (AST_LIST_EMPTY(&user->extensions)) {
AST_LIST_INSERT_HEAD(&user->extensions, exten, entry);
@@ -867,14 +868,18 @@ static int add_user_extension(struct user *user, struct extension *exten)
static int build_user_routes(struct user *user)
{
struct phoneprov_file *pp_file;
struct ast_str *str;
AST_LIST_TRAVERSE(&user->profile->dynamic_files, pp_file, entry) {
char expand_buf[VAR_BUF_SIZE] = { 0, };
pbx_substitute_variables_varshead(AST_LIST_FIRST(&user->extensions)->headp, pp_file->format, expand_buf, sizeof(expand_buf));
build_route(pp_file, user, expand_buf);
if (!(str = ast_str_create(16))) {
return -1;
}
AST_LIST_TRAVERSE(&user->profile->dynamic_files, pp_file, entry) {
ast_str_substitute_variables_varshead(&str, 0, AST_LIST_FIRST(&user->extensions)->headp, pp_file->format);
build_route(pp_file, user, ast_str_buffer(str));
}
ast_free(str);
return 0;
}
@@ -1053,17 +1058,22 @@ static void delete_profiles(void)
}
/*! \brief A dialplan function that can be used to print a string for each phoneprov user */
static int pp_each_user_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int pp_each_user_helper(struct ast_channel *chan, char *data, char *buf, struct ast_str **bufstr, int len)
{
char *tmp, expand_buf[VAR_BUF_SIZE] = {0,};
char *tmp;
struct ao2_iterator i;
struct user *user;
struct ast_str *str;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(string);
AST_APP_ARG(exclude_mac);
);
AST_STANDARD_APP_ARGS(args, data);
if (!(str = ast_str_create(16))) {
return -1;
}
/* Fix data by turning %{ into ${ */
while ((tmp = strstr(args.string, "%{")))
*tmp = '$';
@@ -1073,14 +1083,30 @@ static int pp_each_user_exec(struct ast_channel *chan, const char *cmd, char *da
if (!ast_strlen_zero(args.exclude_mac) && !strcasecmp(user->macaddress, args.exclude_mac)) {
continue;
}
pbx_substitute_variables_varshead(AST_LIST_FIRST(&user->extensions)->headp, args.string, expand_buf, sizeof(expand_buf));
ast_build_string(&buf, &len, "%s", expand_buf);
ast_str_substitute_variables_varshead(&str, len, AST_LIST_FIRST(&user->extensions)->headp, args.string);
if (buf) {
size_t slen = len;
ast_build_string(&buf, &slen, "%s", ast_str_buffer(str));
} else {
ast_str_append(bufstr, len, "%s", ast_str_buffer(str));
}
user = unref_user(user);
}
ast_free(str);
return 0;
}
static int pp_each_user_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
return pp_each_user_helper(chan, data, buf, NULL, len);
}
static int pp_each_user_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, int len)
{
return pp_each_user_helper(chan, data, NULL, buf, len);
}
static struct ast_custom_function pp_each_user_function = {
.name = "PP_EACH_USER",
.synopsis = "Generate a string for each phoneprov user",
@@ -1091,17 +1117,19 @@ static struct ast_custom_function pp_each_user_function = {
"excluding ones with MAC address <exclude_mac>. Probably not useful outside of\n"
"res_phoneprov.\n"
"\nExample: ${PP_EACH_USER(<item><fn>%{DISPLAY_NAME}</fn></item>|${MAC})",
.read = pp_each_user_exec,
.read = pp_each_user_read,
.read2 = pp_each_user_read2,
};
/*! \brief A dialplan function that can be used to output a template for each extension attached to a user */
static int pp_each_extension_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int pp_each_extension_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, int len)
{
struct user *user;
struct extension *exten;
char path[PATH_MAX];
char *file;
int filelen;
struct ast_str *str;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(mac);
AST_APP_ARG(template);
@@ -1133,19 +1161,38 @@ static int pp_each_extension_exec(struct ast_channel *chan, const char *cmd, cha
return 0;
}
if (!(str = ast_str_create(filelen))) {
return 0;
}
AST_LIST_TRAVERSE(&user->extensions, exten, entry) {
char expand_buf[VAR_BUF_SIZE] = {0,};
pbx_substitute_variables_varshead(exten->headp, file, expand_buf, sizeof(expand_buf));
ast_build_string(&buf, &len, "%s", expand_buf);
ast_str_substitute_variables_varshead(&str, 0, exten->headp, file);
if (buf) {
size_t slen = len;
ast_build_string(&buf, &slen, "%s", ast_str_buffer(str));
} else {
ast_str_append(bufstr, len, "%s", ast_str_buffer(str));
}
}
ast_free(file);
ast_free(str);
user = unref_user(user);
return 0;
}
static int pp_each_extension_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
return pp_each_extension_helper(chan, cmd, data, buf, NULL, len);
}
static int pp_each_extension_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, int len)
{
return pp_each_extension_helper(chan, cmd, data, NULL, buf, len);
}
static struct ast_custom_function pp_each_extension_function = {
.name = "PP_EACH_EXTENSION",
.synopsis = "Execute specified template for each extension",
@@ -1153,7 +1200,8 @@ static struct ast_custom_function pp_each_extension_function = {
.desc =
"Output the specified template for each extension associated with the specified\n"
"MAC address.",
.read = pp_each_extension_exec,
.read = pp_each_extension_read,
.read2 = pp_each_extension_read2,
};
/*! \brief CLI command to list static and dynamic routes */

241
tests/test_substitution.c Normal file
View File

@@ -0,0 +1,241 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2009, Digium, Inc.
*
* Tilghman Lesher <tlesher AT digium DOT com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Substitution Test
*
* \author\verbatim Tilghman Lesher <tlesher AT digium DOT com> \endverbatim
*
* \ingroup tests
*/
/*** MODULEINFO
<defaultenabled>no</defaultenabled>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
#include "asterisk/strings.h"
#include "asterisk/stringfields.h"
#include "asterisk/threadstorage.h"
#include "asterisk/cli.h"
AST_THREADSTORAGE(buf_buf);
AST_THREADSTORAGE(var_buf);
static void test_chan_integer(int fd, struct ast_channel *c, int *ifield, const char *expression)
{
int i, okay = 1, value1 = -1, value2 = -1;
char workspace[4096];
struct ast_str *str = ast_str_thread_get(&buf_buf, 16);
for (i = 0; i < 256; i++) {
*ifield = i;
ast_str_substitute_variables(&str, 0, c, expression);
pbx_substitute_variables_helper(c, expression, workspace, sizeof(workspace));
if (sscanf(workspace, "%d", &value1) != 1 || value1 != i || sscanf(ast_str_buffer(str), "%d", &value2) != 1 || value2 != i) {
ast_cli(fd, "%s != %s and/or %d != %d != %d\n", ast_str_buffer(str), workspace, value1, value2, i);
okay = 0;
break;
}
}
ast_cli(fd, "Testing '%s' . . . . . %s\n", expression, okay ? "passed" : "FAILED");
}
static void test_chan_string(int fd, struct ast_channel *c, char *cfield, size_t cfieldsize, const char *expression)
{
const char *values[] = { "one", "three", "reallylongdinosaursoundingthingwithwordsinit" };
int i, okay = 1;
char workspace[4096];
struct ast_str *str = ast_str_thread_get(&buf_buf, 16);
for (i = 0; i < ARRAY_LEN(values); i++) {
ast_copy_string(cfield, values[i], cfieldsize);
ast_str_substitute_variables(&str, 0, c, expression);
pbx_substitute_variables_helper(c, expression, workspace, sizeof(workspace));
if (strcmp(cfield, ast_str_buffer(str)) != 0 || strcmp(cfield, workspace) != 0) {
ast_cli(fd, "%s != %s != %s\n", cfield, ast_str_buffer(str), workspace);
okay = 0;
break;
}
}
ast_cli(fd, "Testing '%s' . . . . . %s\n", expression, okay ? "passed" : "FAILED");
}
static void test_chan_variable(int fd, struct ast_channel *c, const char *varname)
{
const char *values[] = { "one", "three", "reallylongdinosaursoundingthingwithwordsinit" };
int i, okay = 1;
char workspace[4096];
struct ast_str *str = ast_str_thread_get(&buf_buf, 16);
struct ast_str *var = ast_str_thread_get(&var_buf, 16);
ast_str_set(&var, 0, "${%s}", varname);
for (i = 0; i < ARRAY_LEN(values); i++) {
pbx_builtin_setvar_helper(c, varname, values[i]);
ast_str_substitute_variables(&str, 0, c, ast_str_buffer(var));
pbx_substitute_variables_helper(c, ast_str_buffer(var), workspace, sizeof(workspace));
if (strcmp(values[i], ast_str_buffer(str)) != 0 || strcmp(values[i], workspace) != 0) {
ast_cli(fd, "%s != %s != %s\n", values[i], ast_str_buffer(str), workspace);
okay = 0;
break;
}
}
ast_cli(fd, "Testing '%s' . . . . . %s\n", ast_str_buffer(var), okay ? "passed" : "FAILED");
}
static void test_chan_function(int fd, struct ast_channel *c, const char *expression)
{
int okay = 1;
char workspace[4096];
struct ast_str *str = ast_str_thread_get(&buf_buf, 16);
ast_str_substitute_variables(&str, 0, c, expression);
pbx_substitute_variables_helper(c, expression, workspace, sizeof(workspace));
if (strcmp(workspace, ast_str_buffer(str)) != 0) {
ast_cli(fd, "%s != %s\n", ast_str_buffer(str), workspace);
okay = 0;
}
ast_cli(fd, "Testing '%s' . . . . . %s\n", expression, okay ? "passed" : "FAILED");
}
static void test_2way_function(int fd, struct ast_channel *c, const char *encode1, const char *encode2, const char *decode1, const char *decode2)
{
struct ast_str *str = ast_str_thread_get(&buf_buf, 16), *expression = ast_str_alloca(120);
ast_str_set(&expression, 0, "%s%s%s", encode1, "foobarbaz", encode2);
ast_str_substitute_variables(&str, 0, c, ast_str_buffer(expression));
ast_str_set(&expression, 0, "%s%s%s", decode1, ast_str_buffer(str), decode2);
ast_str_substitute_variables(&str, 0, c, ast_str_buffer(expression));
ast_cli(fd, "Testing '%s%s' and '%s%s' . . . . . %s\n", encode1, encode2, decode1, decode2, !strcmp(ast_str_buffer(str), "foobarbaz") ? "passed" : "FAILED");
if (strcmp(ast_str_buffer(str), "foobarbaz")) {
ast_cli(fd, " '%s' != 'foobarbaz'\n", ast_str_buffer(str));
}
}
static void test_expected_result(int fd, struct ast_channel *c, const char *expression, const char *result)
{
struct ast_str *str = ast_str_thread_get(&buf_buf, 16);
ast_str_substitute_variables(&str, 0, c, expression);
ast_cli(fd, "Testing '%s' ('%s') == '%s' . . . . . %s\n", ast_str_buffer(str), expression, result, !strcmp(ast_str_buffer(str), result) ? "passed" : "FAILED");
}
static char *handle_cli_test_substitution(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ast_channel *c;
int i;
switch (cmd) {
case CLI_INIT:
e->command = "test substitution";
e->usage = ""
"Usage: test substitution\n"
" Test variable and function substitution.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != e->args) {
return CLI_SHOWUSAGE;
}
ast_cli(a->fd, "Testing variable substitution ...\n");
c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Test/substitution");
test_chan_integer(a->fd, c, &c->cid.cid_pres, "${CALLINGPRES}");
test_chan_integer(a->fd, c, &c->cid.cid_ani2, "${CALLINGANI2}");
test_chan_integer(a->fd, c, &c->cid.cid_ton, "${CALLINGTON}");
test_chan_integer(a->fd, c, &c->cid.cid_tns, "${CALLINGTNS}");
test_chan_integer(a->fd, c, &c->hangupcause, "${HANGUPCAUSE}");
test_chan_integer(a->fd, c, &c->priority, "${PRIORITY}");
test_chan_string(a->fd, c, c->context, sizeof(c->context), "${CONTEXT}");
test_chan_string(a->fd, c, c->exten, sizeof(c->exten), "${EXTEN}");
test_chan_variable(a->fd, c, "CHANNEL(language)");
test_chan_variable(a->fd, c, "CHANNEL(musicclass)");
test_chan_variable(a->fd, c, "CHANNEL(parkinglot)");
test_chan_variable(a->fd, c, "CALLERID(name)");
test_chan_variable(a->fd, c, "CURLOPT(proxyuserpwd)");
test_chan_variable(a->fd, c, "CDR(foo)");
test_chan_variable(a->fd, c, "ENV(foo)");
test_chan_variable(a->fd, c, "GLOBAL(foo)");
test_chan_variable(a->fd, c, "GROUP()");
test_2way_function(a->fd, c, "${AES_ENCRYPT(abcdefghijklmnop,", ")}", "${AES_DECRYPT(abcdefghijklmnop,", ")}");
test_2way_function(a->fd, c, "${BASE64_ENCODE(", ")}", "${BASE64_DECODE(", ")}");
pbx_builtin_setvar_helper(c, "foo", "123");
pbx_builtin_setvar_helper(c, "bar", "foo");
pbx_builtin_setvar_helper(c, "baz", "fo");
test_expected_result(a->fd, c, "${foo}${foo}", "123123");
test_expected_result(a->fd, c, "A${foo}A${foo}A", "A123A123A");
test_expected_result(a->fd, c, "A${${bar}}A", "A123A");
test_expected_result(a->fd, c, "A${${baz}o}A", "A123A");
test_expected_result(a->fd, c, "A${${baz}o:1}A", "A23A");
test_expected_result(a->fd, c, "A${${baz}o:1:1}A", "A2A");
test_expected_result(a->fd, c, "A${${baz}o:1:-1}A", "A2A");
test_expected_result(a->fd, c, "A${${baz}o:-1:1}A", "A3A");
test_expected_result(a->fd, c, "A${${baz}o:-2:1}A", "A2A");
test_expected_result(a->fd, c, "A${${baz}o:-2:-1}A", "A2A");
/* For testing dialplan functions */
for (i = 0; ; i++) {
char *cmd = ast_cli_generator("core show function", "", i);
if (cmd == NULL) {
break;
}
if (strcmp(cmd, "CHANNEL") && strcmp(cmd, "CALLERID") && strcmp(cmd, "CURLOPT") && strncmp(cmd, "AES", 3) && strncmp(cmd, "BASE64", 6) && strcmp(cmd, "CDR") && strcmp(cmd, "ENV") && strcmp(cmd, "GLOBAL") && strcmp(cmd, "GROUP") && strcmp(cmd, "CUT") && strcmp(cmd, "LISTFILTER") && strcmp(cmd, "PP_EACH_EXTENSION") && strcmp(cmd, "SET")) {
struct ast_custom_function *acf = ast_custom_function_find(cmd);
if (acf->read && acf->read2) {
char expression[80];
snprintf(expression, sizeof(expression), "${%s(foo)}", cmd);
test_chan_function(a->fd, c, expression);
}
}
ast_free(cmd);
}
ast_hangup(c);
return CLI_SUCCESS;
}
static struct ast_cli_entry cli_substitution[] = {
AST_CLI_DEFINE(handle_cli_test_substitution, "Test variable substitution"),
};
static int unload_module(void)
{
ast_cli_unregister_multiple(cli_substitution, ARRAY_LEN(cli_substitution));
return 0;
}
static int load_module(void)
{
ast_cli_register_multiple(cli_substitution, ARRAY_LEN(cli_substitution));
return AST_MODULE_LOAD_SUCCESS;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Substitution tests");