You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/modules/erlang/handle_rpc.c

1485 lines
30 KiB

/**
* 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 <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++;
}