add new map utility functiosn and a skeleton AS server
diff --git a/src/map_as_server.erl b/src/map_as_server.erl
new file mode 100644
index 0000000..86c03af
--- /dev/null
+++ b/src/map_as_server.erl
@@ -0,0 +1,121 @@
+
+-module(map_as_server).
+-behaviour(gen_server).
+
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
+
+-export([bind_ac/1, unbind_ac/1, dump/0]).
+
+-record(ass_state, {sap_sup_pid, as_tbl}).
+
+-record(ass_record, {ac, user_pid}).
+
+
+% client side
+
+bind_ac(Srv, AcName) when is_list(AcName) ->
+ bind_ac(Srv, list_to_tuple(AcName);
+bind_ac(Srv, AcName) when is_tuple(AcName) ->
+ gen_server:call(Srv, {bind_ac, AcName}).
+
+unbind_ac(Srv, AcName) when is_list(AcName) ->
+ unbind_ac(Srv, list_to_tuple(AcName);
+unbind_ac(Srv, AcName) when is_tuple(AcName) ->
+ gen_server:call(Srv, {unbind_ac, AcName}).
+
+dump() ->
+ fixme.
+
+
+% gen_fsm callbacks
+
+start_link(Ssn, {M, A, O}) when is_integer(Ssn) ->
+ ProcName = list_to_atom(?MODULE ++ "_" ++ integer_to_list(Ssn)),
+ gen_server:start_link({local, ProcName}, ?MODULE, [Ssn, {M, A, O}], []).
+
+init([Ssn, {M, A, O}]) ->
+ {ok, SapSupPid} = tcap_sap_sup:start_link(M, A, O),
+ AssTbl = ets:new(list_to_atom(?MODULE ++ "_" ++ integer_to_list(Ssn)),
+ [ordered_set, named_table, {keypos, #ass_record.ac}]),
+ {ok, #ass_state{sap_sup_pid = SapSupPid, as_tbl = AssTbl}}.
+
+
+handle_call({bind_ac, Ac}, {FromPid, _FromRef}, LoopDat) ->
+ NewRec = #actbl_record{ac = Ac, user_pid = FromPid},
+ case ets:insert_new(LoopDat#ass_state.as_tbl, NewRec) of
+ false ->
+ {reply, {error, ets_insert}, LoopDat};
+ _ ->
+ link(FromPid),
+ {reply, ok, LoopDat}
+ end;
+
+handle_call({unbind_ac, Ac}, {FromPid, _FromRef}, LoopDat) ->
+ DelRec = #actbl_record{ac = Ac, user_pid = FromPid},
+ ets:delete_object(LoopDat#ass_state.as_tbl, DelRec),
+ {reply, ok, LoopDat}.
+
+handle_cast(Info, LoopDat) ->
+ error_logger:error_report(["unknown handle_cast",
+ {module, ?MODULE}, {info, Info},
+ {state, LoopDat}]),
+ {noreply, LoopDat}.
+
+handle_info({'EXIT', Pid, Reason}, LoopDat) ->
+ io:format("EXIT from process ~p (~p), cleaning up tables~n",
+ [Pid, Reason]),
+ ets:match_delete(LoopDat#ass_state.as_tbl, #actbl_record{user_pid = Pid}),
+ {noreply, LoopDat};
+handle_info(Info, LoopDat) ->
+ error:logger:error_report(["unknown handle_info",
+ {module, ?MODULE}, {info, Info},
+ {state, LoopDat}]),
+ {noreply, LoopDat}.
+
+terminate(Reason, _LoopDat) ->
+ io:format("terminating ~p with reason ~p~n", [self(), Reason]),
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+% server side
+as_for_ac(Ac, LoopDat) ->
+ case ets:lookup(LoopDat#ass_state.as_tbl, Ac) of
+ [#ass_record{user_pid = UserPid}] ->
+ {ok, UserPid};
+ _ ->
+ {error, no_such_ac}
+ end.
+
+handle_tcap({'TC','BEGIN',indication,
+ I='TC-BEGIN'{appContextName = Ac, dialogueID = DlgId}}, LoopDat) ->
+ case as_for_ac(Ac, LoopDat) of
+ {ok, UserPid} ->
+ gen_fsm:send_event(UserPid, I);
+ {error, Reason} ->
+ error_logger:error_report(["TC-BEGIN for non-existing AC",
+ {application_context, Ac}]),
+ ok
+ end;
+handle_tcap({'TC', What, indication, P}) when
+ What == 'CONTINUE'; What == 'END';
+ What == 'U-ABORT'; What == 'P-ABORT';
+ What == 'NOTICE' ->
+ % look up the Pid for the specific Dialogue Handler
+ case as_for_ac(Ac, LoopDat) of
+ FIXME FIXME
+
+ ok;
+handle_tcap({'TC', 'INVOKE', indication, P}) when
+ % look up AS for AC, start new invoke in dialogue
+
+handle_tcap({'TC', What, indication, P}) when
+ What == 'RESULT-L'; What == 'RESULT-NL';
+ What == 'U-ERROR'; What == 'L-CANCEL';
+ What == 'L-REJECT'; What == 'U-REJECT';
+ What == 'TIMER-RESET' ->
+ % look up the gen_fsm for the specific Invoke
+
+ ok.
+
diff --git a/src/map_helper.erl b/src/map_helper.erl
new file mode 100644
index 0000000..872cad2
--- /dev/null
+++ b/src/map_helper.erl
@@ -0,0 +1,120 @@
+% GSM MAP codec wrapper functions
+
+% (C) 2011-2012 by Harald Welte <laforge@gnumonks.org>
+%
+% All Rights Reserved
+%
+% This program is free software; you can redistribute it and/or modify
+% it under the terms of the GNU Affero General Public License as
+% published by the Free Software Foundation; either version 3 of the
+% License, or (at your option) any later version.
+%
+% This program 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 General Public License for more details.
+%
+% You should have received a copy of the GNU Affero General Public License
+% along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+-module(map_helper).
+-author('Harald Welte <laforge@gnumonks.org>').
+
+-include_lib("osmo_ss7/include/isup.hrl").
+-include_lib("osmo_map/include/map.hrl").
+
+-export([postproc/2, postproc_gt/2, postproc_imsi/2, postproc_msisdn/2]).
+
+postproc(M=#'UpdateLocationArg'{imsi = Imsi, 'msc-Number' = Msc, 'vlr-Number' = Vlr,
+ 'v-gmlc-Address' = Gmlc}, Mode) ->
+ M#'UpdateLocationArg'{imsi = postproc_imsi(Imsi, Mode),
+ 'msc-Number' = postproc_gt(Msc, Mode),
+ 'vlr-Number' = postproc_gt(Vlr, Mode),
+ 'v-gmlc-Address' = postproc_gt(Gmlc, Mode)};
+postproc(M=#'UpdateLocationRes'{'hlr-Number' = Hlr}, Mode) ->
+ M#'UpdateLocationRes'{'hlr-Number' = postproc_gt(Hlr, Mode)};
+postproc(M=#'UpdateGprsLocationArg'{imsi = Imsi, 'sgsn-Address' = Sgsn}, Mode) ->
+ M#'UpdateGprsLocationArg'{imsi = postproc_imsi(Imsi, Mode),
+ 'sgsn-Address' = postproc_gt(Sgsn, Mode)};
+postproc(M=#'UpdateGprsLocationRes'{'hlr-Number' = Hlr}, Mode) ->
+ M#'UpdateGprsLocationRes'{'hlr-Number' = postproc_gt(Hlr, Mode)};
+postproc(M=#'SendAuthenticationInfoArg'{imsi = Imsi}, Mode) ->
+ M#'SendAuthenticationInfoArg'{imsi = postproc_imsi(Imsi, Mode)};
+postproc(M=#'ReadyForSM-Arg'{imsi = Imsi}, Mode) ->
+ M#'ReadyForSM-Arg'{imsi = postproc_imsi(Imsi, Mode)};
+postproc(M=#'MO-ForwardSM-Arg'{imsi = Imsi}, Mode) ->
+ M#'MO-ForwardSM-Arg'{imsi = postproc_imsi(Imsi, Mode)};
+postproc(M=#'RoutingInfoForSM-Res'{imsi = Imsi}, Mode) ->
+ M#'RoutingInfoForSM-Res'{imsi = postproc_imsi(Imsi, Mode)};
+postproc(M=#'DeactivateTraceModeArg'{imsi = Imsi}, Mode) ->
+ M#'DeactivateTraceModeArg'{imsi = postproc_imsi(Imsi, Mode)};
+postproc(M=#'ActivateTraceModeArg'{imsi = Imsi}, Mode) ->
+ M#'ActivateTraceModeArg'{imsi = postproc_imsi(Imsi, Mode)};
+postproc(M=#'InsertSubscriberDataArg'{imsi = Imsi, msisdn = Msisdn}, Mode) ->
+ M#'InsertSubscriberDataArg'{imsi = postproc_imsi(Imsi, Mode),
+ msisdn = postproc_msisdn(Msisdn, Mode)};
+postproc(M=#'AuthenticationFailureReportArg'{imsi = Imsi}, Mode) ->
+ M#'AuthenticationFailureReportArg'{imsi = postproc_imsi(Imsi, Mode)};
+postproc(M=#'AlertServiceCentreArg'{msisdn = Msisdn, serviceCentreAddress = Smsc}, Mode) ->
+ M#'AlertServiceCentreArg'{msisdn = postproc_msisdn(Msisdn, Mode),
+ serviceCentreAddress = postproc_gt(Smsc, Mode)};
+postproc(M=#'RoutingInfoForSM-Arg'{msisdn = Msisdn, serviceCentreAddress = Smsc}, Mode) ->
+ M#'RoutingInfoForSM-Arg'{msisdn = postproc_msisdn(Msisdn, Mode),
+ serviceCentreAddress = postproc_gt(Smsc, Mode)};
+postproc(M=#'ProvideRoamingNumberArg'{imsi = Imsi, msisdn = Msisdn,
+ 'gmsc-Address' = Gmsc}, Mode) ->
+ M#'ProvideRoamingNumberArg'{imsi = postproc_imsi(Imsi, Mode),
+ msisdn = postproc_msisdn(Msisdn, Mode),
+ 'gmsc-Address' = postproc_gt(Gmsc, Mode)};
+postproc(M=#'SendRoutingInfoRes'{msisdn = Msisdn}, Mode) ->
+ M#'SendRoutingInfoRes'{msisdn = postproc_msisdn(Msisdn, Mode)};
+postproc(M=#'SendRoutingInfoArg'{msisdn = Msisdn, 'gmsc-OrGsmSCF-Address' = Gmsc}, Mode) ->
+ M#'SendRoutingInfoArg'{msisdn = postproc_msisdn(Msisdn, Mode),
+ 'gmsc-OrGsmSCF-Address' = postproc_gt(Gmsc, Mode)};
+postproc(M=#'USSD-Arg'{msisdn = Msisdn}, Mode) ->
+ M#'USSD-Arg'{msisdn = postproc_msisdn(Msisdn, Mode)};
+postproc(M=#'ReportSM-DeliveryStatusArg'{msisdn = Msisdn,
+ serviceCentreAddress = Smsc}, Mode) ->
+ M#'ReportSM-DeliveryStatusArg'{msisdn = postproc_msisdn(Msisdn, Mode),
+ serviceCentreAddress = postproc_gt(Smsc, Mode)};
+postproc(M=#'AnyTimeInterrogationArg'{'gsmSCF-Address' = Scf}, Mode) ->
+ M#'AnyTimeInterrogationArg'{'gsmSCF-Address' = postproc_gt(Scf, Mode)};
+postproc(M, _Mode) ->
+ M.
+
+
+postproc_gt(In, post) when is_binary(In) ->
+ postproc_gt(binary_to_list(In), post);
+postproc_gt(asn1_NOVALUE, post) ->
+ undefined;
+postproc_gt(In, post) ->
+ map_codec:parse_addr_string(In);
+postproc_gt(undefined, pre) ->
+ asn1_NOVALUE;
+postproc_gt(In, pre) when is_record(In, party_number) ->
+ map_codec:encode_addr_string(In);
+postproc_gt(In, pre) ->
+ In.
+
+postproc_imsi(asn1_NOVALUE, post) ->
+ undefined;
+postproc_imsi(In, post) ->
+ map_codec:parse_map_addr(In);
+postproc_imsi(undefined, pre) ->
+ asn1_NOVALUE;
+postproc_imsi([], pre) ->
+ asn1_NOVALUE;
+postproc_imsi(In, pre) ->
+ map_codec:encode_map_tbcd(In).
+
+postproc_msisdn(asn1_NOVALUE, post) ->
+ undefined;
+postproc_msisdn(In, post) ->
+ map_codec:parse_map_addr(In);
+postproc_msisdn(undefined, pre) ->
+ asn1_NOVALUE;
+postproc_msisdn([], pre) ->
+ asn1_NOVALUE;
+postproc_msisdn(In, pre) ->
+ map_codec:encode_map_tbcd(In).
+