/** * Copyright (C) 2015 Bicom Systems Ltd, (bicomsystems.com) * * Author: Seudin Kasumovic (seudin.kasumovic@gmail.com) * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include "../../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">>,<>}]}} */ 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++; }