Original
I was trying for days to adapt an open source Ejabberd module (mod_offline_http_post), working for previous versions, to the current version (20.04). For example, some parts are now important to be exported like mod_options and mod_depends. They were not part of the module source code. I added them. I still got an error.
.ejabberd-modules/sources/mod_offline_http_post/src/mod_offline_http_post.erl
%% name of module must match file name
%% Update: info@ph-f.nl
-module(mod_offline_http_post).
-author("dev@codepond.org").
-behaviour(gen_mod).
-export([start/2, stop/1, depends/2, mod_options/1, create_message/1, create_message/3]).
%% Required by ?INFO_MSG macros
-include("logger.hrl").
-include("scram.hrl").
-include("xmpp.hrl").
start(_Host, _Opt) ->
?INFO_MSG("mod_offline_http_post loading", []),
inets:start(),
?INFO_MSG("HTTP client started", []),
ejabberd_hooks:add(offline_message_hook, _Host, ?MODULE, create_message, 1).
stop (_Host) ->
?INFO_MSG("stopping mod_offline_http_post", []),
ejabberd_hooks:delete(offline_message_hook, _Host, ?MODULE, create_message, 1).
depends(_Host, _Opts) ->
[].
mod_options(_Host) ->
[].
create_message({Action, Packet} = Acc) when (Packet#message.type == chat) and (Packet#message.body /= []) ->
[{text, _, Body}] = Packet#message.body,
post_offline_message(Packet#message.from, Packet#message.to, Body, Packet#message.id),
Acc;
create_message(Acc) ->
Acc.
create_message(_From, _To, Packet) when (Packet#message.type == chat) and (Packet#message.body /= []) ->
Body = fxml:get_path_s(Packet, [{elem, list_to_binary("body")}, cdata]),
MessageId = fxml:get_tag_attr_s(list_to_binary("id"), Packet),
post_offline_message(_From, _To, Body, MessageId),
ok.
post_offline_message(From, To, Body, MessageId) ->
?INFO_MSG("Posting From ~p To ~p Body ~p ID ~p~n",[From, To, Body, MessageId]),
Token = gen_mod:get_module_opt(To#jid.lserver, ?MODULE, auth_token, fun(S) -> iolist_to_binary(S) end, list_to_binary("")),
PostUrl = gen_mod:get_module_opt(To#jid.lserver, ?MODULE, post_url, fun(S) -> iolist_to_binary(S) end, list_to_binary("")),
ToUser = To#jid.luser,
FromUser = From#jid.luser,
Vhost = To#jid.lserver,
case gen_mod:get_module_opt(To#jid.lserver, ?MODULE, confidential, false) of
true -> Data = string:join(["to=", binary_to_list(ToUser), "&from=", binary_to_list(FromUser), "&vhost=", binary_to_list(Vhost), "&messageId=", binary_to_list(MessageId)], "");
false -> Data = string:join(["to=", binary_to_list(ToUser), "&from=", binary_to_list(FromUser), "&vhost=", binary_to_list(Vhost), "&body=", binary_to_list(Body), "&messageId=", binary_to_list(MessageId)], "")
end,
Request = {binary_to_list(PostUrl), [{"Authorization", binary_to_list(Token)}], "application/x-www-form-urlencoded", Data},
httpc:request(post, Request,[],[]),
?INFO_MSG("post request sent", []).
.ejabberd-modules/sources/mod_offline_http_post/conf/mod_offline_http_post.yml
modules:
mod_offline_http_post:
auth_token: "secret"
post_url: "http://SERVER_IP_ADDRESS/end_of_url"
confidential: false
### Local Variables:
### mode: yaml
### End:
### vim: set filetype=yaml tabstop=8
I can provide more details if needed.
Thank's in advance for your help.
EDIT 1
It seems it is working now... There is a log when a message is sent to an offline user and options are now recognized. I will post what I did to make it work, as an answer, once there is no more problem. This, for the other people.
The remaining problem is that I doubt that the module is called at all. The log I talked about is not from the module but probably from the Ejabberd router. I talked about it because there was nothing like this in the past. There is something positive going on, then. To make the matter more certain, it displays "Bad module" when I uninstall the module.
Here is the new erl code:
%% name of module must match file name
%% Update: info@ph-f.nl
-module(mod_offline_http_post).
-author("dev@codepond.org").
-behaviour(gen_mod).
-export([start/2,
stop/1,
depends/2,
mod_options/1,
mod_opt_type/1,
create_message/1,
create_message/3]).
%% Required by ?INFO_MSG macros
-include("logger.hrl").
-include("scram.hrl").
-include("xmpp.hrl").
start(_Host, _Opt) ->
?INFO_MSG("mod_offline_http_post loading", []),
inets:start(),
?INFO_MSG("HTTP client started", []),
ejabberd_hooks:add(offline_message_hook, _Host, ?MODULE, create_message, 1).
stop (_Host) ->
?INFO_MSG("stopping mod_offline_http_post", []),
ejabberd_hooks:delete(offline_message_hook, _Host, ?MODULE, create_message, 1).
depends(_Host, _Opts) ->
[].
mod_options(_Host) ->
[{auth_token, <<"secret">>},
{post_url, <<"http://example.com/test">>},
{confidential, false}].
mod_opt_type(auth_token) ->
fun iolist_to_binary/1;
mod_opt_type(post_url) ->
fun iolist_to_binary/1;
mod_opt_type(confidential) ->
fun (B) when is_boolean(B) -> B end.
create_message({Action, Packet} = Acc) when (Packet#message.type == chat) and (Packet#message.body /= []) ->
[{text, _, Body}] = Packet#message.body,
post_offline_message(Packet#message.from, Packet#message.to, Body, Packet#message.id),
Acc;
create_message(Acc) ->
Acc.
create_message(_From, _To, Packet) when (Packet#message.type == chat) and (Packet#message.body /= []) ->
Body = fxml:get_path_s(Packet, [{elem, list_to_binary("body")}, cdata]),
MessageId = fxml:get_tag_attr_s(list_to_binary("id"), Packet),
post_offline_message(_From, _To, Body, MessageId),
ok.
post_offline_message(From, To, Body, MessageId) ->
?DEBUG("Posting From ~p To ~p Body ~p ID ~p~n",[From, To, Body, MessageId]),
Token = gen_mod:get_module_opt(To#jid.lserver, ?MODULE, auth_token, fun(S) -> iolist_to_binary(S) end, list_to_binary("")),
PostUrl = gen_mod:get_module_opt(To#jid.lserver, ?MODULE, post_url, fun(S) -> iolist_to_binary(S) end, list_to_binary("")),
ToUser = To#jid.luser,
FromUser = From#jid.luser,
Vhost = To#jid.lserver,
case gen_mod:get_module_opt(To#jid.lserver, ?MODULE, confidential, false) of
true -> Data = string:join(["to=", binary_to_list(ToUser), "&from=", binary_to_list(FromUser), "&vhost=", binary_to_list(Vhost), "&messageId=", binary_to_list(MessageId)], "");
false -> Data = string:join(["to=", binary_to_list(ToUser), "&from=", binary_to_list(FromUser), "&vhost=", binary_to_list(Vhost), "&body=", binary_to_list(Body), "&messageId=", binary_to_list(MessageId)], "")
end,
Request = {binary_to_list(PostUrl), [{"Authorization", binary_to_list(Token)}], "application/x-www-form-urlencoded", Data},
httpc:request(post, Request,[],[]),
?DEBUG("post request sent", []).
EDIT 2
Here is a very promising case. The module is called. It's just that it crashes which is another very interesting case. Here is the error log:
2020-05-21 11:53:35.897 [error] <0.508.0>@ejabberd_hooks:safe_apply:240 Hook offline_message_hook crashed when running mod_offline_http_post:create_message/1:
** exception error: undefined function gen_mod:get_module_opt/5
in function mod_offline_http_post:post_offline_message/4 (/opt/ejabberd/.ejabberd-modules/sources/mod_offline_http_post/src/mod_offline_http_post.erl, line 63)
in call from mod_offline_http_post:create_message/1 (/opt/ejabberd/.ejabberd-modules/sources/mod_offline_http_post/src/mod_offline_http_post.erl, line 49)
in call from ejabberd_hooks:safe_apply/4 (src/ejabberd_hooks.erl, line 236)
in call from ejabberd_hooks:run_fold1/4 (src/ejabberd_hooks.erl, line 217)
in call from ejabberd_sm:route/1 (src/ejabberd_sm.erl, line 146)
in call from ejabberd_router:do_route/1 (src/ejabberd_router.erl, line 399)
in call from ejabberd_router:route/1 (src/ejabberd_router.erl, line 92)