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).
+