mirror of https://github.com/sipwise/kamailio.git
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.
1485 lines
30 KiB
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++;
|
|
}
|