Merge commit 'origin/ser_core_cvs'
authorAndrei Pelinescu-Onciul <andrei@iptel.org>
Fri, 20 Mar 2009 20:47:32 +0000 (21:47 +0100)
committerAndrei Pelinescu-Onciul <andrei@iptel.org>
Fri, 20 Mar 2009 20:47:32 +0000 (21:47 +0100)
* commit 'origin/ser_core_cvs':
  tcp: minor optimization
  sercmd: minor makefile fix
  tcp internal: send timeout is now kept only in ticks
  core: typo fix
  tcp: typo & minor optimization
  Rename Contact parameter select 'method' to 'methods'
  Rename Contact parameter 'method' to 'methods'
  make install: avoid re-linking lib dependent modules
  build system: avoid libraries re-compiling
  fixing missing $ in ser_mysql.sh script
  Debian packaging - fixing path to mysql sql files in my_create.sh script
  The fixup function prototypes of the config variables have been
  Strip, prefix, rewriteuser, ... all the SET_* actions preserve the
  core: check & fix Content-Length when sending on tcp
  tcp: config option for the async write block size
  tcp: config option for the read buffer size
  tcp: dyn. config fix for tcp_con_lifetime
  tcp: diff. connect timeout for async & states cleanup

Conflicts:
Makefile.rules
action.c
cfg.lex
cfg.y
route_struct.h

435 files changed:
.gitignore [new file with mode: 0644]
Makefile.rules
NEWS
action.c
cfg.lex
cfg.y
cfg_core.c
cfg_core.h
config.h
core_cmd.c
db/Makefile [deleted file]
doc/doxygen/main.dox
dprint.c
dprint.h
dset.c
dset.h
flags.c
flags.h
ip_addr.h
lib/README
lib/kmi/Makefile [new file with mode: 0644]
lib/kmi/attr.c [new file with mode: 0644]
lib/kmi/attr.h [new file with mode: 0644]
lib/kmi/fmt.c [new file with mode: 0644]
lib/kmi/fmt.h [new file with mode: 0644]
lib/kmi/mi.c [new file with mode: 0644]
lib/kmi/mi.h [new file with mode: 0644]
lib/kmi/mi_core.c [new file with mode: 0644]
lib/kmi/mi_core.h [new file with mode: 0644]
lib/kmi/tree.c [new file with mode: 0644]
lib/kmi/tree.h [new file with mode: 0644]
lib/srdb1/Makefile [new file with mode: 0644]
lib/srdb1/README [new file with mode: 0644]
lib/srdb1/db.c [new file with mode: 0644]
lib/srdb1/db.h [new file with mode: 0644]
lib/srdb1/db_cap.h [new file with mode: 0644]
lib/srdb1/db_con.h [new file with mode: 0644]
lib/srdb1/db_id.c [new file with mode: 0644]
lib/srdb1/db_id.h [new file with mode: 0644]
lib/srdb1/db_key.h [new file with mode: 0644]
lib/srdb1/db_op.h [new file with mode: 0644]
lib/srdb1/db_pool.c [new file with mode: 0644]
lib/srdb1/db_pool.h [new file with mode: 0644]
lib/srdb1/db_query.c [new file with mode: 0644]
lib/srdb1/db_query.h [new file with mode: 0644]
lib/srdb1/db_res.c [new file with mode: 0644]
lib/srdb1/db_res.h [new file with mode: 0644]
lib/srdb1/db_row.c [new file with mode: 0644]
lib/srdb1/db_row.h [new file with mode: 0644]
lib/srdb1/db_ut.c [new file with mode: 0644]
lib/srdb1/db_ut.h [new file with mode: 0644]
lib/srdb1/db_val.c [new file with mode: 0644]
lib/srdb1/db_val.h [new file with mode: 0644]
lib/srdb1/schema/Makefile [new file with mode: 0644]
lib/srdb1/schema/acc.xml [new file with mode: 0644]
lib/srdb1/schema/address.xml [new file with mode: 0644]
lib/srdb1/schema/aliases.xml [new file with mode: 0644]
lib/srdb1/schema/carrier_name.xml [new file with mode: 0644]
lib/srdb1/schema/carrierfailureroute.xml [new file with mode: 0644]
lib/srdb1/schema/carrierroute.xml [new file with mode: 0644]
lib/srdb1/schema/cpl.xml [new file with mode: 0644]
lib/srdb1/schema/dbaliases.xml [new file with mode: 0644]
lib/srdb1/schema/dialog.xml [new file with mode: 0644]
lib/srdb1/schema/dialplan.xml [new file with mode: 0644]
lib/srdb1/schema/dispatcher.xml [new file with mode: 0644]
lib/srdb1/schema/domain.xml [new file with mode: 0644]
lib/srdb1/schema/domain_name.xml [new file with mode: 0644]
lib/srdb1/schema/domainpolicy.xml [new file with mode: 0644]
lib/srdb1/schema/entities.xml [new file with mode: 0644]
lib/srdb1/schema/globalblacklist.xml [new file with mode: 0644]
lib/srdb1/schema/grp.xml [new file with mode: 0644]
lib/srdb1/schema/gw.xml [new file with mode: 0644]
lib/srdb1/schema/htable.xml [new file with mode: 0644]
lib/srdb1/schema/imc_members.xml [new file with mode: 0644]
lib/srdb1/schema/imc_rooms.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-acc.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-alias_db.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-auth_db.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-avpops.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-carrierroute.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-cpl.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-dialog.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-dialplan.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-dispatcher.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-domain.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-domainpolicy.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-extensions.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-group.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-htable.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-imc.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-lcr.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-msilo.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-pdt.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-permissions.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-presence.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-registrar.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-rls.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-siptrace.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-speeddial.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-standard.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-uri_db.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-userblacklist.xml [new file with mode: 0644]
lib/srdb1/schema/kamailio-usrloc.xml [new file with mode: 0644]
lib/srdb1/schema/lcr.xml [new file with mode: 0644]
lib/srdb1/schema/location.xml [new file with mode: 0644]
lib/srdb1/schema/missed_calls.xml [new file with mode: 0644]
lib/srdb1/schema/pdt.xml [new file with mode: 0644]
lib/srdb1/schema/pr_active_watchers.xml [new file with mode: 0644]
lib/srdb1/schema/pr_presentity.xml [new file with mode: 0644]
lib/srdb1/schema/pr_pua.xml [new file with mode: 0644]
lib/srdb1/schema/pr_watchers.xml [new file with mode: 0644]
lib/srdb1/schema/pr_xcap.xml [new file with mode: 0644]
lib/srdb1/schema/re_grp.xml [new file with mode: 0644]
lib/srdb1/schema/rls_presentity.xml [new file with mode: 0644]
lib/srdb1/schema/rls_watchers.xml [new file with mode: 0644]
lib/srdb1/schema/silo.xml [new file with mode: 0644]
lib/srdb1/schema/sip_trace.xml [new file with mode: 0644]
lib/srdb1/schema/speed_dial.xml [new file with mode: 0644]
lib/srdb1/schema/subscriber.xml [new file with mode: 0644]
lib/srdb1/schema/template.xml [new file with mode: 0644]
lib/srdb1/schema/trusted.xml [new file with mode: 0644]
lib/srdb1/schema/uri.xml [new file with mode: 0644]
lib/srdb1/schema/userblacklist.xml [new file with mode: 0644]
lib/srdb1/schema/usr_preferences.xml [new file with mode: 0644]
lib/srdb1/schema/version.xml [new file with mode: 0644]
lib/srdb2/Makefile [new file with mode: 0644]
lib/srdb2/README [moved from db/README with 100% similarity]
lib/srdb2/db.c [moved from db/db.c with 89% similarity]
lib/srdb2/db.h [moved from db/db.h with 66% similarity]
lib/srdb2/db_cmd.c [moved from db/db_cmd.c with 98% similarity]
lib/srdb2/db_cmd.h [moved from db/db_cmd.h with 100% similarity]
lib/srdb2/db_con.c [moved from db/db_con.c with 98% similarity]
lib/srdb2/db_con.h [moved from db/db_con.h with 100% similarity]
lib/srdb2/db_ctx.c [moved from db/db_ctx.c with 99% similarity]
lib/srdb2/db_ctx.h [moved from db/db_ctx.h with 98% similarity]
lib/srdb2/db_drv.c [moved from db/db_drv.c with 75% similarity]
lib/srdb2/db_drv.h [moved from db/db_drv.h with 98% similarity]
lib/srdb2/db_fld.c [moved from db/db_fld.c with 98% similarity]
lib/srdb2/db_fld.h [moved from db/db_fld.h with 99% similarity]
lib/srdb2/db_gen.c [moved from db/db_gen.c with 100% similarity]
lib/srdb2/db_gen.h [moved from db/db_gen.h with 99% similarity]
lib/srdb2/db_pool.c [moved from db/db_pool.c with 99% similarity]
lib/srdb2/db_pool.h [moved from db/db_pool.h with 99% similarity]
lib/srdb2/db_rec.c [moved from db/db_rec.c with 97% similarity]
lib/srdb2/db_rec.h [moved from db/db_rec.h with 100% similarity]
lib/srdb2/db_res.c [moved from db/db_res.c with 97% similarity]
lib/srdb2/db_res.h [moved from db/db_res.h with 100% similarity]
lib/srdb2/db_uri.c [moved from db/db_uri.c with 97% similarity]
lib/srdb2/db_uri.h [moved from db/db_uri.h with 98% similarity]
lib/srdb2/schema/Makefile [moved from db/schema/Makefile with 100% similarity]
lib/srdb2/schema/acc.xml [moved from db/schema/acc.xml with 100% similarity]
lib/srdb2/schema/aliases.xml [moved from db/schema/aliases.xml with 100% similarity]
lib/srdb2/schema/attr_types.xml [moved from db/schema/attr_types.xml with 100% similarity]
lib/srdb2/schema/contact_attrs.xml [moved from db/schema/contact_attrs.xml with 100% similarity]
lib/srdb2/schema/cpl.xml [moved from db/schema/cpl.xml with 100% similarity]
lib/srdb2/schema/credentials.xml [moved from db/schema/credentials.xml with 100% similarity]
lib/srdb2/schema/customers.xml [moved from db/schema/customers.xml with 100% similarity]
lib/srdb2/schema/domain.xml [moved from db/schema/domain.xml with 100% similarity]
lib/srdb2/schema/domain_attrs.xml [moved from db/schema/domain_attrs.xml with 100% similarity]
lib/srdb2/schema/domain_settings.xml [moved from db/schema/domain_settings.xml with 100% similarity]
lib/srdb2/schema/entities.xml [moved from db/schema/entities.xml with 100% similarity]
lib/srdb2/schema/extra_attrs.xml [moved from db/schema/extra_attrs.xml with 100% similarity]
lib/srdb2/schema/global_attrs.xml [moved from db/schema/global_attrs.xml with 100% similarity]
lib/srdb2/schema/grp.xml [moved from db/schema/grp.xml with 100% similarity]
lib/srdb2/schema/gw.xml [moved from db/schema/gw.xml with 100% similarity]
lib/srdb2/schema/gw_grp.xml [moved from db/schema/gw_grp.xml with 100% similarity]
lib/srdb2/schema/i18n-cs.xml [moved from db/schema/i18n-cs.xml with 100% similarity]
lib/srdb2/schema/i18n-en.xml [moved from db/schema/i18n-en.xml with 100% similarity]
lib/srdb2/schema/i18n.xml [moved from db/schema/i18n.xml with 100% similarity]
lib/srdb2/schema/ipmatch.xml [moved from db/schema/ipmatch.xml with 100% similarity]
lib/srdb2/schema/lcr.xml [moved from db/schema/lcr.xml with 100% similarity]
lib/srdb2/schema/location.xml [moved from db/schema/location.xml with 100% similarity]
lib/srdb2/schema/missed_calls.xml [moved from db/schema/missed_calls.xml with 100% similarity]
lib/srdb2/schema/offline_winfo.xml [moved from db/schema/offline_winfo.xml with 100% similarity]
lib/srdb2/schema/pdt.xml [moved from db/schema/pdt.xml with 100% similarity]
lib/srdb2/schema/phonebook.xml [moved from db/schema/phonebook.xml with 100% similarity]
lib/srdb2/schema/presentity.xml [moved from db/schema/presentity.xml with 100% similarity]
lib/srdb2/schema/presentity_contact.xml [moved from db/schema/presentity_contact.xml with 100% similarity]
lib/srdb2/schema/presentity_extensions.xml [moved from db/schema/presentity_extensions.xml with 100% similarity]
lib/srdb2/schema/presentity_notes.xml [moved from db/schema/presentity_notes.xml with 100% similarity]
lib/srdb2/schema/rls_subscription.xml [moved from db/schema/rls_subscription.xml with 100% similarity]
lib/srdb2/schema/rls_vs.xml [moved from db/schema/rls_vs.xml with 100% similarity]
lib/srdb2/schema/rls_vs_names.xml [moved from db/schema/rls_vs_names.xml with 100% similarity]
lib/srdb2/schema/sd_attrs.xml [moved from db/schema/sd_attrs.xml with 100% similarity]
lib/srdb2/schema/ser.xml [moved from db/schema/ser.xml with 100% similarity]
lib/srdb2/schema/silo.xml [moved from db/schema/silo.xml with 100% similarity]
lib/srdb2/schema/speed_dial.xml [moved from db/schema/speed_dial.xml with 100% similarity]
lib/srdb2/schema/trusted.xml [moved from db/schema/trusted.xml with 100% similarity]
lib/srdb2/schema/tuple_extensions.xml [moved from db/schema/tuple_extensions.xml with 100% similarity]
lib/srdb2/schema/tuple_notes.xml [moved from db/schema/tuple_notes.xml with 100% similarity]
lib/srdb2/schema/uri.xml [moved from db/schema/uri.xml with 100% similarity]
lib/srdb2/schema/uri_attrs.xml [moved from db/schema/uri_attrs.xml with 100% similarity]
lib/srdb2/schema/user_attrs.xml [moved from db/schema/user_attrs.xml with 100% similarity]
lib/srdb2/schema/watcherinfo.xml [moved from db/schema/watcherinfo.xml with 100% similarity]
lib/trie/Makefile [new file with mode: 0644]
lib/trie/dtrie.c [new file with mode: 0644]
lib/trie/dtrie.h [new file with mode: 0644]
lvalue.c [new file with mode: 0644]
lvalue.h [new file with mode: 0644]
main.c
md5utils.c
md5utils.h
mem/mem.h
mi/mi.h [new file with mode: 0644]
mod_fix.c [new file with mode: 0644]
mod_fix.h [new file with mode: 0644]
modparam.c
modules/carrierroute/Makefile [new file with mode: 0644]
modules/carrierroute/README [new file with mode: 0644]
modules/carrierroute/carrierroute.c [new file with mode: 0644]
modules/carrierroute/carrierroute.h [new file with mode: 0644]
modules/carrierroute/cr_carrier.c [new file with mode: 0644]
modules/carrierroute/cr_carrier.h [new file with mode: 0644]
modules/carrierroute/cr_config.c [new file with mode: 0644]
modules/carrierroute/cr_config.h [new file with mode: 0644]
modules/carrierroute/cr_data.c [new file with mode: 0644]
modules/carrierroute/cr_data.h [new file with mode: 0644]
modules/carrierroute/cr_db.c [new file with mode: 0644]
modules/carrierroute/cr_db.h [new file with mode: 0644]
modules/carrierroute/cr_domain.c [new file with mode: 0644]
modules/carrierroute/cr_domain.h [new file with mode: 0644]
modules/carrierroute/cr_fifo.c [new file with mode: 0644]
modules/carrierroute/cr_fifo.h [new file with mode: 0644]
modules/carrierroute/cr_fixup.c [new file with mode: 0644]
modules/carrierroute/cr_fixup.h [new file with mode: 0644]
modules/carrierroute/cr_func.c [new file with mode: 0644]
modules/carrierroute/cr_func.h [new file with mode: 0644]
modules/carrierroute/cr_map.c [new file with mode: 0644]
modules/carrierroute/cr_map.h [new file with mode: 0644]
modules/carrierroute/cr_rule.c [new file with mode: 0644]
modules/carrierroute/cr_rule.h [new file with mode: 0644]
modules/carrierroute/db_carrierroute.c [new file with mode: 0644]
modules/carrierroute/db_carrierroute.h [new file with mode: 0644]
modules/carrierroute/doc/carrierroute.xml [new file with mode: 0644]
modules/carrierroute/doc/carrierroute_admin.xml [new file with mode: 0644]
modules/carrierroute/doc/carrierroute_db.xml [new file with mode: 0644]
modules/carrierroute/prime_hash.c [new file with mode: 0644]
modules/carrierroute/prime_hash.h [new file with mode: 0644]
modules/db_berkeley/Makefile [new file with mode: 0644]
modules/db_berkeley/README [new file with mode: 0644]
modules/db_berkeley/bdb_cmd.c [new file with mode: 0644]
modules/db_berkeley/bdb_cmd.h [new file with mode: 0644]
modules/db_berkeley/bdb_con.c [new file with mode: 0644]
modules/db_berkeley/bdb_con.h [new file with mode: 0644]
modules/db_berkeley/bdb_fld.c [new file with mode: 0644]
modules/db_berkeley/bdb_fld.h [new file with mode: 0644]
modules/db_berkeley/bdb_lib.c [new file with mode: 0644]
modules/db_berkeley/bdb_lib.h [new file with mode: 0644]
modules/db_berkeley/bdb_mod.c [new file with mode: 0644]
modules/db_berkeley/bdb_mod.h [new file with mode: 0644]
modules/db_berkeley/bdb_res.c [new file with mode: 0644]
modules/db_berkeley/bdb_res.h [new file with mode: 0644]
modules/db_berkeley/bdb_uri.c [new file with mode: 0644]
modules/db_berkeley/bdb_uri.h [new file with mode: 0644]
modules/db_berkeley/doc/db_berkeley.xml [new file with mode: 0644]
modules/db_berkeley/doc/db_berkeley_admin.xml [new file with mode: 0644]
modules/db_berkeley/km_bdb_lib.c [new file with mode: 0644]
modules/db_berkeley/km_bdb_lib.h [new file with mode: 0644]
modules/db_berkeley/km_bdb_mi.c [new file with mode: 0644]
modules/db_berkeley/km_bdb_mi.h [new file with mode: 0644]
modules/db_berkeley/km_bdb_res.c [new file with mode: 0644]
modules/db_berkeley/km_bdb_res.h [new file with mode: 0644]
modules/db_berkeley/km_bdb_util.c [new file with mode: 0644]
modules/db_berkeley/km_bdb_util.h [new file with mode: 0644]
modules/db_berkeley/km_bdb_val.c [new file with mode: 0644]
modules/db_berkeley/km_bdb_val.h [new file with mode: 0644]
modules/db_berkeley/km_db_berkeley.c [new file with mode: 0644]
modules/db_berkeley/km_db_berkeley.h [new file with mode: 0644]
modules/db_berkeley/km_doc/db_berkeley.xml [new file with mode: 0644]
modules/db_berkeley/km_doc/db_berkeley_admin.xml [new file with mode: 0644]
modules/db_flatstore/Makefile [new file with mode: 0644]
modules/db_flatstore/README [new file with mode: 0644]
modules/db_flatstore/doc/Makefile [new file with mode: 0644]
modules/db_flatstore/doc/flatstore.xml [new file with mode: 0644]
modules/db_flatstore/doc/functions.xml [new file with mode: 0644]
modules/db_flatstore/doc/params.xml [new file with mode: 0644]
modules/db_flatstore/flat_cmd.c [new file with mode: 0644]
modules/db_flatstore/flat_cmd.h [new file with mode: 0644]
modules/db_flatstore/flat_con.c [new file with mode: 0644]
modules/db_flatstore/flat_con.h [new file with mode: 0644]
modules/db_flatstore/flat_rpc.c [new file with mode: 0644]
modules/db_flatstore/flat_rpc.h [new file with mode: 0644]
modules/db_flatstore/flat_uri.c [new file with mode: 0644]
modules/db_flatstore/flat_uri.h [new file with mode: 0644]
modules/db_flatstore/flatstore_mod.c [new file with mode: 0644]
modules/db_flatstore/flatstore_mod.h [new file with mode: 0644]
modules/db_flatstore/km_doc/db_flatstore.xml [new file with mode: 0644]
modules/db_flatstore/km_doc/db_flatstore_admin.xml [new file with mode: 0644]
modules/db_flatstore/km_doc/db_flatstore_devel.xml [new file with mode: 0644]
modules/db_flatstore/km_flat_con.c [new file with mode: 0644]
modules/db_flatstore/km_flat_con.h [new file with mode: 0644]
modules/db_flatstore/km_flat_id.c [new file with mode: 0644]
modules/db_flatstore/km_flat_id.h [new file with mode: 0644]
modules/db_flatstore/km_flat_mi.c [new file with mode: 0644]
modules/db_flatstore/km_flat_mi.h [new file with mode: 0644]
modules/db_flatstore/km_flat_pool.c [new file with mode: 0644]
modules/db_flatstore/km_flat_pool.h [new file with mode: 0644]
modules/db_flatstore/km_flatstore.c [new file with mode: 0644]
modules/db_flatstore/km_flatstore.h [new file with mode: 0644]
modules/db_flatstore/km_flatstore_mod.c [new file with mode: 0644]
modules/db_flatstore/km_flatstore_mod.h [new file with mode: 0644]
modules/db_flatstore/ser-flatstore.cfg [new file with mode: 0644]
modules/db_flatstore/todo.txt [new file with mode: 0644]
modules/db_mysql/Makefile [new file with mode: 0644]
modules/db_mysql/doc/mysql_parser.dia [new file with mode: 0644]
modules/db_mysql/km_README [new file with mode: 0644]
modules/db_mysql/km_db_mysql.c [new file with mode: 0644]
modules/db_mysql/km_db_mysql.h [new file with mode: 0644]
modules/db_mysql/km_dbase.c [new file with mode: 0644]
modules/db_mysql/km_dbase.h [new file with mode: 0644]
modules/db_mysql/km_doc/db_mysql.xml [new file with mode: 0644]
modules/db_mysql/km_doc/db_mysql_admin.xml [new file with mode: 0644]
modules/db_mysql/km_doc/db_mysql_parser.dia [new file with mode: 0644]
modules/db_mysql/km_my_con.c [new file with mode: 0644]
modules/db_mysql/km_my_con.h [new file with mode: 0644]
modules/db_mysql/km_res.c [new file with mode: 0644]
modules/db_mysql/km_res.h [new file with mode: 0644]
modules/db_mysql/km_row.c [new file with mode: 0644]
modules/db_mysql/km_row.h [new file with mode: 0644]
modules/db_mysql/km_val.c [new file with mode: 0644]
modules/db_mysql/km_val.h [new file with mode: 0644]
modules/db_mysql/my_cmd.c [new file with mode: 0644]
modules/db_mysql/my_cmd.h [new file with mode: 0644]
modules/db_mysql/my_con.c [new file with mode: 0644]
modules/db_mysql/my_con.h [new file with mode: 0644]
modules/db_mysql/my_fld.c [new file with mode: 0644]
modules/db_mysql/my_fld.h [new file with mode: 0644]
modules/db_mysql/my_res.c [new file with mode: 0644]
modules/db_mysql/my_res.h [new file with mode: 0644]
modules/db_mysql/my_uri.c [new file with mode: 0644]
modules/db_mysql/my_uri.h [new file with mode: 0644]
modules/db_mysql/mysql_mod.c [new file with mode: 0644]
modules/db_mysql/mysql_mod.h [new file with mode: 0644]
modules/db_postgres/Makefile [new file with mode: 0644]
modules/db_postgres/README [new file with mode: 0644]
modules/db_postgres/km_db_postgres.c [new file with mode: 0644]
modules/db_postgres/km_db_postgres.h [new file with mode: 0644]
modules/db_postgres/km_dbase.c [new file with mode: 0644]
modules/db_postgres/km_dbase.h [new file with mode: 0644]
modules/db_postgres/km_doc/db_postgres.xml [new file with mode: 0644]
modules/db_postgres/km_doc/db_postgres_admin.xml [new file with mode: 0644]
modules/db_postgres/km_pg_con.c [new file with mode: 0644]
modules/db_postgres/km_pg_con.h [new file with mode: 0644]
modules/db_postgres/km_pg_type.h [new file with mode: 0644]
modules/db_postgres/km_res.c [new file with mode: 0644]
modules/db_postgres/km_res.h [new file with mode: 0644]
modules/db_postgres/km_val.c [new file with mode: 0644]
modules/db_postgres/km_val.h [new file with mode: 0644]
modules/db_postgres/pg_cmd.c [new file with mode: 0644]
modules/db_postgres/pg_cmd.h [new file with mode: 0644]
modules/db_postgres/pg_con.c [new file with mode: 0644]
modules/db_postgres/pg_con.h [new file with mode: 0644]
modules/db_postgres/pg_fld.c [new file with mode: 0644]
modules/db_postgres/pg_fld.h [new file with mode: 0644]
modules/db_postgres/pg_mod.c [new file with mode: 0644]
modules/db_postgres/pg_mod.h [new file with mode: 0644]
modules/db_postgres/pg_oid.c [new file with mode: 0644]
modules/db_postgres/pg_oid.h [new file with mode: 0644]
modules/db_postgres/pg_res.c [new file with mode: 0644]
modules/db_postgres/pg_res.h [new file with mode: 0644]
modules/db_postgres/pg_sql.c [new file with mode: 0644]
modules/db_postgres/pg_sql.h [new file with mode: 0644]
modules/db_postgres/pg_uri.c [new file with mode: 0644]
modules/db_postgres/pg_uri.h [new file with mode: 0644]
modules/tm/Makefile
modules/tm/config.c
modules/tm/config.h
modules/tm/dlg.h
modules/tm/doc/params.xml
modules/tm/h_table.h
modules/tm/sip_msg.c
modules/tm/t_funcs.c
modules/tm/t_fwd.c
modules/tm/t_lookup.c
modules/tm/t_msgbuilder.c
modules/tm/t_reply.c
modules/tm/t_reply.h
modules/tm/tm.c
modules/tm/tm_load.c
modules/tm/tm_load.h
modules/tm/uac.c
msg_translator.c
onsend.h
parser/case_path.h [new file with mode: 0644]
parser/hf.c
parser/hf.h
parser/keys.h
parser/msg_parser.c
parser/msg_parser.h
parser/parse_cseq.c
parser/parse_cseq.h
parser/parse_diversion.c
parser/parse_diversion.h
parser/parse_event.c
parser/parse_event.h
parser/parse_from.h
parser/parse_hname2.c
parser/parse_methods.c
parser/parse_methods.h
parser/parse_param.c
parser/parse_param.h
parser/parse_to.c
parser/parse_to.h
pvapi.c [new file with mode: 0644]
pvar.h [new file with mode: 0644]
re.c
re.h
receive.c
route.c
route.h
route_struct.c
route_struct.h
rvalue.c [new file with mode: 0644]
rvalue.h [new file with mode: 0644]
scripts/kam_to_sr.sh [new file with mode: 0755]
scripts/ser_to_sr.sh [new file with mode: 0755]
select.c
select_core.c
socket_info.c
socket_info.h
sr_compat.c [new file with mode: 0644]
sr_compat.h [new file with mode: 0644]
sr_module.c
sr_module.h
statistics.h [new file with mode: 0644]
str.h
switch.c [new file with mode: 0644]
switch.h [new file with mode: 0644]
tags.h
timer_proc.c [new file with mode: 0644]
timer_proc.h [new file with mode: 0644]
usr_avp.c
usr_avp.h
ut.c
ut.h

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..c3a6ef0
--- /dev/null
@@ -0,0 +1,32 @@
+# ignore autogenerated files
+cfg.tab.c
+cfg.tab.h
+lex.yy.c
+librpath.lst
+makecfg.lst
+config.mak
+modules.lst
+# ignore dependency files
+*.d
+# ignore binary files and objects
+ser
+*.so
+*.so.*
+*.o
+# archives
+*.tar.gz
+*.deb
+*.rpm
+# tags
+tags
+TAGS
+# vi swaps
+.*.swp
+# various other binaries
+test/udp_flood
+utils/gen_ha1/gen_ha1
+utils/sercmd/sercmd
+# Emacs backup files
+*~
+# Emacs file locks
+.#*
index af51b08..8767ec0 100644 (file)
 # (if not rebuild everything)
 ifeq (,$(filter $(nodep_targets),$(MAKECMDGOALS)))
 -include makecfg.lst
-ifneq ($(strip $(C_DEFS) $(DEFS)), $(strip $(CFG_DEFS)))
-#$(warning different defs: <$(strip $(C_DEFS) $(DEFS))> != )
+# if trying  to build a lib automatically and the lib is already compiled,
+# don't rebuild it if the only differences in DEFS or INCLUDES are covered
+# by LIB_NOREBUILD_DEFS/LIB_NOREBUILD_INCLUDES
+LIB_NOREBUILD_DEFS=
+
+# don't rebuild if the differences are covered by NOREBUILD_DEFS or 
+# NOREBUILD_INCLUDES
+ifneq ($(strip $(filter-out $(NOREBUILD_DEFS),\
+               $(C_DEFS) $(DEFS))),$(strip $(CFG_DEFS)))
+#$(warning different defs: <$(strip $(DEFS))> != )
 #$(warning               : <$(strip $(CFG_DEFS))>)
 $(shell rm -f makecfg.lst)
 endif
-ifneq ($(strip $(C_INCLUDES) $(INCLUDES)), $(strip $(CFG_INCLUDES)))
+ifneq ($(strip $(filter-out $(NOREBUILD_INCLUDES),\
+                       $(C_INCLUDES) $(INCLUDES))),$(strip $(CFG_INCLUDES)))
 $(shell rm -f makecfg.lst)
 endif
 endif
@@ -99,7 +108,9 @@ ALL_LIBS+=$(foreach l, $(SER_LIBS), -L$(dir $l) -l$(notdir $l))
 $(NAME): librpath.lst $(SER_LIBS_DEPS)
 
 $(SER_LIBS_DEPS): FORCE
-       $(MAKE) -wC $(dir $@)  compile_for_install=$(lib_compile_for_install)
+       @$(MAKE) -wC $(dir $@)  compile_for_install=$(lib_compile_for_install) \
+               NOREBUILD_DEFS="$(NOREBUILD_DEFS) $(LIB_NOREBUILD_DEFS)" \
+               NOREBUILD_INCLUDES="$(NOREBUILD_INCLUDES) $(LIB_NOREBUILD_INCLUDES)"
 
 .PHONY: FORCE
 FORCE:
@@ -138,10 +149,11 @@ librpath.lst: $(ALLDEP)
        @echo LIB_RPATH_LST:=$(SER_RPATH_LST) >librpath.lst
 
 makecfg.lst:
-       @echo CFG_DEFS:=\
-               $(subst ',\', $(subst ",\", $(strip $(C_DEFS) $(DEFS)))) >>$@
-       @echo CFG_INCLUDES:=\
-               $(subst ',\', $(subst ",\", $(strip $(C_INCLUDES) $(INCLUDES)))) >>$@
+       @echo CFG_DEFS:=$(subst ',\', $(subst ",\",$(strip \
+                       $(filter-out $(NOREBUILD_DEFS), $(C_DEFS) $(DEFS))))) >>$@
+       @echo CFG_INCLUDES:=$(subst ',\', $(subst ",\",$(strip \
+                       $(filter-out $(NOREBUILD_INCLUDES),\
+                               $(C_INCLUDES) $(INCLUDES))))) >>$@
 .PHONY: all
 all: $(NAME) modules
 
diff --git a/NEWS b/NEWS
index abe35ea..d3d6418 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,8 +1,28 @@
-Release notes for SIP Express Router (ser)
+Release notes for SIP Router (sr)
 ***********************************************
 
 $Id$
 
+sip-router changes
+
+core:
+  - support for dual module interfaces: ser and kamailio
+config script changes:
+  - script mode can be switched between ser compatible, kamailio compatible
+    and max compatibility (compatible with both as much as possible), using
+      #!SER
+      #!KAMAILIO
+      #!OPENSER
+      #!ALL
+      #!MAXCOMPAT
+    where #!KAMAILIO is equivalent with #!OPENSER and #!ALL with #!MAXCOMPAT
+  - support for kamailio style pvars
+  - C-like switch()/case (integer only)
+  - while()
+  - max_while_loops - maximum iterations allowed for a while, can be changed
+    at runtime. Default 100.
+
+
 
 
 2.1.0 changes
index b35a543..82812d7 100644 (file)
--- a/action.c
+++ b/action.c
@@ -46,6 +46,8 @@
  *              (andrei)
  *  2007-06-14  run_actions & do_action need a ctx or handle now, no more
  *               static vars (andrei)
+ *  2008-11-18  support for variable parameter module functions (andrei)
+ *  2008-12-03  use lvalues/rvalues for assignments (andrei)
  *  2008-12-17  added UDP_MTU_TRY_PROTO_T (andrei)
  */
 
@@ -63,6 +65,7 @@
 #include "parser/msg_parser.h"
 #include "parser/parse_uri.h"
 #include "ut.h"
+#include "lvalue.h"
 #include "sr_module.h"
 #include "mem/mem.h"
 #include "globals.h"
@@ -75,6 +78,7 @@
 #ifdef USE_SCTP
 #include "sctp_server.h"
 #endif
+#include "switch.h"
 
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -102,14 +106,22 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
        struct dest_info dst;
        char* tmp;
        char *new_uri, *end, *crt;
+       void* f;
        int len;
        int user;
        struct sip_uri uri, next_hop;
        struct sip_uri *u;
        unsigned short port;
-       unsigned short flags;
-       int_str name, value;
        str* dst_host;
+       int i, flags;
+       struct switch_cond_table* sct;
+       struct switch_jmp_table*  sjt;
+       struct rval_expr* rve;
+       struct match_cond_table* mct;
+       struct rvalue* rv;
+       struct rvalue* rv1;
+       struct rval_cache c1;
+       str s;
        int orig_p2t;
 
        /* reset the value of error to E_UNSPEC so avoid unknowledgable
@@ -304,7 +316,8 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                ret=E_BUG;
                                break;
                        }
-                       LOG_(a->val[0].u.number, "<script>: ", "%s", a->val[1].u.string);
+                       LOG_(DEFAULT_FACILITY, a->val[0].u.number, "<script>: ", "%s", 
+                                a->val[1].u.string);
                        ret=1;
                        break;
 
@@ -448,7 +461,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                        /*ret=((ret=run_actions(rlist[a->val[0].u.number], msg))<0)?ret:1;*/
                        ret=run_actions(h, main_rt.rlist[a->val[0].u.number], msg);
                        h->last_retcode=ret;
-                       h->run_flags&=~RETURN_R_F; /* absorb returns */
+                       h->run_flags&=~(RETURN_R_F|BREAK_R_F); /* absorb return & break */
                        break;
                case EXEC_T:
                        if (a->val[0].type!=STRING_ST){
@@ -477,6 +490,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                case SET_HOST_T:
                case SET_HOSTPORT_T:
                case SET_HOSTPORTTRANS_T:
+               case SET_HOSTALL_T:
                case SET_USER_T:
                case SET_USERPASS_T:
                case SET_PORT_T:
@@ -688,6 +702,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                /* host */
                                if ((a->type==SET_HOST_T)
                                                || (a->type==SET_HOSTPORT_T)
+                                               || (a->type==SET_HOSTALL_T)
                                                || (a->type==SET_HOSTPORTTRANS_T)
                                ) {
                                        tmp=a->val[0].u.string;
@@ -710,6 +725,8 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                        if(crt+len>end) goto error_uri;
                                        memcpy(crt,tmp,len);crt+=len;
                                }
+                               if(a->type==SET_HOSTALL_T)
+                                       goto done_seturi;
                                /* port */
                                if ((a->type==SET_HOSTPORT_T)
                                                || (a->type==SET_HOSTPORTTRANS_T))
@@ -777,6 +794,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                        *crt='?'; crt++;
                                        memcpy(crt,tmp,len);crt+=len;
                                }
+       done_seturi:
                                *crt=0; /* null terminate the thing */
                                /* copy it to the msg */
                                if (msg->new_uri.s) pkg_free(msg->new_uri.s);
@@ -804,7 +822,8 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                                ret=0;
                                                break;
                                        }
-                                       h->run_flags &= ~RETURN_R_F; /* catch returns in expr */
+                                       h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return &
+                                                                                                                           break in expr*/
                                        ret=1;  /*default is continue */
                                        if (v>0) {
                                                if ((a->val[1].type==ACTIONS_ST)&&a->val[1].u.data){
@@ -818,17 +837,274 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                }
                        break;
                case MODULE_T:
-                       if ( a->val[0].type==MODEXP_ST && a->val[0].u.data && ((cmd_export_t*)a->val[0].u.data)->function ){
-                               ret=((cmd_export_t*)a->val[0].u.data)->function(msg,
-                                       (char*)a->val[2].u.data,
-                                       (char*)a->val[3].u.data
-                               );
+                       if ( a->val[0].type==MODEXP_ST && a->val[0].u.data && 
+                                       (f=((union cmd_export_u*)a->val[0].u.data)->c.function)){
+                               ret=((cmd_function)f)(msg,
+                                                                               (char*)a->val[2].u.data,
+                                                                               (char*)a->val[3].u.data
+                                                                       );
                                if (ret==0) h->run_flags|=EXIT_R_F;
                                h->last_retcode=ret;
                        } else {
                                LOG(L_CRIT,"BUG: do_action: bad module call\n");
                        }
                        break;
+               /* instead of using the parameter number, we use different names
+                * for calls to functions with 3, 4, 5, 6 or variable number of
+                * parameters due to performance reasons */
+               case MODULE3_T:
+                       if ( a->val[0].type==MODEXP_ST && a->val[0].u.data && 
+                                       (f=((union cmd_export_u*)a->val[0].u.data)->c.function)){
+                               ret=((cmd_function3)f)(msg,
+                                                                               (char*)a->val[2].u.data,
+                                                                               (char*)a->val[3].u.data,
+                                                                               (char*)a->val[4].u.data
+                                                                       );
+                               if (ret==0) h->run_flags|=EXIT_R_F;
+                               h->last_retcode=ret;
+                       } else {
+                               LOG(L_CRIT,"BUG: do_action: bad module call\n");
+                       }
+                       break;
+               case MODULE4_T:
+                       if ( a->val[0].type==MODEXP_ST && a->val[0].u.data && 
+                                       (f=((union cmd_export_u*)a->val[0].u.data)->c.function)){
+                               ret=((cmd_function4)f)(msg,
+                                                                               (char*)a->val[2].u.data,
+                                                                               (char*)a->val[3].u.data,
+                                                                               (char*)a->val[4].u.data,
+                                                                               (char*)a->val[5].u.data
+                                                                       );
+                               if (ret==0) h->run_flags|=EXIT_R_F;
+                               h->last_retcode=ret;
+                       } else {
+                               LOG(L_CRIT,"BUG: do_action: bad module call\n");
+                       }
+                       break;
+               case MODULE5_T:
+                       if ( a->val[0].type==MODEXP_ST && a->val[0].u.data && 
+                                       (f=((union cmd_export_u*)a->val[0].u.data)->c.function)){
+                               ret=((cmd_function5)f)(msg,
+                                                                               (char*)a->val[2].u.data,
+                                                                               (char*)a->val[3].u.data,
+                                                                               (char*)a->val[4].u.data,
+                                                                               (char*)a->val[5].u.data,
+                                                                               (char*)a->val[6].u.data
+                                                                       );
+                               if (ret==0) h->run_flags|=EXIT_R_F;
+                               h->last_retcode=ret;
+                       } else {
+                               LOG(L_CRIT,"BUG: do_action: bad module call\n");
+                       }
+                       break;
+               case MODULE6_T:
+                       if ( a->val[0].type==MODEXP_ST && a->val[0].u.data && 
+                                       (f=((union cmd_export_u*)a->val[0].u.data)->c.function)){
+                               ret=((cmd_function6)f)(msg,
+                                                                               (char*)a->val[2].u.data,
+                                                                               (char*)a->val[3].u.data,
+                                                                               (char*)a->val[4].u.data,
+                                                                               (char*)a->val[5].u.data,
+                                                                               (char*)a->val[6].u.data,
+                                                                               (char*)a->val[7].u.data
+                                                                       );
+                               if (ret==0) h->run_flags|=EXIT_R_F;
+                               h->last_retcode=ret;
+                       } else {
+                               LOG(L_CRIT,"BUG: do_action: bad module call\n");
+                       }
+                       break;
+               case MODULEX_T:
+                       if ( a->val[0].type==MODEXP_ST && a->val[0].u.data && 
+                                       (f=((union cmd_export_u*)a->val[0].u.data)->c.function)){
+                               ret=((cmd_function_var)f)(msg,
+                                                                                       a->val[1].u.number,
+                                                                                       &a->val[2]
+                                                                               );
+                               if (ret==0) h->run_flags|=EXIT_R_F;
+                               h->last_retcode=ret;
+                       } else {
+                               LOG(L_CRIT,"BUG: do_action: bad module call\n");
+                       }
+                       break;
+               case EVAL_T:
+                       /* only eval the expression to account for possible
+                          side-effect */
+                       rval_expr_eval_int(h, msg, &v,
+                                       (struct rval_expr*)a->val[0].u.data);
+                       if (h->run_flags & EXIT_R_F){
+                               ret=0;
+                               break;
+                       }
+                       h->run_flags &= ~RETURN_R_F|BREAK_R_F; /* catch return & break in
+                                                                                                         expr */
+                       ret=1; /* default is continue */
+                       break;
+               case SWITCH_COND_T:
+                       sct=(struct switch_cond_table*)a->val[1].u.data;
+                       if (unlikely( rval_expr_eval_int(h, msg, &v,
+                                                                       (struct rval_expr*)a->val[0].u.data) <0)){
+                               /* handle error in expression => use default */
+                               ret=-1;
+                               goto sw_cond_def;
+                       }
+                       if (h->run_flags & EXIT_R_F){
+                               ret=0;
+                               break;
+                       }
+                       h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
+                                                                                                           in expr */
+                       ret=1; /* default is continue */
+                       for(i=0; i<sct->n; i++)
+                               if (sct->cond[i]==v){
+                                       if (likely(sct->jump[i])){
+                                               ret=run_actions(h, sct->jump[i], msg);
+                                               h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+                                                                                                          returns passthrough */
+                                       }
+                                       goto skip;
+                               }
+sw_cond_def:
+                       if (sct->def){
+                               ret=run_actions(h, sct->def, msg);
+                               h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+                                                                                          returns passthrough */
+                       }
+                       break;
+               case SWITCH_JT_T:
+                       sjt=(struct switch_jmp_table*)a->val[1].u.data;
+                       if (unlikely( rval_expr_eval_int(h, msg, &v,
+                                                                       (struct rval_expr*)a->val[0].u.data) <0)){
+                               /* handle error in expression => use default */
+                               ret=-1;
+                               goto sw_jt_def;
+                       }
+                       if (h->run_flags & EXIT_R_F){
+                               ret=0;
+                               break;
+                       }
+                       h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
+                                                                                                           in expr */
+                       ret=1; /* default is continue */
+                       if (likely(v >= sjt->first && v <= sjt->last)){
+                               if (likely(sjt->tbl[v - sjt->first])){
+                                       ret=run_actions(h, sjt->tbl[v - sjt->first], msg);
+                                       h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+                                                                                                  returns passthrough */
+                               }
+                               break; 
+                       }else{
+                               for(i=0; i<sjt->rest.n; i++)
+                                       if (sjt->rest.cond[i]==v){
+                                               if (likely(sjt->rest.jump[i])){
+                                                       ret=run_actions(h, sjt->rest.jump[i], msg);
+                                                       h->run_flags &= ~BREAK_R_F; /* catch breaks, but 
+                                                                                                                  let returns pass */
+                                               }
+                                               goto skip;
+                                       }
+                       }
+                       /* not found => try default */
+sw_jt_def:
+                       if (sjt->rest.def){
+                               ret=run_actions(h, sjt->rest.def, msg);
+                               h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+                                                                                          returns passthrough */
+                       }
+                       break;
+               case BLOCK_T:
+                       if (likely(a->val[0].u.data)){
+                               ret=run_actions(h, (struct action*)a->val[0].u.data, msg);
+                               h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+                                                                                          returns passthrough */
+                       }
+                       break;
+               case MATCH_COND_T:
+                       mct=(struct match_cond_table*)a->val[1].u.data;
+                       rval_cache_init(&c1);
+                       rv=0;
+                       rv1=0;
+                       ret=rval_expr_eval_rvint(h, msg, &rv, &v, 
+                                                                       (struct rval_expr*)a->val[0].u.data, &c1);
+                                                                       
+                       if (unlikely( ret<0)){
+                               /* handle error in expression => use default */
+                               ret=-1;
+                               goto match_cond_def;
+                       }
+                       if (h->run_flags & EXIT_R_F){
+                               ret=0;
+                               break;
+                       }
+                       h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
+                                                                                                           in expr */
+                       if (likely(rv)){
+                               rv1=rval_convert(h, msg, RV_STR, rv, &c1);
+                               if (unlikely(rv1==0)){
+                                       ret=-1;
+                                       goto match_cond_def;
+                               }
+                               s=rv1->v.s;
+                       }else{
+                               /* int result in v */
+                               rval_cache_clean(&c1);
+                               s.s=sint2str(v, &s.len);
+                       }
+                       ret=1; /* default is continue */
+                       for(i=0; i<mct->n; i++)
+                               if (( mct->match[i].type==MATCH_STR &&
+                                               mct->match[i].l.s.len==s.len &&
+                                               memcmp(mct->match[i].l.s.s, s.s, s.len) == 0 ) ||
+                                        ( mct->match[i].type==MATCH_RE &&
+                                         regexec(mct->match[i].l.regex, s.s, 0, 0, 0) == 0)
+                                       ){
+                                       if (likely(mct->jump[i])){
+                                               ret=run_actions(h, mct->jump[i], msg);
+                                               h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+                                                                                                          returns passthrough */
+                                       }
+                                       goto match_cleanup;
+                               }
+match_cond_def:
+                       if (mct->def){
+                               ret=run_actions(h, mct->def, msg);
+                               h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+                                                                                          returns passthrough */
+                       }
+match_cleanup:
+                       if (rv1){
+                               rval_destroy(rv1);
+                               rval_destroy(rv);
+                               rval_cache_clean(&c1);
+                       }else if (rv){
+                               rval_destroy(rv);
+                               rval_cache_clean(&c1);
+                       }
+                       break;
+               case WHILE_T:
+                       i=0;
+                       flags=0;
+                       rve=(struct rval_expr*)a->val[0].u.data;
+                       ret=1;
+                       while(!(flags & BREAK_R_F) && 
+                                       (rval_expr_eval_int(h, msg, &v, rve) == 0) && v){
+                               i++;
+                               if (unlikely(i > cfg_get(core, core_cfg, max_while_loops))){
+                                       LOG(L_ERR, "ERROR: runaway while (%d, %d): more then"
+                                                               " %d loops\n", 
+                                                               rve->fpos.s_line, rve->fpos.s_col,
+                                                               cfg_get(core, core_cfg, max_while_loops));
+                                       ret=-1;
+                                       break;
+                               }
+                               if (likely(a->val[1].u.data)){
+                                       ret=run_actions(h, (struct action*)a->val[1].u.data, msg);
+                                       flags|=h->run_flags;
+                                       h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+                                                                                                  returns passthrough */
+                               }
+                       }
+                       break;
                case FORCE_RPORT_T:
                        msg->msg_flags|=FL_FORCE_RPORT;
                        ret=1; /* continue processing */
@@ -897,125 +1173,20 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 
                case ADD_T:
                case ASSIGN_T:
-
-                       /* If the left attr was specified without indexing brackets delete
-                        * existing AVPs before adding new ones
-                        */
-                       if ((a->val[0].u.attr->type & AVP_INDEX_ALL) != AVP_INDEX_ALL) delete_avp(a->val[0].u.attr->type, a->val[0].u.attr->name);
-
-                       if (a->val[1].type == STRING_ST) {
-                               value.s = a->val[1].u.str;
-                               flags = a->val[0].u.attr->type | AVP_VAL_STR;
-                               name = a->val[0].u.attr->name;
-                               ret = 1;
-                       } else if (a->val[1].type == NUMBER_ST) {
-                               value.n = a->val[1].u.number;
-                               flags = a->val[0].u.attr->type;
-                               name = a->val[0].u.attr->name;
+                       v=lval_assign(h, msg, (struct lvalue*)a->val[0].u.data,
+                                                                 (struct rval_expr*)a->val[1].u.data);
+                       if (likely(v>=0))
                                ret = 1;
-                       } else if (a->val[1].type == ACTION_ST) {
-                               flags = a->val[0].u.attr->type;
-                               name = a->val[0].u.attr->name;
-                               if (a->val[1].u.data) {
-                                       value.n = run_actions(h, (struct action*)a->val[1].u.data,
-                                                                                       msg);
-                               } else {
-                                       value.n = -1;
-                               }
-                               ret = value.n;
-                       } else if(a->val[1].type == EXPR_ST && a->val[1].u.data) {
-                               v = eval_expr(h, (struct expr*)a->val[1].u.data, msg);
-                               if (v < 0) {
-                                       if (v == EXPR_DROP){ /* hack to quit on DROP*/
-                                               ret = 0;
-                                               break;
-                                       } else {
-                                               LOG(L_WARN,"WARNING: do_action: error in expression\n");
-                                               v = 0; /* error is treated as false (Miklos) */
-                                       }
-                               }
-
-                               flags = a->val[0].u.attr->type;
-                               name = a->val[0].u.attr->name;
-                               value.n = v;
-                       } else if (a->val[1].type == AVP_ST) {
-                               struct search_state st;
-                               avp_t* avp;
-                               avp_t* avp_mark;
-
-                               avp_mark = NULL;
-                               if ((a->val[1].u.attr->type & AVP_INDEX_ALL) == AVP_INDEX_ALL) {
-                                       avp = search_first_avp(a->val[1].u.attr->type, a->val[1].u.attr->name, &value, &st);
-                                       while(avp) {
-                                                    /* We take only the type of value and name from the source avp
-                                                     * and reset class and track flags
-                                                     */
-                                               flags = (a->val[0].u.attr->type & ~AVP_INDEX_ALL) | (avp->flags & ~(AVP_CLASS_ALL|AVP_TRACK_ALL));
-
-                                               if (add_avp_before(avp_mark, flags, a->val[0].u.attr->name, value) < 0) {
-                                                       LOG(L_CRIT, "ERROR: Failed to assign value to attribute\n");
-                                                       ret=E_UNSPEC;
-                                                       break;
-                                               }
-
-                                               /* move the mark, so the next found AVP will come before the one currently added
-                                                * so they will have the same order as in the source list
-                                                */
-                                               if (avp_mark) {
-                                                       avp_mark=avp_mark->next;
-                                               } else {
-                                                       avp_mark=search_first_avp(flags, a->val[0].u.attr->name, NULL, NULL);
-                                               }
-
-                                               avp = search_next_avp(&st, &value);
-                                       }
-                                       ret = 1;
-                                       break;
-                               } else {
-                                       avp = search_avp_by_index(a->val[1].u.attr->type, a->val[1].u.attr->name, &value, a->val[1].u.attr->index);
-                                       if (avp) {
-                                               flags = a->val[0].u.attr->type | (avp->flags & ~(AVP_CLASS_ALL|AVP_TRACK_ALL));
-                                               name = a->val[0].u.attr->name;
-                                               ret = 1;
-                                       } else {
-                                               ret = E_UNSPEC;
-                                               break;
-                                       }
-                               }
-                       } else if (a->val[1].type == SELECT_ST) {
-                               int r;
-                               r = run_select(&value.s, a->val[1].u.select, msg);
-                               if (r < 0) {
-                                       ret=E_UNSPEC;
-                                       break;
-                               } else if (r > 0) {
-                                       value.s.s = "";
-                                       value.s.len = 0;
-                               }
-
-                               flags = a->val[0].u.attr->type | AVP_VAL_STR;
-                               name = a->val[0].u.attr->name;
-                               ret = 1;
-                       } else {
-                               LOG(L_CRIT, "BUG: do_action: Bad right side of avp assignment\n");
-                               ret=E_BUG;
-                               break;
-                       }
-
-                       /* If the action is assign then remove the old avp value
-                        * before adding new ones */
-/*                     if ((unsigned char)a->type == ASSIGN_T) delete_avp(flags, name); */
-                       if (add_avp(flags & ~AVP_INDEX_ALL, name, value) < 0) {
-                               LOG(L_CRIT, "ERROR: Failed to assign value to attribute\n");
-                               ret=E_UNSPEC;
-                               break;
-                       }
+                       else if (unlikely (v == EXPR_DROP)) /* hack to quit on DROP*/
+                               ret=0;
+                       else
+                               ret=v;
                        break;
 
                default:
                        LOG(L_CRIT, "BUG: do_action: unknown type %d\n", a->type);
        }
-/*skip:*/
+skip:
        return ret;
 
 error_uri:
@@ -1065,7 +1236,9 @@ int run_actions(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 
        for (t=a; t!=0; t=t->next){
                ret=do_action(h, t, msg);
-               if (h->run_flags & (RETURN_R_F|EXIT_R_F)){
+               /* break, return or drop/exit stop execution of the current
+                  block */
+               if (h->run_flags & (BREAK_R_F|RETURN_R_F|EXIT_R_F)){
                        if (h->run_flags & EXIT_R_F){
 #ifdef USE_LONGJMP
                                h->last_retcode=ret;
@@ -1082,9 +1255,11 @@ end:
        /* process module onbreak handlers if present */
        if (h->rec_lev==0 && ret==0)
                for (mod=modules;mod;mod=mod->next)
-                       if (mod->exports && mod->exports->onbreak_f) {
-                               mod->exports->onbreak_f( msg );
-                               DBG("DEBUG: %s onbreak handler called\n", mod->exports->name);
+                       if ((mod->mod_interface_ver==0) && mod->exports && 
+                                       mod->exports->v0.onbreak_f) {
+                               mod->exports->v0.onbreak_f( msg );
+                               DBG("DEBUG: %s onbreak handler called\n",
+                                               mod->exports->c.name);
                        }
        return ret;
 
diff --git a/cfg.lex b/cfg.lex
index 73c23ef..9f42697 100644 (file)
--- a/cfg.lex
+++ b/cfg.lex
@@ -75,6 +75,8 @@
  *  2007-11-28  added TCP_OPT_{FD_CACHE, DEFER_ACCEPT, DELAYED_ACK, SYNCNT,
  *              LINGER2, KEEPALIVE, KEEPIDLE, KEEPINTVL, KEEPCNT} (andrei)
  *  2008-01-24  added CFG_DESCRIPTION used by cfg_var (Miklos)
+ *  2008-11-28  added support for kamailio pvars and avp/pvar guessing (andrei)
+ *  2008-12-11  added support for "string1" "string2" (andrei)
  *  2009-03-10  added SET_USERPHONE action (Miklos)
 */
 
        #include "usr_avp.h"
        #include "select.h"
        #include "cfg.tab.h"
+       #include "sr_compat.h"
 
        /* states */
        #define INITIAL_S               0
        #define COMMENT_S               1
        #define COMMENT_LN_S            2
        #define STRING_S                3
-       #define ATTR_S                  4
-        #define SELECT_S                5
+       #define ATTR_S                  4  /* avp/attr */
+       #define SELECT_S                5
+       #define AVP_PVAR_S              6  /* avp or pvar */
+       #define PVAR_P_S                7  /* pvar: $(...)  or $foo(...)*/
+       #define PVARID_S                8  /* $foo.bar...*/
+       #define STR_BETWEEN_S           9
 
        #define STR_BUF_ALLOC_UNIT      128
        struct str_buf{
 
 
        static int comment_nest=0;
+       static int p_nest=0;
        static int state=0, old_state=0, old_initial=0;
        static struct str_buf s_buf;
        int line=1;
        int column=1;
        int startcolumn=1;
+       int startline=1;
+       static int ign_lines=0;
+       static int ign_columns=0;
 
        static char* addchar(struct str_buf *, char);
        static char* addstr(struct str_buf *, char*, int);
        static void count();
+       static void count_more();
+       static void count_ignore();
 
 
 %}
 
 /* start conditions */
-%x STRING1 STRING2 COMMENT COMMENT_LN ATTR SELECT
+%x STRING1 STRING2 STR_BETWEEN COMMENT COMMENT_LN ATTR SELECT AVP_PVAR PVAR_P 
+%x PVARID
+
+/* config script types : #!SER  or #!KAMAILIO or #!MAX_COMPAT */
+SER_CFG                        SER
+KAMAILIO_CFG   KAMAILIO|OPENSER
+MAXCOMPAT_CFG  MAXCOMPAT|ALL
 
 /* action keywords */
 FORWARD        forward
@@ -172,6 +191,10 @@ ELSE                       "else"
 SET_ADV_ADDRESS        "set_advertised_address"
 SET_ADV_PORT   "set_advertised_port"
 FORCE_SEND_SOCKET      "force_send_socket"
+SWITCH                 "switch"
+CASE                   "case"
+DEFAULT                        "default"
+WHILE                  "while"
 
 /*ACTION LVALUES*/
 URIHOST                        "uri:host"
@@ -223,7 +246,8 @@ PLUS        "+"
 MINUS  "-"
 
 /* Attribute specification */
-ATTR_MARK   "$"|"%"
+ATTR_MARK   "%"
+VAR_MARK    "$"
 SELECT_MARK  "@"
 ATTR_FROM         "f"
 ATTR_TO           "t"
@@ -235,6 +259,9 @@ ATTR_FROMDOMAIN   "fd"
 ATTR_TODOMAIN     "td"
 ATTR_GLOBAL       "g"
 
+/* avp prefix */
+AVP_PREF       (([ft][rud]?)|g)\.
+
 /* config vars. */
 DEBUG  debug
 FORK   fork
@@ -356,6 +383,7 @@ MCAST_TTL           "mcast_ttl"
 TOS                    "tos"
 PMTU_DISCOVERY "pmtu_discovery"
 KILL_TIMEOUT   "exit_timeout"|"ser_kill_timeout"
+MAX_WLOOPS             "max_while_loops"
 
 /* stun config variables */
 STUN_REFRESH_INTERVAL "stun_refresh_interval"
@@ -391,7 +419,7 @@ ID                  {LETTER}{ALPHANUM}*
 HEX                    [0-9a-fA-F]
 HEXNUMBER      0x{HEX}+
 OCTNUMBER      0[0-7]+
-DECNUMBER       0|-?([1-9]{DIGIT}*)
+DECNUMBER       0|([1-9]{DIGIT}*)
 BINNUMBER       [0-1]+b
 HEX4           {HEX}{1,4}
 IPV6ADDR       ({HEX4}":"){7}{HEX4}|({HEX4}":"){1,7}(":"{HEX4}){1,7}|":"(":"{HEX4}){1,7}|({HEX4}":"){1,7}":"|"::"
@@ -484,6 +512,10 @@ EAT_ABLE   [\ \t\b\r]
                                                                                return SET_ADV_PORT; }
 <INITIAL>{FORCE_SEND_SOCKET}   {       count(); yylval.strval=yytext;
                                                                        return FORCE_SEND_SOCKET; }
+<INITIAL>{SWITCH}      { count(); yylval.strval=yytext; return SWITCH; }
+<INITIAL>{CASE}        { count(); yylval.strval=yytext; return CASE; }
+<INITIAL>{DEFAULT}     { count(); yylval.strval=yytext; return DEFAULT; }
+<INITIAL>{WHILE}       { count(); yylval.strval=yytext; return WHILE; }
 
 <INITIAL>{URIHOST}     { count(); yylval.strval=yytext; return URIHOST; }
 <INITIAL>{URIPORT}     { count(); yylval.strval=yytext; return URIPORT; }
@@ -705,6 +737,8 @@ EAT_ABLE    [\ \t\b\r]
                                                                        return PMTU_DISCOVERY; }
 <INITIAL>{KILL_TIMEOUT}                        {       count(); yylval.strval=yytext;
                                                                        return KILL_TIMEOUT; }
+<INITIAL>{MAX_WLOOPS}                  {       count(); yylval.strval=yytext;
+                                                                       return MAX_WLOOPS; }
 <INITIAL>{SERVER_ID}  { count(); yylval.strval=yytext; return SERVER_ID;}
 <INITIAL>{CFG_DESCRIPTION}     { count(); yylval.strval=yytext; return CFG_DESCRIPTION; }
 <INITIAL>{LOADMODULE}  { count(); yylval.strval=yytext; return LOADMODULE; }
@@ -747,7 +781,8 @@ EAT_ABLE    [\ \t\b\r]
 <SELECT>{BINNUMBER}     { count(); yylval.intval=(int)strtol(yytext, 0, 2); return NUMBER; }
 
 
-<INITIAL>{ATTR_MARK}    { count(); state = ATTR_S; BEGIN(ATTR); return ATTR_MARK; }
+<INITIAL>{ATTR_MARK}    { count(); state = ATTR_S; BEGIN(ATTR);
+                                                       return ATTR_MARK; }
 <ATTR>{ATTR_FROM}       { count(); return ATTR_FROM; }
 <ATTR>{ATTR_TO}         { count(); return ATTR_TO; }
 <ATTR>{ATTR_FROMURI}    { count(); return ATTR_FROMURI; }
@@ -760,15 +795,88 @@ EAT_ABLE  [\ \t\b\r]
 <ATTR>{DOT}             { count(); return DOT; }
 <ATTR>{LBRACK}          { count(); return LBRACK; }
 <ATTR>{RBRACK}          { count(); return RBRACK; }
-<ATTR>{STAR}           { count(); return STAR; }
-<ATTR>{DECNUMBER}      { count(); yylval.intval=atoi(yytext);return NUMBER; }
-<ATTR>{ID}             { count(); addstr(&s_buf, yytext, yyleng);
-                           yylval.strval=s_buf.s;
-                          memset(&s_buf, 0, sizeof(s_buf));
-                           state = INITIAL_S;
-                           BEGIN(INITIAL);
-                          return ID;
-                        }
+<ATTR>{STAR}                   { count(); return STAR; }
+<ATTR>{DECNUMBER}              { count(); yylval.intval=atoi(yytext);return NUMBER; }
+<ATTR>{ID}                             { count(); addstr(&s_buf, yytext, yyleng);
+                                                       yylval.strval=s_buf.s;
+                                                       memset(&s_buf, 0, sizeof(s_buf));
+                                                       state = INITIAL_S;
+                                                       BEGIN(INITIAL);
+                                                       return ID;
+                                               }
+
+<INITIAL>{VAR_MARK}{LPAREN}    {
+                                                               switch(sr_cfg_compat){
+                                                                       case SR_COMPAT_SER:
+                                                                               state=ATTR_S; BEGIN(ATTR);
+                                                                               yyless(1);
+                                                                               count();
+                                                                               return ATTR_MARK;
+                                                                               break;
+                                                                       case SR_COMPAT_KAMAILIO:
+                                                                       case SR_COMPAT_MAX:
+                                                                       default:
+                                                                               state = PVAR_P_S; BEGIN(PVAR_P);
+                                                                               p_nest=1; yymore();
+                                                                               break;
+                                                               }
+                                                       }
+       /* eat everything between 2 () and return PVAR token and a string
+          containing everything (including $ and ()) */
+<PVAR_P>{RPAREN}                       {       p_nest--;
+                                                               if (p_nest==0){
+                                                                       count();
+                                                                       addstr(&s_buf, yytext, yyleng);
+                                                                       yylval.strval=s_buf.s;
+                                                                       memset(&s_buf, 0, sizeof(s_buf));
+                                                                       state=INITIAL_S;
+                                                                       BEGIN(INITIAL);
+                                                                       return PVAR;
+                                                               }
+                                                               yymore();
+                                                       }
+<PVAR_P>{LPAREN}                       { p_nest++; yymore(); }
+<PVAR_P>.                                      { yymore(); }
+
+<PVARID>{ID}|'.'                       {yymore(); }
+<PVARID>{LPAREN}                       {       state = PVAR_P_S; BEGIN(PVAR_P);
+                                                               p_nest=1; yymore(); }
+<PVARID>.                                      { yyless(0); state=INITIAL_S; BEGIN(INITIAL);
+                                                               return PVAR;
+                                                       }
+
+
+<INITIAL>{VAR_MARK}                    {
+                                                               switch(sr_cfg_compat){
+                                                                       case SR_COMPAT_SER:
+                                                                               count();
+                                                                               state=ATTR_S; BEGIN(ATTR);
+                                                                               return ATTR_MARK;
+                                                                               break;
+                                                                       case SR_COMPAT_KAMAILIO:
+                                                                               state=PVARID_S; BEGIN(PVARID);
+                                                                               yymore();
+                                                                               break;
+                                                                       case SR_COMPAT_MAX:
+                                                                       default: 
+                                                                               state=AVP_PVAR_S; BEGIN(AVP_PVAR);
+                                                                               yymore();
+                                                                               break;
+                                                               }
+                                                       }
+       /* avp prefix detected -> go to avp mode */
+<AVP_PVAR>{AVP_PREF}           |
+<AVP_PVAR>{ID}{LBRACK}         { state = ATTR_S; BEGIN(ATTR); yyless(1); count();
+                                                         return ATTR_MARK; }
+<AVP_PVAR>{ID}{LPAREN}         { state = PVAR_P_S; p_nest=1; BEGIN(PVAR_P);
+                                                               yymore(); }
+<AVP_PVAR>{ID}                         {       count(); addstr(&s_buf, yytext, yyleng);
+                                                               yylval.strval=s_buf.s;
+                                                               memset(&s_buf, 0, sizeof(s_buf));
+                                                               state = INITIAL_S;
+                                                               BEGIN(INITIAL);
+                                                               return AVP_OR_PVAR;
+                                                       }
 
 <INITIAL>{IPV6ADDR}            { count(); yylval.strval=yytext; return IPV6ADDR; }
 <INITIAL>{DECNUMBER}           { count(); yylval.intval=atoi(yytext);return NUMBER; }
@@ -799,7 +907,7 @@ EAT_ABLE    [\ \t\b\r]
 <INITIAL>{COMMA}               { count(); return COMMA; }
 <INITIAL>{SEMICOLON}   { count(); return SEMICOLON; }
 <INITIAL>{COLON}       { count(); return COLON; }
-<INITIAL>{STAR}        { count(); return STAR; }
+<INITIAL>{STAR}                { count(); return STAR; }
 <INITIAL>{RPAREN}      { count(); return RPAREN; }
 <INITIAL>{LPAREN}      { count(); return LPAREN; }
 <INITIAL>{LBRACE}      { count(); return LBRACE; }
@@ -812,18 +920,19 @@ EAT_ABLE  [\ \t\b\r]
 <INITIAL>{CR}          { count();/* return CR;*/ }
 
 
-<INITIAL,SELECT>{QUOTES} { count(); old_initial = YY_START; old_state = state; state=STRING_S; BEGIN(STRING1); }
-<INITIAL>{TICK} { count(); old_initial = YY_START; old_state = state; state=STRING_S; BEGIN(STRING2); }
+<INITIAL,SELECT>{QUOTES} { count(); old_initial = YY_START; 
+                                                       old_state = state; state=STRING_S;
+                                                       BEGIN(STRING1); }
+<INITIAL>{TICK} { count(); old_initial = YY_START; old_state = state;
+                                       state=STRING_S; BEGIN(STRING2); }
 
 
-<STRING1>{QUOTES} { count(); state=old_state; BEGIN(old_initial);
+<STRING1>{QUOTES} { count_more(); 
                                                yytext[yyleng-1]=0; yyleng--;
                                                addstr(&s_buf, yytext, yyleng);
-                                               yylval.strval=s_buf.s;
-                                               memset(&s_buf, 0, sizeof(s_buf));
-                                               return STRING;
+                                               BEGIN(STR_BETWEEN);
                                        }
-<STRING2>{TICK}  { count(); state=old_state; BEGIN(old_initial);
+<STRING2>{TICK}  { count_more(); state=old_state; BEGIN(old_initial);
                                                yytext[yyleng-1]=0; yyleng--;
                                                addstr(&s_buf, yytext, yyleng);
                                                yylval.strval=s_buf.s;
@@ -832,21 +941,33 @@ EAT_ABLE  [\ \t\b\r]
                                        }
 <STRING2>.|{EAT_ABLE}|{CR}     { yymore(); }
 
-<STRING1>\\n           { count(); addchar(&s_buf, '\n'); }
-<STRING1>\\r           { count(); addchar(&s_buf, '\r'); }
-<STRING1>\\a           { count(); addchar(&s_buf, '\a'); }
-<STRING1>\\t           { count(); addchar(&s_buf, '\t'); }
-<STRING1>\\{QUOTES}    { count(); addchar(&s_buf, '"');  }
-<STRING1>\\\\          { count(); addchar(&s_buf, '\\'); }
-<STRING1>\\x{HEX}{1,2} { count(); addchar(&s_buf,
+<STRING1>\\n           { count_more(); addchar(&s_buf, '\n'); }
+<STRING1>\\r           { count_more(); addchar(&s_buf, '\r'); }
+<STRING1>\\a           { count_more(); addchar(&s_buf, '\a'); }
+<STRING1>\\t           { count_more(); addchar(&s_buf, '\t'); }
+<STRING1>\\{QUOTES}    { count_more(); addchar(&s_buf, '"');  }
+<STRING1>\\\\          { count_more(); addchar(&s_buf, '\\'); }
+<STRING1>\\x{HEX}{1,2} { count_more(); addchar(&s_buf,
                                                                                        (char)strtol(yytext+2, 0, 16)); }
  /* don't allow \[0-7]{1}, it will eat the backreferences from
     subst_uri if allowed (although everybody should use '' in subt_uri) */
-<STRING1>\\[0-7]{2,3}  { count(); addchar(&s_buf,
+<STRING1>\\[0-7]{2,3}  { count_more(); addchar(&s_buf,
                                                                                        (char)strtol(yytext+1, 0, 8));  }
-<STRING1>\\{CR}                { count(); } /* eat escaped CRs */
-<STRING1>.|{EAT_ABLE}|{CR}     { addchar(&s_buf, *yytext); }
-
+<STRING1>\\{CR}                { count_more(); } /* eat escaped CRs */
+<STRING1>.|{EAT_ABLE}|{CR}     { count_more(); addchar(&s_buf, *yytext); }
+
+<STR_BETWEEN>{EAT_ABLE}|{CR}   { count_ignore(); }
+<STR_BETWEEN>{QUOTES}                  { count_more(); state=STRING_S;
+                                                                 BEGIN(STRING1);}
+<STR_BETWEEN>.                                 {       
+                                                                       yyless(0); /* reparse it */
+                                                                       /* ignore the whitespace now that is
+                                                                         counted, return saved string value */
+                                                                       state=old_state; BEGIN(old_initial);
+                                                                       yylval.strval=s_buf.s;
+                                                                       memset(&s_buf, 0, sizeof(s_buf));
+                                                                       return STRING;
+                                                               }
 
 <INITIAL,COMMENT>{COM_START}   { count(); comment_nest++; state=COMMENT_S;
                                                                                BEGIN(COMMENT); }
@@ -858,6 +979,12 @@ EAT_ABLE   [\ \t\b\r]
                                                                }
 <COMMENT>.|{EAT_ABLE}|{CR}                             { count(); };
 
+<INITIAL>{COM_LINE}!{SER_CFG}{CR}              { count();
+                                                                                       sr_cfg_compat=SR_COMPAT_SER;}
+<INITIAL>{COM_LINE}!{KAMAILIO_CFG}{CR} { count(); 
+                                                                                       sr_cfg_compat=SR_COMPAT_KAMAILIO;}
+<INITIAL>{COM_LINE}!{MAXCOMPAT_CFG}{CR}        { count(); 
+                                                                                               sr_cfg_compat=SR_COMPAT_MAX;}
 <INITIAL>{COM_LINE}.*{CR}      { count(); }
 
 <INITIAL>{ID}                  { count(); addstr(&s_buf, yytext, yyleng);
@@ -870,6 +997,12 @@ EAT_ABLE   [\ \t\b\r]
 
 <<EOF>>                                                        {
                                                                        switch(state){
+                                                                               case STR_BETWEEN_S:
+                                                                                       state=old_state;
+                                                                                       BEGIN(old_initial);
+                                                                                       yylval.strval=s_buf.s;
+                                                                                       memset(&s_buf, 0, sizeof(s_buf));
+                                                                                       return STRING;
                                                                                case STRING_S:
                                                                                        LOG(L_CRIT, "ERROR: cfg. parser: unexpected EOF in"
                                                                                                                " unclosed string\n");
@@ -887,6 +1020,23 @@ EAT_ABLE  [\ \t\b\r]
                                                                                        LOG(L_CRIT, "ERROR: unexpected EOF:"
                                                                                                                "comment line open\n");
                                                                                        break;
+                                                                               case  ATTR_S:
+                                                                                       LOG(L_CRIT, "ERROR: unexpected EOF"
+                                                                                                       " while parsing"
+                                                                                                       " avp name\n");
+                                                                                       break;
+                                                                               case PVARID_S:
+                                                                                       p_nest=0;
+                                                                               case PVAR_P_S: 
+                                                                                       LOG(L_CRIT, "ERROR: unexpected EOF"
+                                                                                                       " while parsing pvar name"
+                                                                                                       " (%d paranthesis open)\n",
+                                                                                                       p_nest);
+                                                                                       break;
+                                                                               case AVP_PVAR_S:
+                                                                                       LOG(L_CRIT, "ERROR: unexpected EOF"
+                                                                                                       " while parsing"
+                                                                                                       " avp or pvar name\n");
                                                                        }
                                                                        return 0;
                                                                }
@@ -935,26 +1085,66 @@ error:
 
 
 
-static void count()
+/** helper function for count_*(). */
+static void count_lc(int* l, int* c)
 {
        int i;
-
-       startcolumn=column;
        for (i=0; i<yyleng;i++){
                if (yytext[i]=='\n'){
-                       line++;
-                       column=startcolumn=1;
+                       (*l)++;
+                       (*c)=1;
                }else if (yytext[i]=='\t'){
-                       column++;
-                       /*column+=8 -(column%8);*/
+                       (*c)++;
+                       /*(*c)+=8 -((*c)%8);*/
                }else{
-                       column++;
+                       (*c)++;
                }
        }
 }
 
 
 
+/* helper function */
+static void count_restore_ignored()
+{
+       if (ign_lines) /* ignored line(s) => column has changed */
+               column=ign_columns;
+       else
+               column+=ign_columns;
+       line+=ign_lines;
+       ign_lines=ign_columns=0;
+}
+
+
+
+/** count/record position for stuff added to the current token. */
+static void count_more()
+{
+       count_restore_ignored();
+       count_lc(&line, &column);
+}
+
+
+
+/** count/record position for a new token. */
+static void count()
+{
+       count_restore_ignored();
+       startline=line;
+       startcolumn=column;
+       count_more();
+}
+
+
+
+/** record discarded stuff (not contained in the token) so that
+    the next token position can be adjusted properly*/
+static void count_ignore()
+{
+       count_lc(&ign_lines, &ign_columns);
+}
+
+
 /* replacement yywrap, removes libfl dependency */
 int yywrap()
 {
diff --git a/cfg.y b/cfg.y
index bcd42f0..f2383a2 100644 (file)
--- a/cfg.y
+++ b/cfg.y
  * 2007-11-28  added TCP_OPT_{FD_CACHE, DEFER_ACCEPT, DELAYED_ACK, SYNCNT,
  *              LINGER2, KEEPALIVE, KEEPIDLE, KEEPINTVL, KEEPCNT} (andrei)
  * 2008-01-24  added cfg_var definition (Miklos)
+ * 2008-11-18  support for variable parameter module functions (andrei)
+ * 2007-12-03  support for generalised lvalues and rvalues:
+ *               lval=rval_expr, where lval=avp|pvar  (andrei)
+ * 2007-12-06  expression are now evaluated in terms of rvalues;
+ *             NUMBER is now always positive; cleanup (andrei)
+ * 2009-01-26  case/switch() support (andrei)
  * 2009-03-10  added SET_USERPHONE action (Miklos)
 */
 
 
 #include <stdlib.h>
 #include <stdio.h>
+#include <stdarg.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include "route_struct.h"
 #include "globals.h"
 #include "route.h"
+#include "switch.h"
 #include "dprint.h"
 #include "sr_module.h"
 #include "modparam.h"
 #include "tcp_init.h"
 #include "tcp_options.h"
 #include "sctp_options.h"
+#include "pvar.h"
+#include "lvalue.h"
+#include "rvalue.h"
+#include "sr_compat.h"
 #include "msg_translator.h"
 
 #include "config.h"
 
 
 extern int yylex();
-static void yyerror(char* s);
+static void yyerror(char* s, ...);
+static void yyerror_at(struct cfg_pos* pos, char* s, ...);
 static char* tmp;
 static int i_tmp;
+static unsigned u_tmp;
 static struct socket_id* lst_tmp;
 static struct name_lst*  nl_tmp;
 static int rt;  /* Type of route block for find_export */
@@ -186,15 +200,30 @@ static struct ip_addr* ip_tmp;
 static struct avp_spec* s_attr;
 static select_t sel;
 static select_t* sel_ptr;
+static pv_spec_t* pv_spec;
 static struct action *mod_func_action;
-
-static void warn(char* s);
+static struct lvalue* lval_tmp;
+static struct rvalue* rval_tmp;
+
+static void warn(char* s, ...);
+static void warn_at(struct cfg_pos* pos, char* s, ...);
+static void get_cpos(struct cfg_pos* pos);
+static struct rval_expr* mk_rve_rval(enum rval_type, void* v);
+static struct rval_expr* mk_rve1(enum rval_expr_op op, struct rval_expr* rve1);
+static struct rval_expr* mk_rve2(enum rval_expr_op op, struct rval_expr* rve1,
+                                                                       struct rval_expr* rve2);
 static struct socket_id* mk_listen_id(char*, int, int);
 static struct name_lst* mk_name_lst(char* name, int flags);
 static struct socket_id* mk_listen_id2(struct name_lst*, int, int);
 static void free_name_lst(struct name_lst* lst);
 static void free_socket_id_lst(struct socket_id* i);
 
+static struct case_stms* mk_case_stm(struct rval_expr* ct, int is_re, 
+                                                                       struct action* a, int* err);
+static int case_check_type(struct case_stms* stms);
+static int case_check_default(struct case_stms* stms);
+
+
 %}
 
 %union {
@@ -203,11 +232,16 @@ static void free_socket_id_lst(struct socket_id* i);
        char* strval;
        struct expr* expr;
        struct action* action;
+       struct case_stms* case_stms;
        struct net* ipnet;
        struct ip_addr* ipaddr;
        struct socket_id* sockid;
        struct name_lst* name_l;
        struct avp_spec* attr;
+       struct _pv_spec* pvar;
+       struct lvalue* lval;
+       struct rvalue* rval;
+       struct rval_expr* rv_expr;
        select_t* select;
 }
 
@@ -255,6 +289,10 @@ static void free_socket_id_lst(struct socket_id* i);
 %token SET_ADV_ADDRESS
 %token SET_ADV_PORT
 %token FORCE_SEND_SOCKET
+%token SWITCH
+%token CASE
+%token DEFAULT
+%token WHILE
 %token URIHOST
 %token URIPORT
 %token MAX_LEN
@@ -414,6 +452,7 @@ static void free_socket_id_lst(struct socket_id* i);
 %token TOS
 %token PMTU_DISCOVERY
 %token KILL_TIMEOUT
+%token MAX_WLOOPS
 %token CFG_DESCRIPTION
 %token SERVER_ID
 
@@ -433,32 +472,33 @@ static void free_socket_id_lst(struct socket_id* i);
 %token ATTR_GLOBAL
 %token ADDEQ
 
+
 %token STUN_REFRESH_INTERVAL
 %token STUN_ALLOW_STUN
 %token STUN_ALLOW_FP
 
 
-/* operators */
-%nonassoc EQUAL
-%nonassoc EQUAL_T
-%nonassoc GT
-%nonassoc LT
-%nonassoc GTE
-%nonassoc LTE
-%nonassoc DIFF
-%nonassoc MATCH
+/* operators, C like precedence */
+%right EQUAL
 %left LOG_OR
 %left LOG_AND
 %left BIN_OR
 %left BIN_AND
+%left EQUAL_T DIFF MATCH
+%left GT LT GTE LTE
 %left PLUS MINUS
+%left STAR SLASH
 %right NOT
+%left DOT
 
 /* values */
 %token <intval> NUMBER
 %token <strval> ID
 %token <strval> STRING
 %token <strval> IPV6ADDR
+%token <strval> PVAR
+/* not clear yet if this is an avp or pvar */
+%token <strval> AVP_OR_PVAR
 
 /* other */
 %token COMMA
@@ -469,16 +509,17 @@ static void free_socket_id_lst(struct socket_id* i);
 %token RBRACE
 %token LBRACK
 %token RBRACK
-%token SLASH
-%token DOT
 %token CR
 %token COLON
-%token STAR
 
 
 /*non-terminals */
-%type <expr> exp exp_elem /*, condition*/
-%type <action> action actions cmd fcmd if_cmd stm exp_stm assign_action
+%type <expr> exp exp_elem
+%type <intval> intno eint_op eint_op_onsend
+%type <intval> eip_op eip_op_onsend
+%type <action> action actions cmd fcmd if_cmd stm /*exp_stm*/ assign_action
+%type <action> switch_cmd while_cmd
+%type <case_stms> single_case case_stms
 %type <ipaddr> ipv4 ipv6 ipv6addr ip
 %type <ipnet> ipnet
 %type <strval> host
@@ -489,22 +530,27 @@ static void free_socket_id_lst(struct socket_id* i);
 %type <sockid>  phostport
 %type <sockid>  listen_phostport
 %type <intval> proto port
-%type <intval> equalop strop intop binop
-%type <strval> host_sep
+%type <intval> equalop strop cmpop rve_cmpop rve_equalop
 %type <intval> uri_type
 %type <attr> attr_id
 %type <attr> attr_id_num_idx
 %type <attr> attr_id_no_idx
 %type <attr> attr_id_ass
-%type <attr> attr_id_val
+/*%type <attr> attr_id_val*/
 %type <attr> attr_id_any
 %type <attr> attr_id_any_str
+%type <pvar> pvar
+%type <lval> lval
+%type <rv_expr> rval rval_expr ct_rval
+%type <lval> avp_pvar
 /* %type <intval> class_id */
 %type <intval> assign_op
 %type <select> select_id
 %type <strval> flag_name;
 %type <strval> route_name;
 %type <intval> avpflag_oper
+%type <intval> rve_un_op
+/* %type <intval> rve_op */
 
 /*%type <route_el> rules;
   %type <route_el> rule;
@@ -616,6 +662,10 @@ id_lst:
        | listen_phostport id_lst       { $$=$1; $$->next=$2; }
        ;
 
+intno: NUMBER
+       |  MINUS %prec NOT NUMBER { $$=-$2; }
+       ;
+
 flags_decl:            FLAGS_DECL      flag_list
                        |       FLAGS_DECL error { yyerror("flag list expected\n"); }
 ;
@@ -651,7 +701,7 @@ avpflag_spec:
        }
        ;
 assign_stm:
-       DEBUG_V EQUAL NUMBER { default_core_cfg.debug=$3; }
+       DEBUG_V EQUAL intno { default_core_cfg.debug=$3; }
        | DEBUG_V EQUAL error  { yyerror("number  expected"); }
        | FORK  EQUAL NUMBER { dont_fork= ! $3; }
        | FORK  EQUAL error  { yyerror("boolean value expected"); }
@@ -674,13 +724,13 @@ assign_stm:
        | DNS_TRY_NAPTR error { yyerror("boolean value expected"); }
        | DNS_SRV_LB EQUAL NUMBER   { IF_DNS_FAILOVER(default_core_cfg.dns_srv_lb=$3); }
        | DNS_SRV_LB error { yyerror("boolean value expected"); }
-       | DNS_UDP_PREF EQUAL NUMBER { IF_NAPTR(default_core_cfg.dns_udp_pref=$3);}
+       | DNS_UDP_PREF EQUAL intno { IF_NAPTR(default_core_cfg.dns_udp_pref=$3);}
        | DNS_UDP_PREF error { yyerror("number expected"); }
-       | DNS_TCP_PREF EQUAL NUMBER { IF_NAPTR(default_core_cfg.dns_tcp_pref=$3);}
+       | DNS_TCP_PREF EQUAL intno { IF_NAPTR(default_core_cfg.dns_tcp_pref=$3);}
        | DNS_TCP_PREF error { yyerror("number expected"); }
-       | DNS_TLS_PREF EQUAL NUMBER { IF_NAPTR(default_core_cfg.dns_tls_pref=$3);}
+       | DNS_TLS_PREF EQUAL intno { IF_NAPTR(default_core_cfg.dns_tls_pref=$3);}
        | DNS_TLS_PREF error { yyerror("number expected"); }
-       | DNS_SCTP_PREF EQUAL NUMBER { 
+       | DNS_SCTP_PREF EQUAL intno { 
                                                                IF_NAPTR(default_core_cfg.dns_sctp_pref=$3); }
        | DNS_SCTP_PREF error { yyerror("number expected"); }
        | DNS_RETR_TIME EQUAL NUMBER   { default_core_cfg.dns_retr_time=$3; }
@@ -740,9 +790,9 @@ assign_stm:
        | PHONE2TEL EQUAL error { yyerror("boolean value expected"); }
        | SYN_BRANCH EQUAL NUMBER { syn_branch=$3; }
        | SYN_BRANCH EQUAL error { yyerror("boolean value expected"); }
-       | MEMLOG EQUAL NUMBER { memlog=$3; }
+       | MEMLOG EQUAL intno { memlog=$3; }
        | MEMLOG EQUAL error { yyerror("int value expected"); }
-       | MEMDBG EQUAL NUMBER { memdbg=$3; }
+       | MEMDBG EQUAL intno { memdbg=$3; }
        | MEMDBG EQUAL error { yyerror("int value expected"); }
        | SIP_WARNING EQUAL NUMBER { sip_warning=$3; }
        | SIP_WARNING EQUAL error { yyerror("boolean value expected"); }
@@ -784,7 +834,7 @@ assign_stm:
                #endif
        }
        | TCP_CHILDREN EQUAL error { yyerror("number expected"); }
-       | TCP_CONNECT_TIMEOUT EQUAL NUMBER {
+       | TCP_CONNECT_TIMEOUT EQUAL intno {
                #ifdef USE_TCP
                        tcp_default_cfg.connect_timeout_s=$3;
                #else
@@ -792,7 +842,7 @@ assign_stm:
                #endif
        }
        | TCP_CONNECT_TIMEOUT EQUAL error { yyerror("number expected"); }
-       | TCP_SEND_TIMEOUT EQUAL NUMBER {
+       | TCP_SEND_TIMEOUT EQUAL intno {
                #ifdef USE_TCP
                        tcp_default_cfg.send_timeout=S_TO_TICKS($3);
                #else
@@ -800,7 +850,7 @@ assign_stm:
                #endif
        }
        | TCP_SEND_TIMEOUT EQUAL error { yyerror("number expected"); }
-       | TCP_CON_LIFETIME EQUAL NUMBER {
+       | TCP_CON_LIFETIME EQUAL intno {
                #ifdef USE_TCP
                        if ($3<0)
                                tcp_default_cfg.con_lifetime=-1;
@@ -1277,6 +1327,8 @@ assign_stm:
        | PMTU_DISCOVERY error { yyerror("number expected"); }
        | KILL_TIMEOUT EQUAL NUMBER { ser_kill_timeout=$3; }
        | KILL_TIMEOUT EQUAL error { yyerror("number expected"); }
+       | MAX_WLOOPS EQUAL NUMBER { default_core_cfg.max_while_loops=$3; }
+       | MAX_WLOOPS EQUAL error { yyerror("number expected"); }
        | STUN_REFRESH_INTERVAL EQUAL NUMBER { IF_STUN(stun_refresh_interval=$3); }
        | STUN_REFRESH_INTERVAL EQUAL error{ yyerror("number expected"); }
        | STUN_ALLOW_STUN EQUAL NUMBER { IF_STUN(stun_allow_stun=$3); }
@@ -1505,281 +1557,183 @@ send_route_stm: ROUTE_SEND LBRACE actions RBRACE {
        }
        | ROUTE_SEND error { yyerror("invalid onsend_route statement"); }
        ;
-/*
-rules:
-       rules rule { push($2, &$1); $$=$1; }
-       | rule {$$=$1; }
-       | rules error { $$=0; yyerror("invalid rule"); }
-       ;
-rule:
-       condition actions CR {
-               $$=0;
-               if (add_rule($1, $2, &$$)<0) {
-                       yyerror("error calling add_rule");
-                       YYABORT;
+
+exp:   rval_expr
+               {
+                       if (!rve_check_type((enum rval_type*)&i_tmp, $1, 0, 0 ,0)){
+                               yyerror("invalid expression");
+                               $$=0;
+                       }else if (i_tmp!=RV_INT && i_tmp!=RV_NONE){
+                               yyerror("invalid expression type, int expected\n");
+                               $$=0;
+                       }else
+                               $$=mk_elem(NO_OP, RVEXP_O, $1, 0, 0);
                }
-       }
-       | CR    { $$=0;}
-       | condition error { $$=0; yyerror("bad actions in rule"); }
-       ;
-condition:
-       exp {$$=$1;}
-*/
-exp:   exp LOG_AND exp         { $$=mk_exp(LOGAND_OP, $1, $3); }
-       | exp LOG_OR exp        { $$=mk_exp(LOGOR_OP, $1, $3);  }
-       | NOT exp               { $$=mk_exp(NOT_OP, $2, 0);  }
-       | LPAREN exp RPAREN     { $$=$2; }
-       | exp_elem              { $$=$1; }
        ;
+
+/* exp elem operators */
 equalop:
        EQUAL_T {$$=EQUAL_OP; }
        | DIFF  {$$=DIFF_OP; }
        ;
-intop: equalop {$$=$1; }
-       | GT    {$$=GT_OP; }
+cmpop:
+         GT    {$$=GT_OP; }
        | LT    {$$=LT_OP; }
        | GTE   {$$=GTE_OP; }
        | LTE   {$$=LTE_OP; }
        ;
-binop :
-       BIN_OR { $$= BINOR_OP; }
-       | BIN_AND { $$ = BINAND_OP; }
-       ;
 strop:
        equalop {$$=$1; }
        | MATCH {$$=MATCH_OP; }
        ;
+
+
+/* rve expr. operators */
+rve_equalop:
+       EQUAL_T {$$=RVE_EQ_OP; }
+       | DIFF  {$$=RVE_DIFF_OP; }
+       ;
+rve_cmpop:
+         GT    {$$=RVE_GT_OP; }
+       | LT    {$$=RVE_LT_OP; }
+       | GTE   {$$=RVE_GTE_OP; }
+       | LTE   {$$=RVE_LTE_OP; }
+       ;
+
+
+
+/* boolean expression uri operands */
 uri_type:
        URI             {$$=URI_O;}
        | FROM_URI      {$$=FROM_URI_O;}
        | TO_URI        {$$=TO_URI_O;}
        ;
 
+
+/* boolean expression integer operands, available only in the
+  onsend route */
+eint_op_onsend:
+                       SNDPORT         { $$=SNDPORT_O; }
+               |       TOPORT          { $$=TOPORT_O; }
+               |       SNDPROTO        { $$=SNDPROTO_O; }
+               |       SNDAF           { $$=SNDAF_O; }
+               ;
+
+/* boolean expression integer operands */
+eint_op:       SRCPORT         { $$=SRCPORT_O; }
+               |       DSTPORT         { $$=DSTPORT_O; }
+               |       PROTO           { $$=PROTO_O; }
+               |       AF                      { $$=AF_O; }
+               |       MSGLEN          { $$=MSGLEN_O; }
+               |       RETCODE         { $$=RETCODE_O; }
+               | eint_op_onsend
+       ;
+
+/* boolean expression ip/ipnet operands */
+eip_op_onsend:
+                       SNDIP           { onsend_check("snd_ip"); $$=SNDIP_O; }
+               |       TOIP            { onsend_check("to_ip");  $$=TOIP_O; }
+               ;
+
+eip_op:                SRCIP           { $$=SRCIP_O; }
+               |       DSTIP           { $$=DSTIP_O; }
+               | eip_op_onsend
+               ;
+
+
+
 exp_elem:
-       METHOD strop STRING     {$$= mk_elem($2, METHOD_O, 0, STRING_ST, $3);}
-       | METHOD strop attr_id_val  {$$ = mk_elem($2, METHOD_O, 0, AVP_ST, $3); }
-       | METHOD strop select_id {$$ = mk_elem($2, METHOD_O, 0, SELECT_ST, $3); }
-       | METHOD strop  ID      {$$ = mk_elem($2, METHOD_O, 0, STRING_ST,$3); }
+       METHOD strop %prec EQUAL_T rval_expr
+               {$$= mk_elem($2, METHOD_O, 0, RVE_ST, $3);}
+       | METHOD strop %prec EQUAL_T ID
+               {$$ = mk_elem($2, METHOD_O, 0, STRING_ST,$3); }
        | METHOD strop error { $$=0; yyerror("string expected"); }
-       | METHOD error  { $$=0; yyerror("invalid operator,== , !=, or =~ expected"); }
-       | uri_type strop STRING {$$ = mk_elem($2, $1, 0, STRING_ST, $3); }
-       | uri_type strop host   {$$ = mk_elem($2, $1, 0, STRING_ST, $3); }
-       | uri_type strop attr_id_val {$$ = mk_elem($2, $1, 0, AVP_ST, $3); }
-       | uri_type strop select_id {$$ = mk_elem($2, $1, 0, SELECT_ST, $3); }
-       | uri_type equalop MYSELF {$$=mk_elem($2, $1, 0, MYSELF_ST, 0); }
-       | uri_type strop error { $$=0; yyerror("string or MYSELF expected"); }
-       | uri_type error        { $$=0; yyerror("invalid operator, == , != or =~ expected"); }
-
-       | SRCPORT intop NUMBER { $$=mk_elem($2, SRCPORT_O, 0, NUMBER_ST, (void*)$3 ); }
-       | SRCPORT intop attr_id_val { $$=mk_elem($2, SRCPORT_O, 0, AVP_ST, (void*)$3 ); }
-       | SRCPORT intop error { $$=0; yyerror("number expected"); }
-       | SRCPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
-
-       | DSTPORT intop NUMBER  { $$=mk_elem($2, DSTPORT_O, 0, NUMBER_ST, (void*)$3 ); }
-       | DSTPORT intop attr_id_val     { $$=mk_elem($2, DSTPORT_O, 0, AVP_ST, (void*)$3 ); }
-       | DSTPORT intop error { $$=0; yyerror("number expected"); }
-       | DSTPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
-
-       | SNDPORT intop NUMBER {
-               onsend_check("snd_port");
-               $$=mk_elem($2, SNDPORT_O, 0, NUMBER_ST, (void*)$3 );
-       }
-       | SNDPORT intop attr_id_val {
-               onsend_check("snd_port");
-               $$=mk_elem($2, SNDPORT_O, 0, AVP_ST, (void*)$3 );
-       }
-       | SNDPORT intop error { $$=0; yyerror("number expected"); }
-       | SNDPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
-
-       | TOPORT intop NUMBER {
-               onsend_check("to_port");
-               $$=mk_elem($2, TOPORT_O, 0, NUMBER_ST, (void*)$3 );
-       }
-       | TOPORT intop attr_id_val {
-               onsend_check("to_port");
-               $$=mk_elem($2, TOPORT_O, 0, AVP_ST, (void*)$3 );
-       }
-       | TOPORT intop error { $$=0; yyerror("number expected"); }
-       | TOPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
-
-       | PROTO intop proto     { $$=mk_elem($2, PROTO_O, 0, NUMBER_ST, (void*)$3 ); }
-       | PROTO intop attr_id_val       { $$=mk_elem($2, PROTO_O, 0, AVP_ST, (void*)$3 ); }
-       | PROTO intop error { $$=0; yyerror("protocol expected (udp, tcp or tls)"); }
-
-       | PROTO error { $$=0; yyerror("equal/!= operator expected"); }
-
-       | SNDPROTO intop proto  {
-               onsend_check("snd_proto");
-               $$=mk_elem($2, SNDPROTO_O, 0, NUMBER_ST, (void*)$3 );
-       }
-       | SNDPROTO intop attr_id_val {
-               onsend_check("snd_proto");
-               $$=mk_elem($2, SNDPROTO_O, 0, AVP_ST, (void*)$3 );
-       }
-       | SNDPROTO intop error { $$=0; yyerror("protocol expected (udp, tcp or tls)"); }
-       | SNDPROTO error { $$=0; yyerror("equal/!= operator expected"); }
-
-       | AF intop NUMBER       { $$=mk_elem($2, AF_O, 0, NUMBER_ST,(void *) $3 ); }
-       | AF intop attr_id_val  { $$=mk_elem($2, AF_O, 0, AVP_ST,(void *) $3 ); }
-       | AF intop error { $$=0; yyerror("number expected"); }
-       | AF error { $$=0; yyerror("equal/!= operator expected"); }
-
-       | SNDAF intop NUMBER {
-               onsend_check("snd_af");
-               $$=mk_elem($2, SNDAF_O, 0, NUMBER_ST, (void *) $3 ); }
-       | SNDAF intop attr_id_val {
-               onsend_check("snd_af");
-               $$=mk_elem($2, SNDAF_O, 0, AVP_ST, (void *) $3 );
-       }
-       | SNDAF intop error { $$=0; yyerror("number expected"); }
-       | SNDAF error { $$=0; yyerror("equal/!= operator expected"); }
-
-       | MSGLEN intop NUMBER           { $$=mk_elem($2, MSGLEN_O, 0, NUMBER_ST, (void *) $3 ); }
-       | MSGLEN intop attr_id_val      { $$=mk_elem($2, MSGLEN_O, 0, AVP_ST, (void *) $3 ); }
-       | MSGLEN intop MAX_LEN          { $$=mk_elem($2, MSGLEN_O, 0, NUMBER_ST, (void *) BUF_SIZE); }
-       | MSGLEN intop error { $$=0; yyerror("number expected"); }
-       | MSGLEN error { $$=0; yyerror("equal/!= operator expected"); }
-
-       | RETCODE intop NUMBER  { $$=mk_elem($2, RETCODE_O, 0, NUMBER_ST, (void *) $3 ); }
-       | RETCODE intop attr_id_val     { $$=mk_elem($2, RETCODE_O, 0, AVP_ST, (void *) $3 ); }
-       | RETCODE intop error { $$=0; yyerror("number expected"); }
-       | RETCODE error { $$=0; yyerror("equal/!= operator expected"); }
-
-       | SRCIP equalop ipnet   { $$=mk_elem($2, SRCIP_O, 0, NET_ST, $3); }
-       | SRCIP strop STRING {
-               s_tmp.s=$3;
-               s_tmp.len=strlen($3);
-               ip_tmp=str2ip(&s_tmp);
-       #ifdef USE_IPV6
-               if (ip_tmp==0)
-                       ip_tmp=str2ip6(&s_tmp);
-       #endif
-               if (ip_tmp) {
-                       $$=mk_elem($2, SRCIP_O, 0, NET_ST, mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
-               } else {
-                       $$=mk_elem($2, SRCIP_O, 0, STRING_ST, $3);
-               }
-       }
-       | SRCIP strop host      { $$=mk_elem($2, SRCIP_O, 0, STRING_ST, $3); }
-       | SRCIP equalop MYSELF  { $$=mk_elem($2, SRCIP_O, 0, MYSELF_ST, 0);
-                                                       }
-       | SRCIP strop error { $$=0; yyerror( "ip address or hostname expected" ); }
-       | SRCIP error  { $$=0; yyerror("invalid operator, ==, != or =~ expected");}
-       | DSTIP equalop ipnet   { $$=mk_elem(   $2, DSTIP_O, 0, NET_ST, (void*)$3); }
-       | DSTIP strop STRING    {
-               s_tmp.s=$3;
-               s_tmp.len=strlen($3);
-               ip_tmp=str2ip(&s_tmp);
-       #ifdef USE_IPV6
-               if (ip_tmp==0)
-                       ip_tmp=str2ip6(&s_tmp);
-       #endif /* USE_IPV6 */
-               if (ip_tmp) {
-                       $$=mk_elem($2, DSTIP_O, 0, NET_ST, mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
-               } else {
-                       $$=mk_elem($2, DSTIP_O, 0, STRING_ST, $3);
-               }
-       }
-       | DSTIP strop host      { $$=mk_elem(   $2, DSTIP_O, 0, STRING_ST, $3); }
-       | DSTIP equalop MYSELF  { $$=mk_elem(   $2, DSTIP_O, 0, MYSELF_ST, 0); }
-       | DSTIP strop error { $$=0; yyerror( "ip address or hostname expected" ); }
-       | DSTIP error { $$=0; yyerror("invalid operator, ==, != or =~ expected"); }
-       | SNDIP equalop ipnet {
-               onsend_check("snd_ip");
-               $$=mk_elem($2, SNDIP_O, 0, NET_ST, $3);
-       }
-       | SNDIP strop STRING    {
-               onsend_check("snd_ip");
-               s_tmp.s=$3;
-               s_tmp.len=strlen($3);
-               ip_tmp=str2ip(&s_tmp);
-       #ifdef USE_IPV6
-               if (ip_tmp==0)
-                       ip_tmp=str2ip6(&s_tmp);
-       #endif /* USE_IPV6 */
-               if (ip_tmp) {
-                       $$=mk_elem($2, SNDIP_O, 0, NET_ST, mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
-               } else {
-                       $$=mk_elem($2, SNDIP_O, 0, STRING_ST, $3);
-               }
-       }
-       | SNDIP strop host      {
-               onsend_check("snd_ip");
-               $$=mk_elem($2, SNDIP_O, 0, STRING_ST, $3);
-       }
-       | SNDIP equalop attr_id_val     {
-               onsend_check("snd_ip");
-           $$=mk_elem($2, SNDIP_O, 0, AVP_ST, (void*)$3 ); 
-       }
-       | SNDIP equalop MYSELF  {
-               onsend_check("snd_ip");
-               $$=mk_elem($2, SNDIP_O, 0, MYSELF_ST, 0);
-       }
-       | SNDIP strop error { $$=0; yyerror( "ip address or hostname expected" ); }
-       | SNDIP error  { $$=0; yyerror("invalid operator, ==, != or =~ expected"); }
-       | TOIP equalop ipnet    {
-               onsend_check("to_ip");
-               $$=mk_elem($2, TOIP_O, 0, NET_ST, $3);
-       }
-       | TOIP strop STRING     {
-               onsend_check("to_ip");
-               s_tmp.s=$3;
-               s_tmp.len=strlen($3);
-               ip_tmp=str2ip(&s_tmp);
-       #ifdef USE_IPV6
-               if (ip_tmp==0)
-                       ip_tmp=str2ip6(&s_tmp);
-       #endif /* USE_IPV6 */
-               if (ip_tmp) {
-                       $$=mk_elem($2, TOIP_O, 0, NET_ST, mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
-               } else {
-                       $$=mk_elem($2, TOIP_O, 0, STRING_ST, $3);
+       | METHOD error  
+               { $$=0; yyerror("invalid operator,== , !=, or =~ expected"); }
+       | uri_type strop %prec EQUAL_T rval_expr
+               {$$ = mk_elem($2, $1, 0, RVE_ST, $3); }
+       | uri_type strop %prec EQUAL_T MYSELF
+               {$$=mk_elem($2, $1, 0, MYSELF_ST, 0); }
+       | uri_type strop %prec EQUAL_T error
+               { $$=0; yyerror("string or MYSELF expected"); }
+       | uri_type error
+               { $$=0; yyerror("invalid operator, == , != or =~ expected"); }
+       | eint_op cmpop %prec GT rval_expr { $$=mk_elem($2, $1, 0, RVE_ST, $3 ); }
+       | eint_op equalop %prec EQUAL_T rval_expr 
+               { $$=mk_elem($2, $1, 0, RVE_ST, $3 ); }
+       | eint_op cmpop error   { $$=0; yyerror("number expected"); }
+       | eint_op equalop error { $$=0; yyerror("number expected"); }
+       | eint_op error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
+       | eip_op strop %prec EQUAL_T ipnet { $$=mk_elem($2, $1, 0, NET_ST, $3); }
+       | eip_op strop %prec EQUAL_T rval_expr {
+                       s_tmp.s=0;
+                       $$=0;
+                       if (rve_is_constant($3)){
+                               i_tmp=rve_guess_type($3);
+                               if (i_tmp==RV_INT)
+                                       yyerror("string expected");
+                               else if (i_tmp==RV_STR){
+                                       if (((rval_tmp=rval_expr_eval(0, 0, $3))==0) ||
+                                                               (rval_get_str(0, 0, &s_tmp, rval_tmp, 0)<0)){
+                                               rval_destroy(rval_tmp);
+                                               yyerror("bad rvalue expression");
+                                       }else{
+                                               rval_destroy(rval_tmp);
+                                       }
+                               }else{
+                                       yyerror("BUG: unexpected dynamic type");
+                               }
+                       }else{
+                                       warn("non constant rvalue in ip comparison");
+                       }
+                       if (s_tmp.s){
+                               ip_tmp=str2ip(&s_tmp);
+                       #ifdef USE_IPV6
+                               if (ip_tmp==0)
+                                       ip_tmp=str2ip6(&s_tmp);
+                       #endif
+                               pkg_free(s_tmp.s);
+                               if (ip_tmp) {
+                                       $$=mk_elem($2, $1, 0, NET_ST, 
+                                                               mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
+                               } else {
+                                       $$=mk_elem($2, $1, 0, RVE_ST, $3);
+                               }
+                       }else{
+                               $$=mk_elem($2, $1, 0, RVE_ST, $3);
+                       }
                }
-       }
-       | TOIP strop host       {
-               onsend_check("to_ip");
-               $$=mk_elem($2, TOIP_O, 0, STRING_ST, $3);
-       }
-       | TOIP equalop attr_id_val      {
-               onsend_check("to_ip");
-           $$=mk_elem($2, TOIP_O, 0, AVP_ST, (void*)$3 ); 
-       }
-       | TOIP equalop MYSELF  {
-               onsend_check("to_ip");
-               $$=mk_elem($2, TOIP_O, 0, MYSELF_ST, 0);
-       }
-       | TOIP strop error { $$=0; yyerror( "ip address or hostname expected" ); }
-       | TOIP error  { $$=0; yyerror("invalid operator, ==, != or =~ expected"); }
-
-       | MYSELF equalop uri_type       { $$=mk_elem($2, $3, 0, MYSELF_ST, 0); }
-       | MYSELF equalop SRCIP  { $$=mk_elem($2, SRCIP_O, 0, MYSELF_ST, 0); }
-       | MYSELF equalop DSTIP  { $$=mk_elem($2, DSTIP_O, 0, MYSELF_ST, 0); }
-       | MYSELF equalop SNDIP  {
-               onsend_check("snd_ip");
-               $$=mk_elem($2, SNDIP_O, 0, MYSELF_ST, 0);
-       }
-       | MYSELF equalop TOIP  {
-               onsend_check("to_ip");
-               $$=mk_elem($2, TOIP_O, 0, MYSELF_ST, 0);
-       }
-       | MYSELF equalop error { $$=0; yyerror(" URI, SRCIP or DSTIP expected"); }
+       | eip_op strop %prec EQUAL_T host
+               { $$=mk_elem($2, $1, 0, STRING_ST, $3); }
+       | eip_op strop %prec EQUAL_T MYSELF
+               { $$=mk_elem($2, $1, 0, MYSELF_ST, 0); }
+       | eip_op strop %prec EQUAL_T error
+               { $$=0; yyerror( "ip address or hostname expected" ); }
+       | eip_op error
+               { $$=0; yyerror("invalid operator, ==, != or =~ expected");}
+       
+       | MYSELF equalop %prec EQUAL_T uri_type
+               { $$=mk_elem($2, $3, 0, MYSELF_ST, 0); }
+       | MYSELF equalop %prec EQUAL_T eip_op
+               { $$=mk_elem($2, $3, 0, MYSELF_ST, 0); }
+       | MYSELF equalop %prec EQUAL_T error
+               { $$=0; yyerror(" URI, SRCIP or DSTIP expected"); }
        | MYSELF error  { $$=0; yyerror ("invalid operator, == or != expected"); }
-       | exp_stm       { $$=mk_elem( NO_OP, ACTION_O, 0, ACTIONS_ST, $1);  }
-       | NUMBER        { $$=mk_elem( NO_OP, NUMBER_O, 0, NUMBER_ST, (void*)$1 ); }
-
-       | attr_id_any                           {$$=mk_elem( NO_OP, AVP_O, (void*)$1, 0, 0); }
-       | attr_id_val strop STRING      {$$=mk_elem( $2, AVP_O, (void*)$1, STRING_ST, $3); }
-       | attr_id_val strop select_id   {$$=mk_elem( $2, AVP_O, (void*)$1, SELECT_ST, $3); }
-       | attr_id_val intop NUMBER      {$$=mk_elem( $2, AVP_O, (void*)$1, NUMBER_ST, (void*)$3); }
-       | attr_id_val binop NUMBER      {$$=mk_elem( $2, AVP_O, (void*)$1, NUMBER_ST, (void*)$3); }
-       | attr_id_val strop attr_id_val {$$=mk_elem( $2, AVP_O, (void*)$1, AVP_ST, (void*)$3); }
-       | attr_id_val intop attr_id_val {$$=mk_elem( $2, AVP_O, (void*)$1, AVP_ST, (void*)$3); }
-
-       | select_id                 { $$=mk_elem( NO_OP, SELECT_O, $1, 0, 0); }
-       | select_id strop STRING    { $$=mk_elem( $2, SELECT_O, $1, STRING_ST, $3); }
-       | select_id strop attr_id_val   { $$=mk_elem( $2, SELECT_O, $1, AVP_ST, (void*)$3); }
-       | select_id strop select_id { $$=mk_elem( $2, SELECT_O, $1, SELECT_ST, $3); }
+       ;
+/*
+exp_elem2:
+       rval_expr cmpop %prec GT rval_expr
+               { $$=mk_elem( $2, RVE_ST, $1, RVE_ST, $3);}
+       |
+       rval_expr equalop %prec EQUAL_T rval_expr
+               { $$=mk_elem( $2, RVE_ST, $1, RVE_ST, $3);}
+       | rval_expr LOG_AND rval_expr
+               { $$=mk_exp_rve(LOGAND_OP, $1, $3);}
+       | rval_expr LOG_OR rval_expr
+               { $$=mk_exp_rve(LOGOR_OP, $1, $3);}
 ;
+*/
+
 ipnet:
        ip SLASH ip     { $$=mk_net($1, $3); }
        | ip SLASH NUMBER {
@@ -1796,20 +1750,29 @@ ipnet:
        | ip    { $$=mk_net_bitlen($1, $1->len*8); }
        | ip SLASH error { $$=0; yyerror("netmask (eg:255.0.0.0 or 8) expected"); }
        ;
-host_sep:
-       DOT {$$=".";}
-       | MINUS {$$="-"; }
-       ;
 
 host:
        ID { $$=$1; }
-       | host host_sep ID {
+       | host DOT ID {
                $$=(char*)pkg_malloc(strlen($1)+1+strlen($3)+1);
                if ($$==0) {
                        LOG(L_CRIT, "ERROR: cfg. parser: memory allocation failure while parsing host\n");
                } else {
                        memcpy($$, $1, strlen($1));
-                       $$[strlen($1)]=*$2;
+                       $$[strlen($1)]='.';
+                       memcpy($$+strlen($1)+1, $3, strlen($3));
+                       $$[strlen($1)+1+strlen($3)]=0;
+               }
+               pkg_free($1);
+               pkg_free($3);
+       }
+       | host MINUS ID {
+               $$=(char*)pkg_malloc(strlen($1)+1+strlen($3)+1);
+               if ($$==0) {
+                       LOG(L_CRIT, "ERROR: cfg. parser: memory allocation failure while parsing host\n");
+               } else {
+                       memcpy($$, $1, strlen($1));
+                       $$[strlen($1)]='-';
                        memcpy($$+strlen($1)+1, $3, strlen($3));
                        $$[strlen($1)+1+strlen($3)]=0;
                }
@@ -1844,12 +1807,14 @@ fcmd:
                }
        }
        ;
+/*
 exp_stm:
        fcmd    { $$=$1; }
        | if_cmd        { $$=$1; }
        | assign_action { $$ = $1; }
        | LBRACE actions RBRACE { $$=$2; }
        ;
+*/
 stm:
        action  { $$=$1; }
        | LBRACE actions RBRACE { $$=$2; }
@@ -1862,6 +1827,8 @@ actions:
 action:
        fcmd SEMICOLON {$$=$1;}
        | if_cmd {$$=$1;}
+       | switch_cmd {$$=$1;}
+       | while_cmd { $$=$1; }
        | assign_action SEMICOLON {$$=$1;}
        | SEMICOLON /* null action */ {$$=0;}
        | fcmd error { $$=0; yyerror("bad command: missing ';'?"); }
@@ -1870,6 +1837,127 @@ if_cmd:
        IF exp stm              { $$=mk_action( IF_T, 3, EXPR_ST, $2, ACTIONS_ST, $3, NOSUBTYPE, 0); }
        | IF exp stm ELSE stm   { $$=mk_action( IF_T, 3, EXPR_ST, $2, ACTIONS_ST, $3, ACTIONS_ST, $5); }
        ;
+
+ct_rval: rval_expr {
+                       $$=0;
+                       if (!rve_is_constant($1)){
+                               yyerror("constant expected");
+                       /*
+                       } else if (!rve_check_type((enum rval_type*)&i_tmp, $1, 0, 0 ,0)){
+                               yyerror("invalid expression (bad type)");
+                       }else if (i_tmp!=RV_INT){
+                               yyerror("invalid expression type, int expected\n");
+                       */
+                       }else
+                               $$=$1;
+               }
+;
+single_case:
+       CASE ct_rval COLON actions {
+               $$=0;
+               if ($2==0) yyerror ("bad case label");
+               else if ((($$=mk_case_stm($2, 0, $4, &i_tmp))==0) && (i_tmp==-10)){
+                               YYABORT;
+               }
+       }
+| CASE SLASH ct_rval COLON actions {
+               $$=0;
+               if ($3==0) yyerror ("bad case label");
+               else if ((($$=mk_case_stm($3, 1, $5, &i_tmp))==0) && (i_tmp==-10)){
+                               YYABORT;
+               }
+       }
+       | CASE ct_rval COLON {
+               $$=0;
+               if ($2==0) yyerror ("bad case label");
+               else if ((($$=mk_case_stm($2, 0, 0, &i_tmp))==0) && (i_tmp==-10)){
+                               YYABORT;
+               }
+       }
+       | CASE SLASH ct_rval COLON {
+               $$=0;
+               if ($3==0) yyerror ("bad case label");
+               else if ((($$=mk_case_stm($3, 1, 0, &i_tmp))==0) && (i_tmp==-10)){
+                               YYABORT;
+               }
+       }
+       | DEFAULT COLON actions {
+               if ((($$=mk_case_stm(0, 0, $3, &i_tmp))==0) && (i_tmp=-10)){
+                               YYABORT;
+               }
+       }
+       | DEFAULT COLON {
+               if ((($$=mk_case_stm(0, 0, 0, &i_tmp))==0) && (i_tmp==-10)){
+                               YYABORT;
+               }
+       }
+       | CASE error { $$=0; yyerror("bad case label"); }
+       | CASE ct_rval COLON error { $$=0; yyerror ("bad case body"); }
+;
+case_stms:
+       case_stms single_case {
+               $$=$1;
+               if ($2==0) yyerror ("bad case");
+               if ($$){
+                       *($$->append)=$2;
+                       if (*($$->append)!=0)
+                               $$->append=&((*($$->append))->next);
+               }
+       }
+       | single_case {
+               $$=$1;
+               if ($1==0) yyerror ("bad case");
+               else $$->append=&($$->next);
+       }
+;
+switch_cmd:
+         SWITCH rval_expr LBRACE case_stms RBRACE { 
+               $$=0;
+               if ($2==0) yyerror("bad expression in switch(...)");
+               else if ($4==0) yyerror ("bad switch body");
+               else if (case_check_default($4)!=0)
+                       yyerror_at(&$2->fpos, "bad switch(): too many "
+                                                       "\"default:\" labels\n");
+               else if (case_check_type($4)!=0)
+                       yyerror_at(&$2->fpos, "bad switch(): mixed integer and"
+                                                       " string/RE cases not allowed\n");
+               else{
+                       $$=mk_action(SWITCH_T, 2, RVE_ST, $2, CASE_ST, $4);
+                       if ($$==0) {
+                               yyerror("internal error");
+                               YYABORT;
+                       }
+               }
+       }
+       | SWITCH rval_expr LBRACE RBRACE {
+               $$=0;
+               warn("empty switch()");
+               if ($2==0) yyerror("bad expression in switch(...)");
+               else{
+                       /* it might have sideffects, so leave it for the optimizer */
+                       $$=mk_action(SWITCH_T, 2, RVE_ST, $2, CASE_ST, 0);
+                       if ($$==0) {
+                               yyerror("internal error");
+                               YYABORT;
+                       }
+               }
+       }
+       | SWITCH error { $$=0; yyerror ("bad expression in switch(...)"); }
+       | SWITCH rval_expr LBRACE error RBRACE 
+               {$$=0; yyerror ("bad switch body"); }
+;
+
+while_cmd:
+       WHILE rval_expr stm {
+               if ($2){
+                       if (rve_is_constant($2))
+                               warn_at(&$2->fpos, "constant value in while(...)");
+               }else
+                       yyerror_at(&$2->fpos, "bad while(...) expression");
+               $$=mk_action( WHILE_T, 2, RVE_ST, $2, ACTIONS_ST, $3);
+       }
+;
+
 /* class_id:
        LBRACK ATTR_USER RBRACK { $$ = AVP_CLASS_USER; }
        | LBRACK ATTR_DOMAIN RBRACK { $$ = AVP_CLASS_DOMAIN; }
@@ -1955,7 +2043,7 @@ attr_id:
        attr_mark attr_spec { $$ = s_attr; }
        ;
 attr_id_num_idx:
-       attr_mark attr_spec LBRACK NUMBER RBRACK {
+       attr_mark attr_spec LBRACK intno RBRACK {
                s_attr->type|= (AVP_NAME_STR | ($4<0?AVP_INDEX_BACKWARD:AVP_INDEX_FORWARD));
                s_attr->index = ($4<0?-$4:$4);
                $$ = s_attr;
@@ -1971,10 +2059,12 @@ attr_id_ass:
        attr_id
        | attr_id_no_idx
        ;
+/*
 attr_id_val:
        attr_id
        | attr_id_num_idx
        ;
+*/
 attr_id_any:
        attr_id
        | attr_id_no_idx
@@ -1997,7 +2087,7 @@ attr_id_any_str:
                s.len = strlen(s.s);
                if (parse_avp_name(&s, &type, &avp_spec->name, &idx)) {
                        yyerror("error when parsing AVP");
-                       pkg_free(avp_spec);
+                       pkg_free(avp_spec);
                        YYABORT;
                }
                avp_spec->type = type;
@@ -2005,6 +2095,49 @@ attr_id_any_str:
                $$ = avp_spec;
        }
        ;
+
+pvar:  PVAR {
+                       pv_spec=pkg_malloc(sizeof(*pv_spec));
+                       if (!pv_spec) {
+                               yyerror("Not enough memory");
+                               YYABORT;
+                       }
+                       memset(pv_spec, 0, sizeof(*pv_spec));
+                       s_tmp.s=$1; s_tmp.len=strlen($1);
+                       if (pv_parse_spec(&s_tmp, pv_spec)==0){
+                               yyerror("unknown script pseudo variable");
+                               pkg_free(pv_spec);
+                               pv_spec=0;
+                               YYABORT;
+                       }
+                       $$=pv_spec;
+               }
+       ;
+
+avp_pvar:      AVP_OR_PVAR {
+                               lval_tmp=pkg_malloc(sizeof(*lval_tmp));
+                               if (!lval_tmp) {
+                                       yyerror("Not enough memory");
+                                       YYABORT;
+                               }
+                               memset(lval_tmp, 0, sizeof(*lval_tmp));
+                               s_tmp.s=$1; s_tmp.len=strlen(s_tmp.s);
+                               if (pv_parse_spec2(&s_tmp, &lval_tmp->lv.pvs, 1)==0){
+                                       /* not a pvar, try avps */
+                                       lval_tmp->lv.avps.type|= AVP_NAME_STR;
+                                       lval_tmp->lv.avps.name.s.s = s_tmp.s+1;
+                                       lval_tmp->lv.avps.name.s.len = s_tmp.len-1;
+                                       lval_tmp->type=LV_AVP;
+                               }else{
+                                       lval_tmp->type=LV_PVAR;
+                               }
+                               $$ = lval_tmp;
+                               DBG("parsed ambigous avp/pvar \"%.*s\" to %d\n",
+                                                       s_tmp.len, s_tmp.s, lval_tmp->type);
+                       }
+       ;
+
+
 /*
 assign_op:
        ADDEQ { $$ = ADD_T; }
@@ -2014,6 +2147,127 @@ assign_op:
 assign_op:
        EQUAL { $$ = ASSIGN_T; }
        ;
+
+
+lval: attr_id_ass {
+                                       lval_tmp=pkg_malloc(sizeof(*lval_tmp));
+                                       if (!lval_tmp) {
+                                               yyerror("Not enough memory");
+                                               YYABORT;
+                                       }
+                                       lval_tmp->type=LV_AVP; lval_tmp->lv.avps=*$1;
+                                       pkg_free($1); /* free the avp spec we just copied */
+                                       $$=lval_tmp;
+                               }
+       | pvar        {
+                                       if (!pv_is_w($1))
+                                               yyerror("read only pvar in assignment left side");
+                                       if ($1->trans!=0)
+                                               yyerror("pvar with transformations in assignment"
+                                                               " left side");
+                                       lval_tmp=pkg_malloc(sizeof(*lval_tmp));
+                                       if (!lval_tmp) {
+                                               yyerror("Not enough memory");
+                                               YYABORT;
+                                       }
+                                       lval_tmp->type=LV_PVAR; lval_tmp->lv.pvs=*($1);
+                                       pkg_free($1); /* free the pvar spec we just copied */
+                                       $$=lval_tmp;
+                               }
+       | avp_pvar    {
+                                       if (($1)->type==LV_PVAR){
+                                               if (!pv_is_w(&($1)->lv.pvs))
+                                                       yyerror("read only pvar in assignment left side");
+                                               if ($1->lv.pvs.trans!=0)
+                                                       yyerror("pvar with transformations in assignment"
+                                                                       " left side");
+                                       }
+                                       $$=$1;
+                               }
+       ;
+
+rval: intno                    {$$=mk_rve_rval(RV_INT, (void*)$1); }
+       | STRING                        {       s_tmp.s=$1; s_tmp.len=strlen($1);
+                                                       $$=mk_rve_rval(RV_STR, &s_tmp); }
+       | attr_id_any           {$$=mk_rve_rval(RV_AVP, $1); pkg_free($1); }
+       | pvar                          {$$=mk_rve_rval(RV_PVAR, $1); pkg_free($1); }
+       | avp_pvar                      {
+                                                       switch($1->type){
+                                                               case LV_AVP:
+                                                                       $$=mk_rve_rval(RV_AVP, &$1->lv.avps);
+                                                                       break;
+                                                               case LV_PVAR:
+                                                                       $$=mk_rve_rval(RV_PVAR, &$1->lv.pvs);
+                                                                       break;
+                                                               default:
+                                                                       yyerror("BUG: invalid lvalue type ");
+                                                                       YYABORT;
+                                                       }
+                                                       pkg_free($1); /* not needed anymore */
+                                               }
+       | select_id                     {$$=mk_rve_rval(RV_SEL, $1); pkg_free($1); }
+       | fcmd                          {$$=mk_rve_rval(RV_ACTION_ST, $1); }
+       | exp_elem { $$=mk_rve_rval(RV_BEXPR, $1); }
+       | LBRACE actions RBRACE {$$=mk_rve_rval(RV_ACTION_ST, $2); }
+       | LBRACE error RBRACE   { yyerror("bad command block"); }
+       | LPAREN assign_action RPAREN   {$$=mk_rve_rval(RV_ACTION_ST, $2); }
+       | LPAREN error RPAREN   { yyerror("bad expression"); }
+       ;
+
+
+rve_un_op: NOT { $$=RVE_LNOT_OP; }
+               |  MINUS %prec NOT      { $$=RVE_UMINUS_OP; } 
+               /* TODO: RVE_BOOL_OP, RVE_NOT_OP? */
+       ;
+
+/*
+rve_op:                PLUS            { $$=RVE_PLUS_OP; }
+               |       MINUS           { $$=RVE_MINUS_OP; }
+               |       STAR            { $$=RVE_MUL_OP; }
+               |       SLASH           { $$=RVE_DIV_OP; }
+       ;
+*/
+
+rval_expr: rval                                                { $$=$1;
+                                                                                       if ($$==0){
+                                                                                               yyerror("out of memory\n");
+                                                                                               YYABORT;
+                                                                                       }
+                                                                       }
+               | rve_un_op %prec NOT rval_expr {$$=mk_rve1($1, $2); }
+               | rval_expr PLUS rval_expr              {$$=mk_rve2(RVE_PLUS_OP, $1, $3); }
+               | rval_expr MINUS rval_expr             {$$=mk_rve2(RVE_MINUS_OP, $1, $3); }
+               | rval_expr STAR rval_expr              {$$=mk_rve2(RVE_MUL_OP, $1, $3); }
+               | rval_expr SLASH rval_expr             {$$=mk_rve2(RVE_DIV_OP, $1, $3); }
+               | rval_expr BIN_OR rval_expr    {$$=mk_rve2(RVE_BOR_OP, $1,  $3); }
+               | rval_expr BIN_AND rval_expr   {$$=mk_rve2(RVE_BAND_OP, $1,  $3);}
+               | rval_expr rve_cmpop %prec GT rval_expr { $$=mk_rve2( $2, $1, $3);}
+               | rval_expr rve_equalop %prec EQUAL_T rval_expr
+                       { $$=mk_rve2( $2, $1, $3);}
+               | rval_expr LOG_AND rval_expr   { $$=mk_rve2(RVE_LAND_OP, $1, $3);}
+               | rval_expr LOG_OR rval_expr    { $$=mk_rve2(RVE_LOR_OP, $1, $3);}
+               | LPAREN rval_expr RPAREN               { $$=$2;}
+               | rve_un_op %prec NOT error             { yyerror("bad expression"); }
+               | rval_expr PLUS error                  { yyerror("bad expression"); }
+               | rval_expr MINUS error                 { yyerror("bad expression"); }
+               | rval_expr STAR error                  { yyerror("bad expression"); }
+               | rval_expr SLASH error                 { yyerror("bad expression"); }
+               | rval_expr BIN_OR error                { yyerror("bad expression"); }
+               | rval_expr BIN_AND error               { yyerror("bad expression"); }
+               | rval_expr rve_cmpop %prec GT error
+                       { yyerror("bad expression"); }
+               | rval_expr rve_equalop %prec EQUAL_T error
+                       { yyerror("bad expression"); }
+               | rval_expr LOG_AND error               { yyerror("bad expression"); }
+               | rval_expr LOG_OR error                { yyerror("bad expression"); }
+               ;
+
+assign_action: lval assign_op  rval_expr       { $$=mk_action($2, 2, LVAL_ST, $1, 
+                                                                                                                         RVE_ST, $3);
+                                                                               }
+       ;
+
+/*
 assign_action:
        attr_id_ass assign_op STRING  { $$=mk_action($2, 2, AVP_ST, $1, STRING_ST, $3); }
        | attr_id_ass assign_op NUMBER  { $$=mk_action($2, 2, AVP_ST, $1, NUMBER_ST, (void*)$3); }
@@ -2022,6 +2276,8 @@ assign_action:
        | attr_id_ass assign_op select_id { $$=mk_action($2, 2, AVP_ST, (void*)$1, SELECT_ST, (void*)$3); }
        | attr_id_ass assign_op LPAREN exp RPAREN { $$ = mk_action($2, 2, AVP_ST, $1, EXPR_ST, $4); }
        ;
+*/
+
 avpflag_oper:
        SETAVPFLAG { $$ = 1; }
        | RESETAVPFLAG { $$ = 0; }
@@ -2239,7 +2495,7 @@ cmd:
        | RETURN                        {$$=mk_action(DROP_T, 2, NUMBER_ST, (void*)1, NUMBER_ST, (void*)RETURN_R_F); }
        | RETURN NUMBER                 {$$=mk_action(DROP_T, 2, NUMBER_ST, (void*)$2, NUMBER_ST, (void*)RETURN_R_F);}
        | RETURN RETCODE                {$$=mk_action(DROP_T, 2, RETCODE_ST, 0, NUMBER_ST, (void*)RETURN_R_F);}
-       | BREAK                         {$$=mk_action(DROP_T, 2, NUMBER_ST, 0, NUMBER_ST, (void*)RETURN_R_F); }
+       | BREAK                         {$$=mk_action(DROP_T, 2, NUMBER_ST, 0, NUMBER_ST, (void*)BREAK_R_F); }
        | LOG_TOK LPAREN STRING RPAREN  {$$=mk_action(LOG_T, 2, NUMBER_ST,
                                                                                (void*)(L_DBG+1), STRING_ST, $3); }
        | LOG_TOK LPAREN NUMBER COMMA STRING RPAREN     {$$=mk_action(LOG_T, 2, NUMBER_ST, (void*)$3, STRING_ST, $5); }
@@ -2410,31 +2666,65 @@ cmd:
                $$=0; yyerror("bad argument, [proto:]host[:port] expected");
        }
        | FORCE_SEND_SOCKET error {$$=0; yyerror("missing '(' or ')' ?"); }
-       | ID {mod_func_action = mk_action(MODULE_T, 2, MODEXP_ST, NULL, NUMBER_ST, 0); } LPAREN func_params RPAREN      {
-               mod_func_action->val[0].u.data = find_export_record($1, mod_func_action->val[1].u.number, rt);
+       | ID {mod_func_action = mk_action(MODULE_T, 2, MODEXP_ST, NULL, NUMBER_ST,
+                       0); } LPAREN func_params RPAREN {
+               mod_func_action->val[0].u.data = 
+                       find_export_record($1, mod_func_action->val[1].u.number, rt,
+                                                               &u_tmp);
                if (mod_func_action->val[0].u.data == 0) {
-                       if (find_export_record($1, mod_func_action->val[1].u.number, 0) ) {
+                       if (find_export_record($1, mod_func_action->val[1].u.number, 0,
+                                                                       &u_tmp) ) {
                                        yyerror("Command cannot be used in the block\n");
                        } else {
                                yyerror("unknown command, missing loadmodule?\n");
                        }
                        pkg_free(mod_func_action);
                        mod_func_action=0;
+               }else{
+                       switch( ((union cmd_export_u*)
+                                               mod_func_action->val[0].u.data)->c.param_no){
+                               case 0:
+                               case 1:
+                               case 2:
+                                       /* MODULE_T used for 0-2 params */
+                                       break;
+                               case 3:
+                                       mod_func_action->type=MODULE3_T;
+                                       break;
+                               case 4:
+                                       mod_func_action->type=MODULE4_T;
+                                       break;
+                               case 5:
+                                       mod_func_action->type=MODULE5_T;
+                                       break;
+                               case 6:
+                                       mod_func_action->type=MODULE6_T;
+                                       break;
+                               case VAR_PARAM_NO:
+                                       mod_func_action->type=MODULEX_T;
+                                       break;
+                               default:
+                                       yyerror("too many parameters for function\n");
+                                       break;
+                       }
                }
                $$ = mod_func_action;
        }
+       | ID error                                      { yyerror("'('')' expected (function call)");}
        ;
 func_params:
        /* empty */
        | func_params COMMA func_param { }
        | func_param {}
-       | func_params error { yyerror("call params error\n"); YYABORT; }
+       | func_params error { yyerror("call params error\n"); }
        ;
 func_param:
         NUMBER {
                if (mod_func_action->val[1].u.number < MAX_ACTIONS-2) {
-                       mod_func_action->val[mod_func_action->val[1].u.number+2].type = NUMBER_ST;
-                       mod_func_action->val[mod_func_action->val[1].u.number+2].u.number = $1;
+                       mod_func_action->val[mod_func_action->val[1].u.number+2].type =
+                               NUMBER_ST;
+                       mod_func_action->val[mod_func_action->val[1].u.number+2].u.number =
+                               $1;
                        mod_func_action->val[1].u.number++;
                } else {
                        yyerror("Too many arguments\n");
@@ -2455,21 +2745,160 @@ func_param:
 extern int line;
 extern int column;
 extern int startcolumn;
-static void warn(char* s)
+extern int startline;
+
+
+static void get_cpos(struct cfg_pos* pos)
 {
-       LOG(L_WARN, "cfg. warning: (%d,%d-%d): %s\n", line, startcolumn,
-                       column, s);
+       pos->s_line=startline;
+       pos->e_line=line;
+       pos->s_col=startcolumn;
+       pos->e_col=column-1;
+}
+
+
+static void warn_at(struct cfg_pos* p, char* format, ...)
+{
+       va_list ap;
+       char s[256];
+       
+       va_start(ap, format);
+       vsnprintf(s, sizeof(s), format, ap);
+       va_end(ap);
+       if (p->e_line!=p->s_line)
+               LOG(L_WARN, "cfg. warning: (%d,%d-%d,%d): %s\n",
+                                       p->s_line, p->s_col, p->e_line, p->e_col, s);
+       else if (p->s_col!=p->e_col)
+               LOG(L_WARN, "cfg. warning: (%d,%d-%d): %s\n",
+                                       p->s_line, p->s_col, p->e_col, s);
+       else
+               LOG(L_WARN, "cfg. warning: (%d,%d): %s\n",
+                               p->s_line, p->s_col, s);
        cfg_warnings++;
 }
 
-static void yyerror(char* s)
+
+
+static void yyerror_at(struct cfg_pos* p, char* format, ...)
 {
-       LOG(L_CRIT, "*** PARSE ERROR *** (%d,%d-%d): %s\n", line, startcolumn,
-                       column, s);
+       va_list ap;
+       char s[256];
+       
+       va_start(ap, format);
+       vsnprintf(s, sizeof(s), format, ap);
+       va_end(ap);
+       if (p->e_line!=p->s_line)
+               LOG(L_CRIT, "*** PARSE ERROR *** (%d,%d-%d,%d): %s\n", 
+                                       p->s_line, p->s_col, p->e_line, p->e_col, s);
+       else if (p->s_col!=p->e_col)
+               LOG(L_CRIT, "*** PARSE ERROR *** (%d,%d-%d): %s\n", 
+                                       p->s_line, p->s_col, p->e_col, s);
+       else
+               LOG(L_CRIT, "*** PARSE ERROR *** (%d,%d): %s\n", 
+                                       p->s_line, p->s_col, s);
        cfg_errors++;
 }
 
 
+
+static void warn(char* format, ...)
+{
+       va_list ap;
+       char s[256];
+       struct cfg_pos pos;
+       
+       get_cpos(&pos);
+       va_start(ap, format);
+       vsnprintf(s, sizeof(s), format, ap);
+       va_end(ap);
+       warn_at(&pos, s);
+}
+
+
+
+static void yyerror(char* format, ...)
+{
+       va_list ap;
+       char s[256];
+       struct cfg_pos pos;
+       
+       get_cpos(&pos);
+       va_start(ap, format);
+       vsnprintf(s, sizeof(s), format, ap);
+       va_end(ap);
+       yyerror_at(&pos, s);
+}
+
+
+
+/** mk_rval_expr_v wrapper.
+ *  checks mk_rval_expr_v return value and sets the cfg. pos
+ *  (line and column numbers)
+ *  @return rval_expr* on success, 0 on error (@see mk_rval_expr_v)
+ */
+static struct rval_expr* mk_rve_rval(enum rval_type type, void* v)
+{
+       struct rval_expr* ret;
+       struct cfg_pos pos;
+
+       get_cpos(&pos);
+       ret=mk_rval_expr_v(type, v, &pos);
+       if (ret==0){
+               yyerror("internal error: failed to create rval expr");
+               /* YYABORT; */
+       }
+       return ret;
+}
+
+
+/** mk_rval_expr1 wrapper.
+ *  checks mk_rval_expr1 return value (!=0 and type checking)
+ *  @return rval_expr* on success, 0 on error (@see mk_rval_expr1)
+ */
+static struct rval_expr* mk_rve1(enum rval_expr_op op, struct rval_expr* rve1)
+{
+       struct rval_expr* ret;
+       struct rval_expr* bad_rve;
+       enum rval_type type, bad_t, exp_t;
+       
+       if (rve1==0)
+               return 0;
+       ret=mk_rval_expr1(op, rve1, &rve1->fpos);
+       if (ret && (rve_check_type(&type, ret, &bad_rve, &bad_t, &exp_t)!=1)){
+               yyerror_at(&rve1->fpos, "bad expression: type mismatch"
+                                       " (%s instead of %s)", rval_type_name(bad_t),
+                                       rval_type_name(exp_t));
+       }
+       return ret;
+}
+
+
+/** mk_rval_expr2 wrapper.
+ *  checks mk_rval_expr2 return value (!=0 and type checking)
+ *  @return rval_expr* on success, 0 on error (@see mk_rval_expr2)
+ */
+static struct rval_expr* mk_rve2(enum rval_expr_op op, struct rval_expr* rve1,
+                                                                       struct rval_expr* rve2)
+{
+       struct rval_expr* ret;
+       struct rval_expr* bad_rve;
+       enum rval_type type, bad_t, exp_t;
+       struct cfg_pos pos;
+       
+       if ((rve1==0) || (rve2==0))
+               return 0;
+       cfg_pos_join(&pos, &rve1->fpos, &rve2->fpos);
+       ret=mk_rval_expr2(op, rve1, rve2, &pos);
+       if (ret && (rve_check_type(&type, ret, &bad_rve, &bad_t, &exp_t)!=1)){
+               yyerror_at(&pos, "bad expression: type mismatch:"
+                                               " %s instead of %s at (%d,%d)",
+                                               rval_type_name(bad_t), rval_type_name(exp_t),
+                                               bad_rve->fpos.s_line, bad_rve->fpos.s_col);
+       }
+       return ret;
+}
+
+
 static struct name_lst* mk_name_lst(char* host, int flags)
 {
        struct name_lst* l;
@@ -2554,6 +2983,101 @@ static void free_socket_id_lst(struct socket_id* lst)
        }
 }
 
+
+/** create a temporary case statmenet structure.
+ *  *err will be filled in case of error (return == 0):
+ *   -1 - non constant expression
+ *   -2 - expression error (bad type)
+ *   -10 - memory allocation error
+ */
+static struct case_stms* mk_case_stm(struct rval_expr* ct, int is_re,
+                                                                                       struct action* a, int* err)
+{
+       struct case_stms* s;
+       struct rval_expr* bad_rve;
+       enum rval_type type, bad_t, exp_t;
+       enum match_str_type t;
+       
+       t=MATCH_UNKNOWN;
+       if (ct){
+               /* if ct!=0 => case, else if ct==0 is a default */
+               if (!rve_is_constant(ct)){
+                       yyerror_at(&ct->fpos, "non constant expression in case");
+                       *err=-1;
+                       return 0;
+               }
+               if (rve_check_type(&type, ct, &bad_rve, &bad_t, &exp_t)!=1){
+                       yyerror_at(&ct->fpos, "bad expression: type mismatch:"
+                                                       " %s instead of %s at (%d,%d)",
+                                                       rval_type_name(bad_t), rval_type_name(exp_t),
+                                                       bad_rve->fpos.s_line, bad_rve->fpos.s_col);
+                       *err=-2;
+                       return 0;
+               }
+               if (is_re)
+                       t=MATCH_RE;
+               else if (type==RV_STR)
+                       t=MATCH_STR;
+               else
+                       t=MATCH_INT;
+       }
+
+       s=pkg_malloc(sizeof(*s));
+       if (s==0) {
+               yyerror("internal error: memory allocation failure");
+               *err=-10;
+       } else {
+               memset(s, 0, sizeof(*s));
+               s->ct_rve=ct;
+               s->type=t;
+               s->actions=a;
+               s->next=0;
+               s->append=0;
+       }
+       return s;
+}
+
+
+/*
+ * @return 0 on success, -1 on error.
+ */
+static int case_check_type(struct case_stms* stms)
+{
+       struct case_stms* c;
+       struct case_stms* s;
+       
+       for(c=stms; c ; c=c->next){
+               if (!c->ct_rve) continue;
+               for (s=c->next; s; s=s->next){
+                       if (!s->ct_rve) continue;
+                       if ((s->type!=c->type) &&
+                               !(      (c->type==MATCH_STR || c->type==MATCH_RE) &&
+                                       (s->type==MATCH_STR || s->type==MATCH_RE) ) ){
+                                       yyerror_at(&s->ct_rve->fpos, "type mismatch in case");
+                                       return -1;
+                       }
+               }
+       }
+       return 0;
+}
+
+
+/*
+ * @return 0 on success, -1 on error.
+ */
+static int case_check_default(struct case_stms* stms)
+{
+       struct case_stms* c;
+       int default_no;
+       
+       default_no=0;
+       for(c=stms; c ; c=c->next)
+               if (c->ct_rve==0) default_no++;
+       return (default_no<=1)?0:-1;
+}
+
+
+
 /*
 int main(int argc, char ** argv)
 {
index b25eb25..778a4dd 100644 (file)
@@ -89,6 +89,7 @@ struct cfg_group_core default_core_cfg = {
 #ifdef SHM_MEM
        0, /* mem_dump_shm */
 #endif
+       DEFAULT_MAX_WHILE_LOOPS, /* max_while_loops */
        0, /* udp_mtu (disabled by default) */
        0, /* udp_mtu_try_proto -> default disabled */
        0  /* force_rport */ 
@@ -181,6 +182,8 @@ cfg_def_t core_cfg_def[] = {
        {"mem_dump_shm",        CFG_VAR_INT,    0, 0, mem_dump_shm_fixup, 0,
                "dump shared memory status"},
 #endif
+       {"max_while_loops",     CFG_VAR_INT|CFG_ATOMIC, 0, 0, 0, 0,
+               "maximum iterations allowed for a while loop" },
        {"udp_mtu",     CFG_VAR_INT|CFG_ATOMIC, 0, 65535, 0, 0,
                "fallback to a congestion controlled protocol if send size"
                        " exceeds udp_mtu"},
index 8621cf1..4f493ef 100644 (file)
@@ -85,6 +85,7 @@ struct cfg_group_core {
 #ifdef SHM_MEM
        int mem_dump_shm;
 #endif
+       int max_while_loops;
        int udp_mtu; /**< maximum send size for udp, if > try another protocol*/
        int udp_mtu_try_proto; /**< if packet> udp_mtu, try proto (e.g. TCP) */
        int force_rport; /**< if set rport will always be forced*/
index b9cc4c3..317ff1c 100644 (file)
--- a/config.h
+++ b/config.h
@@ -70,6 +70,8 @@
 
 #define MAX_URI_SIZE 1024      /* used when rewriting URIs */
 
+#define MAX_PATH_SIZE 256 /* Maximum length of path header buffer */
+
 #define MY_VIA "Via: SIP/2.0/UDP "
 #define MY_VIA_LEN (sizeof(MY_VIA) - 1)
 
 
 #define DEFAULT_DID "_default"
 
+/*  maximum allowed iterations for a while (to catch runaways) */
+#define DEFAULT_MAX_WHILE_LOOPS 100
+
 #endif
index a4762c7..2dafdd8 100644 (file)
@@ -199,7 +199,8 @@ static void system_listMethods(rpc_t* rpc, void* c)
        }
 
        for(t = modules; t; t = t->next) {
-               for(ptr = t->exports->rpc_methods; ptr && ptr->name; ptr++) {
+               if (t->mod_interface_ver!=0) continue;
+               for(ptr = t->exports->v0.rpc_methods; ptr && ptr->name; ptr++) {
                        if (rpc->add(c, "s", ptr->name) < 0) return;
                }
        }
@@ -233,7 +234,8 @@ static void system_methodHelp(rpc_t* rpc, void* c)
        }
 
        for(t = modules; t; t = t->next) {
-               for(ptr = t->exports->rpc_methods; ptr && ptr->name; ptr++) {
+               if (t->mod_interface_ver!=0) continue;
+               for(ptr = t->exports->v0.rpc_methods; ptr && ptr->name; ptr++) {
                        if (strcmp(name, ptr->name) == 0) {
                                if (ptr->doc_str && ptr->doc_str[0]) {
                                        rpc->add(c, "s", ptr->doc_str[0]);
diff --git a/db/Makefile b/db/Makefile
deleted file mode 100644 (file)
index e2426c8..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# This is a convenience make which just runs the makefile
-# in parent directory
-#
-
-all clean distclean:
-       $(MAKE) -C .. $(MAKECMDGOALS)
-
index d4135bb..924323d 100644 (file)
@@ -1,14 +1,14 @@
 /**
- * \mainpage SER Development Documentation
+ * \mainpage The SIP Router Development Documentation
  *
- * The development documentation for SER consists of annotated and documented
+ * This development documentation contains annotated and documented
  * source files. For the sake of clarity the documentation is split into the
  * following groups:
  *
  * \section db_layer Database Abstraction Layer
  *
  * \ref db_api Database Abstraction Layer is a group of data structures and functions that
- * are used by all SER modules that need to access a database.
+ * are used by all SIP Router modules that need to access a database.
  *
- * \section ser_modules SER Modules
+ * \section sr_modules SIP Router Modules
  */
index ace2d78..b5bfdb8 100644 (file)
--- a/dprint.c
+++ b/dprint.c
@@ -63,6 +63,7 @@ static int int_fac[]={LOG_AUTH ,  LOG_CRON , LOG_DAEMON ,
 
 struct log_level_info log_level_info[] = {
        {"ALERT", LOG_ALERT},     /* L_ALERT */
+       {"CRITICAL", LOG_CRIT},   /* L_CRIT2 */
        {"BUG", LOG_CRIT},        /* L_CRIT */
        {"ERROR", LOG_ERR},       /* L_ERR */
        {"WARNING", LOG_WARNING}, /* L_WARN */
index 083bd2e..a4d1deb 100644 (file)
--- a/dprint.h
+++ b/dprint.h
 /*
  * Log levels
  */
-#define L_ALERT                -3
-#define L_CRIT         -2
+#define L_ALERT                -4
+#define L_CRIT2                -3  /* like L_CRIT, but not used for BUGs */
+#define L_CRIT         -2  /* used only for BUG */
 #define L_ERR          -1
 #define L_WARN         0
 #define L_NOTICE       1
 #define L_INFO         2
 #define L_DBG          3
 
+/* This is the facility value used to indicate that the caller of the macro
+ * did not override the facility. Value 0 (the defaul) is LOG_KERN on Linux
+ */
+#define DEFAULT_FACILITY 0
+
 #define LOG_LEVEL2NAME(level)  (log_level_info[(level) - (L_ALERT)].name)
 #define LOG2SYSLOG_LEVEL(level) \
        (log_level_info[(level) - (L_ALERT)].syslog_level)
@@ -100,6 +106,7 @@ struct log_level_info {
        int syslog_level;
 };
 
+#define is_printable(level) (cfg_get(core, core_cfg, debug)>=(level))
 extern struct log_level_info log_level_info[];
 
 #ifndef NO_SIG_DEBUG
@@ -123,11 +130,13 @@ int log_facility_fixup(void *handle, str *gname, str *name, void **val);
 #ifdef NO_LOG
 
 #      ifdef __SUNPRO_C
-#              define LOG_(level, prefix, fmt, ...)
+#              define LOG_(facility, level, prefix, fmt, ...)
 #              define LOG(level, fmt, ...)
+#              define LOG_FC(facility, level, fmt, ...)
 #      else
-#              define LOG_(level, prefix, fmt, args...)
+#              define LOG_(facility, level, prefix, fmt, args...)
 #              define LOG(level, fmt, args...)
+#              define LOG_FC(facility, level, fmt, args...)
 #      endif
 
 #else
@@ -143,7 +152,7 @@ int log_facility_fixup(void *handle, str *gname, str *name, void **val);
 #      endif
 
 #      ifdef __SUNPRO_C
-#              define LOG_(level, prefix, fmt, ...) \
+#              define LOG_(facility, level, prefix, fmt, ...) \
                        do { \
                                if (unlikely(cfg_get(core, core_cfg, debug) >= (level) && \
                                                DPRINT_NON_CRIT)) { \
@@ -156,7 +165,9 @@ int log_facility_fixup(void *handle, str *gname, str *name, void **val);
                                                                        __VA_ARGS__); \
                                                } else { \
                                                        syslog(LOG2SYSLOG_LEVEL(level) | \
-                                                                       cfg_get(core, core_cfg, log_facility),\
+                                                                  (((facility) != DEFAULT_FACILITY) ? \
+                                                                       (facility) : \
+                                                                       cfg_get(core, core_cfg, log_facility)), \
                                                                        "%s: %s" fmt, LOG_LEVEL2NAME(level),\
                                                                        (prefix), __VA_ARGS__); \
                                                } \
@@ -168,22 +179,29 @@ int log_facility_fixup(void *handle, str *gname, str *name, void **val);
                                                } else { \
                                                        if ((level)<L_ALERT) \
                                                                syslog(LOG2SYSLOG_LEVEL(L_ALERT) | \
-                                                                               cfg_get(core, core_cfg, log_facility),\
-                                                                               "%s" fmt, (prefix), __VA_ARGS__); \
+                                                                          (((facility) != DEFAULT_FACILITY) ? \
+                                                                               (facility) : \
+                                                                               cfg_get(core, core_cfg, log_facility)),\
+                                                                          "%s" fmt, (prefix), __VA_ARGS__); \
                                                        else \
                                                                syslog(LOG2SYSLOG_LEVEL(L_DBG) | \
-                                                                               cfg_get(core, core_cfg, log_facility),\
-                                                                               "%s" fmt, (prefix), __VA_ARGS__); \
+                                                                          (((facility) != DEFAULT_FACILITY) ? \
+                                                                               (facility) : \
+                                                                               cfg_get(core, core_cfg, log_facility)),\
+                                                                          "%s" fmt, (prefix), __VA_ARGS__); \
                                                } \
                                        } \
                                        DPRINT_CRIT_EXIT; \
                                } \
                        } while(0)
                        
-#              define LOG(level, fmt, ...)  LOG_((level), LOC_INFO, fmt, __VA_ARGS__)
+#              define LOG(level, fmt, ...) \
+       LOG_(DEFAULT_FACILITY, (level), LOC_INFO, fmt, __VA_ARGS__)
+#              define LOG_FC(facility, level, fmt, ...) \
+       LOG_((facility), (level), LOC_INFO, fmt, __VA_ARGS__)
 
 #      else /* ! __SUNPRO_C */
-#              define LOG_(level, prefix, fmt, args...) \
+#              define LOG_(facility, level, prefix, fmt, args...) \
                        do { \
                                if (cfg_get(core, core_cfg, debug) >= (level) && \
                                                DPRINT_NON_CRIT) { \
@@ -195,7 +213,9 @@ int log_facility_fixup(void *handle, str *gname, str *name, void **val);
                                                                        LOG_LEVEL2NAME(level),(prefix), ## args);\
                                                } else { \
                                                        syslog(LOG2SYSLOG_LEVEL(level) |\
-                                                                       cfg_get(core, core_cfg, log_facility), \
+                                                                  (((facility) != DEFAULT_FACILITY) ? \
+                                                                       (facility) : \
+                                                                       cfg_get(core, core_cfg, log_facility)), \
                                                                        "%s: %s" fmt, LOG_LEVEL2NAME(level),\
                                                                        (prefix), ## args); \
                                                } \
@@ -207,11 +227,15 @@ int log_facility_fixup(void *handle, str *gname, str *name, void **val);
                                                } else { \
                                                        if ((level)<L_ALERT) \
                                                                syslog(LOG2SYSLOG_LEVEL(L_ALERT) | \
-                                                                               cfg_get(core, core_cfg, log_facility),\
+                                                                          (((facility) != DEFAULT_FACILITY) ? \
+                                                                               (facility) : \
+                                                                               cfg_get(core, core_cfg, log_facility)),\
                                                                                "%s" fmt, (prefix), ## args); \
                                                        else \
                                                                syslog(LOG2SYSLOG_LEVEL(L_DBG) | \
-                                                                               cfg_get(core, core_cfg, log_facility),\
+                                                                          (((facility) != DEFAULT_FACILITY) ? \
+                                                                               (facility) : \
+                                                                               cfg_get(core, core_cfg, log_facility)),\
                                                                                "%s" fmt, (prefix), ## args); \
                                                } \
                                        } \
@@ -219,7 +243,10 @@ int log_facility_fixup(void *handle, str *gname, str *name, void **val);
                                } \
                        } while(0)
                        
-#              define LOG(level, fmt, args...)  LOG_((level), LOC_INFO, fmt, ## args)
+#              define LOG(level, fmt, args...) \
+       LOG_(DEFAULT_FACILITY, (level), LOC_INFO, fmt, ## args)
+#              define LOG_FC(facility, level, fmt, args...) \
+       LOG_((facility), (level), LOC_INFO, fmt, ## args)
                
 #      endif /* __SUNPRO_C */
 #endif /* NO_LOG */
@@ -235,6 +262,7 @@ int log_facility_fixup(void *handle, str *gname, str *name, void **val);
 #      define WARN(...)   LOG(L_WARN,   __VA_ARGS__)
 #      define NOTICE(...) LOG(L_NOTICE, __VA_ARGS__)
 #      define INFO(...)   LOG(L_INFO,   __VA_ARGS__)
+#      define CRIT(...)    LOG(L_CRIT2,   __VA_ARGS__)
 
 #      ifdef NO_DEBUG
 #              define DBG(...)
@@ -244,14 +272,15 @@ int log_facility_fixup(void *handle, str *gname, str *name, void **val);
 
 /* obsolete, do not use */
 #      define DEBUG(...) DBG(__VA_ARGS__)
-               
-#else
+
+#else /* ! __SUNPRO_C */
 #      define ALERT(fmt, args...)  LOG(L_ALERT,  fmt, ## args)
 #      define BUG(fmt, args...)    LOG(L_CRIT,   fmt, ## args)
 #      define ERR(fmt, args...)    LOG(L_ERR,    fmt, ## args)
 #      define WARN(fmt, args...)   LOG(L_WARN,   fmt, ## args)
 #      define NOTICE(fmt, args...) LOG(L_NOTICE, fmt, ## args)
 #      define INFO(fmt, args...)   LOG(L_INFO,   fmt, ## args)
+#      define CRIT(fmt, args...)   LOG(L_CRIT2,   fmt, ## args)
 
 #      ifdef NO_DEBUG
 #              define DBG(fmt, args...)
@@ -265,4 +294,16 @@ int log_facility_fixup(void *handle, str *gname, str *name, void **val);
 #endif /* __SUNPRO_C */
 
 
+/* kamailio/openser compatibility */
+
+#define LM_GEN1 LOG
+#define LM_GEN2 LOG_FC
+#define LM_ALERT ALERT
+#define LM_CRIT  CRIT
+#define LM_ERR ERR
+#define LM_WARN WARN
+#define LM_NOTICE NOTICE
+#define LM_INFO INFO
+#define LM_DBG DEBUG
+
 #endif /* !dprint_h */
diff --git a/dset.c b/dset.c
index ab58dca..cbc636b 100644 (file)
--- a/dset.c
+++ b/dset.c
@@ -57,9 +57,16 @@ struct branch
        char dst_uri[MAX_URI_SIZE];
        unsigned int dst_uri_len;
 
+       /* Path set */
+       char path[MAX_PATH_SIZE];
+       unsigned int path_len;
+
        int q; /* Preference of the contact among
                * contact within the array */
        struct socket_info* force_send_socket;
+
+       /* Branch flags */
+       flag_t flags;
 };
 
 
@@ -76,7 +83,66 @@ unsigned int nr_branches = 0;
 static int branch_iterator = 0;
 
 /* The q parameter of the Request-URI */
-static qvalue_t ruri_q = Q_UNSPECIFIED; 
+static qvalue_t ruri_q = Q_UNSPECIFIED;
+
+/* Branch flags of the Request-URI */
+static flag_t ruri_bflags;
+
+
+static inline flag_t* get_bflags_ptr(unsigned int branch)
+{
+       if (branch == 0) return &ruri_bflags;
+       if (branch - 1 < nr_branches) return &branches[branch - 1].flags;
+       return NULL;
+}
+
+
+int setbflag(unsigned int branch, flag_t flag)
+{
+       flag_t* flags;
+
+       if ((flags = get_bflags_ptr(branch)) == NULL) return -1;
+       (*flags) |= 1 << flag;
+       return 1;
+}
+
+
+int isbflagset(unsigned int branch, flag_t flag)
+{
+       flag_t* flags;
+
+       if ((flags = get_bflags_ptr(branch)) == NULL) return -1;
+       return ((*flags) & (1 << flag)) ? 1 : -1;
+}
+
+
+int resetbflag(unsigned int branch, flag_t flag)
+{
+       flag_t* flags;
+
+       if ((flags = get_bflags_ptr(branch)) == NULL) return -1;
+       (*flags) &= ~ (1 << flag);
+       return 1;
+}
+
+
+int getbflags(flag_t* res, unsigned int branch)
+{
+       flag_t* flags;
+       if (res == NULL) return -1;
+       if ((flags = get_bflags_ptr(branch)) == NULL) return -1;
+       *res = *flags;
+       return 1;
+}
+
+
+int setbflagsval(unsigned int branch, flag_t val)
+{
+       flag_t* flags;
+       if ((flags = get_bflags_ptr(branch)) == NULL) return -1;
+       *flags = val;
+       return 1;
+}
 
 
 /*
@@ -127,6 +193,46 @@ char* next_branch(int* len, qvalue_t* q, char** dst_uri, int* dst_len, struct so
 }
 
 
+/** \brief Get a branch from the destination set
+ * \return Return the 'i' branch from the dset
+ * array, 0 is returned if there are no
+ * more branches
+ */
+char* get_branch(unsigned int i, int* len, qvalue_t* q, str* dst_uri,
+                                str* path, unsigned int *flags, struct socket_info** force_socket)
+{
+       if (i < nr_branches) {
+               *len = branches[i].len;
+               *q = branches[i].q;
+               if (dst_uri) {
+                       dst_uri->len = branches[i].dst_uri_len;
+                       dst_uri->s = (dst_uri->len)?branches[i].dst_uri:0;
+               }
+               if (path) {
+                       path->len = branches[i].path_len;
+                       path->s = (path->len)?branches[i].path:0;
+               }
+               if (force_socket)
+                       *force_socket = branches[i].force_send_socket;
+               if (flags)
+                       *flags = branches[i].flags;
+               return branches[i].uri;
+       } else {
+               *len = 0;
+               *q = Q_UNSPECIFIED;
+               if (dst_uri) {
+                       dst_uri->s = 0;
+                       dst_uri->len = 0;
+               }
+               if (force_socket)
+                       *force_socket = 0;
+               if (flags)
+                       *flags = 0;
+               return 0;
+       }
+}
+
+
 /*
  * Empty the dset array
  */
@@ -197,6 +303,87 @@ int append_branch(struct sip_msg* msg, char* uri, int uri_len, char* dst_uri, in
 }
 
 
+/* ! \brief
+ * Add a new branch to current transaction using str parameters
+ * Kamailio compatibility version
+ */
+int km_append_branch(struct sip_msg* msg, str* uri, str* dst_uri, str* path,
+               qvalue_t q, unsigned int flags, struct socket_info* force_socket)
+{
+       str luri;
+
+#ifdef USE_LOCAL_ROUTE
+       if (dset_state==0)
+               return -1;
+#endif
+
+       /* if we have already set up the maximum number
+        * of branches, don't try new ones 
+        */
+       if (nr_branches == MAX_BRANCHES - 1) {
+               LOG(L_ERR, "max nr of branches exceeded\n");
+               ser_error = E_TOO_MANY_BRANCHES;
+               return -1;
+       }
+
+       /* if not parameterized, take current uri */
+       if (uri==0 || uri->len==0 || uri->s==0) {
+               if (msg->new_uri.s)
+                       luri = msg->new_uri;
+               else
+                       luri = msg->first_line.u.request.uri;
+       } else {
+               luri = *uri;
+       }
+
+       if (luri.len > MAX_URI_SIZE - 1) {
+               LOG(L_ERR, "too long uri: %.*s\n", luri.len, luri.s);
+               return -1;
+       }
+
+       /* copy the dst_uri */
+       if (dst_uri && dst_uri->len && dst_uri->s) {
+               if (dst_uri->len > MAX_URI_SIZE - 1) {
+                       LOG(L_ERR, "too long dst_uri: %.*s\n",
+                               dst_uri->len, dst_uri->s);
+                       return -1;
+               }
+               memcpy(branches[nr_branches].dst_uri, dst_uri->s, dst_uri->len);
+               branches[nr_branches].dst_uri[dst_uri->len] = 0;
+               branches[nr_branches].dst_uri_len = dst_uri->len;
+       } else {
+               branches[nr_branches].dst_uri[0] = '\0';
+               branches[nr_branches].dst_uri_len = 0;
+       }
+
+       /* copy the path string */
+       if (path && path->len && path->s) {
+               if (path->len > MAX_PATH_SIZE - 1) {
+                       LOG(L_ERR, "too long path: %.*s\n", path->len, path->s);
+                       return -1;
+               }
+               memcpy(branches[nr_branches].path, path->s, path->len);
+               branches[nr_branches].path[path->len] = 0;
+               branches[nr_branches].path_len = path->len;
+       } else {
+               branches[nr_branches].path[0] = '\0';
+               branches[nr_branches].path_len = 0;
+       }
+
+       /* copy the ruri */
+       memcpy(branches[nr_branches].uri, luri.s, luri.len);
+       branches[nr_branches].uri[luri.len] = 0;
+       branches[nr_branches].len = luri.len;
+       branches[nr_branches].q = q;
+
+       branches[nr_branches].force_send_socket = force_socket;
+       branches[nr_branches].flags = flags;
+
+       nr_branches++;
+       return 1;
+}
+
+
 /*
  * Create a Contact header field from the dset
  * array
diff --git a/dset.h b/dset.h
index c23d4ce..e350238 100644 (file)
--- a/dset.h
+++ b/dset.h
@@ -30,6 +30,7 @@
 
 #include "ip_addr.h"
 #include "qvalue.h"
+#include "flags.h"
 
 struct sip_msg;
 
@@ -43,6 +44,10 @@ int append_branch(struct sip_msg* msg, char* uri, int uri_len, char* dst_uri, in
                  qvalue_t q, struct socket_info* force_socket);
 
 
+int km_append_branch(struct sip_msg* msg, str* uri, str* dst_uri, str* path,
+                                        qvalue_t q, unsigned int flags, struct socket_info* force_socket);
+
+
 /* 
  * Iterate through the list of transaction branches 
  */
@@ -55,6 +60,10 @@ void init_branch_iterator(void);
 char* next_branch(int* len, qvalue_t* q, char** dst_uri, int* dst_len, struct socket_info** force_socket);
 
 
+char* get_branch( unsigned int i, int* len, qvalue_t* q, str* dst_uri,
+                                 str* path, unsigned int *flags, struct socket_info** force_socket);
+
+
 /*
  * Empty the array of branches
  */
@@ -82,5 +91,56 @@ qvalue_t get_ruri_q(void);
 int get_request_uri(struct sip_msg* _m, str* _u);
 int rewrite_uri(struct sip_msg* _m, str* _s);
 
+/**
+ * Set a per-branch flag to 1.
+ *
+ * This function sets the value of one particular branch flag to 1.
+ * @param branch Number of the branch (0 for the main Request-URI branch)
+ * @param flag Number of the flag to be set (starting with 0)
+ * @return 1 on success, -1 on failure.
+ */
+int setbflag(unsigned int branch, flag_t flag);
+
+/**
+ * Reset a per-branch flag value to 0.
+ *
+ * This function resets the value of one particular branch flag to 0.
+ * @param branch Number of the branch (0 for the main Request-URI branch)
+ * @param flag Number of the flag to be reset (starting with 0)
+ * @return 1 on success, -1 on failure.
+ */
+int resetbflag(unsigned int branch, flag_t flag);
+
+/**
+ * Determine if a branch flag is set.
+ *
+ * This function tests the value of one particular per-branch flag.
+ * @param branch Number of the branch (0 for the main Request-URI branch)
+ * @param flag Number of the flag to be tested (starting with 0)
+ * @return 1 if the branch flag is set, -1 if not or on failure.
+ */
+int isbflagset(unsigned int branch, flag_t flag);
+
+/**
+ * Get the value of all branch flags for a branch
+ *
+ * This function returns the value of all branch flags
+ * combined in a single variable.
+ * @param res A pointer to a variable to store the result
+ * @param branch Number of the branch (0 for the main Request-URI branch)
+ * @return 1 on success, -1 on failure
+ */
+int getbflags(flag_t* res, unsigned int branch);
+
+/**
+ * Set the value of all branch flags at once for a given branch.
+ *
+ * This function sets the value of all branch flags for a given
+ * branch at once.
+ * @param branch Number of the branch (0 for the main Request-URI branch)
+ * @param val All branch flags combined into a single variable
+ * @return 1 on success, -1 on failure
+ */
+int setbflagsval(unsigned int branch, flag_t val);
 
 #endif /* _DSET_H */
diff --git a/flags.c b/flags.c
index e9f559e..70c3333 100644 (file)
--- a/flags.c
+++ b/flags.c
 #include "clist.h"
 #include "mem/mem.h"
 
+/* Script flags */
+static flag_t sflags = 0;
+
+
 int setflag( struct sip_msg* msg, flag_t flag ) {
        msg->flags |= 1 << flag;
        return 1;
@@ -72,6 +76,39 @@ int flag_in_range( flag_t flag ) {
 }
 
 
+int setsflagsval(flag_t val)
+{
+       sflags = val;
+       return 1;
+}
+
+
+int setsflag(flag_t flag)
+{
+       sflags |= 1 << flag;
+       return 1;
+}
+
+
+int resetsflag(flag_t flag)
+{
+       sflags &= ~ (1 << flag);
+       return 1;
+}
+
+
+int issflagset(flag_t flag)
+{
+       return (sflags & (1<<flag)) ? 1 : -1;
+}
+
+
+flag_t getsflags(void)
+{
+       return sflags;
+}
+
+
 /* use 2^k */
 #define FLAGS_NAME_HASH_ENTRIES                32
 
diff --git a/flags.h b/flags.h
index 62b3235..30809fe 100644 (file)
--- a/flags.h
+++ b/flags.h
@@ -43,6 +43,26 @@ int setflag( struct sip_msg* msg, flag_t flag );
 int resetflag( struct sip_msg* msg, flag_t flag );
 int isflagset( struct sip_msg* msg, flag_t flag );
 
+
+/* Script flag functions. Script flags are global flags that keep their
+ * value regardless of the SIP message being processed.
+ */
+
+/* Set the value of all the global flags */
+int setsflagsval(flag_t val);
+
+/* Set the given flag to 1. Parameter flag contains the index of the flag */
+int setsflag(flag_t flag);
+
+/* Reset the given flag to 0. Parameter flag contains the index of the flag */
+int resetsflag(flag_t flag);
+
+/* Returns 1 if the given flag is set and -1 otherwise */
+int issflagset(flag_t flag);
+
+/* Get the value of all the script flags combined */
+flag_t getsflags(void);
+
 int flag_in_range( flag_t flag );
 
 int register_flag(char* name, int pos);
index fdaf6f3..50bcbe4 100644 (file)
--- a/ip_addr.h
+++ b/ip_addr.h
@@ -113,6 +113,7 @@ struct socket_info{
        struct socket_info* prev;
        unsigned short port_no;  /* port number */
        char proto; /* tcp or udp*/
+       str sock_str; /* Socket proto, ip, and port as string */
        struct addr_info* addr_info_lst; /* extra addresses (e.g. SCTP mh) */
 };
 
index 289b9f6..072ed94 100644 (file)
@@ -26,6 +26,9 @@ xcap - Common XCAP operations and structures (XCAP authorization documents
 
 Used by modules: pa, rls, dialog, rpa
 
+trie - Common digit trie implementation for prefix matching, used by
+       carrierroute and userblacklist
+
 Usage:
 -----
 
diff --git a/lib/kmi/Makefile b/lib/kmi/Makefile
new file mode 100644 (file)
index 0000000..9a70acf
--- /dev/null
@@ -0,0 +1,9 @@
+include ../../Makefile.defs
+auto_gen=
+NAME:=kmi
+MAJOR_VER=1
+MINOR_VER=0
+BUGFIX_VER=0
+LIBS=
+
+include ../../Makefile.libs
diff --git a/lib/kmi/attr.c b/lib/kmi/attr.c
new file mode 100644 (file)
index 0000000..1f254d0
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * $Id: attr.c 4518 2008-07-28 15:39:28Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Attributes
+ * \ingroup mi
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "attr.h"
+#include "fmt.h"
+
+
+extern char *mi_ap_buf;
+extern int  mi_ap_buf_len;
+
+
+struct mi_attr *add_mi_attr(struct mi_node *node, int flags,
+                                               char *name, int name_len, char *value, int value_len)
+{
+       struct mi_attr *new, *p;
+       int size_mem, name_pos, value_pos;
+
+       if(!node)
+               return NULL;
+
+       if (!name) name_len=0;
+       if (!name_len) name=0;
+       if (!value) value_len=0;
+       if (!value_len) value=0;
+
+       if(!name && !value)
+               return NULL;
+
+       size_mem = sizeof(struct mi_attr);
+       value_pos = name_pos = 0;
+
+       if(name && (flags & MI_DUP_NAME)){
+               name_pos = size_mem;
+               size_mem += name_len;
+       }
+       if(value && (flags & MI_DUP_VALUE)){
+               value_pos = size_mem;
+               size_mem += value_len;
+       }
+
+       new = (struct mi_attr *)pkg_malloc(size_mem);
+       if (!new) {
+               LM_ERR("no more pkg mem (%d)\n",size_mem);
+               return NULL;
+       }
+       memset(new,0,size_mem);
+
+       if (name) {
+               new->name.len = name_len;
+               if(flags & MI_DUP_NAME){
+                       new->name.s = ((char *)new) + name_pos;
+                       strncpy(new->name.s, name, name_len);
+               } else{
+                       new->name.s = name;
+               }
+       }
+
+       if (value) {
+               new->value.len = value_len;
+               if(flags & MI_DUP_VALUE){
+                       new->value.s = ((char *)new) + value_pos;
+                       strncpy(new->value.s, value, value_len);
+               }else{
+                       new->value.s = value;
+               }
+       }
+       if(flags & MI_DUP_NAME){
+               name_pos = size_mem;
+               size_mem += name_len * sizeof(char);
+       }
+       if(flags & MI_DUP_VALUE){
+               value_pos = size_mem;
+               size_mem += value_len * sizeof(char);
+       }
+
+       if(!(node->attributes)){
+               new->next = NULL;
+               return (node->attributes = new);
+       }
+
+       for(p = node->attributes ; p->next ; p = p->next);
+
+       new->next = NULL;
+       p->next = new;
+
+       return new;
+}
+
+
+
+struct mi_attr *addf_mi_attr(struct mi_node *node, int flags,
+                                                       char *name, int name_len, char *fmt_val, ...)
+{
+       va_list ap;
+       char *p;
+       int  len = 0;
+
+       va_start(ap, fmt_val);
+       p = mi_print_fmt( fmt_val, ap, &len);
+       va_end(ap);
+       if (p==NULL)
+               return 0;
+       return add_mi_attr(node, flags|MI_DUP_VALUE, name, name_len, p, len);
+}
+
+
+
+struct mi_attr *get_mi_attr_by_name(struct mi_node *node, char *name, int len)
+{
+       struct mi_attr *head;
+
+       if(!node || !name || !(node->attributes))
+               return NULL;
+
+       for(head = node->attributes ; head->next ; head = head->next)
+               if(len == head->name.len 
+               && !strncasecmp(name, head->name.s, head->name.len))
+                       return head;
+
+       return NULL;
+}
+
+
+void del_mi_attr_list(struct mi_node *node)
+{
+       struct mi_attr *p, *head;
+
+       if(!node || !(node->attributes))
+               return;
+
+       for(head = node->attributes; head ;){
+               p = head->next;
+               pkg_free(head);
+               head = p;
+       }
+
+       node->attributes = NULL;
+}
+
diff --git a/lib/kmi/attr.h b/lib/kmi/attr.h
new file mode 100644 (file)
index 0000000..179dab7
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * $Id: attr.h 4518 2008-07-28 15:39:28Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Attributes
+ * \ingroup mi
+ */
+
+#ifndef _MI_ATTR_H
+#define _MI_ATTR_H
+
+#include <stdarg.h>
+#include "../../str.h"
+#include "tree.h"
+
+struct mi_attr{
+       str name;
+       str value;
+       struct mi_attr *next;
+};
+
+
+struct mi_attr *add_mi_attr(struct mi_node *node, int flags,
+       char *name, int name_len, char *value, int value_len);
+
+struct mi_attr *addf_mi_attr(struct mi_node *node, int flags,
+       char *name, int name_len, char *fmt_val, ...);
+
+struct mi_attr *get_mi_attr_by_name(struct mi_node *node,
+       char *name, int len);
+
+void del_mi_attr_list(struct mi_node *node);
+
+
+#endif
+
diff --git a/lib/kmi/fmt.c b/lib/kmi/fmt.c
new file mode 100644 (file)
index 0000000..81ca917
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * $Id: fmt.c 4518 2008-07-28 15:39:28Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Format handling
+ * \ingroup mi
+ */
+
+
+#include <string.h>
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+
+char *mi_fmt_buf = 0;
+int  mi_fmt_buf_len = 0;
+
+
+int mi_fmt_init( unsigned int size )
+{
+       mi_fmt_buf = (char*)pkg_malloc(size);
+       if (mi_fmt_buf==NULL) {
+               LM_ERR("no more pkg mem\n");
+               return -1;
+       }
+       mi_fmt_buf_len = size;
+       return 0;
+}
+
+
diff --git a/lib/kmi/fmt.h b/lib/kmi/fmt.h
new file mode 100644 (file)
index 0000000..73a8027
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * $Id: fmt.h 4518 2008-07-28 15:39:28Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Format handling
+ * \ingroup mi
+ */
+
+
+
+#ifndef _MI_FMT_H_
+#define _MI_FMT_H_
+
+#include <stdarg.h>
+#include <errno.h>
+
+/*! \brief size of the buffer used for printing the FMT */
+#define DEFAULT_MI_FMT_BUF_SIZE 2048
+
+extern char *mi_fmt_buf;
+extern int  mi_fmt_buf_len;
+
+int mi_fmt_init( unsigned int size );
+
+static inline char* mi_print_fmt(char *fmt, va_list ap, int *len)
+{
+       int n;
+
+       if (mi_fmt_buf==NULL) {
+               if (mi_fmt_init(DEFAULT_MI_FMT_BUF_SIZE)!=0) {
+                       LM_ERR("failed to init\n");
+                       return 0;
+               }
+       }
+
+       n = vsnprintf( mi_fmt_buf, mi_fmt_buf_len, fmt, ap);
+       if (n<0 || n>=mi_fmt_buf_len) {
+               LM_ERR("formatting failed with n=%d, %s\n",n,strerror(errno));
+               return 0;
+       }
+
+       *len = n;
+       return mi_fmt_buf;
+}
+
+#endif
diff --git a/lib/kmi/mi.c b/lib/kmi/mi.c
new file mode 100644 (file)
index 0000000..6dfa709
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * $Id: mi.c 4565 2008-08-05 14:58:52Z klaus_darilion $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Attributes
+ * \ingroup mi
+ */
+
+/*!
+ * \defgroup mi Kamailio Management Interface
+ * 
+ * The Kamailio management interface (MI) is a plugin architecture with a few different 
+ * handlers that gives access to the management interface over various transports.
+ *
+ * The Kamailio core and modules register commands to the interface at runtime.
+ * Look into the various module documentation files for information of these
+ * commands.
+ *
+ */
+
+#include <string.h>
+
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "mi.h"
+
+static struct mi_cmd*  mi_cmds = 0;
+static int mi_cmds_no = 0;
+
+
+static inline int get_mi_id( char *name, int len)
+{
+       int n;
+       int i;
+
+       for( n=0,i=0 ; i<len ; n+=name[i] ,i++ );
+       return n;
+}
+
+
+static inline struct mi_cmd* lookup_mi_cmd_id(int id,char *name, int len)
+{
+       int i;
+
+       for( i=0 ; i<mi_cmds_no ; i++ ) {
+               if ( id==mi_cmds[i].id && len==mi_cmds[i].name.len &&
+               memcmp(mi_cmds[i].name.s,name,len)==0 )
+                       return &mi_cmds[i];
+       }
+
+       return 0;
+}
+
+
+int register_mi_mod( char *mod_name, mi_export_t *mis)
+{
+       int ret;
+       int i;
+
+       if (mis==0)
+               return 0;
+
+       for ( i=0 ; mis[i].name ; i++ ) {
+               ret = register_mi_cmd( mis[i].cmd, mis[i].name, mis[i].param,
+                       mis[i].init_f, mis[i].flags);
+               if (ret!=0) {
+                       LM_ERR("failed to register cmd <%s> for module %s\n",
+                                       mis[i].name,mod_name);
+               }
+       }
+       return 0;
+}
+
+
+int init_mi_child(void)
+{
+       int i;
+
+       for ( i=0 ; i<mi_cmds_no ; i++ ) {
+               if ( mi_cmds[i].init_f && mi_cmds[i].init_f()!=0 ) {
+                       LM_ERR("failed to init <%.*s>\n",
+                                       mi_cmds[i].name.len,mi_cmds[i].name.s);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+
+
+int register_mi_cmd( mi_cmd_f f, char *name, void *param,
+                                                                       mi_child_init_f in, unsigned int flags)
+{
+       struct mi_cmd *cmds;
+       int id;
+       int len;
+
+       if (f==0 || name==0) {
+               LM_ERR("invalid params f=%p, name=%s\n", f, name);
+               return -1;
+       }
+
+       if (flags&MI_NO_INPUT_FLAG && flags&MI_ASYNC_RPL_FLAG) {
+               LM_ERR("invalids flags for <%s> - "
+                       "async functions must take input\n",name);
+       }
+
+       len = strlen(name);
+       id = get_mi_id(name,len);
+
+       if (lookup_mi_cmd_id( id, name, len)) {
+               LM_ERR("command <%.*s> already registered\n", len, name);
+               return -1;
+       }
+
+       cmds = (struct mi_cmd*)pkg_realloc( mi_cmds,
+                       (mi_cmds_no+1)*sizeof(struct mi_cmd) );
+       if (cmds==0) {
+               LM_ERR("no more pkg memory\n");
+               return -1;
+       }
+
+       mi_cmds = cmds;
+       mi_cmds_no++;
+
+       cmds = &cmds[mi_cmds_no-1];
+
+       cmds->f = f;
+       cmds->init_f = in;
+       cmds->flags = flags;
+       cmds->name.s = name;
+       cmds->name.len = len;
+       cmds->id = id;
+       cmds->param = param;
+
+       return 0;
+}
+
+
+struct mi_cmd* lookup_mi_cmd( char *name, int len)
+{
+       int id;
+
+       id = get_mi_id(name,len);
+       return lookup_mi_cmd_id( id, name, len);
+}
+
+
+void get_mi_cmds( struct mi_cmd** cmds, int *size)
+{
+       *cmds = mi_cmds;
+       *size = mi_cmds_no;
+}
+
+
diff --git a/lib/kmi/mi.h b/lib/kmi/mi.h
new file mode 100644 (file)
index 0000000..5c0ac19
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * $Id: mi.h 5003 2008-09-26 11:01:51Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Management
+ * \ingroup mi
+ */
+
+
+#ifndef _MI_MI_H_
+#define _MI_MI_H_
+
+#include "../../str.h"
+#include "tree.h"
+
+#define MI_ASYNC_RPL_FLAG   (1<<0)
+#define MI_NO_INPUT_FLAG    (1<<1)
+
+#define MI_ROOT_ASYNC_RPL   ((struct mi_root*)-1)
+
+struct mi_handler;
+
+typedef struct mi_root* (mi_cmd_f)(struct mi_root*, void *param);
+typedef int (mi_child_init_f)(void);
+typedef void (mi_handler_f)(struct mi_root *, struct mi_handler *, int);
+
+
+struct mi_handler {
+       mi_handler_f *handler_f;
+       void * param;
+};
+
+
+struct mi_cmd {
+       int id;
+       str name;
+       mi_child_init_f *init_f;
+       mi_cmd_f *f;
+       unsigned int flags;
+       void *param;
+};
+
+
+typedef struct mi_export_ {
+       char *name;
+       mi_cmd_f *cmd;
+       unsigned int flags;
+       void *param;
+       mi_child_init_f *init_f;
+}mi_export_t;
+
+
+int register_mi_cmd( mi_cmd_f f, char *name, void *param,
+               mi_child_init_f in, unsigned int flags);
+
+int register_mi_mod( char *mod_name, mi_export_t *mis);
+
+int init_mi_child(void);
+
+struct mi_cmd* lookup_mi_cmd( char *name, int len);
+
+static inline struct mi_root* run_mi_cmd(struct mi_cmd *cmd, struct mi_root *t)
+{
+       return cmd->f( t, cmd->param);
+}
+
+void get_mi_cmds( struct mi_cmd** cmds, int *size);
+
+int init_mi_core(void);
+
+#endif
+
diff --git a/lib/kmi/mi_core.c b/lib/kmi/mi_core.c
new file mode 100644 (file)
index 0000000..debd599
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * $Id: mi_core.c 4762 2008-08-28 11:27:31Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * history:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+
+/*!
+ * \file 
+ * \brief MI :: Core 
+ * \ingroup mi
+ */
+
+
+
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include "../../dprint.h"
+#include "../../globals.h"
+#include "../../ut.h"
+#include "../../pt.h"
+#include "../../mem/mem.h"
+#include "mi.h"
+#include "../../version.h"
+
+#define BUILD_STR __FILE__ " compiled on "__TIME__ " " __DATE__ " with " COMPILER "\n"
+#define BUILD_STR_LEN (sizeof(BUILD_STR)-1)
+
+#ifndef SVNREVISION
+#define SVNREVISION "unknown"
+#endif
+
+static time_t kmi_up_since;
+static str    kmi_up_since_ctime;
+
+static int init_mi_uptime(void)
+{
+       char *p;
+
+       time(&kmi_up_since);
+       p = ctime(&kmi_up_since);
+       kmi_up_since_ctime.len = strlen(p)-1;
+       kmi_up_since_ctime.s = (char*)pkg_malloc(kmi_up_since_ctime.len);
+       if (kmi_up_since_ctime.s==0) {
+               LM_ERR("no more pkg mem\n");
+               return -1;
+       }
+       memcpy(kmi_up_since_ctime.s, p, kmi_up_since_ctime.len);
+       return 0;
+}
+
+
+static struct mi_root *mi_uptime(struct mi_root *cmd, void *param)
+{
+       struct mi_root *rpl_tree;
+       struct mi_node *rpl;
+       struct mi_node *node;
+       time_t now;
+       char   *p;
+
+       rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
+       if (rpl_tree==0)
+               return 0;
+       rpl = &rpl_tree->node;
+
+       time(&now);
+       p = ctime(&now);
+       node = add_mi_node_child( rpl, MI_DUP_VALUE, MI_SSTR("Now"),
+               p, strlen(p)-1);
+       if (node==0)
+               goto error;
+
+       node = add_mi_node_child( rpl, 0, MI_SSTR("Up since"),
+               kmi_up_since_ctime.s, kmi_up_since_ctime.len);
+       if (node==0)
+               goto error;
+
+       node = addf_mi_node_child( rpl, 0, MI_SSTR("Up time"),
+               "%lu [sec]", (unsigned long)difftime(now, kmi_up_since) );
+       if (node==0)
+               goto error;
+
+       return rpl_tree;
+error:
+       LM_ERR("failed to add node\n");
+       free_mi_tree(rpl_tree);
+       return 0;
+}
+
+static struct mi_root *mi_version(struct mi_root *cmd, void *param)
+{
+       struct mi_root *rpl_tree;
+       struct mi_node *rpl;
+       struct mi_node *node;
+
+       rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
+       if (rpl_tree==0)
+               return 0;
+       rpl = &rpl_tree->node;
+
+       node = add_mi_node_child( rpl, 0, MI_SSTR("Server"), SERVER_HDR+8,
+               SERVER_HDR_LEN-8);
+       if (node==0) 
+               goto error;
+
+       node = add_mi_node_child( rpl, 0, MI_SSTR("Build"), BUILD_STR,
+                       BUILD_STR_LEN);
+       if (node==0) 
+               goto error;
+
+#ifndef KAMAILIO_COMPILE_FLAGS
+#define KAMAILIO_COMPILE_FLAGS SER_COMPILE_FLAGS
+#endif
+
+       node = add_mi_node_child( rpl, 0, MI_SSTR("Flags"), KAMAILIO_COMPILE_FLAGS,
+                       sizeof(KAMAILIO_COMPILE_FLAGS)-1);
+       if (node==0) 
+               goto error;     
+       
+       node = add_mi_node_child( rpl, 0, MI_SSTR("SVN"), SVNREVISION,
+                       sizeof(SVNREVISION)-1);
+       if (node==0) 
+               goto error;
+
+               
+       
+       return rpl_tree;
+error:
+       LM_ERR("failed to add node\n");
+  &n