From e0b348319ad6902ffcbb663e81c29b229c551b61 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 18 Nov 2002 20:39:47 +0000 Subject: [PATCH] Initial revision SVN Revision: 2 --- src/Makefile | 18 ++++ src/ejabberd.erl | 17 +++ src/ejabberd.hrl | 17 +++ src/ejabberd_c2s.erl | 219 ++++++++++++++++++++++++++++++++++++++ src/ejabberd_listener.erl | 42 ++++++++ src/expat_erl.c | 155 +++++++++++++++++++++++++++ src/xml_stream.erl | 77 ++++++++++++++ 7 files changed, 545 insertions(+) create mode 100644 src/Makefile create mode 100644 src/ejabberd.erl create mode 100644 src/ejabberd.hrl create mode 100644 src/ejabberd_c2s.erl create mode 100644 src/ejabberd_listener.erl create mode 100644 src/expat_erl.c create mode 100644 src/xml_stream.erl diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 000000000..ff57225b0 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,18 @@ +# $Id$ + +INCLUDES = -I/usr/lib/erlang/usr/include \ + -I/usr/lib/erlang/lib/erl_interface-3.3.0/include + +LIBDIRS = -L/usr/lib/erlang/lib/erl_interface-3.3.0/lib + +all: expat_erl.so + erl -make + +expat_erl.so: expat_erl.c + gcc -Wall $(INCLUDES) $(LIBDIRS) \ + -lexpat \ + expat_erl.c \ + -lerl_interface \ + -lei \ + -o expat_erl.so -fpic -shared \ + diff --git a/src/ejabberd.erl b/src/ejabberd.erl new file mode 100644 index 000000000..5f0fe02ad --- /dev/null +++ b/src/ejabberd.erl @@ -0,0 +1,17 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd.erl +%%% Author : Alexey Shchepin +%%% Purpose : +%%% Created : 16 Nov 2002 by Alexey Shchepin +%%% Id : $Id$ +%%%---------------------------------------------------------------------- + +-module(ejabberd). +-author('alexey@sevcom.net'). +-vsn('$Revision$ '). + +-export([start/0]). + +start() -> + {ok, _} = erl_ddll:start(), + ejabberd_listener:start(). diff --git a/src/ejabberd.hrl b/src/ejabberd.hrl new file mode 100644 index 000000000..35972c46b --- /dev/null +++ b/src/ejabberd.hrl @@ -0,0 +1,17 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd.hrl +%%% Author : Alexey Shchepin +%%% Purpose : +%%% Created : 17 Nov 2002 by Alexey Shchepin +%%% Id : $Id$ +%%%---------------------------------------------------------------------- + +-define(ejabberd_debug, true). + +-ifdef(ejabberd_debug). +-define(DEBUG(Format, Args), io:format("D(~p:~p:~p) : "++Format++"~n", + [self(),?MODULE,?LINE]++Args)). +-else. +-define(DEBUG(F,A),[]). +-endif. + diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl new file mode 100644 index 000000000..21aef0610 --- /dev/null +++ b/src/ejabberd_c2s.erl @@ -0,0 +1,219 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd_c2s.erl +%%% Author : Alexey Shchepin +%%% Purpose : +%%% Created : 16 Nov 2002 by Alexey Shchepin +%%% Id : $Id$ +%%%---------------------------------------------------------------------- + +-module(ejabberd_c2s). +-author('alexey@sevcom.net'). +-vsn('$Revision$ '). + +-behaviour(gen_fsm). + +%% External exports +-export([start/1, receiver/2, sender/1, send_text/2]). + +%% gen_fsm callbacks +%-export([init/1, state_name/2, state_name/3, handle_event/3, +% handle_sync_event/4, handle_info/3, terminate/3]). +% +-export([init/1, wait_for_stream/2, wait_for_auth/2, terminate/3]). + +-record(state, {socket, sender, receiver}). + +-include("ejabberd.hrl"). + +%start_old(Socket) -> +% spawn(?MODULE, init, [Socket]). + +%init_old(Socket) -> +% SenderPid = spawn(?MODULE, sender, [Socket]), +% ReceiverPid = spawn(?MODULE, receiver, [Socket, self()]), +% loop_old(Socket, SenderPid, ReceiverPid). +% +%loop_old(Socket, SenderPid, ReceiverPid) -> +% receive +% {xmlstreamstart, Name, Attrs} -> +% ?DEBUG("Socket(~p) -> XML Stream start~n" +% " Name: ~s~n" +% " Attrs: ~p~n", [Socket, Name, Attrs]), +% loop_old(Socket, SenderPid, ReceiverPid); +% {xmlstreamend, Name} -> +% ?DEBUG("Socket(~p) -> XML Stream end~n" +% " Name: ~s~n", [Socket, Name]), +% loop_old(Socket, SenderPid, ReceiverPid); +% {xmlstreamelement, El} -> +% ?DEBUG("Socket(~p) -> XML Stream element~n" +% " Element: ~p~n", [Socket, El]), +% loop_old(Socket, SenderPid, ReceiverPid); +% {xmlstreamerror, Err} -> +% ?DEBUG("Socket(~p) -> XML Stream error~n" +% " Error: ~p~n", [Socket, Err]), +% loop_old(Socket, SenderPid, ReceiverPid) +% end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(DBGFSM, true). + +-ifdef(DBGFSM). +-define(FSMOPTS, [{debug, [trace]}]). +-else. +-define(FSMOPTS, []). +-endif. + +-define(STREAM_HEADER, + "" + ). + +%%%---------------------------------------------------------------------- +%%% API +%%%---------------------------------------------------------------------- +start(Socket) -> + gen_fsm:start(ejabberd_c2s, [Socket], ?FSMOPTS). +%start_old(Socket) -> +% spawn(?MODULE, init, [Socket]). + +%%%---------------------------------------------------------------------- +%%% Callback functions from gen_fsm +%%%---------------------------------------------------------------------- + +%%---------------------------------------------------------------------- +%% Func: init/1 +%% Returns: {ok, StateName, StateData} | +%% {ok, StateName, StateData, Timeout} | +%% ignore | +%% {stop, StopReason} +%%---------------------------------------------------------------------- +init([Socket]) -> + SenderPid = spawn(?MODULE, sender, [Socket]), + ReceiverPid = spawn(?MODULE, receiver, [Socket, self()]), + {ok, wait_for_stream, #state{socket = Socket, + receiver = ReceiverPid, + sender = SenderPid}}. + +%%---------------------------------------------------------------------- +%% Func: StateName/2 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} +%%---------------------------------------------------------------------- +state_name(Event, StateData) -> + {next_state, state_name, StateData}. + +wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> + case lists:keysearch("xmlns:stream", 1, Attrs) of + {value, {"xmlns:stream", "http://etherx.jabber.org/streams"}} -> + % TODO + Header = io_lib:format(?STREAM_HEADER, ["SID", "localhost"]), + StateData#state.sender ! {text, Header}, + {next_state, wait_for_auth, StateData}; + _ -> + {stop, error, StateData} + end; + +wait_for_stream(closed, StateData) -> + {stop, normal, StateData}. + + +wait_for_auth({xmlstreamelement, El}, StateData) -> + % TODO + {next_state, wait_for_auth, StateData}; + +wait_for_auth({xmlstreamend, Name}, StateData) -> + % TODO + {stop, normal, StateData}; + +wait_for_auth(closed, StateData) -> + {stop, normal, StateData}. + + + + +%%---------------------------------------------------------------------- +%% Func: StateName/3 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {reply, Reply, NextStateName, NextStateData} | +%% {reply, Reply, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} | +%% {stop, Reason, Reply, NewStateData} +%%---------------------------------------------------------------------- +state_name(Event, From, StateData) -> + Reply = ok, + {reply, Reply, state_name, StateData}. + +%%---------------------------------------------------------------------- +%% Func: handle_event/3 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} +%%---------------------------------------------------------------------- +handle_event(Event, StateName, StateData) -> + {next_state, StateName, StateData}. + +%%---------------------------------------------------------------------- +%% Func: handle_sync_event/4 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {reply, Reply, NextStateName, NextStateData} | +%% {reply, Reply, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} | +%% {stop, Reason, Reply, NewStateData} +%%---------------------------------------------------------------------- +handle_sync_event(Event, From, StateName, StateData) -> + Reply = ok, + {reply, Reply, StateName, StateData}. + +%%---------------------------------------------------------------------- +%% Func: handle_info/3 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} +%%---------------------------------------------------------------------- +handle_info(Info, StateName, StateData) -> + {next_state, StateName, StateData}. + +%%---------------------------------------------------------------------- +%% Func: terminate/3 +%% Purpose: Shutdown the fsm +%% Returns: any +%%---------------------------------------------------------------------- +terminate(Reason, StateName, StatData) -> + ok. + +%%%---------------------------------------------------------------------- +%%% Internal functions +%%%---------------------------------------------------------------------- + +receiver(Socket, C2SPid) -> + XMLStreamPid = xml_stream:start(C2SPid), + receiver(Socket, C2SPid, XMLStreamPid). + +receiver(Socket, C2SPid, XMLStreamPid) -> + case gen_tcp:recv(Socket, 0) of + {ok, Text} -> + xml_stream:send_text(XMLStreamPid, Text), + receiver(Socket, C2SPid, XMLStreamPid); + {error, closed} -> + gen_fsm:send_event(C2SPid, closed), + ok + end. + +sender(Socket) -> + receive + {text, Text} -> + gen_tcp:send(Socket,Text), + sender(Socket); + closed -> + ok + end. + +send_text(Pid, Text) -> + Pid ! {text, Text}. + diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl new file mode 100644 index 000000000..532a06f3f --- /dev/null +++ b/src/ejabberd_listener.erl @@ -0,0 +1,42 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd_listener.erl +%%% Author : Alexey Shchepin +%%% Purpose : +%%% Created : 16 Nov 2002 by Alexey Shchepin +%%% Id : $Id$ +%%%---------------------------------------------------------------------- + +-module(ejabberd_listener). +-author('alexey@sevcom.net'). +-vsn('$Revision$ '). + +-export([start/0, init/0]). + +start() -> + register(ejabberd_listener, spawn(?MODULE, init, [])). + +init() -> + {ok, ListenSocket} = gen_tcp:listen(5522, [binary, + {packet, 0}, + {active, false}, + {reuseaddr, true}]), + accept(ListenSocket). + +accept(ListenSocket) -> + case gen_tcp:accept(ListenSocket) of + {ok,Socket} -> + ejabberd_c2s:start(Socket), + accept(ListenSocket) + end. + + + +do_recv(Sock, Bs) -> + case gen_tcp:recv(Sock, 0) of + {ok, B} -> + do_recv(Sock, [Bs, B]); + {error, closed} -> + {ok, list_to_binary(Bs)} + end. + + diff --git a/src/expat_erl.c b/src/expat_erl.c new file mode 100644 index 000000000..b330eca16 --- /dev/null +++ b/src/expat_erl.c @@ -0,0 +1,155 @@ +/* $Id$ */ + +#include +#include +#include +#include + +typedef struct { + ErlDrvPort port; + XML_Parser parser; +} expat_data; + + +void *erlXML_StartElementHandler(expat_data *d, + const XML_Char *name, + const XML_Char **atts) +{ + int i; + ei_x_buff buf; + + ei_x_new_with_version(&buf); + ei_x_encode_tuple_header(&buf, 2); + ei_x_encode_atom(&buf, "xmlstart"); + ei_x_encode_tuple_header(&buf, 2); + ei_x_encode_string(&buf, name); + + for (i = 0; atts[i]; i += 2) {} + + ei_x_encode_list_header(&buf, i/2); + + for (i = 0; atts[i]; i += 2) + { + ei_x_encode_tuple_header(&buf, 2); + ei_x_encode_string(&buf, atts[i]); + ei_x_encode_string(&buf, atts[i+1]); + } + + ei_x_encode_empty_list(&buf); + + driver_output(d->port, buf.buff, buf.index); + ei_x_free(&buf); + return NULL; +} + +void *erlXML_EndElementHandler(expat_data *d, + const XML_Char *name) +{ + ei_x_buff buf; + + ei_x_new_with_version(&buf); + ei_x_encode_tuple_header(&buf, 2); + ei_x_encode_atom(&buf, "xmlend"); + ei_x_encode_string(&buf, name); + + driver_output(d->port, buf.buff, buf.index); + ei_x_free(&buf); + return NULL; +} + +void *erlXML_CharacterDataHandler(expat_data *d, + const XML_Char *s, + int len) +{ + ei_x_buff buf; + + ei_x_new_with_version(&buf); + ei_x_encode_tuple_header(&buf, 2); + ei_x_encode_atom(&buf, "xmlcdata"); + ei_x_encode_string_len(&buf, s, len); + + driver_output(d->port, buf.buff, buf.index); + ei_x_free(&buf); + 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("UTF-8"); + XML_SetUserData(d->parser, d); + + XML_SetStartElementHandler( + d->parser, (XML_StartElementHandler)erlXML_StartElementHandler); + XML_SetEndElementHandler( + d->parser, (XML_EndElementHandler)erlXML_EndElementHandler); + XML_SetCharacterDataHandler( + d->parser, (XML_CharacterDataHandler)erlXML_CharacterDataHandler); + + + return (ErlDrvData)d; +} + +static void expat_erl_stop(ErlDrvData handle) +{ + /* TODO: free parser */ + driver_free((char*)handle); +} + +static void expat_erl_output(ErlDrvData handle, char *buff, int bufflen) +{ + expat_data* d = (expat_data*)handle; + int res, errcode; + char *errstring; + ei_x_buff buf; + + /*buff[bufflen] = 0; + + fprintf(stderr, "RCVD: '%s'\n", buff); + fflush(stderr);*/ + + res = XML_Parse(d->parser, buff, bufflen, 0); + + if(!res) + { + errcode = XML_GetErrorCode(d->parser); + errstring = (char *)XML_ErrorString(errcode); + + ei_x_new_with_version(&buf); + ei_x_encode_tuple_header(&buf, 2); + ei_x_encode_atom(&buf, "xmlerror"); + ei_x_encode_tuple_header(&buf, 2); + ei_x_encode_long(&buf, errcode); + ei_x_encode_string(&buf, errstring); + + driver_output(d->port, buf.buff, buf.index); + ei_x_free(&buf); + } + + //driver_output(d->port, &res, 1); +} + + + +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 */ + expat_erl_output, /* 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, /* F_PTR control, port_command callback */ + NULL, /* F_PTR timeout, reserved */ + NULL /* F_PTR outputv, reserved */ +}; + +DRIVER_INIT(expat_erl) /* must match name in driver_entry */ +{ + return &expat_driver_entry; +} + + diff --git a/src/xml_stream.erl b/src/xml_stream.erl new file mode 100644 index 000000000..b21ae13c0 --- /dev/null +++ b/src/xml_stream.erl @@ -0,0 +1,77 @@ +%%%---------------------------------------------------------------------- +%%% File : xml_stream.erl +%%% Author : Alexey Shchepin +%%% Purpose : +%%% Created : 17 Nov 2002 by Alexey Shchepin +%%% Id : $Id$ +%%%---------------------------------------------------------------------- + +-module(xml_stream). +-author('alexey@sevcom.net'). +-vsn('$Revision$ '). + +-export([start/1, init/1, send_text/2]). + +start(CallbackPid) -> + spawn(?MODULE, init, [CallbackPid]). + +init(CallbackPid) -> + ok = erl_ddll:load_driver(".", expat_erl), + Port = open_port({spawn, expat_erl}, [binary]), + loop(CallbackPid, Port, []). + +loop(CallbackPid, Port, Stack) -> + receive + {Port, {data, Bin}} -> + %CallbackPid ! binary_to_term(Bin), + Data = binary_to_term(Bin), + loop(CallbackPid, Port, process_data(CallbackPid, Stack, Data)); + {From, {send, Str}} -> + Port ! {self(), {command, Str}}, + loop(CallbackPid, Port, Stack) + end. + +process_data(CallbackPid, Stack, Data) -> + case Data of + {xmlstart, {Name, Attrs}} -> + if Stack == [] -> + gen_fsm:send_event(CallbackPid, + {xmlstreamstart, Name, Attrs}); + true -> true + end, + [{xmlelement, Name, Attrs, []} | Stack]; + {xmlend, EndName} -> + case Stack of + [{xmlelement, Name, Attrs, Els} | Tail] -> + NewEl = {xmlelement, Name, Attrs, lists:reverse(Els)}, + Len = length(Tail), + if + Len > 1 -> add_subelement(NewEl, Tail); + Len == 1 -> + gen_fsm:send_event(CallbackPid, + {xmlstreamelement, NewEl}), + Tail; + Len == 0 -> + gen_fsm:send_event(CallbackPid, + {xmlstreamend, EndName}), + Tail + end + end; + {xmlcdata, CData} -> + add_subelement({xmlcdata, CData}, Stack); + {xmlerror, Err} -> gen_fsm:send_event(CallbackPid, + {xmlstreamerror, Err}) + end. + + +add_subelement(El, Stack) -> + case Stack of + [{xmlelement, Name, Attrs, Els} | Tail] -> + [{xmlelement, Name, Attrs, [El | Els]} | Tail]; + [] -> [] + end. + + +send_text(Pid, Text) -> + Pid ! {self(), {send, Text}}. +