mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2087 lines
51 KiB
2087 lines
51 KiB
/*
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2006 iptelorg GmbH
|
|
*
|
|
* This file is part of ser, a free SIP server.
|
|
*
|
|
* ser is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* For a license to use the ser software under conditions
|
|
* other than those described here, or to purchase support for this
|
|
* software, please contact iptel.org by e-mail at the following addresses:
|
|
* info@iptel.org
|
|
*
|
|
* ser is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include "../../route.h"
|
|
#include "../../sr_module.h"
|
|
#include "../../mem/mem.h"
|
|
#include "../../str.h"
|
|
#include "../../error.h"
|
|
#include "../../config.h"
|
|
#include "../../trim.h"
|
|
#include "../../select.h"
|
|
#include "../../ut.h"
|
|
#include "../../modules/xprint/xp_lib.h"
|
|
#include "../../select_buf.h"
|
|
|
|
#include "../../globals.h"
|
|
#include "../../route.h"
|
|
#include "../../parser/msg_parser.h"
|
|
#include "../../action.h"
|
|
#include "../../script_cb.h"
|
|
#include "../../dset.h"
|
|
#include "../../usr_avp.h"
|
|
|
|
MODULE_VERSION
|
|
|
|
#define MODULE_NAME "eval"
|
|
|
|
enum {evtVoid=0, evtInt, evtStr};
|
|
|
|
struct eval_str {
|
|
str s;
|
|
int cnt;
|
|
};
|
|
|
|
struct eval_value {
|
|
union {
|
|
long n;
|
|
struct eval_str *s;
|
|
} u;
|
|
int type;
|
|
};
|
|
|
|
struct register_item {
|
|
char *name;
|
|
struct eval_value value;
|
|
struct register_item *next;
|
|
};
|
|
|
|
struct stack_item {
|
|
struct eval_value value;
|
|
struct stack_item *prev;
|
|
struct stack_item *next;
|
|
};
|
|
|
|
static int stack_no = 0;
|
|
static struct stack_item *stack_head = 0;
|
|
static struct stack_item *stack_tail = 0;
|
|
|
|
static struct register_item* registers = 0;
|
|
|
|
|
|
#define destroy_value(val) { \
|
|
if ((val).type == evtStr && (val).u.s && (val).u.s->cnt > 0) { \
|
|
(val).u.s->cnt--; \
|
|
if ((val).u.s->cnt == 0) \
|
|
pkg_free((val).u.s); \
|
|
} \
|
|
(val).type = evtVoid; \
|
|
}
|
|
|
|
#define assign_value(dest, src) { \
|
|
if (&(dest) != &(src)) { \
|
|
destroy_value(dest); \
|
|
dest = src; \
|
|
if ((dest).type == evtStr && (dest).u.s && (dest).u.s->cnt > 0) \
|
|
(dest).u.s->cnt++; \
|
|
} \
|
|
}
|
|
|
|
static int get_as_int(struct eval_value *value, long* val) {
|
|
switch (value->type) {
|
|
case evtInt:
|
|
*val = value->u.n;
|
|
return 1;
|
|
case evtStr:
|
|
if (value->u.s->s.s && value->u.s->s.len && value->u.s->s.len <= 25) {
|
|
char *err;
|
|
char buf[25+1];
|
|
memcpy(buf, value->u.s->s.s, value->u.s->s.len);
|
|
buf[value->u.s->s.len] = '\0';
|
|
*val = strtol(buf, &err, 10);
|
|
if (*err == 0)
|
|
return 1;
|
|
}
|
|
ERR(MODULE_NAME": cannot convert '%.*s' as int\n", value->u.s->s.len, value->u.s->s.s);
|
|
return -1;
|
|
default:
|
|
BUG("Bad value type %d\n", value->type);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static void get_as_str(struct eval_value *value, str *s) {
|
|
static char buf[25];
|
|
switch (value->type) {
|
|
case evtInt:
|
|
s->len = snprintf(buf, sizeof(buf)-1, "%ld", value->u.n);
|
|
s->s = buf;
|
|
break;
|
|
case evtStr:
|
|
*s = value->u.s->s;
|
|
break;
|
|
default:
|
|
s->s = 0;
|
|
s->len = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int get_as_bool(struct eval_value *value) {
|
|
switch (value->type) {
|
|
case evtVoid:
|
|
return 0;
|
|
case evtInt:
|
|
return value->u.n != 0;
|
|
case evtStr:
|
|
return (value->u.s->s.s && value->u.s->s.len > 0);
|
|
default:
|
|
BUG("Bad value type %d\n", value->type);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static struct eval_str* eval_str_malloc(str* s) {
|
|
struct eval_str* p;
|
|
p = pkg_malloc(sizeof(*p)+s->len);
|
|
if (p) {
|
|
p->s.s = (char*)p+sizeof(*p);
|
|
if (s->len && s->s != 0)
|
|
memcpy(p->s.s, s->s, s->len);
|
|
if (s->s == 0 && s->len)
|
|
s->s = p->s.s;
|
|
p->s.len = s->len;
|
|
p->cnt = 1;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/* taken from modules/textops */
|
|
#define is_space(_p) ((_p) == '\t' || (_p) == '\n' || (_p) == '\r' || (_p) == ' ')
|
|
|
|
static void get_uri_and_skip_until_params(str *param_area, str *uri) {
|
|
int i, quoted, uri_pos, uri_done;
|
|
|
|
uri->len = 0;
|
|
uri->s = 0;
|
|
uri_done = 0;
|
|
for (i=0; i<param_area->len && param_area->s[i]!=';'; ) { /* [ *(token LSW)/quoted-string ] "<" addr-spec ">" | addr-spec */
|
|
/* skip name */
|
|
|
|
for (quoted=0, uri_pos=i; i<param_area->len; i++) {
|
|
if (!quoted) {
|
|
if (param_area->s[i] == '\"') {
|
|
quoted = 1;
|
|
uri_pos = -1;
|
|
}
|
|
else if (param_area->s[i] == '<' || param_area->s[i] == ';' || is_space(param_area->s[i])) break;
|
|
}
|
|
else if (param_area->s[i] == '\"' && param_area->s[i-1] != '\\') quoted = 0;
|
|
}
|
|
if (uri_pos >= 0 && !uri_done) {
|
|
uri->s = param_area->s+uri_pos;
|
|
uri->len = param_area->s+i-uri->s;
|
|
}
|
|
/* skip uri */
|
|
while (i<param_area->len && is_space(param_area->s[i])) i++;
|
|
if (i<param_area->len && param_area->s[i]=='<') {
|
|
uri->s = param_area->s+i;
|
|
uri->len = 0;
|
|
for (quoted=0; i<param_area->len; i++) {
|
|
if (!quoted) {
|
|
if (param_area->s[i] == '\"') quoted = 1;
|
|
else if (param_area->s[i] == '>') {
|
|
uri->len = param_area->s+i-uri->s+1;
|
|
uri_done = 1;
|
|
break;
|
|
}
|
|
}
|
|
else if (param_area->s[i] == '\"' && param_area->s[i-1] != '\\') quoted = 0;
|
|
}
|
|
}
|
|
}
|
|
param_area->s+= i;
|
|
param_area->len-= i;
|
|
}
|
|
|
|
static int find_next_value(char** start, char* end, str* val, str* lump_val) {
|
|
int quoted = 0;
|
|
lump_val->s = *start;
|
|
while (*start < end && is_space(**start) ) (*start)++;
|
|
val->s = *start;
|
|
while ( *start < end && (**start != ',' || quoted) ) {
|
|
if (**start == '\"' && (!quoted || (*start)[-1]!='\\') )
|
|
quoted = ~quoted;
|
|
(*start)++;
|
|
}
|
|
val->len = *start - val->s;
|
|
while (val->len > 0 && is_space(val->s[val->len-1])) val->len--;
|
|
/* we cannot automatically strip quotes!!! an example why: "name" <sip:ssss>;param="bar"
|
|
if (val->len >= 2 && val->s[0] == '\"' && val->s[val->len-1] == '\"') {
|
|
val->s++;
|
|
val->len -= 2;
|
|
}
|
|
*/
|
|
while (*start < end && **start != ',') (*start)++;
|
|
if (*start < end) {
|
|
(*start)++;
|
|
}
|
|
lump_val->len = *start - lump_val->s;
|
|
return (*start < end);
|
|
}
|
|
|
|
#define MAX_HF_VALUES 30
|
|
|
|
static int parse_hf_values(str s, int* n, str** vals) {
|
|
static str values[MAX_HF_VALUES];
|
|
char *start, *end;
|
|
str lump_val;
|
|
*n = 0;
|
|
*vals = values;
|
|
if (!s.s) return 1;
|
|
start = s.s;
|
|
end = start+s.len;
|
|
while (start < end) {
|
|
find_next_value(&start, end, &values[*n], &lump_val);
|
|
if (*n >= MAX_HF_VALUES) {
|
|
ERR(MODULE_NAME": too many values\n");
|
|
return -1;
|
|
}
|
|
(*n)++;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void destroy_stack() {
|
|
struct stack_item *p;
|
|
while (stack_head) {
|
|
destroy_value(stack_head->value);
|
|
p = stack_head;
|
|
stack_head = stack_head->next;
|
|
pkg_free(p);
|
|
}
|
|
stack_tail = stack_head;
|
|
stack_no = 0;
|
|
}
|
|
|
|
static void destroy_register_values() {
|
|
struct register_item *p;
|
|
for (p=registers; p; p=p->next) {
|
|
destroy_value(p->value);
|
|
}
|
|
}
|
|
|
|
static void remove_stack_item(struct stack_item *s) {
|
|
if (s->prev)
|
|
s->prev->next = s->next;
|
|
else
|
|
stack_head = s->next;
|
|
if (s->next)
|
|
s->next->prev = s->prev;
|
|
else
|
|
stack_tail = s->prev;
|
|
destroy_value(s->value);
|
|
pkg_free(s);
|
|
stack_no--;
|
|
}
|
|
|
|
static void insert_stack_item(struct stack_item *s, struct stack_item *pivot, int behind) {
|
|
if (stack_head == NULL) {
|
|
s->prev = s->next = 0;
|
|
}
|
|
else if (behind) {
|
|
if (pivot) {
|
|
s->next = pivot->next;
|
|
s->prev = pivot;
|
|
}
|
|
else {
|
|
s->next = 0;
|
|
s->prev = stack_tail; /* bottom (tail) */
|
|
}
|
|
}
|
|
else {
|
|
if (pivot) {
|
|
s->prev = pivot->prev;
|
|
s->next = pivot;
|
|
}
|
|
else {
|
|
s->next = stack_head; /* top (head) */
|
|
s->prev = 0;
|
|
}
|
|
}
|
|
if (!s->prev)
|
|
stack_head = s;
|
|
else
|
|
s->prev->next = s;
|
|
if (!s->next)
|
|
stack_tail = s;
|
|
else
|
|
s->next->prev = s;
|
|
stack_no++;
|
|
}
|
|
|
|
static int declare_register(modparam_t type, char* param) {
|
|
struct register_item **p;
|
|
char *c;
|
|
for (c=param; *c; c++) {
|
|
if ( (*c >= 'a' && *c <= 'z') ||
|
|
(*c >= 'A' && *c <= 'Z') ||
|
|
(*c >= '0' && *c <= '9') ||
|
|
(*c == '_') ) {
|
|
;
|
|
} else {
|
|
ERR(MODULE_NAME": illegal register name\n");
|
|
return E_CFG;
|
|
}
|
|
}
|
|
for (p = ®isters; *p!= 0; p = &(*p)->next);
|
|
*p = pkg_malloc(sizeof(**p));
|
|
if (!*p) return E_OUT_OF_MEM;
|
|
|
|
memset(*p, 0, sizeof(**p));
|
|
(*p)->name = param;
|
|
return 0;
|
|
}
|
|
|
|
static int mod_pre_script_cb(struct sip_msg *msg, unsigned int flags, void *param) {
|
|
destroy_stack();
|
|
destroy_register_values();
|
|
return 1;
|
|
}
|
|
|
|
static struct register_item* find_register(char* s, int len) {
|
|
struct register_item *p;
|
|
for (p=registers; p; p=p->next) {
|
|
if (strlen(p->name) == len && strncasecmp(p->name, s, len) == 0)
|
|
break;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static struct stack_item* find_stack_item(int n) {
|
|
struct stack_item *p;
|
|
if ((n >= 0 && n >= stack_no) || (n<0 && -n > stack_no)) {
|
|
return NULL;
|
|
}
|
|
p = NULL;
|
|
if (n >= 0) {
|
|
for (p = stack_head; p && n>0; p=p->next, n--);
|
|
}
|
|
else {
|
|
for (p = stack_tail, n=-n-1; p && n>0; p=p->prev, n--);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/* module exported functions */
|
|
static void print_eval_value(struct eval_value* v) {
|
|
switch (v->type) {
|
|
case evtStr:
|
|
if (v->u.s)
|
|
fprintf(stderr, "s:'%.*s', cnt:%d\n", v->u.s->s.len, v->u.s->s.s, v->u.s->cnt);
|
|
else
|
|
fprintf(stderr, "s:<null>\n");
|
|
break;
|
|
case evtInt:
|
|
fprintf(stderr, "i:%ld\n", v->u.n);
|
|
break;
|
|
default:;
|
|
fprintf(stderr, "type:%d\n", v->type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int eval_dump_func(struct sip_msg *msg, char *param1, char *param2) {
|
|
struct stack_item *si;
|
|
struct register_item *ri;
|
|
int i;
|
|
fprintf(stderr, "Stack (no=%d):\n", stack_no);
|
|
for (si=stack_head, i=0; si; si=si->next, i++) {
|
|
fprintf(stderr, "# %.2d ", i);
|
|
print_eval_value(&si->value);
|
|
}
|
|
for (si=stack_tail, i=-1; si; si=si->prev, i--) {
|
|
fprintf(stderr, "#%.2d ", i);
|
|
print_eval_value(&si->value);
|
|
}
|
|
fprintf(stderr, "Registers:\n");
|
|
for (ri=registers; ri; ri=ri->next) {
|
|
fprintf(stderr, "%s: ", ri->name);
|
|
print_eval_value(&ri->value);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int xlbuf_size = 4096;
|
|
static xl_print_log_f* xl_print = NULL;
|
|
static xl_parse_format_f* xl_parse = NULL;
|
|
#define NO_SCRIPT -1
|
|
|
|
|
|
enum {esotAdd, esotInsert, esotXchg, esotPut, esotGet, esotPop, esotAddValue, esotInsertValue};
|
|
enum {esovtInt, esovtStr, esovtAvp, esovtXStr, esovtRegister, esovtFunc, esovtSelect};
|
|
enum {esofNone=0, esofTime, esofUuid, esofStackNo};
|
|
|
|
struct eval_location_func {
|
|
int type;
|
|
char *name;
|
|
};
|
|
|
|
static struct eval_location_func loc_functions[] = {
|
|
{esofTime, "time"},
|
|
{esofUuid, "uuid"},
|
|
{esofStackNo, "stackno"},
|
|
|
|
{esofNone, NULL}
|
|
};
|
|
|
|
struct eval_location {
|
|
int value_type;
|
|
union {
|
|
int n;
|
|
struct eval_str s;
|
|
xl_elog_t* xl;
|
|
struct register_item *reg;
|
|
avp_ident_t avp;
|
|
select_t* select;
|
|
struct eval_location_func *func;
|
|
} u;
|
|
};
|
|
|
|
struct eval_stack_oper {
|
|
int oper_type;
|
|
struct eval_location loc;
|
|
};
|
|
|
|
static int parse_location(str s, struct eval_location *p) {
|
|
if (s.len >= 2 && s.s[1] == ':') {
|
|
switch (s.s[0]) {
|
|
case 'r':
|
|
p->u.reg = find_register(s.s+2, s.len-2);
|
|
if (!p->u.reg) {
|
|
ERR(MODULE_NAME": register '%.*s' not found\n", s.len-2, s.s+2);
|
|
return E_CFG;
|
|
}
|
|
p->value_type = esovtRegister;
|
|
break;
|
|
case 'x':
|
|
if (!xl_print) {
|
|
xl_print=(xl_print_log_f*)find_export("xprint", NO_SCRIPT, 0);
|
|
if (!xl_print) {
|
|
ERR(MODULE_NAME": cannot find \"xprint\", is module xprint loaded?\n");
|
|
return E_UNSPEC;
|
|
}
|
|
}
|
|
|
|
if (!xl_parse) {
|
|
xl_parse=(xl_parse_format_f*)find_export("xparse", NO_SCRIPT, 0);
|
|
|
|
if (!xl_parse) {
|
|
ERR(MODULE_NAME": cannot find \"xparse\", is module xprint loaded?\n");
|
|
return E_UNSPEC;
|
|
}
|
|
}
|
|
|
|
if(xl_parse(s.s+2, &p->u.xl) < 0) {
|
|
ERR(MODULE_NAME": wrong xl_lib format '%s'\n", s.s+2);
|
|
return E_UNSPEC;
|
|
}
|
|
p->value_type = esovtXStr;
|
|
break;
|
|
case 'f': {
|
|
struct eval_location_func* f;
|
|
s.s += 2;
|
|
s.len -= 2;
|
|
for (f=loc_functions; f->type != esofNone; f++) {
|
|
if (strlen(f->name)==s.len && strncasecmp(s.s, f->name, s.len) == 0) {
|
|
p->value_type = esovtFunc;
|
|
p->u.func = f;
|
|
break;
|
|
}
|
|
}
|
|
if (!f) {
|
|
ERR(MODULE_NAME": unknown function '%.*s'\n", s.len, s.s);
|
|
return E_CFG;
|
|
}
|
|
break;
|
|
}
|
|
case 's':
|
|
s.s += 2;
|
|
s.len -= 2;
|
|
/* no break */
|
|
default:
|
|
p->u.s.s = s;
|
|
p->u.s.cnt = 0;
|
|
p->value_type = esovtStr;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
char *err;
|
|
if (s.len > 1 && s.s[0]=='$') {
|
|
s.s++;
|
|
s.len--;
|
|
if (parse_avp_ident(&s, &p->u.avp) == 0) {
|
|
if (p->u.avp.flags & AVP_NAME_RE) {
|
|
ERR(MODULE_NAME": avp regex not allowed\n");
|
|
return E_CFG;
|
|
}
|
|
p->value_type = esovtAvp;
|
|
return 1;
|
|
}
|
|
s.s--;
|
|
s.len++;
|
|
}
|
|
else if (s.len > 1 && s.s[0]=='@') {
|
|
if (parse_select(&s.s, &p->u.select) >= 0) {
|
|
p->value_type = esovtSelect;
|
|
return 1;
|
|
}
|
|
}
|
|
p->u.n = strtol(s.s, &err, 10);
|
|
if (*err) {
|
|
p->u.s.s = s;
|
|
p->u.s.cnt = 0;
|
|
p->value_type = esovtStr;
|
|
}
|
|
else {
|
|
p->value_type = esovtInt;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int eval_xl(struct sip_msg *msg, xl_elog_t* xl, str* s) {
|
|
static char *xlbuf=NULL;
|
|
int xllen = 0;
|
|
|
|
if (!xlbuf) {
|
|
xlbuf = (char*) pkg_malloc((xlbuf_size+1)*sizeof(char));
|
|
if (!xlbuf) {
|
|
ERR(MODULE_NAME": eval_xl: No memory left for format buffer\n");
|
|
return E_OUT_OF_MEM;
|
|
}
|
|
}
|
|
xllen = xlbuf_size;
|
|
if (xl_print(msg, xl, xlbuf, &xllen) < 0) {
|
|
ERR(MODULE_NAME": eval_xl: Error while formatting result\n");
|
|
return E_UNSPEC;
|
|
}
|
|
s->s = xlbuf;
|
|
s->len = xllen;
|
|
return 1;
|
|
}
|
|
|
|
SELECT_F(select_sys_unique)
|
|
|
|
static int eval_location(struct sip_msg *msg, struct eval_location* so, struct eval_value* v, int get_static_str) {
|
|
static struct eval_str ss;
|
|
|
|
v->type = evtVoid;
|
|
switch (so->value_type) {
|
|
case esovtInt:
|
|
v->type = evtInt;
|
|
v->u.n = so->u.n;
|
|
break;
|
|
case esovtStr:
|
|
v->type = evtStr;
|
|
v->u.s = &so->u.s;
|
|
break;
|
|
case esovtXStr: {
|
|
str s;
|
|
int ret;
|
|
ret = eval_xl(msg, so->u.xl, &s);
|
|
if (ret < 0) return ret;
|
|
if (get_static_str) {
|
|
ss.s = s;
|
|
ss.cnt = 0;
|
|
v->u.s = &ss;
|
|
}
|
|
else {
|
|
v->u.s = eval_str_malloc(&s);
|
|
if (!v->u.s) {
|
|
ERR(MODULE_NAME": out of memory to allocate xl string\n");
|
|
return E_OUT_OF_MEM;
|
|
}
|
|
}
|
|
v->type = evtStr;
|
|
break;
|
|
}
|
|
case esovtRegister:
|
|
if (get_static_str)
|
|
*v = so->u.reg->value; /* do not incement cnt */
|
|
else
|
|
assign_value(*v, so->u.reg->value);
|
|
break;
|
|
case esovtAvp: {
|
|
avp_t* avp;
|
|
avp_value_t val;
|
|
|
|
if (so->u.avp.flags & AVP_INDEX_ALL)
|
|
avp = search_first_avp(so->u.avp.flags & ~AVP_INDEX_ALL, so->u.avp.name, &val, NULL);
|
|
else
|
|
avp = search_avp_by_index(so->u.avp.flags, so->u.avp.name, &val, so->u.avp.index);
|
|
if (!avp) {
|
|
ERR(MODULE_NAME": avp '%.*s'[%d] not found\n", so->u.avp.name.s.len, so->u.avp.name.s.s, so->u.avp.index);
|
|
return -1;
|
|
}
|
|
if (avp->flags & AVP_VAL_STR) {
|
|
if (get_static_str) {
|
|
ss.s = val.s;
|
|
ss.cnt = 0;
|
|
v->u.s = &ss;
|
|
}
|
|
else {
|
|
v->u.s = eval_str_malloc(&val.s);
|
|
if (!v->u.s) {
|
|
ERR(MODULE_NAME": out of memory to allocate avp string\n");
|
|
return E_OUT_OF_MEM;
|
|
}
|
|
}
|
|
v->type = evtStr;
|
|
}
|
|
else {
|
|
v->type = evtInt;
|
|
v->u.n = val.n;
|
|
}
|
|
break;
|
|
}
|
|
case esovtSelect: {
|
|
str s;
|
|
int ret = run_select(&s, so->u.select, msg);
|
|
if (ret < 0 || ret > 0) return -1;
|
|
if (get_static_str) {
|
|
ss.s = s;
|
|
ss.cnt = 0;
|
|
v->u.s = &ss;
|
|
}
|
|
else {
|
|
v->u.s = eval_str_malloc(&s);
|
|
if (!v->u.s) {
|
|
ERR(MODULE_NAME": out of memory to allocate select string\n");
|
|
return E_OUT_OF_MEM;
|
|
}
|
|
}
|
|
v->type = evtStr;
|
|
break;
|
|
}
|
|
case esovtFunc: {
|
|
switch (so->u.func->type) {
|
|
case esofTime: {
|
|
time_t stamp;
|
|
stamp = time(NULL);
|
|
v->type = evtInt;
|
|
v->u.n = stamp;
|
|
break;
|
|
}
|
|
case esofUuid: {
|
|
str s;
|
|
select_sys_unique(&s, 0, msg);
|
|
if (get_static_str) {
|
|
ss.s = s;
|
|
ss.cnt = 0;
|
|
v->u.s = &ss;
|
|
}
|
|
else {
|
|
v->u.s = eval_str_malloc(&s);
|
|
if (!v->u.s) {
|
|
ERR(MODULE_NAME": out of memory to allocate uuid string\n");
|
|
return E_OUT_OF_MEM;
|
|
}
|
|
}
|
|
v->type = evtStr;
|
|
break;
|
|
}
|
|
case esofStackNo:
|
|
v->type = evtInt;
|
|
v->u.n = stack_no;
|
|
break;
|
|
default:
|
|
BUG("bad func type (%d)\n", so->u.func->type);
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
BUG("Bad value type (%d)\n", so->value_type);
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int fixup_location_12( void** param, int param_no) {
|
|
struct eval_location *so;
|
|
str s;
|
|
s.s = *param;
|
|
s.len = strlen(s.s);
|
|
so = pkg_malloc(sizeof(*so));
|
|
if (!so) return E_OUT_OF_MEM;
|
|
if (parse_location(s, so) < 0) {
|
|
ERR(MODULE_NAME": parse location error '%s'\n", s.s);
|
|
return E_CFG;
|
|
}
|
|
*param = so;
|
|
return 0;
|
|
}
|
|
|
|
static int fixup_stack_oper(void **param, int param_no, int oper_type) {
|
|
str s;
|
|
struct eval_stack_oper *p;
|
|
int ret;
|
|
|
|
if (param_no == 2) {
|
|
return fixup_location_12(param, param_no);
|
|
}
|
|
p = pkg_malloc(sizeof(*p));
|
|
if (!p) return E_OUT_OF_MEM;
|
|
p->oper_type = oper_type;
|
|
s.s = *param;
|
|
s.len = strlen(s.s);
|
|
*param = p;
|
|
ret = parse_location(s, &p->loc);
|
|
if (ret < 0) return ret;
|
|
|
|
switch (p->oper_type) {
|
|
case esotXchg:
|
|
if (p->loc.value_type == esovtAvp || p->loc.value_type == esovtSelect) {
|
|
ERR(MODULE_NAME": avp non supported for xchg\n");
|
|
return E_CFG;
|
|
}
|
|
/* no break */
|
|
case esotPop:
|
|
case esotGet:
|
|
if (p->loc.value_type != esovtRegister && p->loc.value_type != esovtAvp) {
|
|
ERR(MODULE_NAME": non supported read only location\n");
|
|
return E_CFG;
|
|
}
|
|
break;
|
|
default:;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int eval_stack_oper_func(struct sip_msg *msg, char *param1, char *param2) {
|
|
int ret, idx;
|
|
struct stack_item *pivot;
|
|
struct eval_stack_oper *so;
|
|
struct run_act_ctx ra_ctx;
|
|
|
|
so = (struct eval_stack_oper *)param1;
|
|
if (param2) {
|
|
long l;
|
|
struct eval_value v;
|
|
eval_location(msg, (struct eval_location*) param2, &v, 1);
|
|
ret = get_as_int(&v, &l);
|
|
if (ret < 0) return ret;
|
|
idx = l;
|
|
}
|
|
else {
|
|
switch (so->oper_type) { /* default values */
|
|
case esotAdd:
|
|
case esotAddValue:
|
|
idx = -1;
|
|
break;
|
|
default:
|
|
idx = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pivot = find_stack_item(idx);
|
|
if ( !(pivot!=NULL || ((so->oper_type == esotAdd || so->oper_type == esotAddValue) && idx == -1) || ((so->oper_type == esotInsert || so->oper_type == esotInsertValue) && idx == 0)) )
|
|
return -1;
|
|
|
|
switch (so->oper_type) {
|
|
case esotGet:
|
|
case esotPop:
|
|
switch (so->loc.value_type) {
|
|
case esovtRegister:
|
|
assign_value(so->loc.u.reg->value, pivot->value);
|
|
if (so->oper_type == esotPop)
|
|
remove_stack_item(pivot);
|
|
return 1;
|
|
case esovtAvp: {
|
|
struct action a;
|
|
avp_spec_t attr;
|
|
|
|
a.type = ASSIGN_T;
|
|
a.count = 2;
|
|
a.val[0].type = AVP_ST;
|
|
attr.type = so->loc.u.avp.flags;
|
|
attr.name = so->loc.u.avp.name;
|
|
attr.index = so->loc.u.avp.index;
|
|
a.val[0].u.attr = &attr;
|
|
switch (pivot->value.type) {
|
|
case evtInt:
|
|
a.val[1].type = NUMBER_ST;
|
|
a.val[1].u.number = pivot->value.u.n;
|
|
break;
|
|
case evtStr:
|
|
if (pivot->value.u.s)
|
|
a.val[1].u.str = pivot->value.u.s->s;
|
|
else
|
|
a.val[1].u.str.len = 0;
|
|
a.val[1].type = STRING_ST;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
a.next = 0;
|
|
init_run_actions_ctx(&ra_ctx);
|
|
ret = do_action(&ra_ctx, &a, msg);
|
|
if (so->oper_type == esotPop)
|
|
remove_stack_item(pivot);
|
|
return ret<0?-1:1;
|
|
}
|
|
default:
|
|
BUG("Bad value type (%d) for get/pop\n", so->loc.value_type);
|
|
return -1;
|
|
}
|
|
break;
|
|
case esotXchg:
|
|
switch (so->loc.value_type) {
|
|
case esovtRegister: {
|
|
struct eval_value v;
|
|
|
|
v = so->loc.u.reg->value;
|
|
so->loc.u.reg->value = pivot->value;
|
|
pivot->value = v;
|
|
return 1;
|
|
}
|
|
default:
|
|
BUG("Bad value type (%d) for xchg\n", so->loc.value_type);
|
|
return -1;
|
|
}
|
|
break;
|
|
case esotInsert:
|
|
case esotAdd:
|
|
case esotPut: {
|
|
struct eval_value v;
|
|
eval_location(msg, &so->loc, &v, 0);
|
|
|
|
if (so->oper_type == esotInsert || so->oper_type == esotAdd) {
|
|
struct stack_item *si;
|
|
si = pkg_malloc(sizeof(*si));
|
|
if (!si) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
destroy_value(v);
|
|
return -1;
|
|
}
|
|
si->value = v;
|
|
insert_stack_item(si, pivot, so->oper_type == esotAdd);
|
|
return 1;
|
|
}
|
|
else {
|
|
destroy_value(pivot->value);
|
|
pivot->value = v;
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
case esotInsertValue:
|
|
case esotAddValue: {
|
|
struct eval_value v;
|
|
str s, *vals;
|
|
int i, n;
|
|
struct eval_str* es;
|
|
struct stack_item *si;
|
|
eval_location(msg, &so->loc, &v, 0);
|
|
get_as_str(&v, &s);
|
|
if ((parse_hf_values(s, &n, &vals) < 0) || n == 0) {
|
|
destroy_value(v);
|
|
return -1;
|
|
}
|
|
si = pkg_malloc(sizeof(*si));
|
|
if (!si) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
destroy_value(v);
|
|
return -1;
|
|
}
|
|
si->value.type = evtInt;
|
|
si->value.u.n = n;
|
|
insert_stack_item(si, pivot, so->oper_type == esotAddValue);
|
|
pivot = si;
|
|
for (i=0; i<n; i++) {
|
|
si = pkg_malloc(sizeof(*si));
|
|
if (!si) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
destroy_value(v);
|
|
return -1;
|
|
}
|
|
es = eval_str_malloc(vals+i);
|
|
if (!es) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
destroy_value(v);
|
|
return -1;
|
|
}
|
|
si->value.type = evtStr;
|
|
si->value.u.s = es;
|
|
insert_stack_item(si, pivot, 1);
|
|
pivot = si;
|
|
}
|
|
destroy_value(v);
|
|
return 1;
|
|
}
|
|
default:
|
|
BUG("Unexpected operation (%d)\n", so->oper_type);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int eval_add_fixup( void** param, int param_no) {
|
|
return fixup_stack_oper(param, param_no, esotAdd);
|
|
}
|
|
|
|
static int eval_insert_fixup( void** param, int param_no) {
|
|
return fixup_stack_oper(param, param_no, esotInsert);
|
|
}
|
|
|
|
static int eval_put_fixup( void** param, int param_no) {
|
|
return fixup_stack_oper(param, param_no, esotPut);
|
|
}
|
|
|
|
static int eval_get_fixup( void** param, int param_no) {
|
|
return fixup_stack_oper(param, param_no, esotGet);
|
|
}
|
|
|
|
static int eval_pop_fixup( void** param, int param_no) {
|
|
return fixup_stack_oper(param, param_no, esotPop);
|
|
}
|
|
|
|
static int eval_xchg_fixup( void** param, int param_no) {
|
|
return fixup_stack_oper(param, param_no, esotXchg);
|
|
}
|
|
|
|
static int eval_add_value_fixup( void** param, int param_no) {
|
|
return fixup_stack_oper(param, param_no, esotAddValue);
|
|
}
|
|
|
|
static int eval_insert_value_fixup( void** param, int param_no) {
|
|
return fixup_stack_oper(param, param_no, esotInsertValue);
|
|
}
|
|
|
|
|
|
static int eval_remove_func(struct sip_msg *msg, char *param1, char *param2) {
|
|
struct stack_item *p, *p2;
|
|
int ret, len, start;
|
|
struct eval_value v;
|
|
|
|
if (param1) {
|
|
long l;
|
|
eval_location(msg, (struct eval_location*) param1, &v, 1);
|
|
ret = get_as_int(&v, &l);
|
|
if (ret < 0) return ret;
|
|
start = l;
|
|
}
|
|
else
|
|
start = 0;
|
|
p = find_stack_item(start);
|
|
if (p) {
|
|
if (param2) {
|
|
long l;
|
|
eval_location(msg, (struct eval_location*) param2, &v, 1);
|
|
ret = get_as_int(&v, &l);
|
|
if (ret < 0) return ret;
|
|
len = l;
|
|
}
|
|
else
|
|
len = 1;
|
|
|
|
if (start < 0) {
|
|
start = stack_no + start;
|
|
if (start < 0) start = 0;
|
|
}
|
|
else {
|
|
if (start > stack_no) start = stack_no;
|
|
}
|
|
if (len < 0) {
|
|
len = stack_no - start + len;
|
|
if (len < 0)
|
|
len = 0;
|
|
}
|
|
else {
|
|
if (start + len > stack_no)
|
|
len = stack_no - start;
|
|
}
|
|
|
|
for (; len > 0 && p; len--) {
|
|
p2 = p;
|
|
p = p->next;
|
|
remove_stack_item(p2);
|
|
}
|
|
return 1;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static int eval_clear_func(struct sip_msg *msg, char *param1, char *param2) {
|
|
int n;
|
|
if (get_int_fparam(&n, msg, (fparam_t*)param1)<0) {
|
|
ERR(MODULE_NAME": eval_clear: Invalid number specified\n");
|
|
return -1;
|
|
}
|
|
if (n & 1)
|
|
destroy_stack();
|
|
if (n & 2)
|
|
destroy_register_values();
|
|
return 1;
|
|
}
|
|
|
|
enum {esftNone=0, esftAdd, esftSub, esftMultiplication, esftDivision, esftModulo, esftNeg, esftAbs, esftSgn, esftDec, esftInc,
|
|
esftConcat, esftSubstr, esftStrLen, esftStrStr, esftStrDel, esftStrUpper, esftStrLower,
|
|
esftCastAsInt, esftCastAsStr,
|
|
esftValueAt, esftValueUris, esftValueRev, esftSubValue, esftValueCount, esftValueConcat, esftStrValueAt,
|
|
esftGetUri,
|
|
esftAnd, esftOr, esftNot, esftBitAnd, esftBitOr, esftBitNot, esftBitXor, esftEQ, esftNE, esftGT, esftGE, esftLW, esftLE};
|
|
|
|
struct eval_function_def {
|
|
int type;
|
|
char *name;
|
|
int arg_no;
|
|
};
|
|
|
|
static struct eval_function_def eval_functions[] = {
|
|
{esftAdd, "+", 2},
|
|
{esftSub, "-", 2},
|
|
{esftMultiplication, "*", 2},
|
|
{esftDivision, "/", 2},
|
|
{esftModulo, "%", 2},
|
|
{esftNeg, "neg", 1},
|
|
{esftAbs, "abs", 1},
|
|
{esftDec, "dec", 1},
|
|
{esftInc, "inc", 1},
|
|
{esftSgn, "sgn", 1},
|
|
{esftConcat, "concat", 2},
|
|
{esftSubstr, "substr", 3},
|
|
{esftStrLen, "strlen", 1},
|
|
{esftStrStr, "strstr", 2},
|
|
{esftStrDel, "strdel", 3},
|
|
{esftStrUpper, "strupper", 1},
|
|
{esftStrLower, "strlower", 1},
|
|
{esftCastAsInt, "(int)", 1},
|
|
{esftCastAsStr, "(str)", 1},
|
|
{esftValueAt, "valat", 2},
|
|
{esftValueUris, "valuris", 1},
|
|
{esftValueRev, "valrev", 1},
|
|
{esftSubValue, "subval", 3},
|
|
{esftValueCount, "valcount", 1},
|
|
{esftValueConcat, "valconcat", 1},
|
|
{esftStrValueAt, "strvalat", 2},
|
|
{esftGetUri, "geturi", 1},
|
|
{esftAnd, "&&", 2},
|
|
{esftOr, "||", 2},
|
|
{esftNot, "!", 1},
|
|
{esftBitAnd, "&", 2},
|
|
{esftBitOr, "|", 2},
|
|
{esftBitNot, "~", 1},
|
|
{esftBitXor, "^", 2},
|
|
|
|
{esftEQ, "==", 2},
|
|
{esftNE, "!=", 2},
|
|
{esftGT, ">", 2},
|
|
{esftGE, ">=", 2},
|
|
{esftLW, "<", 2},
|
|
{esftLE, "<=", 2},
|
|
|
|
{esftNone, NULL}
|
|
};
|
|
|
|
struct eval_function {
|
|
int resolved; /* is oper.d valid ? */
|
|
union {
|
|
struct eval_function_def *d;
|
|
struct eval_location loc;
|
|
} oper;
|
|
struct eval_function* next;
|
|
};
|
|
|
|
static int eval_stack_func_fixup( void** param, int param_no) {
|
|
char *c, *c2;
|
|
struct eval_function_def* d;
|
|
struct eval_function **p, *head;
|
|
if (param_no == 2) {
|
|
return fixup_location_12(param, param_no);
|
|
}
|
|
|
|
head = 0;
|
|
p = &head;
|
|
c = *param;
|
|
while (*c) {
|
|
str s;
|
|
struct eval_location so;
|
|
while( (*c<=' ' || *c == ',') && *c ) c++;
|
|
if (*c == '\0')
|
|
break;
|
|
c2 = c;
|
|
while (*c && *c!=',') c++;
|
|
while (c > c2 && *(c-1) <= ' ') c--;
|
|
|
|
s.s = c2;
|
|
s.len = c-c2;
|
|
|
|
if (parse_location(s, &so) < 0) {
|
|
ERR(MODULE_NAME": parse operation error near '%s'\n", c2);
|
|
return E_CFG;
|
|
}
|
|
*p = pkg_malloc(sizeof(**p));
|
|
if (!*p) return E_OUT_OF_MEM;
|
|
(*p)->next = 0;
|
|
switch (so.value_type) {
|
|
case esovtStr:
|
|
for (d=eval_functions; d->type; d++) {
|
|
if (strlen(d->name) == so.u.s.s.len && strncasecmp(d->name, so.u.s.s.s, so.u.s.s.len)==0) {
|
|
(*p)->oper.d = d;
|
|
break;
|
|
}
|
|
}
|
|
if (!d->type) {
|
|
ERR(MODULE_NAME": unknown eval function near '%s'\n", so.u.s.s.s);
|
|
return E_CFG;
|
|
}
|
|
(*p)->resolved = 1;
|
|
break;
|
|
case esovtAvp:
|
|
case esovtXStr:
|
|
case esovtRegister:
|
|
case esovtSelect:
|
|
case esovtFunc:
|
|
(*p)->oper.loc = so;
|
|
(*p)->resolved = 0;
|
|
break;
|
|
default:
|
|
ERR(MODULE_NAME": location %d not allowed\n", so.value_type);
|
|
return E_CFG;
|
|
}
|
|
p = &(*p)->next;
|
|
|
|
}
|
|
*param = head;
|
|
return 0;
|
|
}
|
|
|
|
#ifndef _GNU_SOURCE
|
|
void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen);
|
|
#endif
|
|
|
|
static int eval_stack_func_func(struct sip_msg *msg, char *param1, char *param2) {
|
|
struct eval_function *f;
|
|
struct stack_item *pivot;
|
|
struct eval_function_def *d;
|
|
int stack_idx = 0;
|
|
int ret = -1;
|
|
|
|
if (param2) {
|
|
long l;
|
|
int ret;
|
|
struct eval_value v;
|
|
eval_location(msg, (struct eval_location*) param2, &v, 1);
|
|
ret = get_as_int(&v, &l);
|
|
if (ret < 0) return ret;
|
|
stack_idx = l;
|
|
}
|
|
|
|
for (f = (struct eval_function*) param1; f; f=f->next, ret = 1) {
|
|
if (f->resolved) {
|
|
d = f->oper.d;
|
|
}
|
|
else {
|
|
str fn;
|
|
struct eval_value v;
|
|
eval_location(msg, &f->oper.loc, &v, 1);
|
|
get_as_str(&v, &fn);
|
|
for (d=eval_functions; d->type; d++) {
|
|
if (strlen(d->name) == fn.len && strncasecmp(d->name, fn.s, fn.len)==0) {
|
|
break;
|
|
}
|
|
}
|
|
if (!d->type) {
|
|
ERR(MODULE_NAME": unknown eval function '%.*s'\n", fn.len, fn.s);
|
|
return -1;
|
|
}
|
|
}
|
|
DEBUG(MODULE_NAME": eval_oper: %s, stack_idx: %d, stack_no: %d\n", d->name, stack_idx, stack_no);
|
|
if ( ((stack_idx >= 0) && (stack_idx+d->arg_no > stack_no)) ||
|
|
((stack_idx < 0) && (stack_no+stack_idx < 0 || stack_no+stack_idx+d->arg_no > stack_no)) ) {
|
|
ERR(MODULE_NAME": operation out of stack range\n");
|
|
return -1;
|
|
}
|
|
pivot = find_stack_item(stack_idx);
|
|
if (!pivot) {
|
|
BUG("stack test error\n");
|
|
return -1;
|
|
}
|
|
switch (d->type) {
|
|
|
|
case esftAdd:
|
|
case esftSub:
|
|
case esftMultiplication:
|
|
case esftDivision:
|
|
case esftModulo:
|
|
case esftAnd:
|
|
case esftOr:
|
|
case esftBitAnd:
|
|
case esftBitOr:
|
|
case esftBitXor: {
|
|
long a, b;
|
|
if (get_as_int(&pivot->value, &a) < 0) return -1;
|
|
if (get_as_int(&pivot->next->value, &b) < 0) return -1;
|
|
switch (d->type) {
|
|
case esftAdd:
|
|
a = a + b;
|
|
break;
|
|
case esftSub:
|
|
a = a - b;
|
|
break;
|
|
case esftMultiplication:
|
|
a = a * b;
|
|
break;
|
|
case esftDivision:
|
|
if (b == 0) {
|
|
ERR(MODULE_NAME": division by zero\n");
|
|
return -1;
|
|
}
|
|
a = a / b;
|
|
break;
|
|
case esftModulo:
|
|
if (b == 0) {
|
|
ERR(MODULE_NAME": division by zero\n");
|
|
return -1;
|
|
}
|
|
a = a % b;
|
|
break;
|
|
case esftAnd:
|
|
a = a && b;
|
|
break;
|
|
case esftOr:
|
|
a = a || b;
|
|
break;
|
|
case esftBitAnd:
|
|
a = a & b;
|
|
break;
|
|
case esftBitOr:
|
|
a = a | b;
|
|
break;
|
|
case esftBitXor:
|
|
a = a ^ b;
|
|
break;
|
|
}
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtInt;
|
|
pivot->value.u.n = a;
|
|
remove_stack_item(pivot->next);
|
|
break;
|
|
}
|
|
case esftNeg:
|
|
case esftAbs:
|
|
case esftSgn:
|
|
case esftDec:
|
|
case esftInc:
|
|
case esftNot:
|
|
case esftBitNot:
|
|
case esftCastAsInt: {
|
|
long a;
|
|
if (get_as_int(&pivot->value, &a) < 0) return -1;
|
|
switch (d->type) {
|
|
case esftNeg:
|
|
a = -a;
|
|
break;
|
|
case esftAbs:
|
|
a = abs(a);
|
|
break;
|
|
case esftSgn:
|
|
if (a < 0)
|
|
a = -1;
|
|
else if (a > 0)
|
|
a = 1;
|
|
else
|
|
a = 0;
|
|
break;
|
|
case esftDec:
|
|
a--;
|
|
break;
|
|
case esftInc:
|
|
a++;
|
|
break;
|
|
case esftNot:
|
|
a = !a;
|
|
break;
|
|
case esftBitNot:
|
|
a = ~a;
|
|
break;
|
|
case esftCastAsInt:
|
|
break;
|
|
}
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtInt;
|
|
pivot->value.u.n = a;
|
|
break;
|
|
}
|
|
case esftCastAsStr:
|
|
if (pivot->value.type != evtStr) {
|
|
str s;
|
|
get_as_str(&pivot->value, &s);
|
|
destroy_value(pivot->value);
|
|
pivot->value.u.s = eval_str_malloc(&s);
|
|
if (!pivot->value.u.s) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
return -1;
|
|
}
|
|
pivot->value.type = evtStr;
|
|
}
|
|
break;
|
|
case esftEQ:
|
|
case esftNE:
|
|
case esftGT:
|
|
case esftGE:
|
|
case esftLW:
|
|
case esftLE: {
|
|
long a;
|
|
if (pivot->value.type == evtStr || pivot->next->value.type == evtStr) {
|
|
str s1, s2;
|
|
int l;
|
|
get_as_str(&pivot->value, &s1);
|
|
get_as_str(&pivot->next->value, &s2);
|
|
l = (s1.len < s2.len)?s1.len:s2.len;
|
|
if (l > 0)
|
|
a = strncasecmp(s1.s, s2.s, l);
|
|
else
|
|
a = 0;
|
|
switch (d->type) {
|
|
case esftEQ:
|
|
a = a == 0 && s1.len == s2.len;
|
|
break;
|
|
case esftNE:
|
|
a = a != 0 || s1.len != s2.len;
|
|
break;
|
|
case esftGT:
|
|
a = a > 0 || (a == 0 && s1.len > s2.len);
|
|
break;
|
|
case esftGE:
|
|
a = a > 0 || (a == 0 && s1.len >= s2.len);
|
|
break;
|
|
case esftLW:
|
|
a = a < 0 || (a == 0 && s1.len < s2.len);
|
|
break;
|
|
case esftLE:
|
|
a = a < 0 || (a == 0 && s1.len <= s2.len);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
long b;
|
|
if (get_as_int(&pivot->value, &a) < 0) return -1;
|
|
if (get_as_int(&pivot->next->value, &b) < 0) return -1;
|
|
switch (d->type) {
|
|
case esftEQ:
|
|
a = a == b;
|
|
break;
|
|
case esftNE:
|
|
a = a != b;
|
|
break;
|
|
case esftGT:
|
|
a = a > b;
|
|
break;
|
|
case esftGE:
|
|
a = a >= b;
|
|
break;
|
|
case esftLW:
|
|
a = a < b;
|
|
break;
|
|
case esftLE:
|
|
a = a <= b;
|
|
break;
|
|
}
|
|
}
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtInt;
|
|
pivot->value.u.n = a;
|
|
remove_stack_item(pivot->next);
|
|
break;
|
|
}
|
|
case esftConcat: {
|
|
char buf[25];
|
|
str s, s1, s2;
|
|
struct eval_str* es;
|
|
get_as_str(&pivot->value, &s1);
|
|
if (pivot->value.type == evtInt && pivot->next->value.type == evtInt) {
|
|
memcpy(buf, s1.s, s1.len); /* result in static buffer */
|
|
s1.s = buf;
|
|
}
|
|
get_as_str(&pivot->next->value, &s2);
|
|
s.len = s1.len + s2.len;
|
|
s.s = 0;
|
|
es = eval_str_malloc(&s);
|
|
if (!es) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
return -1;
|
|
}
|
|
memcpy(s.s, s1.s, s1.len);
|
|
memcpy(s.s+s1.len, s2.s, s2.len);
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtStr;
|
|
pivot->value.u.s = es;
|
|
remove_stack_item(pivot->next);
|
|
break;
|
|
}
|
|
case esftSubstr: {
|
|
long start, len;
|
|
str s1;
|
|
struct eval_str* es;
|
|
get_as_str(&pivot->value, &s1);
|
|
if (get_as_int(&pivot->next->value, &start) < 0) return -1;
|
|
if (get_as_int(&pivot->next->next->value, &len) < 0) return -1;
|
|
|
|
if (start < 0) {
|
|
start = s1.len + start;
|
|
if (start < 0) start = 0;
|
|
}
|
|
else {
|
|
if (start > s1.len) start = s1.len;
|
|
}
|
|
if (len < 0) {
|
|
len = s1.len - start + len;
|
|
if (len < 0)
|
|
len = 0;
|
|
}
|
|
else {
|
|
if (start + len > s1.len)
|
|
len = s1.len - start;
|
|
}
|
|
s1.s += start;
|
|
s1.len = len;
|
|
es = eval_str_malloc(&s1);
|
|
if (!es) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
return -1;
|
|
}
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtStr;
|
|
pivot->value.u.s = es;
|
|
remove_stack_item(pivot->next);
|
|
remove_stack_item(pivot->next);
|
|
break;
|
|
}
|
|
case esftStrLen: {
|
|
long len;
|
|
str s1;
|
|
get_as_str(&pivot->value, &s1);
|
|
len = s1.len;
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtInt;
|
|
pivot->value.u.n = len;
|
|
break;
|
|
}
|
|
case esftStrStr: {
|
|
char buf[25], *p;
|
|
str s1, s2;
|
|
|
|
get_as_str(&pivot->value, &s1);
|
|
if (pivot->value.type == evtInt && pivot->next->value.type == evtInt) {
|
|
memcpy(buf, s1.s, s1.len); /* result in static buffer */
|
|
s1.s = buf;
|
|
}
|
|
get_as_str(&pivot->next->value, &s2);
|
|
p = (char *) memmem(s1.s, s1.len, s2.s, s2.len);
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtInt;
|
|
pivot->value.u.n = p?p-s1.s:-1;
|
|
remove_stack_item(pivot->next);
|
|
break;
|
|
}
|
|
case esftStrDel: {
|
|
long start, len;
|
|
str s1, s;
|
|
struct eval_str* es;
|
|
get_as_str(&pivot->value, &s1);
|
|
if (get_as_int(&pivot->next->value, &start) < 0) return -1;
|
|
if (get_as_int(&pivot->next->next->value, &len) < 0) return -1;
|
|
|
|
if (start < 0) {
|
|
start = s1.len + start;
|
|
if (start < 0) start = 0;
|
|
}
|
|
else {
|
|
if (start > s1.len) start = s1.len;
|
|
}
|
|
if (len < 0) {
|
|
len = s1.len - start + len;
|
|
if (len < 0)
|
|
len = 0;
|
|
}
|
|
else {
|
|
if (start + len > s1.len)
|
|
len = s1.len - start;
|
|
}
|
|
s.s = 0;
|
|
s.len = s1.len - len;
|
|
es = eval_str_malloc(&s);
|
|
if (!es) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
return -1;
|
|
}
|
|
if (start > 0)
|
|
memcpy(s.s, s1.s, start);
|
|
memcpy(s.s+start, s1.s+start+len, s1.len-(start+len));
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtStr;
|
|
pivot->value.u.s = es;
|
|
remove_stack_item(pivot->next);
|
|
remove_stack_item(pivot->next);
|
|
break;
|
|
}
|
|
case esftStrUpper:
|
|
case esftStrLower: {
|
|
str s1;
|
|
int i;
|
|
struct eval_str* es;
|
|
get_as_str(&pivot->value, &s1);
|
|
|
|
es = eval_str_malloc(&s1);
|
|
if (!es) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
return -1;
|
|
}
|
|
for (i=0; i<es->s.len; i++)
|
|
es->s.s[i] = (d->type == esftStrUpper) ? toupper(es->s.s[i]) : tolower(es->s.s[i]);
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtStr;
|
|
pivot->value.u.s = es;
|
|
break;
|
|
}
|
|
case esftValueAt: {
|
|
str s1, *vals;
|
|
long idx;
|
|
int n;
|
|
struct eval_str* es;
|
|
|
|
get_as_str(&pivot->value, &s1);
|
|
if (get_as_int(&pivot->next->value, &idx) < 0) return -1;
|
|
if (parse_hf_values(s1, &n, &vals) < 0) return -1;
|
|
if (idx < 0|| idx >= n) {
|
|
ERR(MODULE_NAME": index (%ld) of of range (%d)\n", idx, n);
|
|
return -1;
|
|
}
|
|
es = eval_str_malloc(vals+idx);
|
|
if (!es) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
return -1;
|
|
}
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtStr;
|
|
pivot->value.u.s = es;
|
|
remove_stack_item(pivot->next);
|
|
break;
|
|
}
|
|
case esftStrValueAt: {
|
|
char buf[25];
|
|
str s1, s2, *vals;
|
|
int i, n;
|
|
|
|
get_as_str(&pivot->value, &s1);
|
|
if (pivot->value.type == evtInt && pivot->next->value.type == evtInt) {
|
|
memcpy(buf, s1.s, s1.len); /* result in static buffer */
|
|
s1.s = buf;
|
|
}
|
|
get_as_str(&pivot->next->value, &s2);
|
|
if (parse_hf_values(s1, &n, &vals) < 0) return -1;
|
|
for (i=0; i<n; i++) {
|
|
if (s2.len == vals[i].len && strncmp(s2.s, vals[i].s, s2.len) == 0)
|
|
break;
|
|
}
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtInt;
|
|
pivot->value.u.n = (i>=n)?-1:i;
|
|
remove_stack_item(pivot->next);
|
|
break;
|
|
}
|
|
case esftValueCount: {
|
|
str s1, *vals;
|
|
int n;
|
|
|
|
get_as_str(&pivot->value, &s1);
|
|
if (parse_hf_values(s1, &n, &vals) < 0) return -1;
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtInt;
|
|
pivot->value.u.n = n;
|
|
break;
|
|
}
|
|
case esftSubValue: {
|
|
long start, len;
|
|
int i, n, pos;
|
|
str s1, s, *vals;
|
|
struct eval_str* es;
|
|
get_as_str(&pivot->value, &s1);
|
|
if (get_as_int(&pivot->next->value, &start) < 0) return -1;
|
|
if (get_as_int(&pivot->next->next->value, &len) < 0) return -1;
|
|
if (parse_hf_values(s1, &n, &vals) < 0) return -1;
|
|
|
|
if (start < 0) {
|
|
start = n + start;
|
|
if (start < 0) start = 0;
|
|
}
|
|
else {
|
|
if (start > n) start = n;
|
|
}
|
|
if (len < 0) {
|
|
len = n - start + len;
|
|
if (len < 0)
|
|
len = 0;
|
|
}
|
|
else {
|
|
if (start + len > n)
|
|
len = n - start;
|
|
}
|
|
s.len = 0;
|
|
for (i=0; i<len; i++) {
|
|
s.len += vals[start+i].len+1/*delim*/;
|
|
}
|
|
if (s.len)
|
|
s.len--;
|
|
s.s = 0;
|
|
es = eval_str_malloc(&s);
|
|
if (!es) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
return -1;
|
|
}
|
|
for (i=0, pos=0; i<len; i++) {
|
|
if (pos > 0)
|
|
s.s[pos++] = ',';
|
|
memcpy(s.s+pos, vals[start+i].s, vals[start+i].len);
|
|
pos += vals[start+i].len;
|
|
}
|
|
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtStr;
|
|
pivot->value.u.s = es;
|
|
remove_stack_item(pivot->next);
|
|
remove_stack_item(pivot->next);
|
|
break;
|
|
}
|
|
case esftValueConcat: {
|
|
long n;
|
|
int i, pos;
|
|
str s1, s;
|
|
struct eval_str* es;
|
|
struct stack_item *si;
|
|
|
|
if (get_as_int(&pivot->value, &n) < 0) return -1;
|
|
for (si=pivot->next, s.len=0, i=0; i<n && si; i++, si=si->next) {
|
|
get_as_str(&si->value, &s1);
|
|
s.len += s1.len+1;
|
|
}
|
|
if (s.len)
|
|
s.len--;
|
|
s.s = 0;
|
|
es = eval_str_malloc(&s);
|
|
if (!es) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
return -1;
|
|
}
|
|
for (si=pivot->next, i=0, pos=0; i<n && si; i++, si=si->next) {
|
|
if (pos > 0)
|
|
s.s[pos++] = ',';
|
|
get_as_str(&si->value, &s1);
|
|
memcpy(s.s+pos, s1.s, s1.len);
|
|
pos += s1.len;
|
|
}
|
|
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtStr;
|
|
pivot->value.u.s = es;
|
|
for (si=pivot->next, i=0; i<n && si; i++) {
|
|
struct stack_item *si2;
|
|
si2 = si;
|
|
si=si->next;
|
|
remove_stack_item(si2);
|
|
}
|
|
break;
|
|
|
|
}
|
|
case esftValueRev: {
|
|
int i, n, pos;
|
|
str s1, s, *vals;
|
|
struct eval_str* es;
|
|
get_as_str(&pivot->value, &s1);
|
|
if (parse_hf_values(s1, &n, &vals) < 0) return -1;
|
|
|
|
s.len = 0;
|
|
for (i=0; i<n; i++) {
|
|
s.len += vals[i].len+1/*delim*/;
|
|
}
|
|
if (s.len)
|
|
s.len--;
|
|
s.s = 0;
|
|
es = eval_str_malloc(&s);
|
|
if (!es) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
return -1;
|
|
}
|
|
for (i=n-1, pos=0; i>=0; i--) {
|
|
if (pos > 0)
|
|
s.s[pos++] = ',';
|
|
memcpy(s.s+pos, vals[i].s, vals[i].len);
|
|
pos += vals[i].len;
|
|
}
|
|
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtStr;
|
|
pivot->value.u.s = es;
|
|
break;
|
|
}
|
|
case esftValueUris: {
|
|
int i, n, pos;
|
|
str s1, s, *vals;
|
|
struct eval_str* es;
|
|
get_as_str(&pivot->value, &s1);
|
|
if (parse_hf_values(s1, &n, &vals) < 0) return -1;
|
|
|
|
s.len = 0;
|
|
for (i=0; i<n; i++) {
|
|
s.len += vals[i].len+1/*delim*/;
|
|
}
|
|
if (s.len)
|
|
s.len--;
|
|
s.s = 0;
|
|
es = eval_str_malloc(&s);
|
|
if (!es) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
return -1;
|
|
}
|
|
for (i=0, pos=0; i<n; i++) {
|
|
str hval1, huri;
|
|
if (pos > 0)
|
|
s.s[pos++] = ',';
|
|
hval1 = *(vals+i);
|
|
get_uri_and_skip_until_params(&hval1, &huri);
|
|
if (huri.len) {
|
|
/* TODO: normalize uri, lowercase except quoted params */
|
|
memcpy(s.s+pos, huri.s, huri.len);
|
|
pos += huri.len;
|
|
}
|
|
}
|
|
es->s.len = pos;
|
|
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtStr;
|
|
pivot->value.u.s = es;
|
|
break;
|
|
}
|
|
case esftGetUri: {
|
|
str s1, huri;
|
|
struct eval_str* es;
|
|
get_as_str(&pivot->value, &s1);
|
|
get_uri_and_skip_until_params(&s1, &huri);
|
|
if (huri.len && *(huri.s) == '<') {
|
|
huri.s++; /* strip < & > */
|
|
huri.len-=2;
|
|
}
|
|
es = eval_str_malloc(&huri);
|
|
if (!es) {
|
|
ERR(MODULE_NAME": out of memory\n");
|
|
return -1;
|
|
}
|
|
destroy_value(pivot->value);
|
|
pivot->value.type = evtStr;
|
|
pivot->value.u.s = es;
|
|
break;
|
|
}
|
|
default:
|
|
BUG("Bad operation %d\n", d->type);
|
|
return -1;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int eval_while_fixup(void **param, int param_no) {
|
|
|
|
if (param_no == 2) {
|
|
return fixup_location_12(param, param_no);
|
|
}
|
|
else if (param_no == 1) {
|
|
int n;
|
|
n = route_get(&main_rt, (char*) *param);
|
|
if (n == -1) {
|
|
ERR(MODULE_NAME": eval_while: bad route\n");
|
|
return E_CFG;
|
|
}
|
|
pkg_free(*param);
|
|
*param=(void*) (intptr_t) n;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int eval_while_func(struct sip_msg *msg, char *route_no, char *param2) {
|
|
int ret, idx;
|
|
struct stack_item *pivot;
|
|
struct run_act_ctx ra_ctx;
|
|
|
|
if (param2) {
|
|
long l;
|
|
struct eval_value v;
|
|
eval_location(msg, (struct eval_location*) param2, &v, 1);
|
|
ret = get_as_int(&v, &l);
|
|
if (ret < 0) return ret;
|
|
idx = l;
|
|
}
|
|
else {
|
|
idx = 0; /* default values */
|
|
}
|
|
|
|
ret = -1;
|
|
while (1) {
|
|
pivot = find_stack_item(idx);
|
|
if (!pivot) break;
|
|
if (get_as_bool(&pivot->value) <= 0) break;
|
|
if ((intptr_t)route_no >= main_rt.idx) {
|
|
BUG("invalid routing table number #%d of %d\n", (int)(intptr_t) route_no, main_rt.idx);
|
|
return -1;
|
|
}
|
|
if (!main_rt.rlist[(intptr_t) route_no]) {
|
|
WARN(MODULE_NAME": route not declared (hash:%d)\n", (int)(intptr_t) route_no);
|
|
return -1;
|
|
}
|
|
/* exec the routing script */
|
|
init_run_actions_ctx(&ra_ctx);
|
|
ret = run_actions(&ra_ctx, main_rt.rlist[(intptr_t) route_no], msg);
|
|
if (ret <= 0) break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int eval_while_stack_func(struct sip_msg *msg, char *route_no, char *param2) {
|
|
int ret, count;
|
|
struct run_act_ctx ra_ctx;
|
|
|
|
if (param2) {
|
|
long l;
|
|
struct eval_value v;
|
|
eval_location(msg, (struct eval_location*) param2, &v, 1);
|
|
ret = get_as_int(&v, &l);
|
|
if (ret < 0) return ret;
|
|
count = l;
|
|
}
|
|
else {
|
|
count = 0; /* default values */
|
|
}
|
|
ret = -1;
|
|
while ((count >= 0 && stack_no > count) || (count < 0 && stack_no < -count)) {
|
|
if ((intptr_t)route_no >= main_rt.idx) {
|
|
BUG("invalid routing table number #%d of %d\n", (int)(intptr_t) route_no, main_rt.idx);
|
|
return -1;
|
|
}
|
|
if (!main_rt.rlist[(intptr_t) route_no]) {
|
|
WARN(MODULE_NAME": route not declared (hash:%d)\n", (int)(intptr_t) route_no);
|
|
return -1;
|
|
}
|
|
/* exec the routing script */
|
|
init_run_actions_ctx(&ra_ctx);
|
|
ret = run_actions(&ra_ctx, main_rt.rlist[(intptr_t) route_no], msg);
|
|
if (ret <= 0) break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* select functions */
|
|
static int sel_value2str(str* res, struct eval_value *v, int force_copy) {
|
|
res->len = 0;
|
|
switch (v->type) {
|
|
case evtInt: {
|
|
char buf[30];
|
|
res->len = snprintf(buf, sizeof(buf)-1, "%ld", v->u.n);
|
|
res->s = get_static_buffer(res->len);
|
|
if (res->s)
|
|
memcpy(res->s, buf, res->len);
|
|
else
|
|
res->len = 0;
|
|
break;
|
|
}
|
|
case evtStr:
|
|
if (v->u.s) {
|
|
*res = v->u.s->s;
|
|
if (force_copy && res->len) {
|
|
res->s = get_static_buffer(res->len);
|
|
if (res->s)
|
|
memcpy(res->s, v->u.s->s.s, res->len);
|
|
else
|
|
res->len = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sel_eval(str* res, select_t* s, struct sip_msg* msg) { /* dummy */
|
|
return 0;
|
|
}
|
|
|
|
static int sel_register(str* res, select_t* s, struct sip_msg* msg) {
|
|
if (msg == 0) {
|
|
struct register_item *p = find_register(s->params[2].v.s.s, s->params[2].v.s.len);
|
|
if (p == 0) {
|
|
ERR(MODULE_NAME": select: register '%.*s' not found\n", s->params[2].v.s.len, s->params[2].v.s.s);
|
|
return E_CFG;
|
|
}
|
|
s->params[2].v.p = p;
|
|
s->params[2].type = SEL_PARAM_PTR;
|
|
}
|
|
else {
|
|
return sel_value2str(res, &((struct register_item *)s->params[2].v.p)->value, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sel_get_and_remove(str* res, select_t* s, struct sip_msg* msg) {
|
|
struct stack_item* p;
|
|
res->len = 0;
|
|
p = find_stack_item(s->params[2].v.i);
|
|
if (p) {
|
|
sel_value2str(res, &p->value, 1);
|
|
remove_stack_item(p);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sel_get(str* res, select_t* s, struct sip_msg* msg) {
|
|
struct stack_item* p;
|
|
res->len = 0;
|
|
p = find_stack_item(s->params[2].v.i);
|
|
if (p) {
|
|
sel_value2str(res, &p->value, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
SELECT_F(select_any_nameaddr)
|
|
SELECT_F(select_any_uri)
|
|
SELECT_F(select_anyheader_params)
|
|
|
|
select_row_t sel_declaration[] = {
|
|
{ NULL, SEL_PARAM_STR, STR_STATIC_INIT(MODULE_NAME), sel_eval, SEL_PARAM_EXPECTED},
|
|
|
|
{ sel_eval, SEL_PARAM_STR, STR_STATIC_INIT("pop"), sel_get_and_remove, CONSUME_NEXT_INT },
|
|
{ sel_eval, SEL_PARAM_STR, STR_STATIC_INIT("get"), sel_get, CONSUME_NEXT_INT },
|
|
{ sel_eval, SEL_PARAM_STR, STR_STATIC_INIT("reg"), sel_register, CONSUME_NEXT_STR|FIXUP_CALL },
|
|
|
|
{ sel_get, SEL_PARAM_STR, STR_STATIC_INIT("nameaddr"), select_any_nameaddr, NESTED | CONSUME_NEXT_STR},
|
|
{ sel_get, SEL_PARAM_STR, STR_STATIC_INIT("uri"), select_any_uri, NESTED | CONSUME_NEXT_STR},
|
|
{ sel_get, SEL_PARAM_STR, STR_STATIC_INIT("params"), select_anyheader_params, NESTED},
|
|
{ sel_register, SEL_PARAM_STR, STR_STATIC_INIT("nameaddr"), select_any_nameaddr, NESTED | CONSUME_NEXT_STR},
|
|
{ sel_register, SEL_PARAM_STR, STR_STATIC_INIT("uri"), select_any_uri, NESTED | CONSUME_NEXT_STR},
|
|
{ sel_register, SEL_PARAM_STR, STR_STATIC_INIT("params"), select_anyheader_params, NESTED},
|
|
|
|
/* for backward compatability only, use @sys.unique */
|
|
{ sel_eval, SEL_PARAM_STR, STR_STATIC_INIT("uuid"), select_sys_unique, 0},
|
|
|
|
{ NULL, SEL_PARAM_INT, STR_NULL, NULL, 0}
|
|
};
|
|
|
|
|
|
static int mod_init() {
|
|
|
|
register_script_cb(mod_pre_script_cb, REQUEST_CB | ONREPLY_CB | PRE_SCRIPT_CB, 0);
|
|
register_select_table(sel_declaration);
|
|
return 0;
|
|
}
|
|
|
|
static int child_init(int rank) {
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void destroy_mod(void) {
|
|
struct register_item *p;
|
|
destroy_stack();
|
|
destroy_register_values();
|
|
while (registers) {
|
|
p = registers;
|
|
registers = registers->next;
|
|
pkg_free(p);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Exported functions
|
|
*/
|
|
static cmd_export_t cmds[] = {
|
|
{MODULE_NAME"_add", eval_stack_oper_func, 2, eval_add_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_add", eval_stack_oper_func, 1, eval_add_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_push", eval_stack_oper_func, 2, eval_add_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_push", eval_stack_oper_func, 1, eval_add_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_insert", eval_stack_oper_func, 2, eval_insert_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_insert", eval_stack_oper_func, 1, eval_insert_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_xchg", eval_stack_oper_func, 2, eval_xchg_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_xchg", eval_stack_oper_func, 1, eval_xchg_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_get", eval_stack_oper_func, 2, eval_get_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_get", eval_stack_oper_func, 1, eval_get_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_put", eval_stack_oper_func, 2, eval_put_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_put", eval_stack_oper_func, 1, eval_put_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_pop", eval_stack_oper_func, 2, eval_pop_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_pop", eval_stack_oper_func, 1, eval_pop_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_add_value", eval_stack_oper_func, 2, eval_add_value_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_add_value", eval_stack_oper_func, 1, eval_add_value_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_insert_value", eval_stack_oper_func, 2, eval_insert_value_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_insert_value", eval_stack_oper_func, 1, eval_insert_value_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
|
|
{MODULE_NAME"_remove", eval_remove_func, 0, fixup_location_12, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_remove", eval_remove_func, 1, fixup_location_12, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_remove", eval_remove_func, 2, fixup_location_12, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_clear", eval_clear_func, 1, fixup_int_12, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
|
|
{MODULE_NAME"_oper", eval_stack_func_func, 2, eval_stack_func_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_oper", eval_stack_func_func, 1, eval_stack_func_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
|
|
{MODULE_NAME"_while", eval_while_func, 1, eval_while_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_while", eval_while_func, 2, eval_while_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_while_stack", eval_while_stack_func, 1, eval_while_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
{MODULE_NAME"_while_stack", eval_while_stack_func, 2, eval_while_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
|
|
{MODULE_NAME"_dump", eval_dump_func, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
|
|
|
|
{0, 0, 0, 0, 0}
|
|
};
|
|
|
|
/*
|
|
* Exported parameters
|
|
*/
|
|
static param_export_t params[] = {
|
|
{"declare_register", PARAM_STRING|PARAM_USE_FUNC, (void*) declare_register},
|
|
{"xlbuf_size", PARAM_INT, &xlbuf_size},
|
|
{0, 0, 0}
|
|
};
|
|
|
|
|
|
struct module_exports exports = {
|
|
MODULE_NAME,
|
|
cmds, /* Exported commands */
|
|
0, /* RPC */
|
|
params, /* Exported parameters */
|
|
mod_init, /* module initialization function */
|
|
0, /* response function*/
|
|
destroy_mod, /* destroy function */
|
|
0, /* oncancel function */
|
|
child_init /* per-child init function */
|
|
};
|