%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%
%%----------------------------------------------------------------------
%% Purpose : Define semantic text parser actions
%%----------------------------------------------------------------------
-include_lib("megaco/include/megaco.hrl").
-include_lib("megaco/include/megaco_message_v2.hrl").
-define(encoder_version_pre_prev3c,true).
-include("megaco_text_tokens.hrl").
-define(d(F,A), io:format("DBG:"++F++"~n",A)).
-ifdef(megaco_parser_inline).
-compile({inline,[{make_safe_token,1}]}).
-endif.
make_safe_token(Token) ->
{_TokenTag, Line, Text} = Token,
{safeToken, Line, Text}.
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_value,1}]}).
-endif.
ensure_value(Token) ->
case Token of
{safeToken, _Line, Text} when is_list(Text) ->
Text; % We really should ensure length
{'QuotedChars', _Line, Text} when is_list(Text) ->
Text; % We really should ensure length
Text when is_list(Text) ->
Text % We really should ensure length
end.
%% NAME = ALPHA *63(ALPHA / DIGIT / "_" )
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_NAME,1}]}).
-endif.
ensure_NAME(Token) ->
{_TokenTag, _Line, Text} = Token,
Text. %% BUGBUG: ensure length and chars
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_requestID,1}]}).
-endif.
ensure_requestID(Token) ->
case Token of
{safeToken, _Line, "*"} ->
?megaco_all_request_id;
_ ->
ensure_uint32(Token)
end.
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_streamID,1}]}).
-endif.
ensure_streamID(StreamId) ->
ensure_uint16(StreamId).
ensure_auth_header(SpiToken, SnToken, AdToken) ->
Spi = ensure_hex(SpiToken, 8, 8),
Sn = ensure_hex(SnToken, 8, 8),
Ad = ensure_hex(AdToken, 24, 64),
#'AuthenticationHeader'{secParmIndex = Spi, seqNum = Sn, ad = Ad}.
%% The values 0x0, 0xFFFFFFFE and 0xFFFFFFFF are reserved.
%% ContextID = (UINT32 / "*" / "-" / "$")
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_contextID,1}]}).
-endif.
ensure_contextID(Token) ->
{_TokenTag, Line, Text} = Token,
case Text of
"*" -> ?megaco_all_context_id;
"-" -> ?megaco_null_context_id;
"\$" -> ?megaco_choose_context_id;
Int ->
CID = ensure_uint32(Int),
if
(CID =/= 0) andalso
(CID =/= 16#FFFFFFFE) andalso
(CID =/= 16#FFFFFFFF) ->
CID;
true ->
return_error(Line, {bad_ContextID, CID})
end
end.
ensure_domainAddress([{_T, _L, _A} = Addr0], Port) ->
Addr = ensure_ip4addr(Addr0),
{ip4Address, #'IP4Address'{address = Addr, portNumber = Port}};
ensure_domainAddress([colon,colon], Port) ->
Addr = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
{ip6Address, #'IP6Address'{address = Addr, portNumber = Port}};
ensure_domainAddress(Addr0, Port) ->
Addr = ensure_ip6addr(Addr0),
{ip6Address, #'IP6Address'{address = Addr, portNumber = Port}}.
ensure_ip4addr(Token) ->
{_TokenTag, Line, Addr} = Token,
case split_ip4addr_text(Addr, []) of
[T1, T2, T3, T4] ->
%% We optimize by sending only the text part (Addr) of
%% the token to the function.
%% If something is wrong, then we do not get a proper
%% position and therefor we catch and issue the
%% the error again (with the proper line number).
case (catch [
ensure_uint(T1, 0, 255),
ensure_uint(T2, 0, 255),
ensure_uint(T3, 0, 255),
ensure_uint(T4, 0, 255)
]) of
A when is_list(A) ->
A;
_ ->
return_error(Line, {bad_IP4address, Addr})
end;
_ ->
return_error(Line, {bad_IP4address, Addr})
end.
split_ip4addr_text([], Acc) ->
[ lists:reverse(Acc) ];
split_ip4addr_text([$. | Rest], Acc) ->
[ lists:reverse(Acc) | split_ip4addr_text(Rest, []) ];
split_ip4addr_text([H | T], Acc) ->
split_ip4addr_text(T, [H | Acc]).
ensure_ip6addr([colon,colon|T]) ->
[H1|T1] = lists:reverse(T),
case do_ensure_ip6addr(T1, true, [ensure_hex4_or_ip4addr(H1)], 1) of
{true, A} when (length(A) =:= 16) ->
A;
{true, B} when length(B) < 16 ->
lists:duplicate(16 - length(B), 0) ++ B;
{true, C} ->
throw({error, {?MODULE, {bad_mid_ip6addr_length, C}}})
end;
ensure_ip6addr(L) ->
case lists:reverse(L) of
[colon, colon| T] ->
case do_ensure_ip6addr(T, true, [], 1) of
{true, A} when (length(A) =:= 16) ->
A;
{true, B} when length(B) < 16 ->
B ++ lists:duplicate(16 - length(B), 0);
{true, C} ->
throw({error, {?MODULE, {bad_mid_ip6addr_length, C}}})
end;
[H|L1] -> % A (last element) could be an ip4 address
case do_ensure_ip6addr(L1,false,[ensure_hex4_or_ip4addr(H)],1) of
{false, A} when (length(A) =:= 16) ->
A;
%% allow a pad even if the address is full (i.e. 16)
{true, B} when length(B) =< 17 ->
do_ensure_ip6addr_padding(B, 0);
{Pad, C} ->
throw({error, {?MODULE, {bad_mid_ip6addr_length, Pad, C}}})
end
end.
do_ensure_ip6addr([], Pad, Acc, _) ->
{Pad, lists:flatten(Acc)};
do_ensure_ip6addr([colon,colon|T], false, Acc, Line) ->
do_ensure_ip6addr(T, true, [pad|Acc], Line);
do_ensure_ip6addr([colon,colon|T], true, Acc, Line) ->
return_error(Line, {bad_mid_duplicate_padding, T, Acc});
do_ensure_ip6addr([colon|T], Pad, Acc, Line) ->
do_ensure_ip6addr(T, Pad, Acc, Line);
do_ensure_ip6addr([{_, Line, _} = A|T], Pad, Acc, _) ->
do_ensure_ip6addr(T, Pad, [ensure_hex4(A)|Acc], Line).
do_ensure_ip6addr_padding([], _) ->
[];
do_ensure_ip6addr_padding([pad|T], N) ->
lists:duplicate(16 - (N + length(T)), 0) ++ T;
do_ensure_ip6addr_padding([H|T], N) ->
[H|do_ensure_ip6addr_padding(T, N+1)].
ensure_hex4_or_ip4addr({TokenTag, Line, Addr} = V) ->
case string:tokens(Addr, [$.]) of
[T1, T2, T3, T4] ->
A1 = ensure_uint({TokenTag, Line, T1}, 0, 255),
A2 = ensure_uint({TokenTag, Line, T2}, 0, 255),
A3 = ensure_uint({TokenTag, Line, T3}, 0, 255),
A4 = ensure_uint({TokenTag, Line, T4}, 0, 255),
[A1, A2, A3, A4];
_ ->
ensure_hex4(V)
%% %% BMK BMK BMK
%% %% Here we should test for hexseq
%% return_error(Line, {bad_IP4address, Addr})
end.
ensure_hex4({_TokenTag, Line, Hex4})
when length(Hex4) =< 4, length(Hex4) > 0 ->
case (catch do_ensure_hex4(Hex4)) of
IL when is_list(IL) andalso (length(IL) =:= 2) ->
IL;
Error ->
return_error(Line, {bad_hex4, Hex4, Error})
end.
do_ensure_hex4([_H1, _H2, _H3, _H4] = H) ->
hex_to_int(H, []);
do_ensure_hex4([H2, H3, H4]) ->
hex_to_int([$0, H2, H3, H4], []);
do_ensure_hex4([H3, H4]) ->
hex_to_int([$0, $0, H3, H4], []);
do_ensure_hex4([H4]) ->
hex_to_int([$0, $0, $0, H4], []).
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_domainName,2}]}).
-endif.
ensure_domainName(Token, Port) ->
{_TokenTag, _Line, Name} = Token,
%% BUGBUG: validate name
{domainName, #'DomainName'{name = Name, portNumber = Port}}.
%% extensionParameter= "X" ("-" / "+") 1*6(ALPHA / DIGIT)
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_extensionParameter,1}]}).
-endif.
ensure_extensionParameter(Token) ->
{_TokenTag, Line, Text} = Token,
case Text of
[X, S | _Chars] ->
if
(X =/= $X) andalso (X =/= $x) andalso
(S =/= $+) andalso (S =/= $-) ->
return_error(Line, {bad_extension_parameter, Text});
true ->
{extension_parameter, Text}
end;
_ ->
return_error(Line, {bad_extension_parameter, Text})
end.
ensure_message(MegacopToken, MID, Body) ->
%% #'ServiceChangeProfile'{profileName = Name,
%% version = Version} =
%% ensure_profile(MegacopToken),
%% case Name of
%% "megaco" ->
%% #'Message'{version = Version, mId = MID, messageBody = Body};
%% [$!] ->
%% #'Message'{version = Version, mId = MID, messageBody = Body}
%% end.
{_TokenTag, Line, Text} = MegacopToken,
case split_Megacop(Text, []) of
{Name, Version} ->
Version2 = ensure_version(Version),
case Name of
"megaco" ->
#'Message'{version = Version2,
mId = MID,
messageBody = Body};
[$!] ->
#'Message'{version = Version2,
mId = MID,
messageBody = Body}
end;
_ ->
return_error(Line, {bad_name_or_version, Text})
end.
split_Megacop([], _) ->
error;
split_Megacop([$/ | Version], Acc) ->
{lists:reverse(Acc), Version};
split_Megacop([H | T], Acc) ->
split_Megacop(T, [H | Acc]).
%% Corr1:
%% As of corr 1 ModemDescriptor has been deprecated.
%% and since this functon is only used when creating
%% a ModemDescriptor, iit is removed.
%% modemType = (V32bisToken / V22bisToken / V18Token /
%% V22Token / V32Token / V34Token / V90Token /
%% V91Token / SynchISDNToken / extensionParameter)
%% ensure_modemType({_TokenTag, _Line, Text} = Token) ->
%% case Text of
%% "v32b" -> v32bis;
%% "v22b" -> v22bis;
%% "v18" -> v18;
%% "v22" -> v22;
%% "v32" -> v32;
%% "v34" -> v34;
%% "v90" -> v90;
%% "v91" -> v91;
%% "synchisdn" -> synchISDN;
%% "sn" -> synchISDN;
%% [$x | _] -> ensure_extensionParameter(Token)
%% end.
%% An mtp address is five octets long
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_mtpAddress,1}]}).
-endif.
ensure_mtpAddress(Token) ->
{_TokenTag, _Line, Addr} = Token,
%% BUGBUG: validate address
{mtpAddress, Addr}.
%% MuxType = ( H221Token / H223Token / H226Token / V76Token / extensionParameter )
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_muxType,1}]}).
-endif.
ensure_muxType(Token) ->
{_TokenTag, _Line, Text} = Token,
case Text of
"h221" -> h221;
"h223" -> h223;
"h226" -> h226;
"v76" -> v76;
"nx64k" -> nx64k; % v2
[$x | _] -> ensure_extensionParameter(Token)
end.
%% packagesItem = NAME "-" UINT16
%% NAME = ALPHA *63(ALPHA / DIGIT / "_" )
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_packagesItem,1}]}).
-endif.
ensure_packagesItem(Token) ->
{_TokenTag, Line, Text} = Token,
case split_packagesItem(Text, []) of
{Name, Version} ->
%% As we don't ensure length of the names, there is no point
%% in doing the ensure_NAME thing...
#'PackagesItem'{packageName = Name,
packageVersion = ensure_uint(Version, 0, 99)};
_ ->
return_error(Line, {bad_PackagesItem, Text})
end.
split_packagesItem([], _) ->
error;
split_packagesItem([$- | Version], Acc) ->
{lists:reverse(Acc), Version};
split_packagesItem([H|T], Acc) ->
split_packagesItem(T, [H|Acc]).
%% pkgdName = (PackageName / "*") SLASH (ItemID / "*" )
%% PackageName = NAME
%% ItemID = NAME
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_pkgdName,1}]}).
-endif.
ensure_pkgdName(Token) ->
{_TokenTag, Line, Text} = Token,
case ensure_pkgdName(Text, []) of
ok ->
%% As we don't really do any checks on the strings
%% (length or content) there is really no point in
%% "ensuring" the name and item part of the
%% package name
%% ensure_name_or_star(Name),
%% ensure_name_or_star(Item),
Text;
_ ->
return_error(Line, {bad_pkgdName, Text})
end.
ensure_pkgdName([], _) ->
error;
ensure_pkgdName([$/ | T], Acc)
when ((length(T) > 0) andalso (length(Acc) > 0)) ->
ok;
ensure_pkgdName([H | T], Acc) ->
ensure_pkgdName(T, [H | Acc]).
%% -compile({inline,[{ensure_name_or_star,1}]}).
%% ensure_name_or_star(Val) ->
%% %% case Token of
%% %% {_, _, Name} when Name =:= "*" ->
%% %% Name;
%% %% _ ->
%% %% ensure_NAME(Token)
%% %% end.
%% if
%% Val =:= "*" ->
%% Val;
%% true ->
%% %% as we don't really validate the text part of the token(s),
%% %% we can just return the value assuming it to be correct...
%% Val
%% end.
%% v2 - start
merge_indAudMediaDescriptor({termStateDescr, Val}) ->
#'IndAudMediaDescriptor'{termStateDescr = Val};
merge_indAudMediaDescriptor({streamParm, Val}) ->
#'IndAudMediaDescriptor'{streams = {oneStream, Val}};
merge_indAudMediaDescriptor({streamDescr, Val}) ->
#'IndAudMediaDescriptor'{streams = {multiStream, [Val]}}.
-ifdef(megaco_parser_inline).
-compile({inline,[{merge_indAudLocalControlDescriptor,1}]}).
-endif.
merge_indAudLocalControlDescriptor(Parms) ->
do_merge_indAudLocalControlDescriptor(Parms,
#'IndAudLocalControlDescriptor'{}).
do_merge_indAudLocalControlDescriptor([Parm | Parms], Desc) ->
case Parm of
modeToken when Desc#'IndAudLocalControlDescriptor'.streamMode == asn1_NOVALUE ->
Desc2 = Desc#'IndAudLocalControlDescriptor'{streamMode = 'NULL'},
do_merge_indAudLocalControlDescriptor(Parms, Desc2);
reservedGroupToken when Desc#'IndAudLocalControlDescriptor'.reserveGroup == asn1_NOVALUE ->
Desc2 = Desc#'IndAudLocalControlDescriptor'{reserveGroup = 'NULL'},
do_merge_indAudLocalControlDescriptor(Parms, Desc2);
reservedValueToken when Desc#'IndAudLocalControlDescriptor'.reserveValue == asn1_NOVALUE ->
Desc2 = Desc#'IndAudLocalControlDescriptor'{reserveValue = 'NULL'},
do_merge_indAudLocalControlDescriptor(Parms, Desc2);
{pkgdName, Val} when Desc#'IndAudLocalControlDescriptor'.propertyParms == asn1_NOVALUE ->
PropParms = [#'IndAudPropertyParm'{name = Val}],
Desc2 = Desc#'IndAudLocalControlDescriptor'{propertyParms = PropParms},
do_merge_indAudLocalControlDescriptor(Parms, Desc2);
{pkgdName, Val} when is_list(Desc#'IndAudLocalControlDescriptor'.propertyParms) ->
PropParms = Desc#'IndAudLocalControlDescriptor'.propertyParms,
PropParms2 = [#'IndAudPropertyParm'{name = Val} | PropParms],
Desc2 = Desc#'IndAudLocalControlDescriptor'{propertyParms = PropParms2},
do_merge_indAudLocalControlDescriptor(Parms, Desc2)
end;
do_merge_indAudLocalControlDescriptor([], Desc) ->
case Desc#'IndAudLocalControlDescriptor'.propertyParms of
[_ | _] = PropParms -> % List has more then one element
PropParms2= lists:reverse(PropParms),
Desc#'IndAudLocalControlDescriptor'{propertyParms = PropParms2};
_ ->
Desc
end.
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_indAudLocalParm,1}]}).
-endif.
ensure_indAudLocalParm(Token) ->
case Token of
{safeToken, _Line, "mode"} -> modeToken;
{safeToken, _Line, "mo"} -> modeToken;
{safeToken, _Line, "reservedgroup"} -> reservedGroupToken;
{safeToken, _Line, "rg"} -> reservedGroupToken;
{safeToken, _Line, "reservedvalue"} -> reservedValueToken;
{safeToken, _Line, "rv"} -> reservedValueToken;
PkgdName -> {pkgdName,
ensure_pkgdName(PkgdName)}
end.
merge_indAudTerminationStateDescriptor({pkgdName, Val}) ->
PropParm = #'IndAudPropertyParm'{name = Val},
#'IndAudTerminationStateDescriptor'{propertyParms = [PropParm]};
merge_indAudTerminationStateDescriptor(serviceStatesToken) ->
#'IndAudTerminationStateDescriptor'{serviceState = 'NULL'};
merge_indAudTerminationStateDescriptor(bufferToken) ->
#'IndAudTerminationStateDescriptor'{eventBufferControl = 'NULL'}.
-ifdef(megaco_parser_inline).
-compile({inline,[{merge_indAudEventBufferDescriptor,2}]}).
-endif.
merge_indAudEventBufferDescriptor(EventName, SpecParams) ->
IAEBD = #'IndAudEventBufferDescriptor'{eventName = EventName},
do_merge_indAudEventBufferDescriptor(SpecParams, IAEBD).
do_merge_indAudEventBufferDescriptor(asn1_NOVALUE, IAEBD) ->
IAEBD;
do_merge_indAudEventBufferDescriptor({streamID, StreamID}, IAEBD) ->
IAEBD#'IndAudEventBufferDescriptor'{streamID = StreamID};
do_merge_indAudEventBufferDescriptor({eventParameterName, _Name} = EPN,
IAEBD) ->
%% BUGBUG BUGBUG BUGBUG
%% This is an ugly hack to allow the eventParamName which only
%% exists in the text encoding...
IAEBD#'IndAudEventBufferDescriptor'{streamID = EPN}.
ensure_indAudSignalListParm(SIG) when is_record(SIG, 'Signal') ->
ensure_indAudSignal(SIG).
ensure_indAudSignal(#'Signal'{signalName = SignalName,
streamID = asn1_NOVALUE,
sigType = asn1_NOVALUE,
duration = asn1_NOVALUE,
notifyCompletion = asn1_NOVALUE,
keepActive = asn1_NOVALUE,
sigParList = []}) ->
#'IndAudSignal'{signalName = SignalName}.
ensure_IADMD({_TokenTag, _Line,
#'DigitMapDescriptor'{digitMapName = Name,
digitMapValue = asn1_NOVALUE}}) ->
#'IndAudDigitMapDescriptor'{digitMapName = Name}.
merge_indAudPackagesDescriptor(#'PackagesItem'{packageName = N,
packageVersion = V}) ->
#'IndAudPackagesDescriptor'{packageName = N,
packageVersion = V}.
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_indAudTerminationStateParm,1}]}).
-endif.
ensure_indAudTerminationStateParm(Token) ->
case Token of
{safeToken, _Line, "servicestates"} -> serviceStatesToken;
{safeToken, _Line, "si"} -> serviceStatesToken;
{safeToken, _Line, "buffer"} -> bufferToken;
{safeToken, _Line, "bf"} -> bufferToken;
PkgdName -> {pkgdName,
ensure_pkgdName(PkgdName)}
end.
%% Types modified by v2:
merge_auditDescriptor([]) ->
#'AuditDescriptor'{};
merge_auditDescriptor(Tokens) when is_list(Tokens) ->
case lists:keysearch(terminationAudit, 1, Tokens) of
{value, {terminationAudit, TA}} ->
case lists:keydelete(terminationAudit, 1, Tokens) of
[] ->
#'AuditDescriptor'{auditPropertyToken = TA};
AuditTokens ->
#'AuditDescriptor'{auditToken = AuditTokens,
auditPropertyToken = TA}
end;
false ->
#'AuditDescriptor'{auditToken = Tokens}
end;
merge_auditDescriptor(_) ->
#'AuditDescriptor'{}.
%% v2 - end
-ifdef(megaco_parser_inline).
-compile({inline,[{merge_ServiceChangeParm,1}]}).
-endif.
merge_ServiceChangeParm(Parms) ->
Required = [serviceChangeReason, serviceChangeMethod],
merge_ServiceChangeParm(Parms, #'ServiceChangeParm'{}, Required).
merge_ServiceChangeParm([], SCP, []) ->
SCP;
merge_ServiceChangeParm([], _SCP, Required) ->
exit({missing_required_serviceChangeParm, Required});
merge_ServiceChangeParm([{address, Val}|Parms], SCP0, Req)
when ((SCP0#'ServiceChangeParm'.serviceChangeAddress =:= asn1_NOVALUE)
andalso
(SCP0#'ServiceChangeParm'.serviceChangeMgcId =:= asn1_NOVALUE)) ->
SCP = SCP0#'ServiceChangeParm'{serviceChangeAddress = Val},
merge_ServiceChangeParm(Parms, SCP, Req);
merge_ServiceChangeParm([{address, Val}|_Parms], SCP0, _Req)
when SCP0#'ServiceChangeParm'.serviceChangeAddress == asn1_NOVALUE ->
MgcId = SCP0#'ServiceChangeParm'.serviceChangeMgcId,
exit({not_both_address_mgcid_serviceChangeParm, Val, MgcId});
merge_ServiceChangeParm([{mgc_id, Val}|Parms], SCP0, Req)
when ((SCP0#'ServiceChangeParm'.serviceChangeMgcId =:= asn1_NOVALUE) andalso
(SCP0#'ServiceChangeParm'.serviceChangeAddress =:= asn1_NOVALUE)) ->
SCP = SCP0#'ServiceChangeParm'{serviceChangeMgcId = Val},
merge_ServiceChangeParm(Parms, SCP, Req);
merge_ServiceChangeParm([{mgc_id, Val}|_Parms], SCP0, _Req)
when SCP0#'ServiceChangeParm'.serviceChangeMgcId == asn1_NOVALUE ->
Addr = SCP0#'ServiceChangeParm'.serviceChangeAddress,
exit({not_both_address_mgcid_serviceChangeParm, Val, Addr});
merge_ServiceChangeParm([{profile, Val}|Parms], SCP0, Req)
when SCP0#'ServiceChangeParm'.serviceChangeProfile == asn1_NOVALUE ->
SCP = SCP0#'ServiceChangeParm'{serviceChangeProfile = Val},
merge_ServiceChangeParm(Parms, SCP, Req);
merge_ServiceChangeParm([{version, Val}|Parms], SCP0, Req)
when SCP0#'ServiceChangeParm'.serviceChangeVersion == asn1_NOVALUE ->
SCP = SCP0#'ServiceChangeParm'{serviceChangeVersion = Val},
merge_ServiceChangeParm(Parms, SCP, Req);
%% REQUIRED (i.e. no default value)
merge_ServiceChangeParm([{reason, Val}|Parms], SCP0, Req0)
when SCP0#'ServiceChangeParm'.serviceChangeReason == undefined ->
SCP = SCP0#'ServiceChangeParm'{serviceChangeReason = Val},
Req = lists:delete(serviceChangeReason, Req0),
merge_ServiceChangeParm(Parms, SCP, Req);
merge_ServiceChangeParm([{delay, Val}|Parms], SCP0, Req)
when SCP0#'ServiceChangeParm'.serviceChangeDelay == asn1_NOVALUE ->
SCP = SCP0#'ServiceChangeParm'{serviceChangeDelay = Val},
merge_ServiceChangeParm(Parms, SCP, Req);
%% REQUIRED (i.e. no default value)
merge_ServiceChangeParm([{method, Val}|Parms], SCP0, Req0)
when SCP0#'ServiceChangeParm'.serviceChangeMethod == undefined ->
SCP = SCP0#'ServiceChangeParm'{serviceChangeMethod = Val},
Req = lists:delete(serviceChangeMethod, Req0),
merge_ServiceChangeParm(Parms, SCP, Req);
merge_ServiceChangeParm([{time_stamp, Val}|Parms], SCP0, Req)
when SCP0#'ServiceChangeParm'.timeStamp == asn1_NOVALUE ->
SCP = SCP0#'ServiceChangeParm'{timeStamp = Val},
merge_ServiceChangeParm(Parms, SCP, Req);
merge_ServiceChangeParm([{extension, _Val}|Parms], SCP0, Req) ->
merge_ServiceChangeParm(Parms, SCP0, Req);
%% OTP-5353.
merge_ServiceChangeParm([{audit_item, Val}|Parms], SCP0, Req)
when ((SCP0#'ServiceChangeParm'.serviceChangeInfo =:= asn1_NOVALUE)
andalso is_atom(Val)) ->
SCI = #'AuditDescriptor'{auditToken = [Val]},
SCP = SCP0#'ServiceChangeParm'{serviceChangeInfo = SCI},
merge_ServiceChangeParm(Parms, SCP, Req);
merge_ServiceChangeParm([{audit_item, Val}|Parms], SCP0, Req)
when ((SCP0#'ServiceChangeParm'.serviceChangeInfo =:= asn1_NOVALUE)
andalso is_tuple(Val)) ->
SCI = #'AuditDescriptor'{auditPropertyToken = [Val]},
SCP = SCP0#'ServiceChangeParm'{serviceChangeInfo = SCI},
merge_ServiceChangeParm(Parms, SCP, Req);
merge_ServiceChangeParm([{audit_item, Val}|Parms], SCP0, Req)
when (is_record(SCP0#'ServiceChangeParm'.serviceChangeInfo, 'AuditDescriptor')
andalso is_atom(Val)) ->
SCI0 = SCP0#'ServiceChangeParm'.serviceChangeInfo,
L = SCI0#'AuditDescriptor'.auditToken,
SCI = SCI0#'AuditDescriptor'{auditToken = [Val|L]},
SCP = SCP0#'ServiceChangeParm'{serviceChangeInfo = SCI},
merge_ServiceChangeParm(Parms, SCP, Req);
merge_ServiceChangeParm([{audit_item, Val}|Parms], SCP0, Req)
when (is_record(SCP0#'ServiceChangeParm'.serviceChangeInfo, 'AuditDescriptor')
andalso is_tuple(Val)) ->
SCI0 = SCP0#'ServiceChangeParm'.serviceChangeInfo,
L = SCI0#'AuditDescriptor'.auditPropertyToken,
SCI = SCI0#'AuditDescriptor'{auditPropertyToken = [Val|L]},
SCP = SCP0#'ServiceChangeParm'{serviceChangeInfo = SCI},
merge_ServiceChangeParm(Parms, SCP, Req);
merge_ServiceChangeParm([{Tag, Val}|_Parms], SCP, _Req) ->
Val2 =
case Tag of
address ->
SCP#'ServiceChangeParm'.serviceChangeAddress;
mgc_id ->
SCP#'ServiceChangeParm'.serviceChangeMgcId;
profile ->
SCP#'ServiceChangeParm'.serviceChangeProfile;
version ->
SCP#'ServiceChangeParm'.serviceChangeVersion;
reason ->
SCP#'ServiceChangeParm'.serviceChangeReason;
delay ->
SCP#'ServiceChangeParm'.serviceChangeDelay;
method ->
SCP#'ServiceChangeParm'.serviceChangeMethod;
time_stamp ->
SCP#'ServiceChangeParm'.timeStamp;
audit_item ->
SCP#'ServiceChangeParm'.serviceChangeInfo
end,
exit({at_most_once_serviceChangeParm, {Tag, Val, Val2}}).
-ifdef(megaco_parser_inline).
-compile({inline,[{merge_ServiceChangeResParm,1}]}).
-endif.
merge_ServiceChangeResParm(Parms) ->
merge_ServiceChangeResParm(Parms, #'ServiceChangeResParm'{}).
merge_ServiceChangeResParm([], SCRP) ->
SCRP;
merge_ServiceChangeResParm([{address, Val}|Parms], SCRP0)
when SCRP0#'ServiceChangeResParm'.serviceChangeAddress == asn1_NOVALUE,
SCRP0#'ServiceChangeResParm'.serviceChangeMgcId == asn1_NOVALUE ->
SCRP = SCRP0#'ServiceChangeResParm'{serviceChangeAddress = Val},
merge_ServiceChangeResParm(Parms, SCRP);
merge_ServiceChangeResParm([{address, Val}|_Parms], SCRP0)
when SCRP0#'ServiceChangeResParm'.serviceChangeAddress == asn1_NOVALUE ->
MgcId = SCRP0#'ServiceChangeResParm'.serviceChangeMgcId,
exit({not_both_address_mgcid_servChgReplyParm, Val, MgcId});
merge_ServiceChangeResParm([{mgc_id, Val}|Parms], SCRP0)
when SCRP0#'ServiceChangeResParm'.serviceChangeMgcId == asn1_NOVALUE,
SCRP0#'ServiceChangeResParm'.serviceChangeAddress == asn1_NOVALUE ->
SCRP = SCRP0#'ServiceChangeResParm'{serviceChangeMgcId = Val},
merge_ServiceChangeResParm(Parms, SCRP);
merge_ServiceChangeResParm([{mgc_id, Val}|_Parms], SCRP0)
when SCRP0#'ServiceChangeResParm'.serviceChangeMgcId == asn1_NOVALUE ->
Addr = SCRP0#'ServiceChangeResParm'.serviceChangeAddress,
exit({not_both_address_mgcid_servChgReplyParm, Val, Addr});
merge_ServiceChangeResParm([{profile, Val}|Parms], SCRP0)
when SCRP0#'ServiceChangeResParm'.serviceChangeProfile == asn1_NOVALUE ->
SCRP = SCRP0#'ServiceChangeResParm'{serviceChangeProfile = Val},
merge_ServiceChangeResParm(Parms, SCRP);
merge_ServiceChangeResParm([{version, Val}|Parms], SCRP0)
when SCRP0#'ServiceChangeResParm'.serviceChangeVersion == asn1_NOVALUE ->
SCRP = SCRP0#'ServiceChangeResParm'{serviceChangeVersion = Val},
merge_ServiceChangeResParm(Parms, SCRP);
merge_ServiceChangeResParm([{time_stamp, Val}|Parms], SCRP0)
when SCRP0#'ServiceChangeResParm'.timeStamp == asn1_NOVALUE ->
SCRP = SCRP0#'ServiceChangeResParm'{timeStamp = Val},
merge_ServiceChangeResParm(Parms, SCRP);
merge_ServiceChangeResParm([{Tag, Val}|_Parms], SCRP) ->
Val2 =
case Tag of
address -> SCRP#'ServiceChangeResParm'.serviceChangeAddress;
mgc_id -> SCRP#'ServiceChangeResParm'.serviceChangeMgcId;
profile -> SCRP#'ServiceChangeResParm'.serviceChangeProfile;
version -> SCRP#'ServiceChangeResParm'.serviceChangeVersion;
time_stamp -> SCRP#'ServiceChangeResParm'.timeStamp
end,
exit({at_most_once_servChgReplyParm, {Tag, Val, Val2}}).
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_serviceChangeMethod,1}]}).
-endif.
ensure_serviceChangeMethod(Token) ->
case Token of
{safeToken, _Line, "fl"} ->
failover;
{safeToken, _Line, "failover"} ->
failover;
{safeToken, _Line, "fo"} ->
forced;
{safeToken, _Line, "forced"} ->
forced;
{safeToken, _Line, "gr"} ->
graceful;
{safeToken, _Line, "graceful"} ->
graceful;
{safeToken, _Line, "rs"} ->
restart;
{safeToken, _Line, "restart"} ->
restart;
{safeToken, _Line, "dc"} ->
disconnected;
{safeToken, _Line, "disconnected"} ->
disconnected;
{safeToken, _Line, "ho"} ->
handOff;
{safeToken, _Line, "handoff"} ->
handOff;
{safeToken, Line, Text} ->
return_error(Line, {bad_serviceChangeMethod, Text})
end.
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_profile,1}]}).
-endif.
ensure_profile({_TokenTag, Line, Text}) ->
case string:tokens(Text, [$/]) of
[Name, Version] ->
Version2 = ensure_version(Version),
#'ServiceChangeProfile'{profileName = Name, version = Version2};
_ ->
return_error(Line, {bad_profile, Text})
end.
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_version,1}]}).
-endif.
ensure_version(Version) ->
ensure_uint(Version, 0, 99).
-ifdef(megaco_parser_inline).
-compile({inline,[{merge_signalRequest,2}]}).
-endif.
merge_signalRequest(SignalName, PropertyParms) ->
Sig = #'Signal'{signalName = SignalName},
SPL = [],
do_merge_signalRequest(Sig, PropertyParms, SPL).
do_merge_signalRequest(Sig, [H | T], SPL) ->
case H of
{stream, StreamId} when Sig#'Signal'.streamID == asn1_NOVALUE ->
do_merge_signalRequest(Sig#'Signal'{streamID = StreamId}, T, SPL);
{signal_type, SigType} when Sig#'Signal'.sigType == asn1_NOVALUE ->
do_merge_signalRequest(Sig#'Signal'{sigType = SigType}, T, SPL);
{duration, Duration} when Sig#'Signal'.duration == asn1_NOVALUE ->
do_merge_signalRequest(Sig#'Signal'{duration = Duration}, T, SPL);
{notify_completion, NC} when Sig#'Signal'.notifyCompletion == asn1_NOVALUE ->
do_merge_signalRequest(Sig#'Signal'{notifyCompletion = NC}, T, SPL);
keepActive when Sig#'Signal'.keepActive == asn1_NOVALUE->
do_merge_signalRequest(Sig#'Signal'{keepActive = true}, T, SPL);
{other, Name, PP} ->
SP = #'SigParameter'{sigParameterName = Name,
value = PP#'PropertyParm'.value,
extraInfo = PP#'PropertyParm'.extraInfo},
do_merge_signalRequest(Sig, T, [SP | SPL]);
_ ->
return_error(0, {bad_sigParm, H})
end;
do_merge_signalRequest(Sig, [], SPL) ->
Sig#'Signal'{sigParList = lists:reverse(SPL)} .
%% eventStream = StreamToken EQUAL StreamID
%% eventOther = eventParameterName parmValue
-ifdef(megaco_parser_inline).
-compile({inline,[{select_stream_or_other,2}]}).
-endif.
select_stream_or_other(EventParameterName, ParmValue) ->
if
(EventParameterName =:= "st") orelse
(EventParameterName =:= "stream") ->
case ParmValue of
#'PropertyParm'{value = [Value]} ->
{stream, ensure_uint16(Value)};
_ ->
{stream, ensure_uint16(ParmValue)}
end;
true ->
#'PropertyParm'{value = Value} = ParmValue,
EP = #'EventParameter'{eventParameterName = EventParameterName,
value = Value},
{other, EP}
end.
%% select_stream_or_other("st", #'PropertyParm'{value = [Value]}) ->
%% {stream, ensure_uint16(Value)};
%% select_stream_or_other("st", Value) ->
%% {stream, ensure_uint16(Value)};
%% select_stream_or_other("stream", #'PropertyParm'{value = [Value]}) ->
%% {stream, ensure_uint16(Value)};
%% select_stream_or_other("stream", Value) ->
%% {stream, ensure_uint16(Value)};
%% select_stream_or_other(Name, #'PropertyParm'{value = Value}) ->
%% EP = #'EventParameter'{eventParameterName = Name,
%% value = Value},
%% {other, EP}.
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_eventDM,1}]}).
-endif.
ensure_eventDM(Token) ->
{_TokenTag, Line, DMD} = Token,
if
is_record(DMD, 'DigitMapDescriptor') ->
Name = DMD#'DigitMapDescriptor'.digitMapName,
Val = DMD#'DigitMapDescriptor'.digitMapValue,
if
(Name =:= asn1_NOVALUE) andalso (Val =/= asn1_NOVALUE) ->
{'DigitMapValue', Start, Short, Long, Duration, Body} = Val,
DMV = #'DigitMapValue'{startTimer = Start,
shortTimer = Short,
longTimer = Long,
digitMapBody = Body,
durationTimer = Duration},
{eventDM, {digitMapValue, DMV}};
(Name =/= asn1_NOVALUE) andalso (Val =:= asn1_NOVALUE) ->
{eventDM, {digitMapName, Name}};
true ->
return_error(Line, {bad_eventDM, DMD})
end;
true ->
return_error(Line, {bad_eventDM, DMD})
end.
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_DMD,1}]}).
-endif.
ensure_DMD(Token) ->
{_TokenTag, Line, DMD} = Token,
if
is_record(DMD, 'DigitMapDescriptor') ->
Val2 =
case DMD#'DigitMapDescriptor'.digitMapValue of
%% Note that the values of the digitMapBody and
%% durationTimers are swapped by the scanner
%% (this is done because of a problem in the flex scanner).
#'DigitMapValue'{startTimer = asn1_NOVALUE,
shortTimer = asn1_NOVALUE,
longTimer = asn1_NOVALUE,
durationTimer = [],
digitMapBody = asn1_NOVALUE} ->
asn1_NOVALUE;
#'DigitMapValue'{durationTimer = Body,
digitMapBody = Duration} = DMV ->
%% Convert to version 1 DigitMapValue
DMV#'DigitMapValue'{digitMapBody = Body,
durationTimer = Duration};
Other ->
Other
end,
DMD#'DigitMapDescriptor'{digitMapValue = Val2};
true ->
return_error(Line, {bad_DigitMapDescriptor, DMD})
end.
-ifdef(megaco_parser_inline).
-compile({inline,[{merge_observed_event,3}]}).
-endif.
merge_observed_event(ObservedEvents, EventName, TimeStamp) ->
StreamId = asn1_NOVALUE,
EPL = [],
do_merge_observed_event(ObservedEvents, EventName, TimeStamp, StreamId, EPL).
do_merge_observed_event([{stream, StreamID} | T], EventName, TimeStamp, asn1_NOVALUE, EPL) ->
do_merge_observed_event(T, EventName, TimeStamp, StreamID, EPL);
do_merge_observed_event([{other, PP} | T], EventName, TimeStamp, StreamID, EPL) ->
do_merge_observed_event(T, EventName, TimeStamp, StreamID, [PP | EPL]);
do_merge_observed_event([], EventName, TimeStamp, StreamID, EPL) ->
#'ObservedEvent'{eventName = EventName,
timeNotation = TimeStamp,
streamID = StreamID,
eventParList = lists:reverse(EPL)}.
merge_eventSpec(OE)
when is_record(OE, 'ObservedEvent') andalso
(OE#'ObservedEvent'.timeNotation == asn1_NOVALUE) ->
#'EventSpec'{eventName = OE#'ObservedEvent'.eventName,
streamID = OE#'ObservedEvent'.streamID,
eventParList = OE#'ObservedEvent'.eventParList};
merge_eventSpec(OE) ->
return_error(0, {bad_event_spec, OE}).
-ifdef(megaco_parser_inline).
-compile({inline,[{merge_eventParameters,1}]}).
-endif.
merge_eventParameters(Params) ->
StreamId = asn1_NOVALUE,
EPL = [],
RA = #'RequestedActions'{},
HasA = no,
do_merge_eventParameters(Params, StreamId, EPL, RA, HasA) .
do_merge_eventParameters([H | T], StreamId, EPL, RA, HasA) ->
case H of
keepActive when RA#'RequestedActions'.keepActive == asn1_NOVALUE ->
RA2 = RA#'RequestedActions'{keepActive = true},
do_merge_eventParameters(T, StreamId, EPL, RA2, yes);
{embed, SD, SED} when RA#'RequestedActions'.signalsDescriptor == asn1_NOVALUE ->
RA2 = RA#'RequestedActions'{signalsDescriptor = SD,
secondEvent = SED},
do_merge_eventParameters(T, StreamId, EPL, RA2, yes);
{eventDM, DM} when RA#'RequestedActions'.eventDM == asn1_NOVALUE ->
RA2 = RA#'RequestedActions'{eventDM = DM},
do_merge_eventParameters(T, StreamId, EPL, RA2, yes);
{stream, NewStreamId} when StreamId == asn1_NOVALUE ->
do_merge_eventParameters(T, NewStreamId, EPL, RA, HasA);
{other, PP} when is_record(PP, 'PropertyParm') ->
EP = #'EventParameter'{eventParameterName = PP#'PropertyParm'.name,
value = PP#'PropertyParm'.value,
extraInfo = PP#'PropertyParm'.extraInfo},
do_merge_eventParameters(T, StreamId, [EP | EPL], RA, HasA);
{other, EP} when is_record(EP, 'EventParameter') ->
do_merge_eventParameters(T, StreamId, [EP | EPL], RA, HasA);
_ ->
return_error(0, {bad_eventParameter, H})
end;
do_merge_eventParameters([], StreamId, EPL, RA, yes) ->
#'RequestedEvent'{streamID = StreamId,
eventAction = RA,
evParList = lists:reverse(EPL)};
do_merge_eventParameters([], StreamId, EPL, _RA, no) ->
#'RequestedEvent'{streamID = StreamId,
eventAction = asn1_NOVALUE,
evParList = lists:reverse(EPL)}.
-ifdef(megaco_parser_inline).
-compile({inline,[{merge_secondEventParameters,1}]}).
-endif.
merge_secondEventParameters(Params) ->
StreamId = asn1_NOVALUE,
EPL = [],
SRA = #'SecondRequestedActions'{},
HasA = no,
do_merge_secondEventParameters(Params, StreamId, EPL, SRA, HasA) .
do_merge_secondEventParameters([H | T], StreamId, EPL, SRA, HasA) ->
case H of
keepActive when SRA#'SecondRequestedActions'.keepActive == asn1_NOVALUE ->
SRA2 = SRA#'SecondRequestedActions'{keepActive = true},
do_merge_secondEventParameters(T, StreamId, EPL, SRA2, yes);
{second_embed, SD} when SRA#'SecondRequestedActions'.signalsDescriptor == asn1_NOVALUE ->
SRA2 = SRA#'SecondRequestedActions'{signalsDescriptor = SD},
do_merge_secondEventParameters(T, StreamId, EPL, SRA2, yes);
{eventDM, DM} when SRA#'SecondRequestedActions'.eventDM == asn1_NOVALUE ->
SRA2 = SRA#'SecondRequestedActions'{eventDM = DM},
do_merge_secondEventParameters(T, StreamId, EPL, SRA2, yes);
{stream, NewStreamId} when StreamId == asn1_NOVALUE ->
do_merge_secondEventParameters(T, NewStreamId, EPL, SRA, HasA);
{other, PP} when is_record(PP, 'PropertyParm') ->
EP = #'EventParameter'{eventParameterName = PP#'PropertyParm'.name,
value = PP#'PropertyParm'.value,
extraInfo = PP#'PropertyParm'.extraInfo},
do_merge_secondEventParameters(T, StreamId, [EP | EPL], SRA, HasA);
{other, EP} when is_record(EP, 'EventParameter') ->
do_merge_secondEventParameters(T, StreamId, [EP | EPL], SRA, HasA);
_ ->
return_error(0, {bad_secondEventParameter, H})
end;
do_merge_secondEventParameters([], StreamId, EPL, SRA, yes) ->
#'SecondRequestedEvent'{streamID = StreamId,
eventAction = SRA,
evParList = lists:reverse(EPL)};
do_merge_secondEventParameters([], StreamId, EPL, _SRA, no) ->
#'SecondRequestedEvent'{streamID = StreamId,
eventAction = asn1_NOVALUE,
evParList = lists:reverse(EPL)}.
%% terminationID = "ROOT" / pathName / "$" / "*"
%% Total length of pathName must not exceed 64 chars.
%% pathName = ["*"] NAME *("/" / "*"/ ALPHA / DIGIT /"_" / "$" )
%% ["@" pathDomainName ]
%% ABNF allows two or more consecutive "." although it is meaningless
%% in a path domain name.
%% pathDomainName = (ALPHA / DIGIT / "*" )
%% *63(ALPHA / DIGIT / "-" / "*" / ".")
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_terminationID,1}]}).
-endif.
ensure_terminationID(Token) ->
{safeToken, _Line, LowerText} = Token,
%% terminationID = "ROOT" / pathName / "$" / "*"
decode_term_id(LowerText, false, [], []).
decode_term_id([H | T], Wild, Id, Component) ->
case H of
$/ -> decode_term_id(T, Wild, [lists:reverse(Component) | Id], []);
$* -> decode_term_id(T, true, Id, [?megaco_all | Component]);
$$ -> decode_term_id(T, true, Id, [?megaco_choose | Component]);
_ -> decode_term_id(T, Wild, Id, [H | Component])
end;
decode_term_id([], Wild, Id, Component) ->
Id2 = [lists:reverse(Component) | Id],
#megaco_term_id{contains_wildcards = Wild, id = lists:reverse(Id2)}.
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_pathName,1}]}).
-endif.
ensure_pathName(Token) ->
{_TokenTag, _Line, Text} = Token,
Text. %% BUGBUG: ensure values
%% TimeStamp = Date "T" Time ; per ISO 8601:1988
%% Date = 8(DIGIT) ; Date = yyyymmdd
%% Time = 8(DIGIT) ; Time = hhmmssss
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_timeStamp,1}]}).
-endif.
ensure_timeStamp(Token) ->
{'TimeStampToken', Line, Text} = Token,
case string:tokens(Text, [$T, $t]) of
[Date, Time] ->
#'TimeNotation'{date = Date, time = Time};
_ ->
return_error(Line, {bad_timeStamp, Text})
end.
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_transactionID,1}]}).
-endif.
ensure_transactionID(TransId) ->
ensure_uint32(TransId).
%% transactionAck = transactionID / (transactionID "-" transactionID)
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_transactionAck,1}]}).
-endif.
ensure_transactionAck(Tokens) ->
{safeToken, _Line, Text} = Tokens,
ensure_transactionAck2(Text, []).
ensure_transactionAck2([], Acc) ->
Id = lists:reverse(Acc),
#'TransactionAck'{firstAck = ensure_transactionID(Id)};
ensure_transactionAck2([$- | Id2], Acc) ->
Id1 = lists:reverse(Acc),
#'TransactionAck'{firstAck = ensure_transactionID(Id1),
lastAck = ensure_transactionID(Id2)};
ensure_transactionAck2([H|T], Acc) ->
ensure_transactionAck2(T, [H|Acc]).
-ifdef(megaco_parser_inline).
-compile({inline,[{merge_action_requests,2}]}).
-endif.
merge_action_requests(CtxId, Items) ->
CtxReq = #'ContextRequest'{},
CtxAuditReq = asn1_NOVALUE,
CmdReq = [],
TopReq = [],
do_merge_action_requests(CtxId, CtxReq, CtxAuditReq, CmdReq, TopReq, Items).
do_merge_action_requests(CtxId, CtxReq, CtxAuditReq, CmdReq, TopReq, [H | T]) ->
case H of
_ when is_record(H, 'CommandRequest') ->
do_merge_action_requests(CtxId, CtxReq, CtxAuditReq, [H | CmdReq], TopReq, T);
{priority, Int} when CtxReq#'ContextRequest'.priority == asn1_NOVALUE ->
CtxReq2 = CtxReq#'ContextRequest'{priority = Int},
do_merge_action_requests(CtxId, CtxReq2, CtxAuditReq, CmdReq,
TopReq, T);
{emergency, Bool} when CtxReq#'ContextRequest'.emergency == asn1_NOVALUE ->
CtxReq2 = CtxReq#'ContextRequest'{emergency = Bool},
do_merge_action_requests(CtxId, CtxReq2, CtxAuditReq, CmdReq,
TopReq, T);
{topology, Desc} ->
TopReq2 = Desc ++ TopReq, %% OTP-4088
do_merge_action_requests(CtxId, CtxReq, CtxAuditReq, CmdReq,
TopReq2, T);
{contextAudit, CAs} ->
CtxAuditReq2 = merge_context_attr_audit_request(CtxAuditReq, CAs),
do_merge_action_requests(CtxId, CtxReq, CtxAuditReq2, CmdReq,
TopReq, T)
end;
do_merge_action_requests(CtxId, CtxReq, CtxAuditReq, CmdReq, TopReq, []) ->
#'ActionRequest'{contextId = CtxId,
contextRequest = strip_contextRequest(CtxReq, TopReq),
contextAttrAuditReq = strip_contextAttrAuditRequest(CtxAuditReq),
commandRequests = lists:reverse(CmdReq)}.
merge_context_attr_audit_request(asn1_NOVALUE, CAs) ->
merge_context_attr_audit_request(#'ContextAttrAuditRequest'{}, CAs);
merge_context_attr_audit_request(CAAR, [H|T]) ->
CAAR2 =
case H of
priorityAudit when CAAR#'ContextAttrAuditRequest'.priority == asn1_NOVALUE ->
CAAR#'ContextAttrAuditRequest'{priority = 'NULL'};
priorityAudit ->
Prio = CAAR#'ContextAttrAuditRequest'.priority,
exit({only_once, priorityAudit, Prio});
emergencyAudit when CAAR#'ContextAttrAuditRequest'.emergency == asn1_NOVALUE ->
CAAR#'ContextAttrAuditRequest'{emergency = 'NULL'};
emergencyAudit ->
Em = CAAR#'ContextAttrAuditRequest'.emergency,
exit({only_once, emergencyAudit, Em});
topologyAudit when CAAR#'ContextAttrAuditRequest'.topology == asn1_NOVALUE ->
CAAR#'ContextAttrAuditRequest'{topology = 'NULL'};
topologyAudit ->
Top = CAAR#'ContextAttrAuditRequest'.topology,
exit({only_once, topologyAudit, Top})
end,
merge_context_attr_audit_request(CAAR2, T);
merge_context_attr_audit_request(CAAR, []) ->
CAAR.
%% OTP-5085:
%% In order to solve a problem in the parser, the error descriptor
%% has been put last in the non-empty commandReplyList, if it is not
%% asn1_NOVALUE
-ifdef(megaco_parser_inline).
-compile({inline,[{merge_action_reply,1}]}).
-endif.
merge_action_reply(ReplyList) ->
CtxReq = #'ContextRequest'{},
TopReq = [],
CmdList = [],
case lists:reverse(ReplyList) of
[ED|RL2] when is_record(ED, 'ErrorDescriptor') ->
AR = do_merge_action_reply(lists:reverse(RL2),
CtxReq, TopReq, CmdList),
AR#'ActionReply'{errorDescriptor = ED};
_ ->
do_merge_action_reply(ReplyList, CtxReq, TopReq, CmdList)
end.
do_merge_action_reply([H | T], CtxReq, TopReq, CmdList) ->
case H of
{command, Cmd} ->
do_merge_action_reply(T, CtxReq, TopReq, [Cmd | CmdList]);
{context, Ctx} ->
case Ctx of
{priority, Int} when CtxReq#'ContextRequest'.priority == asn1_NOVALUE ->
CtxReq2 = CtxReq#'ContextRequest'{priority = Int},
do_merge_action_reply(T, CtxReq2, TopReq, CmdList);
{emergency, Bool} when CtxReq#'ContextRequest'.emergency == asn1_NOVALUE ->
CtxReq2 = CtxReq#'ContextRequest'{emergency = Bool},
do_merge_action_reply(T, CtxReq2, TopReq, CmdList);
{topology, Desc} ->
TopReq2 = Desc ++ TopReq, %% OTP-4088
do_merge_action_reply(T, CtxReq, TopReq2, CmdList)
end
end;
do_merge_action_reply([], CtxReq, TopReq, CmdList) ->
#'ActionReply'{contextReply = strip_contextRequest(CtxReq, TopReq),
commandReply = lists:reverse(CmdList)}.
strip_contextRequest(R, TopReq)
when ((R#'ContextRequest'.priority =:= asn1_NOVALUE) andalso
(R#'ContextRequest'.emergency =:= asn1_NOVALUE) andalso
(TopReq =:= [])) ->
asn1_NOVALUE;
strip_contextRequest(R, []) ->
R#'ContextRequest'{topologyReq = asn1_NOVALUE};
strip_contextRequest(R, TopReq) ->
R#'ContextRequest'{topologyReq = TopReq}. %% OTP-4088
strip_contextAttrAuditRequest(R)
when ((R#'ContextAttrAuditRequest'.priority =:= asn1_NOVALUE) andalso
(R#'ContextAttrAuditRequest'.emergency =:= asn1_NOVALUE) andalso
(R#'ContextAttrAuditRequest'.topology =:= asn1_NOVALUE)) ->
asn1_NOVALUE;
strip_contextAttrAuditRequest(R) ->
R.
merge_AmmRequest_descriptors([], Acc) ->
lists:reverse(Acc);
merge_AmmRequest_descriptors([{_, deprecated}|Descs], Acc) ->
merge_AmmRequest_descriptors(Descs, Acc);
merge_AmmRequest_descriptors([Desc|Descs], Acc) ->
merge_AmmRequest_descriptors(Descs, [Desc|Acc]).
make_commandRequest({CmdTag, {_TokenTag, _Line, Text}}, Cmd) ->
Req = #'CommandRequest'{command = {CmdTag, Cmd}},
case Text of
[$w, $- | _] ->
Req#'CommandRequest'{wildcardReturn = 'NULL'};
[$o, $-, $w, $- | _] ->
Req#'CommandRequest'{optional = 'NULL', wildcardReturn = 'NULL'};
[$o, $- | _] ->
Req#'CommandRequest'{optional = 'NULL'};
_ ->
Req
end.
-ifdef(megaco_parser_inline).
-compile({inline,[{merge_terminationAudit,1}]}).
-endif.
merge_terminationAudit(AuditReturnParameters) ->
lists:reverse(do_merge_terminationAudit(AuditReturnParameters, [], [])).
do_merge_terminationAudit([H| T], ARPs, AuditItems) ->
case H of
{auditReturnItem, AuditItem} ->
do_merge_terminationAudit(T, ARPs, [AuditItem | AuditItems]);
AuditReturnParameter ->
do_merge_terminationAudit(T, [AuditReturnParameter | ARPs], AuditItems)
end;
do_merge_terminationAudit([], AuditReturnParameters, []) ->
AuditReturnParameters;
do_merge_terminationAudit([], AuditReturnParameters, AuditItems) ->
AuditDescriptor = #'AuditDescriptor'{auditToken = AuditItems},
AuditReturnParameter = {emptyDescriptors, AuditDescriptor},
[AuditReturnParameter | AuditReturnParameters].
-ifdef(megaco_parser_inline).
-compile({inline,[{merge_mediaDescriptor,1}]}).
-endif.
merge_mediaDescriptor(MediaParms) ->
do_merge_mediaDescriptor(MediaParms, asn1_NOVALUE, [], []).
do_merge_mediaDescriptor([H | T], TS, One, Multi) ->
case H of
{streamParm, Parm} when Multi =:= [] ->
do_merge_mediaDescriptor(T, TS, [Parm | One], Multi);
{streamDescriptor, Desc} when One =:= [] ->
do_merge_mediaDescriptor(T, TS, One, [Desc | Multi]);
{termState, TS2} when TS =:= asn1_NOVALUE ->
do_merge_mediaDescriptor(T, TS2, One, Multi);
_ ->
return_error(0, {bad_merge_mediaDescriptor, [H, TS, One, Multi]})
end;
do_merge_mediaDescriptor([], TS, One, Multi) ->
if
(One =:= []) ->
if (Multi =:= []) ->
#'MediaDescriptor'{streams = asn1_NOVALUE,
termStateDescr = TS};
true -> % (Multi =/= [])
Streams = {multiStream, lists:reverse(Multi)},
#'MediaDescriptor'{streams = Streams,
termStateDescr = TS}
end;
true -> % (One =/= [])
if
(Multi =:= []) ->
Streams = {oneStream, merge_streamParms(One)},
#'MediaDescriptor'{streams = Streams,
termStateDescr = TS};
true -> % (Multi =/= [])
return_error(0,
{bad_merge_mediaDescriptor, [TS, One, Multi]})
end
end.
-ifdef(megaco_parser_inline).
-compile({inline,[{merge_streamParms,1}]}).
-endif.
merge_streamParms(TaggedStreamParms) ->
SP = #'StreamParms'{},
do_merge_streamParms(TaggedStreamParms, SP).
do_merge_streamParms([{Tag, D} | T] = All, SP) ->
case Tag of
local when SP#'StreamParms'.localDescriptor =:= asn1_NOVALUE ->
do_merge_streamParms(T, SP#'StreamParms'{localDescriptor = D});
remote when SP#'StreamParms'.remoteDescriptor =:= asn1_NOVALUE ->
do_merge_streamParms(T, SP#'StreamParms'{remoteDescriptor = D});
control ->
LCD =
case SP#'StreamParms'.localControlDescriptor of
asn1_NOVALUE ->
#'LocalControlDescriptor'{propertyParms = []};
PrevLCD ->
PrevLCD
end,
LCD2 = do_merge_control_streamParms(D, LCD),
do_merge_streamParms(T, SP#'StreamParms'{localControlDescriptor = LCD2});
_ ->
return_error(0, {do_merge_streamParms, [All, SP]})
end;
do_merge_streamParms([], SP)
when is_record(SP#'StreamParms'.localControlDescriptor,
'LocalControlDescriptor') ->
LCD = SP#'StreamParms'.localControlDescriptor,
PP = LCD#'LocalControlDescriptor'.propertyParms,
LCD2 = LCD#'LocalControlDescriptor'{propertyParms = lists:reverse(PP)},
SP#'StreamParms'{localControlDescriptor = LCD2};
do_merge_streamParms([], SP) ->
SP.
do_merge_control_streamParms([{SubTag, SD} | T] = All, LCD) ->
case SubTag of
group when LCD#'LocalControlDescriptor'.reserveGroup =:= asn1_NOVALUE ->
LCD2 = LCD#'LocalControlDescriptor'{reserveGroup = SD},
do_merge_control_streamParms(T, LCD2);
value when LCD#'LocalControlDescriptor'.reserveValue =:= asn1_NOVALUE ->
LCD2 = LCD#'LocalControlDescriptor'{reserveValue = SD},
do_merge_control_streamParms(T, LCD2);
mode when LCD#'LocalControlDescriptor'.streamMode =:= asn1_NOVALUE ->
LCD2 = LCD#'LocalControlDescriptor'{streamMode = SD},
do_merge_control_streamParms(T, LCD2);
prop ->
PP = LCD#'LocalControlDescriptor'.propertyParms,
LCD2 = LCD#'LocalControlDescriptor'{propertyParms = [SD | PP]},
do_merge_control_streamParms(T, LCD2);
_ ->
return_error(0, {do_merge_control_streamParms, [All, LCD]})
end;
do_merge_control_streamParms([], LCD) ->
LCD.
-ifdef(megaco_parser_inline).
-compile({inline,[{merge_terminationStateDescriptor,1}]}).
-endif.
merge_terminationStateDescriptor(Parms) ->
TSD = #'TerminationStateDescriptor'{propertyParms = []},
do_merge_terminationStateDescriptor(Parms, TSD).
do_merge_terminationStateDescriptor([{Tag, Val} | T], TSD) ->
case Tag of
serviceState when TSD#'TerminationStateDescriptor'.serviceState =:= asn1_NOVALUE ->
TSD2 = TSD#'TerminationStateDescriptor'{serviceState = Val},
do_merge_terminationStateDescriptor(T, TSD2);
eventBufferControl when TSD#'TerminationStateDescriptor'.eventBufferControl =:= asn1_NOVALUE->
TSD2 = TSD#'TerminationStateDescriptor'{eventBufferControl = Val},
do_merge_terminationStateDescriptor(T, TSD2);
propertyParm ->
PP = TSD#'TerminationStateDescriptor'.propertyParms,
TSD2 = TSD#'TerminationStateDescriptor'{propertyParms = [Val | PP]},
do_merge_terminationStateDescriptor(T, TSD2)
end;
do_merge_terminationStateDescriptor([], TSD) ->
PP = TSD#'TerminationStateDescriptor'.propertyParms,
TSD#'TerminationStateDescriptor'{propertyParms = lists:reverse(PP)}.
-ifdef(megaco_nscanner_props).
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_prop_groups,1}]}).
-endif.
ensure_prop_groups(Token) ->
{_TokenTag, _Line, Text} = Token,
Group = [],
Groups = [],
parse_prop_name(Text, Group, Groups).
parse_prop_name([Char | Rest] = All, Group, Groups) ->
if
?white_space(Char) ->
parse_prop_name(Rest, Group, Groups);
?end_of_line(Char) ->
parse_prop_name(Rest, Group, Groups);
true ->
Name = [],
do_parse_prop_name(All, Name, Group, Groups)
end;
parse_prop_name([] = All, Group, Groups) ->
Name = [],
do_parse_prop_name(All, Name, Group, Groups).
do_parse_prop_name([Char | Rest], Name, Group, Groups)
when (Char =:= $=) andalso (Name =/= []) ->
%% Now we have a complete name
if
(Name =:= "v") andalso (Group =/= []) ->
%% v= is a property group delimiter,
%% lets create yet another property group.
Groups2 = [lists:reverse(Group) | Groups],
Group2 = [],
parse_prop_value(Rest, Name, Group2, Groups2);
true ->
%% Use current property group
parse_prop_value(Rest, Name, Group, Groups)
end;
do_parse_prop_name([Char | Rest], Name, Group, Groups) ->
case ?classify_char4(Char) of
safe_char_upper ->
do_parse_prop_name(Rest, [Char | Name], Group, Groups);
safe_char ->
do_parse_prop_name(Rest, [Char | Name], Group, Groups);
_ ->
return_error(0, {bad_prop_name, lists:reverse(Name), Char})
end;
do_parse_prop_name([], [], [], Groups) ->
lists:reverse(Groups);
do_parse_prop_name([], [], Group, Groups) ->
Group2 = lists:reverse(Group),
lists:reverse([Group2 | Groups]);
do_parse_prop_name([], Name, Group, Groups) when Name =/= [] ->
%% Assume end of line
Value = [],
PP = make_prop_parm(Name, Value),
Group2 = lists:reverse([PP | Group]),
lists:reverse([Group2 | Groups]).
-ifdef(megaco_parser_inline).
-compile({inline,[{parse_prop_value,4}]}).
-endif.
parse_prop_value(Chars, Name, Group, Groups) ->
Value = [],
do_parse_prop_value(Chars, Name, Value, Group, Groups).
do_parse_prop_value([Char | Rest], Name, Value, Group, Groups) ->
if
?end_of_line(Char) ->
%% Now we have a complete "name=value" pair
PP = make_prop_parm(Name, Value),
parse_prop_name(Rest, [PP | Group], Groups);
true ->
do_parse_prop_value(Rest, Name, [Char | Value], Group, Groups)
end;
do_parse_prop_value([], Name, Value, Group, Groups) ->
%% Assume end of line
PP = make_prop_parm(Name, Value),
Group2 = lists:reverse([PP | Group]),
lists:reverse([Group2 | Groups]).
-ifdef(megaco_parser_inline).
-compile({inline,[{make_prop_parm,2}]}).
-endif.
make_prop_parm(Name, Value) ->
#'PropertyParm'{name = lists:reverse(Name),
value = [lists:reverse(Value)]}.
-else. % -ifdef(megaco_nscanner_props).
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_prop_groups,1}]}).
-endif.
ensure_prop_groups(Token) ->
{_TokenTag, _Line, Groups} = Token,
Groups.
%% -ifdef(megaco_parser_inline).
%% -compile({inline,[{do_ensure_prop_groups,1}]}).
%% -endif.
%% do_ensure_prop_groups(Groups) when is_list(Groups) ->
%% [ensure_prop_group(Group) || Group <- Groups];
%% do_ensure_prop_groups(BadGroups) ->
%% throw({error, {?MODULE, {bad_property_groups, BadGroups}}}).
%% -ifdef(megaco_parser_inline).
%% -compile({inline,[{ensure_prop_group,1}]}).
%% -endif.
%% ensure_prop_group(Group) when is_list(Group) ->
%% [ensure_prop_parm(PropParm) || PropParm <- Group];
%% ensure_prop_group(BadGroup) ->
%% throw({error, {?MODULE, {bad_property_group, BadGroup}}}).
%% -ifdef(megaco_parser_inline).
%% -compile({inline,[{ensure_prop_parm,1}]}).
%% -endif.
%% ensure_prop_parm(#property_parm{name = Name,
%% value = Value}) ->
%% #'PropertyParm'{name = Name,
%% value = Value};
%% ensure_prop_parm(PP) when is_record(PP, 'PropertyParm') ->
%% PP;
%% ensure_prop_parm(BadPropParm) ->
%% throw({error, {?MODULE, {bad_property_parm, BadPropParm}}}).
-endif. % -ifdef(megaco_nscanner_props).
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_uint,3}]}).
-endif.
ensure_uint(Token, Min, Max) ->
case Token of
{_TokenTag, Line, Val} when is_integer(Val) ->
ensure_uint(Val, Min, Max, Line);
{_TokenTag, Line, Text} ->
case (catch list_to_integer(Text)) of
{'EXIT', _} ->
return_error(Line, {not_an_integer, Text});
Val when is_integer(Val) ->
ensure_uint(Val, Min, Max, Line)
end;
Val when is_integer(Val) ->
ensure_uint(Val, Min, Max, 0);
Text ->
case (catch list_to_integer(Text)) of
{'EXIT', _} ->
return_error(0, {not_an_integer, Text});
Val when is_integer(Val) ->
ensure_uint(Val, Min, Max, 0)
end
end.
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_uint,4}]}).
-endif.
ensure_uint(Val, Min, Max, Line) ->
if
is_integer(Min) andalso (Val >= Min) ->
if
is_integer(Max) andalso (Val =< Max) ->
Val;
Max =:= infinity ->
Val;
true ->
return_error(Line, {too_large_integer, Val, Max})
end;
true ->
return_error(Line, {too_small_integer, Val, Min})
end.
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_uint16,1}]}).
-endif.
ensure_uint16(Int) ->
ensure_uint(Int, 0, 65535).
-ifdef(megaco_parser_inline).
-compile({inline,[{ensure_uint32,1}]}).
-endif.
ensure_uint32(Int) ->
ensure_uint(Int, 0, 4294967295) .
%% OTP-4710
ensure_hex({_TokenTag, _Line, [$0, $x |Chars]}, Min, Max) ->
ensure_uint(length(Chars), Min, Max),
hex_to_int(Chars, []);
ensure_hex({_TokenTag, _Line, [$0, $X |Chars]}, Min, Max) ->
ensure_uint(length(Chars), Min, Max),
hex_to_int(Chars, []);
ensure_hex([$0, $x |Chars], Min, Max) ->
ensure_uint(length(Chars), Min, Max),
hex_to_int(Chars, []);
ensure_hex([$0, $X |Chars], Min, Max) ->
ensure_uint(length(Chars), Min, Max),
hex_to_int(Chars, []).
%% OTP-4710
hex_to_int([], Acc) ->
lists:reverse(Acc);
hex_to_int([Char1,Char2|Tail], Acc) ->
Int1 = hchar_to_int(Char1),
Int2 = hchar_to_int(Char2),
Val = Int2 bor (Int1 bsl 4),
hex_to_int(Tail, [Val| Acc]);
hex_to_int([Char], Acc) ->
Int = hchar_to_int(Char),
lists:reverse([Int|Acc]).
hchar_to_int(Char) when ($0 =< Char) andalso (Char =< $9) ->
Char - $0;
hchar_to_int(Char) when ($A =< Char) andalso (Char =< $F) ->
Char - $A + 10; % OTP-4710
hchar_to_int(Char) when ($a =< Char) andalso (Char =< $f) ->
Char - $a + 10. % OTP-4710
-ifdef(megaco_parser_inline).
-compile({inline,[{value_of,1}]}).
-endif.
value_of(Token) ->
{_TokenTag, _Line, Text} = Token,
Text.
% d(F) ->
% d(F, []).
% d(F, A) ->
% d(get(dbg), F, A).
% d(true, F, A) ->
% io:format("~p:" ++ F ++ "~n", [?MODULE|A]);
% d(_, _, _) ->
% ok.