mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-24 16:23:40 +01:00
259 lines
6.0 KiB
C
259 lines
6.0 KiB
C
#include <erl_nif.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#ifdef SSL40
|
|
#define ENIF_ALLOC(SIZE) enif_alloc(SIZE)
|
|
#define ENIF_FREE(PTR) enif_free(PTR)
|
|
#define ENIF_REALLOC(PTR, SIZE) enif_realloc(PTR, SIZE)
|
|
#define ENIF_ALLOC_BINARY(SIZE, BIN) enif_alloc_binary(SIZE, BIN)
|
|
#define ENIF_COMPARE(TERM1, TERM2) enif_compare(TERM1, TERM2)
|
|
#else
|
|
#define ENIF_ALLOC(SIZE) enif_alloc(env, SIZE)
|
|
#define ENIF_FREE(PTR) enif_free(env, PTR)
|
|
#define ENIF_REALLOC(PTR, SIZE) enif_realloc(env, PTR, SIZE)
|
|
#define ENIF_ALLOC_BINARY(SIZE, BIN) enif_alloc_binary(env, SIZE, BIN)
|
|
#define ENIF_COMPARE(TERM1, TERM2) enif_compare(env, TERM1, TERM2)
|
|
#endif
|
|
|
|
static ERL_NIF_TERM atom_xmlelement;
|
|
static ERL_NIF_TERM atom_xmlcdata;
|
|
|
|
struct buf {
|
|
int limit;
|
|
int len;
|
|
unsigned char *b;
|
|
};
|
|
|
|
static int make_element(ErlNifEnv* env, struct buf *rbuf, ERL_NIF_TERM el);
|
|
|
|
static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info)
|
|
{
|
|
atom_xmlelement = enif_make_atom(env, "xmlelement");
|
|
atom_xmlcdata = enif_make_atom(env, "xmlcdata");
|
|
return 0;
|
|
}
|
|
|
|
static struct buf *init_buf(ErlNifEnv* env)
|
|
{
|
|
struct buf *rbuf = ENIF_ALLOC(sizeof(struct buf));
|
|
rbuf->limit = 1024;
|
|
rbuf->len = 0;
|
|
rbuf->b = ENIF_ALLOC(rbuf->limit);
|
|
return rbuf;
|
|
}
|
|
|
|
static void destroy_buf(ErlNifEnv* env, struct buf *rbuf)
|
|
{
|
|
if (rbuf) {
|
|
if (rbuf->b) {
|
|
ENIF_FREE(rbuf->b);
|
|
};
|
|
ENIF_FREE(rbuf);
|
|
};
|
|
}
|
|
|
|
inline void resize_buf(ErlNifEnv* env, struct buf *rbuf, int len_to_add)
|
|
{
|
|
int new_len = rbuf->len + len_to_add;
|
|
|
|
if (new_len >= rbuf->limit) {
|
|
rbuf->limit = ((new_len / 1024) + 1) * 1024;
|
|
rbuf->b = ENIF_REALLOC(rbuf->b, rbuf->limit);
|
|
};
|
|
}
|
|
|
|
static void buf_add_char(ErlNifEnv* env, struct buf *rbuf, unsigned char c)
|
|
{
|
|
resize_buf(env, rbuf, 1);
|
|
(rbuf->b)[rbuf->len] = c;
|
|
rbuf->len += 1;
|
|
}
|
|
|
|
static void buf_add_str(ErlNifEnv* env, struct buf *rbuf, char *data, int len)
|
|
{
|
|
resize_buf(env, rbuf, len);
|
|
memcpy(rbuf->b + rbuf->len, data, len);
|
|
rbuf->len += len;
|
|
}
|
|
|
|
inline void crypt(ErlNifEnv* env, struct buf *rbuf, unsigned char *data, int len)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
switch (data[i]) {
|
|
case '&':
|
|
buf_add_str(env, rbuf, "&", 5);
|
|
break;
|
|
case '<':
|
|
buf_add_str(env, rbuf, "<", 4);
|
|
break;
|
|
case '>':
|
|
buf_add_str(env, rbuf, ">", 4);
|
|
break;
|
|
case '"':
|
|
buf_add_str(env, rbuf, """, 6);
|
|
break;
|
|
case '\'':
|
|
buf_add_str(env, rbuf, "'", 6);
|
|
break;
|
|
default:
|
|
buf_add_char(env, rbuf, data[i]);
|
|
break;
|
|
};
|
|
};
|
|
}
|
|
|
|
static int make_elements(ErlNifEnv* env, struct buf *rbuf, ERL_NIF_TERM els)
|
|
{
|
|
ERL_NIF_TERM head, tail;
|
|
int ret = 0;
|
|
|
|
while (enif_get_list_cell(env, els, &head, &tail)) {
|
|
ret = make_element(env, rbuf, head);
|
|
if (ret) {
|
|
els = tail;
|
|
} else {
|
|
break;
|
|
};
|
|
};
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int make_attrs(ErlNifEnv* env, struct buf *rbuf, ERL_NIF_TERM attrs)
|
|
{
|
|
ErlNifBinary name, data;
|
|
ERL_NIF_TERM head, tail;
|
|
const ERL_NIF_TERM *tuple;
|
|
int arity, ret = 1;
|
|
|
|
while (enif_get_list_cell(env, attrs, &head, &tail)) {
|
|
if (enif_get_tuple(env, head, &arity, &tuple)) {
|
|
if (arity == 2) {
|
|
if (enif_inspect_iolist_as_binary(env, tuple[0], &name) &&
|
|
enif_inspect_iolist_as_binary(env, tuple[1], &data)) {
|
|
buf_add_char(env, rbuf, ' ');
|
|
buf_add_str(env, rbuf, (char *)name.data, name.size);
|
|
buf_add_str(env, rbuf, "='", 2);
|
|
crypt(env, rbuf, data.data, data.size);
|
|
buf_add_char(env, rbuf, '\'');
|
|
attrs = tail;
|
|
} else {
|
|
ret = 0;
|
|
break;
|
|
};
|
|
} else {
|
|
ret = 0;
|
|
break;
|
|
};
|
|
} else {
|
|
ret = 0;
|
|
break;
|
|
};
|
|
};
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int make_element(ErlNifEnv* env, struct buf *rbuf, ERL_NIF_TERM el)
|
|
{
|
|
ErlNifBinary cdata, name;
|
|
const ERL_NIF_TERM *tuple;
|
|
int arity, ret = 0;
|
|
|
|
if (enif_get_tuple(env, el, &arity, &tuple)) {
|
|
if (arity == 2) {
|
|
if (!ENIF_COMPARE(tuple[0], atom_xmlcdata)) {
|
|
if (enif_inspect_iolist_as_binary(env, tuple[1], &cdata)) {
|
|
crypt(env, rbuf, cdata.data, cdata.size);
|
|
ret = 1;
|
|
};
|
|
};
|
|
};
|
|
if (arity == 4) {
|
|
if (!ENIF_COMPARE(tuple[0], atom_xmlelement)) {
|
|
if (enif_inspect_iolist_as_binary(env, tuple[1], &name)) {
|
|
buf_add_char(env, rbuf, '<');
|
|
buf_add_str(env, rbuf, (char *)name.data, name.size);
|
|
ret = make_attrs(env, rbuf, tuple[2]);
|
|
if (ret) {
|
|
if (enif_is_empty_list(env, tuple[3])) {
|
|
buf_add_str(env, rbuf, "/>", 2);
|
|
} else {
|
|
buf_add_char(env, rbuf, '>');
|
|
ret = make_elements(env, rbuf, tuple[3]);
|
|
if (ret) {
|
|
buf_add_str(env, rbuf, "</", 2);
|
|
buf_add_str(env, rbuf, (char*)name.data, name.size);
|
|
buf_add_char(env, rbuf, '>');
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ERL_NIF_TERM element_to(ErlNifEnv* env, int argc,
|
|
const ERL_NIF_TERM argv[],
|
|
int as_string)
|
|
{
|
|
ErlNifBinary output;
|
|
ERL_NIF_TERM result;
|
|
struct buf *rbuf;
|
|
|
|
if (argc == 1) {
|
|
rbuf = init_buf(env);
|
|
if (make_element(env, rbuf, argv[0])) {
|
|
if (as_string) {
|
|
(rbuf->b)[rbuf->len] = 0;
|
|
result = enif_make_string(env, (char *) rbuf->b, ERL_NIF_LATIN1);
|
|
destroy_buf(env, rbuf);
|
|
return result;
|
|
} else {
|
|
if (ENIF_ALLOC_BINARY(rbuf->len, &output)) {
|
|
memcpy(output.data, rbuf->b, rbuf->len);
|
|
result = enif_make_binary(env, &output);
|
|
destroy_buf(env, rbuf);
|
|
return result;
|
|
};
|
|
};
|
|
};
|
|
destroy_buf(env, rbuf);
|
|
};
|
|
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
#ifdef SSL40
|
|
static ERL_NIF_TERM element_to_string(ErlNifEnv* env, int argc,
|
|
const ERL_NIF_TERM argv[])
|
|
{
|
|
return element_to(env, argc, argv, 1);
|
|
}
|
|
#endif
|
|
|
|
static ERL_NIF_TERM element_to_binary(ErlNifEnv* env, int argc,
|
|
const ERL_NIF_TERM argv[])
|
|
{
|
|
return element_to(env, argc, argv, 0);
|
|
}
|
|
|
|
static ErlNifFunc nif_funcs[] =
|
|
{
|
|
/* Stupid Erlang bug with enif_make_string() is fixed
|
|
in R14A only (OTP-8685), so we can't use
|
|
element_to_string in Erlang < R14A.*/
|
|
#ifdef SSL40
|
|
{"element_to_string", 1, element_to_string},
|
|
#endif
|
|
{"element_to_binary", 1, element_to_binary}
|
|
};
|
|
|
|
ERL_NIF_INIT(xml, nif_funcs, load, NULL, NULL, NULL)
|