Explorar el Código

xmlrpc(s): type autoconversion support

- added a new module parameter ("autoconversion") that when enabled
  will try to autoconvert the xmlrpc  parameters types into what
  the rpc method expects (e.g. if the method expects a string, but
  receives an int, convert the int to string). By default it's
  off.

- support for a new modifier: '.' for rpc_scan. When used it turns
  auto-conversion on for the next input.
  E.g. rpc->scan(".s", &string) will work even if the next input
   is an int (it will convert it to string).
  More complex example:
   while((rpc->scan(c, "*.s", &p)>0)) rpc->add(c, "s", p);
  (work with any number of inputs/parameters, convert all of
   them to string and echo them back in the reply)
Andrei Pelinescu-Onciul hace 16 años
padre
commit
6f38c000cc
Se han modificado 1 ficheros con 256 adiciones y 48 borrados
  1. 256 48
      modules_s/xmlrpc/xmlrpc.c

+ 256 - 48
modules_s/xmlrpc/xmlrpc.c

@@ -277,11 +277,12 @@ static str name_suffix    = STR_STATIC_INIT("</name>");
 static struct garbage {
 static struct garbage {
 	enum {
 	enum {
 		JUNK_XMLCHAR,
 		JUNK_XMLCHAR,
-		JUNK_RPCSTRUCT    /**< This type indicates that the memory block was
+		JUNK_RPCSTRUCT,    /**< This type indicates that the memory block was
 						   * allocated for the RPC structure data type, this
 						   * allocated for the RPC structure data type, this
 						   * type needs to be freed differently as it may
 						   * type needs to be freed differently as it may
 						   * contain more allocated memory blocks
 						   * contain more allocated memory blocks
 						   */
 						   */
+		JUNK_PKGCHAR 	  /** This type indicates a pkg_malloc'ed string */
 	} type;               /**< Type of the memory block */
 	} type;               /**< Type of the memory block */
 	void* ptr;            /**< Pointer to the memory block obtained from
 	void* ptr;            /**< Pointer to the memory block obtained from
 							 pkg_malloc */
 							 pkg_malloc */
@@ -389,6 +390,9 @@ sl_api_t sl;
 #endif
 #endif
 
 
 static int xmlrpc_route_no=DEFAULT_RT;
 static int xmlrpc_route_no=DEFAULT_RT;
+/* if set, try autoconverting to the requested type if possible
+  (e.g. convert 1 to "1" if string is requested) */
+static int autoconvert=0;
 
 
 
 
 /*
 /*
@@ -407,6 +411,7 @@ static cmd_export_t cmds[] = {
 static param_export_t params[] = {
 static param_export_t params[] = {
 	{"enable_introspection", PARAM_INT, &enable_introspection},
 	{"enable_introspection", PARAM_INT, &enable_introspection},
 	{"route", PARAM_STRING, &xmlrpc_route},
 	{"route", PARAM_STRING, &xmlrpc_route},
+	{"autoconversion", PARAM_INT, &autoconvert},
 	{0, 0, 0}
 	{0, 0, 0}
 };
 };
 
 
@@ -690,6 +695,13 @@ static void collect_garbage(void)
 			if (s) pkg_free(s);
 			if (s) pkg_free(s);
 			break;
 			break;
 
 
+		case JUNK_PKGCHAR:
+			if (p->ptr){
+				pkg_free(p->ptr);
+				p->ptr=0;
+			}
+			break;
+
 		default:
 		default:
 			ERR("BUG: Unsupported junk type\n");
 			ERR("BUG: Unsupported junk type\n");
 		}
 		}
@@ -1045,6 +1057,45 @@ static time_t xmlrpc2time(const char* str)
 }
 }
 
 
 
 
+
+/* get_* flags: */
+#define GET_X_AUTOCONV 1
+#define GET_X_NOREPLY 2
+
+/* xml value types */
+enum xmlrpc_val_type{
+	XML_T_STR,
+	XML_T_INT,
+	XML_T_BOOL,
+	XML_T_DATE,
+	XML_T_DOUBLE,
+	XML_T_ERR=-1
+};
+
+
+
+/** Returns the XML-RPC value type.
+ * @return value type (>= on success, XML_T_ERR on error/unknown type)
+ */
+static enum xmlrpc_val_type xml_get_type(xmlNodePtr value)
+{
+	if (!xmlStrcmp(value->name, BAD_CAST "string")){
+		return XML_T_STR;
+	} else if ( !xmlStrcmp(value->name, BAD_CAST "i4") ||
+				!xmlStrcmp(value->name, BAD_CAST "int")) {
+		return XML_T_INT;
+	} else if (!xmlStrcmp(value->name, BAD_CAST "boolean")) {
+		return XML_T_BOOL;
+	} else if (!xmlStrcmp(value->name, BAD_CAST "dateTime.iso8601")) {
+		return XML_T_DATE;
+	}else if (!(xmlStrcmp(value->name, BAD_CAST "double"))){
+		return XML_T_DOUBLE;
+	}
+	return XML_T_ERR;
+}
+
+
+
 /** Converts an XML-RPC encoded parameter into integer if possible.
 /** Converts an XML-RPC encoded parameter into integer if possible.
  *
  *
  * This function receives a pointer to a parameter encoded in XML-RPC format
  * This function receives a pointer to a parameter encoded in XML-RPC format
@@ -1059,48 +1110,84 @@ static time_t xmlrpc2time(const char* str)
  * @param doc A pointer to the XML-RPC request document.  
  * @param doc A pointer to the XML-RPC request document.  
  * @param value A pointer to the element containing the parameter to be 
  * @param value A pointer to the element containing the parameter to be 
  *              converted within the document.
  *              converted within the document.
+ * @param flags : GET_X_AUTOCONV - try autoconverting
+ *                GET_X_NOREPLY - do not reply
+ * @return <0 on error, 0 on success
  */
  */
 static int get_int(int* val, struct xmlrpc_reply* reply, 
 static int get_int(int* val, struct xmlrpc_reply* reply, 
-				   xmlDocPtr doc, xmlNodePtr value)
+				   xmlDocPtr doc, xmlNodePtr value, int flags)
 {
 {
-	int type;
+	enum xmlrpc_val_type type;
+	int ret;
 	xmlNodePtr i4;
 	xmlNodePtr i4;
 	char* val_str;
 	char* val_str;
+	char* end_ptr;
 
 
 	if (!value || xmlStrcmp(value->name, BAD_CAST "value")) {
 	if (!value || xmlStrcmp(value->name, BAD_CAST "value")) {
-		set_fault(reply, 400, "Invalid parameter value");
+		if (!(flags & GET_X_NOREPLY))
+			set_fault(reply, 400, "Invalid parameter value");
 		return -1;
 		return -1;
 	}
 	}
 
 
 	i4 = value->xmlChildrenNode;
 	i4 = value->xmlChildrenNode;
-	if (!xmlStrcmp(i4->name, BAD_CAST "i4") || 
-		!xmlStrcmp(i4->name, BAD_CAST "int")) {
-		type = 1;
-	} else if (!xmlStrcmp(i4->name, BAD_CAST "boolean")) {
-		type = 1;
-	} else if (!xmlStrcmp(i4->name, BAD_CAST "dateTime.iso8601")) {
-		type = 2;
-	} else {
-		set_fault(reply, 400, "Invalid Parameter Type");
+	if (!i4){
+		if (!(flags & GET_X_NOREPLY))
+			set_fault(reply, 400, "Invalid Parameter Type");
 		return -1;
 		return -1;
 	}
 	}
+	type=xml_get_type(i4);
+	switch(type){
+		case XML_T_INT:
+		case XML_T_BOOL:
+		case XML_T_DATE:
+			break;
+		case XML_T_DOUBLE:
+		case XML_T_STR:
+			if (flags & GET_X_AUTOCONV)
+				break;
+		case XML_T_ERR:
+			if (!(flags & GET_X_NOREPLY))
+				set_fault(reply, 400, "Invalid Parameter Type");
+			return -1;
+	}
 
 
 	val_str = (char*)xmlNodeListGetString(doc, i4->xmlChildrenNode, 1);
 	val_str = (char*)xmlNodeListGetString(doc, i4->xmlChildrenNode, 1);
 	if (!val_str) {
 	if (!val_str) {
-		set_fault(reply, 400, "Empty Parameter Value");
+		if (!(flags & GET_X_NOREPLY))
+			set_fault(reply, 400, "Empty Parameter Value");
 		return -1;
 		return -1;
 	}
 	}
-	if (type == 1) {
-		     /* Integer/bool conversion */
-		*val = strtol(val_str, 0, 10);
-	} else {
-		*val = xmlrpc2time(val_str);
+	ret=0;
+	switch(type){
+		case XML_T_INT:
+		case XML_T_BOOL:
+		case XML_T_STR:
+			/* Integer/bool conversion */
+			*val = strtol(val_str, &end_ptr, 10);
+			if (val_str==end_ptr)
+				ret=-1;
+			break;
+		case XML_T_DATE:
+			*val = xmlrpc2time(val_str);
+			break;
+		case XML_T_DOUBLE:
+			*val = (int)strtod(val_str, &end_ptr);
+			if (val_str==end_ptr)
+				ret=-1;
+			break;
+		case XML_T_ERR:
+			*val=0;
+			ret=-1;
+			break;
 	}
 	}
 	xmlFree(val_str);
 	xmlFree(val_str);
-	return 0;
+	if (ret==-1 && !(flags & GET_X_NOREPLY))
+		set_fault(reply, 400, "Invalid Value");
+	return ret;
 }
 }
 
 
 
 
+
 /** Converts an XML-RPC encoded parameter into double if possible.
 /** Converts an XML-RPC encoded parameter into double if possible.
  *
  *
  * This function receives a pointer to a parameter encoded in XML-RPC format
  * This function receives a pointer to a parameter encoded in XML-RPC format
@@ -1114,34 +1201,75 @@ static int get_int(int* val, struct xmlrpc_reply* reply,
  * @param doc A pointer to the XML-RPC request document.
  * @param doc A pointer to the XML-RPC request document.
  * @param value A pointer to the element containing the parameter to be 
  * @param value A pointer to the element containing the parameter to be 
  *              converted within the document.
  *              converted within the document.
+ * @param flags : GET_X_AUTOCONV - try autoconverting
+ *                GET_X_NOREPLY - do not reply
+ * @return <0 on error, 0 on success
  */
  */
 static int get_double(double* val, struct xmlrpc_reply* reply, 
 static int get_double(double* val, struct xmlrpc_reply* reply, 
-					  xmlDocPtr doc, xmlNodePtr value)
+					  xmlDocPtr doc, xmlNodePtr value, int flags)
 {
 {
 	xmlNodePtr dbl;
 	xmlNodePtr dbl;
 	char* val_str;
 	char* val_str;
+	char* end_ptr;
+	enum xmlrpc_val_type type;
+	int ret;
 
 
 	if (!value || xmlStrcmp(value->name, BAD_CAST "value")) {
 	if (!value || xmlStrcmp(value->name, BAD_CAST "value")) {
-		set_fault(reply, 400, "Invalid Parameter Value");
+		if (!(flags & GET_X_NOREPLY))
+			set_fault(reply, 400, "Invalid Parameter Value");
 		return -1;
 		return -1;
 	}
 	}
 
 
 	dbl = value->xmlChildrenNode;
 	dbl = value->xmlChildrenNode;
-	if (!dbl || (xmlStrcmp(dbl->name, BAD_CAST "double") && 
-				 xmlStrcmp(dbl->name, BAD_CAST "int") && 
-				 xmlStrcmp(dbl->name, BAD_CAST "int4"))) {
-		set_fault(reply, 400, "Invalid Parameter Type");
+	if (!dbl){
+		if (!(flags & GET_X_NOREPLY))
+			set_fault(reply, 400, "Invalid Parameter Type");
 		return -1;
 		return -1;
 	}
 	}
+	type=xml_get_type(dbl);
+	switch(type){
+		case XML_T_DOUBLE:
+		case XML_T_INT:
+			break;
+		case XML_T_BOOL:
+		case XML_T_DATE:
+		case XML_T_STR:
+			if (flags & GET_X_AUTOCONV)
+				break;
+		case XML_T_ERR:
+			if (!(flags & GET_X_NOREPLY))
+				set_fault(reply, 400, "Invalid Parameter Type");
+			return -1;
+	}
 
 
 	val_str = (char*)xmlNodeListGetString(doc, dbl->xmlChildrenNode, 1);
 	val_str = (char*)xmlNodeListGetString(doc, dbl->xmlChildrenNode, 1);
 	if (!val_str) {
 	if (!val_str) {
-		set_fault(reply, 400, "Empty Double Parameter");
+		if (!(flags & GET_X_NOREPLY))
+			set_fault(reply, 400, "Empty Double Parameter");
 		return -1;
 		return -1;
 	}
 	}
-	*val = strtod(val_str, 0);
+	ret=0;
+	switch(type){
+		case XML_T_DOUBLE:
+		case XML_T_INT:
+		case XML_T_BOOL:
+		case XML_T_STR:
+			*val = strtod(val_str, &end_ptr);
+			if (val_str==end_ptr)
+				ret=-1;
+			break;
+		case XML_T_DATE:
+			*val = (double)xmlrpc2time(val_str);
+			break;
+		case XML_T_ERR:
+			*val=0;
+			ret=-1;
+			break;
+	}
 	xmlFree(val_str);
 	xmlFree(val_str);
-	return 0;
+	if (ret==-1 && !(flags & GET_X_NOREPLY))
+		set_fault(reply, 400, "Invalid Value");
+	return ret;
 }
 }
 
 
 
 
@@ -1154,36 +1282,103 @@ static int get_double(double* val, struct xmlrpc_reply* reply,
  * @param doc A pointer to the XML-RPC request document.
  * @param doc A pointer to the XML-RPC request document.
  * @param value A pointer to the element containing the parameter to be 
  * @param value A pointer to the element containing the parameter to be 
  *              converted within the document.
  *              converted within the document.
+ * @param flags : GET_X_AUTOCONV - try autoconverting
+ *                GET_X_NOREPLY - do not reply
+ * @return <0 on error, 0 on success
  */
  */
 static int get_string(char** val, struct xmlrpc_reply* reply, 
 static int get_string(char** val, struct xmlrpc_reply* reply, 
-					  xmlDocPtr doc, xmlNodePtr value)
+					  xmlDocPtr doc, xmlNodePtr value, int flags)
 {
 {
 	static char* null_str = "";
 	static char* null_str = "";
 	xmlNodePtr dbl;
 	xmlNodePtr dbl;
 	char* val_str;
 	char* val_str;
+	char* end_ptr;
+	char* s;
+	char* p;
+	int i;
+	int len;
+	enum xmlrpc_val_type type;
+	int ret;
 
 
 	if (!value || xmlStrcmp(value->name, BAD_CAST "value")) {
 	if (!value || xmlStrcmp(value->name, BAD_CAST "value")) {
-		set_fault(reply, 400, "Invalid Parameter Value");
+		if (!(flags & GET_X_NOREPLY))
+			set_fault(reply, 400, "Invalid Parameter Value");
 		return -1;
 		return -1;
 	}
 	}
 
 
 	dbl = value->xmlChildrenNode;
 	dbl = value->xmlChildrenNode;
-	if (!dbl || xmlStrcmp(dbl->name, BAD_CAST "string")) {
-		set_fault(reply, 400, "Invalid Parameter Type");
+	if (!dbl){
+		if (!(flags & GET_X_NOREPLY))
+			set_fault(reply, 400, "Invalid Parameter Type");
 		return -1;
 		return -1;
 	}
 	}
-
+	type=xml_get_type(dbl);
+	switch(type){
+		case XML_T_STR:
+			break;
+		case XML_T_INT:
+		case XML_T_BOOL:
+		case XML_T_DATE:
+		case XML_T_DOUBLE:
+			if (flags & GET_X_AUTOCONV)
+				break;
+		case XML_T_ERR:
+			if (!(flags & GET_X_NOREPLY))
+				set_fault(reply, 400, "Invalid Parameter Type");
+			return -1;
+	}
 	val_str = (char*)xmlNodeListGetString(doc, dbl->xmlChildrenNode, 1);
 	val_str = (char*)xmlNodeListGetString(doc, dbl->xmlChildrenNode, 1);
 	if (!val_str) {
 	if (!val_str) {
-		*val = null_str;
-		return 0;
+		if (type==XML_T_STR){
+			*val = null_str;
+			return 0;
+		}else{
+			if (!(flags & GET_X_NOREPLY))
+				set_fault(reply, 400, "Empty Parameter Value");
+			return -1;
+		}
 	}
 	}
-
-	if (add_garbage(JUNK_XMLCHAR, val_str, reply) < 0) return -1;
-	*val = val_str;
-	return 0;
+	ret=0;
+	switch(type){
+		case XML_T_STR:
+		case XML_T_DATE:  /* no special conversion */
+		case XML_T_DOUBLE: /* no special conversion */
+			if (add_garbage(JUNK_XMLCHAR, val_str, reply) < 0){
+				xmlFree(val_str);
+				return -1;
+			}
+			*val = val_str;
+			break;
+		case XML_T_INT:
+		case XML_T_BOOL:
+			/* convert str to int an back to str */
+			i = strtol(val_str, &end_ptr, 10);
+			if (val_str==end_ptr){
+				ret=-1;
+			}else{
+				s=sint2str(i, &len);
+				p=pkg_malloc(len+1);
+				if (p && add_garbage(JUNK_PKGCHAR, p, reply) == 0){
+					memcpy(p, s, len);
+					p[len]=0;
+					*val=p;
+				}else{
+					ret=-1;
+					if (p) pkg_free(p);
+				}
+			}
+			xmlFree(val_str);
+			break;
+		case XML_T_ERR:
+			xmlFree(val_str);
+			ret=-1;
+			break;
+	}
+	return ret;
 }
 }
 
 
+
+
 /** Implementation of rpc->scan function required by the management API in
 /** Implementation of rpc->scan function required by the management API in
  * SER.
  * SER.
  *
  *
@@ -1206,6 +1401,7 @@ static int rpc_scan(rpc_ctx_t* ctx, char* fmt, ...)
 	struct xmlrpc_reply* reply;
 	struct xmlrpc_reply* reply;
 	struct rpc_struct* p;
 	struct rpc_struct* p;
 	int modifiers;
 	int modifiers;
+	int f;
 	va_list ap;
 	va_list ap;
 
 
 	reply = &ctx->reply;
 	reply = &ctx->reply;
@@ -1213,6 +1409,7 @@ static int rpc_scan(rpc_ctx_t* ctx, char* fmt, ...)
 	va_start(ap, fmt);
 	va_start(ap, fmt);
 	modifiers=0;
 	modifiers=0;
 	read = 0;
 	read = 0;
+	f=autoconvert?GET_X_AUTOCONV:0;
 	while(*fmt) {
 	while(*fmt) {
 		if (!ctx->act_param) goto error;
 		if (!ctx->act_param) goto error;
 		value = ctx->act_param->xmlChildrenNode;
 		value = ctx->act_param->xmlChildrenNode;
@@ -1223,28 +1420,35 @@ static int rpc_scan(rpc_ctx_t* ctx, char* fmt, ...)
 			read++;
 			read++;
 			fmt++;
 			fmt++;
 			continue; /* do not advance ctx->act-param */
 			continue; /* do not advance ctx->act-param */
+		case '.': /* autoconvert */
+			modifiers++;
+			read++;
+			fmt++;
+			f|=GET_X_AUTOCONV;
+			continue; /* do not advance ctx->act-param */
 		case 'b': /* Bool */
 		case 'b': /* Bool */
 		case 't': /* Date and time */
 		case 't': /* Date and time */
 		case 'd': /* Integer */
 		case 'd': /* Integer */
 			int_ptr = va_arg(ap, int*);
 			int_ptr = va_arg(ap, int*);
-			if (get_int(int_ptr, reply, ctx->doc, value) < 0) goto error;
+			if (get_int(int_ptr, reply, ctx->doc, value, f) < 0) goto error;
 			break;
 			break;
 			
 			
 		case 'f': /* double */
 		case 'f': /* double */
 			double_ptr = va_arg(ap, double*);
 			double_ptr = va_arg(ap, double*);
-			if (get_double(double_ptr, reply, ctx->doc, value) < 0) {
+			if (get_double(double_ptr, reply, ctx->doc, value, f) < 0) {
 				goto error;
 				goto error;
 			}
 			}
 			break;
 			break;
 
 
 		case 's': /* zero terminated string */
 		case 's': /* zero terminated string */
 			char_ptr = va_arg(ap, char**);
 			char_ptr = va_arg(ap, char**);
-			if (get_string(char_ptr, reply, ctx->doc, value) < 0) goto error;
+			if (get_string(char_ptr, reply, ctx->doc, value, f) < 0)
+				goto error;
 			break;
 			break;
 
 
 		case 'S': /* str structure */
 		case 'S': /* str structure */
 			str_ptr = va_arg(ap, str*);
 			str_ptr = va_arg(ap, str*);
-			if (get_string(&str_ptr->s, reply, ctx->doc, value) < 0) {
+			if (get_string(&str_ptr->s, reply, ctx->doc, value, f) < 0) {
 				goto error;
 				goto error;
 			}
 			}
 			str_ptr->len = strlen(str_ptr->s);
 			str_ptr->len = strlen(str_ptr->s);
@@ -1265,6 +1469,8 @@ static int rpc_scan(rpc_ctx_t* ctx, char* fmt, ...)
 			goto error;
 			goto error;
 		}
 		}
 		ctx->act_param = ctx->act_param->next;
 		ctx->act_param = ctx->act_param->next;
+		/* clear autoconv if not globally on */
+		f=autoconvert?GET_X_AUTOCONV:(f&~GET_X_AUTOCONV);
 		read++;
 		read++;
 		fmt++;
 		fmt++;
 	}
 	}
@@ -1525,22 +1731,24 @@ static int rpc_struct_scan(struct rpc_struct* s, char* fmt, ...)
 		case 't': /* Date and time */
 		case 't': /* Date and time */
 		case 'd': /* Integer */
 		case 'd': /* Integer */
 			int_ptr = va_arg(ap, int*);
 			int_ptr = va_arg(ap, int*);
-			if (get_int(int_ptr, reply, s->doc, value) < 0) goto error;
+			if (get_int(int_ptr, reply, s->doc, value, 0) < 0) goto error;
 			break;
 			break;
 
 
 		case 'f': /* double */
 		case 'f': /* double */
 			double_ptr = va_arg(ap, double*);
 			double_ptr = va_arg(ap, double*);
-			if (get_double(double_ptr, reply, s->doc, value) < 0) goto error;
+			if (get_double(double_ptr, reply, s->doc, value, 0) < 0)
+				goto error;
 			break;
 			break;
 
 
 		case 's': /* zero terminated string */
 		case 's': /* zero terminated string */
 			char_ptr = va_arg(ap, char**);
 			char_ptr = va_arg(ap, char**);
-			if (get_string(char_ptr, reply, s->doc, value) < 0) goto error;
+			if (get_string(char_ptr, reply, s->doc, value, 0) < 0) goto error;
 			break;
 			break;
 
 
 		case 'S': /* str structure */
 		case 'S': /* str structure */
 			str_ptr = va_arg(ap, str*);
 			str_ptr = va_arg(ap, str*);
-			if (get_string(&str_ptr->s, reply, s->doc, value) < 0) goto error;
+			if (get_string(&str_ptr->s, reply, s->doc, value, 0) < 0)
+				goto error;
 			str_ptr->len = strlen(str_ptr->s);
 			str_ptr->len = strlen(str_ptr->s);
 			break;
 			break;
 		default:
 		default: