<commit_before>%%% ocs_pgw_fsm.erl
%%% vim: ts=3
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% @copyright 2016 - 2020 SigScale Global Inc.
%%% @end
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
%%% You may obtain a copy of the License at
%%%
%%%     http://www.apache.org/licenses/LICENSE-2.0
%%%
%%% Unless required by applicable law or agreed to in writing, software
%%% distributed under the License is distributed on an "AS IS" BASIS,
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%%% See the License for the specific language governing permissions and
%%% limitations under the License.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% @doc This {@link //stdlib/gen_fsm. gen_fsm} behaviour callback module
%%% 	implements procedures for user authorization initiated by PDN Gateway.
%%%
%%% @reference <a href="https://webapp.etsi.org/key/key.asp?GSMSpecPart1=29&amp;GSMSpecPart2=273">
%%% 	3GPP TS 29.273 - 3GPP EPS AAA interfaces</a>
%%%
-module(ocs_pgw_fsm).
-copyright('Copyright (c) 2016 - 2020 SigScale Global Inc.').

-behaviour(gen_fsm).

%% export the ocs_pgw_fsm API
-export([]).

%% export the ocs_pgw_fsm state callbacks
-export([idle/3, register/2, profile/2]).

%% export the call backs needed for gen_fsm behaviour
-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3,
			terminate/3, code_change/4]).

-include("ocs.hrl").
-include("diameter_gen_3gpp.hrl").
-include("diameter_3gpp.hrl").
-include("diameter_gen_3gpp_s6b_application.hrl").
-include("diameter_gen_3gpp_swx_application.hrl").
-include_lib("diameter/include/diameter.hrl").
-include_lib("diameter/include/diameter_gen_base_rfc6733.hrl").

-define(IANA_PEN_3GPP, 10415).
-define(S6b_APPLICATION_ID, 16777272).
-define(SWx_APPLICATION_ID, 16777265).
-define(SWx_APPLICATION, ocs_diameter_3gpp_swx_application).
-define(TIMEOUT, 10000).

-record(statedata,
		{identity :: binary() | undefined,
		imsi :: binary() | undefined,
		user_profile :: #'3gpp_swx_Non-3GPP-User-Data'{} | undefined,
		orig_host :: diameter:'OctetString'(),
		orig_realm :: diameter:'OctetString'(),
		dest_host :: diameter:'OctetString'(),
		dest_realm :: diameter:'OctetString'(),
		server_address :: inet:ip_address(),
		server_port :: pos_integer(),
		client_address :: inet:ip_address(),
		client_port :: pos_integer(),
		service :: tuple(),
		hss_realm :: string() | undefined,
		hss_host = [] :: string(),
		pgw_id = [] :: [diameter:'OctetString'()],
		pgw_plmn = [] :: [diameter:'OctetString'()],
		request :: #'3gpp_s6b_AAR'{} | undefined,
		session_id :: string(),
		from :: {pid(), reference()} | undefined}).
-type statedata() :: #statedata{}.

%% support deprecated_time_unit()
-define(MILLISECOND, milli_seconds).
%-define(MILLISECOND, millisecond).

%%----------------------------------------------------------------------
%%  The ocs_pgw_fsm API
%%----------------------------------------------------------------------

%%----------------------------------------------------------------------
%%  The ocs_pgw_fsm gen_fsm call backs
%%----------------------------------------------------------------------

-spec init(Args) -> Result
	when
		Args :: list(),
		Result :: {ok, StateName, StateData}
		| {ok, StateName, StateData, Timeout}
		| {ok, StateName, StateData, hibernate}
		| {stop, Reason} | ignore,
		StateName :: atom(),
		StateData :: statedata(),
		Timeout :: non_neg_integer() | infinity,
		Reason :: term().
%% @doc Initialize the {@module} finite state machine.
%% @see //stdlib/gen_fsm:init/1
%% @private
%%
init([ServiceName, ServerAddress, ServerPort, ClientAddress,
		ClientPort, SessionId, OriginHost, OriginRealm,
		DestinationHost, DestinationRealm] = _Args) ->
	process_flag(trap_exit, true),
	{ok, HssRealm} = application:get_env(hss_realm),
	HssHost = case application:get_env(hss_host) of
		{ok, undefined} ->
			[];
		{ok, HH} ->
			HH
	end,
	{ok, idle, #statedata{service = ServiceName,
			server_address = ServerAddress, server_port = ServerPort,
			client_address = ClientAddress, client_port = ClientPort,
			session_id = SessionId,
			orig_host = OriginHost, orig_realm = OriginRealm,
			dest_host = DestinationHost, dest_realm = DestinationRealm,
			hss_realm = HssRealm, hss_host = HssHost}}.

-spec idle(Event, From, StateData) -> Result
	when
		Event :: timeout | term(),
		From :: {pid(), reference()},
		StateData :: statedata(),
		Result :: {reply, Reply, NextStateName, NewStateData}
			| {reply, Reply, NextStateName, NewStateData, Timeout}
			| {reply, Reply, NextStateName, NewStateData, hibernate}
			| {next_state, NextStateName, NewStateData}
			| {next_state, NextStateName, NewStateData, Timeout}
			| {next_state, NextStateName, NewStateData, hibernate}
			| {stop, Reason, Reply, NewStateData}
			| {stop, Reason, NewStateData},
		Reply :: term(),
		NextStateName :: atom(),
		NewStateData :: statedata(),
		Timeout :: non_neg_integer() | infinity,
		Reason :: normal | term().
%% @doc Handle events sent with {@link //stdlib/gen_fsm:sync_send_event/2.
%%		gen_fsm:sync_send_event/2} in the <b>idle</b> state.
%% @@see //stdlib/gen_fsm:StateName/3
%% @private
%%
idle(#'3gpp_s6b_AAR'{'User-Name' = [Identity], 'Session-Id' = SessionId,
		'Origin-Host' = Host, 'Origin-Realm' = Realm,
		'Auth-Request-Type' = ?'3GPP_SWX_AUTH-REQUEST-TYPE_AUTHORIZE_ONLY',
		'MIP6-Agent-Info' = PGW, 'Visited-Network-Identifier' = VPLMN,
		'Service-Selection' = [APN]} = Request, From, StateData) ->
	[IMSI | _] = binary:split(Identity, <<$@>>, []),
	NewStateData = StateData#statedata{from = From, request = Request,
			identity = Identity, imsi = IMSI, pgw_id = PGW, pgw_plmn = VPLMN},
	F = fun() ->
			mnesia:index_read(session, IMSI, #session.imsi)
	end,
	case mnesia:transaction(F) of
		{atomic, [#session{user_profile = UserProfile,
				hss_realm = HssRealm, hss_host = HssHost} | _]} ->
			NextStateData = NewStateData#statedata{user_profile = UserProfile,
					hss_realm = HssRealm, hss_host = HssHost},
			case lists:keyfind(APN,
					#'3gpp_swx_APN-Configuration'.'Service-Selection',
					UserProfile#'3gpp_swx_Non-3GPP-User-Data'.'APN-Configuration') of
				#'3gpp_swx_APN-Configuration'{} = APN ->
					send_register(NextStateData),
					{next_state, profile, NextStateData, ?TIMEOUT};
				_Other ->
					ResultCode = ?'DIAMETER_BASE_RESULT-CODE_AUTHORIZATION_REJECTED',
					Reply = response(ResultCode, NextStateData),
					{stop, shutdown,  Reply, NextStateData}
			end;
		{atomic, []} ->
			send_profile(NewStateData),<commit_msg>remove unused variable blocking matching apn selection<commit_after>%%% ocs_pgw_fsm.erl
%%% vim: ts=3
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% @copyright 2016 - 2020 SigScale Global Inc.
%%% @end
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
%%% You may obtain a copy of the License at
%%%
%%%     http://www.apache.org/licenses/LICENSE-2.0
%%%
%%% Unless required by applicable law or agreed to in writing, software
%%% distributed under the License is distributed on an "AS IS" BASIS,
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%%% See the License for the specific language governing permissions and
%%% limitations under the License.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% @doc This {@link //stdlib/gen_fsm. gen_fsm} behaviour callback module
%%% 	implements procedures for user authorization initiated by PDN Gateway.
%%%
%%% @reference <a href="https://webapp.etsi.org/key/key.asp?GSMSpecPart1=29&amp;GSMSpecPart2=273">
%%% 	3GPP TS 29.273 - 3GPP EPS AAA interfaces</a>
%%%
-module(ocs_pgw_fsm).
-copyright('Copyright (c) 2016 - 2020 SigScale Global Inc.').

-behaviour(gen_fsm).

%% export the ocs_pgw_fsm API
-export([]).

%% export the ocs_pgw_fsm state callbacks
-export([idle/3, register/2, profile/2]).

%% export the call backs needed for gen_fsm behaviour
-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3,
			terminate/3, code_change/4]).

-include("ocs.hrl").
-include("diameter_gen_3gpp.hrl").
-include("diameter_3gpp.hrl").
-include("diameter_gen_3gpp_s6b_application.hrl").
-include("diameter_gen_3gpp_swx_application.hrl").
-include_lib("diameter/include/diameter.hrl").
-include_lib("diameter/include/diameter_gen_base_rfc6733.hrl").

-define(IANA_PEN_3GPP, 10415).
-define(S6b_APPLICATION_ID, 16777272).
-define(SWx_APPLICATION_ID, 16777265).
-define(SWx_APPLICATION, ocs_diameter_3gpp_swx_application).
-define(TIMEOUT, 10000).

-record(statedata,
		{identity :: binary() | undefined,
		imsi :: binary() | undefined,
		user_profile :: #'3gpp_swx_Non-3GPP-User-Data'{} | undefined,
		orig_host :: diameter:'OctetString'(),
		orig_realm :: diameter:'OctetString'(),
		dest_host :: diameter:'OctetString'(),
		dest_realm :: diameter:'OctetString'(),
		server_address :: inet:ip_address(),
		server_port :: pos_integer(),
		client_address :: inet:ip_address(),
		client_port :: pos_integer(),
		service :: tuple(),
		hss_realm :: string() | undefined,
		hss_host = [] :: string(),
		pgw_id = [] :: [diameter:'OctetString'()],
		pgw_plmn = [] :: [diameter:'OctetString'()],
		request :: #'3gpp_s6b_AAR'{} | undefined,
		session_id :: string(),
		from :: {pid(), reference()} | undefined}).
-type statedata() :: #statedata{}.

%% support deprecated_time_unit()
-define(MILLISECOND, milli_seconds).
%-define(MILLISECOND, millisecond).

%%----------------------------------------------------------------------
%%  The ocs_pgw_fsm API
%%----------------------------------------------------------------------

%%----------------------------------------------------------------------
%%  The ocs_pgw_fsm gen_fsm call backs
%%----------------------------------------------------------------------

-spec init(Args) -> Result
	when
		Args :: list(),
		Result :: {ok, StateName, StateData}
		| {ok, StateName, StateData, Timeout}
		| {ok, StateName, StateData, hibernate}
		| {stop, Reason} | ignore,
		StateName :: atom(),
		StateData :: statedata(),
		Timeout :: non_neg_integer() | infinity,
		Reason :: term().
%% @doc Initialize the {@module} finite state machine.
%% @see //stdlib/gen_fsm:init/1
%% @private
%%
init([ServiceName, ServerAddress, ServerPort, ClientAddress,
		ClientPort, SessionId, OriginHost, OriginRealm,
		DestinationHost, DestinationRealm] = _Args) ->
	process_flag(trap_exit, true),
	{ok, HssRealm} = application:get_env(hss_realm),
	HssHost = case application:get_env(hss_host) of
		{ok, undefined} ->
			[];
		{ok, HH} ->
			HH
	end,
	{ok, idle, #statedata{service = ServiceName,
			server_address = ServerAddress, server_port = ServerPort,
			client_address = ClientAddress, client_port = ClientPort,
			session_id = SessionId,
			orig_host = OriginHost, orig_realm = OriginRealm,
			dest_host = DestinationHost, dest_realm = DestinationRealm,
			hss_realm = HssRealm, hss_host = HssHost}}.

-spec idle(Event, From, StateData) -> Result
	when
		Event :: timeout | term(),
		From :: {pid(), reference()},
		StateData :: statedata(),
		Result :: {reply, Reply, NextStateName, NewStateData}
			| {reply, Reply, NextStateName, NewStateData, Timeout}
			| {reply, Reply, NextStateName, NewStateData, hibernate}
			| {next_state, NextStateName, NewStateData}
			| {next_state, NextStateName, NewStateData, Timeout}
			| {next_state, NextStateName, NewStateData, hibernate}
			| {stop, Reason, Reply, NewStateData}
			| {stop, Reason, NewStateData},
		Reply :: term(),
		NextStateName :: atom(),
		NewStateData :: statedata(),
		Timeout :: non_neg_integer() | infinity,
		Reason :: normal | term().
%% @doc Handle events sent with {@link //stdlib/gen_fsm:sync_send_event/2.
%%		gen_fsm:sync_send_event/2} in the <b>idle</b> state.
%% @@see //stdlib/gen_fsm:StateName/3
%% @private
%%
idle(#'3gpp_s6b_AAR'{'User-Name' = [Identity], 'Session-Id' = SessionId,
		'Origin-Host' = Host, 'Origin-Realm' = Realm,
		'Auth-Request-Type' = ?'3GPP_SWX_AUTH-REQUEST-TYPE_AUTHORIZE_ONLY',
		'MIP6-Agent-Info' = PGW, 'Visited-Network-Identifier' = VPLMN,
		'Service-Selection' = [APN]} = Request, From, StateData) ->
	[IMSI | _] = binary:split(Identity, <<$@>>, []),
	NewStateData = StateData#statedata{from = From, request = Request,
			identity = Identity, imsi = IMSI, pgw_id = PGW, pgw_plmn = VPLMN},
	F = fun() ->
			mnesia:index_read(session, IMSI, #session.imsi)
	end,
	case mnesia:transaction(F) of
		{atomic, [#session{user_profile = UserProfile,
				hss_realm = HssRealm, hss_host = HssHost} | _]} ->
			NextStateData = NewStateData#statedata{user_profile = UserProfile,
					hss_realm = HssRealm, hss_host = HssHost},
			case lists:keyfind(APN,
					#'3gpp_swx_APN-Configuration'.'Service-Selection',
					UserProfile#'3gpp_swx_Non-3GPP-User-Data'.'APN-Configuration') of
				#'3gpp_swx_APN-Configuration'{} ->
					send_register(NextStateData),
					{next_state, profile, NextStateData, ?TIMEOUT};
				_Other ->
					ResultCode = ?'DIAMETER_BASE_RESULT-CODE_AUTHORIZATION_REJECTED',
					Reply = response(ResultCode, NextStateData),
					{stop, shutdown,  Reply, NextStateData}
			end;
		{atomic, []} ->
			send_profile(NewStateData),
{'fraction_non_alphanumeric': 0.2087549658946106, 'fraction_numerical': 0.014691552357394498, 'mean_word_length': 4.010138941043936, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 37, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5174832', 'n_tokens_mistral': 4969, 'n_tokens_neox': 4494, 'n_words': 964}