« Posts under ejabberd

Using Pubsub service of Ejabberd for live data

The concept of real-time Web became more popular. Real-time information to be shared between sites. I made some models and will introduce it soon.

Primary language : Erlang
Protocol : XMPP over BOSH
XMPP server: Ejabberd (had Pubsub service)
Server database: CouchDB or Riak
Web host: Webmachine + Mochiweb behind Ngnix

Beta version at : http://stocktm.com

Testing new XEPs added on the Ejabberd 2.1.0 final version

+ XEP Support :

  • Added XEP-0059 Result Set Management (for listing rooms)
  • Added XEP-0082 Date Time
  • Added XEP-0085 Chat State Notifications
  • Added XEP-0157 Contact Addresses for XMPP Services
  • Added XEP-0158 CAPTCHA Forms (in MUC rooms)
  • Added STUN server, for XEP-0176: Jingle ICE-UDP Transport Method (skipped this XEP because my system to bad :D )
  • Added XEP-0199 XMPP Ping
  • Added XEP-0202 Entity Time
  • Added XEP-0203 Delayed Delivery
  • Added XEP-0227 Portable Import/Export Format for XMPP-IM Servers
  • Added XEP-0237 Roster Versioning

+ Test enviroment :

  • Server : CPU E2200 @ 2.20GHz x86_64bit 4GB Ram
  • OS : Ubuntu server distro with Linux kernel : 2.6.28-13-generic with GNOME desktop env.
  • Erlang R13B (erts-5.7.1) async-thread:1 kernel-poll:true
  • Ejabberd 2.1.0 final version (virtual host : cuonglb-desktop with one node : ejabberd@localhost)
  • Create two users for this test : user_a@cuonglb-desktop and user_b@cuonglb-desktop
  • PSI 0.12 with XML Console
  • Python 2.5.4 r254:67916 [GCC 4.3.3]
  • Twisted 8.2.0

We are starting with XEP-0199 XMPP Ping:
Open config file of ejabberd at ./conf/ejabberd.cfg and add mod_ping in modules terms (no options):

Erlang
    {modules,
     [
      {mod_adhoc,    []},
      ...
      {mod_ping,    []},
      ...
    ]
    }

Ok, now restart ejabberd server : ./bin/ejabberdctrl restart
Start PSI -> General -> Account setup -> Add account of user a : user_a@cuonglb-desktop and set status is online.
Start PSI:XML Console -> right click on the account user_a@cuonglb-desktop from accounts list and click XML Console.
In XML Console dialog click Enable and click XML Input … button and input xml iq stanza bellow :

Client-to-Server ping

XML
<iq from="cuonglb@cuonglb-desktop/cuonglb-desktop" type="get" to="cuonglb-desktop">
  <ping xmlns='urn:xmpp:ping'/>
</iq>

See on XML Console :

XEP-0157: Contact Addresses for XMPP Services

Queries server with iq stanza :

XML
<iq from='cuonglb@jabber.org/cuongle-lappy'
    to='jabber.org'
    id='disco1'
    type='get'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>

My server communicates info

XML
<iq from="jabber.org" type="result" to="cuonglb@jabber.org/cuongle-lappy" id="disco1" >
<query xmlns="http://jabber.org/protocol/disco#info">
<identity category="server" type="im" name="ejabberd" />
<feature var="http://ejabberd.jabberstudio.org/protocol/configure" />
<feature var="http://jabber.org/protocol/commands" />
<feature var="http://jabber.org/protocol/disco#info" />
<feature var="http://jabber.org/protocol/disco#items" />
<feature var="http://jabber.org/protocol/disco#publish" />
<feature var="iq" />
<feature var="jabber:iq:last" />
<feature var="jabber:iq:register" />
<feature var="jabber:iq:time" />
<feature var="jabber:iq:version" />
<feature var="presence" />
<feature var="presence-invisible" />
<feature var="vcard-temp" />
<feature var="http://jabber.org/protocol/commands" />
</query>
</iq>

try to develop a sample service using ejabberd router

Today free, try to develop a sample service using ejabberd router.
+ Requirements:

  • Erlang/OTP, XMPP/Jabber protocol and Module development for Ejabberd skills (of course).
  • Jabber clients for testing this service : PSI, Pidgin, Tkabber … I use all :D
  • Mastery of two modules: gen_mod and gen_server behaviour
  • Emacs with Erlang mode (optional, but recommended). :D

+ Using Emacs to creat a file with file name is : mod_sample.erl
+ OK, now we are using emacs/mode to code generation gen_server’s skeleton :D -> Erlang/Skeletons/gen_server

+ Service name : Sample (sample.example.com) with example.com is virtual host.
+ Scope : I do not think but it could be an ejabberd service pattern :D

Erlang
%%%-------------------------------------------------------------------
%%% File    : mod_sample.erl
%%% Author  : Cuong Le <>
%%% Description : ejabberd service using ejabberd_router
%%%
%%% Created : 24 Nov 2009 by Cuong Le <>
%%%-------------------------------------------------------------------
-module(mod_sample).
 
-behaviour(gen_server).
-behaviour(gen_mod).
 
%% API
-export([start_link/2]).
 
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
   terminate/2, code_change/3]).
 
%% gen_mod callbacks
-export([start/2,stop/1]).
 
-define(PROCNAME, ejabberd_mod_sample).
 
-record(state, {
host
}).
 
-include("ejabberd.hrl").
-include("jlib.hrl").
 
%%====================================================================
%% API
%%====================================================================
 
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
 
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
 
%%====================================================================
%% gen_mod callbacks
%%====================================================================
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]}, permanent, 1000, worker, [?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
 
stop(Host)->
    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
    supervisor:terminate_child(ejabberd_sup, Proc),
    supervisor:delete_child(ejabberd_sup, Proc).
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%%                         {ok, State, Timeout} |
%%                         ignore               |
%%                         {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
 
init([Host, Opts]) ->
 
MyHost = gen_mod:get_opt_host(Host, Opts, "sample.@HOST@"),
 
ejabberd_router:register_route(MyHost),
 
{ok, #state{
host = MyHost
}}.
 
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                      {reply, Reply, State, Timeout} |
%%                                      {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, Reply, State} |
%%                                      {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
 
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
 
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
 
handle_cast(_Msg, State) ->
{noreply, State}.
 
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%%                                       {noreply, State, Timeout} |
%%                                       {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
 
handle_info({route, From, To, Packet}, #state{host = Host} = State) ->
    case catch do_route(From, To, Packet, Host) of
  {'EXIT', Reason}->
      ?ERROR_MSG("~p",[Reason]);
  _ ->
      ok
    end,
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
 
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
 
terminate(_Reason, State) ->
ejabberd_router:unregister_route(State#state.host),
ok.
 
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
 
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
 
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
 
do_route(From, To, Packet, Host)->
    {xmlelement, Name, Attrs, _Els} = Packet,
    case To of 
  #jid{luser = "", lresource = ""} ->
      case Name of
    "iq" ->        
        case jlib:iq_query_info(Packet) of
      #iq{type=get, xmlns = ?NS_DISCO_INFO, sub_el = SubEl, lang = Lang} = IQ ->
          {xmlelement, _, QAttrs, _} = SubEl,
          Node = xml:get_attr_s("node", QAttrs),
          Res = case iq_disco_info(Host, Node, From, Lang) of
              {result, IQRes} ->
            jlib:iq_to_xml(
              IQ#iq{type = result,
              sub_el = [{xmlelement, "query",
                   QAttrs, IQRes}]});
              {error, Error} ->
            jlib:make_error_reply(Packet, Error)
          end,
          ejabberd_router:route(To, From, Res);            
      #iq{} ->
          Err = jlib:make_error_reply(
            Packet,
            ?ERR_FEATURE_NOT_IMPLEMENTED),
          ejabberd_router:route(To, From, Err);          
      _ ->            
          ok
        end;       
    _ ->
        ok
      end;
  _ ->
      ok
    end.
 
string_to_node(SNode) ->
    string:tokens(SNode, "/").
 
iq_disco_info(Host, SNode, From, _Lang) ->
    Node = string_to_node(SNode),
    case Node of
  [] ->
      {result,
       [{xmlelement, "identity",
         [{"category", "test"},
    {"type", "service"},
    {"name", "sample"}], []}]
      };
  _ ->
      ok  
    end.

To be continue … :D