/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of Kamailio, a free SIP server. * * Kamailio 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 * * Kamailio 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 #include #include #include #include #include #include #include #include #include #include "../../parser/parse_uri.h" #include "../../trim.h" #include "../../dprint.h" #include "../../str.h" #include "../../ut.h" #include "CPL_tree.h" #include "sub_list.h" #include "cpl_log.h" static struct node *list = 0; static xmlDtdPtr dtd; /* DTD file */ static xmlValidCtxt cvp; /* validating context */ typedef unsigned short length_type ; typedef length_type* length_type_ptr; enum {EMAIL_TO,EMAIL_HDR_NAME,EMAIL_KNOWN_HDR_BODY,EMAIL_UNKNOWN_HDR_BODY}; #define ENCONDING_BUFFER_SIZE 65536 #define FOR_ALL_ATTR(_node,_attr) \ for( (_attr)=(_node)->properties ; (_attr) ; (_attr)=(_attr)->next) #define check_overflow(_p_,_offset_,_end_,_error_) \ do{\ if ((_p_)+(_offset_)>=(_end_)) { \ LM_ERR("%s:%d: overflow -> buffer to small\n",\ __FILE__,__LINE__);\ goto _error_;\ }\ }while(0)\ #define set_attr_type(_p_,_type_,_end_,_error_) \ do{\ check_overflow(_p_,sizeof(length_type),_end_,_error_);\ *((length_type_ptr)(_p_)) = htons((length_type)(_type_));\ (_p_) += sizeof(length_type);\ }while(0)\ #define append_short_attr(_p_,_n_,_end_,_error_) \ do{\ check_overflow(_p_,sizeof(length_type),_end_,_error_);\ *((length_type_ptr)(_p_)) = htons((length_type)(_n_));\ (_p_) += sizeof(length_type);\ }while(0) #define append_str_attr(_p_,_s_,_end_,_error_) \ do{\ check_overflow(_p_,(_s_).len + 1*((((_s_).len)&0x0001)==1),\ _end_,_error_);\ *((length_type_ptr)(_p_)) = htons((length_type)(_s_).len);\ (_p_) += sizeof(length_type);\ memcpy( (_p_), (_s_).s, (_s_).len);\ (_p_) += (_s_).len + 1*((((_s_).len)&0x0001)==1);\ }while(0) #define append_double_str_attr(_p_,_s1_,_s2_,_end_,_error_) \ do{\ check_overflow(_p_,(_s1_).len + (_s2_).len +\ 1*((((_s2_).len+(_s2_).len)&0x0001)==1), _end_, _error_);\ *((length_type_ptr)(_p_))=htons((length_type)((_s1_).len)+(_s2_).len);\ (_p_) += sizeof(length_type);\ memcpy( (_p_), (_s1_).s, (_s1_).len);\ (_p_) += (_s1_).len;\ memcpy( (_p_), (_s2_).s, (_s2_).len);\ (_p_) += (_s2_).len + 1*((((_s1_).len+(_s2_).len)&0x0001)==1);\ }while(0) #define get_attr_val(_attr_name_,_val_,_error_) \ do { \ (_val_).s = (char*)xmlGetProp(node,(_attr_name_));\ (_val_).len = strlen((_val_).s);\ /* remove all spaces from begin and end */\ trim_spaces_lr( (_val_) );\ if ((_val_).len==0) {\ LM_ERR("%s:%d: attribute <%s> has an "\ "empty value\n",__FILE__,__LINE__,(_attr_name_));\ goto _error_;\ }\ }while(0)\ #define MAX_EMAIL_HDR_SIZE 7 /*we are looking only for SUBJECT and BODY ;-)*/ #define MAX_EMAIL_BODY_SIZE 512 #define MAX_EMAIL_SUBJECT_SIZE 32 static inline char *decode_mail_url(char *p, char *p_end, char *url, unsigned char *nr_attr) { static char buf[ MAX_EMAIL_HDR_SIZE ]; char c; char foo; unsigned short hdr_len; unsigned short *len; int max_len; int status; /* init */ hdr_len = 0; max_len = 0; status = EMAIL_TO; (*nr_attr) ++; set_attr_type(p, TO_ATTR, p_end, error); /* attr type */ len = ((unsigned short*)(p)); /* attr val's len */ *len = 0; /* init the len */ p += 2; /* parse the whole url */ do { /* extract a char from the encoded url */ if (*url=='+') { /* substitute a blank for a plus */ c=' '; url++; /* Look for a hex encoded character */ } else if ( (*url=='%') && *(url+1) && *(url+2) ) { /* hex encoded - convert to a char */ c = hex2int(url[1]); foo = hex2int(url[2]); if (c==-1 || foo==-1) { LM_ERR("non-ASCII escaped " "character in mail url [%.*s]\n", 3, url); goto error; } c = c<<4 | foo; url += 3; } else { /* normal character - just copy it without changing */ c = *url; url++; } /* finally we got a character !! */ switch (c) { case '?': switch (status) { case EMAIL_TO: if (*len==0) { LM_ERR("empty TO " "address found in MAIL node!\n"); goto error; } if (((*len)&0x0001)==1) p++; *len = htons(*len); hdr_len = 0; status = EMAIL_HDR_NAME; break; default: goto parse_error; } break; case '=': switch (status) { case EMAIL_HDR_NAME: LM_DBG("hdr [%.*s] found\n", hdr_len,buf); if ( hdr_len==BODY_EMAILHDR_LEN && strncasecmp(buf,BODY_EMAILHDR_STR,hdr_len)==0 ) { /* BODY hdr found */ set_attr_type( p, BODY_ATTR, p_end, error); max_len = MAX_EMAIL_BODY_SIZE; } else if ( hdr_len==SUBJECT_EMAILHDR_LEN && strncasecmp(buf,SUBJECT_EMAILHDR_STR,hdr_len)==0 ) { /* SUBJECT hdr found */ set_attr_type( p, SUBJECT_ATTR, p_end, error); max_len = MAX_EMAIL_SUBJECT_SIZE; } else { LM_DBG("unknown hdr -> ignoring\n"); status = EMAIL_UNKNOWN_HDR_BODY; break; } (*nr_attr) ++; len = ((unsigned short*)(p)); /* attr val's len */ *len = 0; /* init the len */ p += 2; status = EMAIL_KNOWN_HDR_BODY; break; default: goto parse_error; } break; case '&': switch (status) { case EMAIL_KNOWN_HDR_BODY: if (((*len)&0x0001)==1) p++; *len = htons(*len); case EMAIL_UNKNOWN_HDR_BODY: hdr_len = 0; status = EMAIL_HDR_NAME; break; default: goto parse_error; } break; case 0: switch (status) { case EMAIL_TO: if (*len==0) { LM_ERR("empty TO " "address found in MAIL node!\n"); goto error; } case EMAIL_KNOWN_HDR_BODY: if (((*len)&0x0001)==1) p++; *len = htons(*len); case EMAIL_UNKNOWN_HDR_BODY: break; default: goto parse_error; } break; default: switch (status) { case EMAIL_TO: (*len)++; *(p++) = c; if (*len==URL_MAILTO_LEN && !strncasecmp(p-(*len),URL_MAILTO_STR,(*len))) { LM_DBG("MAILTO: found at" " the beginning of TO -> removed\n"); p -= (*len); *len = 0; } break; case EMAIL_KNOWN_HDR_BODY: if ((*len)name[0]) { case 'i': case 'I': set_attr_type(p, IS_ATTR, buf_end, error); break; case 'c': case 'C': set_attr_type(p, CONTAINS_ATTR, buf_end, error); break; case 's': case 'S': set_attr_type(p, SUBDOMAIN_OF_ATTR, buf_end, error); break; default: LM_ERR("unknown attribute " "<%s>\n",attr->name); goto error; } /* get the value of the attribute */ get_attr_val( attr->name , val, error); /* copy also the \0 from the end of string */ val.len++; append_str_attr(p, val, buf_end, error); } return p-p_orig; error: return -1; } /* Attr. encoding for ADDRESS_SWITCH node: * | attr1_t(2) attr1_val(2) | FIELD attr * [| attr2_t(2) attr2_val(2) |]? SUBFILED attr */ static inline int encode_address_switch_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get the value of the attribute */ get_attr_val( attr->name , val, error); switch(attr->name[0]) { case 'F': case 'f': set_attr_type(p, FIELD_ATTR, buf_end, error); if (val.s[0]=='D' || val.s[0]=='d') append_short_attr(p, DESTINATION_VAL, buf_end, error); else if (val.s[6]=='A' || val.s[6]=='a') append_short_attr(p,ORIGINAL_DESTINATION_VAL,buf_end,error); else if (!val.s[6]) append_short_attr(p, ORIGIN_VAL, buf_end, error); else { LM_ERR("unknown" " value <%s> for FIELD attr\n",val.s); goto error; }; break; case 'S': case 's': set_attr_type(p, SUBFIELD_ATTR, buf_end, error); switch (val.s[0]) { case 'u': case 'U': append_short_attr(p, USER_VAL, buf_end, error); break; case 'h': case 'H': append_short_attr(p, HOST_VAL, buf_end, error); break; case 'p': case 'P': append_short_attr(p, PORT_VAL, buf_end, error); break; case 't': case 'T': append_short_attr(p, TEL_VAL, buf_end, error); break; case 'd': case 'D': /*append_short_attr(p, DISPLAY_VAL, buf_end, error); break;*/ /* NOT YET SUPPORTED BY INTERPRETER */ case 'a': case 'A': /*append_short_attr(p, ADDRESS_TYPE_VAL, buf_end,error); break;*/ /* NOT YET SUPPORTED BY INTERPRETER */ default: LM_ERR("unknown value <%s> for SUBFIELD attr\n",val.s); goto error; } break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for LANGUAGE node: * | attr_t(2) attr_len(2) attr_val(2*x) | MATCHES attr (NNT) */ static inline int encode_lang_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; char *end; char *val_bk; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { /* there is only one attribute -> MATCHES */ if (attr->name[0]!='M' && attr->name[0]!='m') { LM_ERR("unknown attribute " "<%s>\n",attr->name); goto error; } val.s = val_bk = (char*)xmlGetProp(node,attr->name); /* parse the language-tag */ for(end=val.s,val.len=0;;end++) { /* trim all spaces from the beginning of the tag */ if (!val.len && (*end==' ' || *end=='\t')) continue; /* we cannot have more than 2 attrs - LANG_TAG and LANG_SUBTAG */ if ((*nr_attr)>=2) goto lang_error; if (((*end)|0x20)>='a' && ((*end)|0x20)<='z') { val.len++; continue; } else if (*end=='*' && val.len==0 && (*nr_attr)==0 && (*end==' '|| *end=='\t' || *end==0)) { val.len++; set_attr_type(p, MATCHES_TAG_ATTR, buf_end, error); } else if (val.len && (*nr_attr)==0 && *end=='-' ) { set_attr_type(p, MATCHES_TAG_ATTR, buf_end, error); } else if (val.len && ((*nr_attr)==0 || (*nr_attr)==1) && (*end==' '|| *end=='\t' || *end==0)) { set_attr_type(p, (!(*nr_attr))?MATCHES_TAG_ATTR:MATCHES_SUBTAG_ATTR, buf_end, error ); } else goto lang_error; (*nr_attr)++; /*LM_DBG("----> language tag=%d; %d [%.*s]\n",*(p-1), val.len,val.len,end-val.len);*/ val.s = end-val.len; append_str_attr(p, val, buf_end, error); val.len = 0; if (*end==0) break; } } return p-p_orig; lang_error: LM_ERR("bad value for language_tag <%s>\n",val_bk); error: return -1; } /* Attr. encoding for PRIORITY node: * | attr1_t(2) attr1_val(2) | LESS/GREATER/EQUAL attr * [| attr2_t(2) attr2_len(2) attr_val(2*x) |]? PRIOSTR attr (NT) */ static inline int encode_priority_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* attribute's name */ switch(attr->name[0]) { case 'L': case 'l': set_attr_type(p, LESS_ATTR, buf_end, error); break; case 'G': case 'g': set_attr_type(p, GREATER_ATTR, buf_end, error); break; case 'E': case 'e': set_attr_type(p, EQUAL_ATTR, buf_end, error); break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } /* attribute's encoded value */ get_attr_val( attr->name , val, error); if ( val.len==EMERGENCY_STR_LEN && !strncasecmp(val.s,EMERGENCY_STR,val.len) ) { append_short_attr(p, EMERGENCY_VAL, buf_end, error); } else if ( val.len==URGENT_STR_LEN && !strncasecmp(val.s,URGENT_STR,val.len) ) { append_short_attr(p, URGENT_VAL, buf_end, error); } else if ( val.len==NORMAL_STR_LEN && !strncasecmp(val.s,NORMAL_STR,val.len) ) { append_short_attr(p, NORMAL_VAL, buf_end, error); } else if ( val.len==NON_URGENT_STR_LEN && !strncasecmp(val.s,NON_URGENT_STR,val.len) ) { append_short_attr(p, NON_URGENT_VAL, buf_end, error); } else { append_short_attr(p, UNKNOWN_PRIO_VAL, buf_end, error); set_attr_type(p, PRIOSTR_ATTR, buf_end, error); val.len++; /* append \0 also */ append_str_attr(p, val, buf_end, error); } } return p-p_orig; error: return -1; } /* Attr. encoding for STRING_SWITCH node: * [| attr1_t(2) attr1_len(2) attr_val(2*x) |]? IS attr (NT) * [| attr2_t(2) attr2_len(2) attr_val(2*x) |]? CONTAINS attr (NT) */ static inline int encode_string_switch_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* there is only one attribute -> MATCHES */ if (attr->name[0]!='F' && attr->name[0]!='f') { LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } set_attr_type(p, FIELD_ATTR, buf_end, error); /* attribute's encoded value */ get_attr_val( attr->name , val, error); switch (val.s[0]) { case 'S': case 's': append_short_attr(p, SUBJECT_VAL, buf_end, error); break; case 'O': case 'o': append_short_attr(p, ORGANIZATION_VAL, buf_end, error); break; case 'U': case 'u': append_short_attr(p, USER_AGENT_VAL, buf_end, error); break; case 'D': case 'd': append_short_attr(p, DISPLAY_VAL, buf_end, error); break; default: LM_ERR("unknown " "value <%s> for FIELD\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for STRING node: * [| attr1_t(2) attr1_len(2) attr_val(2*x) |]? IS attr (NT) * [| attr2_t(2) attr2_len(2) attr_val(2*x) |]? CONTAINS attr (NT) */ static inline int encode_string_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; switch(attr->name[0]) { case 'I': case 'i': set_attr_type(p, IS_ATTR, buf_end, error); break; case 'C': case 'c': set_attr_type(p, CONTAINS_ATTR, buf_end, error); break; default: LM_ERR("unknown " "attribute <%s>\n",attr->name); goto error; } /* attribute's encoded value */ get_attr_val( attr->name , val, error); val.len++; /* grab also the \0 */ append_str_attr(p,val, buf_end, error); } return p-p_orig; error: return -1; } /* Attr. encoding for TIME_SWITCH node: * [| attr1_t(2) attr1_len(2) attr_val(2*x) |]? TZID attr (NT) * [| attr2_t(2) attr2_len(2) attr_val(2*x) |]? TZURL attr (NT) */ static inline int encode_time_switch_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { static str tz_str = {"TZ=",3}; xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; switch(attr->name[2]) { case 'I': case 'i': set_attr_type(p, TZID_ATTR, buf_end, error); /* attribute's encoded value */ get_attr_val( attr->name , val, error); val.len++; /* grab also the \0 */ append_double_str_attr(p,tz_str,val, buf_end, error); break; case 'U': case 'u': /* set_attr_type(p, TZURL_ATTR, buf_end, error); * is a waste of space to copy the url - the interpreter doesn't * use it at all ;-) */ break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for TIME node: * | attr1_t(2) attr1_len(2) attr1_val(2*x) | DSTART attr (NT) * [| attr2_t(2) attr2_len(2) attr2_val(2*x) |]? DTEND attr (NT) * [| attr3_t(2) attr3_len(2) attr3_val(2*x) |]? DURATION attr (NT) * [| attr4_t(2) attr4_len(2) attr4_val(2*x) |]? FREQ attr (NT) * [| attr5_t(2) attr5_len(2) attr5_val(2*x) |]? WKST attr (NT) * [| attr6_t(2) attr6_len(2) attr6_val(2*x) |]? BYYEARDAY attr (NT) * [| attr7_t(2) attr7_len(2) attr7_val(2*x) |]? COUNT attr (NT) * [| attr8_t(2) attr8_len(2) attr8_val(2*x) |]? BYSETPOS attr (NT) * [| attr9_t(2) attr9_len(2) attr9_val(2*x) |]? BYMONTH attr (NT) * [| attr10_t(2) attr10_len(2) attr_val10(2*x) |]? BYMONTHDAY attr (NT) * [| attr11_t(2) attr11_len(2) attr_val11(2*x) |]? BYMINUTE attr (NT) * [| attr12_t(2) attr12_len(2) attr_val12(2*x) |]? INTERVAL attr (NT) * [| attr13_t(2) attr13_len(2) attr_val13(2*x) |]? UNTIL attr (NT) * [| attr14_t(2) attr14_len(2) attr_val14(2*x) |]? BYSECOND attr (NT) * [| attr15_t(2) attr15_len(2) attr_val15(2*x) |]? BYHOUR attr (NT) * [| attr16_t(2) attr16_len(2) attr_val16(2*x) |]? BYDAY attr (NT) * [| attr17_t(2) attr17_len(2) attr_val17(2*x) |]? BYWEEKNO attr (NT) */ static inline int encode_time_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; switch (attr->name[4]) { case 0: if (attr->name[0]=='F' || attr->name[0]=='f') set_attr_type(p, FREQ_ATTR, buf_end, error); else if (attr->name[0]=='W' || attr->name[0]=='w') set_attr_type(p, WKST_ATTR, buf_end, error); break; case 'a': case 'A': if (attr->name[0]=='D' || attr->name[0]=='d') set_attr_type(p, DTSTART_ATTR, buf_end, error); else if (attr->name[0]=='B' || attr->name[0]=='b') set_attr_type(p, BYYEARDAY_ATTR, buf_end, error); break; case 't': case 'T': if (attr->name[0]=='D' || attr->name[0]=='d') set_attr_type(p, DURATION_ATTR, buf_end, error); else if (attr->name[0]=='C' || attr->name[0]=='c') set_attr_type(p, COUNT_ATTR, buf_end, error); else if (attr->name[0]=='B' || attr->name[0]=='b') set_attr_type(p, BYSETPOS_ATTR, buf_end, error); break; case 'n': case 'N': if (!attr->name[7]) set_attr_type(p, BYMONTH_ATTR, buf_end, error); else if (attr->name[7]=='D' || attr->name[7]=='d') set_attr_type(p, BYMONTHDAY_ATTR, buf_end, error); else if (attr->name[7]=='e' || attr->name[7]=='E') set_attr_type(p, BYMINUTE_ATTR, buf_end, error); break; case 'd': case 'D': set_attr_type(p, DTEND_ATTR, buf_end, error); break; case 'r': case 'R': set_attr_type(p, INTERVAL_ATTR, buf_end, error); break; case 'l': case 'L': set_attr_type(p, UNTIL_ATTR, buf_end, error); break; case 'c': case 'C': set_attr_type(p, BYSECOND_ATTR, buf_end, error); break; case 'u': case 'U': set_attr_type(p, BYHOUR_ATTR, buf_end, error); break; case 'y': case 'Y': set_attr_type(p, BYDAY_ATTR, buf_end, error); break; case 'e': case 'E': set_attr_type(p, BYWEEKNO_ATTR, buf_end, error); break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } /* attribute's encoded value */ get_attr_val( attr->name , val, error); val.len++; /* grab also the \0 */ append_str_attr(p,val, buf_end, error); } return p-p_orig; error: return -1; } /* Attr. encoding for LOOKUP node: * | attr1_t(2) attr1_len(2) attr1_val(2*x) | SOURCE attr (NT) * [| attr2_t(2) attr2_val(2) |]? CLEAR attr */ static inline int encode_lookup_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { /* get attribute's value */ get_attr_val( attr->name , val, error); if ( !strcasecmp((const char*)attr->name,"source") ) { /* this param will not be copied, since it has only one value ;-)*/ if ( val.len!=SOURCE_REG_STR_LEN || strncasecmp( val.s, SOURCE_REG_STR, val.len) ) { LM_ERR("unsupported value" " <%.*s> in SOURCE param\n",val.len,val.s); goto error; } } else if ( !strcasecmp((const char*)attr->name,"clear") ) { (*nr_attr)++; set_attr_type(p, CLEAR_ATTR, buf_end, error); if ( val.len==3 && !strncasecmp(val.s,"yes",3) ) append_short_attr(p, YES_VAL, buf_end, error); else if ( val.len==2 && !strncasecmp(val.s,"no",2) ) append_short_attr(p, NO_VAL, buf_end, error); else { LM_ERR("unknown value " "<%.*s> for attribute CLEAR\n",val.len,val.s); goto error; } } else if ( !strcasecmp((const char*)attr->name,"timeout") ) { LM_WARN("unsupported param TIMEOUT; skipping\n"); } else { LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for LOCATION node: * | attr1_t(2) attr1_len(2) attr1_val(2*x) | URL attr (NT) * [| attr2_t(2) attr2_val(2) |]? PRIORITY attr * [| attr3_t(2) attr3_val(2) |]? CLEAR attr */ static inline int encode_location_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { struct sip_uri uri; xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; unsigned short nr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get attribute's value */ get_attr_val( attr->name , val, error); switch(attr->name[0]) { case 'U': case 'u': set_attr_type(p, URL_ATTR, buf_end, error); /* check if it's a valid SIP URL -> just call * parse uri function and see if returns error ;-) */ if (parse_uri( val.s, val.len, &uri)!=0) { LM_ERR("<%s> is not a valid SIP URL\n",val.s); goto error; } val.len++; /*copy also the \0 */ append_str_attr(p,val, buf_end, error); break; case 'P': case 'p': set_attr_type(p, PRIORITY_ATTR, buf_end, error); if (val.s[0]=='0') nr=0; else if (val.s[0]=='1') nr=10; else goto prio_error; if (val.s[1]!='.') goto prio_error; if (val.s[2]<'0' || val.s[2]>'9') goto prio_error; nr += val.s[2] - '0'; if (nr>10) goto prio_error; append_short_attr(p, nr, buf_end, error); break; case 'C': case 'c': set_attr_type(p, CLEAR_ATTR, buf_end, error); if (val.s[0]=='y' || val.s[0]=='Y') append_short_attr(p, YES_VAL, buf_end, error); else append_short_attr(p, NO_VAL, buf_end, error); break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; prio_error: LM_ERR("invalid priority <%s>\n",val.s); error: return -1; } /* Attr. encoding for REMOVE_LOCATION node: * [| attr1_t(2) attr1_len(2) attr1_val(2*x) |]? LOCATION attr (NT) */ static inline int encode_rmvloc_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { struct sip_uri uri; xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; switch(attr->name[0]) { case 'L': case 'l': set_attr_type(p, LOCATION_ATTR, buf_end, error); /* get the value of the attribute */ get_attr_val( attr->name , val, error); /* check if it's a valid SIP URL -> just call * parse uri function and see if returns error ;-) */ if (parse_uri( val.s, val.len, &uri)!=0) { LM_ERR("<%s> is not a valid SIP URL\n",val.s); goto error; } val.len++; /*copy also the \0 */ append_str_attr(p,val, buf_end, error); break; case 'P': case 'p': case 'V': case 'v': /* as the interpreter ignores PARAM and VALUE attributes, we will * do the same ;-) */ break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for PROXY node: * [| attr1_t(2) attr1_val(2) |]? RECURSE attr * [| attr2_t(2) attr2_val(2) |]? TIMEOUT attr * [| attr3_t(2) attr3_val(2) |]? ORDERING attr */ static inline int encode_proxy_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; unsigned int nr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get the value of the attribute */ get_attr_val( attr->name , val, error); switch(attr->name[0]) { case 'R': case 'r': set_attr_type(p, RECURSE_ATTR, buf_end, error); if (val.s[0]=='y' || val.s[0]=='Y') append_short_attr(p, YES_VAL, buf_end, error); else if (val.s[0]=='n' || val.s[0]=='N') append_short_attr(p, NO_VAL, buf_end, error); else { LM_ERR("unknown value " "<%s> for attribute RECURSE\n",val.s); goto error; } break; case 'T': case 't': set_attr_type(p, TIMEOUT_ATTR, buf_end, error); if (str2int(&val,&nr)==-1) { LM_ERR("bad value <%.*s>" " for attribute TIMEOUT\n",val.len,val.s); goto error; } append_short_attr(p, (unsigned short)nr, buf_end, error); break; case 'O': case 'o': set_attr_type(p, ORDERING_ATTR, buf_end, error); switch (val.s[0]) { case 'p': case'P': append_short_attr(p, PARALLEL_VAL, buf_end, error); break; case 'S': case 's': append_short_attr(p, SEQUENTIAL_VAL, buf_end, error); break; case 'F': case 'f': append_short_attr(p, FIRSTONLY_VAL, buf_end, error); break; default: LM_ERR("unknown " "value <%s> for attribute ORDERING\n",val.s); goto error; } break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for REJECT node: * | attr1_t(2) attr1_val(2) | STATUS attr * [| attr2_t(2) attr2_len(2) attr2_val(2*x)|]? REASON attr (NT) */ static inline int encode_reject_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; unsigned int nr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get the value of the attribute */ get_attr_val( attr->name , val, error); switch(attr->name[0]) { case 'R': case 'r': set_attr_type(p, REASON_ATTR, buf_end, error); val.len++; /* grab also the /0 */ append_str_attr(p, val, buf_end, error); break; case 'S': case 's': set_attr_type(p, STATUS_ATTR, buf_end, error); if (str2int(&val,&nr)==-1) { /*it was a non numeric value */ if (val.len==BUSY_STR_LEN && !strncasecmp(val.s,BUSY_STR,val.len)) { append_short_attr(p, BUSY_VAL, buf_end, error); } else if (val.len==NOTFOUND_STR_LEN && !strncasecmp(val.s,NOTFOUND_STR,val.len)) { append_short_attr(p, NOTFOUND_VAL, buf_end, error); } else if (val.len==ERROR_STR_LEN && !strncasecmp(val.s,ERROR_STR,val.len)) { append_short_attr(p, ERROR_VAL, buf_end, error); } else if (val.len==REJECT_STR_LEN && !strncasecmp(val.s,REJECT_STR,val.len)) { append_short_attr(p, REJECT_VAL, buf_end, error); } else { LM_ERR("bad val. <%s> for STATUS\n",val.s); goto error; } } else if (nr<400 || nr>700) { LM_ERR("bad code <%d> for STATUS\n",nr); goto error; } else { append_short_attr(p, nr, buf_end, error); } break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for REDIRECT node: * | attr1_t(2) attr1_val(2) | STATUS attr * [| attr2_t(2) attr2_len(2) attr2_val(2*x)|]? REASON attr (NT) */ static inline int encode_redirect_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; if (attr->name[0]=='p' || attr->name[0]=='P') { set_attr_type(p, PERMANENT_ATTR, buf_end, error); /* get the value */ get_attr_val( attr->name , val, error); if (val.s[0]=='y' || val.s[0]=='Y') append_short_attr( p, YES_VAL, buf_end, error); else if (val.s[0]=='n' || val.s[0]=='N') append_short_attr( p, NO_VAL, buf_end, error); else { LM_ERR("bad val. <%s> for PERMANENT\n",val.s); goto error; } } else { LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return p-p_orig; error: return -1; } /* Attr. encoding for LOG node: * [| attr1_t(2) attr1_len(2) attr1_val(2*x) |]? NAME attr (NT) * [| attr2_t(2) attr2_len(2) attr2_val(2*x) |]? COMMENT attr (NT) */ static inline int encode_log_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* get the value of the attribute */ get_attr_val( attr->name , val, error); switch (attr->name[0] ) { case 'n': case 'N': if (val.len>MAX_NAME_SIZE) val.len=MAX_NAME_SIZE; set_attr_type(p, NAME_ATTR, buf_end, error); break; case 'c': case 'C': if (val.len>MAX_COMMENT_SIZE) val.len=MAX_COMMENT_SIZE; set_attr_type(p, COMMENT_ATTR, buf_end, error); break; default: LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } /* be sure there is a \0 at the end of string */ val.s[val.len++]=0; append_str_attr(p,val, buf_end, error); } return p-p_orig; error: return -1; } /* Attr. encoding for MAIL node: * | attr1_t(2) attr1_len(2) attr1_val(2*x) | TO_ATTR attr (NNT) * [| attr2_t(2) attr2_len(2) attr2_val(2*x) |]? SUBJECT_ATTR attr (NNT) * [| attr3_t(2) attr3_len(2) attr3_val(2*x) |]? BODY_ATTR attr (NNT) */ static inline int encode_mail_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { /* there is only one attribute -> URL */ if (attr->name[0]!='u' && attr->name[0]!='U') { LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } p = decode_mail_url( p, buf_end, (char*)xmlGetProp(node,attr->name), nr_attr); if (p==0) goto error; } return p-p_orig; error: return -1; } /* Attr. encoding for SUBACTION node: */ static inline int encode_subaction_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; str val; FOR_ALL_ATTR(node,attr) { /* there is only one attribute -> ID */ if ((attr->name[0]|0x20)=='i' && ((attr->name[1]|0x20)=='d') && attr->name[2]==0 ) { /* get the value of the attribute */ get_attr_val( attr->name , val, error); if ((list = append_to_list(list, node_ptr,val.s))==0) { LM_ERR("failed to add " "subaction into list -> pkg_malloc failed?\n"); goto error; } } else { LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } } return 0; error: return -1; } /* Attr. encoding for SUB node: * | attr1_t(2) attr1_val(2) | REF_ATTR attr */ static inline int encode_sub_attr(xmlNodePtr node, char *node_ptr, char *buf_end) { xmlAttrPtr attr; char *p, *p_orig; unsigned char *nr_attr; char *sub_ptr; str val; nr_attr = &(NR_OF_ATTR(node_ptr)); *nr_attr = 0; p = p_orig = ATTR_PTR(node_ptr); FOR_ALL_ATTR(node,attr) { (*nr_attr)++; /* there is only one attribute -> REF */ if ( strcasecmp("ref",(char*)attr->name)!=0 ) { LM_ERR("unknown attribute <%s>\n",attr->name); goto error; } set_attr_type(p, REF_ATTR, buf_end, error); /* get the value of the attribute */ get_attr_val( attr->name , val, error); if ( (sub_ptr=search_the_list(list, val.s))==0 ) { LM_ERR("unable to find declaration " "of subaction <%s>\n",val.s); goto error; } append_short_attr(p,(unsigned short)(node_ptr-sub_ptr),buf_end,error); } return p-p_orig; error: return -1; } /* Returns : -1 - error * >0 - subtree size of the given node */ int encode_node( xmlNodePtr node, char *p, char *p_end) { xmlNodePtr kid; unsigned short sub_tree_size; int attr_size; int kid_size; int foo; /* counting the kids */ for(kid=node->children,foo=0;kid;kid=kid->next) if (kid->type==XML_ELEMENT_NODE) foo++; check_overflow(p,GET_NODE_SIZE(foo),p_end,error); NR_OF_KIDS(p) = foo; /* size of the encoded attributes */ attr_size = 0; /* init the number of attributes */ NR_OF_ATTR(p) = 0; /* encode node name */ switch (node->name[0]) { case 'a':case 'A': switch (node->name[7]) { case 0: NODE_TYPE(p) = ADDRESS_NODE; attr_size = encode_address_attr( node, p, p_end); break; case '-': NODE_TYPE(p) = ADDRESS_SWITCH_NODE; attr_size = encode_address_switch_attr( node, p, p_end); break; default: NODE_TYPE(p) = ANCILLARY_NODE; break; } break; case 'B':case 'b': NODE_TYPE(p) = BUSY_NODE; break; case 'c':case 'C': NODE_TYPE(p) = CPL_NODE; break; case 'd':case 'D': NODE_TYPE(p) = DEFAULT_NODE; break; case 'f':case 'F': NODE_TYPE(p) = FAILURE_NODE; break; case 'i':case 'I': NODE_TYPE(p) = INCOMING_NODE; break; case 'l':case 'L': switch (node->name[2]) { case 'g':case 'G': NODE_TYPE(p) = LOG_NODE; attr_size = encode_log_attr( node, p, p_end); break; case 'o':case 'O': NODE_TYPE(p) = LOOKUP_NODE; attr_size = encode_lookup_attr( node, p, p_end); break; case 'c':case 'C': NODE_TYPE(p) = LOCATION_NODE; attr_size = encode_location_attr( node, p, p_end); break; default: if (node->name[8]) { NODE_TYPE(p) = LANGUAGE_SWITCH_NODE; } else { NODE_TYPE(p) = LANGUAGE_NODE; attr_size = encode_lang_attr( node, p, p_end); } break; } break; case 'm':case 'M': NODE_TYPE(p) = MAIL_NODE; attr_size = encode_mail_attr( node, p, p_end); break; case 'n':case 'N': switch (node->name[3]) { case 'F':case 'f': NODE_TYPE(p) = NOTFOUND_NODE; break; case 'N':case 'n': NODE_TYPE(p) = NOANSWER_NODE; break; default: NODE_TYPE(p) = NOT_PRESENT_NODE; break; } break; case 'o':case 'O': if (node->name[1]=='t' || node->name[1]=='T') { NODE_TYPE(p) = OTHERWISE_NODE; } else { NODE_TYPE(p) = OUTGOING_NODE; } break; case 'p':case 'P': if (node->name[2]=='o' || node->name[2]=='O') { NODE_TYPE(p) = PROXY_NODE; attr_size = encode_proxy_attr( node, p, p_end); } else if (node->name[8]) { NODE_TYPE(p) = PRIORITY_SWITCH_NODE; } else { NODE_TYPE(p) = PRIORITY_NODE; attr_size = encode_priority_attr( node, p, p_end); } break; case 'r':case 'R': switch (node->name[2]) { case 'j':case 'J': NODE_TYPE(p) = REJECT_NODE; attr_size = encode_reject_attr( node, p, p_end); break; case 'm':case 'M': NODE_TYPE(p) = REMOVE_LOCATION_NODE; attr_size = encode_rmvloc_attr( node, p, p_end); break; default: if (node->name[8]) { NODE_TYPE(p) = REDIRECTION_NODE; } else { NODE_TYPE(p) = REDIRECT_NODE; attr_size = encode_redirect_attr( node, p, p_end); } break; } break; case 's':case 'S': switch (node->name[3]) { case 0: NODE_TYPE(p) = SUB_NODE; attr_size = encode_sub_attr( node, p, p_end); break; case 'c':case 'C': NODE_TYPE(p) = SUCCESS_NODE; break; case 'a':case 'A': NODE_TYPE(p) = SUBACTION_NODE; attr_size = encode_subaction_attr( node, p, p_end); break; default: if (node->name[6]) { NODE_TYPE(p) = STRING_SWITCH_NODE; attr_size = encode_string_switch_attr( node, p, p_end); } else { NODE_TYPE(p) = STRING_NODE; attr_size = encode_string_attr( node, p, p_end); } break; } break; case 't':case 'T': if (node->name[4]) { NODE_TYPE(p) = TIME_SWITCH_NODE; attr_size = encode_time_switch_attr( node, p, p_end); } else { NODE_TYPE(p) = TIME_NODE; attr_size = encode_time_attr( node, p, p_end); } break; default: LM_ERR("unknown node <%s>\n",node->name); goto error; } /* compute the total length of the node (including attributes) */ if (attr_size<0) goto error; sub_tree_size = SIMPLE_NODE_SIZE(p) + (unsigned short)attr_size; /* encrypt all the kids */ for(kid = node->children,foo=0;kid;kid=kid->next) { if (kid->type!=XML_ELEMENT_NODE) continue; SET_KID_OFFSET( p, foo, sub_tree_size); kid_size = encode_node( kid, p+sub_tree_size, p_end); if (kid_size<=0) goto error; sub_tree_size += (unsigned short)kid_size; foo++; } return sub_tree_size; error: return -1; } #define BAD_XML "CPL script is not a valid XML document" #define BAD_XML_LEN (sizeof(BAD_XML)-1) #define BAD_CPL "CPL script doesn't respect CPL grammar" #define BAD_CPL_LEN (sizeof(BAD_CPL)-1) #define NULL_CPL "Empty CPL script" #define NULL_CPL_LEN (sizeof(NULL_CPL)-1) #define ENC_ERR "Encoding of the CPL script failed" #define ENC_ERR_LEN (sizeof(ENC_ERR)-1) int encodeCPL( str *xml, str *bin, str *log) { static char buf[ENCONDING_BUFFER_SIZE]; xmlDocPtr doc; xmlNodePtr cur; doc = 0; list = 0; /* reset all the logs (if any) to catch some possible err/warn/notice * from the parser/validater/encoder */ reset_logs(); /* parse the xml */ doc = xmlParseDoc( (unsigned char*)xml->s ); if (!doc) { append_log( 1, MSG_ERR BAD_XML LF, MSG_ERR_LEN+BAD_XML_LEN+LF_LEN); LM_ERR( BAD_XML "\n"); goto error; } /* check the xml against dtd */ if (xmlValidateDtd(&cvp, doc, dtd)!=1) { append_log( 1, MSG_ERR BAD_CPL LF, MSG_ERR_LEN+BAD_CPL_LEN+LF_LEN); LM_ERR( BAD_CPL "\n"); goto error; } cur = xmlDocGetRootElement(doc); if (!cur) { append_log( 1, MSG_ERR NULL_CPL LF, MSG_ERR_LEN+NULL_CPL_LEN+LF_LEN); LM_ERR( NULL_CPL "\n"); goto error; } bin->len = encode_node( cur, buf, buf+ENCONDING_BUFFER_SIZE); if (bin->len<0) { append_log( 1, MSG_ERR ENC_ERR LF, MSG_ERR_LEN+ENC_ERR_LEN+LF_LEN); LM_ERR( ENC_ERR "\n"); goto error; } xmlFreeDoc(doc); if (list) delete_list(list); /* compile the log buffer */ compile_logs( log ); bin->s = buf; return 1; error: if (doc) xmlFreeDoc(doc); if (list) delete_list(list); /* compile the log buffer */ compile_logs( log ); return 0; } /* loads and parse the dtd file; a validating context is created */ int init_CPL_parser( char* DTD_filename ) { dtd = xmlParseDTD( NULL, (unsigned char*)DTD_filename); if (!dtd) { LM_ERR("DTD not parsed successfully\n"); return -1; } cvp.userData = (void *) stderr; cvp.error = (xmlValidityErrorFunc) /*err_print*/ fprintf; cvp.warning = (xmlValidityWarningFunc) /*err_print*/ fprintf; return 1; }