1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2000-2019. 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 Megaco/H.248 text messages from internal form
24%%----------------------------------------------------------------------
25
26-define(META_ENC(Type, Item), Item) .
27%% -define(META_ENC(Type, Item), megaco_meta_package:encode(text, Type, Item)).
28%% -define(META_DEC(Type, Item), megaco_meta_package:decode(text, Type, Item)).
29
30enc_MegacoMessage(Val) ->
31    State = ?INIT_INDENT,
32    enc_MegacoMessage(Val, State).
33
34enc_MegacoMessage(#'MegacoMessage'{authHeader = asn1_NOVALUE,
35				   mess       = Mess}, State) ->
36    [
37     ?LWSP,
38     enc_Message(Mess, State)
39    ];
40enc_MegacoMessage(#'MegacoMessage'{authHeader = Auth,
41				   mess       = Mess}, State) ->
42    [
43     ?LWSP,
44     enc_AuthenticationHeader(Auth, State),
45     enc_Message(Mess, State)
46    ].
47
48%% Note that encoding the transaction this way
49%% make the message look a bit strange.
50enc_Transaction(Val) ->
51    State = ?INIT_INDENT,
52    enc_Transaction(Val, State).
53
54%% Note that encoding the action request's this way
55%% make the message look a bit strange.
56enc_ActionRequests(Val) ->
57    State = ?INIT_INDENT,
58    enc_TransactionRequest_actions(Val, State).
59
60%% Note that encoding the action request this way
61%% make the message look a bit strange.
62enc_ActionRequest(Val) ->
63    State = ?INIT_INDENT,
64    enc_ActionRequest(Val, State).
65
66enc_CommandRequest(Val) ->
67    State = ?INIT_INDENT,
68    enc_CommandRequest(Val, State).
69
70enc_ActionReply(Val) ->
71    State = ?INIT_INDENT,
72    enc_ActionReply(Val, State).
73
74enc_AuthenticationHeader(asn1_NOVALUE, _State) ->
75    [];
76enc_AuthenticationHeader(Val, State)
77  when is_record(Val, 'AuthenticationHeader') ->
78    [
79     ?AuthToken,
80     ?EQUAL,
81     enc_SecurityParmIndex(Val#'AuthenticationHeader'.secParmIndex, State),
82     ?COLON,
83     enc_SequenceNum(Val#'AuthenticationHeader'.seqNum, State),
84     ?COLON,
85     enc_AuthData(Val#'AuthenticationHeader'.ad, State),
86     ?SEP_INDENT(State)
87    ].
88
89enc_SecurityParmIndex({'SecurityParmIndex',Val}, State) ->
90    enc_SecurityParmIndex(Val, State);
91enc_SecurityParmIndex(Val, State) ->
92    [
93     "0x",
94     enc_HEXDIG(Val, State, 8, 8)
95    ].
96
97enc_SequenceNum({'SequenceNum',Val}, State) ->
98    enc_SequenceNum(Val, State);
99enc_SequenceNum(Val, State) ->
100    [
101     "0x",
102     enc_HEXDIG(Val, State, 8, 8)
103    ].
104
105enc_AuthData({'AuthData',Val}, State) ->
106    enc_AuthData(Val, State);
107enc_AuthData(Val, State) ->
108    [
109     "0x",
110     enc_HEXDIG(Val, State, 24, 64)  %% OTP-4710
111    ].
112
113enc_Message(Val, State)
114  when is_record(Val, 'Message') ->
115    [
116     ?MegacopToken,
117     ?SLASH,
118     enc_version(Val#'Message'.version, State),
119     ?SEP,
120     enc_MId(Val#'Message'.mId, State),
121     ?SEP_INDENT(State),
122     enc_Message_messageBody(Val#'Message'.messageBody, State)
123    ].
124
125enc_version(Val, State) when is_integer(Val) andalso (Val >= 0) ->
126    enc_DIGIT(Val, State, 0, 99).
127
128enc_Message_messageBody({'Message_messageBody',Val}, State) ->
129    enc_Message_messageBody(Val, State);
130enc_Message_messageBody({Tag, Val}, State) ->
131    case Tag of
132	messageError ->
133	    enc_ErrorDescriptor(Val, State);
134	transactions ->
135	    enc_Message_messageBody_transactions(Val, State);
136	_ ->
137	    error({invalid_messageBody_tag, Tag})
138    end.
139
140enc_Message_messageBody_transactions({'Message_messageBody_transactions',Val},
141				     State) ->
142    enc_Message_messageBody_transactions(Val, State);
143enc_Message_messageBody_transactions(Val, State)
144  when is_list(Val) andalso (Val =/= []) ->
145    [enc_Transaction(T, State) || T <- Val].
146
147enc_MId({'MId',Val}, State) ->
148    enc_MId(Val, State);
149enc_MId({Tag, Val}, State) ->
150     case Tag of
151	 ip4Address ->
152	     enc_IP4Address(Val, State);
153	 ip6Address ->
154	     enc_IP6Address(Val, State);
155	 domainName ->
156	     enc_DomainName(Val, State);
157	 deviceName ->
158	     enc_PathName(Val, State);
159	 mtpAddress ->
160	     enc_mtpAddress(Val, State);
161	 _ ->
162	     error({invalid_MId_tag, Tag})
163     end.
164
165enc_mtpAddress(Val, State) ->
166    [
167     ?MtpToken,
168     ?LBRKT,
169     enc_OCTET_STRING(Val, State, 2, 4),
170     ?RBRKT
171    ].
172
173enc_DomainName(#'DomainName'{portNumber = asn1_NOVALUE,
174			     name       = Name}, State) ->
175    [
176     $<,
177     %% BUGBUG: (ALPHA / DIGIT) *63(ALPHA / DIGIT / "-" / ".")
178     enc_STRING(Name, State, 1, 64),
179     $>
180    ];
181enc_DomainName(#'DomainName'{portNumber = PortNumber,
182			     name       = Name}, State) ->
183    [
184     $<,
185     %% BUGBUG: (ALPHA / DIGIT) *63(ALPHA / DIGIT / "-" / ".")
186     enc_STRING(Name, State, 1, 64),
187     $>,
188     $:,
189     enc_portNumber(PortNumber, State)
190    ].
191
192enc_IP4Address(#'IP4Address'{portNumber = asn1_NOVALUE,
193			     address    = [A1, A2, A3, A4]}, State) ->
194    [
195     $[,
196       enc_V4hex(A1, State),
197       ?DOT,
198       enc_V4hex(A2, State),
199       ?DOT,
200       enc_V4hex(A3, State),
201       ?DOT,
202       enc_V4hex(A4, State),
203       $]
204    ];
205enc_IP4Address(#'IP4Address'{portNumber = PortNumber,
206			     address    = [A1, A2, A3, A4]}, State) ->
207    [
208     $[,
209       enc_V4hex(A1, State),
210       ?DOT,
211       enc_V4hex(A2, State),
212       ?DOT,
213       enc_V4hex(A3, State),
214       ?DOT,
215       enc_V4hex(A4, State),
216       $],
217     $:,
218     enc_portNumber(PortNumber, State)
219    ].
220
221enc_V4hex(Val, State) ->
222    enc_DIGIT(Val, State, 0, 255).
223
224enc_IP6Address(#'IP6Address'{portNumber = asn1_NOVALUE,
225			     address    = Addr}, State)
226  when is_list(Addr) andalso (length(Addr) =:= 16) ->
227    [
228     $[,
229     enc_IP6Address_address(Addr, State),
230     $]
231    ];
232enc_IP6Address(#'IP6Address'{portNumber = PortNumber,
233			     address    = Addr}, State)
234  when is_list(Addr) andalso (length(Addr) =:= 16) ->
235    [
236     $[,
237     enc_IP6Address_address(Addr, State),
238     $],
239     $:,
240     enc_portNumber(PortNumber, State)
241    ].
242
243enc_IP6Address_address([0, 0|Addr], State) ->
244    enc_IP6Address_address2(Addr, 1, false, true, State);
245enc_IP6Address_address(Addr, State) ->
246    enc_IP6Address_address2(Addr, 0, false, false, State).
247
248enc_IP6Address_address2([0,0], 0, _Padding, _First, _State) ->
249    [$0];
250enc_IP6Address_address2([0,0], PadN, false, true, _State) when PadN > 0 ->
251    [$:, $:]; % Padding from the beginning (all zero's)
252enc_IP6Address_address2([0,0], PadN, false, false, _State) when PadN > 0 ->
253    [$:]; % Padding in the middle or end
254enc_IP6Address_address2([0,0], _, true, _First, _State) ->
255    [$0];
256enc_IP6Address_address2([N1,N2], 0, _Padding, _First, State) ->
257    [enc_hex4([N1, N2], State)];
258enc_IP6Address_address2([N1,N2], 1, _Padding, _First, State) ->
259    [$0, $:, enc_hex4([N1, N2], State)];
260enc_IP6Address_address2([N1,N2], PadN, false, true, State) when PadN > 1 ->
261    [$:, $:, enc_hex4([N1, N2], State)];
262enc_IP6Address_address2([N1,N2], PadN, false, false, State) when PadN > 1 ->
263    [$:, enc_hex4([N1, N2], State)];
264enc_IP6Address_address2([N1,N2], _PadN, true, _First, State) ->
265    [enc_hex4([N1, N2], State)];
266enc_IP6Address_address2([0, 0|Ns], PadN, false, First, State) ->
267    enc_IP6Address_address2(Ns, PadN+1, false, First, State);
268enc_IP6Address_address2([0, 0|Ns], _PadN, true, _First, State) ->
269    [
270     $0,
271     $:,
272     enc_IP6Address_address2(Ns, 0, true, false, State)
273    ];
274enc_IP6Address_address2([N1, N2|Ns], 0, Padded, _First, State) ->
275    [
276     enc_hex4([N1, N2], State),
277     $:,
278     enc_IP6Address_address2(Ns, 0, Padded, false, State)
279    ];
280enc_IP6Address_address2([N1, N2|Ns], 1, Padded, _First, State) ->
281    [
282     $0,
283     $:,
284     enc_hex4([N1, N2], State),
285     $:,
286     enc_IP6Address_address2(Ns, 0, Padded, false, State)
287    ];
288enc_IP6Address_address2([N1, N2|Ns], PadN, false, true, State) when PadN > 1 ->
289    %% Padding from the beginning
290    [
291     $:,
292     $:,
293     enc_hex4([N1, N2], State),
294     $:,
295     enc_IP6Address_address2(Ns, 0, true, false, State)
296    ];
297enc_IP6Address_address2([N1, N2|Ns], PadN, false, false, State)
298  when PadN > 1 ->
299    [
300     $:,  %% The other ':' has already added
301     enc_hex4([N1, N2], State),
302     $:,
303     enc_IP6Address_address2(Ns, 0, true, false, State)
304    ];
305enc_IP6Address_address2([N1, N2|Ns], _PadN, true, _First, State) ->
306    [
307     enc_hex4([N1, N2], State),
308     $:,
309     enc_IP6Address_address2(Ns, 0, true, false, State)
310    ].
311
312
313enc_hex4([0,0], _State) ->
314    $0;
315enc_hex4([0,N], _State) ->
316    hex(N);
317enc_hex4([N1, N2], _State) when N2 =< 15 ->
318    [hex(N1), $0, hex(N2)];
319enc_hex4([N1, N2], _State) ->
320    [hex(N1), hex(N2)].
321
322enc_PathName({'PathName',Val}, State) ->
323    enc_PathName(Val, State);
324enc_PathName(Val, State) ->
325    %% BUGBUG: ["*"] NAME *("/" / "*"/ ALPHA / DIGIT /"_" / "$" )
326    %% BUGBUG: ["@" pathDomainName ]
327    enc_STRING(Val, State, 1, 64).
328
329enc_Transaction(Bin, _State) when is_binary(Bin) ->
330    [Bin]; %% Already encoded...
331enc_Transaction({'Transaction',Val}, State) ->
332    enc_Transaction(Val, State);
333enc_Transaction({Tag, Val}, State) ->
334    case Tag of
335	transactionRequest ->
336	    enc_TransactionRequest(Val, State);
337	transactionPending ->
338	    enc_TransactionPending(Val, State);
339	transactionReply ->
340	    enc_TransactionReply(Val, State);
341	transactionResponseAck ->
342	    enc_TransactionResponseAck(Val, State);
343	_ ->
344	    error({invalid_Transaction_tag, Tag})
345    end.
346
347enc_TransactionResponseAck([Mand], State) ->
348    [
349     ?ResponseAckToken,
350     ?LBRKT_INDENT(State),
351     [enc_TransactionAck(Mand, State)],
352     ?RBRKT_INDENT(State)
353    ];
354enc_TransactionResponseAck([Mand | Opt], State) ->
355    [
356     ?ResponseAckToken,
357     ?LBRKT_INDENT(State),
358     [enc_TransactionAck(Mand, State) |
359      [[?COMMA_INDENT(State),
360	?INC_INDENT(State),
361	enc_TransactionAck(Val, State)] || Val <- Opt]],
362     ?RBRKT_INDENT(State)
363    ].
364
365enc_TransactionAck(Val, State)
366  when is_record(Val, 'TransactionAck') ->
367    [
368     enc_TransactionId(Val#'TransactionAck'.firstAck, ?INC_INDENT(State)),
369     case Val#'TransactionAck'.lastAck of
370	 asn1_NOVALUE ->
371	     [];
372	 LastAck ->
373	     ["-",enc_TransactionId(LastAck, State)]
374     end
375    ].
376
377enc_TransactionId({'TransactionId',Val}, State) ->
378    enc_TransactionId(Val, State);
379enc_TransactionId(Val, State) ->
380    enc_UINT32(Val, State).
381
382enc_TransactionRequest(#'TransactionRequest'{transactionId = Tid,
383					     actions       = Acts}, State) ->
384    [
385     ?TransToken,
386     ?EQUAL,
387     enc_TransactionId(Tid, State),
388     ?LBRKT_INDENT(State),
389     enc_TransactionRequest_actions(Acts, ?INC_INDENT(State)),
390     ?RBRKT_INDENT(State)
391    ];
392enc_TransactionRequest(Bin, _State) when is_binary(Bin) ->
393    [Bin].
394
395
396enc_TransactionRequest_actions(Bin, _State) when is_binary(Bin) ->
397    [Bin]; %% Already encoded...
398enc_TransactionRequest_actions({'TransactionRequest_actions',Val}, State) ->
399    enc_TransactionRequest_actions(Val, State);
400enc_TransactionRequest_actions([Mand], State) ->
401    [enc_ActionRequest(Mand, State)];
402enc_TransactionRequest_actions([Mand | Opt], State) ->
403    [enc_ActionRequest(Mand, State) |
404     [[?COMMA_INDENT(State), enc_ActionRequest(Val, State)] || Val <- Opt]].
405
406enc_TransactionPending(#'TransactionPending'{transactionId = Tid}, State) ->
407    [?PendingToken,
408     ?EQUAL,
409     enc_TransactionId(Tid, State),
410     ?LBRKT_INDENT(State),
411     ?RBRKT_INDENT(State)
412    ];
413enc_TransactionPending(Bin, _State) when is_binary(Bin) ->
414    [Bin].
415
416
417enc_TransactionReply(#'TransactionReply'{transactionId        = Tid,
418					 immAckRequired       = asn1_NOVALUE,
419					 transactionResult    = Res},
420		     State) ->
421    [
422     ?ReplyToken,
423     ?EQUAL,
424     enc_TransactionId(Tid, State),
425     ?LBRKT_INDENT(State),
426     enc_TransactionReply_transactionResult(Res, ?INC_INDENT(State)),
427     ?RBRKT_INDENT(State)
428    ];
429enc_TransactionReply(#'TransactionReply'{transactionId     = Tid,
430					 immAckRequired    = Req,
431					 transactionResult = Res}, State) ->
432    [
433     ?ReplyToken,
434     ?EQUAL,
435     enc_TransactionId(Tid, State),
436     ?LBRKT_INDENT(State),
437     enc_immAckRequired(Req, State),
438     enc_TransactionReply_transactionResult(Res, ?INC_INDENT(State)),
439     ?RBRKT_INDENT(State)
440    ];
441enc_TransactionReply(Bin, _State) when is_binary(Bin) ->
442    [Bin].
443
444
445enc_immAckRequired(Val, _State) ->
446    case Val of
447	asn1_NOVALUE ->
448	    [];
449	'NULL'       ->
450	    [?ImmAckRequiredToken, ?COMMA_INDENT(?INC_INDENT(_State))]
451    end.
452
453enc_TransactionReply_transactionResult({'TransactionReply_transactionResult',Val}, State) ->
454    enc_TransactionReply_transactionResult(Val, State);
455enc_TransactionReply_transactionResult({Tag, Val}, State) ->
456    case Tag of
457	transactionError ->
458	    enc_ErrorDescriptor(Val, State);
459	actionReplies ->
460	    enc_TransactionReply_transactionResult_actionReplies(Val, State);
461	_ ->
462	    error({invalid_TransactionReply_transactionResult_tag, Tag})
463     end.
464
465enc_TransactionReply_transactionResult_actionReplies({'TransactionReply_transactionResult_actionReplies',Val}, State) ->
466    enc_TransactionReply_transactionResult_actionReplies(Val, State);
467enc_TransactionReply_transactionResult_actionReplies([Mand], State) ->
468    [enc_ActionReply(Mand, State)];
469enc_TransactionReply_transactionResult_actionReplies([Mand | Opt], State) ->
470    [enc_ActionReply(Mand, State),
471     [[?COMMA_INDENT(State), enc_ActionReply(Val, State)] || Val <- Opt]].
472
473enc_ErrorDescriptor(#'ErrorDescriptor'{errorText = asn1_NOVALUE,
474				       errorCode = Code}, State) ->
475    [
476     ?ErrorToken,
477     ?EQUAL,
478     enc_ErrorCode(Code, State),
479     ?LBRKT,
480     ?RBRKT
481    ];
482enc_ErrorDescriptor(#'ErrorDescriptor'{errorText = Text,
483				       errorCode = Code}, State) ->
484    [
485     ?ErrorToken,
486     ?EQUAL,
487     enc_ErrorCode(Code, State),
488     ?LBRKT,
489     enc_ErrorText(Text, State),
490     ?RBRKT
491    ].
492
493enc_ErrorCode({'ErrorCode',Val}, State)->
494    enc_ErrorCode(Val, State);
495enc_ErrorCode(Val, State) ->
496    enc_DIGIT(Val, State, 0, 999).
497
498enc_ErrorText({'ErrorText',Val}, State) ->
499    enc_ErrorText(Val, State);
500enc_ErrorText(Val, State)  ->
501    enc_QUOTED_STRING(Val, State).
502
503enc_ContextID({'ContextID',Val}, State) ->
504    enc_ContextID(Val, State);
505enc_ContextID(Val, State) ->
506    case Val of
507	?megaco_all_context_id    -> $*;
508	?megaco_null_context_id   -> $-;
509	?megaco_choose_context_id -> $$;
510	Int when is_integer(Int) -> enc_UINT32(Int, State)
511    end.
512
513enc_ActionRequest(Bin, _State) when is_binary(Bin) ->
514    [Bin]; %% Already encoded...
515enc_ActionRequest(Val, State)
516  when is_record(Val, 'ActionRequest') ->
517    [
518     ?CtxToken,
519     ?EQUAL,
520     enc_ContextID(Val#'ActionRequest'.contextId, State),
521     ?LBRKT_INDENT(State),
522     enc_list([{[Val#'ActionRequest'.contextAttrAuditReq],
523		fun enc_ContextAttrAuditRequest/2}] ++
524	      decompose_ContextRequest(Val#'ActionRequest'.contextRequest) ++
525	      [{Val#'ActionRequest'.commandRequests,
526		fun enc_CommandRequest/2}],
527	      ?INC_INDENT(State)),
528     ?RBRKT_INDENT(State)
529    ].
530
531%% OTP-5085
532enc_ActionReply(#'ActionReply'{contextId       = Id,
533			       errorDescriptor = ED,
534			       contextReply    = CtxRep,
535			       commandReply    = CmdRep},
536		State) ->
537    [
538     ?CtxToken,
539     ?EQUAL,
540     enc_ContextID(Id, State),
541     ?LBRKT_INDENT(State),
542     do_enc_ActionReply(ED, CtxRep, CmdRep, State),
543     ?RBRKT_INDENT(State)
544    ].
545
546do_enc_ActionReply(asn1_NOVALUE, CtxRep, CmdRep, State)
547  when (CtxRep =/= asn1_NOVALUE) orelse (CmdRep =/= []) ->
548    [
549     enc_list(decompose_ContextRequest(CtxRep) ++
550	      [{CmdRep, fun enc_CommandReply/2}],
551	      ?INC_INDENT(State))
552    ];
553do_enc_ActionReply(ED, CtxRep, CmdRep, State)
554  when (CtxRep =/= asn1_NOVALUE) orelse (CmdRep =/= []) ->
555    [
556     enc_list(decompose_ContextRequest(CtxRep) ++
557 	      [{CmdRep, fun enc_CommandReply/2},
558	       {[ED],   fun enc_ErrorDescriptor/2}], % Indention cosmetics
559 	      ?INC_INDENT(State))
560    ];
561do_enc_ActionReply(ED, asn1_NOVALUE, [], State) ->
562    [
563     enc_ErrorDescriptor(ED, ?INC_INDENT(State))
564    ].
565
566
567decompose_ContextRequest(asn1_NOVALUE) ->
568    [{[], dummy}] ;
569decompose_ContextRequest(Val)
570  when is_record(Val, 'ContextRequest') ->
571    OptPriority =
572	case Val#'ContextRequest'.priority of
573	    asn1_NOVALUE -> {[], dummy};
574	    Prio -> {[Prio], fun enc_priority/2}
575	end,
576    OptEmergency =
577	case Val#'ContextRequest'.emergency of
578	    asn1_NOVALUE -> {[], dummy};
579	    false -> {[], dummy};
580	    true -> {[?EmergencyToken], fun(Elem, _) -> Elem end}
581	end,
582    OptTopologyReq =
583	case Val#'ContextRequest'.topologyReq of
584	    asn1_NOVALUE ->
585		{[], dummy};
586	    {'ContextRequest_topologyReq', asn1_NOVALUE} ->
587		{[], dummy};
588	    {'ContextRequest_topologyReq', List} ->
589		{List, fun enc_TopologyRequest/2};
590	    List ->
591		{[List], fun enc_TopologyRequest/2}
592     end,
593    [OptPriority, OptEmergency, OptTopologyReq].
594
595enc_priority(Val, State) ->
596    [
597     ?PriorityToken,
598     ?EQUAL,
599     enc_UINT16(Val, State)
600    ].
601
602enc_ContextAttrAuditRequest(Val, State)
603  when is_record(Val, 'ContextAttrAuditRequest') ->
604    [
605     ?ContextAuditToken,
606     ?LBRKT_INDENT(State),
607     enc_list([{[Val#'ContextAttrAuditRequest'.topology],
608		fun('NULL', _) -> ?TopologyToken end},
609	       {[Val#'ContextAttrAuditRequest'.emergency],
610		fun('NULL', _) -> ?EmergencyToken end},
611	       {[Val#'ContextAttrAuditRequest'.priority],
612		fun('NULL', _) -> ?PriorityToken end}],
613	      ?INC_INDENT(State)),
614     ?RBRKT_INDENT(State)
615    ].
616
617enc_CommandRequest(#'CommandRequest'{optional       = asn1_NOVALUE,
618				     wildcardReturn = asn1_NOVALUE,
619				     command        = Cmd}, State) ->
620    [
621     enc_Command(Cmd, State)
622    ];
623enc_CommandRequest(#'CommandRequest'{optional       = 'NULL',
624				     wildcardReturn = asn1_NOVALUE,
625				     command        = Cmd}, State) ->
626    [
627     "O-",
628     enc_Command(Cmd, State)
629    ];
630enc_CommandRequest(#'CommandRequest'{optional       = asn1_NOVALUE,
631				     wildcardReturn = 'NULL',
632				     command        = Cmd}, State) ->
633    [
634     "W-",
635     enc_Command(Cmd, State)
636    ];
637enc_CommandRequest(#'CommandRequest'{optional       = 'NULL',
638				     wildcardReturn = 'NULL',
639				     command        = Cmd}, State) ->
640    [
641     "O-",
642     "W-",
643     enc_Command(Cmd, State)
644    ].
645
646enc_Command({'Command',Val}, State) ->
647    enc_Command(Val, State);
648enc_Command({Tag, Val}, State) ->
649    case Tag of
650	addReq ->
651	    [?AddToken, enc_AmmRequest(Val, State)];
652	moveReq ->
653	    [?MoveToken, enc_AmmRequest(Val, State)];
654	modReq ->
655	    [?ModifyToken, enc_AmmRequest(Val, State)];
656	subtractReq ->
657	    [?SubtractToken, enc_SubtractRequest(Val, State)];
658	auditCapRequest ->
659	    [?AuditCapToken, enc_AuditRequest(Val, State)];
660	auditValueRequest ->
661	    [?AuditValueToken, enc_AuditRequest(Val, State)];
662	notifyReq ->
663	    [?NotifyToken, enc_NotifyRequest(Val, State)];
664	serviceChangeReq ->
665	    [?ServiceChangeToken, enc_ServiceChangeRequest(Val, State)];
666	_ ->
667	    error({invalid_Command_tag, Tag})
668    end.
669
670enc_CommandReply({'CommandReply',Val}, State) ->
671    enc_CommandReply(Val, State);
672enc_CommandReply({Tag, Val}, State) ->
673    case Tag of
674	addReply ->
675	    [?AddToken, enc_AmmsReply(Val, State)];
676	moveReply ->
677	    [?MoveToken, enc_AmmsReply(Val, State)];
678	modReply ->
679	    [?ModifyToken, enc_AmmsReply(Val, State)];
680	subtractReply ->
681	    [?SubtractToken, enc_AmmsReply(Val, State)];
682	auditCapReply ->
683	    [?AuditCapToken, enc_AuditReply(Val, State)];
684	auditValueReply ->
685	    [?AuditValueToken, enc_AuditReply(Val, State)];
686	notifyReply ->
687	    [?NotifyToken, enc_NotifyReply(Val, State)];
688	serviceChangeReply ->
689	    [?ServiceChangeToken, enc_ServiceChangeReply(Val, State)];
690	_ ->
691	    error({invalid_CommandReply_tag, Tag})
692     end.
693
694enc_TopologyRequest(Val, State)
695  when is_list(Val) ->
696    [
697     ?TopologyToken,
698     ?LBRKT_INDENT(State),
699     enc_list([{Val, fun enc_TopologyRequest1/2}],?INC_INDENT(State)),
700     ?RBRKT_INDENT(State)
701    ].
702
703enc_TopologyRequest1(Val, State)
704  when is_record(Val, 'TopologyRequest') ->
705    [
706     fun(S) ->
707	     [
708	      enc_TerminationID(Val#'TopologyRequest'.terminationFrom, S),
709	      ?COMMA_INDENT(S),
710	      enc_TerminationID(Val#'TopologyRequest'.terminationTo, S),
711	      ?COMMA_INDENT(S),
712	      case Val#'TopologyRequest'.topologyDirection of
713		  bothway -> ?BothwayToken;
714		  isolate -> ?IsolateToken;
715		  oneway ->  ?OnewayToken
716	      end
717	     ]
718     end(?INC_INDENT(State))
719    ].
720
721enc_AmmRequest(Val, State)
722  when is_record(Val, 'AmmRequest') ->
723    [
724     %% Assume that Token is added elsewhere
725     ?EQUAL,
726     enc_TerminationIDList1(Val#'AmmRequest'.terminationID, State),
727     enc_opt_brackets(
728       enc_list([{Val#'AmmRequest'.descriptors, fun enc_ammDescriptor/2}],
729		?INC_INDENT(State)),
730       State)
731    ].
732
733enc_ammDescriptor({Tag, Desc}, State) ->
734    case Tag of
735	mediaDescriptor       -> enc_MediaDescriptor(Desc, State);
736        modemDescriptor       -> enc_ModemDescriptor(Desc, State);
737        muxDescriptor         -> enc_MuxDescriptor(Desc, State);
738        eventsDescriptor      -> enc_EventsDescriptor(Desc, State);
739        eventBufferDescriptor -> enc_EventBufferDescriptor(Desc, State);
740        signalsDescriptor     -> enc_SignalsDescriptor(Desc, State);
741        digitMapDescriptor    -> enc_DigitMapDescriptor(Desc, State);
742        auditDescriptor       -> enc_AuditDescriptor(Desc, State);
743	_ ->
744	    error({invalid_ammDescriptor_tag, Tag})
745    end.
746
747enc_AmmsReply(#'AmmsReply'{terminationID = ID,
748			   terminationAudit = asn1_NOVALUE}, State) ->
749    [
750     ?EQUAL,
751     enc_TerminationIDList1(ID, State)
752    ];
753enc_AmmsReply(#'AmmsReply'{terminationID = ID,
754			   terminationAudit = []}, State) ->
755   [
756     ?EQUAL,
757     enc_TerminationIDList1(ID, State)
758    ];
759enc_AmmsReply(#'AmmsReply'{terminationID = ID,
760			   terminationAudit = Res}, State) ->
761    [
762     ?EQUAL,
763     enc_TerminationIDList1(ID, State),
764     case lists:flatten(enc_TerminationAudit(Res, ?INC_INDENT(State))) of
765	 [] ->
766	     [];
767	 L ->
768	     [
769	      ?LBRKT_INDENT(State),
770	      L,
771	      ?RBRKT_INDENT(State)
772	     ]
773     end
774    ].
775
776enc_SubtractRequest(Val, State)
777  when is_record(Val, 'SubtractRequest') ->
778    [
779     %% Assume that Token is added elsewhere
780     ?EQUAL,
781     enc_TerminationIDList1(Val#'SubtractRequest'.terminationID, State),
782     case Val#'SubtractRequest'.auditDescriptor of
783	 asn1_NOVALUE ->
784	     [];
785	 AuditDescr ->
786	     [
787	      ?LBRKT_INDENT(State) ,
788	      enc_AuditDescriptor(AuditDescr, ?INC_INDENT(State)),
789	      ?RBRKT_INDENT(State)
790	     ]
791     end
792    ].
793
794enc_AuditRequest(Val, State)
795  when is_record(Val, 'AuditRequest') ->
796    [
797     %% Assume that Token is added elsewhere
798     ?EQUAL,
799     enc_TerminationIDList1([Val#'AuditRequest'.terminationID], State),
800     case Val#'AuditRequest'.auditDescriptor of
801	 asn1_NOVALUE ->
802	     [];
803	 AuditDescr ->
804	     [
805	      ?LBRKT_INDENT(State) ,
806	      enc_AuditDescriptor(AuditDescr, ?INC_INDENT(State)),
807	      ?RBRKT_INDENT(State)
808	     ]
809     end
810    ].
811
812%% auditReply           = (AuditValueToken / AuditCapToken )
813%% 			  ( contextTerminationAudit  / auditOther)
814%% auditOther           = EQUAL TerminationID LBRKT
815%% 			  terminationAudit RBRKT
816%% terminationAudit     = auditReturnParameter *(COMMA auditReturnParameter)
817%%
818%% contextTerminationAudit = EQUAL CtxToken ( terminationIDList /
819%% 			  LBRKT errorDescriptor RBRKT )
820enc_AuditReply({Tag, Val}, State) ->
821    case Tag of
822	contextAuditResult ->
823	    [
824	     ?EQUAL,
825	     ?CtxToken,
826	     enc_TerminationIDListN(Val, State)
827	    ];
828	error ->
829	    [
830	     ?EQUAL,
831	     ?CtxToken,
832	     ?LBRKT_INDENT(State),
833	     enc_ErrorDescriptor(Val, ?INC_INDENT(State)),
834	     ?RBRKT_INDENT(State)
835	    ];
836	auditResult when is_record(Val, 'AuditResult') ->
837	    enc_auditOther(Val, State);
838	auditResult ->
839	    error({invalid_auditResult, Val});
840	_ ->
841	    error({invalid_AuditReply_tag, Tag})
842    end.
843
844enc_auditOther(#'AuditResult'{terminationID = ID,
845			      terminationAuditResult = asn1_NOVALUE}, State) ->
846    [
847     ?EQUAL,
848     enc_TerminationID(ID, State)
849    ];
850enc_auditOther(#'AuditResult'{terminationID = ID,
851			      terminationAuditResult = []}, State) ->
852    [
853     ?EQUAL,
854     enc_TerminationID(ID, State)
855    ];
856enc_auditOther(#'AuditResult'{terminationID = ID,
857			      terminationAuditResult = Res}, State) ->
858    [
859     ?EQUAL,
860     enc_TerminationID(ID, State),
861     case lists:flatten(enc_TerminationAudit(Res, ?INC_INDENT(State))) of
862	 [] ->
863	     [];
864	 L ->
865	     [
866	      ?LBRKT_INDENT(State),
867	      L,
868	      ?RBRKT_INDENT(State)
869	     ]
870     end
871    ].
872
873
874enc_AuditDescriptor(#'AuditDescriptor'{auditToken = asn1_NOVALUE},
875		    _State) ->
876    [
877     ?AuditToken,
878     [?LBRKT, ?RBRKT]
879    ];
880enc_AuditDescriptor(#'AuditDescriptor'{auditToken = []},
881		    _State) ->
882    [
883     ?AuditToken,
884     [?LBRKT, ?RBRKT]
885    ];
886enc_AuditDescriptor(#'AuditDescriptor'{auditToken = List},
887		    State) ->
888    [
889     ?AuditToken,
890     [
891      ?LBRKT_INDENT(State),
892      enc_list([{List, fun enc_auditItem/2}], ?INC_INDENT(State)),
893      ?RBRKT_INDENT(State)
894     ]
895    ].
896
897enc_auditItem(signalsToken, _State) ->
898    ?SignalsToken;
899enc_auditItem(eventBufferToken, _State) ->
900    ?EventBufferToken;
901enc_auditItem(eventsToken, _State) ->
902    ?EventsToken;
903enc_auditItem(Val, State) ->
904    enc_auditReturnItem(Val, State).
905
906enc_auditReturnItem(muxToken, _State) ->
907    ?MuxToken;
908enc_auditReturnItem(modemToken, _State) ->
909    ?ModemToken;
910enc_auditReturnItem(mediaToken, _State) ->
911    ?MediaToken;
912enc_auditReturnItem(digitMapToken, _State) ->
913    ?DigitMapToken;
914enc_auditReturnItem(statsToken, _State) ->
915    ?StatsToken;
916enc_auditReturnItem(observedEventsToken, _State) ->
917    ?ObservedEventsToken;
918enc_auditReturnItem(packagesToken, _State) ->
919    ?PackagesToken.
920
921enc_TerminationAudit({'TerminationAudit',Val}, State) ->
922    enc_TerminationAudit(Val, State);
923enc_TerminationAudit([], _State) ->
924    [];
925enc_TerminationAudit([Mand | Opt], State) ->
926    [enc_AuditReturnParameter(Mand, State),
927     [[?COMMA_INDENT(State),
928       enc_AuditReturnParameter(Val, State)] || Val <- Opt]].
929
930
931enc_AuditReturnParameter({'AuditReturnParameter',Val}, State) ->
932    enc_AuditReturnParameter(Val, State);
933enc_AuditReturnParameter({Tag, Val}, State) ->
934    case Tag of
935	mediaDescriptor ->
936	    enc_MediaDescriptor(Val, State);
937	modemDescriptor ->
938	    enc_ModemDescriptor(Val, State);
939	muxDescriptor ->
940	    enc_MuxDescriptor(Val, State);
941	eventsDescriptor ->
942	    enc_EventsDescriptor(Val, State);
943	signalsDescriptor ->
944	    enc_SignalsDescriptor(Val, State);
945	digitMapDescriptor ->
946	    enc_DigitMapDescriptor(Val, State);
947	observedEventsDescriptor ->
948	    enc_ObservedEventsDescriptor(Val, State);
949	eventBufferDescriptor ->
950	    enc_EventBufferDescriptor(Val, State);
951	statisticsDescriptor ->
952	    enc_StatisticsDescriptor(Val, State);
953	packagesDescriptor ->
954	    enc_PackagesDescriptor(Val, State);
955	errorDescriptor ->
956	    enc_ErrorDescriptor(Val, State);
957        emptyDescriptors ->
958            enc_EmptyDescriptors(Val, State);
959	_ ->
960	    error({unknown_AuditReturnParameter_tag, Tag})
961    end.
962
963enc_EmptyDescriptors(#'AuditDescriptor'{auditToken = asn1_NOVALUE}, _State) ->
964    [];
965enc_EmptyDescriptors(#'AuditDescriptor'{auditToken = []}, _State) ->
966    [];
967enc_EmptyDescriptors(#'AuditDescriptor'{auditToken = List}, State) ->
968    enc_list([{List, fun enc_auditReturnItem/2}], ?INC_INDENT(State)).
969
970
971enc_NotifyRequest(Val, State)
972  when is_record(Val, 'NotifyRequest') ->
973    [
974     %% Assume that Token is added elsewhere
975     ?EQUAL,
976     enc_TerminationIDList1(Val#'NotifyRequest'.terminationID, State),
977     ?LBRKT_INDENT(State),
978     %% BUGBUG: Mismatch between ASN.1 and ABNF
979     %% BUGBUG: The following ought to be a 'choice'
980     case Val#'NotifyRequest'.errorDescriptor of
981	 asn1_NOVALUE ->
982	     OED = Val#'NotifyRequest'.observedEventsDescriptor,
983	     enc_ObservedEventsDescriptor(OED, ?INC_INDENT(State));
984	 ErrorDescr ->
985	     enc_ErrorDescriptor(ErrorDescr, ?INC_INDENT(State))
986     end,
987     ?RBRKT_INDENT(State)
988    ].
989
990enc_NotifyReply(Val, State)
991  when is_record(Val, 'NotifyReply') ->
992    [
993     %% Assume that Token is added elsewhere
994     ?EQUAL,
995     case Val#'NotifyReply'.terminationID of
996	 asn1_NOVALUE ->
997	     error(asn1_not_compliant_with_abnf);
998	 TermId ->
999	     enc_TerminationIDList1(TermId, State)
1000     end,
1001     case Val#'NotifyReply'.errorDescriptor of
1002	 asn1_NOVALUE ->
1003	     [];
1004	 ErrorDescr ->
1005	     [
1006	      ?LBRKT_INDENT(State),
1007	      enc_ErrorDescriptor(ErrorDescr, ?INC_INDENT(State)),
1008	      ?RBRKT_INDENT(State)
1009	     ]
1010     end
1011    ].
1012
1013enc_ObservedEventsDescriptor(Val, State)
1014  when is_record(Val, 'ObservedEventsDescriptor') ->
1015    [
1016     ?ObservedEventsToken,
1017     ?EQUAL,
1018     enc_RequestID(Val#'ObservedEventsDescriptor'.requestId, State),
1019     ?LBRKT_INDENT(State),
1020     enc_observedEventsDescriptors(Val#'ObservedEventsDescriptor'.observedEventLst, ?INC_INDENT(State)),
1021     ?RBRKT_INDENT(State)
1022    ].
1023
1024enc_observedEventsDescriptors([Mand | Opt], State) ->
1025    [enc_ObservedEvent(Mand, State),
1026     [[?COMMA_INDENT(State), enc_ObservedEvent(Val, State)] || Val <- Opt]].
1027
1028%% ;time per event, because it might be buffered
1029%% observedEvent        = [ TimeStamp LWSP COLON] LWSP
1030%% 			  pkgdName [ LBRKT observedEventParameter
1031%% 			  *(COMMA observedEventParameter) RBRKT ]
1032%%
1033%% ;at-most-once eventStream, every eventParameterName at most once
1034%% observedEventParameter = eventStream / eventOther
1035enc_ObservedEvent(Val, State)
1036  when is_record(Val, 'ObservedEvent') ->
1037    [
1038     case Val#'ObservedEvent'.timeNotation of
1039	 asn1_NOVALUE ->
1040	     [];
1041	 TimeStamp ->
1042	     [
1043	      enc_TimeNotation(TimeStamp, State),
1044	      ?LWSP,
1045	      ?COLON
1046	     ]
1047     end,
1048     ?LWSP,
1049     enc_EventName(Val#'ObservedEvent'.eventName, State),
1050     enc_opt_brackets(
1051       enc_list([{[Val#'ObservedEvent'.streamID],   fun enc_eventStream/2},
1052		 {Val#'ObservedEvent'.eventParList, fun enc_eventOther/2}],
1053		?INC_INDENT(State)),
1054       State)
1055    ].
1056
1057enc_EventName({'EventName',Val}, State) ->
1058    enc_EventName(Val, State);
1059enc_EventName(Val, State) ->
1060    PkgdName = ?META_ENC(event, Val),
1061    enc_PkgdName(PkgdName, State).
1062
1063enc_eventStream(Val, State) ->
1064    [
1065     ?StreamToken,
1066     ?EQUAL,
1067     enc_StreamID(Val, State)
1068    ].
1069
1070%% The value is already encoded
1071enc_eventOther(#megaco_event_parameter{name  = Name,
1072				       value = Value}, State)
1073  when is_list(Value) ->
1074    [
1075     enc_Name(Name, State),
1076     ?EqualToken,
1077     Value
1078    ];
1079%% Special treatment of the ds parameter of the dd/ce event
1080enc_eventOther(#'EventParameter'{eventParameterName = "ds" = Name,
1081				 value              = [DigitString],
1082				 extraInfo          = asn1_NOVALUE}, State) ->
1083    [
1084     enc_Name(Name, State),
1085     ?EqualToken,
1086     enc_DigitString(DigitString, State)
1087    ];
1088enc_eventOther(#'EventParameter'{eventParameterName = Name,
1089				 value              = Value,
1090				 extraInfo          = Extra}, State) ->
1091    [
1092     enc_Name(Name, State),
1093     enc_propertyParmValues(Value, Extra, State)
1094    ].
1095
1096enc_ServiceChangeRequest(Val, State)
1097  when is_record(Val, 'ServiceChangeRequest') ->
1098    [
1099     %% Assume that Token is added elsewhere
1100     ?EQUAL,
1101     enc_TerminationIDList1(Val#'ServiceChangeRequest'.terminationID, State),
1102     ?LBRKT_INDENT(State),
1103     enc_ServiceChangeParm(Val#'ServiceChangeRequest'.serviceChangeParms,
1104			   ?INC_INDENT(State)),
1105     ?RBRKT_INDENT(State)
1106    ].
1107
1108%% serviceChangeReply   = ServiceChangeToken EQUAL TerminationID
1109%% 			  [LBRKT (errorDescriptor /
1110%% 			  serviceChangeReplyDescriptor) RBRKT]
1111%% serviceChangeReplyDescriptor = ServicesToken LBRKT
1112%% 			  servChgReplyParm *(COMMA servChgReplyParm) RBRKT
1113%%
1114%% ;at-most-once. Version is REQUIRED on first ServiceChange response
1115%% servChgReplyParm     = (serviceChangeAddress / serviceChangeMgcId /
1116%% 			  serviceChangeProfile / serviceChangeVersion )
1117enc_ServiceChangeReply(Val, State)
1118  when is_record(Val, 'ServiceChangeReply') ->
1119    [
1120     %% Assume that Token is added elsewhere
1121     ?EQUAL,
1122     enc_TerminationIDList1(Val#'ServiceChangeReply'.terminationID, State),
1123     enc_ServiceChangeResult(Val#'ServiceChangeReply'.serviceChangeResult, State)
1124     ].
1125
1126enc_ServiceChangeResult({'ServiceChangeResult',Val}, State) ->
1127    enc_ServiceChangeResult(Val, State);
1128enc_ServiceChangeResult({Tag, Val}, State) ->
1129    case Tag of
1130	errorDescriptor ->
1131	    [
1132	     ?LBRKT_INDENT(State),
1133	     enc_ErrorDescriptor(Val, ?INC_INDENT(State)),
1134	     ?RBRKT_INDENT(State)
1135	    ];
1136	serviceChangeResParms ->
1137	    case enc_ServiceChangeResParm(Val, ?INC_INDENT(?INC_INDENT(State))) of
1138		[] ->
1139		    [];
1140		ResParms ->
1141		    [
1142		     ?LBRKT_INDENT(State),
1143		     ?ServicesToken,
1144		     fun(_S) ->
1145			     [
1146			      ?LBRKT_INDENT(_S),
1147			      ResParms,
1148			      ?RBRKT_INDENT(_S)
1149			     ]
1150		     end(?INC_INDENT(State)),
1151		     ?RBRKT_INDENT(State)
1152		    ]
1153	    end;
1154	_ ->
1155	    error({invalid_ServiceChangeResult_tag, Tag})
1156    end.
1157
1158%% Required length of termination ID list is 1
1159enc_TerminationIDList1({'TerminationIDList',Val}, State) ->
1160    enc_TerminationIDList1(Val, State);
1161enc_TerminationIDList1([Singleton], State) ->
1162    enc_TerminationID(Singleton, State).
1163
1164%% No required length of termination ID list
1165enc_TerminationIDListN({'TerminationIDList',Val}, State) ->
1166    enc_TerminationIDListN(Val, State);
1167enc_TerminationIDListN([TID], State) ->
1168    [
1169     ?LBRKT_INDENT(State),
1170     enc_TerminationID(TID, State),
1171     ?RBRKT_INDENT(State)
1172    ];
1173enc_TerminationIDListN(TIDs, State) ->
1174    [
1175     ?LBRKT_INDENT(State),
1176     enc_list([{TIDs, fun enc_TerminationID/2}], State),
1177     ?RBRKT_INDENT(State)
1178    ].
1179
1180
1181%% TerminationID        = "ROOT" / pathNAME / "$" / "*"
1182%% ; Total length of pathNAME must not exceed 64 chars.
1183%% pathNAME             = ["*"] NAME *("/" / "*"/ ALPHA / DIGIT /"_" / "$" )
1184%% 			  ["@" pathDomainName ]
1185enc_TerminationID(Tid, State)
1186  when is_record(Tid,  megaco_term_id) ->
1187    List = [{Tid#megaco_term_id.id, fun enc_tid_component/2 }],
1188    enc_list(List, State, fun(_S) -> ?SLASH end, false).
1189
1190enc_tid_component(Component, State) when is_list(Component) ->
1191    [enc_tid_sub_component(Sub, State) || Sub <- Component];
1192enc_tid_component(Invalid, _State) ->
1193    error({invalid_id_list_component, Invalid}).
1194
1195enc_tid_sub_component(all = _Sub, _State) ->
1196    ?megaco_all;
1197enc_tid_sub_component(choose = _Sub, _State) ->
1198    ?megaco_choose;
1199enc_tid_sub_component(Char, _State) when is_integer(Char) ->
1200    Char;
1201enc_tid_sub_component(Invalid, _State) ->
1202    error({invalid_id_list_sub_component, Invalid}).
1203
1204%% enc_tid_sub_component(Sub, _State) ->
1205%%     case Sub of
1206%% 	all    -> ?megaco_all;
1207%% 	choose -> ?megaco_choose;
1208%% 	Char when is_integer(Char) -> Char
1209%%     end.
1210
1211%% mediaDescriptor      = MediaToken LBRKT mediaParm *(COMMA mediaParm) RBRKT
1212%% ; at-most-once per item
1213%% ; and either streamParm or streamDescriptor but not both
1214%% mediaParm            = (streamParm / streamDescriptor /
1215%% 			   terminationStateDescriptor)
1216%% ; at-most-once
1217%% streamParm           = ( localDescriptor / remoteDescriptor /
1218%% 			   localControlDescriptor )
1219%% streamDescriptor     = StreamToken EQUAL StreamID LBRKT streamParm
1220%% 			  *(COMMA streamParm) RBRKT
1221enc_MediaDescriptor(Val, State)
1222  when is_record(Val, 'MediaDescriptor') ->
1223    [
1224     ?MediaToken,
1225     ?LBRKT_INDENT(State),
1226     enc_list([{[Val#'MediaDescriptor'.termStateDescr],
1227		fun enc_TerminationStateDescriptor/2} |
1228	       decompose_streams(Val#'MediaDescriptor'.streams)],
1229	      ?INC_INDENT(State)),
1230     ?RBRKT_INDENT(State)
1231    ].
1232
1233decompose_streams(asn1_NOVALUE) ->
1234    [];
1235decompose_streams({'MediaDescriptor_streams',Val}) ->
1236    decompose_streams(Val);
1237decompose_streams({Tag, Val}) ->
1238    case Tag of
1239	oneStream ->
1240	    decompose_StreamParms(Val);
1241	multiStream ->
1242	    [{Val, fun enc_StreamDescriptor/2}];
1243	_ ->
1244	    error({invalid_streams_tag, Tag})
1245    end.
1246
1247decompose_StreamParms(Val)
1248  when is_record(Val, 'StreamParms') ->
1249    [
1250     {[Val#'StreamParms'.localControlDescriptor],
1251      fun enc_LocalControlDescriptor/2},
1252     {[Val#'StreamParms'.localDescriptor],
1253      fun enc_localDescriptor/2},
1254     {[Val#'StreamParms'.remoteDescriptor],
1255      fun enc_remoteDescriptor/2}
1256    ].
1257
1258enc_StreamDescriptor(Val, State)
1259    when is_record(Val, 'StreamDescriptor') ->
1260    [
1261     ?StreamToken,
1262     ?EQUAL,
1263     enc_StreamID(Val#'StreamDescriptor'.streamID, State),
1264     ?LBRKT_INDENT(State),
1265     enc_list(decompose_StreamParms(Val#'StreamDescriptor'.streamParms),
1266	      ?INC_INDENT(State)),
1267     ?RBRKT_INDENT(State)
1268    ].
1269
1270%% localControlDescriptor = LocalControlToken LBRKT localParm
1271%% 			    *(COMMA localParm) RBRKT
1272%%
1273%% ; at-most-once per item
1274%% localParm            = ( streamMode / propertyParm /
1275%%                          reservedValueMode  / reservedGroupMode )
1276%% reservedValueMode       = ReservedValueToken EQUAL ( "ON" / "OFF" )
1277%% reservedGroupMode       = ReservedGroupToken EQUAL ( "ON" / "OFF" )
1278%%
1279%% reservedMode	     = ReservedToken EQUAL ( "ON" / "OFF" )
1280%%
1281%% streamMode           = ModeToken EQUAL streamModes
1282enc_LocalControlDescriptor(
1283  #'LocalControlDescriptor'{streamMode    = asn1_NOVALUE,
1284			    reserveValue  = asn1_NOVALUE,
1285			    reserveGroup  = asn1_NOVALUE,
1286			    propertyParms = []}, _State) ->
1287    error({invalid_LocalControlDescriptor, empty});
1288enc_LocalControlDescriptor(
1289  #'LocalControlDescriptor'{streamMode    = SM,
1290			    reserveValue  = RV,
1291			    reserveGroup  = RG,
1292			    propertyParms = PPs}, State) ->
1293    [
1294     ?LocalControlToken,
1295     ?LBRKT_INDENT(State),
1296     enc_list([{[SM], fun enc_StreamMode/2},
1297	       {[RG], fun enc_reservedGroupMode/2},
1298	       {[RV], fun enc_reservedValueMode/2},
1299	       {PPs,  fun enc_PropertyParm/2}], ?INC_INDENT(State)),
1300     ?RBRKT_INDENT(State)
1301    ].
1302
1303enc_reservedGroupMode(Val, _State) ->
1304    [
1305     ?ReservedGroupToken,
1306     ?EQUAL,
1307     case Val of
1308	 false -> ?OffToken;
1309	 true  -> ?OnToken
1310     end
1311    ].
1312
1313enc_reservedValueMode(Val, _State) ->
1314    [
1315     ?ReservedValueToken,
1316     ?EQUAL,
1317     case Val of
1318	 false -> ?OffToken;
1319	 true  -> ?OnToken
1320     end
1321    ].
1322
1323enc_StreamMode({'StreamMode',Val}, State) ->
1324    enc_StreamMode(Val, State);
1325enc_StreamMode(Val, _State) ->
1326    [
1327     ?ModeToken,
1328     ?EQUAL,
1329     case Val of
1330	 sendOnly -> ?SendonlyToken;
1331	 recvOnly -> ?RecvonlyToken;
1332	 sendRecv -> ?SendrecvToken;
1333	 inactive -> ?InactiveToken;
1334	 loopBack -> ?LoopbackToken
1335     end
1336    ].
1337
1338enc_Name({'Name',Val}, State) ->
1339    enc_Name(Val, State);
1340enc_Name(Val, State) ->
1341    %% BUGBUG: NAME = ALPHA *63(ALPHA / DIGIT / "_" )
1342    enc_STRING(Val, State, 1, 64).
1343
1344enc_PkgdName({'PkgdName', Val}, State) ->
1345    enc_PkgdName(Val, State);
1346enc_PkgdName(Val, _State) ->
1347    %% BUGBUG:  pkgdName =  (NAME / "*")  SLASH  (ItemID / "*" )
1348    %% enc_OCTET_STRING(Val, _State, 1, 64).
1349    if
1350	is_list(Val) ->
1351	    Length = length(Val),
1352	    if
1353		(Length >= 1) ->
1354		    if
1355			(Length =< 64) ->
1356			    Val;
1357			true ->
1358			    error({pkgdName_toolong, Length, 64})
1359		    end;
1360		true ->
1361		    error({pkgdName_tooshort, Length, 1})
1362	    end;
1363	true ->
1364	    error({invalid_PkgdName, Val})
1365    end.
1366
1367
1368enc_localDescriptor(Val, State)
1369  when is_record(Val, 'LocalRemoteDescriptor') ->
1370    [
1371     ?LocalToken,
1372     ?LBRKT,
1373     enc_LocalRemoteDescriptor(Val, State),
1374     ?RBRKT_INDENT(State)
1375    ].
1376
1377enc_remoteDescriptor(Val, State)
1378  when is_record(Val, 'LocalRemoteDescriptor') ->
1379    [
1380     ?RemoteToken,
1381     ?LBRKT,
1382     enc_LocalRemoteDescriptor(Val, State),
1383     ?RBRKT_INDENT(State)
1384    ].
1385
1386%% When text encoding the protocol, the descriptors consist of session
1387%% descriptions as defined in SDP (RFC2327), except that the "s=", "t="
1388%% and "o=" lines are optional. When multiple session descriptions are
1389%% provided in one descriptor, the "v=" lines are required as delimiters;
1390%% otherwise they are optional.  Implementations shall accept session
1391%% descriptions that are fully conformant to RFC2327. <When binary
1392%% encoding the protocol the descriptor consists of groups of properties
1393%% (tag-value pairs) as specified in Annex C.  Each such group may
1394%% contain the parameters of a session description.
1395enc_LocalRemoteDescriptor(Val, State)
1396  when is_record(Val, 'LocalRemoteDescriptor') ->
1397    case Val#'LocalRemoteDescriptor'.propGrps of
1398	[] ->
1399	    [];
1400	[OptV | MandV] ->
1401	    [?LfToken,
1402	     enc_PropertyGroup(OptV, opt_v, State) |
1403	     [enc_PropertyGroup(M, mand_v, State) || M <- MandV]]
1404    end.
1405
1406enc_PropertyGroup({'PropertyGroup',Val}, RequiresV, State) ->
1407    enc_PropertyGroup(Val, RequiresV, State);
1408enc_PropertyGroup([H | _T] = List, mand_v, State)
1409  when is_record(H, 'PropertyParm') andalso (H#'PropertyParm'.name =:= "v") ->
1410    enc_PropertyGroup(List, opt_v, State);
1411enc_PropertyGroup(PG, opt_v, State) ->
1412    [
1413     [[enc_PropertyGroupParm(PP, State), ?CrToken, ?LfToken] || PP <- PG]
1414    ].
1415
1416enc_PropertyGroupParm(Val, State)
1417  when is_record(Val, 'PropertyParm') ->
1418    [OctetString] = Val#'PropertyParm'.value,
1419    [
1420     enc_PkgdName(Val#'PropertyParm'.name, State),
1421     ?EqualToken,
1422     enc_OCTET_STRING(OctetString, State, 0, infinity)
1423    ].
1424
1425%% propertyParm         = pkgdName parmValue
1426%% parmValue            = (EQUAL alternativeValue/ INEQUAL VALUE)
1427%% alternativeValue     = ( VALUE / LSBRKT VALUE *(COMMA VALUE) RSBRKT  /
1428%% 			  LSBRKT VALUE DOT DOT VALUE RSBRKT )
1429enc_PropertyParm(Val, State)
1430  when is_record(Val, 'PropertyParm') ->
1431    PkgdName = ?META_ENC(property, Val#'PropertyParm'.name),
1432    [
1433     enc_PkgdName(PkgdName, State),
1434     enc_propertyParmValues(Val#'PropertyParm'.value,
1435			    Val#'PropertyParm'.extraInfo,
1436			    State)
1437    ].
1438
1439enc_propertyParmValues([Single], asn1_NOVALUE, State) ->
1440    [
1441     ?EqualToken,
1442     enc_Value(Single, State)
1443    ];
1444enc_propertyParmValues([Single], {relation, Rel}, State) ->
1445    case Rel of
1446	greaterThan -> [$>, enc_Value(Single, State)];
1447	smallerThan -> [$<, enc_Value(Single, State)];
1448	unequalTo   -> [$#, enc_Value(Single, State)]
1449    end;
1450enc_propertyParmValues([Low, High], {range, true}, State)->
1451    %% Exact two values
1452    [
1453     ?EqualToken,
1454     ?LSBRKT,
1455     enc_Value(Low, State),
1456     ?COLON,
1457     enc_Value(High, State),
1458     ?RSBRKT
1459    ];
1460enc_propertyParmValues(Values, {sublist, true}, State)->
1461    %% sublist (i.e. A AND B AND ...)
1462    [
1463     ?EqualToken,
1464     ?LSBRKT,
1465     enc_list([{Values, fun enc_Value/2}], State),
1466     ?RSBRKT
1467    ];
1468enc_propertyParmValues(Values, {sublist, false}, State) ->
1469    %% alternatives (i.e. A OR B OR ...)
1470    [
1471     ?EqualToken,
1472     ?LBRKT,
1473     enc_list([{Values, fun enc_Value/2}], State),
1474     ?RBRKT
1475    ];
1476enc_propertyParmValues(V, EI, _State) ->
1477    error({invalid_property_parm_values, V, EI}).
1478
1479enc_TerminationStateDescriptor(Val, State)
1480  when is_record(Val, 'TerminationStateDescriptor') ->
1481    [
1482     ?TerminationStateToken,
1483     ?LBRKT_INDENT(State),
1484     enc_list([{Val#'TerminationStateDescriptor'.propertyParms,
1485		fun enc_PropertyParm/2},
1486	       {[Val#'TerminationStateDescriptor'.eventBufferControl],
1487		fun enc_eventBufferControl/2},
1488	       {[Val#'TerminationStateDescriptor'.serviceState],
1489		fun enc_serviceState/2}],
1490	      ?INC_INDENT(State)),
1491     ?RBRKT_INDENT(State)
1492    ].
1493
1494enc_eventBufferControl(Val, _State) ->
1495    [
1496
1497     ?BufferToken,
1498     ?EQUAL,
1499     case Val of
1500	 off      -> ?OffToken;
1501	 lockStep -> ?LockStepToken
1502    end
1503    ].
1504
1505enc_serviceState({'ServiceState',Val}, State) ->
1506    enc_serviceState(Val, State);
1507enc_serviceState(Val, _State) ->
1508    [
1509     ?ServiceStatesToken,
1510     ?EQUAL,
1511     case Val of
1512	 test     -> ?TestToken;
1513	 outOfSvc -> ?OutOfSvcToken;
1514	 inSvc    -> ?InSvcToken
1515     end
1516    ].
1517
1518enc_MuxDescriptor(Val, State)
1519  when is_record(Val, 'MuxDescriptor') ->
1520    [
1521     ?MuxToken,
1522     ?EQUAL,
1523     enc_MuxType(Val#'MuxDescriptor'.muxType, State),
1524     enc_TerminationIDListN(Val#'MuxDescriptor'.termList, State)
1525    ].
1526
1527enc_MuxType({'MuxType',Val}, State) ->
1528    enc_MuxType(Val, State);
1529enc_MuxType(Val, _State) ->
1530    case Val of
1531	h221 -> ?H221Token;
1532	h223 -> ?H223Token;
1533	h226 -> ?H226Token;
1534	v76  -> ?V76Token
1535    end.
1536
1537enc_StreamID({'StreamID',Val}, State) ->
1538    enc_StreamID(Val, State);
1539enc_StreamID(Val, State) ->
1540    enc_UINT16(Val, State).
1541
1542enc_EventsDescriptor(Val, State)
1543  when is_record(Val, 'EventsDescriptor') ->
1544    #'EventsDescriptor'{requestID = RequestId,
1545			eventList = Events} = Val,
1546    if
1547	RequestId == asn1_NOVALUE, Events == [] ->
1548	    [
1549	     ?EventsToken
1550	    ];
1551
1552	RequestId =/= asn1_NOVALUE, Events =/= [] ->
1553	    [
1554	     ?EventsToken,
1555	     ?EQUAL,
1556	     enc_RequestID(RequestId, State),
1557	     ?LBRKT_INDENT(State),
1558	     enc_list([{Events, fun enc_RequestedEvent/2}],
1559		      ?INC_INDENT(State)),
1560	     ?RBRKT_INDENT(State)
1561	    ]
1562    end.
1563
1564enc_RequestedEvent(Val, State)
1565  when is_record(Val, 'RequestedEvent') ->
1566    PkgdName = ?META_ENC(event, Val#'RequestedEvent'.pkgdName),
1567    [
1568     enc_PkgdName(PkgdName, State),
1569     enc_opt_brackets(
1570       enc_list([{[Val#'RequestedEvent'.streamID],  fun enc_eventStream/2},
1571		 {Val#'RequestedEvent'.evParList, fun enc_eventOther/2} |
1572		 decompose_requestedActions(Val#'RequestedEvent'.eventAction)],
1573		?INC_INDENT(State)),
1574      State)
1575    ].
1576
1577decompose_requestedActions(asn1_NOVALUE) ->
1578    [];
1579
1580%%
1581%% This in the ABNF:
1582%% at-most-once each of KeepActiveToken , eventDM and eventStream
1583%% at most one of either embedWithSig or embedNoSig but not both
1584%% KeepActiveToken and embedWithSig must not both be present
1585%%
1586
1587%% embedWithSig
1588decompose_requestedActions(#'RequestedActions'{keepActive        = KA,
1589					       eventDM           = EDM,
1590					       secondEvent       = SE,
1591					       signalsDescriptor = SD})
1592  when (KA =/= true)         andalso
1593       (SD =/= asn1_NOVALUE) andalso
1594       (SD /= []) ->
1595%     d("decompose_requestedActions -> entry with"
1596%       "~n   EDM: ~p"
1597%       "~n   SE:  ~p"
1598%       "~n   SD:  ~p", [EDM, SE, SD]),
1599    [
1600     {[EDM],      fun enc_EventDM/2},
1601     {[{SE, SD}], fun enc_embedWithSig/2}
1602    ];
1603
1604%% embedNoSig
1605decompose_requestedActions(#'RequestedActions'{keepActive        = KA,
1606					       eventDM           = EDM,
1607					       secondEvent       = SE,
1608					       signalsDescriptor = SD})
1609  when (SD =:= asn1_NOVALUE) orelse (SD =:= []) ->
1610    [
1611     {[KA],  fun enc_keepActive/2},
1612     {[EDM], fun enc_EventDM/2},
1613     {[SE],  fun enc_embedNoSig/2}
1614    ];
1615
1616%% Fallback, if everything else failes....
1617decompose_requestedActions(#'RequestedActions'{keepActive        = KA,
1618					       eventDM           = EDM,
1619					       secondEvent       = SE,
1620					       signalsDescriptor = SD}) ->
1621    [
1622     {[KA],       fun enc_keepActive/2},
1623     {[EDM],      fun enc_EventDM/2},
1624     {[{SE, SD}], fun enc_embedWithSig/2}
1625    ].
1626
1627enc_embedNoSig(#'SecondEventsDescriptor'{requestID = RID,
1628					 eventList = Evs}, State) ->
1629    [
1630     ?EmbedToken,
1631     ?LBRKT_INDENT(State),
1632     enc_embedFirst(RID, Evs, ?INC_INDENT(State)),
1633     ?RBRKT_INDENT(State)
1634    ].
1635
1636enc_embedWithSig({asn1_NOVALUE, SD}, State) ->
1637    [
1638     ?EmbedToken,
1639     ?LBRKT_INDENT(State),
1640     enc_SignalsDescriptor(SD, ?INC_INDENT(State)),
1641     ?RBRKT_INDENT(State)
1642    ];
1643enc_embedWithSig({#'SecondEventsDescriptor'{requestID = RID,
1644					    eventList = Evs}, SD}, State) ->
1645    [
1646     ?EmbedToken,
1647     ?LBRKT_INDENT(State),
1648     enc_SignalsDescriptor(SD, ?INC_INDENT(State)),
1649     ?COMMA_INDENT(?INC_INDENT(State)),
1650     enc_embedFirst(RID, Evs, ?INC_INDENT(State)),
1651     ?RBRKT_INDENT(State)
1652    ].
1653
1654enc_keepActive(Val, _State) ->
1655    case Val of
1656	true -> [?KeepActiveToken];
1657	false -> []
1658    end.
1659
1660enc_EventDM({'EventDM',Val}, State) ->
1661    enc_EventDM(Val, State);
1662enc_EventDM({Tag, Val}, State) ->
1663    case Tag of
1664	digitMapName ->
1665	    [
1666	     ?DigitMapToken,
1667	     ?EQUAL,
1668	     enc_DigitMapName(Val, State)
1669	    ];
1670	digitMapValue ->
1671	    [
1672	     ?DigitMapToken,
1673	     ?LBRKT_INDENT(State),
1674	     enc_DigitMapValue(Val, ?INC_INDENT(State)),
1675	     ?RBRKT_INDENT(State)
1676	    ];
1677	_ ->
1678	    error({invalid_EventDM_tag, Tag})
1679    end.
1680
1681enc_embedFirst(RID, Evs, State)
1682  when (RID =/= asn1_NOVALUE) andalso is_list(Evs) andalso (Evs =/= []) ->
1683    [
1684     ?EventsToken,
1685     ?EQUAL,
1686     enc_RequestID(RID, State),
1687     ?LBRKT_INDENT(State),
1688     enc_list([{Evs, fun enc_SecondRequestedEvent/2}], ?INC_INDENT(State)),
1689     ?RBRKT_INDENT(State)
1690    ];
1691enc_embedFirst(_RID, _Evs, _State) ->
1692    [
1693     ?EventsToken
1694    ].
1695
1696enc_SecondRequestedEvent(#'SecondRequestedEvent'{pkgdName    = N,
1697						 streamID    = SID,
1698						 evParList   = EPL,
1699						 eventAction = EA}, State) ->
1700    PkgdName = ?META_ENC(event, N),
1701    [
1702     enc_PkgdName(PkgdName, State),
1703     enc_opt_brackets(
1704       enc_list(
1705	 [{[SID], fun enc_eventStream/2},
1706	  {EPL, fun enc_eventOther/2} |
1707	  decompose_secondRequestedActions(EA)],
1708	 ?INC_INDENT(State)),
1709       State)
1710    ].
1711
1712decompose_secondRequestedActions(asn1_NOVALUE) ->
1713    [];
1714decompose_secondRequestedActions(Val)
1715  when is_record(Val, 'SecondRequestedActions') ->
1716    [
1717     {[Val#'SecondRequestedActions'.keepActive],
1718      fun enc_keepActive/2},
1719     {[Val#'SecondRequestedActions'.eventDM],
1720      fun enc_EventDM/2},
1721     {[Val#'SecondRequestedActions'.signalsDescriptor],
1722      fun enc_embeddedSignalsDescriptor/2}
1723    ].
1724
1725enc_embeddedSignalsDescriptor(Val, State) ->
1726    [
1727     ?EmbedToken,
1728     ?LBRKT_INDENT(State),
1729     enc_SignalsDescriptor(Val, ?INC_INDENT(State)),
1730     ?RBRKT_INDENT(State)
1731    ].
1732
1733enc_EventBufferDescriptor({'EventBufferDescriptor',Val}, State) ->
1734    enc_EventBufferDescriptor(Val, State);
1735enc_EventBufferDescriptor([], _State) ->
1736    [
1737     ?EventBufferToken
1738    ];
1739enc_EventBufferDescriptor(EventSpecs, State)
1740  when is_list(EventSpecs) andalso (length(EventSpecs) >= 1) ->
1741    [
1742     ?EventBufferToken,
1743     ?LBRKT_INDENT(State),
1744     enc_eventSpecs(EventSpecs, ?INC_INDENT(State)),
1745     ?RBRKT_INDENT(State)
1746    ];
1747enc_EventBufferDescriptor(EventSpecs, _State) ->
1748    error({bad_eventSpecs, EventSpecs}).
1749
1750
1751enc_eventSpecs([Mand | Opt], State) ->
1752    [enc_eventSpec(Mand, State),
1753     [[?COMMA_INDENT(State), enc_eventSpec(Val, State)] || Val <- Opt]].
1754
1755enc_eventSpec(#'EventSpec'{eventName    = Name,
1756			   streamID     = SID,
1757			   eventParList = EPL}, State) ->
1758    [
1759     enc_EventName(Name, State),
1760     enc_opt_brackets(
1761       enc_list([{[SID], fun enc_eventStream/2},
1762		 {EPL,   fun enc_eventOther/2}],
1763		?INC_INDENT(State)),
1764       State)
1765    ].
1766
1767enc_SignalsDescriptor({'SignalsDescriptor',Val}, State) ->
1768    enc_SignalsDescriptor(Val, State);
1769enc_SignalsDescriptor([], _State) ->
1770    [
1771     ?SignalsToken
1772    ];
1773enc_SignalsDescriptor(List, State) when is_list(List) ->
1774    [
1775     ?SignalsToken,
1776     ?LBRKT_INDENT(State),
1777     enc_list([{List, fun enc_SignalRequest/2}], ?INC_INDENT(State)),
1778     ?RBRKT_INDENT(State)
1779    ].
1780
1781enc_SignalRequest({'SignalRequest',Val}, State) ->
1782    enc_SignalRequest(Val, State);
1783enc_SignalRequest({Tag, Val}, State) ->
1784    case Tag of
1785	signal ->
1786	    enc_Signal(Val, State);
1787	seqSigList ->
1788	    enc_SeqSigList(Val, State);
1789	_ ->
1790	    error({invalid_SignalRequest_tag, Tag})
1791    end.
1792
1793
1794enc_SeqSigList(Val, State)
1795  when is_record(Val, 'SeqSigList') ->
1796    [
1797     ?SignalListToken,
1798     ?EQUAL,
1799     enc_UINT16(Val#'SeqSigList'.id, State),
1800     ?LBRKT_INDENT(State),
1801     enc_list([{Val#'SeqSigList'.signalList, fun enc_Signal/2}],
1802	      ?INC_INDENT(State)),
1803     ?RBRKT_INDENT(State)
1804    ].
1805
1806enc_Signal(Val, State)
1807  when is_record(Val, 'Signal') ->
1808    [
1809     enc_SignalName(Val#'Signal'.signalName, State),
1810     enc_opt_brackets(
1811       enc_list([{[Val#'Signal'.streamID],         fun enc_sigStream/2},
1812		 {[Val#'Signal'.sigType],          fun enc_sigSignalType/2},
1813		 {[Val#'Signal'.duration],         fun enc_sigDuration/2},
1814		 {[Val#'Signal'.notifyCompletion], fun enc_notifyCompletion/2},
1815		 {[Val#'Signal'.keepActive],       fun enc_keepActive/2},
1816		 {Val#'Signal'.sigParList,         fun enc_sigOther/2}],
1817		?INC_INDENT(State)),
1818      State)
1819    ].
1820
1821enc_sigStream(Val, State) ->
1822    [
1823     ?StreamToken,
1824     ?EQUAL,
1825     enc_StreamID(Val, State)
1826    ].
1827
1828enc_sigSignalType(Val, State) ->
1829    [
1830     ?SignalTypeToken,
1831     ?EQUAL,
1832     enc_SignalType(Val, State)
1833    ].
1834
1835enc_sigDuration(Val, State) ->
1836    [
1837     ?DurationToken,
1838     ?EQUAL,
1839     enc_UINT16(Val, State)
1840    ].
1841
1842enc_notifyCompletion(List, State) when is_list(List) ->
1843    [
1844     ?NotifyCompletionToken,
1845     ?EQUAL,
1846     ?LBRKT_INDENT(State),
1847     enc_list([{List, fun enc_notifyCompletionItem/2}], ?INC_INDENT(State)),
1848     ?RBRKT_INDENT(State)
1849    ].
1850
1851enc_notifyCompletionItem(Val, _State) ->
1852    case Val of
1853	onTimeOut                   -> ?TimeOutToken;
1854        onInterruptByEvent          -> ?InterruptByEventToken;
1855        onInterruptByNewSignalDescr -> ?InterruptByNewSignalsDescrToken;
1856        otherReason                 -> ?OtherReasonToken
1857    end.
1858
1859enc_SignalType({'SignalType',Val}, State) ->
1860    enc_SignalType(Val, State);
1861enc_SignalType(Val, _State) ->
1862    case Val of
1863	brief ->   ?BriefToken;
1864	onOff ->   ?OnOffToken;
1865	timeOut -> ?TimeOutToken
1866    end.
1867
1868enc_SignalName({'SignalName',Val}, State)->
1869    enc_SignalName(Val, State);
1870enc_SignalName(Val, State) ->
1871    PkgdName = ?META_ENC(signal, Val),
1872    enc_PkgdName(PkgdName, State).
1873
1874enc_sigOther(Val, State)
1875  when is_record(Val, 'SigParameter') ->
1876    [
1877     enc_Name(Val#'SigParameter'.sigParameterName, State),
1878     enc_propertyParmValues(Val#'SigParameter'.value,
1879			    Val#'SigParameter'.extraInfo,
1880			    State)
1881    ].
1882
1883enc_RequestID({'RequestID',Val}, State) ->
1884    enc_RequestID(Val, State);
1885enc_RequestID(Val, _State) when (Val =:= ?megaco_all_request_id) ->
1886    "*";
1887enc_RequestID(Val, State) ->
1888    enc_UINT32(Val, State).
1889
1890enc_ModemDescriptor(#'ModemDescriptor'{mtl = [Val],
1891				       mpl = [],
1892				       nonStandardData = asn1_NOVALUE},
1893		    State) ->
1894    [
1895     ?ModemToken,
1896     ?EQUAL,
1897     enc_ModemType(Val, State)
1898    ];
1899enc_ModemDescriptor(Val, State)
1900  when is_record(Val, 'ModemDescriptor') ->
1901    [
1902     ?ModemToken,
1903     ?LSBRKT,
1904     enc_list([{Val#'ModemDescriptor'.mtl, fun enc_ModemType/2}], State),
1905     ?RSBRKT,
1906     enc_opt_brackets(
1907       enc_list([{Val#'ModemDescriptor'.mpl, fun enc_PropertyParm/2}],
1908		?INC_INDENT(State)),
1909       State)
1910     %% BUGBUG: Is PropertyParm == NAME parmValue?
1911    ].
1912
1913
1914enc_ModemType({'ModemType',Val}, State)->
1915    enc_ModemType(Val, State);
1916enc_ModemType(Val, _State) ->
1917    %% BUGBUG: Does not handle extensionParameter
1918    case Val of
1919        v18    	  -> ?V18Token;
1920        v22    	  -> ?V22Token;
1921        v22bis 	  -> ?V22bisToken;
1922        v32    	  -> ?V32Token;
1923        v32bis 	  -> ?V32bisToken;
1924        v34    	  -> ?V34Token;
1925        v90    	  -> ?V90Token;
1926        v91    	  -> ?V91Token;
1927        synchISDN -> ?SynchISDNToken
1928    end.
1929
1930enc_DigitMapDescriptor(#'DigitMapDescriptor'{digitMapName  = asn1_NOVALUE,
1931					     digitMapValue = Value} = Val,
1932		       State)
1933  when (Value =/= asn1_NOVALUE) ->
1934    case is_empty_DigitMapValue(Value) of
1935	true ->
1936	    error({invalid_DigitMapDescriptor, Val});
1937	false ->
1938	    [
1939	     ?DigitMapToken,
1940	     ?EQUAL,
1941	     ?LBRKT_INDENT(State),
1942	     enc_DigitMapValue(Value, ?INC_INDENT(State)),
1943	     ?RBRKT_INDENT(State)
1944	    ]
1945    end;
1946enc_DigitMapDescriptor(#'DigitMapDescriptor'{digitMapName  = Name,
1947					     digitMapValue = asn1_NOVALUE},
1948		       State)
1949  when (Name =/= asn1_NOVALUE) ->
1950    [
1951     ?DigitMapToken,
1952     ?EQUAL,
1953     enc_DigitMapName(Name, State)
1954    ];
1955enc_DigitMapDescriptor(#'DigitMapDescriptor'{digitMapName  = Name,
1956					     digitMapValue = Value},
1957		       State)
1958  when (Name =/= asn1_NOVALUE) andalso (Value =/= asn1_NOVALUE) ->
1959    case is_empty_DigitMapValue(Value) of
1960	true ->
1961	    [
1962	     ?DigitMapToken,
1963	     ?EQUAL,
1964	     enc_DigitMapName(Name, State)
1965	    ];
1966	false ->
1967	    [
1968	     ?DigitMapToken,
1969	     ?EQUAL,
1970	     enc_DigitMapName(Name, State),
1971	     ?LBRKT_INDENT(State),
1972	     enc_DigitMapValue(Value, ?INC_INDENT(State)),
1973	     ?RBRKT_INDENT(State)
1974	    ]
1975    end;
1976enc_DigitMapDescriptor(BadVal, _State) ->
1977    error({invalid_DigitMapDescriptor, BadVal}).
1978
1979enc_DigitMapName({'DigitMapName',Val}, State) ->
1980    enc_DigitMapName(Val, State);
1981enc_DigitMapName(Val, State) ->
1982    enc_Name(Val, State).
1983
1984is_empty_DigitMapValue(#'DigitMapValue'{startTimer   = asn1_NOVALUE,
1985					shortTimer   = asn1_NOVALUE,
1986					longTimer    = asn1_NOVALUE,
1987					digitMapBody = []}) ->
1988    true;
1989is_empty_DigitMapValue(#'DigitMapValue'{}) ->
1990    false.
1991
1992enc_DigitMapValue(Val, State)
1993  when is_record(Val, 'DigitMapValue') ->
1994    [
1995     enc_timer(Val#'DigitMapValue'.startTimer, $T, State),
1996     enc_timer(Val#'DigitMapValue'.shortTimer, $S, State),
1997     enc_timer(Val#'DigitMapValue'.longTimer,  $L, State),
1998     %% BUGBUG: digitMapBody not handled at all
1999     enc_STRING(Val#'DigitMapValue'.digitMapBody, State, 0, infinity)
2000    ].
2001
2002enc_timer(asn1_NOVALUE, _Prefix, _State) ->
2003    [];
2004enc_timer(Timer, Prefix, State) ->
2005    [
2006     Prefix,
2007     ?COLON,
2008     enc_DIGIT(Timer, State, 0, 99),
2009     ?COMMA_INDENT(State)
2010    ].
2011
2012enc_ServiceChangeParm(Val, State)
2013  when is_record(Val, 'ServiceChangeParm') ->
2014    [
2015     ?ServicesToken,
2016     ?LBRKT_INDENT(State),
2017     enc_list([{[Val#'ServiceChangeParm'.serviceChangeMethod],
2018		fun enc_ServiceChangeMethod/2},
2019	       {[Val#'ServiceChangeParm'.serviceChangeAddress],
2020		fun enc_ServiceChangeAddress/2},
2021	       {[Val#'ServiceChangeParm'.serviceChangeVersion],
2022		fun enc_serviceChangeVersion/2},
2023	       {[Val#'ServiceChangeParm'.serviceChangeProfile],
2024		fun enc_ServiceChangeProfile/2},
2025	       {[{reason, Val#'ServiceChangeParm'.serviceChangeReason}],
2026		fun enc_serviceChangeReason/2},
2027	       {[Val#'ServiceChangeParm'.serviceChangeDelay],
2028		fun enc_serviceChangeDelay/2},
2029	       {[Val#'ServiceChangeParm'.serviceChangeMgcId],
2030		fun enc_serviceChangeMgcId/2},
2031	       {[Val#'ServiceChangeParm'.timeStamp],
2032		fun enc_TimeNotation/2}],
2033	      ?INC_INDENT(State)),
2034     ?RBRKT_INDENT(State)
2035    ].
2036
2037enc_ServiceChangeMethod({'ServiceChangeMethod',Val}, State) ->
2038    enc_ServiceChangeMethod(Val, State);
2039enc_ServiceChangeMethod(Val, _State) ->
2040    [
2041     ?MethodToken,
2042     ?EQUAL,
2043     case Val of
2044        failover      -> ?FailoverToken;
2045        forced        -> ?ForcedToken;
2046        graceful      -> ?GracefulToken;
2047        restart       -> ?RestartToken;
2048        disconnected  -> ?DisconnectedToken;
2049        handOff       -> ?HandOffToken
2050     end
2051     %% BUGBUG: extension
2052    ].
2053
2054enc_ServiceChangeAddress({'ServiceChangeAddress',Val}, State) ->
2055    enc_ServiceChangeAddress(Val, State);
2056enc_ServiceChangeAddress({Tag, Val}, State) ->
2057    [
2058     ?ServiceChangeAddressToken,
2059     ?EQUAL,
2060     case Tag of
2061	 portNumber ->
2062	     enc_portNumber(Val, State);
2063	 ip4Address ->
2064	     enc_IP4Address(Val, State);
2065	 ip6Address ->
2066	     enc_IP6Address(Val, State);
2067	 domainName ->
2068	     enc_DomainName(Val, State);
2069	 deviceName ->
2070	     enc_PathName(Val, State);
2071	 mtpAddress ->
2072	     enc_mtpAddress(Val, State);
2073	 _ ->
2074	     error({invalid_ServiceChangeAddress_tag, Tag})
2075     end
2076    ].
2077
2078enc_serviceChangeVersion(Val, State) ->
2079    [
2080     ?VersionToken,
2081     ?EQUAL,
2082     enc_version(Val, State)
2083    ].
2084
2085enc_ServiceChangeProfile(#'ServiceChangeProfile'{profileName = Name,
2086						 version     = Version},
2087			 State) ->
2088    [
2089     ?ProfileToken,
2090     ?EQUAL,
2091     enc_Name(Name, State),
2092     ?SLASH,
2093     enc_version(Version, State)
2094    ].
2095
2096enc_serviceChangeReason({reason, Val}, State) ->
2097    case Val of
2098	asn1_NOVALUE ->
2099	    [];
2100	[List] when is_list(List) ->
2101	    [
2102	     ?ReasonToken,
2103	     ?EQUAL,
2104	     enc_QUOTED_STRING(List,State) % OTP-4632 enc_Value(List, State)
2105	    ]
2106    end.
2107
2108enc_serviceChangeDelay(Val, State) ->
2109    [
2110     ?DelayToken,
2111     ?EQUAL,
2112     enc_UINT32(Val, State)
2113    ].
2114
2115enc_serviceChangeMgcId(Val, State) ->
2116    [
2117     ?MgcIdToken,
2118     ?EQUAL,
2119     enc_MId(Val, State)
2120    ].
2121
2122enc_portNumber(Val, State) when is_integer(Val) andalso (Val >= 0) ->
2123    enc_UINT16(Val, State).
2124
2125enc_ServiceChangeResParm(Val, State)
2126  when is_record(Val, 'ServiceChangeResParm') ->
2127    enc_list([{[Val#'ServiceChangeResParm'.serviceChangeAddress],
2128	       fun enc_ServiceChangeAddress/2},
2129	      {[Val#'ServiceChangeResParm'.serviceChangeVersion],
2130	       fun enc_serviceChangeVersion/2},
2131	      {[Val#'ServiceChangeResParm'.serviceChangeProfile],
2132	       fun enc_ServiceChangeProfile/2},
2133	      {[Val#'ServiceChangeResParm'.serviceChangeMgcId],
2134	       fun enc_serviceChangeMgcId/2},
2135	      {[Val#'ServiceChangeResParm'.timeStamp],
2136	       fun enc_TimeNotation/2}],
2137	     State).
2138
2139enc_PackagesDescriptor({'PackagesDescriptor',Val}, State) ->
2140    enc_PackagesDescriptor(Val, State);
2141enc_PackagesDescriptor(Val, State) ->
2142    [
2143     ?PackagesToken,
2144     ?LBRKT_INDENT(State),
2145     enc_list([{Val, fun enc_PackagesItem/2}], ?INC_INDENT(State)),
2146     ?RBRKT_INDENT(State)
2147    ].
2148
2149enc_PackagesItem(Val, State)
2150  when is_record(Val, 'PackagesItem') ->
2151    PkgdName = ?META_ENC(package, Val#'PackagesItem'.packageName),
2152    [
2153     enc_Name(PkgdName, State),
2154     "-",
2155     enc_UINT16(Val#'PackagesItem'.packageVersion, State)
2156    ].
2157
2158enc_StatisticsDescriptor({'StatisticsDescriptor',Val}, State) ->
2159    enc_StatisticsDescriptor(Val, State);
2160enc_StatisticsDescriptor(List, State) when is_list(List) ->
2161    [
2162     ?StatsToken,
2163     ?LBRKT_INDENT(State),
2164     enc_list([{List, fun enc_StatisticsParameter/2}], ?INC_INDENT(State)),
2165     ?RBRKT_INDENT(State)
2166    ].
2167
2168enc_StatisticsParameter(Val, State)
2169  when is_record(Val, 'StatisticsParameter') ->
2170    PkgdName = ?META_ENC(statistics, Val#'StatisticsParameter'.statName),
2171    case Val#'StatisticsParameter'.statValue of
2172	asn1_NOVALUE ->
2173	    [
2174	     enc_PkgdName(PkgdName, State)
2175	    ];
2176	[StatVal] when is_list(StatVal) ->
2177	    [
2178	     enc_PkgdName(PkgdName, State),
2179	     ?EQUAL,
2180	     enc_Value(StatVal, State)
2181	    ]
2182    end.
2183
2184enc_TimeNotation(Val, State)
2185  when is_record(Val, 'TimeNotation') ->
2186    [
2187     enc_STRING(Val#'TimeNotation'.date, State, 8, 8), % "yyyymmdd"
2188     "T",
2189     enc_STRING(Val#'TimeNotation'.time, State, 8, 8)  % "hhmmssss"
2190    ].
2191
2192%% BUGBUG: Does not verify that string must contain at least one char
2193%% BUGBUG: This violation of the is required in order to comply with
2194%% BUGBUG: the dd/ce ds parameter that may possibly be empty.
2195enc_Value({'Value',Val}, State) ->
2196    enc_Value(Val, State);
2197enc_Value(String, _State) ->
2198    case quoted_string_count(String, 0, true, false) of
2199	{_, 0, _} ->
2200	    [?DQUOTE, String, ?DQUOTE];
2201	{false, _, _} ->
2202	    [?DQUOTE, String, ?DQUOTE];
2203	{true, _, _} ->
2204	    [String]
2205    end.
2206
2207quoted_string_count([?DoubleQuoteToken | T], 0 = Count, _IsSafe, _MaybeQuoted) ->
2208    %% Already a quoted string. Make sure it ends
2209    quoted_string_count(T, Count + 1, true, true);
2210quoted_string_count([?DoubleQuoteToken], Count, IsSafe, true = MaybeQuoted) ->
2211    %% An explicitly quoted string
2212    {IsSafe, Count, MaybeQuoted};
2213quoted_string_count([H | T], Count, IsSafe, MaybeQuoted) ->
2214    case ?classify_char(H) of
2215	safe_char_upper -> quoted_string_count(T, Count + 1, IsSafe, MaybeQuoted);
2216	safe_char       -> quoted_string_count(T, Count + 1, IsSafe, MaybeQuoted);
2217	rest_char       -> quoted_string_count(T, Count + 1, false, MaybeQuoted);
2218	white_space     -> quoted_string_count(T, Count + 1, false, MaybeQuoted);
2219	_               -> error({illegal_char, H})
2220    end;
2221quoted_string_count([], _Count, _IsSafe, true = _MaybeQuoted) ->
2222    error({illegal_char, ?DoubleQuoteToken});
2223quoted_string_count([], Count, IsSafe, MaybeQuoted) ->
2224    {IsSafe, Count, MaybeQuoted}.
2225
2226enc_DigitString(String, _State) when is_list(String) ->
2227    [?DQUOTE, String, ?DQUOTE].
2228
2229
2230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2231
2232%% Encode an octet string, escape } by \ if necessary
2233enc_OCTET_STRING(List, State, Min, Max) ->
2234    do_enc_OCTET_STRING(List, State, Min, Max, 0).
2235
2236do_enc_OCTET_STRING([H | T], State, Min, Max, Count) ->
2237    case H of
2238	$} ->
2239	    [$\\, H | do_enc_OCTET_STRING(T, State, Min, Max, Count + 1)];
2240	_ ->
2241	    [H | do_enc_OCTET_STRING(T, State, Min, Max, Count + 1)]
2242    end;
2243do_enc_OCTET_STRING([], _State, Min, Max, Count) ->
2244    verify_count(Count, Min, Max),
2245    [].
2246
2247enc_QUOTED_STRING(String, _State) when is_list(String) ->
2248    case quoted_string_count(String, 0, true, false) of
2249	{_IsSafe, Count, false = _QuotedString} ->
2250	    verify_count(Count, 1, infinity),
2251	    [?DQUOTE, String, ?DQUOTE];
2252	{_IsSafe, Count, true = _QuotedString} ->
2253	    verify_count(Count, 3, infinity), % quotes not included in the count
2254	    [String]
2255    end.
2256
2257
2258%% The internal format of hex digits is a list of octets
2259%% Min and Max means #hexDigits
2260%% Leading zeros are prepended in order to fulfill Min
2261enc_HEXDIG(Octets, State, Min, Max) when is_list(Octets) ->
2262    do_enc_HEXDIG(Octets, State, Min, Max, 0, []).
2263
2264do_enc_HEXDIG([Octet | Rest], State, Min, Max, Count, Acc)
2265  when (Octet >= 0) andalso (Octet =< 255)  ->
2266    Hex = hex(Octet), % OTP-4921
2267    if
2268	Octet =< 15 ->
2269	    Acc2 = [[$0|Hex]|Acc],  % OTP-4921
2270	    do_enc_HEXDIG(Rest, State, Min, Max, Count + 2, Acc2);
2271	true ->
2272	    Acc2 = [Hex|Acc], % OTP-4921
2273	    do_enc_HEXDIG(Rest, State, Min, Max, Count + 2, Acc2)
2274    end;
2275do_enc_HEXDIG([], State, Min, Max, Count, Acc)
2276  when is_integer(Min) andalso (Count < Min) ->
2277    do_enc_HEXDIG([0], State, Min, Max, Count, Acc);
2278do_enc_HEXDIG([], _State, Min, Max, Count, Acc) -> %% OTP-4710
2279    verify_count(Count, Min, Max),
2280    lists:reverse(Acc).
2281
2282enc_DIGIT(Val, State, Min, Max) ->
2283    enc_integer(Val, State, Min, Max).
2284
2285enc_STRING(String, _State, Min, Max) when is_list(String) ->
2286    verify_count(length(String), Min, Max),
2287    String.
2288
2289enc_UINT16(Val, State) ->
2290    enc_integer(Val, State, 0, 65535).
2291
2292enc_UINT32(Val, State) ->
2293    enc_integer(Val, State, 0, 4294967295).
2294
2295enc_integer(Val, _State, Min, Max) ->
2296    verify_count(Val, Min, Max),
2297    integer_to_list(Val).
2298
2299%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2300%% Encodes a list of elements with separator tokens between
2301%% the elements. Optional asn1_NOVALUE values are ignored.
2302
2303enc_list(List, State) ->
2304    enc_list(List, State, fun(_S) -> ?COMMA_INDENT(_S) end, false).
2305
2306-dialyzer({nowarn_function, enc_list/4}). % Future compat
2307enc_list([], _State, _SepEncoder, _NeedsSep) ->
2308    [];
2309enc_list([{Elems, ElemEncoder} | Tail], State, SepEncoder, NeedsSep) ->
2310    case do_enc_list(Elems, State, ElemEncoder, SepEncoder, NeedsSep) of
2311	[] ->
2312	    enc_list(Tail, State, SepEncoder, NeedsSep);
2313	List ->
2314	    [List,
2315	     enc_list(Tail, State, SepEncoder, true)]
2316    end;
2317enc_list(A, B, C, D) ->
2318    error({invlid_list, A, B, C, D}).
2319
2320do_enc_list(asn1_NOVALUE, _State, _ElemEncoder, _SepEncoder, _NeedsSep) ->
2321    [];
2322do_enc_list([], _State, _ElemEncoder, _SepEncoder, _NeedsSep) ->
2323    [];
2324do_enc_list([asn1_NOVALUE | T], State, ElemEncoder, SepEncoder, NeedsSep) ->
2325    do_enc_list(T, State, ElemEncoder, SepEncoder, NeedsSep);
2326do_enc_list([H | T], State, ElemEncoder, SepEncoder, NeedsSep)
2327  when is_function(ElemEncoder) andalso is_function(SepEncoder) ->
2328    case ElemEncoder(H, State) of
2329	[] ->
2330	    do_enc_list(T, State, ElemEncoder, SepEncoder, NeedsSep);
2331	List when NeedsSep =:= true ->
2332	    [SepEncoder(State),
2333	     List, do_enc_list(T, State, ElemEncoder, SepEncoder, true)];
2334	List when NeedsSep =:= false ->
2335	    [List,
2336	     do_enc_list(T, State, ElemEncoder, SepEncoder, true)]
2337    end.
2338
2339%% Add brackets if list is non-empty
2340enc_opt_brackets([], _State) ->
2341    [];
2342enc_opt_brackets(List, _State) when is_list(List) ->
2343    [?LBRKT_INDENT(_State), List, ?RBRKT_INDENT(_State)].
2344
2345%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2346
2347%% Int -> list of hex chars
2348hex(Int) ->
2349    hexi(get_lo_bits(Int, 4), []).
2350
2351hexi({0, Lo}, Ack) ->
2352    [hex4(Lo) | Ack];
2353hexi({Hi, Lo} , Ack) ->
2354    hexi(get_lo_bits(Hi, 4), [hex4(Lo) | Ack]).
2355
2356hex4(Int) when Int < 10 ->
2357    Int + $0;
2358hex4(Int) ->
2359    ($A - 10) + Int.
2360
2361get_lo_bits(Int, Size) ->
2362    Lo = Int band ones_mask(Size),
2363    Hi = Int bsr Size,
2364    {Hi, Lo}.
2365
2366ones_mask(Ones) ->
2367    (1 bsl Ones) - 1.
2368
2369%% Verify that Count is within the range of Min and Max
2370verify_count(Count, Min, Max) ->
2371    if
2372	is_integer(Count) ->
2373	    if
2374		is_integer(Min) andalso (Count >= Min) ->
2375		    if
2376			is_integer(Max) andalso (Count =< Max) ->
2377			    Count;
2378			Max =:= infinity ->
2379			    Count;
2380			true ->
2381			    error({count_too_large, Count, Max})
2382		    end;
2383		true ->
2384		    error({count_too_small, Count, Min})
2385	    end;
2386	true ->
2387	    error({count_not_an_integer, Count})
2388    end.
2389
2390
2391
2392% d(F) ->
2393%     d(F, []).
2394
2395% d(F, A) ->
2396%     %% d(get(dbg), F, A).
2397%     d(true, F, A).
2398
2399% d(true, F, A) ->
2400%     io:format("DBG:~p:" ++ F ++ "~n", [?MODULE|A]);
2401% d(_, _, _) ->
2402%     ok.
2403