]> granicus.if.org Git - ejabberd/commitdiff
Initial revision
authorAlexey Shchepin <alexey@process-one.net>
Mon, 18 Nov 2002 20:39:47 +0000 (20:39 +0000)
committerAlexey Shchepin <alexey@process-one.net>
Mon, 18 Nov 2002 20:39:47 +0000 (20:39 +0000)
SVN Revision: 2

src/Makefile [new file with mode: 0644]
src/ejabberd.erl [new file with mode: 0644]
src/ejabberd.hrl [new file with mode: 0644]
src/ejabberd_c2s.erl [new file with mode: 0644]
src/ejabberd_listener.erl [new file with mode: 0644]
src/expat_erl.c [new file with mode: 0644]
src/xml_stream.erl [new file with mode: 0644]

diff --git a/src/Makefile b/src/Makefile
new file mode 100644 (file)
index 0000000..ff57225
--- /dev/null
@@ -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 (file)
index 0000000..5f0fe02
--- /dev/null
@@ -0,0 +1,17 @@
+%%%----------------------------------------------------------------------
+%%% File    : ejabberd.erl
+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose : 
+%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
+%%% 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 (file)
index 0000000..35972c4
--- /dev/null
@@ -0,0 +1,17 @@
+%%%----------------------------------------------------------------------
+%%% File    : ejabberd.hrl
+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose : 
+%%% Created : 17 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
+%%% 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 (file)
index 0000000..21aef06
--- /dev/null
@@ -0,0 +1,219 @@
+%%%----------------------------------------------------------------------
+%%% File    : ejabberd_c2s.erl
+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose : 
+%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
+%%% 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,
+       "<stream:stream xmlns='jabber:client' "
+       "xmlns:stream='http://etherx.jabber.org/streams' "
+       "id='~s' from='~s'>"
+       ).
+
+%%%----------------------------------------------------------------------
+%%% 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 (file)
index 0000000..532a06f
--- /dev/null
@@ -0,0 +1,42 @@
+%%%----------------------------------------------------------------------
+%%% File    : ejabberd_listener.erl
+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose : 
+%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
+%%% 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 (file)
index 0000000..b330eca
--- /dev/null
@@ -0,0 +1,155 @@
+/* $Id$ */
+
+#include <stdio.h>
+#include <erl_driver.h>
+#include <ei.h>
+#include <expat.h>
+
+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 (file)
index 0000000..b21ae13
--- /dev/null
@@ -0,0 +1,77 @@
+%%%----------------------------------------------------------------------
+%%% File    : xml_stream.erl
+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose : 
+%%% Created : 17 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
+%%% 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}}.
+