mikutel: initial commit
This commit is contained in:
commit
6d902412d0
7
rebar.config
Normal file
7
rebar.config
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{erl_opts, [debug_info]}.
|
||||
{deps, []}.
|
||||
|
||||
{shell, [
|
||||
%% {config, "config/sys.config"},
|
||||
{apps, [mikuphone]}
|
||||
]}.
|
||||
1
rebar.lock
Normal file
1
rebar.lock
Normal file
|
|
@ -0,0 +1 @@
|
|||
[].
|
||||
101
src/mikuivr.erl
Normal file
101
src/mikuivr.erl
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
-module(mikuivr).
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([start_link/1]).
|
||||
-export([init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2,
|
||||
code_change/3,
|
||||
terminate/2]).
|
||||
|
||||
-record(state, {socket, status, number, callerid}).
|
||||
|
||||
start_link(Args) ->
|
||||
gen_server:start_link(?MODULE, Args, []).
|
||||
|
||||
init([Socket]) ->
|
||||
gen_server:cast(self(), accept),
|
||||
{ok, #state{socket=Socket, status=ok, number=""}}.
|
||||
|
||||
handle_cast(accept, State = #state{socket=ListenSocket}) ->
|
||||
{ok, AcceptSocket} = gen_tcp:accept(ListenSocket),
|
||||
mikuvisor:start_socket(),
|
||||
%% send(AcceptSocket, "S,sranie", []),
|
||||
%% send(AcceptSocket, "H,nasrane", []),
|
||||
send(AcceptSocket, "G,${CALLERID(num)}", []),
|
||||
{noreply, State#state{socket=AcceptSocket}};
|
||||
|
||||
handle_cast(_, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({tcp, Socket, Message}, State) ->
|
||||
handle_message(Message, State),
|
||||
{noreply, State};
|
||||
|
||||
handle_info({tcp_closed, _Socket}, State) ->
|
||||
{stop, normal, State};
|
||||
|
||||
handle_info({tcp_error, _Socket}, State) ->
|
||||
{stop, normal, State};
|
||||
|
||||
handle_info(E, State) ->
|
||||
io:fwrite("unexpected: ~p~n", [E]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_call(_E, _From, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _Tab) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVersion, Tab, _Extra) ->
|
||||
{ok, Tab}.
|
||||
|
||||
send(Socket, Str, Args) ->
|
||||
gen_tcp:send(Socket, io_lib:format(Str++"~n", Args)).
|
||||
|
||||
handle_message(Message, State) ->
|
||||
case Message of
|
||||
"G"++Rest -> handle_variable_receive(Rest, State);
|
||||
"*"++_ -> enable_number_mode(Message, State);
|
||||
"0"++_ -> handle_digit(Message, State);
|
||||
"1"++_ -> handle_digit(Message, State);
|
||||
"2"++_ -> handle_digit(Message, State);
|
||||
"3"++_ -> handle_digit(Message, State);
|
||||
"4"++_ -> handle_digit(Message, State);
|
||||
"5"++_ -> handle_digit(Message, State);
|
||||
"6"++_ -> handle_digit(Message, State);
|
||||
"7"++_ -> handle_digit(Message, State);
|
||||
"8"++_ -> handle_digit(Message, State);
|
||||
"9"++_ -> handle_digit(Message, State);
|
||||
"#"++_ -> disable_number_mode(Message, State);
|
||||
_ -> {noreply, State}
|
||||
end.
|
||||
|
||||
handle_variable_receive(Variable, State) ->
|
||||
["${CALLERID(num)}", CallerID] = strings:lexemes(Variable, "="),
|
||||
{noreply, State#state{callerid = CallerID}}.
|
||||
|
||||
handle_digit(Message, State = #state{status=get_more_digits}) ->
|
||||
Digit = strings:slice(Message, 1, 1),
|
||||
Number = State#state.number,
|
||||
case length(Number) of
|
||||
4 -> disable_number_mode(Message, State);
|
||||
_ -> {noreply, State#state{number = Number ++ Digit}}
|
||||
end;
|
||||
|
||||
handle_digit(Message, State = #state{status=ok}) ->
|
||||
{noreply, State}.
|
||||
|
||||
enable_number_mode(_, State) ->
|
||||
{noreply, State#state{status=get_more_digits, number=""}}.
|
||||
|
||||
disable_number_mode(_, State) ->
|
||||
register(State#state.callerid, State#state.number),
|
||||
{noreply, State#state{status=ok, number=""}}.
|
||||
|
||||
register(CallerID, Number) ->
|
||||
[Supervisor | _] = mikuvisor:get_ancestors(self()),
|
||||
{ok, {_, Mikutel, _, _}} = supervisor:which_child(Supervisor, mikutel),
|
||||
gen_server:cast(Mikutel, {register, CallerID, Number}).
|
||||
16
src/mikuphone.app.src
Normal file
16
src/mikuphone.app.src
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{application, mikuphone, [
|
||||
{description, "The Mikuphone"},
|
||||
{vsn, "1.0.0"},
|
||||
{registered, []},
|
||||
{mod, {mikuphone, [
|
||||
[2949, "ommhost", 12622, "user", "password", []]
|
||||
]}},
|
||||
{applications, [
|
||||
kernel,
|
||||
stdlib
|
||||
]},
|
||||
{env, []},
|
||||
{modules, []},
|
||||
{licenses, ["MIT"]},
|
||||
{links, []}
|
||||
]}.
|
||||
16
src/mikuphone.erl
Normal file
16
src/mikuphone.erl
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
-module(mikuphone).
|
||||
-behaviour(application).
|
||||
-export([start/2,stop/1]).
|
||||
|
||||
%% 2949 is port for ivr
|
||||
%% 12622 port for omm
|
||||
|
||||
start(_StartType, StartArgs) ->
|
||||
%% mikuvisor:start_link(IvrPort),
|
||||
ssl:start(),
|
||||
mikuvisor:start_link(StartArgs).
|
||||
%% spawn(mikutel, connect, [OMMAddress, OMMPort, Username, Password]).
|
||||
%% mikutel:start(OMMAddress, OMMPort, OMMTLS, Username, Password).
|
||||
|
||||
stop(_) ->
|
||||
ok.
|
||||
123
src/mikutel.erl
Normal file
123
src/mikutel.erl
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
-module(mikutel).
|
||||
-behaviour(gen_server).
|
||||
-export([init/1,
|
||||
handle_cast/2,
|
||||
handle_call/3,
|
||||
handle_info/2,
|
||||
code_change/3,
|
||||
terminate/2,
|
||||
start_link/1]).
|
||||
|
||||
-record(state, {socket, modulus, exponent}).
|
||||
|
||||
%% TODO: add fallback TCP Connection
|
||||
|
||||
start_link(Args) ->
|
||||
gen_server:start_link(?MODULE, Args, []).
|
||||
|
||||
init([Host, Port, Username, Password, _]) ->
|
||||
Ciphers = ssl:prepend_cipher_suites([ssl:str_to_suite("AES256-GCM-SHA384")],
|
||||
ssl:cipher_suites(default, 'tlsv1.2')),
|
||||
{ok, Socket} = ssl:connect(Host, Port, [{verify, verify_none},
|
||||
{versions, ['tlsv1.2']},
|
||||
{ciphers, Ciphers},
|
||||
{active, true}]),
|
||||
%% {ok, Socket} = gen_tcp:connect(Host, Port, []),
|
||||
%% Arbitrary sleeps because something drops my Erlang messages??
|
||||
timer:send_interval(60000, ping),
|
||||
login(Socket, Username, Password),
|
||||
timer:sleep(100),
|
||||
set_dect_subscription_mode(Socket, 'Configured'),
|
||||
timer:sleep(100),
|
||||
subscribe(Socket, [{'PPUserCnf', [{uid, -1}]},
|
||||
{'PPDevCnf', [{ppn, -1}]}]),
|
||||
{ok, #state{socket=Socket}}.
|
||||
|
||||
handle_cast(_, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_call(_E, _From, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_cast({register, CallerID, Number}, State) ->
|
||||
%% TODO
|
||||
|
||||
handle_info({subscribe, Events}, State) ->
|
||||
subscribe(State#state.socket, Events);
|
||||
|
||||
handle_info({ssl, Socket, Message}, State) ->
|
||||
handle_response(Message, State);
|
||||
|
||||
handle_info({ssl_closed, Socket}, State) ->
|
||||
ssl:close(Socket),
|
||||
{stop, normal, State};
|
||||
|
||||
%% handle_info({tcp, Socket, Message}, State) ->
|
||||
%% handle_response(Message, State);
|
||||
|
||||
%% handle_info({tcp_closed, Socket}, State) ->
|
||||
%% gen_tcp:close(Socket),
|
||||
%% {stop, normal, State};
|
||||
|
||||
handle_info(ping, State = #state{socket=Socket}) ->
|
||||
Data = {'Ping', [], []},
|
||||
send(Socket, Data),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _Tab) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVersion, Tab, _Extra) ->
|
||||
{ok, Tab}.
|
||||
|
||||
login(Socket, Username, Password) ->
|
||||
Data = {'Open',[{username, Username}, {password, Password}], []},
|
||||
send(Socket, Data).
|
||||
|
||||
set_dect_subscription_mode(Socket, Mode) ->
|
||||
Data = {'SetDECTSubscriptionMode', [{mode, Mode}], []},
|
||||
send(Socket, Data).
|
||||
|
||||
transform_event({Event, Args}) ->
|
||||
{e, [{cmd, 'On'}, {eventType, Event}] ++ Args, []}.
|
||||
|
||||
subscribe(Socket, Args) ->
|
||||
Payload = lists:map(fun transform_event/1, Args),
|
||||
Data = {'Subscribe', [], Payload},
|
||||
%% send(Socket, Data).
|
||||
send(Socket, Data).
|
||||
|
||||
send(Socket, Data) ->
|
||||
RawPayload = lists:flatten(xmerl:export_simple([Data], xmerl_xml, [{prolog, ""}])),
|
||||
BinPayload = list_to_binary(RawPayload),
|
||||
Payload = binary_to_list(<<BinPayload/binary, <<00>>/binary>>),
|
||||
ssl:send(Socket, Payload).
|
||||
%% gen_tcp:send(Socket, Payload).
|
||||
|
||||
handle_response(Message, State) ->
|
||||
{Element, _} = xmerl_scan:string(Message),
|
||||
{Response, Parameters, Body} = xmerl_lib:simplify_element(Element),
|
||||
io:format("Received: ~p~n", [Response]),
|
||||
io:format("Params: ~p~n", [Parameters]),
|
||||
io:format("Body: ~p~n", [Body]),
|
||||
case Response of
|
||||
'OpenResp' -> handle_login(Body, Parameters, State);
|
||||
'EventPPDevCnf' -> handle_device_configuration_event(Body, Parameters, State);
|
||||
_ -> {noreply, State}
|
||||
end.
|
||||
|
||||
handle_login(_, [{publicKey, [{modulus, Modulus}, {exponent, Exponent}], []} | _], State) ->
|
||||
{noreply, State#state{modulus=Modulus, exponent=Exponent}};
|
||||
|
||||
handle_login(Body, [_ | Rest], State) ->
|
||||
handle_login(Body, Rest, State).
|
||||
|
||||
handle_device_configuration_event(_, [{pp, Args, _}], State) ->
|
||||
handle_device_configuration_event(Args, State).
|
||||
|
||||
handle_device_configuration_event([{ipei, Ipei} | _], State) ->
|
||||
ok;
|
||||
%% create_device().
|
||||
|
||||
handle_device_configuration_event([_ | Rest], State) ->
|
||||
handle_device_configuration_event(Rest, State).
|
||||
84
src/mikuvisor.erl
Normal file
84
src/mikuvisor.erl
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
-module(mikuvisor).
|
||||
-behaviour(supervisor).
|
||||
-export([init/1, start_link/1, start_child/0]).
|
||||
|
||||
start_link([IvrPort, OMMAddress, OMMPort, OMMUsername, OMMPassword, OMMTLS]) ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, [IvrPort, OMMAddress, OMMPort, OMMUsername, OMMPassword, OMMTLS]).
|
||||
|
||||
init([IvrPort, OMMAddress, OMMPort, OMMUsername, OMMPassword, OMMTLS]) ->
|
||||
spawn_link(fun empty_listeners/0),
|
||||
{ok, IVRSocket} = gen_tcp:listen(IvrPort, []),
|
||||
%% Flags = #{strategy => one_for_one,
|
||||
%% intensity => 60,
|
||||
%% period => 3600},
|
||||
%% Mikutel = case OMMTLS of
|
||||
%% true -> #{id => mikutel,
|
||||
%% start => {mikutel_ssl, start_link, [[OMMAddress,
|
||||
%% OMMPort,
|
||||
%% OMMUsername,
|
||||
%% OMMPassword,
|
||||
%% OMMTLS]]},
|
||||
%% type => worker,
|
||||
%% modules => [mikutel_ssl]};
|
||||
%% _ -> #{id => mikutel,
|
||||
%% start => {mikutel_plain, start_link, [[OMMAddress,
|
||||
%% OMMPort,
|
||||
%% OMMUsername,
|
||||
%% OMMPassword,
|
||||
%% OMMTLS]]},
|
||||
%% type => worker,
|
||||
%% modules => [mikutel_plain]}
|
||||
%% end,
|
||||
%% Specs = [#{id => mikuivr,
|
||||
%% start => {mikuivr, start_link, [[IVRSocket]]},
|
||||
%% type => worker,
|
||||
%% modules => [mikuivr]},
|
||||
%% Mikutel],
|
||||
%% {ok, {Flags, Specs}}.
|
||||
|
||||
{ok, {{one_for_one, 60, 3600},
|
||||
[
|
||||
{
|
||||
mikuivr,
|
||||
{mikuivr, start_link, [[IVRSocket]]},
|
||||
temporary,
|
||||
1000,
|
||||
worker,
|
||||
[mikuivr]
|
||||
},
|
||||
{
|
||||
mikutel,
|
||||
{mikutel, start_link, [[OMMAddress,
|
||||
OMMPort,
|
||||
OMMUsername,
|
||||
OMMPassword,
|
||||
OMMTLS]]},
|
||||
temporary,
|
||||
1000,
|
||||
worker,
|
||||
[mikuivr]
|
||||
}
|
||||
]
|
||||
}}.
|
||||
|
||||
start_child() ->
|
||||
supervisor:start_child(?MODULE, []).
|
||||
|
||||
empty_listeners() ->
|
||||
[start_child() || _ <- lists:seq(1,20)],
|
||||
ok.
|
||||
|
||||
get_ancestors(PID) when is_pid(PID) ->
|
||||
case erlang:process_info(PID) of
|
||||
{dictionary, Dict} -> ancestors_from_dict(Dict);
|
||||
_ -> []
|
||||
end.
|
||||
|
||||
ancestors_from_dict([]) ->
|
||||
[];
|
||||
|
||||
ancestors_from_dict([{'$ancestors', Ancestors} | _]) ->
|
||||
Ancestors;
|
||||
|
||||
ancestors_from_dict([_ | Rest]) ->
|
||||
ancestors_from_dict(Rest).
|
||||
1
src/rebar.lock
Normal file
1
src/rebar.lock
Normal file
|
|
@ -0,0 +1 @@
|
|||
[].
|
||||
Loading…
Reference in a new issue