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
- Mastery of two modules: gen_mod and gen_server behaviour
- Emacs with Erlang mode (optional, but recommended).
+ 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
-> 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
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 …