mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
288 lines
7.7 KiB
C
288 lines
7.7 KiB
C
/*
|
|
* ejabberd, Copyright (C) 2002-2012 ProcessOne
|
|
*
|
|
* This program 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.
|
|
*
|
|
* This program 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., 59 Temple Place, Suite 330, Boston, MA
|
|
* 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <erl_driver.h>
|
|
#include <ei.h>
|
|
#include <expat.h>
|
|
|
|
|
|
#define XML_START 0
|
|
#define XML_END 1
|
|
#define XML_CDATA 2
|
|
#define XML_ERROR 3
|
|
|
|
#define PARSE_COMMAND 0
|
|
#define PARSE_FINAL_COMMAND 1
|
|
|
|
/*
|
|
* R15B changed several driver callbacks to use ErlDrvSizeT and
|
|
* ErlDrvSSizeT typedefs instead of int.
|
|
* This provides missing typedefs on older OTP versions.
|
|
*/
|
|
#if ERL_DRV_EXTENDED_MAJOR_VERSION < 2
|
|
typedef int ErlDrvSizeT;
|
|
typedef int ErlDrvSSizeT;
|
|
#endif
|
|
|
|
ei_x_buff event_buf;
|
|
ei_x_buff xmlns_buf;
|
|
|
|
typedef struct {
|
|
ErlDrvPort port;
|
|
XML_Parser parser;
|
|
} expat_data;
|
|
|
|
static XML_Memory_Handling_Suite ms;
|
|
|
|
void encode_name(const XML_Char *name)
|
|
{
|
|
char *name_start;
|
|
char *prefix_start;
|
|
char *buf;
|
|
int name_len, prefix_len, buf_len;
|
|
|
|
if ((name_start = strchr(name, '\n'))) {
|
|
if ((prefix_start = strchr(name_start+1, '\n'))) {
|
|
name_len = prefix_start - name_start;
|
|
prefix_len = strlen(prefix_start+1);
|
|
buf_len = prefix_len + name_len;
|
|
buf = driver_alloc(buf_len);
|
|
memcpy(buf, prefix_start+1, prefix_len);
|
|
memcpy(buf+prefix_len, name_start, name_len);
|
|
buf[prefix_len] = ':';
|
|
ei_x_encode_string_len(&event_buf, buf, buf_len);
|
|
driver_free(buf);
|
|
} else {
|
|
ei_x_encode_string(&event_buf, name_start+1);
|
|
};
|
|
} else {
|
|
ei_x_encode_string(&event_buf, name);
|
|
}
|
|
}
|
|
|
|
void *erlXML_StartElementHandler(expat_data *d,
|
|
const XML_Char *name,
|
|
const XML_Char **atts)
|
|
{
|
|
int i;
|
|
|
|
ei_x_encode_list_header(&event_buf, 1);
|
|
ei_x_encode_tuple_header(&event_buf, 2);
|
|
ei_x_encode_long(&event_buf, XML_START);
|
|
ei_x_encode_tuple_header(&event_buf, 2);
|
|
encode_name(name);
|
|
ei_x_append(&event_buf, &xmlns_buf);
|
|
ei_x_free(&xmlns_buf);
|
|
ei_x_new(&xmlns_buf);
|
|
|
|
for (i = 0; atts[i]; i += 2) {}
|
|
|
|
if (i > 0)
|
|
{
|
|
ei_x_encode_list_header(&event_buf, i/2);
|
|
|
|
for (i = 0; atts[i]; i += 2)
|
|
{
|
|
ei_x_encode_tuple_header(&event_buf, 2);
|
|
encode_name(atts[i]);
|
|
ei_x_encode_string(&event_buf, atts[i+1]);
|
|
}
|
|
}
|
|
|
|
ei_x_encode_empty_list(&event_buf);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void *erlXML_EndElementHandler(expat_data *d,
|
|
const XML_Char *name)
|
|
{
|
|
ei_x_encode_list_header(&event_buf, 1);
|
|
ei_x_encode_tuple_header(&event_buf, 2);
|
|
ei_x_encode_long(&event_buf, XML_END);
|
|
encode_name(name);
|
|
return NULL;
|
|
}
|
|
|
|
void *erlXML_CharacterDataHandler(expat_data *d,
|
|
const XML_Char *s,
|
|
int len)
|
|
{
|
|
ei_x_encode_list_header(&event_buf, 1);
|
|
ei_x_encode_tuple_header(&event_buf, 2);
|
|
ei_x_encode_long(&event_buf, XML_CDATA);
|
|
ei_x_encode_binary(&event_buf, s, len);
|
|
return NULL;
|
|
}
|
|
|
|
void *erlXML_StartNamespaceDeclHandler(expat_data *d,
|
|
const XML_Char *prefix,
|
|
const XML_Char *uri)
|
|
{
|
|
int prefix_len;
|
|
char *buf;
|
|
|
|
/* From the expat documentation:
|
|
"For a default namespace declaration (xmlns='...'),
|
|
the prefix will be null ...
|
|
... The URI will be null for the case where
|
|
the default namespace is being unset."
|
|
|
|
FIXME: I'm not quite sure what all that means */
|
|
if (uri == NULL)
|
|
return NULL;
|
|
|
|
ei_x_encode_list_header(&xmlns_buf, 1);
|
|
ei_x_encode_tuple_header(&xmlns_buf, 2);
|
|
if (prefix) {
|
|
prefix_len = strlen(prefix);
|
|
buf = driver_alloc(7 + prefix_len);
|
|
strcpy(buf, "xmlns:");
|
|
strcpy(buf+6, prefix);
|
|
ei_x_encode_string(&xmlns_buf, buf);
|
|
driver_free(buf);
|
|
} else {
|
|
ei_x_encode_string(&xmlns_buf, "xmlns");
|
|
};
|
|
ei_x_encode_string(&xmlns_buf, uri);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static ErlDrvData expat_erl_start(ErlDrvPort port, char *buff)
|
|
{
|
|
expat_data* d = (expat_data*)driver_alloc(sizeof(expat_data));
|
|
d->port = port;
|
|
d->parser = XML_ParserCreate_MM("UTF-8", &ms, "\n");
|
|
XML_SetUserData(d->parser, d);
|
|
|
|
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
|
|
|
|
XML_SetStartElementHandler(
|
|
d->parser, (XML_StartElementHandler)erlXML_StartElementHandler);
|
|
XML_SetEndElementHandler(
|
|
d->parser, (XML_EndElementHandler)erlXML_EndElementHandler);
|
|
XML_SetCharacterDataHandler(
|
|
d->parser, (XML_CharacterDataHandler)erlXML_CharacterDataHandler);
|
|
|
|
XML_SetStartNamespaceDeclHandler(
|
|
d->parser, (XML_StartNamespaceDeclHandler) erlXML_StartNamespaceDeclHandler);
|
|
XML_SetReturnNSTriplet(d->parser, 1);
|
|
|
|
XML_SetDefaultHandler(d->parser, NULL);
|
|
|
|
return (ErlDrvData)d;
|
|
}
|
|
|
|
static void expat_erl_stop(ErlDrvData handle)
|
|
{
|
|
XML_ParserFree(((expat_data *)handle)->parser);
|
|
driver_free((char*)handle);
|
|
}
|
|
|
|
static ErlDrvSSizeT expat_erl_control(ErlDrvData drv_data,
|
|
unsigned int command,
|
|
char *buf, ErlDrvSizeT len,
|
|
char **rbuf, ErlDrvSizeT rlen)
|
|
{
|
|
expat_data* d = (expat_data*)drv_data;
|
|
int res, errcode;
|
|
char *errstring;
|
|
ErlDrvBinary *b;
|
|
size_t size;
|
|
|
|
switch (command)
|
|
{
|
|
case PARSE_COMMAND:
|
|
case PARSE_FINAL_COMMAND:
|
|
ei_x_new_with_version(&event_buf);
|
|
ei_x_new(&xmlns_buf);
|
|
res = XML_Parse(d->parser, buf, len, command == PARSE_FINAL_COMMAND);
|
|
|
|
if(!res)
|
|
{
|
|
errcode = XML_GetErrorCode(d->parser);
|
|
errstring = (char *)XML_ErrorString(errcode);
|
|
|
|
ei_x_encode_list_header(&event_buf, 1);
|
|
ei_x_encode_tuple_header(&event_buf, 2);
|
|
ei_x_encode_long(&event_buf, XML_ERROR);
|
|
ei_x_encode_tuple_header(&event_buf, 2);
|
|
ei_x_encode_long(&event_buf, errcode);
|
|
ei_x_encode_string(&event_buf, errstring);
|
|
}
|
|
|
|
ei_x_encode_empty_list(&event_buf);
|
|
|
|
size = event_buf.index;
|
|
|
|
b = driver_alloc_binary(size);
|
|
memcpy(b->orig_bytes, event_buf.buff, size);
|
|
|
|
ei_x_free(&event_buf);
|
|
ei_x_free(&xmlns_buf);
|
|
|
|
*rbuf = (char *)b;
|
|
return size;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ErlDrvEntry expat_driver_entry = {
|
|
NULL, /* F_PTR init, N/A */
|
|
expat_erl_start, /* L_PTR start, called when port is opened */
|
|
expat_erl_stop, /* F_PTR stop, called when port is closed */
|
|
NULL, /* F_PTR output, called when erlang has sent */
|
|
NULL, /* F_PTR ready_input, called when input descriptor ready */
|
|
NULL, /* F_PTR ready_output, called when output descriptor ready */
|
|
"expat_erl", /* char *driver_name, the argument to open_port */
|
|
NULL, /* F_PTR finish, called when unloaded */
|
|
NULL, /* handle */
|
|
expat_erl_control, /* F_PTR control, port_command callback */
|
|
NULL, /* F_PTR timeout, reserved */
|
|
NULL, /* F_PTR outputv, reserved */
|
|
/* Added in Erlang/OTP R15B: */
|
|
NULL, /* ready_async */
|
|
NULL, /* flush */
|
|
NULL, /* call */
|
|
NULL, /* event */
|
|
ERL_DRV_EXTENDED_MARKER, /* extended_marker */
|
|
ERL_DRV_EXTENDED_MAJOR_VERSION, /* major_version */
|
|
ERL_DRV_EXTENDED_MINOR_VERSION, /* minor_version */
|
|
0, /* driver_flags */
|
|
NULL, /* handle2 */
|
|
NULL, /* process_exit */
|
|
NULL /* stop_select */
|
|
};
|
|
|
|
DRIVER_INIT(expat_erl) /* must match name in driver_entry */
|
|
{
|
|
ms.malloc_fcn = driver_alloc;
|
|
ms.realloc_fcn = driver_realloc;
|
|
ms.free_fcn = driver_free;
|
|
return &expat_driver_entry;
|
|
}
|
|
|
|
|