瀏覽代碼

Merge remote branch 'origin/andrei/rve_f_params'

Automatic support for expressions or variables in lots of module
functions. It applies to all the module functions declared without
a fixup, with a fixup and the corresponding free_fixup function or
with a compatible ser or kamailio style standard fixup (declared
in sr_module.h or mod_fix.h).
E.g.:  f($a, "b = " + $b);  t_set_fr($v + 2 + $x).
       t_set_fr($foo) (equivalent now with t_set_fr("$foo")).

If the expression is constant, then there is no restriction, all the
module functions can take it as parameter.
E.g.: f("7 *" +" 6 = " + 7 * 6) # equivalent to f("7 * 6 = 42")

* origin/andrei/rve_f_params: (21 commits)
  NEWS: notes about expressions in function parameters
  core: enable RVE fixup support when fixup_free is present
  core: automatically fill known fixup_free functions
  core: functions to get a fixup corresp. fixup_free function
  core: k style fixup_free fixes
  core: added generic fparam fixup_free functions
  core: pvapi: added pv_spec_free_contents()
  core: fix "unsigned" bug in sint2str*()
  core: fix auto-deref. for vars in fparam fixups
  perl(k): use sr31_cmd_export_t
  app_python: use sr31_cmd_export_t
  core: internal module interface changes
  print(s): fparam fixup example
  core: support for RVEs in fparam fixups
  core: rval - don't use static buffer for int conversions
  core: ut.* - BSD licence
  core: ut.h: added sint2strbuf()
  perl(k): update api calls:  s/MODULE_T/MODULE2_T/
  app_python: update api calls:  s/MODULE_T/MODULE2_T
  print(s): more module function examples
  core: support for expressions/variables in function parameters

Conflicts:
	NEWS
	action.c
	modules/app_python/python_msgobj.c
	modules_k/perl/openserxs.xs
	pkg/kamailio/debian-lenny
	pvapi.c
	route.c
	route_struct.h
	sr_module.c
Andrei Pelinescu-Onciul 15 年之前
父節點
當前提交
99cff51099
共有 19 個文件被更改,包括 1535 次插入533 次删除
  1. 9 0
      NEWS
  2. 259 77
      action.c
  3. 145 44
      cfg.y
  4. 305 20
      mod_fix.c
  5. 9 0
      mod_fix.h
  6. 4 4
      modparam.c
  7. 6 6
      modules/app_python/python_msgobj.c
  8. 6 6
      modules_k/perl/openserxs.xs
  9. 54 3
      modules_s/print/print.c
  10. 4 4
      pvapi.c
  11. 106 33
      route.c
  12. 10 1
      route_struct.c
  13. 10 2
      route_struct.h
  14. 32 14
      rvalue.c
  15. 5 1
      rvalue.h
  16. 400 260
      sr_module.c
  17. 83 4
      sr_module.h
  18. 14 24
      ut.c
  19. 74 30
      ut.h

+ 9 - 0
NEWS

@@ -12,6 +12,15 @@ core:
   - networks addresses support in ip comparisons (src_ip, dst_ip, to_ip)
   - networks addresses support in ip comparisons (src_ip, dst_ip, to_ip)
     with strings or rvalue expressions.
     with strings or rvalue expressions.
     E.g.: $ip=10.0.0.0;  if (src_ip == $ip +"/8") ....
     E.g.: $ip=10.0.0.0;  if (src_ip == $ip +"/8") ....
+  - lots of module functions automatically support now expressions or
+      variables in function parameters. This applies to all the module
+      functions declared without a fixup, with a fixup and the corresponding
+      free_fixup function or with a compatible ser or kamailio style standard
+      fixup (declared in sr_module.h or mod_fix.h).
+      E.g.: f($a, "b = " + $b);  t_set_fr($v + 2 + $x).
+            t_set_fr($foo) (equivalent now with t_set_fr("$foo")).
+  - all the module functions can now be called with any constant expression
+      as parameters. E.g.: f("7 *" +" 6 = " + 7 * 6);
   - onreply_route {...} is now equivalent with onreply_route[0] {...}
   - onreply_route {...} is now equivalent with onreply_route[0] {...}
   - global, per protocol blacklist ignore masks (via extended send_flags).
   - global, per protocol blacklist ignore masks (via extended send_flags).
     See dst_blacklist_udp_imask a.s.o (dst_blacklist_*_imask).
     See dst_blacklist_udp_imask a.s.o (dst_blacklist_*_imask).

+ 259 - 77
action.c

@@ -51,6 +51,8 @@
  *  2008-12-17  added UDP_MTU_TRY_PROTO_T (andrei)
  *  2008-12-17  added UDP_MTU_TRY_PROTO_T (andrei)
  *  2009-05-04  switched IF_T to rval_expr (andrei)
  *  2009-05-04  switched IF_T to rval_expr (andrei)
  *  2009-09-15  added SET_{FWD,RPL}_NO_CONNECT, SET_{FWD,RPL}_CLOSE (andrei)
  *  2009-09-15  added SET_{FWD,RPL}_NO_CONNECT, SET_{FWD,RPL}_CLOSE (andrei)
+ *  2010-06-01  special hack/support for fparam fixups so that they can handle
+ *               variable RVEs (andrei)
  */
  */
 
 
 /*!
 /*!
@@ -108,6 +110,179 @@
 int _last_returned_code  = 0;
 int _last_returned_code  = 0;
 struct onsend_info* p_onsend=0; /* onsend route send info */
 struct onsend_info* p_onsend=0; /* onsend route send info */
 
 
+
+
+/* handle the exit code of a module function call.
+ * (used internally in do_action())
+ * @param h - script handle (h->last_retcode and h->run_flags will be set).
+ * @param ret - module function (v0 or v2) retcode
+ * Side-effects: sets _last_returned_code
+ */
+#define MODF_HANDLE_RETCODE(h, ret) \
+	do { \
+		/* if (unlikely((ret)==0)) (h)->run_flags|=EXIT_R_F; */ \
+		(h)->run_flags |= EXIT_R_F & (((ret) != 0) -1); \
+		(h)->last_retcode=(ret); \
+		_last_returned_code = (h)->last_retcode; \
+	} while(0)
+
+
+
+/* frees parameters converted using MODF_RVE_PARAM_CONVERT() from dst.
+ * (used internally in do_action())
+ * Assumes src is unchanged.
+ * Side-effects: clobbers i (int).
+ */
+#define MODF_RVE_PARAM_FREE(cmd, src, dst) \
+		for (i=0; i < (dst)[1].u.number; i++) { \
+			if ((src)[i+2].type == RVE_ST && (dst)[i+2].u.data) { \
+				if ((dst)[i+2].type == RVE_FREE_FIXUP_ST) {\
+					/* call free_fixup (which should restore the original
+					   string) */ \
+					call_fixup((cmd)->free_fixup, &(dst)[i+2].u.data, i+1); \
+				} else if ((dst)[i+2].type == FPARAM_DYN_ST) {\
+					/* completely frees fparam and restore original string */\
+					fparam_free_restore(&(dst)[i+2].u.data); \
+				} \
+				/* free allocated string */ \
+				pkg_free((dst)[i+2].u.data); \
+				(dst)[i+2].u.data = 0; \
+			} \
+		}
+
+
+/* fills dst from src, converting RVE_ST params to STRING_ST.
+ * (used internally in do_action())
+ * @param src - source action_u_t array, as in the action structure
+ * @param dst - destination action_u_t array, will be filled from src.
+ * WARNING: dst must be cleaned when done, use MODULE_RVE_PARAM_FREE()
+ * Side-effects: clobbers i (int), s (str), rv (rvalue*), might jump to error.
+ */
+#define MODF_RVE_PARAM_CONVERT(h, msg, cmd, src, dst) \
+	do { \
+		(dst)[1]=(src)[1]; \
+		for (i=0; i < (src)[1].u.number; i++) { \
+			if ((src)[2+i].type == RVE_ST) { \
+				rv=rval_expr_eval((h), (msg), (src)[i+2].u.data); \
+				if (unlikely(rv == 0 || \
+					rval_get_str((h), (msg), &s, rv, 0) < 0)) { \
+					rval_destroy(rv); \
+					ERR("failed to convert RVE to string\n"); \
+					(dst)[1].u.number = i; \
+					MODF_RVE_PARAM_FREE(cmd, src, dst); \
+					goto error; \
+				} \
+				(dst)[i+2].type = STRING_RVE_ST; \
+				(dst)[i+2].u.string = s.s; \
+				(dst)[i+2].u.str.len = s.len; \
+				rval_destroy(rv); \
+				if ((cmd)->fixup) {\
+					if ((cmd)->free_fixup) {\
+						if (likely( call_fixup((cmd)->fixup, \
+										&(dst)[i+2].u.data, i+1) >= 0) ) { \
+							/* success => mark it for calling free fixup */ \
+							if (likely((dst)[i+2].u.data != s.s)) \
+								(dst)[i+2].type = RVE_FREE_FIXUP_ST; \
+						} else { \
+							/* error calling fixup => mark conv. parameter \
+							   and return error */ \
+							(dst)[1].u.number = i; \
+							ERR("runtime fixup failed for %s param %d\n", \
+									(cmd)->name, i+1); \
+							MODF_RVE_PARAM_FREE(cmd, src, dst); \
+							goto error; \
+						} \
+					} else if ((cmd)->fixup_flags & FIXUP_F_FPARAM_RVE) { \
+						if (likely( call_fixup((cmd)->fixup, \
+										&(dst)[i+2].u.data, i+1) >= 0)) { \
+							if ((dst)[i+2].u.data != s.s) \
+								(dst)[i+2].type = FPARAM_DYN_ST; \
+						} else { \
+							/* error calling fixup => mark conv. parameter \
+							   and return error */ \
+							(dst)[1].u.number = i; \
+							ERR("runtime fixup failed for %s param %d\n", \
+									(cmd)->name, i+1); \
+							MODF_RVE_PARAM_FREE(cmd, src, dst); \
+							goto error; \
+						}\
+					} \
+				} \
+			} else \
+				(dst)[i+2]=(src)[i+2]; \
+		} \
+	} while(0)
+
+
+
+/* call a module function with normal STRING_ST params.
+ * (used internally in do_action())
+ * @param f_type - cmd_function type
+ * @param h
+ * @param msg
+ * @param src - source action_u_t array (e.g. action->val)
+ * @param params... - variable list of parameters, passed to the module
+ *               function
+ * Side-effects: sets ret, clobbers i (int), s (str), rv (rvalue*), cmd,
+ *               might jump to error.
+ *
+ */
+#ifdef __SUNPRO_C
+#define MODF_CALL(f_type, h, msg, src, ...) \
+	do { \
+		cmd=(src)[0].u.data; \
+		ret=((f_type)cmd->function)((msg), __VAR_ARGS__); \
+		MODF_HANDLE_RETCODE(h, ret); \
+	} while (0)
+#else  /* ! __SUNPRO_C  (gcc, icc a.s.o) */
+#define MODF_CALL(f_type, h, msg, src, params...) \
+	do { \
+		cmd=(src)[0].u.data; \
+		ret=((f_type)cmd->function)((msg), ## params ); \
+		MODF_HANDLE_RETCODE(h, ret); \
+	} while (0)
+#endif /* __SUNPRO_C */
+
+
+
+/* call a module function with possible RVE params.
+ * (used internally in do_action())
+ * @param f_type - cmd_function type
+ * @param h
+ * @param msg
+ * @param src - source action_u_t array (e.g. action->val)
+ * @param dst - temporary action_u_t array used for conversions. It can be
+ *              used for the function parameters. It's contents it's not
+ *              valid after the call.
+ * @param params... - variable list of parameters, passed to the module
+ *               function
+ * Side-effects: sets ret, clobbers i (int), s (str), rv (rvalue*), f, dst,
+ *               might jump to error.
+ *
+ */
+#ifdef __SUNPRO_C
+#define MODF_RVE_CALL(f_type, h, msg, src, dst, ...) \
+	do { \
+		cmd=(src)[0].u.data; \
+		MODF_RVE_PARAM_CONVERT(h, msg, cmd, src, dst); \
+		ret=((f_type)cmd->function)((msg), __VAR_ARGS__); \
+		MODF_HANDLE_RETCODE(h, ret); \
+		/* free strings allocated by us or fixups */ \
+		MODF_RVE_PARAM_FREE(cmd, src, dst); \
+	} while (0)
+#else  /* ! __SUNPRO_C  (gcc, icc a.s.o) */
+#define MODF_RVE_CALL(f_type, h, msg, src, dst, params...) \
+	do { \
+		cmd=(src)[0].u.data; \
+		MODF_RVE_PARAM_CONVERT(h, msg, cmd, src, dst); \
+		ret=((f_type)cmd->function)((msg), ## params ); \
+		MODF_HANDLE_RETCODE(h, ret); \
+		/* free strings allocated by us or fixups */ \
+		MODF_RVE_PARAM_FREE(cmd, src, dst); \
+	} while (0)
+#endif /* __SUNPRO_C */
+
+
 /* ret= 0! if action -> end of list(e.g DROP),
 /* ret= 0! if action -> end of list(e.g DROP),
       > 0 to continue processing next actions
       > 0 to continue processing next actions
    and <0 on error */
    and <0 on error */
@@ -118,7 +293,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 	struct dest_info dst;
 	struct dest_info dst;
 	char* tmp;
 	char* tmp;
 	char *new_uri, *end, *crt;
 	char *new_uri, *end, *crt;
-	void* f;
+	sr31_cmd_export_t* cmd;
 	int len;
 	int len;
 	int user;
 	int user;
 	struct sip_uri uri, next_hop;
 	struct sip_uri uri, next_hop;
@@ -135,6 +310,13 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 	struct rval_cache c1;
 	struct rval_cache c1;
 	str s;
 	str s;
 	void *srevp[2];
 	void *srevp[2];
+	/* temporary storage space for a struct action.val[] working copy
+	 (needed to transform RVE intro STRING before calling module
+	   functions). [0] is not used (corresp. to the module export pointer),
+	   [1] contains the number of params, and [2..] the param values.
+	   We need [1], because some fixup function use it
+	  (see fixup_get_param_count()).  */
+	static action_u_t mod_f_params[MAX_ACTIONS];
 
 
 	/* reset the value of error to E_UNSPEC so avoid unknowledgable
 	/* reset the value of error to E_UNSPEC so avoid unknowledgable
 	   functions to return with error (status<0) and not setting it
 	   functions to return with error (status<0) and not setting it
@@ -900,108 +1082,111 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 										(struct action*)a->val[2].u.data, msg);
 										(struct action*)a->val[2].u.data, msg);
 					}
 					}
 			break;
 			break;
-		case MODULE_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)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;
-				_last_returned_code = h->last_retcode;
-			} else {
-				LOG(L_CRIT,"BUG: do_action: bad module call\n");
-				goto error;
-			}
+		case MODULE0_T:
+			MODF_CALL(cmd_function, h, msg, a->val, 0, 0);
 			break;
 			break;
 		/* instead of using the parameter number, we use different names
 		/* instead of using the parameter number, we use different names
 		 * for calls to functions with 3, 4, 5, 6 or variable number of
 		 * for calls to functions with 3, 4, 5, 6 or variable number of
 		 * parameters due to performance reasons */
 		 * parameters due to performance reasons */
+		case MODULE1_T:
+			MODF_CALL(cmd_function, h, msg, a->val,
+										(char*)a->val[2].u.data,
+										0
+					);
+			break;
+		case MODULE2_T:
+			MODF_CALL(cmd_function, h, msg, a->val,
+										(char*)a->val[2].u.data,
+										(char*)a->val[3].u.data
+					);
+			break;
 		case MODULE3_T:
 		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,
+			MODF_CALL(cmd_function3, h, msg, a->val,
 										(char*)a->val[2].u.data,
 										(char*)a->val[2].u.data,
 										(char*)a->val[3].u.data,
 										(char*)a->val[3].u.data,
 										(char*)a->val[4].u.data
 										(char*)a->val[4].u.data
-									);
-				if (ret==0) h->run_flags|=EXIT_R_F;
-				h->last_retcode=ret;
-				_last_returned_code = h->last_retcode;
-			} else {
-				LOG(L_CRIT,"BUG: do_action: bad module call\n");
-				goto error;
-			}
+					);
 			break;
 			break;
 		case MODULE4_T:
 		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,
+			MODF_CALL(cmd_function4, h, msg, a->val,
 										(char*)a->val[2].u.data,
 										(char*)a->val[2].u.data,
 										(char*)a->val[3].u.data,
 										(char*)a->val[3].u.data,
 										(char*)a->val[4].u.data,
 										(char*)a->val[4].u.data,
 										(char*)a->val[5].u.data
 										(char*)a->val[5].u.data
-									);
-				if (ret==0) h->run_flags|=EXIT_R_F;
-				h->last_retcode=ret;
-				_last_returned_code = h->last_retcode;
-			} else {
-				LOG(L_CRIT,"BUG: do_action: bad module call\n");
-				goto error;
-			}
+					);
 			break;
 			break;
 		case MODULE5_T:
 		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,
+			MODF_CALL(cmd_function5, h, msg, a->val,
 										(char*)a->val[2].u.data,
 										(char*)a->val[2].u.data,
 										(char*)a->val[3].u.data,
 										(char*)a->val[3].u.data,
 										(char*)a->val[4].u.data,
 										(char*)a->val[4].u.data,
 										(char*)a->val[5].u.data,
 										(char*)a->val[5].u.data,
 										(char*)a->val[6].u.data
 										(char*)a->val[6].u.data
-									);
-				if (ret==0) h->run_flags|=EXIT_R_F;
-				h->last_retcode=ret;
-				_last_returned_code = h->last_retcode;
-			} else {
-				LOG(L_CRIT,"BUG: do_action: bad module call\n");
-				goto error;
-			}
+					);
 			break;
 			break;
 		case MODULE6_T:
 		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,
+			MODF_CALL(cmd_function6, h, msg, a->val,
 										(char*)a->val[2].u.data,
 										(char*)a->val[2].u.data,
 										(char*)a->val[3].u.data,
 										(char*)a->val[3].u.data,
 										(char*)a->val[4].u.data,
 										(char*)a->val[4].u.data,
 										(char*)a->val[5].u.data,
 										(char*)a->val[5].u.data,
 										(char*)a->val[6].u.data,
 										(char*)a->val[6].u.data,
 										(char*)a->val[7].u.data
 										(char*)a->val[7].u.data
-									);
-				if (ret==0) h->run_flags|=EXIT_R_F;
-				h->last_retcode=ret;
-				_last_returned_code = h->last_retcode;
-			} else {
-				LOG(L_CRIT,"BUG: do_action: bad module call\n");
-				goto error;
-			}
+					);
 			break;
 			break;
 		case MODULEX_T:
 		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;
-				_last_returned_code = h->last_retcode;
-			} else {
-				LOG(L_CRIT,"BUG: do_action: bad module call\n");
-				goto error;
-			}
+			MODF_CALL(cmd_function_var, h, msg, a->val,
+							a->val[1].u.number, &a->val[2]);
+			break;
+		case MODULE1_RVE_T:
+			MODF_RVE_CALL(cmd_function, h, msg, a->val, mod_f_params,
+											(char*)mod_f_params[2].u.data,
+											0
+					);
+			break;
+		case MODULE2_RVE_T:
+			MODF_RVE_CALL(cmd_function, h, msg, a->val, mod_f_params,
+											(char*)mod_f_params[2].u.data,
+											(char*)mod_f_params[3].u.data
+					);
+			break;
+		case MODULE3_RVE_T:
+			MODF_RVE_CALL(cmd_function3, h, msg, a->val, mod_f_params,
+											(char*)mod_f_params[2].u.data,
+											(char*)mod_f_params[3].u.data,
+											(char*)mod_f_params[4].u.data
+					);
+			break;
+		case MODULE4_RVE_T:
+			MODF_RVE_CALL(cmd_function4, h, msg, a->val, mod_f_params,
+											(char*)mod_f_params[2].u.data,
+											(char*)mod_f_params[3].u.data,
+											(char*)mod_f_params[4].u.data,
+											(char*)mod_f_params[5].u.data
+					);
+			break;
+		case MODULE5_RVE_T:
+			MODF_RVE_CALL(cmd_function5, h, msg, a->val, mod_f_params,
+											(char*)mod_f_params[2].u.data,
+											(char*)mod_f_params[3].u.data,
+											(char*)mod_f_params[4].u.data,
+											(char*)mod_f_params[5].u.data,
+											(char*)mod_f_params[6].u.data
+					);
+			break;
+		case MODULE6_RVE_T:
+			MODF_RVE_CALL(cmd_function6, h, msg, a->val, mod_f_params,
+											(char*)mod_f_params[2].u.data,
+											(char*)mod_f_params[3].u.data,
+											(char*)mod_f_params[4].u.data,
+											(char*)mod_f_params[5].u.data,
+											(char*)mod_f_params[6].u.data,
+											(char*)mod_f_params[7].u.data
+					);
+			break;
+		case MODULEX_RVE_T:
+			MODF_RVE_CALL(cmd_function_var, h, msg, a->val, mod_f_params,
+							a->val[1].u.number, &mod_f_params[2]);
 			break;
 			break;
 		case EVAL_T:
 		case EVAL_T:
 			/* only eval the expression to account for possible
 			/* only eval the expression to account for possible
@@ -1357,11 +1542,8 @@ end:
 	/* process module onbreak handlers if present */
 	/* process module onbreak handlers if present */
 	if (unlikely(h->rec_lev==0 && ret==0))
 	if (unlikely(h->rec_lev==0 && ret==0))
 		for (mod=modules;mod;mod=mod->next)
 		for (mod=modules;mod;mod=mod->next)
-			if (unlikely((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);
+			if (unlikely(mod->exports.onbreak_f)) {
+				mod->exports.onbreak_f( msg );
 			}
 			}
 	return ret;
 	return ret;
 
 

+ 145 - 44
cfg.y

@@ -243,6 +243,8 @@ static struct case_stms* mk_case_stm(struct rval_expr* ct, int is_re,
 									struct action* a, int* err);
 									struct action* a, int* err);
 static int case_check_type(struct case_stms* stms);
 static int case_check_type(struct case_stms* stms);
 static int case_check_default(struct case_stms* stms);
 static int case_check_default(struct case_stms* stms);
+static int mod_f_params_pre_fixup(struct action* a);
+static void free_mod_func_action(struct action* a);
 
 
 
 
 extern int line;
 extern int line;
@@ -2232,7 +2234,14 @@ fcmd:
 				case RESETFLAG_T:
 				case RESETFLAG_T:
 				case ISFLAGSET_T:
 				case ISFLAGSET_T:
 				case IF_T:
 				case IF_T:
-				case MODULE_T:
+				case MODULE0_T:
+				case MODULE1_T:
+				case MODULE2_T:
+				case MODULE3_T:
+				case MODULE4_T:
+				case MODULE5_T:
+				case MODULE6_T:
+				case MODULEX_T:
 				case SET_FWD_NO_CONNECT_T:
 				case SET_FWD_NO_CONNECT_T:
 				case SET_RPL_NO_CONNECT_T:
 				case SET_RPL_NO_CONNECT_T:
 				case SET_FWD_CLOSE_T:
 				case SET_FWD_CLOSE_T:
@@ -2705,7 +2714,7 @@ rval: intno			{$$=mk_rve_rval(RV_INT, (void*)$1); }
 
 
 
 
 rve_un_op: NOT	{ $$=RVE_LNOT_OP; }
 rve_un_op: NOT	{ $$=RVE_LNOT_OP; }
-		|  MINUS %prec UNARY	{ $$=RVE_UMINUS_OP; } 
+		|  MINUS %prec UNARY	{ $$=RVE_UMINUS_OP; }
 		/* TODO: RVE_BOOL_OP, RVE_NOT_OP? */
 		/* TODO: RVE_BOOL_OP, RVE_NOT_OP? */
 	;
 	;
 
 
@@ -3221,9 +3230,9 @@ cmd:
 	| SET_RPL_CLOSE	{
 	| SET_RPL_CLOSE	{
 		$$=mk_action(SET_RPL_CLOSE_T, 0); set_cfg_pos($$);
 		$$=mk_action(SET_RPL_CLOSE_T, 0); set_cfg_pos($$);
 	}
 	}
-	| ID {mod_func_action = mk_action(MODULE_T, 2, MODEXP_ST, NULL, NUMBER_ST,
+	| ID {mod_func_action = mk_action(MODULE0_T, 2, MODEXP_ST, NULL, NUMBER_ST,
 			0); } LPAREN func_params RPAREN	{
 			0); } LPAREN func_params RPAREN	{
-		mod_func_action->val[0].u.data = 
+		mod_func_action->val[0].u.data =
 			find_export_record($1, mod_func_action->val[1].u.number, rt,
 			find_export_record($1, mod_func_action->val[1].u.number, rt,
 								&u_tmp);
 								&u_tmp);
 		if (mod_func_action->val[0].u.data == 0) {
 		if (mod_func_action->val[0].u.data == 0) {
@@ -3233,34 +3242,14 @@ cmd:
 			} else {
 			} else {
 				yyerror("unknown command, missing loadmodule?\n");
 				yyerror("unknown command, missing loadmodule?\n");
 			}
 			}
-			pkg_free(mod_func_action);
+			free_mod_func_action(mod_func_action);
 			mod_func_action=0;
 			mod_func_action=0;
 		}else{
 		}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;
+			if (mod_func_action && mod_f_params_pre_fixup(mod_func_action)<0) {
+				/* error messages are printed inside the function */
+				free_mod_func_action(mod_func_action);
+				mod_func_action = 0;
+				YYERROR;
 			}
 			}
 		}
 		}
 		$$ = mod_func_action;
 		$$ = mod_func_action;
@@ -3272,27 +3261,20 @@ func_params:
 	/* empty */
 	/* empty */
 	| func_params COMMA func_param { }
 	| func_params COMMA func_param { }
 	| func_param {}
 	| func_param {}
-	| func_params error { yyerror("call params error\n"); }
 	;
 	;
 func_param:
 func_param:
-        intno {
-		if (mod_func_action->val[1].u.number < MAX_ACTIONS-2) {
+	rval_expr {
+		if ($1 && mod_func_action->val[1].u.number < MAX_ACTIONS-2) {
 			mod_func_action->val[mod_func_action->val[1].u.number+2].type =
 			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 =
+				RVE_ST;
+			mod_func_action->val[mod_func_action->val[1].u.number+2].u.data =
 				$1;
 				$1;
 			mod_func_action->val[1].u.number++;
 			mod_func_action->val[1].u.number++;
-		} else {
+		} else if ($1) {
 			yyerror("Too many arguments\n");
 			yyerror("Too many arguments\n");
-		}
-	}
-	| STRING {
-		if (mod_func_action->val[1].u.number < MAX_ACTIONS-2) {
-			mod_func_action->val[mod_func_action->val[1].u.number+2].type = STRING_ST;
-			mod_func_action->val[mod_func_action->val[1].u.number+2].u.string = $1;
-			mod_func_action->val[1].u.number++;
+			YYERROR;
 		} else {
 		} else {
-			yyerror("Too many arguments\n");
+			YYERROR;
 		}
 		}
 	}
 	}
 	;
 	;
@@ -3735,6 +3717,125 @@ static int case_check_default(struct case_stms* stms)
 
 
 
 
 
 
+/** fixes the parameters and the type of a module function call.
+ * It is done here instead of fix action, to have quicker feedback
+ * on error cases (e.g. passing a non constant to a function with a 
+ * declared fixup) 
+ * The rest of the fixup is done inside do_action().
+ * @param a - filled module function call (MODULE*_T) action structure
+ *            complete with parameters, starting at val[2] and parameter
+ *            number at val[1].
+ * @return 0 on success, -1 on error (it will also print the error msg.).
+ *
+ */
+static int mod_f_params_pre_fixup(struct action* a)
+{
+	sr31_cmd_export_t* cmd_exp;
+	action_u_t* params;
+	int param_no;
+	struct rval_expr* rve;
+	struct rvalue* rv;
+	int r;
+	str s;
+	
+	cmd_exp = a->val[0].u.data;
+	param_no = a->val[1].u.number;
+	params = &a->val[2];
+	
+	switch(cmd_exp->param_no) {
+		case 0:
+			a->type = MODULE0_T;
+			break;
+		case 1:
+			a->type = MODULE1_T;
+			break;
+		case 2:
+			a->type = MODULE2_T;
+			break;
+		case 3:
+			a->type = MODULE3_T;
+			break;
+		case 4:
+			a->type = MODULE4_T;
+			break;
+		case 5:
+			a->type = MODULE5_T;
+			break;
+		case 6:
+			a->type = MODULE6_T;
+			break;
+		case VAR_PARAM_NO:
+			a->type = MODULEX_T;
+			break;
+		default:
+			yyerror("function %s: bad definition"
+					" (invalid number of parameters)", cmd_exp->name);
+			return -1;
+	}
+	
+	if ( cmd_exp->fixup) {
+		if (is_fparam_rve_fixup(cmd_exp->fixup))
+			/* mark known fparam rve safe fixups */
+			cmd_exp->fixup_flags  |= FIXUP_F_FPARAM_RVE;
+		else if (!(cmd_exp->fixup_flags & FIXUP_F_FPARAM_RVE) &&
+				 cmd_exp->free_fixup == 0) {
+			/* v0 or v1 functions that have fixups and no coresp. fixup_free
+			   functions, need constant, string params.*/
+			for (r=0; r < param_no; r++) {
+				rve=params[r].u.data;
+				if (!rve_is_constant(rve)) {
+					yyerror_at(&rve->fpos, "function %s: parameter %d is not"
+								" constant\n", cmd_exp->name, r+1);
+					return -1;
+				}
+				if ((rv = rval_expr_eval(0, 0, rve)) == 0 ||
+						rval_get_str(0, 0, &s, rv, 0) < 0 ) {
+					/* out of mem or bug ? */
+					rval_destroy(rv);
+					yyerror_at(&rve->fpos, "function %s: bad parameter %d"
+									" expression\n", cmd_exp->name, r+1);
+					return -1;
+				}
+				rval_destroy(rv);
+				rve_destroy(rve);
+				params[r].type = STRING_ST; /* asciiz */
+				params[r].u.string = s.s;
+				params[r].u.str.len = s.len; /* not used right now */
+			}
+		}
+	}/* else
+		if no fixups are present, the RVEs can be transformed
+		into strings at runtime, allowing seamless var. use
+		even with old functions.
+		Further optimizations -> in fix_actions()
+		*/
+	return 0;
+}
+
+
+
+/** frees a filled module function call action structure.
+ * @param a - filled module function call action structure
+ *            complete with parameters, starting at val[2] and parameter
+ *            number at val[1].
+ */
+static void free_mod_func_action(struct action* a)
+{
+	action_u_t* params;
+	int param_no;
+	int r;
+	
+	param_no = a->val[1].u.number;
+	params = &a->val[2];
+	
+	for (r=0; r < param_no; r++)
+		if (params[r].u.data)
+			rve_destroy(params[r].u.data);
+	pkg_free(a);
+}
+
+
+
 /*
 /*
 int main(int argc, char ** argv)
 int main(int argc, char ** argv)
 {
 {

+ 305 - 20
mod_fix.c

@@ -34,6 +34,7 @@
 
 
 #include "mod_fix.h"
 #include "mod_fix.h"
 #include "mem/mem.h"
 #include "mem/mem.h"
+#include "trim.h"
 
 
 
 
 
 
@@ -50,11 +51,8 @@ int fixup_regexpNL_none(void** param, int param_no); /* textops */
 	{ \
 	{ \
 		if ((param_no > (maxp)) || (param_no < (minp))) \
 		if ((param_no > (maxp)) || (param_no < (minp))) \
 			return E_UNSPEC; \
 			return E_UNSPEC; \
-		if (*param){ \
-			fparam_free_contents((fparam_t*)*param); \
-			pkg_free(*param); \
-			*param=0; \
-		} \
+		if (*param) \
+			fparam_free_restore(param); \
 		return 0; \
 		return 0; \
 	}
 	}
 
 
@@ -149,7 +147,7 @@ int fixup_regexpNL_none(void** param, int param_no); /* textops */
 		int ret; \
 		int ret; \
 		if (param && *param){ \
 		if (param && *param){ \
 			p=(param_no>(no1))? *param - (long)&((fparam_t*)0)->v : *param;\
 			p=(param_no>(no1))? *param - (long)&((fparam_t*)0)->v : *param;\
-			if ((ret=fixup_free_fpt_##suffix(&p, param_no))==0) *param=0; \
+			if ((ret=fixup_free_fpt_##suffix(&p, param_no))==0) *param=p; \
 			return ret; \
 			return ret; \
 		} \
 		} \
 		return 0; \
 		return 0; \
@@ -168,24 +166,288 @@ int fixup_regexpNL_none(void** param, int param_no); /* textops */
 
 
 FIXUP_F1T(str_null, 1, 1, FPARAM_STR)
 FIXUP_F1T(str_null, 1, 1, FPARAM_STR)
 FIXUP_F1T(str_str, 1, 2,  FPARAM_STR)
 FIXUP_F1T(str_str, 1, 2,  FPARAM_STR)
+FIXUP_F1T(str_all, 1, 100,  FPARAM_STR)
 
 
-/* TODO: int can be converted in place, no need for pkg_malloc'ed fparam_t*/
+/*
+  no free fixups possible for unit_*
+  (they overwrite the pointer with the converted number => the original
+   value cannot be recovered)
 FIXUP_F1T(uint_null, 1, 1, FPARAM_INT)
 FIXUP_F1T(uint_null, 1, 1, FPARAM_INT)
 FIXUP_F1T(uint_uint, 1, 2, FPARAM_INT)
 FIXUP_F1T(uint_uint, 1, 2, FPARAM_INT)
+*/
+
+
+
+int fixup_uint_uint(void** param, int param_no)
+{
+	str s;
+	unsigned int num;
+	
+	s.s = *param;
+	s.len = strlen(s.s);
+	if (likely(str2int(&s, &num) == 0)) {
+		*param = (void*)(long)num;
+	} else
+		/* not a number */
+		return E_UNSPEC;
+	return 0;
+}
+
+
+
+int fixup_uint_null(void** param, int param_no)
+{
+	if (param_no == 1)
+		return fixup_uint_uint(param, param_no);
+	return E_UNSPEC;
+}
+
 
 
+/* fixup_regexp_null() has to be written "by hand", since
+   it needs to save the original pointer (the fixup users expects
+   a pointer to the regex in *param and hence the original value
+   needed on free cannot be recovered directly).
 FIXUP_F1T(regexp_null, 1, 1, FPARAM_REGEX)
 FIXUP_F1T(regexp_null, 1, 1, FPARAM_REGEX)
+*/
 
 
+struct regex_fixup {
+	regex_t regex; /* compiled regex */
+	void* orig;    /* original pointer */
+};
+
+int fixup_regexp_null(void** param, int param_no)
+{
+	struct regex_fixup* re;
+	
+	if (param_no != 1)
+		return E_UNSPEC;
+	if ((re=pkg_malloc(sizeof(*re))) ==0) {
+		ERR("No memory left\n");
+		goto error;
+	}
+	if (regcomp(&re->regex, *param,
+				REG_EXTENDED|REG_ICASE|REG_NEWLINE))
+		goto error;
+	re->orig = *param;
+	*param = re;
+	return 0;
+error:
+	if (re)
+		pkg_free(re);
+	return E_UNSPEC;
+}
+
+
+int fixup_free_regexp_null(void** param, int param_no)
+{
+	struct regex_fixup* re;
+	
+	if (param_no != 1)
+		return E_UNSPEC;
+	if (*param) {
+		re = *param;
+		*param = re->orig;
+		regfree(&re->regex);
+		pkg_free(re);
+	}
+	return 0;
+}
+
+/* fixup_pvar_*() has to be written "by hand", since
+   it needs to save the original pointer (the fixup users expects
+   a pointer to the pv_spec_t in *param and hence the original value
+   needed on free cannot be recovered directly).
 FIXUP_F1T(pvar_null, 1, 1, FPARAM_PVS)
 FIXUP_F1T(pvar_null, 1, 1, FPARAM_PVS)
 FIXUP_F1T(pvar_pvar, 1, 2, FPARAM_PVS)
 FIXUP_F1T(pvar_pvar, 1, 2, FPARAM_PVS)
+*/
+
+struct pvs_fixup {
+	pv_spec_t pvs; /* parsed pv spec */
+	void* orig;    /* original pointer */
+};
+
+int fixup_pvar_all(void** param, int param_no)
+{
+	struct pvs_fixup* pvs_f;
+	str name;
+	
+	pvs_f = 0;
+	name.s = *param;
+	name.len = strlen(name.s);
+	trim(&name);
+	if (name.len == 0 || name.s[0] != '$')
+		/* not a pvs id */
+		goto error;
+	if ((pvs_f=pkg_malloc(sizeof(*pvs_f))) == 0) {
+		ERR("No memory left\n");
+		goto error;
+	}
+	if (pv_parse_spec2(&name, &pvs_f->pvs, 1) == 0)
+		/* not a valid pvs identifier */
+		goto error;
+	pvs_f->orig = *param;
+	*param = pvs_f;
+	return 0;
+error:
+	if (pvs_f)
+		pkg_free(pvs_f);
+	return E_UNSPEC;
+}
+
+
+
+int fixup_free_pvar_all(void** param, int param_no)
+{
+	struct pvs_fixup* pvs_f;
+	
+	if (*param) {
+		pvs_f = *param;
+		*param = pvs_f->orig;
+		/* free only the contents (don't attempt to free &pvs_f->pvs)*/
+		pv_spec_destroy(&pvs_f->pvs);
+		/* free the whole pvs_fixup */
+		pkg_free(pvs_f);
+	}
+	return 0;
+}
+
+
+
+int fixup_pvar_pvar(void** param, int param_no)
+{
+	if (param_no > 2)
+		return E_UNSPEC;
+	return fixup_free_pvar_all(param, param_no);
+}
+
+
+
+int fixup_free_pvar_pvar(void** param, int param_no)
+{
+	if (param_no > 2)
+		return E_UNSPEC;
+	return fixup_free_pvar_all(param, param_no);
+}
+
+
+
+int fixup_pvar_null(void** param, int param_no)
+{
+	if (param_no != 1)
+		return E_UNSPEC;
+	return fixup_pvar_all(param, param_no);
+}
+
 
 
+
+int fixup_free_pvar_null(void** param, int param_no)
+{
+	if (param_no != 1)
+		return E_UNSPEC;
+	return fixup_free_pvar_all(param, param_no);
+}
+
+/* must be written "by hand", see above (fixup_pvar_pvar).
 FIXUP_F2T(pvar_str, 1, 2, 1, FPARAM_PVS, FPARAM_STR)
 FIXUP_F2T(pvar_str, 1, 2, 1, FPARAM_PVS, FPARAM_STR)
 FIXUP_F2T(pvar_str_str, 1, 3, 1, FPARAM_PVS, FPARAM_STR)
 FIXUP_F2T(pvar_str_str, 1, 3, 1, FPARAM_PVS, FPARAM_STR)
+*/
+
+int fixup_pvar_str(void** param, int param_no)
+{
+	if (param_no == 1)
+		return fixup_pvar_all(param, param_no);
+	else if (param_no == 2)
+		return fixup_str_str(param, param_no);
+	return E_UNSPEC;
+}
+
+
+
+int fixup_free_pvar_str(void** param, int param_no)
+{
+	if (param_no == 1)
+		return fixup_free_pvar_all(param, param_no);
+	else if (param_no == 2)
+		return fixup_free_str_str(param, param_no);
+	return E_UNSPEC;
+}
+
+
+
+int fixup_pvar_str_str(void** param, int param_no)
+{
+	if (param_no == 1)
+		return fixup_pvar_all(param, param_no);
+	else if (param_no == 2 || param_no == 3)
+		return fixup_str_all(param, param_no);
+	return E_UNSPEC;
+}
+
+
+
+int fixup_free_pvar_str_str(void** param, int param_no)
+{
+	if (param_no == 1)
+		return fixup_free_pvar_all(param, param_no);
+	else if (param_no == 2 || param_no == 3)
+		return fixup_free_str_all(param, param_no);
+	return E_UNSPEC;
+}
+
+
 
 
 FIXUP_F2FP(igp_null, 1, 1, 1, FPARAM_INT|FPARAM_PVS, 0)
 FIXUP_F2FP(igp_null, 1, 1, 1, FPARAM_INT|FPARAM_PVS, 0)
 FIXUP_F2FP(igp_igp, 1, 2, 2,  FPARAM_INT|FPARAM_PVS, 0)
 FIXUP_F2FP(igp_igp, 1, 2, 2,  FPARAM_INT|FPARAM_PVS, 0)
-FIXUP_F2FP(igp_pvar, 1, 2, 1,  FPARAM_INT|FPARAM_PVS, FPARAM_PVS)
 
 
+/* must be declared by hand, because of the pvar special handling
+   (see above)
+FIXUP_F2FP(igp_pvar, 1, 2, 1,  FPARAM_INT|FPARAM_PVS, FPARAM_PVS)
 FIXUP_F2FP_T(igp_pvar_pvar, 1, 3, 1, FPARAM_INT|FPARAM_PVS, FPARAM_PVS)
 FIXUP_F2FP_T(igp_pvar_pvar, 1, 3, 1, FPARAM_INT|FPARAM_PVS, FPARAM_PVS)
+*/
+
+int fixup_igp_pvar(void** param, int param_no)
+{
+	if (param_no == 1)
+		return fixup_igp_null(param, param_no);
+	else if (param_no == 2)
+		return fixup_pvar_all(param, param_no);
+	return E_UNSPEC;
+}
+
+
+
+int fixup_free_igp_pvar(void** param, int param_no)
+{
+	if (param_no == 1)
+		return fixup_free_igp_null(param, param_no);
+	else if (param_no == 2)
+		return fixup_free_pvar_all(param, param_no);
+	return E_UNSPEC;
+}
+
+
+
+int fixup_igp_pvar_pvar(void** param, int param_no)
+{
+	if (param_no == 1)
+		return fixup_igp_null(param, param_no);
+	else if (param_no == 2 || param_no == 3)
+		return fixup_pvar_all(param, param_no);
+	return E_UNSPEC;
+}
+
+
+
+int fixup_free_igp_pvar_pvar(void** param, int param_no)
+{
+	if (param_no == 1)
+		return fixup_free_igp_null(param, param_no);
+	else if (param_no == 2 || param_no == 3)
+		return fixup_free_pvar_all(param, param_no);
+	return E_UNSPEC;
+}
+
+
 
 
 /** macro for declaring a spve fixup and the corresponding free_fixup
 /** macro for declaring a spve fixup and the corresponding free_fixup
   * for a function expecting first no1 params as fparam converted spve 
   * for a function expecting first no1 params as fparam converted spve 
@@ -202,21 +464,16 @@ FIXUP_F2FP_T(igp_pvar_pvar, 1, 3, 1, FPARAM_INT|FPARAM_PVS, FPARAM_PVS)
 	int fixup_##suffix (void** param, int param_no) \
 	int fixup_##suffix (void** param, int param_no) \
 	{ \
 	{ \
 		int ret; \
 		int ret; \
-		char * bkp; \
 		fparam_t* fp; \
 		fparam_t* fp; \
 		if (param_no<=(no1)){ \
 		if (param_no<=(no1)){ \
 			if ((ret=fix_param_types(FPARAM_PVE, param))<0){ \
 			if ((ret=fix_param_types(FPARAM_PVE, param))<0){ \
-				ERR("Cannot convert function parameter %d to" #type2 "\n", \
+				ERR("Cannot convert function parameter %d to spve \n", \
 						param_no);\
 						param_no);\
 				return E_UNSPEC; \
 				return E_UNSPEC; \
 			} else{ \
 			} else{ \
 				fp=(fparam_t*)*param; \
 				fp=(fparam_t*)*param; \
 				if ((ret==0) && (fp->v.pve->spec.getf==0)){ \
 				if ((ret==0) && (fp->v.pve->spec.getf==0)){ \
-					bkp=fp->orig; \
-					fp->orig=0; /* make sure orig string is not freed */ \
-					fparam_free_contents(fp); \
-					pkg_free(fp); \
-					*param=bkp; \
+					fparam_free_restore(param); \
 					return fix_param_types(FPARAM_STR, param); \
 					return fix_param_types(FPARAM_STR, param); \
 				} else if (ret==1) \
 				} else if (ret==1) \
 					return fix_param_types(FPARAM_STR, param); \
 					return fix_param_types(FPARAM_STR, param); \
@@ -228,11 +485,9 @@ FIXUP_F2FP_T(igp_pvar_pvar, 1, 3, 1, FPARAM_INT|FPARAM_PVS, FPARAM_PVS)
 	int fixup_free_##suffix (void** param, int param_no) \
 	int fixup_free_##suffix (void** param, int param_no) \
 	{ \
 	{ \
 		if (param && *param){ \
 		if (param && *param){ \
-			if (param_no<=(no1)){ \
-				fparam_free_contents((fparam_t*)*param); \
-				pkg_free(*param); \
-				*param=0; \
-			} else \
+			if (param_no<=(no1)) \
+				fparam_free_restore(param); \
+			else \
 				return fixup_free_spvet_##suffix(param, param_no); \
 				return fixup_free_spvet_##suffix(param, param_no); \
 		} \
 		} \
 		return 0; \
 		return 0; \
@@ -244,3 +499,33 @@ FIXUP_F_SPVE_T(spve_spve, 1, 2, 2, 0)
 FIXUP_F_SPVE_T(spve_uint, 1, 2, 1, FPARAM_INT)
 FIXUP_F_SPVE_T(spve_uint, 1, 2, 1, FPARAM_INT)
 FIXUP_F_SPVE_T(spve_str, 1, 2, 1, FPARAM_STR)
 FIXUP_F_SPVE_T(spve_str, 1, 2, 1, FPARAM_STR)
 FIXUP_F_SPVE_T(spve_null, 1, 1, 1, 0)
 FIXUP_F_SPVE_T(spve_null, 1, 1, 1, 0)
+
+/** get the corresp. fixup_free* function.
+ * @param f -fixup function pointer.
+ * @return  - pointer to free_fixup function if known, 0 otherwise.
+ */
+free_fixup_function mod_fix_get_fixup_free(fixup_function f)
+{
+	if (f == fixup_str_null) return fixup_free_str_null;
+	if (f == fixup_str_str) return fixup_free_str_str;
+	/* no free fixup for fixup_uint_* (they overwrite the pointer
+	   value with a number and the original value cannot be recovered) */
+	if (f == fixup_uint_null) return 0;
+	if (f == fixup_uint_uint) return 0;
+	if (f == fixup_regexp_null) return fixup_free_regexp_null;
+	if (f == fixup_pvar_null) return fixup_free_pvar_null;
+	if (f == fixup_pvar_pvar) return fixup_free_pvar_pvar;
+	if (f == fixup_pvar_str) return fixup_free_pvar_str;
+	if (f == fixup_pvar_str_str) return fixup_free_pvar_str_str;
+	if (f == fixup_igp_igp) return fixup_free_igp_igp;
+	if (f == fixup_igp_null) return fixup_free_igp_null;
+	if (f == fixup_igp_pvar) return fixup_free_igp_pvar;
+	if (f == fixup_igp_pvar_pvar) return fixup_free_igp_pvar_pvar;
+	if (f == fixup_spve_spve) return fixup_free_spve_spve;
+	if (f == fixup_spve_null) return fixup_free_spve_null;
+	/* no free fixup, because of the uint part (the uint cannot be freed,
+	   see above fixup_uint_null) */
+	if (f == fixup_spve_uint) return 0;
+	if (f == fixup_spve_str) return fixup_free_spve_str;
+	return 0;
+}

+ 9 - 0
mod_fix.h

@@ -106,7 +106,9 @@ int fixup_pvar_str_str(void** param, int param_no);
 int fixup_free_pvar_str_str(void** param, int param_no);
 int fixup_free_pvar_str_str(void** param, int param_no);
 
 
 int fixup_igp_igp(void** param, int param_no);
 int fixup_igp_igp(void** param, int param_no);
+int fixup_free_igp_igp(void** param, int param_no);
 int fixup_igp_null(void** param, int param_no);
 int fixup_igp_null(void** param, int param_no);
+int fixup_free_igp_null(void** param, int param_no);
 int fixup_get_ivalue(struct sip_msg* msg, gparam_p gp, int *val);
 int fixup_get_ivalue(struct sip_msg* msg, gparam_p gp, int *val);
 
 
 int fixup_igp_pvar(void** param, int param_no);
 int fixup_igp_pvar(void** param, int param_no);
@@ -116,8 +118,15 @@ int fixup_igp_pvar_pvar(void** param, int param_no);
 int fixup_free_igp_pvar_pvar(void** param, int param_no);
 int fixup_free_igp_pvar_pvar(void** param, int param_no);
 
 
 int fixup_spve_spve(void** param, int param_no);
 int fixup_spve_spve(void** param, int param_no);
+int fixup_free_spve_spve(void** param, int param_no);
 int fixup_spve_null(void** param, int param_no);
 int fixup_spve_null(void** param, int param_no);
+int fixup_free_spve_null(void** param, int param_no);
 int fixup_spve_uint(void** param, int param_no);
 int fixup_spve_uint(void** param, int param_no);
 int fixup_spve_str(void** param, int param_no);
 int fixup_spve_str(void** param, int param_no);
+int fixup_free_spve_str(void** param, int param_no);
+
+
+/** get the corresp. free fixup function.*/
+free_fixup_function mod_fix_get_fixup_free(fixup_function f);
 
 
 #endif
 #endif

+ 4 - 4
modparam.c

@@ -91,9 +91,9 @@ int set_mod_param_regex(char* regex, char* name, modparam_t type, void* val)
 
 
 	mod_found = 0;
 	mod_found = 0;
 	for(t = modules; t; t = t->next) {
 	for(t = modules; t; t = t->next) {
-		if (regexec(&preg, t->exports->c.name, 0, 0, 0) == 0) {
+		if (regexec(&preg, t->exports.name, 0, 0, 0) == 0) {
 			DBG("set_mod_param_regex: '%s' matches module '%s'\n",
 			DBG("set_mod_param_regex: '%s' matches module '%s'\n",
-					regex, t->exports->c.name);
+					regex, t->exports.name);
 			mod_found = 1;
 			mod_found = 1;
 			/* PARAM_STR (PARAM_STRING) may be assigned also to PARAM_STRING(PARAM_STR) so let get both module param */
 			/* PARAM_STR (PARAM_STRING) may be assigned also to PARAM_STRING(PARAM_STR) so let get both module param */
 			ptr = find_param_export(t, name, type | ((type & (PARAM_STR|PARAM_STRING))?PARAM_STR|PARAM_STRING:0), &param_type);
 			ptr = find_param_export(t, name, type | ((type & (PARAM_STR|PARAM_STRING))?PARAM_STR|PARAM_STRING:0), &param_type);
@@ -109,7 +109,7 @@ int set_mod_param_regex(char* regex, char* name, modparam_t type, void* val)
 					val2 = val;
 					val2 = val;
 				}
 				}
 				DBG("set_mod_param_regex: found <%s> in module %s [%s]\n",
 				DBG("set_mod_param_regex: found <%s> in module %s [%s]\n",
-						name, t->exports->c.name, t->path);
+						name, t->exports.name, t->path);
 				if (param_type & PARAM_USE_FUNC) {
 				if (param_type & PARAM_USE_FUNC) {
 					if ( ((param_func_t)(ptr))(param_type, val2) < 0) {
 					if ( ((param_func_t)(ptr))(param_type, val2) < 0) {
 						regfree(&preg);
 						regfree(&preg);
@@ -147,7 +147,7 @@ int set_mod_param_regex(char* regex, char* name, modparam_t type, void* val)
 			}
 			}
 			else {
 			else {
 				LOG(L_ERR, "set_mod_param_regex: parameter <%s> not found in"
 				LOG(L_ERR, "set_mod_param_regex: parameter <%s> not found in"
-							" module <%s>\n", name, t->exports->c.name);
+							" module <%s>\n", name, t->exports.name);
 				regfree(&preg);
 				regfree(&preg);
 				pkg_free(reg);
 				pkg_free(reg);
 				return -3;
 				return -3;

+ 6 - 6
modules/app_python/python_msgobj.c

@@ -193,7 +193,7 @@ msg_call_function(msgobject *self, PyObject *args)
 {
 {
     int i, rval;
     int i, rval;
     char *fname, *arg1, *arg2;
     char *fname, *arg1, *arg2;
-    union cmd_export_u* fexport;
+    sr31_cmd_export_t* fexport;
     struct action *act;
     struct action *act;
     struct run_act_ctx ra_ctx;
     struct run_act_ctx ra_ctx;
     unsigned mod_ver;
     unsigned mod_ver;
@@ -222,7 +222,7 @@ msg_call_function(msgobject *self, PyObject *args)
         return Py_None;
         return Py_None;
     }
     }
 
 
-    act = mk_action(MODULE_T, 4 /* number of (type, value) pairs */,
+    act = mk_action(MODULE2_T, 4 /* number of (type, value) pairs */,
                     MODEXP_ST, fexport, /* function */
                     MODEXP_ST, fexport, /* function */
                     NUMBER_ST, 2,       /* parameter number */
                     NUMBER_ST, 2,       /* parameter number */
                     STRING_ST, arg1,    /* param. 1 */
                     STRING_ST, arg1,    /* param. 1 */
@@ -236,9 +236,9 @@ msg_call_function(msgobject *self, PyObject *args)
         return Py_None;
         return Py_None;
     }
     }
 
 
-    if (fexport->v1.fixup != NULL) {
+    if (fexport->fixup != NULL) {
         if (i >= 3) {
         if (i >= 3) {
-            rval = fexport->v1.fixup(&(act->val[3].u.data), 2);
+            rval = fexport->fixup(&(act->val[3].u.data), 2);
             if (rval < 0) {
             if (rval < 0) {
                 PyErr_SetString(PyExc_RuntimeError, "Error in fixup (2)");
                 PyErr_SetString(PyExc_RuntimeError, "Error in fixup (2)");
                 Py_INCREF(Py_None);
                 Py_INCREF(Py_None);
@@ -247,7 +247,7 @@ msg_call_function(msgobject *self, PyObject *args)
             act->val[3].type = MODFIXUP_ST;
             act->val[3].type = MODFIXUP_ST;
         }
         }
         if (i >= 2) {
         if (i >= 2) {
-            rval = fexport->v1.fixup(&(act->val[2].u.data), 1);
+            rval = fexport->fixup(&(act->val[2].u.data), 1);
             if (rval < 0) {
             if (rval < 0) {
                 PyErr_SetString(PyExc_RuntimeError, "Error in fixup (1)");
                 PyErr_SetString(PyExc_RuntimeError, "Error in fixup (1)");
                 Py_INCREF(Py_None);
                 Py_INCREF(Py_None);
@@ -256,7 +256,7 @@ msg_call_function(msgobject *self, PyObject *args)
             act->val[2].type = MODFIXUP_ST;
             act->val[2].type = MODFIXUP_ST;
         }
         }
         if (i == 1) {
         if (i == 1) {
-            rval = fexport->v1.fixup(0, 0);
+            rval = fexport->fixup(0, 0);
             if (rval < 0) {
             if (rval < 0) {
                 PyErr_SetString(PyExc_RuntimeError, "Error in fixup (0)");
                 PyErr_SetString(PyExc_RuntimeError, "Error in fixup (0)");
                 Py_INCREF(Py_None);
                 Py_INCREF(Py_None);

+ 6 - 6
modules_k/perl/openserxs.xs

@@ -215,7 +215,7 @@ int moduleFunc(struct sip_msg *m, char *func,
 	       char *param1, char *param2,
 	       char *param1, char *param2,
 	       int *retval) {
 	       int *retval) {
 
 
-    union cmd_export_u* exp_func_struct;
+	sr31_cmd_export_t* exp_func_struct;
 	struct action *act;
 	struct action *act;
 	unsigned mod_ver;
 	unsigned mod_ver;
 	char *argv[2];
 	char *argv[2];
@@ -259,7 +259,7 @@ int moduleFunc(struct sip_msg *m, char *func,
 		return -1;
 		return -1;
 	}
 	}
 
 
-	act = mk_action(MODULE_T, 4 /* number of (type, value) pairs */,
+	act = mk_action(MODULE2_T, 4 /* number of (type, value) pairs */,
 					MODEXP_ST, exp_func_struct, /* function */
 					MODEXP_ST, exp_func_struct, /* function */
 					NUMBER_ST, 2,  /* parameter number */
 					NUMBER_ST, 2,  /* parameter number */
 					STRING_ST, argv[0], /* param. 1 */
 					STRING_ST, argv[0], /* param. 1 */
@@ -275,7 +275,7 @@ int moduleFunc(struct sip_msg *m, char *func,
 	}
 	}
 
 
 
 
-	if (exp_func_struct->v1.fixup) {
+	if (exp_func_struct->fixup) {
 		if (!unsafemodfnc) {
 		if (!unsafemodfnc) {
 			LM_ERR("Module function '%s' is unsafe. Call is refused.\n", func);
 			LM_ERR("Module function '%s' is unsafe. Call is refused.\n", func);
 			if (argv[0]) pkg_free(argv[0]);
 			if (argv[0]) pkg_free(argv[0]);
@@ -285,7 +285,7 @@ int moduleFunc(struct sip_msg *m, char *func,
 		}
 		}
 
 
 		if (argc>=2) {
 		if (argc>=2) {
-			*retval = exp_func_struct->v1.fixup(&(act->val[3].u.data), 2);
+			*retval = exp_func_struct->fixup(&(act->val[3].u.data), 2);
 			if (*retval < 0) {
 			if (*retval < 0) {
 				LM_ERR("Error in fixup (2)\n");
 				LM_ERR("Error in fixup (2)\n");
 				return -1;
 				return -1;
@@ -293,7 +293,7 @@ int moduleFunc(struct sip_msg *m, char *func,
 			act->val[3].type = MODFIXUP_ST;
 			act->val[3].type = MODFIXUP_ST;
 		}
 		}
 		if (argc>=1) {
 		if (argc>=1) {
-			*retval = exp_func_struct->v1.fixup(&(act->val[2].u.data), 1);
+			*retval = exp_func_struct->fixup(&(act->val[2].u.data), 1);
 			if (*retval < 0) {
 			if (*retval < 0) {
 				LM_ERR("Error in fixup (1)\n");
 				LM_ERR("Error in fixup (1)\n");
 				return -1;
 				return -1;
@@ -301,7 +301,7 @@ int moduleFunc(struct sip_msg *m, char *func,
 			act->val[2].type = MODFIXUP_ST;
 			act->val[2].type = MODFIXUP_ST;
 		}
 		}
 		if (argc==0) {
 		if (argc==0) {
-			*retval = exp_func_struct->v1.fixup(0, 0);
+			*retval = exp_func_struct->fixup(0, 0);
 			if (*retval < 0) {
 			if (*retval < 0) {
 				LM_ERR("Error in fixup (0)\n");
 				LM_ERR("Error in fixup (0)\n");
 				return -1;
 				return -1;

+ 54 - 3
modules_s/print/print.c

@@ -46,9 +46,13 @@ MODULE_VERSION
 
 
 static int print_fixup_f_1(void **param, int param_no);
 static int print_fixup_f_1(void **param, int param_no);
 static int print_fixup_f_2(void **param, int param_no);
 static int print_fixup_f_2(void **param, int param_no);
-static int print_f_0(struct sip_msg*, char*,char*);
-static int print_f_1(struct sip_msg*, char*,char*);
-static int print_f_2(struct sip_msg*, char*,char*);
+static int print_f_0(struct sip_msg*, char*, char*);
+static int print_f_1(struct sip_msg*, char*, char*);
+static int print_f_2(struct sip_msg*, char*, char*);
+static int print_f1(struct sip_msg*, char*, char*);
+static int print_f2(struct sip_msg*, char*, char*);
+static int print_f3(struct sip_msg*, char*, char*, char*);
+static int print_f_var(struct sip_msg*, int argc, action_u_t argv[]);
 static int mod_init(void);
 static int mod_init(void);
 
 
 /* the parameters are not used, they are only meant as an example*/
 /* the parameters are not used, they are only meant as an example*/
@@ -60,6 +64,10 @@ static cmd_export_t cmds[]={
 	{"print", print_f_0, 0, 0, REQUEST_ROUTE},   // overload test
 	{"print", print_f_0, 0, 0, REQUEST_ROUTE},   // overload test
 	{"print", print_f_1, 1, print_fixup_f_1, REQUEST_ROUTE},
 	{"print", print_f_1, 1, print_fixup_f_1, REQUEST_ROUTE},
 	{"print", print_f_2, 2, print_fixup_f_2, REQUEST_ROUTE},
 	{"print", print_f_2, 2, print_fixup_f_2, REQUEST_ROUTE},
+	{"print1", print_f1, 1, 0, REQUEST_ROUTE},
+	{"print2", print_f2, 2, fixup_var_str_12, REQUEST_ROUTE},
+	{"print3", (cmd_function)print_f3, 3, 0, REQUEST_ROUTE},
+	{"printv", (cmd_function)print_f_var, VAR_PARAM_NO, 0, REQUEST_ROUTE},
 	{0, 0, 0, 0, 0}
 	{0, 0, 0, 0, 0}
 };
 };
 
 
@@ -90,6 +98,7 @@ static int mod_init(void)
 	DBG("print: string_param = '%s'\n", string_param);
 	DBG("print: string_param = '%s'\n", string_param);
 	DBG("print: str_param = '%.*s'\n", str_param.len, str_param.s);
 	DBG("print: str_param = '%.*s'\n", str_param.len, str_param.s);
 	DBG("print: int_param = %d\n", int_param);
 	DBG("print: int_param = %d\n", int_param);
+	WARN("this is an example module, it has no practical use\n");
 	return 0;
 	return 0;
 }
 }
 
 
@@ -132,3 +141,45 @@ static int print_fixup_f_2(void **param, int param_no) {
 	DBG("print: print_fixup_f_2('%s')\n", (char*)*param);
 	DBG("print: print_fixup_f_2('%s')\n", (char*)*param);
 	return print_fixup_f(param, param_no);
 	return print_fixup_f(param, param_no);
 }
 }
+
+
+
+/* 1 parameter, no fixup version */
+static int print_f1(struct sip_msg* msg, char* s1, char* not_used)
+{
+	printf("%s\n", s1);
+	return 1;
+}
+
+
+/* 2 parameters, fparam fixup version */
+static int print_f2(struct sip_msg* msg, char* s1, char* s2)
+{
+	str a, b;
+	if (get_str_fparam(&a, msg, (fparam_t*)s1) !=0 ||
+		 get_str_fparam(&b, msg, (fparam_t*)s2) !=0) {
+		BUG("get_str_fparam failed\n");
+		return -1;
+	}
+	printf("%.*s%.*s\n", a.len, a.s, b.len, b.s);
+	return 1;
+}
+
+
+/* 3 parameters, no fixup version */
+static int print_f3(struct sip_msg* msg, char* s1, char* s2, char* s3)
+{
+	printf("%s%s%s\n", s1, s2, s3);
+	return 1;
+}
+
+
+/* variable number of parameters, no fixup version */
+static int print_f_var(struct sip_msg* msg, int argc, action_u_t argv[])
+{
+	int i;
+	for (i = 0; i < argc; i++)
+		printf("%s", argv[i].u.string);
+	printf("\n");
+	return 1;
+}

+ 4 - 4
pvapi.c

@@ -1147,8 +1147,9 @@ error:
 	return NULL;
 	return NULL;
 }
 }
 
 
-/**
- * destroy the content of pv_spec_t structure
+
+
+/** destroy the content of pv_spec_t structure.
  */
  */
 void pv_spec_destroy(pv_spec_t *spec)
 void pv_spec_destroy(pv_spec_t *spec)
 {
 {
@@ -1160,8 +1161,7 @@ void pv_spec_destroy(pv_spec_t *spec)
 		tr_free((trans_t*)spec->trans);
 		tr_free((trans_t*)spec->trans);
 }
 }
 
 
-/**
- * free the pv_spec_t structure
+/** free the pv_spec_t structure.
  */
  */
 void pv_spec_free(pv_spec_t *spec)
 void pv_spec_free(pv_spec_t *spec)
 {
 {

+ 106 - 33
route.c

@@ -50,6 +50,8 @@
  *  		unless the operator is DIFF_OP (Miklos)
  *  		unless the operator is DIFF_OP (Miklos)
  *  2008-12-03  fixups for rvalues in assignments (andrei)
  *  2008-12-03  fixups for rvalues in assignments (andrei)
  *  2009-05-04  switched IF_T to rval_expr (andrei)
  *  2009-05-04  switched IF_T to rval_expr (andrei)
+ *  2010-06-01  special hack/support for fparam fixups so that they can handle
+ *               variable RVEs (andrei)
  *  2010-06-18  ip comparison (comp_ip()) normalizes strings to
  *  2010-06-18  ip comparison (comp_ip()) normalizes strings to
  *              ip/netmask  (andrei)
  *              ip/netmask  (andrei)
  */
  */
@@ -634,9 +636,10 @@ int fix_actions(struct action* a)
 	struct action *t;
 	struct action *t;
 	struct proxy_l* p;
 	struct proxy_l* p;
 	char *tmp;
 	char *tmp;
+	void *tmp_p;
 	int ret;
 	int ret;
 	int i;
 	int i;
-	union cmd_export_u* cmd;
+	sr31_cmd_export_t* cmd;
 	str s;
 	str s;
 	struct hostent* he;
 	struct hostent* he;
 	struct ip_addr ip;
 	struct ip_addr ip;
@@ -644,11 +647,9 @@ int fix_actions(struct action* a)
 	struct lvalue* lval;
 	struct lvalue* lval;
 	struct rval_expr* rve;
 	struct rval_expr* rve;
 	struct rval_expr* err_rve;
 	struct rval_expr* err_rve;
-	struct rvalue* rv;
 	enum rval_type rve_type, err_type, expected_type;
 	enum rval_type rve_type, err_type, expected_type;
-
-	
-	char buf[30]; /* tmp buffer needed for module param fixups */
+	struct rvalue* rv;
+	int rve_param_no;
 
 
 	if (a==0){
 	if (a==0){
 		LOG(L_CRIT,"BUG: fix_actions: null pointer\n");
 		LOG(L_CRIT,"BUG: fix_actions: null pointer\n");
@@ -913,47 +914,119 @@ int fix_actions(struct action* a)
 					goto error;
 					goto error;
 				break;
 				break;
 
 
-			case MODULE_T:
+			case MODULE0_T:
+			case MODULE1_T:
+			case MODULE2_T:
 			case MODULE3_T:
 			case MODULE3_T:
 			case MODULE4_T:
 			case MODULE4_T:
 			case MODULE5_T:
 			case MODULE5_T:
 			case MODULE6_T:
 			case MODULE6_T:
 			case MODULEX_T:
 			case MODULEX_T:
 				cmd = t->val[0].u.data;
 				cmd = t->val[0].u.data;
-				if (cmd && cmd->c.fixup) {
-					DBG("fixing %s()\n", cmd->c.name);
+				rve_param_no = 0;
+				if (cmd) {
+					DBG("fixing %s()\n", cmd->name);
 					if (t->val[1].u.number==0) {
 					if (t->val[1].u.number==0) {
-						ret = cmd->c.fixup(0, 0);
+						ret = call_fixup(cmd->fixup, 0, 0);
 						if (ret < 0)
 						if (ret < 0)
 							goto error;
 							goto error;
 					}
 					}
-					/* type cast NUMBER to STRING, old modules may expect
-					 * all STRING params during fixup */
-					for (i=0; i<t->val[1].u.number; i++) {
-						if (t->val[i+2].type == NUMBER_ST) {
-							snprintf(buf, sizeof(buf)-1, "%ld", 
-										t->val[i+2].u.number);
-							/* fixup currently requires string pkg_malloced*/
-							t->val[i+2].u.string = pkg_malloc(strlen(buf)+1);
-							if (!t->val[i+2].u.string) {
-								LOG(L_CRIT, "ERROR: cannot translate NUMBER"
-											" to STRING\n");
-								ret = E_OUT_OF_MEM;
-								goto error;
+					for (i=0; i < t->val[1].u.number; i++) {
+						if (t->val[i+2].type == RVE_ST) {
+							rve = t->val[i+2].u.data;
+							if (rve_is_constant(rve)) {
+								/* if expression is constant => evaluate it
+								   as string and replace it with the corresp.
+								   string */
+								rv = rval_expr_eval(0, 0, rve);
+								if (rv == 0 ||
+										rval_get_str( 0, 0, &s, rv, 0) < 0 ) {
+									ERR("failed to fix constant rve");
+									if (rv) rval_destroy(rv);
+									ret = E_BUG;
+									goto error;
+								}
+								rval_destroy(rv);
+								rve_destroy(rve);
+								t->val[i+2].type = STRING_ST;/*asciiz string*/
+								t->val[i+2].u.string = s.s;
+								/* len is not used for now */
+								t->val[i+2].u.str.len = s.len;
+								tmp_p = t->val[i+2].u.data;
+								ret = call_fixup(cmd->fixup,
+												&t->val[i+2].u.data, i+1);
+								if (t->val[i+2].u.data != tmp_p)
+									t->val[i+2].type = MODFIXUP_ST;
+								if (ret < 0)
+									goto error;
+							} else {
+								/* expression is not constant => fixup &
+								   optimize it */
+								rve_param_no++;
+								if ((ret=fix_rval_expr(&t->val[i+2].u.data))
+										< 0) {
+									ERR("rve fixup failed\n");
+									ret = E_BUG;
+									goto error;
+								}
 							}
 							}
-							strcpy(t->val[i+2].u.string, buf);
-							t->val[i+2].type = STRING_ST;
+						} else  if (t->val[i+2].type == STRING_ST) {
+							tmp_p = t->val[i+2].u.data;
+							ret = call_fixup(cmd->fixup,
+											&t->val[i+2].u.data, i+1);
+							if (t->val[i+2].u.data != tmp_p)
+								t->val[i+2].type = MODFIXUP_ST;
+							if (ret < 0)
+								goto error;
+						} else {
+							BUG("invalid module function param type %d\n",
+									t->val[i+2].type);
+							ret = E_BUG;
+							goto error;
 						}
 						}
-					}
-					for (i=0; i<t->val[1].u.number; i++) {
-						void *p;
-						p = t->val[i+2].u.data;
-						ret = cmd->c.fixup(&t->val[i+2].u.data, i+1);
-						if (t->val[i+2].u.data != p)
-							t->val[i+2].type = MODFIXUP_ST;
-						if (ret < 0)
+					} /* for */
+					/* here all the params are either STRING_ST
+					   (constant RVEs), MODFIXUP_ST (fixed up)
+					   or RVE_ST (non-ct RVEs) */
+					if (rve_param_no) { /* we have to fix the type */
+						if (cmd->fixup &&
+							!(cmd->fixup_flags & FIXUP_F_FPARAM_RVE) &&
+							cmd->free_fixup == 0) {
+							BUG("non-ct RVEs (%d) in module function call"
+									"that does not support them (%s)\n",
+									rve_param_no, cmd->name);
+							ret = E_BUG;
 							goto error;
 							goto error;
-					}
+						}
+						switch(t->type) {
+							case MODULE1_T:
+								t->type = MODULE1_RVE_T;
+								break;
+							case MODULE2_T:
+								t->type = MODULE2_RVE_T;
+								break;
+							case MODULE3_T:
+								t->type = MODULE3_RVE_T;
+								break;
+							case MODULE4_T:
+								t->type = MODULE4_RVE_T;
+								break;
+							case MODULE5_T:
+								t->type = MODULE5_RVE_T;
+								break;
+							case MODULE6_T:
+								t->type = MODULE6_RVE_T;
+								break;
+							case MODULEX_T:
+								t->type = MODULEX_RVE_T;
+								break;
+							default:
+								BUG("unsupported module function type %d\n",
+										t->type);
+								ret = E_BUG;
+								goto error;
+						}
+					} /* if rve_param_no */
 				}
 				}
 				break;
 				break;
 			case FORCE_SEND_SOCKET_T:
 			case FORCE_SEND_SOCKET_T:

+ 10 - 1
route_struct.c

@@ -443,12 +443,21 @@ void print_action(struct action* t)
 		case IF_T:
 		case IF_T:
 			DBG("if (");
 			DBG("if (");
 			break;
 			break;
-		case MODULE_T:
+		case MODULE0_T:
+		case MODULE1_T:
+		case MODULE2_T:
 		case MODULE3_T:
 		case MODULE3_T:
 		case MODULE4_T:
 		case MODULE4_T:
 		case MODULE5_T:
 		case MODULE5_T:
 		case MODULE6_T:
 		case MODULE6_T:
 		case MODULEX_T:
 		case MODULEX_T:
+		case MODULE1_RVE_T:
+		case MODULE2_RVE_T:
+		case MODULE3_RVE_T:
+		case MODULE4_RVE_T:
+		case MODULE5_RVE_T:
+		case MODULE6_RVE_T:
+		case MODULEX_RVE_T:
 			DBG(" external_module_call(");
 			DBG(" external_module_call(");
 			break;
 			break;
 		case FORCE_RPORT_T:
 		case FORCE_RPORT_T:

+ 10 - 2
route_struct.h

@@ -85,7 +85,12 @@ enum action_type{
 		SET_USERPHONE_T,
 		SET_USERPHONE_T,
 		IF_T, SWITCH_T /* only until fixup*/,
 		IF_T, SWITCH_T /* only until fixup*/,
 		BLOCK_T, EVAL_T, SWITCH_JT_T, SWITCH_COND_T, MATCH_COND_T, WHILE_T,
 		BLOCK_T, EVAL_T, SWITCH_JT_T, SWITCH_COND_T, MATCH_COND_T, WHILE_T,
-		MODULE_T, MODULE3_T, MODULE4_T, MODULE5_T, MODULE6_T, MODULEX_T,
+		/* module function calls with string only parameters */
+		MODULE0_T, MODULE1_T, MODULE2_T, MODULE3_T, MODULE4_T, MODULE5_T,
+		MODULE6_T, MODULEX_T,
+		/* module function calls, that have some RVE parameters */
+		MODULE1_RVE_T, MODULE2_RVE_T, MODULE3_RVE_T,
+		MODULE4_RVE_T, MODULE5_RVE_T, MODULE6_RVE_T, MODULEX_RVE_T,
 		SETFLAG_T, RESETFLAG_T, ISFLAGSET_T ,
 		SETFLAG_T, RESETFLAG_T, ISFLAGSET_T ,
 		AVPFLAG_OPER_T,
 		AVPFLAG_OPER_T,
 		LEN_GT_T, PREFIX_T, STRIP_T,STRIP_TAIL_T,
 		LEN_GT_T, PREFIX_T, STRIP_T,STRIP_TAIL_T,
@@ -122,7 +127,10 @@ enum _operand_subtype{
 		LVAL_ST,  RVE_ST,
 		LVAL_ST,  RVE_ST,
 		RETCODE_ST, CASE_ST,
 		RETCODE_ST, CASE_ST,
 		BLOCK_ST, JUMPTABLE_ST, CONDTABLE_ST, MATCH_CONDTABLE_ST,
 		BLOCK_ST, JUMPTABLE_ST, CONDTABLE_ST, MATCH_CONDTABLE_ST,
-		SELECT_UNFIXED_ST
+		SELECT_UNFIXED_ST,
+		STRING_RVE_ST /* RVE converted to a string (fparam hack) */,
+		RVE_FREE_FIXUP_ST /* (str)RVE fixed up by a reversable fixup */,
+		FPARAM_DYN_ST /* temporary only (fparam hack) */
 };
 };
 
 
 typedef enum _expr_l_type expr_l_type;
 typedef enum _expr_l_type expr_l_type;

+ 32 - 14
rvalue.c

@@ -1062,12 +1062,14 @@ error:
   * if rv == undefined select, avp or pvar, return "".
   * if rv == undefined select, avp or pvar, return "".
   * if an error occurs while evaluating a select, avp or pvar, behave as
   * if an error occurs while evaluating a select, avp or pvar, behave as
   * for the undefined case (and return success).
   * for the undefined case (and return success).
-  * The result points either to a temporary string or inside
-  * new_cache. new_cache must be non zero, initialized previously,
-  * and it _must_ be rval_cache_clean(...)'ed when done.
-  * WARNING: it's not intended for general use, it might return a pointer
-  * to a static buffer (int2str) so use the result a.s.a.p, make a copy.
-  * or use rval_get_str() instead.
+  * The result points either inside the passed rv, inside
+  * new_cache or inside an avp. new_cache must be non zero,
+  * initialized previously and it _must_ be rval_cache_clean(...)'ed when
+  * done.
+  * WARNING: it's not intended for general use. It might return a pointer
+  * inside rv so the result _must_ be treated as read-only. rv and new_cache
+  * must not be released/freed until the result is no longer needed.
+  * For general use see  rval_get_str().
   * @param h - script context handle
   * @param h - script context handle
   * @param msg - sip msg
   * @param msg - sip msg
   * @param tmpv - str return value (pointer to a str struct that will be
   * @param tmpv - str return value (pointer to a str struct that will be
@@ -1089,7 +1091,9 @@ int rval_get_tmp_str(struct run_act_ctx* h, struct sip_msg* msg,
 	
 	
 	switch(rv->type){
 	switch(rv->type){
 		case RV_INT:
 		case RV_INT:
-			tmpv->s=int2str(rv->v.l, &tmpv->len);
+			tmpv->s=sint2strbuf(rv->v.l, tmp_cache->i2s,
+								sizeof(tmp_cache->i2s), &tmpv->len);
+			tmp_cache->cache_type = RV_CACHE_INT2STR;
 			break;
 			break;
 		case RV_STR:
 		case RV_STR:
 			*tmpv=rv->v.s;
 			*tmpv=rv->v.s;
@@ -1099,16 +1103,22 @@ int rval_get_tmp_str(struct run_act_ctx* h, struct sip_msg* msg,
 				i=(run_actions(h, rv->v.action, msg)>0);
 				i=(run_actions(h, rv->v.action, msg)>0);
 			else
 			else
 				i=0;
 				i=0;
-			tmpv->s=int2str(i, &tmpv->len);
+			tmpv->s=sint2strbuf(i, tmp_cache->i2s,
+								sizeof(tmp_cache->i2s), &tmpv->len);
+			tmp_cache->cache_type = RV_CACHE_INT2STR;
 			break;
 			break;
 		case RV_BEXPR:
 		case RV_BEXPR:
 			i=eval_expr(h, rv->v.bexpr, msg);
 			i=eval_expr(h, rv->v.bexpr, msg);
 			if (i==EXPR_DROP){
 			if (i==EXPR_DROP){
 				i=0; /* false */
 				i=0; /* false */
-				tmpv->s=int2str(i, &tmpv->len);
+				tmpv->s=sint2strbuf(i, tmp_cache->i2s,
+						sizeof(tmp_cache->i2s), &tmpv->len);
+				tmp_cache->cache_type = RV_CACHE_INT2STR;
 				return EXPR_DROP;
 				return EXPR_DROP;
 			}
 			}
-			tmpv->s=int2str(i, &tmpv->len);
+			tmpv->s=sint2strbuf(i, tmp_cache->i2s, sizeof(tmp_cache->i2s),
+								&tmpv->len);
+			tmp_cache->cache_type = RV_CACHE_INT2STR;
 			break;
 			break;
 		case RV_SEL:
 		case RV_SEL:
 			i=run_select(tmpv, &rv->v.sel, msg);
 			i=run_select(tmpv, &rv->v.sel, msg);
@@ -1126,7 +1136,9 @@ int rval_get_tmp_str(struct run_act_ctx* h, struct sip_msg* msg,
 					*tmpv=cache->c.avp_val.s;
 					*tmpv=cache->c.avp_val.s;
 				}else if (cache->val_type==RV_INT){
 				}else if (cache->val_type==RV_INT){
 					i=cache->c.avp_val.n;
 					i=cache->c.avp_val.n;
-					tmpv->s=int2str(i, &tmpv->len);
+					tmpv->s=sint2strbuf(i, tmp_cache->i2s,
+										sizeof(tmp_cache->i2s), &tmpv->len);
+					tmp_cache->cache_type = RV_CACHE_INT2STR;
 				}else if (cache->val_type==RV_NONE){
 				}else if (cache->val_type==RV_NONE){
 					goto undef;
 					goto undef;
 				}else goto error_cache;
 				}else goto error_cache;
@@ -1141,7 +1153,9 @@ int rval_get_tmp_str(struct run_act_ctx* h, struct sip_msg* msg,
 						*tmpv=tmp_cache->c.avp_val.s;
 						*tmpv=tmp_cache->c.avp_val.s;
 					}else{
 					}else{
 						i=tmp_cache->c.avp_val.n;
 						i=tmp_cache->c.avp_val.n;
-						tmpv->s=int2str(i, &tmpv->len);
+						tmpv->s=sint2strbuf(i, tmp_cache->i2s,
+										sizeof(tmp_cache->i2s), &tmpv->len);
+						tmp_cache->cache_type = RV_CACHE_INT2STR;
 					}
 					}
 				}else goto undef;
 				}else goto undef;
 			}
 			}
@@ -1152,7 +1166,9 @@ int rval_get_tmp_str(struct run_act_ctx* h, struct sip_msg* msg,
 					*tmpv=cache->c.pval.rs;
 					*tmpv=cache->c.pval.rs;
 				}else if (cache->val_type==RV_INT){
 				}else if (cache->val_type==RV_INT){
 					i=cache->c.pval.ri;
 					i=cache->c.pval.ri;
-					tmpv->s=int2str(i, &tmpv->len);
+					tmpv->s=sint2strbuf(i, tmp_cache->i2s,
+										sizeof(tmp_cache->i2s), &tmpv->len);
+					tmp_cache->cache_type = RV_CACHE_INT2STR;
 				}else if (cache->val_type==RV_NONE){
 				}else if (cache->val_type==RV_NONE){
 					goto undef;
 					goto undef;
 				}else goto error_cache;
 				}else goto error_cache;
@@ -1170,7 +1186,9 @@ int rval_get_tmp_str(struct run_act_ctx* h, struct sip_msg* msg,
 					}else if (likely(tmp_cache->c.pval.flags & PV_VAL_INT)){
 					}else if (likely(tmp_cache->c.pval.flags & PV_VAL_INT)){
 						i=tmp_cache->c.pval.ri;
 						i=tmp_cache->c.pval.ri;
 						pv_value_destroy(&tmp_cache->c.pval);
 						pv_value_destroy(&tmp_cache->c.pval);
-						tmpv->s=int2str(i, &tmpv->len);
+						tmpv->s=sint2strbuf(i, tmp_cache->i2s,
+										sizeof(tmp_cache->i2s), &tmpv->len);
+						tmp_cache->cache_type = RV_CACHE_INT2STR;
 					}else{
 					}else{
 						/* no PV_VAL_STR and no PV_VAL_INT => undef
 						/* no PV_VAL_STR and no PV_VAL_INT => undef
 						   (PV_VAL_NULL) */
 						   (PV_VAL_NULL) */

+ 5 - 1
rvalue.h

@@ -26,12 +26,14 @@
  *  2009-04-28  added string and interger versions for the EQ and DIFF
  *  2009-04-28  added string and interger versions for the EQ and DIFF
  *              operators (andrei)
  *              operators (andrei)
  *  2009-05-05  casts operator for int & string (andrei)
  *  2009-05-05  casts operator for int & string (andrei)
+ *  2010-03-16  space for an int2str result inside rval_cache (andrei)
  */
  */
 
 
 #ifndef _rvalue_h_
 #ifndef _rvalue_h_
 #define _rvalue_h_
 #define _rvalue_h_
 
 
 #include "str.h"
 #include "str.h"
+#include "ut.h"
 #include "usr_avp.h"
 #include "usr_avp.h"
 #include "select.h"
 #include "select.h"
 #include "pvar.h"
 #include "pvar.h"
@@ -138,7 +140,8 @@ enum rval_cache_type{
 	RV_CACHE_EMPTY,
 	RV_CACHE_EMPTY,
 	RV_CACHE_PVAR,
 	RV_CACHE_PVAR,
 	RV_CACHE_AVP,
 	RV_CACHE_AVP,
-	RV_CACHE_SELECT
+	RV_CACHE_SELECT,
+	RV_CACHE_INT2STR
 };
 };
 
 
 /** value cache for a rvalue struct.
 /** value cache for a rvalue struct.
@@ -152,6 +155,7 @@ struct rval_cache{
 		int_str avp_val; /**< avp value */
 		int_str avp_val; /**< avp value */
 		pv_value_t pval; /**< pvar value */
 		pv_value_t pval; /**< pvar value */
 	}c;
 	}c;
+	char i2s[INT2STR_MAX_LEN]; /**< space for converting an int to string*/
 };
 };
 
 
 
 

+ 400 - 260
sr_module.c

@@ -49,6 +49,7 @@
  */
  */
 
 
 #include "sr_module.h"
 #include "sr_module.h"
+#include "mod_fix.h"
 #include "dprint.h"
 #include "dprint.h"
 #include "error.h"
 #include "error.h"
 #include "mem/mem.h"
 #include "mem/mem.h"
@@ -150,6 +151,69 @@ int register_builtin_modules()
 
 
 
 
 
 
+/** convert cmd exports to current format.
+  * @param ver - module interface versions (0 == ser, 1 == kam).
+  * @param src - null terminated array of cmd exports
+  *              (either ser_cmd_export_t or kam_cmd_export_t, depending
+  *               on ver).
+  * @param mod - pointer to module exports structure.
+  * @return - pkg_malloc'ed null terminated sr_cmd_export_v31_t array with
+  *           the converted cmd exports  or 0 on error.
+  */
+static sr31_cmd_export_t* sr_cmd_exports_convert(unsigned ver,
+													void* src, void* mod)
+{
+	int i, n;
+	ser_cmd_export_t* ser_cmd;
+	kam_cmd_export_t* kam_cmd;
+	sr31_cmd_export_t* ret;
+	
+	ser_cmd = 0;
+	kam_cmd = 0;
+	ret = 0;
+	n = 0;
+	/* count the number of elements */
+	if (ver == 0) {
+		ser_cmd = src;
+		for (; ser_cmd[n].name; n++);
+	} else if (ver == 1) {
+		kam_cmd = src;
+		for (; kam_cmd[n].name; n++);
+	} else goto error; /* unknown interface version */
+	/* alloc & init new array */
+	ret = pkg_malloc(sizeof(*ret)*(n+1));
+	memset(ret, 0, sizeof(*ret)*(n+1));
+	/* convert/copy */
+	for (i=0; i < n; i++) {
+		if (ver == 0) {
+			ret[i].name = ser_cmd[i].name;
+			ret[i].function = ser_cmd[i].function;
+			ret[i].param_no = ser_cmd[i].param_no;
+			ret[i].fixup = ser_cmd[i].fixup;
+			ret[i].free_fixup = 0; /* no present in ser  <= 2.1 */
+			ret[i].flags = ser_cmd[i].flags;
+		} else {
+			ret[i].name = kam_cmd[i].name;
+			ret[i].function = kam_cmd[i].function;
+			ret[i].param_no = kam_cmd[i].param_no;
+			ret[i].fixup = kam_cmd[i].fixup;
+			ret[i].free_fixup = kam_cmd[i].free_fixup;
+			ret[i].flags = kam_cmd[i].flags;
+		}
+		/* 3.1+ specific stuff */
+		ret[i].fixup_flags = 0;
+		ret[i].module_exports = mod;
+		/* fill known free fixups */
+		if (ret[i].fixup && ret[i].free_fixup == 0)
+			ret[i].free_fixup = get_fixup_free(ret[i].fixup);
+	}
+	return ret;
+error:
+	return 0;
+}
+
+
+
 /* registers a module,  register_f= module register  functions
 /* registers a module,  register_f= module register  functions
  * returns <0 on error, 0 on success */
  * returns <0 on error, 0 on success */
 static int register_module(unsigned ver, union module_exports_u* e,
 static int register_module(unsigned ver, union module_exports_u* e,
@@ -169,36 +233,96 @@ static int register_module(unsigned ver, union module_exports_u* e,
 	memset(mod,0, sizeof(struct sr_module));
 	memset(mod,0, sizeof(struct sr_module));
 	mod->path=path;
 	mod->path=path;
 	mod->handle=handle;
 	mod->handle=handle;
-	mod->mod_interface_ver=ver;
-	mod->exports=e;
-	mod->next=modules;
-	modules=mod;
+	mod->orig_mod_interface_ver=ver;
+	/* convert exports to sr31 format */
+	if (ver == 0) {
+		/* ser <= 3.0 */
+		mod->exports.name = e->v0.name;
+		if (e->v0.cmds) {
+			mod->exports.cmds = sr_cmd_exports_convert(ver, e->v0.cmds, mod);
+			if (mod->exports.cmds == 0) {
+				ERR("failed to convert module command exports to 3.1 format"
+						" for module \"%s\" (%s), interface version %d\n",
+						mod->exports.name, mod->path, ver);
+				ret = E_UNSPEC;
+				goto error;
+			}
+		}
+		mod->exports.params = e->v0.params;
+		mod->exports.init_f = e->v0.init_f;
+		mod->exports.response_f = e->v0.response_f;
+		mod->exports.destroy_f = e->v0.destroy_f;
+		mod->exports.onbreak_f = e->v0.onbreak_f;
+		mod->exports.init_child_f = e->v0.init_child_f;
+		mod->exports.dlflags = 0; /* not used in ser <= 3.0 */
+		mod->exports.rpc_methods = e->v0.rpc_methods;
+		/* the rest are 0, not used in ser */
+	} else if (ver == 1) {
+		/* kamailio <= 3.0 */
+		mod->exports.name = e->v1.name;
+		if (e->v1.cmds) {
+			mod->exports.cmds = sr_cmd_exports_convert(ver, e->v1.cmds, mod);
+			if (mod->exports.cmds == 0) {
+				ERR("failed to convert module command exports to 3.1 format"
+						" for module \"%s\" (%s), interface version %d\n",
+						mod->exports.name, mod->path, ver);
+				ret = E_UNSPEC;
+				goto error;
+			}
+		}
+		mod->exports.params = e->v1.params;
+		mod->exports.init_f = e->v1.init_f;
+		mod->exports.response_f = e->v1.response_f;
+		mod->exports.destroy_f = e->v1.destroy_f;
+		mod->exports.onbreak_f = 0; /* not used in k <= 3.0 */
+		mod->exports.init_child_f = e->v1.init_child_f;
+		mod->exports.dlflags = e->v1.dlflags;
+		mod->exports.rpc_methods = 0; /* not used in k <= 3.0 */
+		mod->exports.stats = e->v1.stats;
+		mod->exports.mi_cmds = e->v1.mi_cmds;
+		mod->exports.items = e->v1.items;
+		mod->exports.procs = e->v1.procs;
+	} else {
+		ERR("unsupported module interface version %d\n", ver);
+		ret = E_UNSPEC;
+		goto error;
+	}
 
 
-	if (ver==1 && e->v1.items) {
+	if (mod->exports.items) {
 		/* register module pseudo-variables for kamailio modules */
 		/* register module pseudo-variables for kamailio modules */
-		LM_DBG("register PV from: %s\n", e->c.name);
-		if (register_pvars_mod(e->c.name, e->v1.items)!=0) {
-			LM_ERR("failed to register pseudo-variables for module %s\n",
-				e->c.name);
-			pkg_free(mod);
-			return -1;
+		LM_DBG("register PV from: %s\n", mod->exports.name);
+		if (register_pvars_mod(mod->exports.name, mod->exports.items)!=0) {
+			LM_ERR("failed to register pseudo-variables for module %s (%s)\n",
+				mod->exports.name, path);
+			ret = E_UNSPEC;
+			goto error;
 		}
 		}
-	}else if (ver==0 && e->v0.rpc_methods){
+	}
+	if (mod->exports.rpc_methods){
 		/* register rpcs for ser modules */
 		/* register rpcs for ser modules */
-		i=rpc_register_array(e->v0.rpc_methods);
+		i=rpc_register_array(mod->exports.rpc_methods);
 		if (i<0){
 		if (i<0){
-			ERR("failed to register RPCs for module %s\n", e->c.name);
+			ERR("failed to register RPCs for module %s (%s)\n",
+					mod->exports.name, path);
+			ret = E_UNSPEC;
 			goto error;
 			goto error;
 		}else if (i>0){
 		}else if (i>0){
 			ERR("%d duplicate RPCs name detected while registering RPCs"
 			ERR("%d duplicate RPCs name detected while registering RPCs"
-					" declared in modules %s\n", i, e->c.name);
+					" declared in module %s (%s)\n",
+					i, mod->exports.name, path);
+			ret = E_UNSPEC;
 			goto error;
 			goto error;
 		}
 		}
 		/* i==0 => success */
 		/* i==0 => success */
 	}
 	}
 
 
+	/* link module in the list */
+	mod->next=modules;
+	modules=mod;
 	return 0;
 	return 0;
 error:
 error:
+	if (mod)
+		pkg_free(mod);
 	return ret;
 	return ret;
 }
 }
 
 
@@ -473,51 +597,33 @@ skip:
 
 
 /* searches the module list for function name in module mod and returns 
 /* searches the module list for function name in module mod and returns 
  *  a pointer to the "name" function record union or 0 if not found
  *  a pointer to the "name" function record union or 0 if not found
- * sets also *mod_if_ver to the module interface version (needed to know
- * which member of the union should be accessed v0 or v1)
+ * sets also *mod_if_ver to the original module interface version.
  * mod==0 is a wildcard matching all modules
  * mod==0 is a wildcard matching all modules
  * flags parameter is OR value of all flags that must match
  * flags parameter is OR value of all flags that must match
  */
  */
-union cmd_export_u* find_mod_export_record(char* mod, char* name,
+sr31_cmd_export_t* find_mod_export_record(char* mod, char* name,
 											int param_no, int flags,
 											int param_no, int flags,
 											unsigned* mod_if_ver)
 											unsigned* mod_if_ver)
 {
 {
 	struct sr_module* t;
 	struct sr_module* t;
-	union cmd_export_u* cmd;
-	int i;
-	unsigned mver;
-
-#define FIND_EXPORT_IN_MOD(VER) \
-		if (t->exports->VER.cmds) \
-			for(i=0, cmd=(void*)&t->exports->VER.cmds[0]; cmd->VER.name; \
-					i++, cmd=(void*)&t->exports->VER.cmds[i]){\
-				if((strcmp(name, cmd->VER.name)==0)&& \
-					((cmd->VER.param_no==param_no) || \
-					 (cmd->VER.param_no==VAR_PARAM_NO)) && \
-					((cmd->VER.flags & flags) == flags) \
-				){ \
-					DBG("find_export_record: found <%s> in module %s [%s]\n", \
-						name, t->exports->VER.name, t->path); \
-					*mod_if_ver=mver; \
-					return cmd; \
-				} \
-			}
+	sr31_cmd_export_t* cmd;
 
 
 	for(t=modules;t;t=t->next){
 	for(t=modules;t;t=t->next){
-		if (mod!=0 && (strcmp(t->exports->c.name, mod) !=0))
+		if (mod!=0 && (strcmp(t->exports.name, mod) !=0))
 			continue;
 			continue;
-		mver=t->mod_interface_ver;
-		switch (mver){
-			case 0:
-				FIND_EXPORT_IN_MOD(v0);
-				break;
-			case 1:
-				FIND_EXPORT_IN_MOD(v1);
-				break;
-			default:
-				BUG("invalid module interface version %d for modules %s\n",
-						t->mod_interface_ver, t->path);
-		}
+		if (t->exports.cmds)
+			for(cmd=&t->exports.cmds[0]; cmd->name; cmd++) {
+				if((strcmp(name, cmd->name) == 0) &&
+					((cmd->param_no == param_no) ||
+					 (cmd->param_no==VAR_PARAM_NO)) &&
+					((cmd->flags & flags) == flags)
+				){
+					DBG("find_export_record: found <%s> in module %s [%s]\n",
+						name, t->exports.name, t->path);
+					*mod_if_ver=t->orig_mod_interface_ver;
+					return cmd;
+				}
+			}
 	}
 	}
 	DBG("find_export_record: <%s> not found \n", name);
 	DBG("find_export_record: <%s> not found \n", name);
 	return 0;
 	return 0;
@@ -532,7 +638,7 @@ union cmd_export_u* find_mod_export_record(char* mod, char* name,
  * mod==0 is a wildcard matching all modules
  * mod==0 is a wildcard matching all modules
  * flags parameter is OR value of all flags that must match
  * flags parameter is OR value of all flags that must match
  */
  */
-union cmd_export_u* find_export_record(char* name,
+sr31_cmd_export_t* find_export_record(char* name,
 											int param_no, int flags,
 											int param_no, int flags,
 											unsigned* mod_if_ver)
 											unsigned* mod_if_ver)
 {
 {
@@ -543,11 +649,11 @@ union cmd_export_u* find_export_record(char* name,
 
 
 cmd_function find_export(char* name, int param_no, int flags)
 cmd_function find_export(char* name, int param_no, int flags)
 {
 {
-	union cmd_export_u* cmd;
+	sr31_cmd_export_t* cmd;
 	unsigned mver;
 	unsigned mver;
 	
 	
 	cmd = find_export_record(name, param_no, flags, &mver);
 	cmd = find_export_record(name, param_no, flags, &mver);
-	return cmd?cmd->c.function:0;
+	return cmd?cmd->function:0;
 }
 }
 
 
 
 
@@ -565,12 +671,12 @@ rpc_export_t* find_rpc_export(char* name, int flags)
  */
  */
 cmd_function find_mod_export(char* mod, char* name, int param_no, int flags)
 cmd_function find_mod_export(char* mod, char* name, int param_no, int flags)
 {
 {
-	union cmd_export_u* cmd;
+	sr31_cmd_export_t* cmd;
 	unsigned mver;
 	unsigned mver;
 
 
 	cmd=find_mod_export_record(mod, name, param_no, flags, &mver);
 	cmd=find_mod_export_record(mod, name, param_no, flags, &mver);
 	if (cmd)
 	if (cmd)
-		return cmd->c.function;
+		return cmd->function;
 	
 	
 	DBG("find_mod_export: <%s> in module <%s> not found\n", name, mod);
 	DBG("find_mod_export: <%s> in module <%s> not found\n", name, mod);
 	return 0;
 	return 0;
@@ -581,7 +687,7 @@ struct sr_module* find_module_by_name(char* mod) {
 	struct sr_module* t;
 	struct sr_module* t;
 
 
 	for(t = modules; t; t = t->next) {
 	for(t = modules; t; t = t->next) {
-		if (strcmp(mod, t->exports->c.name) == 0) {
+		if (strcmp(mod, t->exports.name) == 0) {
 			return t;
 			return t;
 		}
 		}
 	}
 	}
@@ -597,30 +703,17 @@ void* find_param_export(struct sr_module* mod, char* name,
 
 
 	if (!mod)
 	if (!mod)
 		return 0;
 		return 0;
-	param=0;
-	switch(mod->mod_interface_ver){
-		case 0:
-			param=mod->exports->v0.params;
-			break;
-		case 1:
-			param=mod->exports->v1.params;
-			break;
-		default:
-			BUG("bad module interface version %d in module %s [%s]\n",
-					mod->mod_interface_ver, mod->exports->c.name, mod->path);
-			return 0;
-	}
-	for(;param && param->name ; param++) {
+	for(param = mod->exports.params ;param && param->name ; param++) {
 		if ((strcmp(name, param->name) == 0) &&
 		if ((strcmp(name, param->name) == 0) &&
 			((param->type & PARAM_TYPE_MASK(type_mask)) != 0)) {
 			((param->type & PARAM_TYPE_MASK(type_mask)) != 0)) {
 			DBG("find_param_export: found <%s> in module %s [%s]\n",
 			DBG("find_param_export: found <%s> in module %s [%s]\n",
-				name, mod->exports->c.name, mod->path);
+				name, mod->exports.name, mod->path);
 			*param_type = param->type;
 			*param_type = param->type;
 			return param->param_pointer;
 			return param->param_pointer;
 		}
 		}
 	}
 	}
 	DBG("find_param_export: parameter <%s> not found in module <%s>\n",
 	DBG("find_param_export: parameter <%s> not found in module <%s>\n",
-			name, mod->exports->c.name);
+			name, mod->exports.name);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -632,19 +725,8 @@ void destroy_modules()
 	t=modules;
 	t=modules;
 	while(t) {
 	while(t) {
 		foo=t->next;
 		foo=t->next;
-		if (t->exports){
-			switch(t->mod_interface_ver){
-				case 0:
-					if ((t->exports->v0.destroy_f)) t->exports->v0.destroy_f();
-					break;
-				case 1:
-					if ((t->exports->v1.destroy_f)) t->exports->v1.destroy_f();
-					break;
-				default:
-					BUG("bad module interface version %d in module %s [%s]\n",
-						t->mod_interface_ver, t->exports->c.name,
-						t->path);
-			}
+		if (t->exports.destroy_f){
+			t->exports.destroy_f();
 		}
 		}
 		pkg_free(t);
 		pkg_free(t);
 		t=foo;
 		t=foo;
@@ -667,36 +749,14 @@ int init_modules(void)
 	struct sr_module* t;
 	struct sr_module* t;
 
 
 	for(t = modules; t; t = t->next) {
 	for(t = modules; t; t = t->next) {
-		if (t->exports){
-			switch(t->mod_interface_ver){
-				case 0:
-					if (t->exports->v0.init_f)
-						if (t->exports->v0.init_f() != 0) {
-							LOG(L_ERR, "init_modules(): Error while"
-										" initializing module %s\n",
-										t->exports->v0.name);
-							return -1;
-						}
-					if (t->exports->v0.response_f)
-						mod_response_cbk_no++;
-					break;
-				case 1:
-					if (t->exports->v1.init_f)
-						if (t->exports->v1.init_f() != 0) {
-							LOG(L_ERR, "init_modules(): Error while"
-										" initializing module %s\n",
-										t->exports->v1.name);
-							return -1;
-						}
-					if (t->exports->v1.response_f)
-						mod_response_cbk_no++;
-					break;
-				default:
-					BUG("bad module interface version %d in module %s [%s]\n",
-						t->exports->c.name, t->path);
-					return -1;
+		if (t->exports.init_f)
+			if (t->exports.init_f() != 0) {
+				LOG(L_ERR, "init_modules(): Error while"
+						" initializing module %s\n", t->exports.name);
+				return -1;
 			}
 			}
-		}
+		if (t->exports.response_f)
+			mod_response_cbk_no++;
 	}
 	}
 	mod_response_cbks=pkg_malloc(mod_response_cbk_no * 
 	mod_response_cbks=pkg_malloc(mod_response_cbk_no * 
 									sizeof(response_function));
 									sizeof(response_function));
@@ -705,27 +765,17 @@ int init_modules(void)
 					" for %d response_f callbacks\n", mod_response_cbk_no);
 					" for %d response_f callbacks\n", mod_response_cbk_no);
 		return -1;
 		return -1;
 	}
 	}
-	for (t=modules, i=0; t && (i<mod_response_cbk_no); t=t->next){
-		if (t->exports){
-			switch(t->mod_interface_ver){
-				case 0:
-					if (t->exports->v0.response_f){
-						mod_response_cbks[i]=t->exports->v0.response_f;
-						i++;
-					}
-					break;
-				case 1:
-					if (t->exports->v1.response_f){
-						mod_response_cbks[i]=t->exports->v1.response_f;
-						i++;
-					}
-					break;
-			}
+	for (t=modules, i=0; t && (i<mod_response_cbk_no); t=t->next) {
+		if (t->exports.response_f) {
+			mod_response_cbks[i]=t->exports.response_f;
+			i++;
 		}
 		}
 	}
 	}
 	return 0;
 	return 0;
 }
 }
 
 
+
+
 /*
 /*
  * per-child initialization
  * per-child initialization
  */
  */
@@ -745,30 +795,12 @@ int init_child(int rank)
 
 
 
 
 	for(t = modules; t; t = t->next) {
 	for(t = modules; t; t = t->next) {
-		switch(t->mod_interface_ver){
-			case 0:
-				if (t->exports->v0.init_child_f) {
-					if ((t->exports->v0.init_child_f(rank)) < 0) {
-						LOG(L_ERR, "init_child(): Initialization of child"
-									" %d failed\n", rank);
-						return -1;
-					}
-				}
-				break;
-			case 1:
-				if (t->exports->v1.init_child_f) {
-					if ((t->exports->v1.init_child_f(rank)) < 0) {
-						LOG(L_ERR, "init_child(): Initialization of child"
-									" %d failed\n", rank);
-						return -1;
-					}
-				}
-				break;
-			default:
-				BUG("bad module interface version %d in module %s [%s]\n",
-						t->mod_interface_ver, t->exports->c.name,
-						t->path);
+		if (t->exports.init_child_f) {
+			if ((t->exports.init_child_f(rank)) < 0) {
+				LOG(L_ERR, "init_child(): Initialization of child"
+							" %d failed\n", rank);
 				return -1;
 				return -1;
+			}
 		}
 		}
 	}
 	}
 	return 0;
 	return 0;
@@ -789,43 +821,19 @@ static int init_mod_child( struct sr_module* m, int rank )
 		   propagate it up the stack
 		   propagate it up the stack
 		 */
 		 */
 		if (init_mod_child(m->next, rank)!=0) return -1;
 		if (init_mod_child(m->next, rank)!=0) return -1;
-		if (m->exports){
-			switch(m->mod_interface_ver){
-				case 0:
-					if (m->exports->v0.init_child_f) {
-						DBG("DEBUG: init_mod_child (%d): %s\n",
-								rank, m->exports->v0.name);
-						if (m->exports->v0.init_child_f(rank)<0) {
-							LOG(L_ERR, "init_mod_child(): Error while"
-										" initializing module %s\n",
-										m->exports->v0.name);
-							return -1;
-						} else {
-							/* module correctly initialized */
-							return 0;
-						}
-					}
-					/* no init function -- proceed with success */
-					return 0;
-				case 1:
-					if (m->exports->v1.init_child_f) {
-						DBG("DEBUG: init_mod_child (%d): %s\n",
-								rank, m->exports->v1.name);
-						if (m->exports->v1.init_child_f(rank)<0) {
-							LOG(L_ERR, "init_mod_child(): Error while"
-										" initializing module %s\n",
-										m->exports->v1.name);
-							return -1;
-						} else {
-							/* module correctly initialized */
-							return 0;
-						}
-					}
-					/* no init function -- proceed with success */
-					return 0;
+		if (m->exports.init_child_f) {
+			DBG("DEBUG: init_mod_child (%d): %s\n", rank, m->exports.name);
+			if (m->exports.init_child_f(rank)<0) {
+				LOG(L_ERR, "init_mod_child(): Error while"
+							" initializing module %s (%s)\n",
+							m->exports.name, m->path);
+				return -1;
+			} else {
+				/* module correctly initialized */
+				return 0;
 			}
 			}
 		}
 		}
-		/* no exports -- proceed with success */
+		/* no init function -- proceed with success */
 		return 0;
 		return 0;
 	} else {
 	} else {
 		/* end of list */
 		/* end of list */
@@ -856,40 +864,20 @@ static int init_mod( struct sr_module* m )
 		   propagate it up the stack
 		   propagate it up the stack
 		 */
 		 */
 		if (init_mod(m->next)!=0) return -1;
 		if (init_mod(m->next)!=0) return -1;
-		if (m->exports){
-			switch(m->mod_interface_ver){
-				case 0:
-					if ( m->exports->v0.init_f) {
-						DBG("DEBUG: init_mod: %s\n", m->exports->v0.name);
-						if (m->exports->v0.init_f()!=0) {
-							LOG(L_ERR, "init_mod(): Error while initializing"
-										" module %s\n", m->exports->v0.name);
-							return -1;
-						} else {
-							/* module correctly initialized */
-							return 0;
-						}
-					}
-					/* no init function -- proceed with success */
-					return 0;
-				case 1:
-					if ( m->exports->v1.init_f) {
-						DBG("DEBUG: init_mod: %s\n", m->exports->v1.name);
-						if (m->exports->v1.init_f()!=0) {
-							LOG(L_ERR, "init_mod(): Error while initializing"
-										" module %s\n", m->exports->v1.name);
-							return -1;
-						} else {
-							/* module correctly initialized */
-							return 0;
-						}
-					}
-					/* no init function -- proceed with success */
+			if (m->exports.init_f) {
+				DBG("DEBUG: init_mod: %s\n", m->exports.name);
+				if (m->exports.init_f()!=0) {
+					LOG(L_ERR, "init_mod(): Error while initializing"
+								" module %s (%s)\n",
+								m->exports.name, m->path);
+					return -1;
+				} else {
+					/* module correctly initialized */
 					return 0;
 					return 0;
+				}
 			}
 			}
-		}
-		/* no exports -- proceed with success */
-		return 0;
+			/* no init function -- proceed with success */
+			return 0;
 	} else {
 	} else {
 		/* end of list */
 		/* end of list */
 		return 0;
 		return 0;
@@ -910,18 +898,8 @@ int init_modules(void)
 		return i;
 		return i;
 
 
 	for(t = modules; t; t = t->next)
 	for(t = modules; t; t = t->next)
-		if (t->exports){
-			switch(t->mod_interface_ver){
-				case 0:
-					if (t->exports->v0.response_f)
-						mod_response_cbk_no++;
-					break;
-				case 1:
-					if (t->exports->v1.response_f)
-						mod_response_cbk_no++;
-					break;
-			}
-		}
+		if (t->exports.response_f)
+			mod_response_cbk_no++;
 	mod_response_cbks=pkg_malloc(mod_response_cbk_no * 
 	mod_response_cbks=pkg_malloc(mod_response_cbk_no * 
 									sizeof(response_function));
 									sizeof(response_function));
 	if (mod_response_cbks==0){
 	if (mod_response_cbks==0){
@@ -929,24 +907,11 @@ int init_modules(void)
 					" for %d response_f callbacks\n", mod_response_cbk_no);
 					" for %d response_f callbacks\n", mod_response_cbk_no);
 		return -1;
 		return -1;
 	}
 	}
-	for (t=modules, i=0; t && (i<mod_response_cbk_no); t=t->next){
-		if (t->exports){
-			switch(t->mod_interface_ver){
-				case 0:
-					if (t->exports->v0.response_f){
-						mod_response_cbks[i]=t->exports->v0.response_f;
-						i++;
-					}
-					break;
-				case 1:
-					if (t->exports->v1.response_f){
-						mod_response_cbks[i]=t->exports->v1.response_f;
-						i++;
-					}
-					break;
-			}
+	for (t=modules, i=0; t && (i<mod_response_cbk_no); t=t->next)
+		if (t->exports.response_f) {
+			mod_response_cbks[i]=t->exports.response_f;
+			i++;
 		}
 		}
-	}
 	
 	
 	return 0;
 	return 0;
 }
 }
@@ -974,6 +939,30 @@ int fixup_get_param_count(void **cur_param, int cur_param_no)
 }
 }
 
 
 
 
+
+/** get a pointer to a parameter internal type.
+ * @param param
+ * @return pointer to the parameter internal type.
+ */
+action_param_type* fixup_get_param_ptype(void** param)
+{
+	action_u_t* a;
+	a = (void*)((char*)param - (char*)&(((action_u_t*)(0))->u.string));
+	return &a->type;
+}
+
+
+/** get a parameter internal type.
+ * @see fixup_get_param_ptype().
+ * @return paramter internal type.
+ */
+action_param_type fixup_get_param_type(void** param)
+{
+	return *fixup_get_param_ptype(param);
+}
+
+
+
 /* fixes flag params (resolves possible named flags)
 /* fixes flag params (resolves possible named flags)
  * use PARAM_USE_FUNC|PARAM_STRING as a param. type and create
  * use PARAM_USE_FUNC|PARAM_STRING as a param. type and create
  * a wrapper function that does just:
  * a wrapper function that does just:
@@ -1184,6 +1173,7 @@ error:
 
 
 /** fparam_t free function.
 /** fparam_t free function.
  *  Frees the "content" of a fparam, but not the fparam itself.
  *  Frees the "content" of a fparam, but not the fparam itself.
+ *  Note: it doesn't free fp->orig!
  *  Assumes pkg_malloc'ed content.
  *  Assumes pkg_malloc'ed content.
  *  @param fp -  fparam to be freed
  *  @param fp -  fparam to be freed
  *
  *
@@ -1235,10 +1225,25 @@ void fparam_free_contents(fparam_t* fp)
 			}
 			}
 			break;
 			break;
 	}
 	}
-	if (fp->orig){
-		pkg_free(fp->orig);
-		fp->orig=0;
-	}
+}
+
+
+
+/** generic free fixup type function for a fixed fparam.
+ * It will free whatever was allocated during the initial fparam fixup
+ * and restore the original param value.
+ */
+void fparam_free_restore(void** param)
+{
+	fparam_t *fp;
+	void *orig;
+	
+	fp = *param;
+	orig = fp->orig;
+	fp->orig = 0;
+	fparam_free_contents(fp);
+	pkg_free(fp);
+	*param = orig;
 }
 }
 
 
 
 
@@ -1255,6 +1260,13 @@ int fix_param_types(int types, void** param)
 	int ret;
 	int ret;
 	int t;
 	int t;
 	
 	
+	if (fixup_get_param_type(param) == STRING_RVE_ST &&
+			(types & (FPARAM_INT|FPARAM_STR|FPARAM_STRING))) {
+		/* if called with a RVE already converted to string =>
+		   don't try AVP, PVAR or SELECT (to avoid double
+		   deref., e.g.: $foo="$bar"; f($foo) ) */
+		types &= ~ (FPARAM_AVP|FPARAM_PVS|FPARAM_SELECT|FPARAM_PVE);
+	}
 	for (t=types & ~(types-1); types; types&=(types-1), t=types & ~(types-1)){
 	for (t=types & ~(types-1); types; types&=(types-1), t=types & ~(types-1)){
 		if ((ret=fix_param(t, param))<=0) return ret;
 		if ((ret=fix_param(t, param))<=0) return ret;
 	}
 	}
@@ -1275,10 +1287,17 @@ int fix_param_types(int types, void** param)
 int fixup_var_str_12(void** param, int param_no)
 int fixup_var_str_12(void** param, int param_no)
 {
 {
 	int ret;
 	int ret;
-	if ((ret = fix_param(FPARAM_PVS, param)) <= 0) return ret;
-	if ((ret = fix_param(FPARAM_AVP, param)) <= 0) return ret;
-	if ((ret = fix_param(FPARAM_SELECT, param)) <= 0) return ret;
-	if ((ret = fix_param(FPARAM_PVE, param)) <= 0) return ret;
+	if (fixup_get_param_type(param) != STRING_RVE_ST) {
+		/* if called with a RVE already converted to string =>
+		   don't try AVP, PVAR or SELECT (to avoid double
+		   deref., e.g.: $foo="$bar"; f($foo) ) */
+		if ((ret = fix_param(FPARAM_PVS, param)) <= 0) return ret;
+		if ((ret = fix_param(FPARAM_AVP, param)) <= 0) return ret;
+		if ((ret = fix_param(FPARAM_SELECT, param)) <= 0) return ret;
+		/* FIXME: if not PVE (string only), fix as string! or
+		   make a separate fixup  fixup_varpve_... */
+		if ((ret = fix_param(FPARAM_PVE, param)) <= 0) return ret;
+	}
 	if ((ret = fix_param(FPARAM_STR, param)) <= 0) return ret;
 	if ((ret = fix_param(FPARAM_STR, param)) <= 0) return ret;
 	ERR("Error while fixing parameter, PV, AVP, SELECT, and str conversions"
 	ERR("Error while fixing parameter, PV, AVP, SELECT, and str conversions"
 			" failed\n");
 			" failed\n");
@@ -1312,9 +1331,14 @@ int fixup_var_str_2(void** param, int param_no)
 int fixup_var_int_12(void** param, int param_no)
 int fixup_var_int_12(void** param, int param_no)
 {
 {
 	int ret;
 	int ret;
-	if ((ret = fix_param(FPARAM_PVS, param)) <= 0) return ret;
-	if ((ret = fix_param(FPARAM_AVP, param)) <= 0) return ret;
-	if ((ret = fix_param(FPARAM_SELECT, param)) <= 0) return ret;
+	if (fixup_get_param_type(param) != STRING_RVE_ST) {
+		/* if called with a RVE already converted to string =>
+		   don't try AVP, PVAR or SELECT (to avoid double
+		   deref., e.g.: $foo="$bar"; f($foo) ) */
+		if ((ret = fix_param(FPARAM_PVS, param)) <= 0) return ret;
+		if ((ret = fix_param(FPARAM_AVP, param)) <= 0) return ret;
+		if ((ret = fix_param(FPARAM_SELECT, param)) <= 0) return ret;
+	}
 	if ((ret = fix_param(FPARAM_INT, param)) <= 0) return ret;
 	if ((ret = fix_param(FPARAM_INT, param)) <= 0) return ret;
 	ERR("Error while fixing parameter, PV, AVP, SELECT, and int conversions"
 	ERR("Error while fixing parameter, PV, AVP, SELECT, and int conversions"
 			" failed\n");
 			" failed\n");
@@ -1568,3 +1592,119 @@ int get_regex_fparam(regex_t *dst, struct sip_msg* msg, fparam_t* param)
 	}
 	}
 	return -1;
 	return -1;
 }
 }
+
+
+
+/** generic free fixup function for "pure" fparam type fixups.
+ * @param  param - double pointer to param, as for normal fixup functions.
+ * @param  param_no - parameter number, ignored.
+ * @return 0 on success (always).
+ */
+int fixup_free_fparam_all(void** param, int param_no)
+{
+	fparam_free_restore(param);
+	return 0;
+}
+
+
+
+/** generic free fixup function for "pure"  first parameter fparam type fixups.
+ * @param  param - double pointer to param, as for normal fixup functions.
+ * @param  param_no - parameter number: the function will work only for
+ *                     param_no == 1 (first parameter).
+ * @return 0 on success (always).
+ */
+int fixup_free_fparam_1(void** param, int param_no)
+{
+	if (param_no == 1)
+		fparam_free_restore(param);
+	return 0;
+}
+
+
+
+/** generic free fixup function for "pure"  2nd parameter fparam type fixups.
+ * @param  param - double pointer to param, as for normal fixup functions.
+ * @param  param_no - parameter number: the function will work only for
+ *                     param_no == 2 (2nd parameter).
+ * @return 0 on success (always).
+ */
+int fixup_free_fparam_2(void** param, int param_no)
+{
+	if (param_no == 2)
+		fparam_free_restore(param);
+	return 0;
+}
+
+
+
+/** returns true if a fixup is a fparam_t* one.
+ * Used to automatically detect "pure" fparam fixups that can be used with non
+ * contant RVEs.
+ * @param f - function pointer
+ * @return 1 for fparam fixups, 0 for others.
+ */
+int is_fparam_rve_fixup(fixup_function f)
+{
+	if (f == fixup_var_str_12 ||
+		f == fixup_var_str_1 ||
+		f == fixup_var_str_2 ||
+		f == fixup_var_int_12 ||
+		f == fixup_var_int_1 ||
+		f == fixup_var_int_2 ||
+		f == fixup_int_12 ||
+		f == fixup_int_1 ||
+		f == fixup_int_2 ||
+		f == fixup_str_12 ||
+		f == fixup_str_1 ||
+		f == fixup_str_2 ||
+		f == fixup_regex_12 ||
+		f == fixup_regex_1 ||
+		f == fixup_regex_2
+		)
+		return 1;
+	return 0;
+}
+
+
+
+/** returns the corresponding fixup_free* for various known fixup types.
+ * Used to automatically fill in free_fixup* functions.
+ * @param f - fixup function pointer
+ * @return - free fixup function pointer on success, 0 on failure (unknown
+ *           fixup or no free fixup function).
+ */
+free_fixup_function get_fixup_free(fixup_function f)
+{
+	free_fixup_function ret;
+	/* "pure" fparam, all parameters */
+	if (f == fixup_var_str_12 ||
+		f == fixup_var_int_12 ||
+		f == fixup_int_12 ||
+		f == fixup_str_12 ||
+		f == fixup_regex_12)
+		return fixup_free_fparam_all;
+	
+	/* "pure" fparam, 1st parameter */
+	if (f == fixup_var_str_1 ||
+		f == fixup_var_int_1 ||
+		f == fixup_int_1 ||
+		f == fixup_str_1 ||
+		f == fixup_regex_1)
+		return fixup_free_fparam_1;
+	
+	/* "pure" fparam, 2nd parameters */
+	if (f == fixup_var_str_2 ||
+		f == fixup_var_int_2 ||
+		f == fixup_int_2 ||
+		f == fixup_str_2 ||
+		f == fixup_regex_2)
+		return fixup_free_fparam_2;
+	
+	/* mod_fix.h kamailio style fixups */
+	if ((ret = mod_fix_get_fixup_free(f)) != 0)
+		return ret;
+	
+	/* unknown */
+	return 0;
+}

+ 83 - 4
sr_module.h

@@ -149,6 +149,14 @@ typedef int (*param_func_t)( modparam_t type, void* val);
 #define VAR_PARAM_NO  -128  /* function has variable number of parameters
 #define VAR_PARAM_NO  -128  /* function has variable number of parameters
 							   (see cmd_function_var for the prototype) */
 							   (see cmd_function_var for the prototype) */
 
 
+/* special fixup function flags.
+ * They are kept in the first 2 bits inside the pointer
+ */
+#define FIXUP_F_FPARAM_RVE (unsigned long)1 /* fparam fixup, rve ready */
+
+#define call_fixup(fixup, param, param_no) \
+	((fixup) ? (fixup)(param, param_no) : 0)
+
 /* Macros - used as rank in child_init function */
 /* Macros - used as rank in child_init function */
 #define PROC_MAIN      0  /* Main ser process */
 #define PROC_MAIN      0  /* Main ser process */
 #define PROC_TIMER    -1  /* Timer attendant process */
 #define PROC_TIMER    -1  /* Timer attendant process */
@@ -212,6 +220,20 @@ struct kam_cmd_export_ {
 };
 };
 
 
 
 
+struct sr31_cmd_export_ {
+	char* name;             /* null terminated command name */
+	cmd_function function;  /* pointer to the corresponding function */
+	int param_no;           /* number of parameters used by the function */
+	fixup_function fixup;   /* pointer to the function called to "fix" the
+							   parameters */
+	free_fixup_function free_fixup; /* function called to free the "fixed"
+									   parameters */
+	int flags;              /* Function flags */
+	int fixup_flags;
+	void* module_exports; /* pointer to module structure */
+};
+
+
 /* members situated at the same place in memory in both ser & kamailio
 /* members situated at the same place in memory in both ser & kamailio
    cmd_export */
    cmd_export */
 struct cmd_export_common_ {
 struct cmd_export_common_ {
@@ -277,12 +299,15 @@ typedef struct param_export_ param_export_t;
 typedef struct ser_cmd_export_ ser_cmd_export_t;
 typedef struct ser_cmd_export_ ser_cmd_export_t;
 typedef struct kam_cmd_export_ kam_cmd_export_t;
 typedef struct kam_cmd_export_ kam_cmd_export_t;
 typedef struct cmd_export_common_ cmd_export_common_t;
 typedef struct cmd_export_common_ cmd_export_common_t;
+typedef struct sr31_cmd_export_ sr31_cmd_export_t;
 
 
+#if 0
 union cmd_export_u{
 union cmd_export_u{
 	cmd_export_common_t c; /* common members for everybody */
 	cmd_export_common_t c; /* common members for everybody */
 	ser_cmd_export_t v0;
 	ser_cmd_export_t v0;
 	kam_cmd_export_t v1;
 	kam_cmd_export_t v1;
 };
 };
+#endif
 
 
 
 
 /* ser module exports version */
 /* ser module exports version */
@@ -350,6 +375,47 @@ struct kam_module_exports {
 
 
 
 
 
 
+/** sr/ser 3.1+ module exports version.
+ * Includes ser and kamailio versions, re-arraranged + some extras.
+ * Note: some of the members will be obsoleted and are kept only for
+ * backward compatibility (avoid re-writing all the modules exports
+ * declarations).
+ */
+struct sr31_module_exports {
+	char* name;                     /* null terminated module name */
+	sr31_cmd_export_t* cmds;      /* null terminated array of the exported
+									   commands */
+	param_export_t* params;         /* null terminated array of the exported
+									   module parameters */
+	init_function init_f;           /* Initialization function */
+	response_function response_f;   /* function used for responses,
+									   returns yes or no; can be null */
+	destroy_function destroy_f;     /* function called when the module should
+									   be "destroyed", e.g: on ser exit;
+									   can be null */
+	onbreak_function onbreak_f;
+	child_init_function init_child_f;  /* function called by all processes
+										  after the fork */
+	unsigned int dlflags;           /**< flags for dlopen */
+	/* ser specific exports
+	   (to be obsoleted and replaced by register_...) */
+	rpc_export_t* rpc_methods;      /* null terminated array of exported
+									   rpc methods */
+	/* kamailio specific exports
+	   (to be obsoleted and replaced by register_...) */
+	stat_export_t* stats;			/*!< null terminated array of the exported
+									  module statistics */
+	mi_export_t* mi_cmds;			/*!< null terminated array of the exported
+									  MI functions */
+	pv_export_t* items;				/*!< null terminated array of the exported
+									   module items (pseudo-variables) */
+	proc_export_t* procs;			/*!< null terminated array of the
+									  additional processes required by the
+									  module */
+};
+
+
+
 /* module exports in the same place in memory in both ser & kamailio */
 /* module exports in the same place in memory in both ser & kamailio */
 struct module_exports_common{
 struct module_exports_common{
 	char* name;
 	char* name;
@@ -366,8 +432,8 @@ union module_exports_u {
 struct sr_module{
 struct sr_module{
 	char* path;
 	char* path;
 	void* handle;
 	void* handle;
-	unsigned int mod_interface_ver;
-	union module_exports_u* exports;
+	unsigned int orig_mod_interface_ver;
+	struct sr31_module_exports exports;
 	struct sr_module* next;
 	struct sr_module* next;
 };
 };
 
 
@@ -377,9 +443,8 @@ extern response_function* mod_response_cbks;/* response callback array */
 extern int mod_response_cbk_no;    /* size of reponse callbacks array */
 extern int mod_response_cbk_no;    /* size of reponse callbacks array */
 
 
 int register_builtin_modules(void);
 int register_builtin_modules(void);
-/*int register_module(unsigned , struct module_exports*, char*,  void*);*/
 int load_module(char* path);
 int load_module(char* path);
-union cmd_export_u* find_export_record(char* name, int param_no, int flags,
+sr31_cmd_export_t* find_export_record(char* name, int param_no, int flags,
 										unsigned *ver);
 										unsigned *ver);
 cmd_function find_export(char* name, int param_no, int flags);
 cmd_function find_export(char* name, int param_no, int flags);
 cmd_function find_mod_export(char* mod, char* name, int param_no, int flags);
 cmd_function find_mod_export(char* mod, char* name, int param_no, int flags);
@@ -524,4 +589,18 @@ int get_int_fparam(int* dst, struct sip_msg* msg, fparam_t* param);
 int get_regex_fparam(regex_t *dst, struct sip_msg* msg, fparam_t* param);
 int get_regex_fparam(regex_t *dst, struct sip_msg* msg, fparam_t* param);
 
 
 
 
+int is_fparam_rve_fixup(fixup_function f);
+
+
+/** generic free fixup type function for a fixed fparam.
+ * It will free whatever was allocated during the initial fparam fixup
+ * and restore the original param value.
+ */
+void fparam_free_restore(void** param);
+int fixup_free_fparam_all(void** param, int param_no);
+int fixup_free_fparam_1(void** param, int param_no);
+int fixup_free_fparam_2(void** param, int param_no);
+
+free_fixup_function get_fixup_free(fixup_function f);
+
 #endif /* sr_module_h */
 #endif /* sr_module_h */

+ 14 - 24
ut.c

@@ -5,35 +5,25 @@
  *
  *
  * Copyright (C) 2001-2003 FhG Fokus
  * Copyright (C) 2001-2003 FhG Fokus
  *
  *
- * This file is part of ser, a free SIP server.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
  *
- * ser is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * For a license to use the ser software under conditions
- * other than those described here, or to purchase support for this
- * software, please contact iptel.org by e-mail at the following addresses:
- *    [email protected]
- *
- * ser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License 
- * along with this program; if not, write to the Free Software 
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
  *
  */
  */
 
 
 
 
-/*!
- * \file
- * \brief SIP-router core :: 
- * \ingroup core
- * Module: \ref core
+/** various general purpose/helper functions.
+ * @file
+ * @ingroup core
+ * Module: @ref core
  */
  */
 
 
 #include <sys/types.h>
 #include <sys/types.h>

+ 74 - 30
ut.h

@@ -5,26 +5,17 @@
  *
  *
  * Copyright (C) 2001-2003 FhG Fokus
  * Copyright (C) 2001-2003 FhG Fokus
  *
  *
- * This file is part of ser, a free SIP server.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
  *
- * ser is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * For a license to use the ser software under conditions
- * other than those described here, or to purchase support for this
- * software, please contact iptel.org by e-mail at the following addresses:
- *    [email protected]
- *
- * ser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License 
- * along with this program; if not, write to the Free Software 
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
  *
  * History
  * History
  * ------
  * ------
@@ -42,6 +33,11 @@
  * 2007-05-14 added get_sys_ver() (andrei)
  * 2007-05-14 added get_sys_ver() (andrei)
  * 2007-06-05 added MAX_UVAR_VALUE(), MAX_int(a,b) MIN_int(a,b) (andrei)
  * 2007-06-05 added MAX_UVAR_VALUE(), MAX_int(a,b) MIN_int(a,b) (andrei)
  * 2008-05-21 added ushort2sbuf(), ushort2str() (andrei)
  * 2008-05-21 added ushort2sbuf(), ushort2str() (andrei)
+ * 2009-03-16 added sint2strbuf() and incremented INST2STR_MAX_LEN to account
+ *             for sign (andrei)
+ */
+/** various general purpose/helper functions.
+ * @file
  */
  */
 
 
 
 
@@ -245,7 +241,8 @@ static inline int btostr( char *p,  unsigned char val)
 }
 }
 
 
 
 
-#define INT2STR_MAX_LEN  (19+1+1) /* 2^64~= 16*10^18 => 19+1 digits + \0 */
+#define INT2STR_MAX_LEN  (19+1+1+1) /* 2^64~= 16*10^18 =>
+									   19+1 digits + sign + \0 */
 
 
 /* 
 /* 
  * returns a pointer to a static buffer containing l in asciiz (with base "base") & sets len 
  * returns a pointer to a static buffer containing l in asciiz (with base "base") & sets len 
@@ -292,9 +289,19 @@ static inline char* int2str_base(unsigned int l, int* len, int base)
 
 
 
 
 
 
-/* print int to asciiz in a string buffer
- * - be sure result buffer is at least INT2STR_MAX_LEN in size */
-static inline char* int2strbuf(unsigned int l, char *r, int r_size, int* len)
+/** unsigned long to str conversion using a provided buffer.
+ * Converts/prints an unsigned long to a string. The result buffer must be
+ * provided  and its length must be at least INT2STR_MAX_LEN.
+ * @param l - unsigned long to be converted
+ * @param r - pointer to result buffer
+ * @param r_size - result buffer size, must be at least INT2STR_MAX_LEN.
+ * @param *len - length of the written string, _without_ the terminating 0.
+ * @return  pointer _inside_ r, to the converted string (note: the string
+ *  is written from the end of the buffer and not from the start and hence
+ *  the returned pointer will most likely not be equal to r). In case of error
+ *  it returns 0 (the only error being insufficient provided buffer size).
+ */
+static inline char* int2strbuf(unsigned long l, char *r, int r_size, int* len)
 {
 {
 	int i;
 	int i;
 
 
@@ -318,33 +325,70 @@ static inline char* int2strbuf(unsigned int l, char *r, int r_size, int* len)
 }
 }
 
 
 extern char ut_buf_int2str[INT2STR_MAX_LEN];
 extern char ut_buf_int2str[INT2STR_MAX_LEN];
-/* returns a pointer to a static buffer containing l in asciiz & sets len */
+/** interger(long) to string conversion.
+ * This version uses a static buffer (shared with sint2str()).
+ * WARNING: other function calls might overwrite the static buffer, so
+ * either always save the result immediately or use int2strbuf(...).
+ * @param l - unsigned long to be converted/printed.
+ * @param *len - will be filled with the final length (without the terminating
+ *   0).
+ * @return a pointer to a static buffer containing l in asciiz & sets len.
+ */
 static inline char* int2str(unsigned long l, int* len)
 static inline char* int2str(unsigned long l, int* len)
 {
 {
 	return int2strbuf(l, ut_buf_int2str, INT2STR_MAX_LEN, len);
 	return int2strbuf(l, ut_buf_int2str, INT2STR_MAX_LEN, len);
 }
 }
 
 
-/* Signed INTeger-TO-STRing: convers a long to a string
- * returns a pointer to a static buffer containing l in asciiz & sets len */
-static inline char* sint2str(long l, int* len)
+
+
+/** signed long to str conversion using a provided buffer.
+ * Converts a long to a signed string. The result buffer must be provided
+ * and its length must be at least INT2STR_MAX_LEN.
+ * @param l - long to be converted
+ * @param r - pointer to result buffer
+ * @param r_size - result buffer size, must be at least INT2STR_MAX_LEN.
+ * @param *len - length of the written string, _without_ the terminating 0.
+ * @return  pointer _inside_ r, to the converted string (note: the string
+ *  is written from the end of the buffer and not from the start and hence
+ *  the returned pointer will most likely not be equal to r). In case of error
+ *  it returns 0 (the only error being insufficient provided buffer size).
+ */
+static inline char* sint2strbuf(long l, char* r, int r_size, int* len)
 {
 {
 	int sign;
 	int sign;
 	char *p;
 	char *p;
+	int p_len;
 
 
 	sign = 0;
 	sign = 0;
 	if(l<0) {
 	if(l<0) {
 		sign = 1;
 		sign = 1;
 		l = -l;
 		l = -l;
 	}
 	}
-	p = int2str((unsigned long)l, len);
-	if(sign && *len<(INT2STR_MAX_LEN-1)) {
+	p = int2strbuf((unsigned long)l, r, r_size, &p_len);
+	if(sign && p_len<(r_size-1)) {
 		*(--p) = '-';
 		*(--p) = '-';
-		if (len) (*len)++;
+		p_len++;;
 	}
 	}
+	if (likely(len))
+		*len = p_len;
 	return p;
 	return p;
 }
 }
 
 
 
 
+/** Signed INTeger-TO-STRing: converts a long to a string.
+ * This version uses a static buffer, shared with int2str().
+ * WARNING: other function calls might overwrite the static buffer, so
+ * either always save the result immediately or use sint2strbuf(...).
+ * @param l - long to be converted/printed.
+ * @param *len - will be filled with the final length (without the terminating
+ *   0).
+ * @return a pointer to a static buffer containing l in asciiz & sets len.
+ */
+static inline char* sint2str(long l, int* len)
+{
+	return sint2strbuf(l, ut_buf_int2str, INT2STR_MAX_LEN, len);
+}
+
 
 
 
 
 #define USHORT2SBUF_MAX_LEN  5 /* 65535*/
 #define USHORT2SBUF_MAX_LEN  5 /* 65535*/