/* * $Id: avpops_parse.c 1525 2007-01-19 10:04:29Z miconda $ * * Copyright (C) 2004-2006 Voice Sistem SRL * * This file is part of Open SIP Express Router (openser). * * openser 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. * * openser 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * History: * --------- * 2004-10-04 first version (ramona) * 2004-11-11 DB scheme added (ramona) * 2004-11-17 aligned to new AVP core global aliases (ramona) */ #include #include #include "../../ut.h" #include "../../dprint.h" #include "../../usr_avp.h" #include "../../mem/mem.h" #include "avpops_parse.h" #define SCHEME_UUID_COL "uuid_col" #define SCHEME_UUID_COL_LEN (sizeof(SCHEME_UUID_COL)-1) #define SCHEME_USERNAME_COL "username_col" #define SCHEME_USERNAME_COL_LEN (sizeof(SCHEME_USERNAME_COL)-1) #define SCHEME_DOMAIN_COL "domain_col" #define SCHEME_DOMAIN_COL_LEN (sizeof(SCHEME_DOMAIN_COL)-1) #define SCHEME_VALUE_COL "value_col" #define SCHEME_VALUE_COL_LEN (sizeof(SCHEME_VALUE_COL)-1) #define SCHEME_TABLE "table" #define SCHEME_TABLE_LEN (sizeof(SCHEME_TABLE)-1) #define SCHEME_VAL_TYPE "value_type" #define SCHEME_VAL_TYPE_LEN (sizeof(SCHEME_VAL_TYPE)-1) #define SCHEME_INT_TYPE "integer" #define SCHEME_INT_TYPE_LEN (sizeof(SCHEME_INT_TYPE)-1) #define SCHEME_STR_TYPE "string" #define SCHEME_STR_TYPE_LEN (sizeof(SCHEME_STR_TYPE)-1) char *parse_avp_attr(char *s, struct fis_param *attr, char end) { unsigned int uint; str tmp; /*DBG("s=%p s=%c(%d)\n",s,*s,*s);*/ /* search for type identifier */ if ( s[0] && s[1]==':' ) { switch (s[0]) { case 'i': case 'I': attr->opd |= AVPOPS_VAL_INT; break; case 's': case 'S': attr->opd |= AVPOPS_VAL_STR; break; default: LOG(L_ERR,"ERROR:avpops:parse_avp_attr: invalid type '%c'\n", s[0]); goto error; } s += 2; } /* search for the avp name */ tmp.s = s; while ( *s && *s!=end && !isspace((int)*s)) s++; tmp.len = s - tmp.s; if (tmp.len==0) { attr->opd |= AVPOPS_VAL_NONE; } else { if ( attr->opd&AVPOPS_VAL_INT) { /* convert to ID (int) */ if ( str2int( &tmp, &uint)==-1 ) { LOG(L_ERR,"ERROR:avpops:parse_avp_attr: attribute is not " "int as type says <%s>\n", tmp.s); goto error; } attr->sval.p.val.s = 0; attr->sval.p.val.len = (int)uint; } else { /* duplicate name as str NULL terminated */ attr->sval.p.val.s = (char*)pkg_malloc(tmp.len + 1); if (attr->sval.p.val.s==0) { LOG(L_ERR,"ERROR:avpops:parse_avp_attr: no more pkg mem\n"); goto error; } attr->sval.p.val.len = tmp.len; memcpy(attr->sval.p.val.s, tmp.s, tmp.len); attr->sval.p.val.s[attr->sval.p.val.len] = 0; } } return s; error: return 0; } struct fis_param *avpops_parse_pvar(char *s, int flags) { struct fis_param *ap; char *p; /* compose the param structure */ ap = (struct fis_param*)pkg_malloc(sizeof(struct fis_param)); if (ap==0) { LOG(L_ERR,"ERROR:avpops:avpops_parse_pvar: no more pkg mem\n"); return NULL; } memset( ap, 0, sizeof(struct fis_param)); p = xl_parse_spec(s, &ap->sval, flags); if(p==0) { pkg_free(ap); return NULL; } ap->opd |= AVPOPS_VAL_PVAR; return ap; } int parse_avp_db(char *s, struct db_param *dbp, int allow_scheme) { unsigned long ul; str tmp; str s0; char have_scheme; char *p; char *p0; unsigned int flags; tmp.s = s; /* parse the attribute name - check first if it's not an alias */ p0=strchr(tmp.s, '/'); if(p0!=NULL) *p0=0; if ( *s!='$') { if(strlen(s)<1) { LOG(L_ERR,"ERROR:avops:parse_avp_db: bad param - " "expected : $avp(name), *, s or i value\n"); return E_UNSPEC; } switch(*s) { case 's': case 'S': dbp->a.opd = AVPOPS_VAL_NONE|AVPOPS_VAL_STR; break; case 'i': case 'I': dbp->a.opd = AVPOPS_VAL_NONE|AVPOPS_VAL_INT; break; case '*': case 'a': case 'A': dbp->a.opd = AVPOPS_VAL_NONE; break; default: LOG(L_ERR,"ERROR:avops:parse_avp_db: bad param - " "expected : *, s or i AVP flag\n"); return E_UNSPEC; } /* flags */ flags = 0; if(*(s+1)!='\0') { s0.s = s+1; s0.len = strlen(s0.s); if(str2int(&s0, &flags)!=0) { LOG(L_ERR, "ERROR:avops:parse_avp_db:: error - bad avp flags\n"); goto error; } } dbp->a.sval.flags |= flags<<24; } else { p = xl_parse_spec(s, &dbp->a.sval, XL_THROW_ERROR|XL_DISABLE_MULTI|XL_DISABLE_COLORS); if (p==0 || *p!='\0' || dbp->a.sval.type!=XL_AVP) { LOG(L_ERR,"ERROR:avops:parse_avp_db: bad param - " "expected : $avp(name) or int/str value\n"); return E_UNSPEC; } if(dbp->a.sval.flags&XL_DPARAM) { dbp->a.opd = AVPOPS_VAL_PVAR; } else { dbp->a.opd = (dbp->a.sval.p.val.s)?AVPOPS_VAL_STR:AVPOPS_VAL_INT; } } /* optimize and keep the attribute name as str also to * speed up db querie builds */ if (!(dbp->a.opd&AVPOPS_VAL_PVAR) && !(dbp->a.opd&AVPOPS_VAL_NONE)) { if (dbp->a.opd&AVPOPS_VAL_STR) { dbp->sa = dbp->a.sval.p.val; dbp->sa.s[dbp->sa.len] = '\0'; } else { ul = (unsigned long)dbp->a.sval.p.val.len; tmp.s = int2str( ul, &(tmp.len) ); dbp->sa.s = (char*)pkg_malloc( tmp.len + 1 ); if (dbp->sa.s==0) { LOG(L_ERR,"ERROR:avpops:parse_avp_db: no more pkg mem\n"); goto error; } memcpy( dbp->sa.s, tmp.s, tmp.len); dbp->sa.len = tmp.len; dbp->sa.s[dbp->sa.len] = 0; } } /* restore '/' */ if(p0) *p0 = '/'; /* is there a table name ? */ s = p0; if (s && *s) { s++; if (*s=='$') { if (allow_scheme==0) { LOG(L_ERR,"ERROR:avpops:parse_avp_db: function doesn't " "support DB schemes\n"); goto error; } if (dbp->a.opd&AVPOPS_VAL_NONE) { LOG(L_ERR,"ERROR:avpops:parse_avp_db: inconsistent usage of " "DB scheme without complet specification of AVP name\n"); goto error; } have_scheme = 1; s++; } else { have_scheme = 0; } tmp.s = s; tmp.len = 0; while ( *s ) s++; tmp.len = s - tmp.s; if (tmp.len==0) { LOG(L_ERR,"ERROR:avpops:parse_av_dbp: empty scheme/table name\n"); goto error; } if (have_scheme) { dbp->scheme = avp_get_db_scheme( tmp.s ); if (dbp->scheme==0) { LOG(L_ERR,"ERROR:avpops:parse_avp_db: scheme <%s> not found\n", tmp.s); goto error; } /* update scheme flags with AVP name type*/ dbp->scheme->db_flags|=dbp->a.opd&AVPOPS_VAL_STR?AVP_NAME_STR:0; } else { /* duplicate table as str NULL terminated */ dbp->table = (char*)pkg_malloc( tmp.len + 1 ); if (dbp->table==0) { LOG(L_ERR,"ERROR:avpops:parse_avp_db: no more pkg mem\n"); goto error;; } memcpy( dbp->table, tmp.s, tmp.len); dbp->table[tmp.len] = 0; } } return 0; error: return -1; } struct fis_param* parse_intstr_value(char *p, int len) { struct fis_param *vp; unsigned int uint; str val_str; int flags; if (p==0 || len==0) goto error; if (len>1 && *(p+1)==':') { if (*p=='i' || *p=='I') flags = AVPOPS_VAL_INT; else if (*p=='s' || *p=='S') flags = AVPOPS_VAL_STR; else { LOG(L_ERR,"ERROR:avpops:parse_intstr_value: unknown value type " "<%c>\n",*p); goto error; } p += 2; len -= 2; if (*p==0 || len<=0 ) { LOG(L_ERR,"ERROR:avpops:parse_intstr_value: parse error arround " "<%.*s>\n",len,p); goto error; } } else { flags = AVPOPS_VAL_STR; } /* get the value */ vp = (struct fis_param*)pkg_malloc(sizeof(struct fis_param)); if (vp==0) { LOG(L_ERR,"ERROR:avpops:parse_intstr_value: no more pkg mem\n"); goto error;; } memset( vp, 0, sizeof(struct fis_param)); vp->opd = flags; val_str.s = p; val_str.len = len; if (flags&AVPOPS_VAL_INT) { /* convert the value to integer */ if(val_str.len>2 && p[0]=='0' && (p[1]=='x' || p[1]=='X')) { if(hexstr2int(val_str.s+2, val_str.len-2, &uint)) { LOG(L_ERR,"ERROR:avpops:parse_intstr_value: value is not hex" " int as type says <%.*s>\n", val_str.len, val_str.s); goto error; } } else { if(str2sint( &val_str, (int*)&uint)==-1) { LOG(L_ERR,"ERROR:avpops:parse_intstr_value: value is not int" " as type says <%.*s>\n", val_str.len, val_str.s); goto error; } } vp->sval.p.val.len = (int)uint; } else { /* duplicate the value as string */ vp->sval.p.val.s = (char*)pkg_malloc((val_str.len+1)*sizeof(char)); if (vp->sval.p.val.s==0) { LOG(L_ERR,"ERROR:avpops:parse_intstr_value: no more pkg mem\n"); goto error; } vp->sval.p.val.len = val_str.len; memcpy(vp->sval.p.val.s, val_str.s, val_str.len); vp->sval.p.val.s[vp->sval.p.val.len] = 0; } return vp; error: return 0; } #define duplicate_str(_p, _str, _error) \ do { \ _p = (char*)pkg_malloc(_str.len+1); \ if (_p==0) \ { \ LOG(L_ERR,"ERROR:avpops:parse_avp_sb_scheme: " \ "no more pkg memory\n");\ goto _error; \ } \ memcpy( _p, _str.s, _str.len); \ _p[_str.len] = 0; \ }while(0) int parse_avp_db_scheme( char *s, struct db_scheme *scheme) { str foo; str bar; char *p; if (s==0 || *s==0) goto error; p = s; /*parse the name */ while (*p && isspace((int)*p)) p++; foo.s = p; while (*p && *p!=':' && !isspace((int)*p)) p++; if (foo.s==p || *p==0) /* missing name or empty scheme */ goto parse_error; foo.len = p - foo.s; /* dulicate it */ duplicate_str( scheme->name, foo, error); /* parse the ':' separator */ while (*p && isspace((int)*p)) p++; if (*p!=':') goto parse_error; p++; while (*p && isspace((int)*p)) p++; if (*p==0) goto parse_error; /* set as default value type string */ scheme->db_flags = AVP_VAL_STR; /* parse the attributes */ while (*p) { /* get the attribute name */ foo.s = p; while (*p && *p!='=' && !isspace((int)*p)) p++; if (p==foo.s || *p==0) /* missing attribute name */ goto parse_error; foo.len = p - foo.s; /* parse the '=' separator */ while (*p && isspace((int)*p)) p++; if (*p!='=') goto parse_error; p++; while (*p && isspace((int)*p)) p++; if (*p==0) goto parse_error; /* parse the attribute value */ bar.s = p; while (*p && *p!=';' && !isspace((int)*p)) p++; if (p==bar.s) /* missing attribute value */ goto parse_error; bar.len = p - bar.s; /* parse the ';' separator, if any */ while (*p && isspace((int)*p)) p++; if (*p!=0 && *p!=';') goto parse_error; if (*p==';') p++; while (*p && isspace((int)*p)) p++; /* identify the attribute */ if ( foo.len==SCHEME_UUID_COL_LEN && !strncasecmp( foo.s, SCHEME_UUID_COL, foo.len) ) { duplicate_str( scheme->uuid_col, bar, error); } else if ( foo.len==SCHEME_USERNAME_COL_LEN && !strncasecmp( foo.s, SCHEME_USERNAME_COL, foo.len) ) { duplicate_str( scheme->username_col, bar, error); } else if ( foo.len==SCHEME_DOMAIN_COL_LEN && !strncasecmp( foo.s, SCHEME_DOMAIN_COL, foo.len) ) { duplicate_str( scheme->domain_col, bar, error); } else if ( foo.len==SCHEME_VALUE_COL_LEN && !strncasecmp( foo.s, SCHEME_VALUE_COL, foo.len) ) { duplicate_str( scheme->value_col, bar, error); } else if ( foo.len==SCHEME_TABLE_LEN && !strncasecmp( foo.s, SCHEME_TABLE, foo.len) ) { duplicate_str( scheme->table, bar, error); } else if ( foo.len==SCHEME_VAL_TYPE_LEN && !strncasecmp( foo.s, SCHEME_VAL_TYPE, foo.len) ) { if ( bar.len==SCHEME_INT_TYPE_LEN && !strncasecmp( bar.s, SCHEME_INT_TYPE, bar.len) ) scheme->db_flags &= (~AVP_VAL_STR); else if ( bar.len==SCHEME_STR_TYPE_LEN && !strncasecmp( bar.s, SCHEME_STR_TYPE, bar.len) ) scheme->db_flags = AVP_VAL_STR; else { LOG(L_ERR,"ERROR:avpops:parse_avp_sb_scheme: unknown " "value type <%.*s>\n",bar.len,bar.s); goto error; } } else { LOG(L_ERR,"ERROR:avpops:parse_avp_sb_scheme: unknown " "attribute <%.*s>\n",foo.len,foo.s); goto error; } } /* end while */ return 0; parse_error: LOG(L_ERR,"ERROR:avpops:parse_avp_sb_scheme: parse error in <%s> " "around %ld\n", s, (long)(p-s)); error: return -1; } struct fis_param* parse_check_value(char *s) { struct fis_param *vp; int ops; int opd; char *p; char *t; int len; ops = 0; opd = 0; vp = 0; if ( (p=strchr(s,'/'))==0 || (p-s!=2&&p-s!=3) ) goto parse_error; /* get the operation */ if (strncasecmp(s,"eq",2)==0) { ops |= AVPOPS_OP_EQ; } else if (strncasecmp(s,"ne",2)==0) { ops |= AVPOPS_OP_NE; } else if (strncasecmp(s,"lt",2)==0) { ops |= AVPOPS_OP_LT; } else if (strncasecmp(s,"le",2)==0) { ops |= AVPOPS_OP_LE; } else if (strncasecmp(s,"gt",2)==0) { ops |= AVPOPS_OP_GT; } else if (strncasecmp(s,"ge",2)==0) { ops |= AVPOPS_OP_GE; } else if (strncasecmp(s,"re",2)==0) { ops |= AVPOPS_OP_RE; } else if (strncasecmp(s,"fm",2)==0) { ops |= AVPOPS_OP_FM; } else if (strncasecmp(s,"and",3)==0) { ops |= AVPOPS_OP_BAND; } else if (strncasecmp(s,"or",2)==0) { ops |= AVPOPS_OP_BOR; } else if (strncasecmp(s,"xor",3)==0) { ops |= AVPOPS_OP_BXOR; } else { LOG(L_ERR,"ERROR:avpops:parse_check_value: unknown operation " "<%.*s>\n",2,s); goto error; } /* get the value */ if (*(++p)==0) goto parse_error; if ( (t=strchr(p,'/'))==0) len = strlen(p); else len = t-p; if (*p=='$') { /* is variable */ vp = avpops_parse_pvar(p, XL_THROW_ERROR|XL_DISABLE_COLORS); if (vp==0) { LOG(L_ERR,"ERROR:avpops:parse_check_value: unable to get" " pseudo-variable\n"); goto error; } if (vp->sval.type==XL_NULL) { LOG(L_ERR,"ERROR:avops:parse_check_value: bad param; " "expected : $pseudo-variable or int/str value\n"); goto error; } opd |= AVPOPS_VAL_PVAR; DBG("flag==%d/%d\n", opd, ops); } else { /* value is explicitly given */ if ( (vp=parse_intstr_value(p,len))==0) { LOG(L_ERR,"ERROR:avpops:parse_check_value: unable to " "parse value\n"); goto error; } } p = t; /* any flags */ if (p!=NULL && *p!=0) { if (*p!='/' || *(++p)==0) goto parse_error; while (*p) { switch (*p) { case 'g': case 'G': ops|=AVPOPS_FLAG_ALL; break; case 'i': case 'I': ops|=AVPOPS_FLAG_CI; break; default: LOG(L_ERR,"ERROR:avpops:parse_check_value: unknown flag " "<%c>\n",*p); goto error; } p++; } } vp->ops |= ops; vp->opd |= opd; return vp; parse_error: LOG(L_ERR,"ERROR:avpops:parse_check_value: parse error in <%s> pos %ld\n", s,(long)(p-s)); error: if (vp) pkg_free(vp); return 0; } struct fis_param* parse_op_value(char *s) { struct fis_param *vp; int ops; int opd; char *p; char *t; int len; ops = 0; opd = 0; vp = 0; if ( (p=strchr(s,'/'))==0 || (p-s!=2&&p-s!=3) ) goto parse_error; /* get the operation */ if (strncasecmp(s,"add",3)==0) { ops |= AVPOPS_OP_ADD; } else if (strncasecmp(s,"sub",3)==0) { ops |= AVPOPS_OP_SUB; } else if (strncasecmp(s,"mul",3)==0) { ops |= AVPOPS_OP_MUL; } else if (strncasecmp(s,"div",3)==0) { ops |= AVPOPS_OP_DIV; } else if (strncasecmp(s,"mod",3)==0) { ops |= AVPOPS_OP_MOD; } else if (strncasecmp(s,"and",3)==0) { ops |= AVPOPS_OP_BAND; } else if (strncasecmp(s,"or",2)==0) { ops |= AVPOPS_OP_BOR; } else if (strncasecmp(s,"xor",3)==0) { ops |= AVPOPS_OP_BXOR; } else if (strncasecmp(s,"not",3)==0) { ops |= AVPOPS_OP_BNOT; } else { LOG(L_ERR,"ERROR:avpops:parse_op_value: unknown operation " "<%.*s>\n",2,s); goto error; } /* get the value */ if (*(++p)==0) goto parse_error; if ( (t=strchr(p,'/'))==0) len = strlen(p); else len = t-p; if (*p=='$') { /* is variable */ vp = avpops_parse_pvar(p, XL_THROW_ERROR|XL_DISABLE_COLORS); if (vp==0) { LOG(L_ERR,"ERROR:avpops:parse_op_value: unable to get" " pseudo-variable\n"); goto error; } if (vp->sval.type==XL_NULL) { LOG(L_ERR,"ERROR:avops:parse_op_value: bad param; " "expected : $pseudo-variable or int/str value\n"); goto error; } opd |= AVPOPS_VAL_PVAR; DBG("avops:parse_op_value: flag==%d/%d\n", opd, ops); } else { /* value is explicitly given */ if ( (vp=parse_intstr_value(p,len))==0) { LOG(L_ERR,"ERROR:avpops:parse_op_value: unable to parse value\n"); goto error; } if((vp->opd&AVPOPS_VAL_INT)==0) { LOG(L_ERR,"ERROR:avpops:parse_op_value: value must be int\n"); goto error; } } /* any flags */ p = t; if (p!=0 && *p!=0 ) { if (*p!='/' || *(++p)==0) goto parse_error; while (*p) { switch (*p) { case 'g': case 'G': ops|=AVPOPS_FLAG_ALL; break; case 'd': case 'D': ops|=AVPOPS_FLAG_DELETE; break; default: LOG(L_ERR,"ERROR:avpops:parse_op_value: unknown flag " "<%c>\n",*p); goto error; } p++; } } vp->ops |= ops; vp->opd |= opd; return vp; parse_error: LOG(L_ERR,"ERROR:avpops:parse_op_value: parse error in <%s> pos %ld\n", s,(long)(p-s)); error: if (vp) pkg_free(vp); return 0; }