|
@@ -0,0 +1,1484 @@
|
|
|
+/**
|
|
|
+ * Copyright (C) 2015 Bicom Systems Ltd, (bicomsystems.com)
|
|
|
+ *
|
|
|
+ * Author: Seudin Kasumovic ([email protected])
|
|
|
+ *
|
|
|
+ * This file is part of Kamailio, a free SIP server.
|
|
|
+ *
|
|
|
+ * Kamailio is free software; you can redistribute it and/or modify
|
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
|
+ * (at your option) any later version
|
|
|
+ *
|
|
|
+ * Kamailio is distributed in the hope that it will be useful,
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+ * GNU General Public License for more details.
|
|
|
+ *
|
|
|
+ * You should have received a copy of the GNU General Public License
|
|
|
+ * along with this program; if not, write to the Free Software
|
|
|
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+#include <stdarg.h>
|
|
|
+#include <stdlib.h>
|
|
|
+
|
|
|
+#include "../../mem/mem.h"
|
|
|
+#include "../../dprint.h"
|
|
|
+
|
|
|
+#include "handle_rpc.h"
|
|
|
+#include "mod_erlang.h"
|
|
|
+
|
|
|
+/* create empty recycle bin */
|
|
|
+static struct erl_rpc_garbage *recycle_bin = 0;
|
|
|
+
|
|
|
+static int get_int(int *int_ptr,erl_rpc_ctx_t *ctx, int reads, int autoconvert);
|
|
|
+static int get_double(double *double_prt,erl_rpc_ctx_t *ctx, int reads, int autoconvert);
|
|
|
+static int get_str(str *str_ptr, erl_rpc_ctx_t *ctx, int reads, int autoconvert);
|
|
|
+static int find_member(erl_rpc_ctx_t *ctx, int arity, const char* member_name);
|
|
|
+static int add_to_recycle_bin(int type, void* ptr, erl_rpc_ctx_t *ctx);
|
|
|
+
|
|
|
+erl_rpc_param_t *erl_new_param(erl_rpc_ctx_t *ctx);
|
|
|
+void erl_rpc_append_param(erl_rpc_ctx_t *ctx, erl_rpc_param_t *param);
|
|
|
+
|
|
|
+/*
|
|
|
+ * RPC holder
|
|
|
+ */
|
|
|
+rpc_t erl_rpc_func_param;
|
|
|
+
|
|
|
+/*
|
|
|
+ * Function returns always success - we uses EPMD for transport
|
|
|
+ */
|
|
|
+int erl_rpc_send(erl_rpc_ctx_t *ctx, int depth)
|
|
|
+{
|
|
|
+ if (ctx->response_sent) return 0;
|
|
|
+ ctx->response_sent = 1;
|
|
|
+ erl_rpc_ctx_t *handler;
|
|
|
+ erl_rpc_param_t *fault = *(ctx->fault_p);
|
|
|
+
|
|
|
+ if (fault)
|
|
|
+ {
|
|
|
+ LM_ERR("fault: %d %.*s\n",fault->type, STR_FMT(&fault->value.S));
|
|
|
+ /* restore clear point */
|
|
|
+ ctx->response->index = ctx->response_index;
|
|
|
+
|
|
|
+ /* {error,{struct,[ {"code", 400}, {"error","Error message"}]}}*/
|
|
|
+ if (ei_x_encode_tuple_header(ctx->response,1)) goto error; /* {error,{_,_}} */
|
|
|
+ if (rpc_reply_with_struct && ei_x_encode_atom(ctx->response,"struct")) goto error; /* {error,{struct,_}} */
|
|
|
+ if (ei_x_encode_list_header(ctx->response,2)) goto error; /* {error,{struct,[_,_]}} */
|
|
|
+ if (ei_x_encode_tuple_header(ctx->response,2)) goto error; /* {error,{struct,[{_,_},_]}} */
|
|
|
+ if (ei_x_encode_atom(ctx->response,"code")) goto error; /* {error,{struct,[{code,_},_]}} */
|
|
|
+ if (ei_x_encode_long(ctx->response,fault->type)) goto error;/* {error,{struct,[{code,400},_]}} */
|
|
|
+ if (ei_x_encode_tuple_header(ctx->response,2)) goto error; /* {error,{struct,[{code,400},{_,_}]}} */
|
|
|
+ if (ei_x_encode_binary(ctx->response,"error",sizeof("error")-1)) goto error; /* {error,{struct,[{code,400},{<<"error">>,_}]}} */
|
|
|
+ if (ei_x_encode_binary(ctx->response,(void*)fault->value.S.s,fault->value.S.len)) /* {error,{struct,[{code,400},{<<"error">>,<<Msg>>}]}} */
|
|
|
+ goto error;
|
|
|
+ if (ei_x_encode_empty_list(ctx->response)) goto error;
|
|
|
+ }
|
|
|
+ else if (ctx->reply_params)
|
|
|
+ {
|
|
|
+ while(ctx->reply_params)
|
|
|
+ {
|
|
|
+ if (ctx->reply_params->member_name)
|
|
|
+ {
|
|
|
+ /* {"member_name", _} */
|
|
|
+ if (ei_x_encode_tuple_header(ctx->response,2)) goto error;
|
|
|
+ if (ei_x_encode_binary(ctx->response,ctx->reply_params->member_name, strlen(ctx->reply_params->member_name)))
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ /* {"member_name", MemberValue} */
|
|
|
+ switch (ctx->reply_params->type) {
|
|
|
+ case ERL_INTEGER_EXT:
|
|
|
+ if(ei_x_encode_long(ctx->response,ctx->reply_params->value.n)) goto error;
|
|
|
+ break;
|
|
|
+ case ERL_FLOAT_EXT:
|
|
|
+ if(ei_x_encode_double(ctx->response,ctx->reply_params->value.d)) goto error;
|
|
|
+ break;
|
|
|
+ case ERL_STRING_EXT:
|
|
|
+ if(ei_x_encode_binary(ctx->response,ctx->reply_params->value.S.s,ctx->reply_params->value.S.len)) goto error;
|
|
|
+ break;
|
|
|
+ case ERL_SMALL_TUPLE_EXT: /* add as {struct,list(no_params)} */
|
|
|
+ handler = (erl_rpc_ctx_t*)ctx->reply_params->value.handler;
|
|
|
+ if (ei_x_encode_tuple_header(ctx->response,1)) goto error;
|
|
|
+ if (rpc_reply_with_struct && ei_x_encode_atom(ctx->response,"struct")) goto error;
|
|
|
+ if (ei_x_encode_list_header(ctx->response,handler->no_params)) goto error;
|
|
|
+ if (erl_rpc_send(handler, depth++)) goto error;
|
|
|
+ if (ei_x_encode_empty_list(ctx->response)) goto error;
|
|
|
+ break;
|
|
|
+ case ERL_LIST_EXT: /* add as [list(no_params)] */
|
|
|
+ handler = (erl_rpc_ctx_t*)ctx->reply_params->value.handler;
|
|
|
+ if (ei_x_encode_list_header(ctx->response,handler->no_params)) goto error;
|
|
|
+ if (erl_rpc_send(handler, depth++)) goto error;
|
|
|
+ if (handler->no_params)
|
|
|
+ if (ei_x_encode_empty_list(ctx->response)) goto error;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ LM_ERR("Unknown type '%c' for encoding RPC reply\n",ctx->reply_params->type);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ ctx->reply_params=ctx->reply_params->next;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (!depth)
|
|
|
+ {
|
|
|
+ /* restore start point */
|
|
|
+ LM_WARN("encode empty response -> ok");
|
|
|
+ ctx->response->index = ctx->response_index;
|
|
|
+ if (ei_x_encode_atom(ctx->response,"ok")) goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+error:
|
|
|
+ LM_ERR("error while encoding response\n");
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+void erl_rpc_fault(erl_rpc_ctx_t* ctx, int code, char* fmt, ...)
|
|
|
+{
|
|
|
+ static char buf[FAULT_BUF_LEN];
|
|
|
+ erl_rpc_param_t *fault = *(ctx->fault_p);
|
|
|
+ int len;
|
|
|
+
|
|
|
+ va_list ap;
|
|
|
+
|
|
|
+ if (fault) return;
|
|
|
+
|
|
|
+ va_start(ap, fmt);
|
|
|
+ len = vsnprintf(buf, FAULT_BUF_LEN, fmt, ap);
|
|
|
+ va_end(ap);
|
|
|
+
|
|
|
+ fault=(erl_rpc_param_t*)pkg_malloc(sizeof(erl_rpc_param_t));
|
|
|
+
|
|
|
+ if (fault == 0)
|
|
|
+ {
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR,(void*)fault,ctx))
|
|
|
+ {
|
|
|
+ pkg_free(fault);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ fault->type = code;
|
|
|
+ fault->value.S.s = buf;
|
|
|
+ fault->value.S.len = len;
|
|
|
+ ctx->fault = fault;
|
|
|
+}
|
|
|
+
|
|
|
+int erl_rpc_add(erl_rpc_ctx_t* ctx, char* fmt, ...)
|
|
|
+{
|
|
|
+ void **void_ptr;
|
|
|
+ int int_ptr;
|
|
|
+ double double_ptr;
|
|
|
+ char *char_ptr;
|
|
|
+ str *str_ptr;
|
|
|
+ erl_rpc_ctx_t *handler;
|
|
|
+ erl_rpc_param_t *param;
|
|
|
+
|
|
|
+ int reads=0;
|
|
|
+
|
|
|
+ va_list ap;
|
|
|
+
|
|
|
+ va_start(ap,fmt);
|
|
|
+
|
|
|
+ while(*fmt)
|
|
|
+ {
|
|
|
+ if ((param = erl_new_param(ctx))==0)
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch(*fmt)
|
|
|
+ {
|
|
|
+ case 'b': /* Bool */
|
|
|
+ case 't': /* Date and time */
|
|
|
+ case 'd': /* Integer */
|
|
|
+ int_ptr = va_arg(ap, int);
|
|
|
+ param->type = ERL_INTEGER_EXT;
|
|
|
+ param->value.n = int_ptr;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'f': /* double */
|
|
|
+ double_ptr = va_arg(ap, double);
|
|
|
+ param->type = ERL_FLOAT_EXT;
|
|
|
+ param->value.d = double_ptr;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'S': /* str structure */
|
|
|
+ str_ptr = va_arg(ap, str*);
|
|
|
+
|
|
|
+ param->type = ERL_STRING_EXT;
|
|
|
+ param->value.S = *str_ptr;
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 's':/* zero terminated string */
|
|
|
+
|
|
|
+ char_ptr = va_arg(ap, char *);
|
|
|
+
|
|
|
+ param->type = ERL_STRING_EXT;
|
|
|
+ param->value.S.len = strlen(char_ptr);
|
|
|
+
|
|
|
+ param->value.S.s = (char*)pkg_malloc(param->value.S.len);
|
|
|
+
|
|
|
+ if (!param->value.S.s)
|
|
|
+ {
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR, param->value.S.s, ctx))
|
|
|
+ {
|
|
|
+ pkg_free(param->value.S.s);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(param->value.S.s,char_ptr,param->value.S.len);
|
|
|
+
|
|
|
+ break;
|
|
|
+ case '{':
|
|
|
+ void_ptr = va_arg(ap,void**);
|
|
|
+
|
|
|
+ handler = (erl_rpc_ctx_t*)pkg_malloc(sizeof(erl_rpc_ctx_t));
|
|
|
+
|
|
|
+ if (!handler)
|
|
|
+ {
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR,(void*)handler,ctx))
|
|
|
+ {
|
|
|
+ pkg_free(handler);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ *handler = *ctx;
|
|
|
+ handler->no_params = 0;
|
|
|
+ handler->reply_params=0;
|
|
|
+ handler->tail = 0;
|
|
|
+
|
|
|
+ /* go where we stopped */
|
|
|
+ *(erl_rpc_ctx_t**)void_ptr = handler;
|
|
|
+
|
|
|
+ param->type = ERL_SMALL_TUPLE_EXT;
|
|
|
+ param->value.handler = (void*)handler;
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '[':
|
|
|
+ void_ptr = va_arg(ap,void**);
|
|
|
+
|
|
|
+ handler = (erl_rpc_ctx_t*)pkg_malloc(sizeof(erl_rpc_ctx_t));
|
|
|
+
|
|
|
+ if (!handler)
|
|
|
+ {
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR,(void*)handler,ctx))
|
|
|
+ {
|
|
|
+ pkg_free(handler);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ *handler = *ctx;
|
|
|
+ handler->no_params = 0;
|
|
|
+ handler->reply_params=0;
|
|
|
+ handler->tail = 0;
|
|
|
+
|
|
|
+ /* go where we stopped */
|
|
|
+ *(erl_rpc_ctx_t**)void_ptr = handler;
|
|
|
+
|
|
|
+ param->type = ERL_LIST_EXT;
|
|
|
+ param->value.handler = (void*)handler;
|
|
|
+
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ LM_ERR("Invalid type '%c' in formatting string\n", *fmt);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ erl_rpc_append_param(ctx,param);
|
|
|
+
|
|
|
+ reads++;
|
|
|
+ fmt++;
|
|
|
+ }
|
|
|
+ va_end(ap);
|
|
|
+ return reads;
|
|
|
+
|
|
|
+error:
|
|
|
+ LM_ERR("Failed to encode parameter #%d into response.\n",reads);
|
|
|
+ va_end(ap);
|
|
|
+ return -reads;
|
|
|
+}
|
|
|
+
|
|
|
+int erl_rpc_scan(erl_rpc_ctx_t* ctx, char* fmt, ...)
|
|
|
+{
|
|
|
+ int* int_ptr;
|
|
|
+ char** char_ptr;
|
|
|
+ str* str_ptr;
|
|
|
+ double* double_ptr;
|
|
|
+ void** void_ptr;
|
|
|
+ str s; /* helper str */
|
|
|
+
|
|
|
+ int reads = 0;
|
|
|
+ int modifiers = 0;
|
|
|
+ int autoconv = 0;
|
|
|
+
|
|
|
+ int type,size;
|
|
|
+ erl_rpc_ctx_t *handler;
|
|
|
+
|
|
|
+ va_list ap;
|
|
|
+
|
|
|
+ va_start(ap,fmt);
|
|
|
+
|
|
|
+ while(*fmt && ctx->size)
|
|
|
+ {
|
|
|
+ /* optional and we at the end of decoding params */
|
|
|
+ if (ctx->optional && !ctx->size)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ei_get_type(ctx->request->buff,&ctx->request_index,&type,&size))
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Can't determine data type, for parameter #%d",reads);
|
|
|
+ LM_ERR("Can't determine data type, for parameter #%d",reads);
|
|
|
+
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch(*fmt)
|
|
|
+ {
|
|
|
+ case '*': /* start of optional parameters */
|
|
|
+ modifiers++;
|
|
|
+ ctx->optional = 1;
|
|
|
+ reads++;
|
|
|
+ fmt++;
|
|
|
+ continue;
|
|
|
+ case '.': /* autoconvert */
|
|
|
+ modifiers++;
|
|
|
+ autoconv = 1;
|
|
|
+ reads++;
|
|
|
+ fmt++;
|
|
|
+ continue;
|
|
|
+ case 'b': /* Bool */
|
|
|
+ case 't': /* Date and time */
|
|
|
+ case 'd': /* Integer */
|
|
|
+ int_ptr = va_arg(ap, int*);
|
|
|
+
|
|
|
+ if (get_int(int_ptr,ctx,reads,autoconv))
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ case 'f': /* double */
|
|
|
+ double_ptr = va_arg(ap, double*);
|
|
|
+
|
|
|
+ if (get_double(double_ptr,ctx,reads,autoconv))
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ case 'S': /* str structure */
|
|
|
+
|
|
|
+ str_ptr = va_arg(ap, str*);
|
|
|
+
|
|
|
+ if (get_str(str_ptr,ctx,reads,autoconv))
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ case 's':/* zero terminated string */
|
|
|
+
|
|
|
+ char_ptr = va_arg(ap, char **);
|
|
|
+ if (get_str(&s,ctx,reads,autoconv))
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ *char_ptr = s.s;
|
|
|
+
|
|
|
+ break;
|
|
|
+ case '{':
|
|
|
+ void_ptr = va_arg(ap,void**);
|
|
|
+
|
|
|
+ if (type!=ERL_SMALL_TUPLE_EXT && type!=ERL_LARGE_TUPLE_EXT)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Bad type of parameter #%d (t=%c).",reads,type);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ handler = (erl_rpc_ctx_t*)pkg_malloc(sizeof(erl_rpc_ctx_t));
|
|
|
+
|
|
|
+ if (!handler)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,500, "Internal Server Error (No memory left)");
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ *handler = *ctx; /* copy state */
|
|
|
+ handler->optional = 0;
|
|
|
+ handler->no_params = 0;
|
|
|
+ handler->size = size; /* size of tuple */
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR,handler,ctx))
|
|
|
+ {
|
|
|
+ pkg_free(handler);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* skip element */
|
|
|
+ if (ei_skip_term(ctx->request->buff,&ctx->request_index))
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* go where we stopped */
|
|
|
+ *(erl_rpc_ctx_t**)void_ptr = handler;
|
|
|
+
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ LM_ERR("Invalid parameter type in formatting string: %c\n", *fmt);
|
|
|
+ erl_rpc_fault(ctx, 500, "Server Internal Error (Invalid Formatting String)");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ autoconv = 0; /* reset autovoncersion for next parameter */
|
|
|
+ reads++;
|
|
|
+ fmt++;
|
|
|
+ ctx->size--;
|
|
|
+ }
|
|
|
+ va_end(ap);
|
|
|
+ return reads-modifiers;
|
|
|
+
|
|
|
+error:
|
|
|
+ va_end(ap);
|
|
|
+ return -(reads-modifiers);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+int erl_rpc_struct_scan(erl_rpc_ctx_t* ctx, char* fmt, ...)
|
|
|
+{
|
|
|
+ int* int_ptr;
|
|
|
+ char** char_ptr;
|
|
|
+ str* str_ptr;
|
|
|
+ double* double_ptr;
|
|
|
+ char* member_name;
|
|
|
+ str s; /* helper str */
|
|
|
+
|
|
|
+ int reads = 0;
|
|
|
+ int modifiers = 0;
|
|
|
+ int index;
|
|
|
+ int autoconv = 0;
|
|
|
+
|
|
|
+ int arity;
|
|
|
+
|
|
|
+ va_list ap;
|
|
|
+
|
|
|
+ /* save index */
|
|
|
+ index = ctx->request_index;
|
|
|
+
|
|
|
+ if(ei_decode_tuple_header(ctx->request->buff,&ctx->request_index, &arity))
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Bad tuple");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ va_start(ap,fmt);
|
|
|
+
|
|
|
+ while(*fmt)
|
|
|
+ {
|
|
|
+ member_name = va_arg(ap, char*);
|
|
|
+
|
|
|
+ if (find_member(ctx,arity,member_name))
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch(*fmt)
|
|
|
+ {
|
|
|
+ case 'b': /* Bool */
|
|
|
+ case 't': /* Date and time */
|
|
|
+ case 'd': /* Integer */
|
|
|
+ int_ptr = va_arg(ap, int*);
|
|
|
+
|
|
|
+ if (get_int(int_ptr,ctx,reads,autoconv))
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ case 'f': /* double */
|
|
|
+ double_ptr = va_arg(ap, double*);
|
|
|
+
|
|
|
+ if (get_double(double_ptr,ctx,reads,autoconv))
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ case 'S': /* str structure */
|
|
|
+
|
|
|
+ str_ptr = va_arg(ap, str*);
|
|
|
+
|
|
|
+ if (get_str(str_ptr,ctx,reads,autoconv))
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ case 's':/* zero terminated string */
|
|
|
+
|
|
|
+ char_ptr = va_arg(ap,char**);
|
|
|
+
|
|
|
+ if (get_str(&s,ctx,reads,autoconv))
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ *char_ptr = s.s;
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ LM_ERR("Invalid parameter type in formatting string: %c\n", *fmt);
|
|
|
+ erl_rpc_fault(ctx, 500, "Server Internal Error (Invalid Formatting String)");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ reads++;
|
|
|
+ fmt++;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* restore index */
|
|
|
+ ctx->request_index = index;
|
|
|
+
|
|
|
+ va_end(ap);
|
|
|
+ return reads-modifiers;
|
|
|
+
|
|
|
+error:
|
|
|
+ va_end(ap);
|
|
|
+ return -(reads-modifiers);
|
|
|
+}
|
|
|
+
|
|
|
+#define RPC_BUF_SIZE 1024
|
|
|
+
|
|
|
+/*
|
|
|
+ * adds formated string into RPC response buffer as Erlang string/list
|
|
|
+ */
|
|
|
+int erl_rpc_printf(erl_rpc_ctx_t* ctx, char* fmt, ...)
|
|
|
+{
|
|
|
+ int n, buff_size;
|
|
|
+ char *buff = 0;
|
|
|
+ va_list ap;
|
|
|
+ erl_rpc_param_t *param;
|
|
|
+
|
|
|
+ buff = (char*)pkg_malloc(RPC_BUF_SIZE);
|
|
|
+ if (!buff) {
|
|
|
+ erl_rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
|
|
|
+ ERR("No memory left\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ buff_size = RPC_BUF_SIZE;
|
|
|
+
|
|
|
+ while(1)
|
|
|
+ {
|
|
|
+ /* Try to print in the allocated space. */
|
|
|
+ va_start(ap, fmt);
|
|
|
+ n = vsnprintf(buff, buff_size, fmt, ap);
|
|
|
+ va_end(ap);
|
|
|
+ /* If that worked, return the string. */
|
|
|
+ if (n > -1 && n < buff_size)
|
|
|
+ {
|
|
|
+ if(add_to_recycle_bin(JUNK_PKGCHAR,(void*)buff,ctx))
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ else if ((param = erl_new_param(ctx)))
|
|
|
+ {
|
|
|
+ param->type = ERL_STRING_EXT;
|
|
|
+ param->value.S.s = buff;
|
|
|
+ param->value.S.len = n;
|
|
|
+ erl_rpc_append_param(ctx,param);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Else try again with more space. */
|
|
|
+ if (n > -1)
|
|
|
+ { /* glibc 2.1 */
|
|
|
+ buff_size = n + 1; /* precisely what is needed */
|
|
|
+ }
|
|
|
+ else
|
|
|
+ { /* glibc 2.0 */
|
|
|
+ buff_size *= 2; /* twice the old size */
|
|
|
+ }
|
|
|
+ if ((buff = pkg_realloc(buff, buff_size)) == 0)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
|
|
|
+ ERR("No memory left\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+error:
|
|
|
+ if(buff) pkg_free(buff);
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+int erl_rpc_struct_add(erl_rpc_ctx_t* ctx, char* fmt, ...)
|
|
|
+{
|
|
|
+ void **void_ptr;
|
|
|
+ char *char_ptr;
|
|
|
+ str *str_ptr;
|
|
|
+ erl_rpc_ctx_t *handler;
|
|
|
+ erl_rpc_param_t *param;
|
|
|
+
|
|
|
+ int reads=0;
|
|
|
+
|
|
|
+ va_list ap;
|
|
|
+
|
|
|
+ va_start(ap,fmt);
|
|
|
+
|
|
|
+ while(*fmt)
|
|
|
+ {
|
|
|
+ if ((param = erl_new_param(ctx))==0)
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ param->member_name = va_arg(ap, char*);
|
|
|
+
|
|
|
+ switch(*fmt)
|
|
|
+ {
|
|
|
+ case 'b': /* Bool */
|
|
|
+ case 't': /* Date and time */
|
|
|
+ case 'd': /* Integer */
|
|
|
+ param->type = ERL_INTEGER_EXT;
|
|
|
+ param->value.n = va_arg(ap, int);;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'f': /* double */
|
|
|
+ param->type = ERL_FLOAT_EXT;
|
|
|
+ param->value.d = va_arg(ap, double);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'S': /* str structure */
|
|
|
+ str_ptr = va_arg(ap, str*);
|
|
|
+
|
|
|
+ param->type = ERL_STRING_EXT;
|
|
|
+ param->value.S = *str_ptr;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 's':/* zero terminated string */
|
|
|
+
|
|
|
+ char_ptr = va_arg(ap, char *);
|
|
|
+
|
|
|
+ param->type = ERL_STRING_EXT;
|
|
|
+ param->value.S.len = strlen(char_ptr);
|
|
|
+
|
|
|
+ param->value.S.s = (char*)pkg_malloc(param->value.S.len);
|
|
|
+
|
|
|
+ if (!param->value.S.s)
|
|
|
+ {
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR, param->value.S.s, ctx))
|
|
|
+ {
|
|
|
+ pkg_free(param->value.S.s);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(param->value.S.s,char_ptr,param->value.S.len);
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '{':
|
|
|
+ void_ptr = va_arg(ap,void**);
|
|
|
+
|
|
|
+ handler = (erl_rpc_ctx_t*)pkg_malloc(sizeof(erl_rpc_ctx_t));
|
|
|
+
|
|
|
+ if (!handler)
|
|
|
+ {
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR,(void*)handler,ctx))
|
|
|
+ {
|
|
|
+ pkg_free(handler);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ *handler = *ctx;
|
|
|
+ handler->no_params = 0;
|
|
|
+ handler->reply_params=0;
|
|
|
+ handler->tail = 0;
|
|
|
+
|
|
|
+ /* go where we stopped */
|
|
|
+ *(erl_rpc_ctx_t**)void_ptr = handler;
|
|
|
+
|
|
|
+ param->type = ERL_SMALL_TUPLE_EXT;
|
|
|
+ param->value.handler = (void*)handler;
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '[':
|
|
|
+ void_ptr = va_arg(ap,void**);
|
|
|
+
|
|
|
+ handler = (erl_rpc_ctx_t*)pkg_malloc(sizeof(erl_rpc_ctx_t));
|
|
|
+
|
|
|
+ if (!handler)
|
|
|
+ {
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR,(void*)handler,ctx))
|
|
|
+ {
|
|
|
+ pkg_free(handler);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ *handler = *ctx;
|
|
|
+ handler->no_params = 0;
|
|
|
+ handler->reply_params=0;
|
|
|
+ handler->tail = 0;
|
|
|
+
|
|
|
+ /* go where we stopped */
|
|
|
+ *(erl_rpc_ctx_t**)void_ptr = handler;
|
|
|
+
|
|
|
+ param->type = ERL_LIST_EXT;
|
|
|
+ param->value.handler = (void*)handler;
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ LM_ERR("Invalid type '%c' in formatting string\n", *fmt);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ erl_rpc_append_param(ctx,param);
|
|
|
+
|
|
|
+ reads++;
|
|
|
+ fmt++;
|
|
|
+ }
|
|
|
+ va_end(ap);
|
|
|
+ return reads;
|
|
|
+
|
|
|
+error:
|
|
|
+
|
|
|
+ LM_ERR("Failed to encode parameter #%d into response.\n",reads);
|
|
|
+ va_end(ap);
|
|
|
+ return -reads;
|
|
|
+}
|
|
|
+
|
|
|
+int erl_rpc_array_add(erl_rpc_ctx_t* ctx, char* fmt, ...)
|
|
|
+{
|
|
|
+ void **void_ptr;
|
|
|
+ char *char_ptr;
|
|
|
+ str *str_ptr;
|
|
|
+ erl_rpc_ctx_t *handler;
|
|
|
+ erl_rpc_param_t *param;
|
|
|
+
|
|
|
+ int reads=0;
|
|
|
+
|
|
|
+ va_list ap;
|
|
|
+
|
|
|
+ va_start(ap,fmt);
|
|
|
+
|
|
|
+ LM_DBG("ctx=%p add fmt=<%s>\n",(void*)ctx,fmt);
|
|
|
+
|
|
|
+ while(*fmt)
|
|
|
+ {
|
|
|
+ if ((param = erl_new_param(ctx))==0)
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ param->member_name = NULL;
|
|
|
+
|
|
|
+ switch(*fmt)
|
|
|
+ {
|
|
|
+ case 'b': /* Bool */
|
|
|
+ case 't': /* Date and time */
|
|
|
+ case 'd': /* Integer */
|
|
|
+ param->type = ERL_INTEGER_EXT;
|
|
|
+ param->value.n = va_arg(ap, int);;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'f': /* double */
|
|
|
+ param->type = ERL_FLOAT_EXT;
|
|
|
+ param->value.d = va_arg(ap, double);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'S': /* str structure */
|
|
|
+ str_ptr = va_arg(ap, str*);
|
|
|
+
|
|
|
+ param->type = ERL_STRING_EXT;
|
|
|
+ param->value.S = *str_ptr;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 's':/* zero terminated string */
|
|
|
+
|
|
|
+ char_ptr = va_arg(ap, char *);
|
|
|
+
|
|
|
+ param->type = ERL_STRING_EXT;
|
|
|
+ param->value.S.len = strlen(char_ptr);
|
|
|
+
|
|
|
+ param->value.S.s = (char*)pkg_malloc(param->value.S.len);
|
|
|
+
|
|
|
+ if (!param->value.S.s)
|
|
|
+ {
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR, param->value.S.s, ctx))
|
|
|
+ {
|
|
|
+ pkg_free(param->value.S.s);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(param->value.S.s,char_ptr,param->value.S.len);
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '{':
|
|
|
+ void_ptr = va_arg(ap,void**);
|
|
|
+
|
|
|
+ handler = (erl_rpc_ctx_t*)pkg_malloc(sizeof(erl_rpc_ctx_t));
|
|
|
+
|
|
|
+ if (!handler)
|
|
|
+ {
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR,(void*)handler,ctx))
|
|
|
+ {
|
|
|
+ pkg_free(handler);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ *handler = *ctx;
|
|
|
+ handler->no_params = 0;
|
|
|
+ handler->reply_params=0;
|
|
|
+ handler->tail = 0;
|
|
|
+
|
|
|
+ /* go where we stopped */
|
|
|
+ *(erl_rpc_ctx_t**)void_ptr = handler;
|
|
|
+
|
|
|
+ param->type = ERL_SMALL_TUPLE_EXT;
|
|
|
+ param->value.handler = (void*)handler;
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '[':
|
|
|
+ void_ptr = va_arg(ap,void**);
|
|
|
+
|
|
|
+ handler = (erl_rpc_ctx_t*)pkg_malloc(sizeof(erl_rpc_ctx_t));
|
|
|
+
|
|
|
+ if (!handler)
|
|
|
+ {
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR,(void*)handler,ctx))
|
|
|
+ {
|
|
|
+ pkg_free(handler);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ *handler = *ctx;
|
|
|
+ handler->no_params = 0;
|
|
|
+ handler->reply_params=0;
|
|
|
+ handler->tail = 0;
|
|
|
+
|
|
|
+ /* go where we stopped */
|
|
|
+ *(erl_rpc_ctx_t**)void_ptr = handler;
|
|
|
+
|
|
|
+ param->type = ERL_LIST_EXT;
|
|
|
+ param->value.handler = (void*)handler;
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ LM_ERR("Invalid type '%c' in formatting string\n", *fmt);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ erl_rpc_append_param(ctx,param);
|
|
|
+
|
|
|
+ reads++;
|
|
|
+ fmt++;
|
|
|
+ }
|
|
|
+ va_end(ap);
|
|
|
+ return reads;
|
|
|
+
|
|
|
+error:
|
|
|
+
|
|
|
+ LM_ERR("Failed to encode parameter #%d into response.\n",reads);
|
|
|
+ va_end(ap);
|
|
|
+ return -reads;
|
|
|
+}
|
|
|
+
|
|
|
+int erl_rpc_struct_printf(erl_rpc_ctx_t* ctx, char* name, char* fmt, ...)
|
|
|
+{
|
|
|
+ int n, buff_size;
|
|
|
+ char *buff;
|
|
|
+ va_list ap;
|
|
|
+ erl_rpc_param_t *param;
|
|
|
+
|
|
|
+ LM_ERR("parsing name:%s fmt: %s\n",name, fmt);
|
|
|
+
|
|
|
+ buff = (char*)pkg_malloc(RPC_BUF_SIZE);
|
|
|
+ if (!buff) {
|
|
|
+ ERR("No memory left\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ buff_size = RPC_BUF_SIZE;
|
|
|
+
|
|
|
+ while(1)
|
|
|
+ {
|
|
|
+ /* Try to print in the allocated space. */
|
|
|
+ va_start(ap, fmt);
|
|
|
+ n = vsnprintf(buff, buff_size, fmt, ap);
|
|
|
+ va_end(ap);
|
|
|
+ /* If that worked, return the string. */
|
|
|
+ if (n > -1 && n < buff_size)
|
|
|
+ {
|
|
|
+
|
|
|
+ if(add_to_recycle_bin(JUNK_PKGCHAR,(void*)buff,ctx))
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ else if ((param = erl_new_param(ctx)))
|
|
|
+ {
|
|
|
+ param->type = ERL_STRING_EXT;
|
|
|
+ param->value.S.s = buff;
|
|
|
+ param->value.S.len = n;
|
|
|
+ param->member_name = name;
|
|
|
+ erl_rpc_append_param(ctx,param);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Else try again with more space. */
|
|
|
+ if (n > -1)
|
|
|
+ { /* glibc 2.1 */
|
|
|
+ buff_size = n + 1; /* precisely what is needed */
|
|
|
+ }
|
|
|
+ else
|
|
|
+ { /* glibc 2.0 */
|
|
|
+ buff_size *= 2; /* twice the old size */
|
|
|
+ }
|
|
|
+ if ((buff = pkg_realloc(buff, buff_size)) == 0)
|
|
|
+ {
|
|
|
+ ERR("No memory left\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+error:
|
|
|
+ if(buff) pkg_free(buff);
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+int erl_rpc_capabilities(erl_rpc_ctx_t* ctx)
|
|
|
+{
|
|
|
+ return 0; /* no RPC_DELAYED_REPLY */
|
|
|
+}
|
|
|
+
|
|
|
+/** Add a memory to the list of memory blocks that
|
|
|
+ * need to be re-claimed later.
|
|
|
+ *
|
|
|
+ * @param type The type of the memory block.
|
|
|
+ * @param ptr A pointer to the memory block.
|
|
|
+ * @param ctx The context.
|
|
|
+ * @return 0 on success, a negative number on error.
|
|
|
+ * @sa empty_recycle_bin()
|
|
|
+ */
|
|
|
+static int add_to_recycle_bin(int type, void *ptr, erl_rpc_ctx_t *ctx)
|
|
|
+{
|
|
|
+ struct erl_rpc_garbage *p;
|
|
|
+
|
|
|
+ p = (struct erl_rpc_garbage*)pkg_malloc(sizeof(struct erl_rpc_garbage));
|
|
|
+
|
|
|
+ if (!p)
|
|
|
+ {
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ p->type = type;
|
|
|
+ p->ptr = ptr;
|
|
|
+ p->next = recycle_bin;
|
|
|
+ recycle_bin = p;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/** Re-claims all memory allocated in the process of building XML-RPC
|
|
|
+ * reply.
|
|
|
+ */
|
|
|
+void empty_recycle_bin(void)
|
|
|
+{
|
|
|
+ struct erl_rpc_garbage* p;
|
|
|
+ /* Collect garbage */
|
|
|
+ while(recycle_bin)
|
|
|
+ {
|
|
|
+ p = recycle_bin;
|
|
|
+ recycle_bin = recycle_bin->next;
|
|
|
+ switch(p->type)
|
|
|
+ {
|
|
|
+ case JUNK_EI_X_BUFF:
|
|
|
+
|
|
|
+ if (p->ptr)
|
|
|
+ {
|
|
|
+ ei_x_free((ei_x_buff*)p->ptr);
|
|
|
+ pkg_free(p->ptr);
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case JUNK_PKGCHAR:
|
|
|
+
|
|
|
+ if (p->ptr)
|
|
|
+ {
|
|
|
+ pkg_free(p->ptr);
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ ERR("BUG: Unsupported junk type\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ pkg_free(p);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Get int parameter
|
|
|
+ */
|
|
|
+static int get_int(int *int_ptr,erl_rpc_ctx_t *ctx, int reads, int autoconvert)
|
|
|
+{
|
|
|
+ int type, size;
|
|
|
+ char *p;
|
|
|
+ char *endptr;
|
|
|
+ double d;
|
|
|
+ long l;
|
|
|
+
|
|
|
+ if (ei_get_type(ctx->request->buff,&ctx->request_index,&type,&size))
|
|
|
+ {
|
|
|
+ if(ctx->optional) return 0;
|
|
|
+
|
|
|
+ erl_rpc_fault(ctx,400,"Can't determine data type of parameter #%d",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch(type)
|
|
|
+ {
|
|
|
+ case ERL_SMALL_INTEGER_EXT:
|
|
|
+ case ERL_INTEGER_EXT:
|
|
|
+ if(ei_decode_long(ctx->request->buff, &ctx->request_index, &l))
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Bad value of parameter #%d.",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ *int_ptr = (int)l;
|
|
|
+
|
|
|
+ break;
|
|
|
+ case ERL_STRING_EXT:
|
|
|
+ case ERL_LIST_EXT:
|
|
|
+
|
|
|
+ if (autoconvert == 0)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Bad type of parameter #%d",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* allocate buffer */
|
|
|
+ p = (char*)pkg_malloc(size+1);
|
|
|
+
|
|
|
+ if (!p)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,500, "Internal Server Error (No memory left)");
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ *int_ptr = strtol(p,&endptr,10);
|
|
|
+ if (p == endptr)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Unable to convert %s into integer for parameter at position %d",p,reads);
|
|
|
+ pkg_free(p);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ pkg_free(p);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case ERL_FLOAT_EXT:
|
|
|
+
|
|
|
+ if (autoconvert == 0)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Bad type of parameter #%d",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ei_decode_double(ctx->request->buff,&ctx->request_index,&d))
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400, "Can't read parameter #%d",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ *int_ptr=(int)d;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ LM_ERR("Unsupported type ('%c') for conversion into integer parameter #%d.\n",type,reads);
|
|
|
+ erl_rpc_fault(ctx,400,"Unsupported type ('%c') for conversion into integer parameter #%d.",type,reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int get_double(double *double_prt,erl_rpc_ctx_t *ctx, int reads, int autoconvert)
|
|
|
+{
|
|
|
+ int type, size;
|
|
|
+ char *p;
|
|
|
+ char *endptr;
|
|
|
+ long n;
|
|
|
+
|
|
|
+ if (ei_get_type(ctx->request->buff,&ctx->request_index,&type,&size)){
|
|
|
+ erl_rpc_fault(ctx,400,"Can't determine data type of parameter #%d",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch(type)
|
|
|
+ {
|
|
|
+ case ERL_FLOAT_EXT:
|
|
|
+
|
|
|
+ if (ei_decode_double(ctx->request->buff,&ctx->request_index,double_prt))
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400, "Bad value of parameter #%d.",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ case ERL_STRING_EXT:
|
|
|
+ case ERL_LIST_EXT:
|
|
|
+
|
|
|
+ if (autoconvert == 0)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Bad type of parameter #%d",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* allocate buffer */
|
|
|
+ p = (char*)pkg_malloc(size+1);
|
|
|
+
|
|
|
+ if (!p)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,500, "Internal Server Error (No memory left)");
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ *double_prt = strtod(p,&endptr);
|
|
|
+ if (p == endptr)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Unable to convert %s into double, parameter at position #%d",p,reads);
|
|
|
+ pkg_free(p);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ pkg_free(p);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case ERL_SMALL_INTEGER_EXT:
|
|
|
+ case ERL_INTEGER_EXT:
|
|
|
+
|
|
|
+ if (autoconvert == 0)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Bad type of parameter #%d",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(ei_decode_long(ctx->request->buff, &ctx->request_index, &n))
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Can't read parameter #%d",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ *double_prt=n;
|
|
|
+
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ erl_rpc_fault(ctx,400,"Can't convert to double parameter #%d.",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#define MAX_DIGITS 20
|
|
|
+
|
|
|
+static int get_str(str *str_ptr, erl_rpc_ctx_t *ctx, int reads, int autoconvert)
|
|
|
+{
|
|
|
+ int type, size;
|
|
|
+ char *p;
|
|
|
+ double d;
|
|
|
+ long n;
|
|
|
+
|
|
|
+ if (ei_get_type(ctx->request->buff,&ctx->request_index,&type,&size))
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Can't determine data type of parameter #%d",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch(type)
|
|
|
+ {
|
|
|
+ case ERL_FLOAT_EXT:
|
|
|
+
|
|
|
+ if (autoconvert == 0)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Bad type of parameter #%d",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ei_decode_double(ctx->request->buff,&ctx->request_index,&d))
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400, "Bad value of parameter #%d.",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ p=(char*)pkg_malloc(MAX_DIGITS);
|
|
|
+
|
|
|
+ if (!p)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,500, "Internal Server Error (No memory left)");
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR, p, ctx))
|
|
|
+ {
|
|
|
+ pkg_free(p);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ str_ptr->len=snprintf(p, MAX_DIGITS, "%f", d);
|
|
|
+ str_ptr->s = p;
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case ERL_STRING_EXT:
|
|
|
+ case ERL_LIST_EXT:
|
|
|
+ case ERL_BINARY_EXT:
|
|
|
+
|
|
|
+ /* allocate buffer */
|
|
|
+ p = (char*)pkg_malloc(size+1);
|
|
|
+
|
|
|
+ if (!p)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,500, "Internal Server Error (No memory left)");
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR, p, ctx))
|
|
|
+ {
|
|
|
+ pkg_free(p);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(ei_decode_strorbin(ctx->request->buff,&ctx->request_index,size+1,p))
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400, "Can't read parameter #%d",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ str_ptr->s=p;
|
|
|
+ str_ptr->len=size;
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case ERL_SMALL_INTEGER_EXT:
|
|
|
+ case ERL_INTEGER_EXT:
|
|
|
+
|
|
|
+ if (autoconvert == 0)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Bad type of parameter #%d",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ei_decode_long(ctx->request->buff,&ctx->request_index,&n))
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400, "Bad value of parameter #%d.",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ p=(char*)pkg_malloc(MAX_DIGITS);
|
|
|
+
|
|
|
+ if (!p)
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,500, "Internal Server Error (No memory left)");
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR, p, ctx))
|
|
|
+ {
|
|
|
+ pkg_free(p);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ str_ptr->len=snprintf(p, MAX_DIGITS, "%ld", n);
|
|
|
+ str_ptr->s = p;
|
|
|
+
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ erl_rpc_fault(ctx,400,"Can't convert to string parameter #%d.",reads);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ LM_ERR("parameter #%d:<%.*s>\n",reads,STR_FMT(str_ptr));
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Find member in tuple (aka RPC struct)
|
|
|
+ */
|
|
|
+static int find_member(erl_rpc_ctx_t *ctx, int arity, const char* member_name)
|
|
|
+{
|
|
|
+ int index,i=0;
|
|
|
+ int type,size;
|
|
|
+ char key_name[MAXATOMLEN];
|
|
|
+
|
|
|
+ /* save position */
|
|
|
+ index = ctx->request_index;
|
|
|
+
|
|
|
+ /* { name, Value, name, Value...} */
|
|
|
+ while (i < arity)
|
|
|
+ {
|
|
|
+ if (ei_get_type(ctx->request->buff,&ctx->request_index,&type,&size))
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Bad struct member type");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(ei_decode_atom(ctx->request->buff,&ctx->request_index, key_name))
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Bad member name");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (strcasecmp(member_name,key_name))
|
|
|
+ {
|
|
|
+ if(ei_skip_term(ctx->request->buff,&ctx->request_index))
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,400,"Unexpected end of struct tuple");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ /* return at current position */
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+
|
|
|
+ erl_rpc_fault(ctx,400, "Member %s not found",member_name);
|
|
|
+
|
|
|
+error:
|
|
|
+ ctx->request_index = index;
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+void init_rpc_handlers()
|
|
|
+{
|
|
|
+ erl_rpc_func_param.send = (rpc_send_f)erl_rpc_send;
|
|
|
+ erl_rpc_func_param.fault = (rpc_fault_f)erl_rpc_fault;
|
|
|
+ erl_rpc_func_param.add = (rpc_add_f)erl_rpc_add;
|
|
|
+ erl_rpc_func_param.scan = (rpc_scan_f)erl_rpc_scan;
|
|
|
+ erl_rpc_func_param.rpl_printf = (rpc_rpl_printf_f)erl_rpc_printf;
|
|
|
+ erl_rpc_func_param.struct_add = (rpc_struct_add_f)erl_rpc_struct_add;
|
|
|
+ erl_rpc_func_param.array_add = (rpc_array_add_f)erl_rpc_array_add;
|
|
|
+ erl_rpc_func_param.struct_scan = (rpc_struct_scan_f)erl_rpc_struct_scan;
|
|
|
+ erl_rpc_func_param.struct_printf = (rpc_struct_printf_f)erl_rpc_struct_printf;
|
|
|
+ erl_rpc_func_param.capabilities = (rpc_capabilities_f)erl_rpc_capabilities;
|
|
|
+ erl_rpc_func_param.delayed_ctx_new = 0;
|
|
|
+ erl_rpc_func_param.delayed_ctx_close = 0;
|
|
|
+}
|
|
|
+
|
|
|
+erl_rpc_param_t *erl_new_param(erl_rpc_ctx_t *ctx)
|
|
|
+{
|
|
|
+ erl_rpc_param_t *p = (erl_rpc_param_t *)pkg_malloc(sizeof(erl_rpc_param_t));
|
|
|
+
|
|
|
+ if (add_to_recycle_bin(JUNK_PKGCHAR,(void*)p,ctx))
|
|
|
+ {
|
|
|
+ erl_rpc_fault(ctx,500, "Internal Server Error (No memory left)");
|
|
|
+ LM_ERR("Not enough memory\n");
|
|
|
+
|
|
|
+ pkg_free(p);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ p->next = 0;
|
|
|
+ p->member_name = 0;
|
|
|
+ return p;
|
|
|
+}
|
|
|
+
|
|
|
+void erl_rpc_append_param(erl_rpc_ctx_t *ctx, erl_rpc_param_t *param)
|
|
|
+{
|
|
|
+
|
|
|
+ if (ctx->tail)
|
|
|
+ {
|
|
|
+ ctx->tail->next = param;
|
|
|
+ ctx->tail = param;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ ctx->reply_params = ctx->tail = param;
|
|
|
+ }
|
|
|
+
|
|
|
+ param->next = 0;
|
|
|
+ ctx->no_params++;
|
|
|
+}
|