0

I wrote the following module for MongooseIM but nothing is posted to the PHP file.

start(_Host, _Opt) -> 
    inets:start(),
    ejabberd_hooks:add(user_send_packet, _Host, ?MODULE, fetchPacketData, 50).
stop (_Host) -> 
    ejabberd_hooks:delete(user_send_packet, _Host, ?MODULE, fetchPacketData, 50).
fetchPacketData(_From, _To, Packet) ->
    To = xml:get_tag_attr_s(<<"to">>, Packet),
    httpc:request(post, {"http://example.com/receiver.php",[],
            "application/x-www-form-urlencoded",
            lists:concat(["To=",To,"&Type=1","&Body=ABC"])}, [], []).

I was able to successfully implement the module after following erszcz's suggestion(Please see below). Below is the code i used. Hope it helps someone else too :)

start(Host, _Opts)->
  inets:start(),
  ejabberd_hooks:add(user_send_packet, Host, ?MODULE, sendMessage, 50),
  ok.
stop(Host)->
  ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, sendMessage, 50),
  ok.
sendMessage(_From, _To, Packet) ->
  case xml:get_tag_attr_s(<<"type">>, Packet) of
    <<"chat">> ->
      To = lists:flatten(io_lib:format("~s", [xml:get_tag_attr_s(<<"to">>, Packet)])),
      ** post variables to PHP file using httpc:request **
      ok;
    _ ->
      ok
    end.
Vitul Goyal
  • 611
  • 7
  • 19

1 Answers1

0

offline_message_hook is only called when the server is routing a message to a user who is not online. user_send_packet is run every time the server receives a stanza from a client. This might explain why the handler is not run, though it depends on how you test. There's an article with one section describing some hooks in MongooseIM available on the official wiki.

As for problems with retrieving packet attributes, either logging the incoming packet for inspection or using dbg in the server Erlang shell to trace the actual calls done by your module might be the way to tell what's happening.


An example session with dbg debugging the problem might look like this:

(mongooseim@localhost)1> dbg:tracer().
{ok,<0.570.0>}
(mongooseim@localhost)2> dbg:p(all, call).
{ok,[{matched,mongooseim@localhost,279}]}
(mongooseim@localhost)3> dbg:tpl(mod_test, x).
{ok,[{matched,mongooseim@localhost,5},{saved,x}]}
(mongooseim@localhost)4> (<0.576.0>) call mod_test:fetchPacketData({jid,<<"alice">>,<<"localhost">>,<<"escalus-default-resource">>,<<"alice">>,
     <<"localhost">>,<<"escalus-default-resource">>},{jid,<<"alice">>,<<"localhost">>,<<>>,<<"alice">>,<<"localhost">>,<<>>},{xmlel,<<"presence">>,[{<<"xml:lang">>,<<"en">>}],[]})
(<0.576.0>) exception_from {mod_test,fetchPacketData,3} {error,function_clause}
2015-03-15 11:46:03.028 [error] <0.576.0>@ejabberd_hooks:run1:240 {function_clause,[{lists,thing_to_list,[<<>>],[{file,"lists.erl"},{line,601}]},{lists,flatmap,2,[{file,"lists.erl"},{line,1248}]},{lists,flatmap,2,[{file,"lists.erl"},{line,1248}]},{mod_test,fetchPacketData,3,[{file,"src/mod_test.erl"},{line,15}]},{safely,apply,3,[{file,"src/safely.erl"},{line,19}]},{ejabberd_hooks,run1,3,[{file,"src/ejabberd_hooks.erl"},{line,236}]},{ejabberd_c2s,session_established2,2,[{file,"src/ejabberd_c2s.erl"},{line,1063}]},{p1_fsm_old,handle_msg,10,[{file,"src/p1_fsm_old.erl"},{line,542}]}]}
    Running hook: {user_send_packet,[{jid,<<"alice">>,<<"localhost">>,<<"escalus-default-resource">>,<<"alice">>,<<"localhost">>,<<"escalus-default-resource">>},{jid,<<"alice">>,<<"localhost">>,<<>>,<<"alice">>,<<"localhost">>,<<>>},{xmlel,<<"presence">>,[{<<"xml:lang">>,<<"en">>}],[]}]} 
    Callback: mod_test:fetchPacketData

We see that the handler errors out with function_clause when calling lists:thing_to_list(<<>>). The empty binary is a result of xml:get_tag_attr_s/2 when the attribute asked for is not found. lists:thing_to_list/1 is called to convert each parameter of lists:concat/1 to a list, but it's not possible to convert an empty binary <<>> to a list, hence the crash.

Match on the result of xml:get_tag_attr_s/2 and craft your logic appropriately to each case: when the attribute is found and when it's not there.


I don't know how to start a module in dbg. I tried what you had shared above and I think you missed the 4th command which could be an example of how to initiate a module.

This is a raw dump of my console without any edits - I did not miss any part. You don't "start a module in dbg." You simply start a module the usual way and then use dbg from the server shell.

What I did was I took your example code, put it into apps/ejabberd/src/mod_test.erl file and built a release. After that you can enable the module either in ejabberd.cfg of the release (look for the modules section and do it similarly as examples there show) or you can start the server in live mode with mongooseimctl live and start the module manually with gen_mod:start_module(<<"localhost">>, mod_test, []) (where <<"localhost">> is just an example XMPP domain - substitute your own suitable domain there).

When the module is running (can be checked with gen_mod:is_loaded(<<"your-xmpp-domain">>, mod_name_goes_here)) you have to enable dbg. This is shown in the listing I added previously. I won't delve into describing how to use dbg as a very good introduction is already available on StackOverflow.


an example of how to test if an attribute exists or not

case xml:get_tag_attr_s(<<"some-attribute">>, Packet) of
    <<>> ->
        %% attribute does not exist, as get_tag_attr_s returned the default value
        ok;
    <<"some-value">> ->
        %% do something sensible with <<"some-value">>
        ok
end

Alternatively, you can use exml which is also part of MongooseIM (but not original ejabberd) and is more explicit about not finding the attribute you ask for:

case exml_query:attr(Packet, <<"some-attribute">>) of
    undefined ->
        %% not found
        ok;
    <<"some-value">> ->
        %% do something
        ...
end
Community
  • 1
  • 1
erszcz
  • 1,630
  • 10
  • 16
  • thank you erszcz. i tried dbg but couldn't solve the problem. can i POST the Packet as it is to the PHP file and check via PHP what is wrong? (i tried this but nothing was posted to PHP) – Vitul Goyal Mar 15 '15 at 13:56
  • The code as you have it now has an error which crashes your function and `httpc:request` never gets called - I described the error above. You can post the whole packet to the PHP app, but you have to serialize it to a string beforehand. Why wasn't your dbg session successful? – erszcz Mar 15 '15 at 20:32
  • i dont know how to start a module in dbg. i tried what you had shared above and i think you missed the 4th command which could be an example of how to initiate a module. – Vitul Goyal Mar 16 '15 at 06:25
  • also if you could show an example of how to test if an attribute exists or not. – Vitul Goyal Mar 16 '15 at 06:49
  • I edited the original answer addressing your questions. – erszcz Mar 16 '15 at 10:53
  • thanks erszcz for the detailed explanation. i have successfully implemented the test to check if the attributes exists or not. also, i have used the below method to convert the values to a string and then post it to PHP and it is working fine. To = lists:flatten(io_lib:format("~s", [xml:get_tag_attr_s(<<"to">>, Packet)])). Thanks a lot for the help. :) – Vitul Goyal Mar 17 '15 at 07:57