1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2000-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22%%----------------------------------------------------------------------
23%% Purpose: Encode COMPACT Megaco/H.248 text messages from internal form
24%%----------------------------------------------------------------------
25
26-module(megaco_compact_text_encoder).
27
28-behaviour(megaco_encoder).
29
30-export([encode_message/3, decode_message/3,
31	 decode_mini_message/3,
32
33	 version_of/2,
34
35	 encode_transaction/3,
36	 encode_action_requests/3,
37	 encode_action_request/3,
38	 encode_command_request/3,
39	 encode_action_reply/3]).
40
41-export([token_tag2string/1, token_tag2string/2]).
42
43%% Backward compatible funcs:
44-export([encode_message/2, decode_message/2]).
45
46
47-include_lib("megaco/src/engine/megaco_message_internal.hrl").
48
49-define(V1_PARSE_MOD,     megaco_text_parser_v1).
50-define(V2_PARSE_MOD,     megaco_text_parser_v2).
51-define(V3_PARSE_MOD,     megaco_text_parser_v3).
52
53
54%%----------------------------------------------------------------------
55%% Convert a 'MegacoMessage' record into a binary
56%% Return {ok, DeepIoList} | {error, Reason}
57%%----------------------------------------------------------------------
58
59encode_message(EncodingConfig,
60	       #'MegacoMessage'{mess = #'Message'{version = V}} = MegaMsg) ->
61    encode_message(EncodingConfig, V, MegaMsg).
62
63%% <BACKWARD-COMPAT-CLAUSE>
64encode_message([{version3,_}|EC], 1, MegaMsg) ->
65    megaco_compact_text_encoder_v1:encode_message(EC, MegaMsg);
66%% </BACKWARD-COMPAT-CLAUSE>
67
68encode_message(EC, 1, MegaMsg) ->
69    megaco_compact_text_encoder_v1:encode_message(EC, MegaMsg);
70
71%% <BACKWARD-COMPAT-CLAUSE>
72encode_message([{version3,_}|EC], 2, MegaMsg) ->
73    megaco_compact_text_encoder_v2:encode_message(EC, MegaMsg);
74%% </BACKWARD-COMPAT-CLAUSE>
75
76encode_message(EC, 2, MegaMsg) ->
77    megaco_compact_text_encoder_v2:encode_message(EC, MegaMsg);
78
79%% <BACKWARD-COMPAT-CLAUSE>
80encode_message([{version3,v3}|EC], 3, MegaMsg) ->
81    megaco_compact_text_encoder_v3:encode_message(EC, MegaMsg);
82%% </BACKWARD-COMPAT-CLAUSE>
83
84encode_message(EC, 3, MegaMsg) ->
85    megaco_compact_text_encoder_v3:encode_message(EC, MegaMsg);
86encode_message(_EC, V, _MegaMsg) ->
87    {error, {bad_version, V}}.
88
89
90%%----------------------------------------------------------------------
91%% Convert a binary into a 'MegacoMessage' record
92%% Return {ok, MegacoMessageRecord} | {error, Reason}
93%%----------------------------------------------------------------------
94
95version_of(_EC, Bin) ->
96    case megaco_text_scanner:scan(Bin) of
97	{ok, _Tokens, V, _LastLine} ->
98	    {ok, V};
99	{error, Reason, Line} ->
100	    {error, {decode_failed, Reason, Line}}
101    end.
102
103decode_message(EC, Bin) ->
104    %% d("decode_message -> entry with"
105    %%   "~n   EC: ~p", [EC]),
106    decode_message(EC, dynamic, Bin).
107
108decode_message([], _, Bin) when is_binary(Bin) ->
109    %% d("decode_message([]) -> entry"),
110    case megaco_text_scanner:scan(Bin) of
111	{ok, Tokens, 1, _LastLine} ->
112	    do_decode_message(?V1_PARSE_MOD, Tokens, Bin);
113
114	{ok, Tokens, 2, _LastLine} ->
115	    do_decode_message(?V2_PARSE_MOD, Tokens, Bin);
116
117	{ok, Tokens, 3, _LastLine} ->
118	    do_decode_message(?V3_PARSE_MOD, Tokens, Bin);
119
120	{ok, _Tokens, V, _LastLine} ->
121	    {error, {unsupported_version, V}};
122
123	{error, Reason, Tokens, Line} ->
124	    scan_error(Reason, Line, Tokens, Bin);
125
126	{error, Reason, Line} ->               %% OTP-4007
127	    scan_error(Reason, Line, Bin) %% OTP-4007
128    end;
129
130%% <BACKWARD-COMPAT-CLAUSE>
131decode_message([{version3,v3}], _, Bin) when is_binary(Bin) ->
132    %% d("decode_message(v3) -> entry"),
133    case megaco_text_scanner:scan(Bin) of
134	{ok, Tokens, 1, _LastLine} ->
135	    do_decode_message(?V1_PARSE_MOD, Tokens, Bin);
136
137	{ok, Tokens, 2, _LastLine} ->
138	    do_decode_message(?V2_PARSE_MOD, Tokens, Bin);
139
140	{ok, Tokens, 3, _LastLine} ->
141	    do_decode_message(?V3_PARSE_MOD, Tokens, Bin);
142
143	{ok, _Tokens, V, _LastLine} ->
144	    {error, {unsupported_version, V}};
145
146	{error, Reason, Tokens, Line} ->
147	    scan_error(Reason, Line, Tokens, Bin);
148
149	{error, Reason, Line} ->               %% OTP-4007
150	    scan_error(Reason, Line, Bin) %% OTP-4007
151    end;
152%% </BACKWARD-COMPAT-CLAUSE>
153
154decode_message([{flex, Port}], _, Bin) when is_binary(Bin) ->
155    %% d("decode_message(flex) -> entry"),
156    case megaco_flex_scanner:scan(Bin, Port) of
157	{ok, Tokens, 1, _LastLine} ->
158	    do_decode_message(?V1_PARSE_MOD, Tokens, Bin);
159
160	{ok, Tokens, 2, _LastLine} ->
161	    do_decode_message(?V2_PARSE_MOD, Tokens, Bin);
162
163	{ok, Tokens, 3, _LastLine} ->
164	    do_decode_message(?V3_PARSE_MOD, Tokens, Bin);
165
166	{ok, _Tokens, V, _LastLine} ->
167	    {error, {unsupported_version, V}};
168
169	%% {error, Reason, Tokens, Line} ->
170	%%     scan_error(Reason, Line, Tokens, Bin);
171
172	{error, Reason, Line} ->               %% OTP-4007
173	    scan_error(Reason, Line, Bin) %% OTP-4007
174    end;
175
176%% <BACKWARD-COMPAT-CLAUSE>
177decode_message([{version3,v3},{flex, Port}], _, Bin) when is_binary(Bin) ->
178    %% d("decode_message(v3,flex) -> entry"),
179    case megaco_flex_scanner:scan(Bin, Port) of
180	{ok, Tokens, 1, _LastLine} ->
181	    do_decode_message(?V1_PARSE_MOD, Tokens, Bin);
182
183	{ok, Tokens, 2, _LastLine} ->
184	    do_decode_message(?V2_PARSE_MOD, Tokens, Bin);
185
186	{ok, Tokens, 3, _LastLine} ->
187	    do_decode_message(?V3_PARSE_MOD, Tokens, Bin);
188
189	{ok, _Tokens, V, _LastLine} ->
190	    {error, {unsupported_version, V}};
191
192	%% {error, Reason, Tokens, Line} ->
193	%%     scan_error(Reason, Line, Tokens, Bin);
194
195	{error, Reason, Line} ->               %% OTP-4007
196	    scan_error(Reason, Line, Bin) %% OTP-4007
197    end;
198%% <BACKWARD-COMPAT-CLAUSE>
199
200decode_message(EC, _, Bin) when is_binary(Bin) ->
201    {error, {bad_encoding_config, EC}};
202decode_message(_EC, _, _BadBin) ->
203    {error, bad_binary}.
204
205
206do_decode_message(ParseMod, Tokens, Bin) ->
207%%     d("do_decode_message -> entry with"
208%%       "~n   ParseMod: ~p"
209%%       "~n   Tokens:   ~p", [ParseMod, Tokens]),
210    case (catch ParseMod:parse(Tokens)) of
211	{ok, MegacoMessage} ->
212%% 	    d("do_decode_message -> "
213%% 	      "~n   MegacoMessage: ~p", [MegacoMessage]),
214	    {ok, MegacoMessage};
215	{error, Reason} ->
216	    parse_error(Reason, Tokens, Bin);
217
218	%% OTP-4007
219	{'EXIT', Reason} ->
220	    parse_error(Reason, Tokens, Bin)
221    end.
222
223
224decode_mini_message(EC, _, Bin) when is_binary(Bin) ->
225    megaco_text_mini_decoder:decode_message(EC, Bin).
226
227
228scan_error(Reason, Line, Bin) ->
229    scan_error(Reason, Line, [], Bin).
230
231scan_error("bad_property_parm: " ++ Reason, _Line, _Tokens, _Bin) ->
232    {error, {bad_property_parm, Reason}};
233scan_error(Reason, Line, Tokens, Bin) ->
234    %%     io:format("scanner error: "
235    %% 	      "~n   Reason: ~p"
236    %% 	      "~n   Line:   ~p"
237    %% 	      "~n   Tokens: ~p"
238    %% 	      "~n   Bin:    ~p"
239    %% 	      "~n", [Reason, Line, Tokens, Bin]),
240    {error, [{reason, Reason, Line}, {token, Tokens}, {chars, Bin}]}.
241
242parse_error(Reason, Tokens, Chars) ->
243    %%     io:format("parser error -> entry with"
244    %%               "~n   Reason: ~p"
245    %%               "~n   Tokens: ~p"
246    %%               "~n", [Reason, Tokens]),
247    case Reason of
248	{Line, Mod, [Prefix, [$[, TokenStringRaw, $]]]} when
249	      is_integer(Line) andalso
250	      is_atom(Mod) andalso
251	      is_list(Prefix) andalso
252	      is_list(TokenStringRaw) ->
253	    TokenString = [l2i(X) || X <- TokenStringRaw, is_list(X)],
254	    ReasonStr = Prefix ++ TokenString,
255	    {error, [{reason, ReasonStr, Line}, {tokens, Tokens}, {chars, Chars}, {module, Mod}]};
256        _ ->
257            {error, [{reason, Reason}, {token, Tokens}, {chars, Chars}]}
258    end.
259
260
261l2i(L) when is_list(L) ->
262    case (catch list_to_integer(L)) of
263	I when is_integer(I) ->
264	    I;
265	_ ->
266	    L
267    end.
268
269
270%%----------------------------------------------------------------------
271%% Convert a transaction record into a deep io list
272%% Return {ok, DeepIoList} | {error, Reason}
273%%----------------------------------------------------------------------
274
275%% <BACKWARD-COMPAT-CLAUSE>
276encode_transaction([{version3,_}|EC], 1, Trans) ->
277    megaco_compact_text_encoder_v1:encode_transaction(EC, Trans);
278%% </BACKWARD-COMPAT-CLAUSE>
279
280encode_transaction(EC, 1, Trans) ->
281    megaco_compact_text_encoder_v1:encode_transaction(EC, Trans);
282
283%% <BACKWARD-COMPAT-CLAUSE>
284encode_transaction([{version3,_}|EC], 2, Trans) ->
285    megaco_compact_text_encoder_v2:encode_transaction(EC, Trans);
286%% </BACKWARD-COMPAT-CLAUSE>
287
288encode_transaction(EC, 2, Trans) ->
289    megaco_compact_text_encoder_v2:encode_transaction(EC, Trans);
290
291%% <BACKWARD-COMPAT-CLAUSE>
292encode_transaction([{version3,v3}|EC], 3, Trans) ->
293    megaco_compact_text_encoder_v3:encode_transaction(EC, Trans);
294%% </BACKWARD-COMPAT-CLAUSE>
295
296encode_transaction(EC, 3, Trans) ->
297    megaco_compact_text_encoder_v3:encode_transaction(EC, Trans);
298encode_transaction(_EC, V, _Trans) ->
299    {error, {bad_version, V}}.
300
301
302%%----------------------------------------------------------------------
303%% Convert a list of ActionRequest record's into a binary
304%% Return {ok, DeepIoList} | {error, Reason}
305%%----------------------------------------------------------------------
306
307%% <BACKWARD-COMPAT-CLAUSE>
308encode_action_requests([{version3,_}|EC], 1, ActReqs)
309  when is_list(ActReqs) ->
310    megaco_compact_text_encoder_v1:encode_action_requests(EC, ActReqs);
311%% </BACKWARD-COMPAT-CLAUSE>
312
313encode_action_requests(EC, 1, ActReqs) when is_list(ActReqs) ->
314    megaco_compact_text_encoder_v1:encode_action_requests(EC, ActReqs);
315
316%% <BACKWARD-COMPAT-CLAUSE>
317encode_action_requests([{version3,_}|EC], 2, ActReqs)
318  when is_list(ActReqs) ->
319    megaco_compact_text_encoder_v2:encode_action_requests(EC, ActReqs);
320%% </BACKWARD-COMPAT-CLAUSE>
321
322encode_action_requests(EC, 2, ActReqs) when is_list(ActReqs) ->
323    megaco_compact_text_encoder_v2:encode_action_requests(EC, ActReqs);
324
325%% <BACKWARD-COMPAT-CLAUSE>
326encode_action_requests([{version3,v3}|EC], 3, ActReqs)
327  when is_list(ActReqs) ->
328    megaco_compact_text_encoder_v3:encode_action_requests(EC, ActReqs);
329%% </BACKWARD-COMPAT-CLAUSE>
330
331encode_action_requests(EC, 3, ActReqs) when is_list(ActReqs) ->
332    megaco_compact_text_encoder_v3:encode_action_requests(EC, ActReqs);
333encode_action_requests(_EC, V, _ActReqs) ->
334    {error, {bad_version, V}}.
335
336
337%%----------------------------------------------------------------------
338%% Convert a ActionRequest record into a binary
339%% Return {ok, DeepIoList} | {error, Reason}
340%%----------------------------------------------------------------------
341
342%% <BACKWARD-COMPAT-CLAUSE>
343encode_action_request([{version3,_}|EC], 1, ActReq) ->
344    megaco_compact_text_encoder_v1:encode_action_request(EC, ActReq);
345%% </BACKWARD-COMPAT-CLAUSE>
346
347encode_action_request(EC, 1, ActReq) ->
348    megaco_compact_text_encoder_v1:encode_action_request(EC, ActReq);
349
350%% <BACKWARD-COMPAT-CLAUSE>
351encode_action_request([{version3,_}|EC], 2, ActReq) ->
352    megaco_compact_text_encoder_v2:encode_action_request(EC, ActReq);
353%% </BACKWARD-COMPAT-CLAUSE>
354
355encode_action_request(EC, 2, ActReq) ->
356    megaco_compact_text_encoder_v2:encode_action_request(EC, ActReq);
357
358%% <BACKWARD-COMPAT-CLAUSE>
359encode_action_request([{version3,v3}|EC], 3, ActReq) ->
360    megaco_compact_text_encoder_v3:encode_action_request(EC, ActReq);
361%% </BACKWARD-COMPAT-CLAUSE>
362
363encode_action_request(EC, 3, ActReq) ->
364    megaco_compact_text_encoder_v3:encode_action_request(EC, ActReq);
365encode_action_request(_EC, V, _ActReq) ->
366    {error, {bad_version, V}}.
367
368
369%%----------------------------------------------------------------------
370%% Convert a CommandRequest record into a deep io list
371%% Return {ok, DeepIoList} | {error, Reason}
372%%----------------------------------------------------------------------
373
374%% <BACKWARD-COMPAT-CLAUSE>
375encode_command_request([{version3,_}|EC], 1, CmdReq) ->
376    megaco_compact_text_encoder_v1:encode_command_request(EC, CmdReq);
377%% </BACKWARD-COMPAT-CLAUSE>
378
379encode_command_request(EC, 1, CmdReq) ->
380    megaco_compact_text_encoder_v1:encode_command_request(EC, CmdReq);
381
382%% <BACKWARD-COMPAT-CLAUSE>
383encode_command_request([{version3,_}|EC], 2, CmdReq) ->
384    megaco_compact_text_encoder_v2:encode_command_request(EC, CmdReq);
385%% </BACKWARD-COMPAT-CLAUSE>
386
387encode_command_request(EC, 2, CmdReq) ->
388    megaco_compact_text_encoder_v2:encode_command_request(EC, CmdReq);
389
390%% <BACKWARD-COMPAT-CLAUSE>
391encode_command_request([{version3,v3}|EC], 3, CmdReq) ->
392    megaco_compact_text_encoder_v3:encode_command_request(EC, CmdReq);
393%% </BACKWARD-COMPAT-CLAUSE>
394
395encode_command_request(EC, 3, CmdReq) ->
396    megaco_compact_text_encoder_v3:encode_command_request(EC, CmdReq);
397encode_command_request(_EC, V, _CmdReq) ->
398    {error, {bad_version, V}}.
399
400
401%%----------------------------------------------------------------------
402%% Convert a action reply into a deep io list
403%% Return {ok, DeepIoList} | {error, Reason}
404%%----------------------------------------------------------------------
405
406%% <BACKWARD-COMPAT-CLAUSE>
407encode_action_reply([{version3,_}|EC], 1, ActRep) ->
408    megaco_compact_text_encoder_v1:encode_action_reply(EC, ActRep);
409%% </BACKWARD-COMPAT-CLAUSE>
410
411encode_action_reply(EC, 1, ActRep) ->
412    megaco_compact_text_encoder_v1:encode_action_reply(EC, ActRep);
413
414%% <BACKWARD-COMPAT-CLAUSE>
415encode_action_reply([{version3,_}|EC], 2, ActRep) ->
416    megaco_compact_text_encoder_v2:encode_action_reply(EC, ActRep);
417%% </BACKWARD-COMPAT-CLAUSE>
418
419encode_action_reply(EC, 2, ActRep) ->
420    megaco_compact_text_encoder_v2:encode_action_reply(EC, ActRep);
421
422%% <BACKWARD-COMPAT-CLAUSE>
423encode_action_reply([{version3,v3}|EC], 3, ActRep) ->
424    megaco_compact_text_encoder_v3:encode_action_reply(EC, ActRep);
425%% </BACKWARD-COMPAT-CLAUSE>
426
427encode_action_reply(EC, 3, ActRep) ->
428    megaco_compact_text_encoder_v3:encode_action_reply(EC, ActRep);
429encode_action_reply(_EC, V, _ActRep) ->
430    {error, {bad_version, V}}.
431
432
433
434%%----------------------------------------------------------------------
435%% A utility function to pretty print the tags found in a megaco message
436%%----------------------------------------------------------------------
437
438-define(TT2S_BEST_VERSION, v3).
439
440token_tag2string(Tag) ->
441    token_tag2string(Tag, ?TT2S_BEST_VERSION).
442
443token_tag2string(Tag, 1) ->
444    token_tag2string(Tag, v1);
445token_tag2string(Tag, v1) ->
446    megaco_compact_text_encoder_v1:token_tag2string(Tag);
447token_tag2string(Tag, 2) ->
448    token_tag2string(Tag, v2);
449token_tag2string(Tag, v2) ->
450    megaco_compact_text_encoder_v2:token_tag2string(Tag);
451token_tag2string(Tag, 3) ->
452    token_tag2string(Tag, v3);
453token_tag2string(Tag, v3) ->
454    megaco_compact_text_encoder_v3:token_tag2string(Tag);
455
456token_tag2string(Tag, _Vsn) ->
457    token_tag2string(Tag, ?TT2S_BEST_VERSION).
458
459