%%% Copyright (C) 2004 Enrique Marcote Peņa %%% %%% This library is free software; you can redistribute it and/or %%% modify it under the terms of the GNU Lesser General Public %%% License as published by the Free Software Foundation; either %%% version 2.1 of the License, or (at your option) any later version. %%% %%% This library is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% Lesser General Public License for more details. %%% %%% You should have received a copy of the GNU Lesser General Public %%% License along with this library; if not, write to the Free Software %%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA %%% @doc Sample echo ESME. %%% %%%

This ESME echoes SMs back to the caller.

%%% %%% @copyright 2004 Enrique Marcote Peņa %%% @author Enrique Marcote Peņa %%% [http://oserl.sourceforge.net/] %%% @version 1.1, {23 Jun 2004} {@time}. %%% @end -module(echo_esme). -behaviour(gen_esme). %%%------------------------------------------------------------------- %%% Include files %%%------------------------------------------------------------------- -include("oserl.hrl"). %%%------------------------------------------------------------------- %%% External exports %%%------------------------------------------------------------------- -export([start_link/0, stop/0]). %%%------------------------------------------------------------------- %%% Internal ESME exports %%%------------------------------------------------------------------- -export([init/1, handle_outbind/3, handle_alert_notification/3, handle_operation/3, handle_unbind/3, handle_listen_error/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %%%------------------------------------------------------------------- %%% Macros %%%------------------------------------------------------------------- -define(SERVER, ?MODULE). -define(SMSC_ADDRESS, {192, 168, 1, 4}). -define(SMPP_PORT, ?DEFAULT_SMPP_PORT). -define(SYSTEM_ID, atom_to_list(?MODULE)). -define(PASSWORD, "secret"). -define(SOURCE_ADDR, "1948"). %%%------------------------------------------------------------------- %%% Records %%%------------------------------------------------------------------- %% %@spec {state, TrxSession} %% %% %@doc Representation of the ESME server state. %% %%
%%
TrxSession:
Pid of the transceiver session.
%%
%% %@end -record(state, {trx_session}). %%%=================================================================== %%% External functions %%%=================================================================== %% @spec start_link() -> Result %% Result = {ok, Pid} | ignore | {error, Error} %% Pid = pid() %% Error = {already_started, Pid} | term() %% %% @doc Starts the ESME server. %% %% @see gen_esme %% @see start/0 %% @end start_link() -> gen_esme:start_link({local, ?SERVER}, ?MODULE, [], []). %% @spec stop() -> ok %% %% @doc Stops the ESME server. %% %% @see handle_call/3 %% %% @equiv gen_esme:call(SERVER, die, 10000) %% @end stop() -> gen_esme:call(?SERVER, die, 10000). %%%=================================================================== %%% Server functions %%%=================================================================== %% @spec init(Args) -> Result %% Args = term() %% Result = {ok, State} | {ok, State, Timeout} | ignore | {stop, Reason} %% State = term() %% Timeout = int() | infinity %% Reason = term() %% %% @doc %% gen_esme - init/1 callback implementation. %% %%

Initiates the server.

%% @end init([]) -> case gen_esme:session_start(?SMSC_ADDRESS, ?SMPP_PORT) of {ok, Trx} -> erlang:monitor(process, Trx), ParamList = [{system_id, ?SYSTEM_ID}, {password, ?PASSWORD}, {source_addr, ?SOURCE_ADDR}], case gen_esme:bind_transceiver(Trx, ParamList) of {ok, _PduResp} -> {ok, #state{trx_session = Trx}}; BindError -> {stop, BindError} end; SessionError -> {stop, SessionError} end. %% @spec handle_outbind(Outbind, From, State) -> Result %% OutBind = {outbind, Session, Pdu} %% Session = pid() %% Pdu = pdu() %% From = term() %% State = term() %% Result = {noreply, NewState} | %% {noreply, NewState, Timeout} | %% {stop, Reason, NewState} %% ParamList = [{ParamName, ParamValue}] %% ParamName = atom() %% ParamValue = term() %% NewState = term() %% Timeout = int() %% Reason = term() %% %% @doc gen_esme - handle_outbind/3 callback implementation. %% %%

Handle oubind requests from the peer SMSC.

%% @end handle_outbind({outbind, Session, Pdu}, From, State) -> {noreply, State}. %% @spec handle_alert_notification(AlertNotification, From, State) -> Result %% AlertNotification = {alert_notification, Session, Pdu} %% Session = pid() %% Pdu = pdu() %% From = term() %% State = term() %% Result = {noreply, NewState} | %% {noreply, NewState, Timeout} | %% {stop, Reason, NewState} %% ParamList = [{ParamName, ParamValue}] %% ParamName = atom() %% ParamValue = term() %% NewState = term() %% Timeout = int() %% Reason = term() %% %% @doc gen_esme - handle_alert_notification/3 callback implementation. %% %%

Handle alert_notification requests from the peer SMSC.

%% @end handle_alert_notification({alert_notification, Session, Pdu}, From, State) -> {noreply, State}. %% @spec handle_operation(Operation, From, State) -> Result %% Operation = {deliver_sm, Session, Pdu} | {data_sm, Session, Pdu} %% Session = pid() %% Pdu = pdu() %% From = term() %% State = term() %% Result = {reply, Reply, NewState} | %% {reply, Reply, NewState, Timeout} | %% {noreply, NewState} | %% {noreply, NewState, Timeout} | %% {stop, Reason, Reply, NewState} | %% {stop, Reason, NewState} %% Reply = {ok, ParamList} | {error, Error, ParamList} %% ParamList = [{ParamName, ParamValue}] %% ParamName = atom() %% ParamValue = term() %% NewState = term() %% Timeout = int() %% Reason = term() %% %% @doc gen_esme - handle_operation/3 callback implementation. %% %%

Handle deliver_sm and data_sm operations (from the peer %% SMSCs) to the callback ESME.

%% %%

The ParamList included in the response is used to construct %% the response PDU. If a command_status other than ESME_ROK is to %% be returned by the ESME in the response PDU, the callback should return the %% term {error, Error, ParamList}, where Error is the %% desired command_status error code.

%% @end handle_operation({deliver_sm, _Session, Pdu}, From, S) -> Mesg = sm:message_user_data(Pdu), % gets incoming short message Dest = sm:reply_address(Pdu), % source address as response address io:format("Echoing SM: ~p~n", [Mesg]), spawn(fun() -> gen_esme:submit_sm(S#state.trx_session, [Mesg|Dest]) end), {reply, {ok, []}, S}; handle_operation({data_sm, Session, Pdu}, From, S) -> Mesg = sm:message_user_data(Pdu), % gets incoming short message Dest = sm:reply_address(Pdu), % source address as response address io:format("Echoing SM: ~p~n", [Mesg]), spawn(fun() -> gen_esme:data_sm(S#state.trx_session, [Mesg|Dest]) end), {reply, {ok, []}, S}; handle_operation({CmdName, _Session, _Pdu}, _From, S) -> % Don't know how to handle CmdName io:format("Don't know how to handle ~p~n", [CmdName]), {reply, {error, ?ESME_RINVCMDID, []}, S}. %% @spec handle_unbind(Unbind, From, State) -> Result %% Unbind = {unbind, Session, Pdu} %% Session = pid() %% Pdu = pdu() %% Result = {reply, Reply, NewState} | %% {reply, Reply, NewState, Timeout} | %% {noreply, NewState} | %% {noreply, NewState, Timeout} | %% {stop, Reason, Reply, NewState} | %% {stop, Reason, NewState} %% Reply = ok | {error, Error} %% Error = int() %% NewState = term() %% Timeout = int() %% Reason = term() %% %% @doc gen_esme - handle_unbind/3 callback implementation. %% %%

Handle unbind requests from the peer SMSC.

%% %%

If ok returned an unbind_resp with a ESME_ROK %% command_status is sent to the MC and the session moves into the unbound %% state. When {error, Error} is returned by the ESME, the %% response PDU sent by the session to the MC will have an Error %% command_status and the session will remain on it's current bound state %% (bound_rx, bound_tx or bound_trx).

%% @end handle_unbind({unbind, Trx, Pdu}, From, #state{trx_session = Trx} = S) -> io:format("unbind: ~p~n", [Trx]), {stop, normal, S#state{trx_session = undefined}}. %% @spec handle_listen_error(State) -> Result %% State = term() %% Result = {noreply, NewState} | %% {noreply, NewState, Timeout} | %% {stop, Reason, NewState} %% NewState = term() %% Timeout = int() %% Reason = term() %% %% @doc gen_esme - handle_listen_error/1 callback implementation. %% %%

Handle listen failures.

%% @end handle_listen_error(State) -> {stop, {error, listen_error}, State}. %% @spec handle_call(Request, From, State) -> Result %% Request = term() %% From = {pid(), Tag} %% State = term() %% Result = {reply, Reply, NewState} | %% {reply, Reply, NewState, Timeout} | %% {noreply, NewState} | %% {noreply, NewState, Timeout} | %% {stop, Reason, Reply, NewState} | %% {stop, Reason, NewState} %% Reply = term() %% NewState = term() %% Timeout = int() | infinity %% Reason = term() %% %% @doc gen_esme - handle_call/3 callback implementation. %% %%

Handling call messages.

%% %% %% %% @see terminate/2 %% @end handle_call(die, _From, State) -> {stop, normal, ok, State}. %% @spec handle_cast(Request, State) -> Result %% Request = term() %% Result = {noreply, NewState} | %% {noreply, NewState, Timeout} | %% {stop, Reason, NewState} %% NewState = term() %% Timeout = int() | infinity %% Reason = normal | term() %% %% @doc gen_esme - handle_cast/2 callback implementation. %% %%

Handling cast messages.

%% %% %% %% @see terminate/2 %% @end handle_cast(Request, State) -> {noreply, State}. %% @spec handle_info(Info, State) -> Result %% Info = timeout | term() %% State = term() %% Result = {noreply, NewState} | %% {noreply, NewState, Timeout} | %% {stop, Reason, NewState} %% NewState = term() %% Timeout = int() | infinity %% Reason = normal | term() %% %% @doc gen_esme - handle_info/2 callback implementation. %% %%

Handling all non call/cast messages.

%% %% %% %% @see terminate/2 %% @end handle_info({'DOWN', _, process, Trx, Info}, #state{trx_session = Trx} = S) -> {stop, Info, S}; handle_info(Info, S) -> {noreply, S}. %% @spec terminate(Reason, State) -> ok %% Reason = normal | shutdown | term() %% State = term() %% %% @doc %% gen_esme - terminate/2 callback implementation. %% %%

Shutdown the ESME server.

%% %%

Return value is ignored by gen_esme.

%% @end terminate(kill, S) -> ok; terminate(Reason, S) -> catch gen_smsc:unbind(S#state.trx_session), catch gen_smsc:session_stop(S#state.trx_session). %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} %% OldVsn = undefined | term() %% State = term() %% Extra = term %% NewState = term() %% %% @doc gen_esme - code_change/2 callback implementation. %% %%

Convert process state when code is changed.

%% @end code_change(OldVsn, State, Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%===================================================================