1

I am new in erlang and ejabbered/mongooseIM. I am trying to write a very simple mongoose module that will add an extra child element to the packets before sending. Below is my code:

-module(mod_test).

-behavior(gen_mod).


-export([start/2, stop/1]).
-export([add_child/1]).

-include("ejabberd.hrl").

start(Host, Opts) ->
    ejabberd_hooks:add(filter_packet, Host, ?MODULE, add_child, 0),
    ?DEBUG(" MOD_TEST Started",[]),
    ok.

stop(Host) ->
    ejabberd_hooks:delete(filter_packet, Host, ?MODULE, add_child, 0),
    ok.

add_child({From, To, XML} = Packet) ->
    Tag = {"a","b"},
    NewPacket = xml:append_subtags(Packet, [Tag]),
    ?INFO_MSG("  To party: ~p~n",[To]),
    NewPacket.

I can compile the code with few warnings

mod_test.erl:3: Warning: behaviour gen_mod undefined
mod_test.erl:11: Warning: variable 'Opts' is unused
mod_test.erl:20: Warning: variable 'From' is unused
mod_test.erl:20: Warning: variable 'XML' is unused

Then when I add the module and run it it gets started but it doesnt make any changes to the packets and doesnt generate any logs either.

Another issue is, if I add a log within my start function, it gets compiled but I see errors while starting the module

2015-03-03 16:36:34.772 [critical] <0.200.0>@gen_mod:start_module:94 Problem starting the module mod_test for host <<"localhost">>
 options: []
 error: undef
[{lager,info,["  mod_test starting ...",[[]]],[]},
 {mod_test,start,2,[{file,"mod_test.erl"},{line,13}]},
 {gen_mod,start_module,3,[{file,"src/gen_mod.erl"},{line,83}]},
 {lists,foreach,2,[{file,"lists.erl"},{line,1336}]},
 {ejabberd_app,start,2,[{file,"src/ejabberd_app.erl"},{line,69}]},
 {application_master,start_it_old,4,
                     [{file,"application_master.erl"},{line,272}]}]
2015-03-03 16:36:34.773 [critical] <0.200.0>@gen_mod:start_module:99 ejabberd initialization was aborted because a module start failed.
The trace is [{lager,info,["  mod_test starting ...",[[]]],[]},{mod_test,start,2,[{file,"mod_test.erl"},{line,13}]},{gen_mod,start_module,3,[{file,"src/gen_mod.erl"},{line,83}]},{lists,foreach,2,[{file,"lists.erl"},{line,1336}]},{ejabberd_app,start,2,[{file,"src/ejabberd_app.erl"},{line,69}]},{application_master,start_it_old,4,[{file,"application_master.erl"},{line,272}]}].

Crash dump was written to: erl_crash.dump
Problem starting the module mod_test for host <<"localhost">>
 options: []
 error: undef
[{lager,info,["  mod_xyz starting ...",[[]]],[]},
 {mod_test,start,2,[{file,"mod_timetagg

What wrong am I doing?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Kamrul Khan
  • 3,260
  • 4
  • 32
  • 59

2 Answers2

3

So your example is actually a bit tricky, because of how the filter_packet hook works. You picked the worst hook to register for on your first attempt ;)

If you look into ejabberd_router:do_route, you'll see that filter_packet is run without a Host parameter -- it is a global hook, so when you register your add_child function for a particular Host, it will essentially be ignored.

Try the following:

-module(mod_test).
-behavior(gen_mod).

-export([start/2, stop/1]).
-export([add_child/1]).

-include_lib("ejabberd/include/ejabberd.hrl").
-include_lib("exml/include/exml.hrl").

start(Host, Opts) ->
    ejabberd_loglevel:set_custom(?MODULE, 5),
    ejabberd_hooks:add(filter_local_packet, Host, ?MODULE, add_child, 1),
    ?DEBUG(" MOD_TEST Started",[]),
    ok.

stop(Host) ->
    ejabberd_hooks:delete(filter_local_packet, Host, ?MODULE, add_child, 1),
    ok.

add_child({From, To, Element} = HookData) ->
    ?DEBUG("Filtering ~p~n", [HookData]),
    case Element#xmlel.name of
        <<"message">> ->
            Tag = #xmlel{name = <<"added-tag">>, attrs = [], children = []},
            NewElement = xml:append_subtags(Element, [Tag]),
            ?DEBUG("will return new el: ~p", [NewElement]),
            {From, To, NewElement};
        _ ->
            ?DEBUG("will pass old el: ~p", [Element]),
            HookData
    end.

Registering for filter_local_packet on your given Host will now work, and all incoming stanzas will be passed to your function. It is important to remember that adding spurious tags to all stanzas may break things, so the above code will only add a <added-tag> element to <message> stanzas.

Use the example above and work from there.

Good luck!

Simon Zelazny
  • 386
  • 1
  • 5
  • just a question. I tried the same code with hook filter_packet changing Host into global. It did call add_child function and updated the packets but didnt send the NewPacket (as I didnt see the in packet trace). Any idea why ? – Kamrul Khan Mar 05 '15 at 19:44
  • can you show the code? Remember that in Erlang you can't update things in place -- maybe you're not returning the proper value? – Simon Zelazny Mar 06 '15 at 08:31
1

The error is undef, which means that a function was called that isn't defined/exported. The stacktrace shows that the function in question is lager:info/2.

Lager (the logging library that handles your ?INFO_MSG) has a special quirk in that your code would call non-existent functions, but the code gets transformed by a parse transform before being compiled. It looks like this didn't happen for you.

The rebar.config file in apps/ejabberd in the MongooseIM tree contains {parse_transform, lager_transform} in erl_opts, which asks the compiler to apply the parse transform. I'd suggest putting mod_test.erl into apps/ejabberd/src and build the entire MongooseIM tree; that would ensure that your file gets built with the correct options.

legoscia
  • 39,593
  • 22
  • 116
  • 167
  • Hi, thanks for your reply. I have rebuild entire mongoose now I can my module is starting up propoerly and I can see the debug log MOD_TEST Started. But still the add_child function is not being triggered and I dont even see the log I put inside the add_child function. Any idea why ? – Kamrul Khan Mar 04 '15 at 19:48