I am writing an ejabberd hook to create an outgoing SSL connection with Apple Push Notification Server. I have tested the send method outside of ejabberd (in the erlang interpreter) and can verify that it works. I am unsure as to why this module:
-module(mod_http_offline).
-author("Joseph Martin").
%% Every ejabberd module implements the gen_mod behavior
%% The gen_mod behavior requires two functions: start/2 and stop/1
-behaviour(gen_mod).
%% public methods for this module
-export([start/2, stop/1, create_message/3]).
%% included for writing to ejabberd log file
-include("ejabberd.hrl").
%% ejabberd functions for JID manipulation called jlib.
-include("jlib.hrl").
start(_Host, _Opt) ->
?INFO_MSG("mod_http_offline loading", []),
%send("Test Push"),
ejabberd_hooks:add(offline_message_hook, _Host, ?MODULE, create_message, 50).
stop (_Host) ->
?INFO_MSG("stopping mod_http_offline", []),
ejabberd_hooks:delete(offline_message_hook, _Host, ?MODULE, create_message, 50).
create_message(_From, _To, Packet) ->
Type = xml:get_tag_attr_s("type", Packet),
FromS = xml:get_tag_attr_s("from", Packet),
ToS = xml:get_tag_attr_s("to", Packet),
Body = xml:get_path_s(Packet, [{elem, "body"}, cdata]),
if (Type == "chat") ->
send("You did it!","10","Chime")
end.
% All argument fields expect a string
send(Msg) ->
send_pn([{alert, Msg}]).
% send a string and and a bagde number
send(Msg, Badge) ->
send_pn([{alert, Msg}, {badge, Badge}]).
% send a string, a badge number and play a sound
send(Msg, Badge, Sound) ->
send_pn([{alert, Msg}, {badge, Badge}, {sound, Sound}]).
send_pn(Msg) ->
% start ssl
ssl:start(),
% application:start(ssl),
% socket configuration, may need to increase the timeout if concurrency becomes an issue
Address = "gateway.sandbox.push.apple.com",
% Address = "gateway.push.apple.com",
Port = 2195,
Cert = "/Users/joemartin/Desktop/PushNotificationCertificates/PushChatCert.pem",
Key = "/Users/joemartin/Desktop/PushNotificationCertificates/PushChatKey.pem",
Options = [{certfile, Cert}, {keyfile, Key}, {password, "mypassword"}, {mode, binary}, {verify, verify_none}],
Timeout = 5000,
case ssl:connect(Address, Port, Options, Timeout) of
{ok, Socket} ->
% Convert the device token from hex to int to binary
Token = "3eca19d7...mydevicetokenstring",
TokenNum = erlang:list_to_integer(Token, 16),
TokenBin = <<TokenNum:32/integer-unit:8>>,
% Construct the protocol packet
PayloadString = create_json(Msg),
Payload = list_to_binary(PayloadString),
PayloadLength = byte_size(Payload),
Packet = <<0:8, 32:16, TokenBin/binary, PayloadLength:16, Payload/binary>>,
% Send the packet then close the socket
ssl:send(Socket, Packet),
ssl:close(Socket),
% Return the PayloadString (for debugging purposes)
PayloadString;
{error, Reason} ->
?INFO_MSG("THE SSL CONNECTION FAILED", []),
Reason
end.
% helper for creating json
create_json(List) ->
lists:append(["{\"aps\":{", create_keyvalue(List), "}}"]).
create_keyvalue([Head]) ->
create_pair(Head);
create_keyvalue([Head|Tail]) ->
lists:append([create_pair(Head), ",", create_keyvalue(Tail)]).
create_pair({Key, Value}) ->
lists:append([add_quotes(atom_to_list(Key)), ":", add_quotes(Value)]).
add_quotes(String) ->
lists:append(["\"", String, "\""]).
Creates this stacktrace in my error log:
=ERROR REPORT==== 2013-12-31 16:23:57 ===
E(<0.1558.0>:ejabberd_hooks:294) : {undef,
[{tls,connect,
["gateway.sandbox.push.apple.com",2195,
[{certfile,
"/Users/joemartin/Desktop/PushNotificationCertificates/PushChatCert.pem"},
{keyfile,
"/Users/joemartin/Desktop/PushNotificationCertificates/PushChatKey.pem"},
{password,"mypassword"},
{mode,binary},
{verify,verify_none}],
5000],
[]},
{mod_http_offline,send_pn,1,
[{file,"mod_http_offline.erl"},
{line,69}]},
{ejabberd_hooks,run1,3,
[{file,"ejabberd_hooks.erl"},
{line,290}]},
{ejabberd_sm,route,3,
[{file,"ejabberd_sm.erl"},{line,87}]},
{ejabberd_local,route,3,
[{file,"ejabberd_local.erl"},
{line,120}]},
{ejabberd_router,route,3,
[{file,"ejabberd_router.erl"},
{line,68}]},
{ejabberd_c2s,session_established2,2,
[{file,"ejabberd_c2s.erl"},{line,1122}]},
{p1_fsm,handle_msg,10,
[{file,"p1_fsm.erl"},{line,544}]}]}
I have read that ejabberd 2 has old-style SSL disabled by default, but thought that was only for incoming client connections. I also haven't found a good example of a hook that makes an outgoing SSL connection. I also realize the name mod_http_offline is a misnomer. Any ideas?
Thank you,
Joe