1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2001-2016. 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: RFC 4566
24%%----------------------------------------------------------------------
25
26-module(megaco_sdp).
27
28%%----------------------------------------------------------------------
29%% Include files
30%%----------------------------------------------------------------------
31
32-include_lib("megaco/include/megaco.hrl").
33-include_lib("megaco/src/app/megaco_internal.hrl").
34-include_lib("megaco/include/megaco_message_v1.hrl").
35-include_lib("megaco/include/megaco_sdp.hrl").
36
37
38%%----------------------------------------------------------------------
39%% External exports
40%%----------------------------------------------------------------------
41
42%% Avoid warning for local function error/1 clashing with autoimported BIF.
43-compile({no_auto_import,[error/1]}).
44-export([
45	 decode/1, encode/1,
46	 get_sdp_record_from_PropertyGroup/2
47        ]).
48
49
50%%----------------------------------------------------------------------
51%% Internal exports
52%%----------------------------------------------------------------------
53
54
55%%----------------------------------------------------------------------
56%% Macros
57%%----------------------------------------------------------------------
58
59
60%%----------------------------------------------------------------------
61%% Records
62%%----------------------------------------------------------------------
63
64
65%%======================================================================
66%% External functions
67%%======================================================================
68
69%% ---------------------------------------------------------------------
70%% decode(PP) -> {ok, SDP} | {error, Reason}
71%%
72%% This function performs the following conversion:
73%% property_parm()   -> sdp()
74%% property_group()  -> sdp_property_group()
75%% property_groups() -> sdp_property_groups()
76%%
77%% ---------------------------------------------------------------------
78
79decode(SDP) ->
80    case (catch do_decode(SDP)) of
81	{ok, _} = OK ->
82	    OK;
83	{error, _} = ERR ->
84	    ERR;
85	{'EXIT', Reason} ->
86	    {error, {exit, Reason}};
87	CRAP ->
88	    {error, {crap, CRAP}}
89    end.
90
91do_decode(PP) when is_record(PP, 'PropertyParm') ->
92    decode_PropertyParm(PP);
93do_decode([PP|_] = PG) when is_record(PP, 'PropertyParm') ->
94    decode_PropertyGroup(PG);
95do_decode([H|_] = PGs) when is_list(H) ->
96    decode_PropertyGroups(PGs);
97do_decode(asn1_NOVALUE = V) ->
98    {ok, V};
99do_decode(Bad) ->
100    {error, {bad_sdp, Bad}}.
101
102
103%% ---------------------------------------------------------------------
104%% encode(SDPs) -> {ok, PP} | {error, Reason}
105%%
106%% This function performs the following conversion:
107%% sdp()                 -> property_parm()
108%% sdp_property_group()  -> property_group()
109%% sdp_property_groups() -> property_groups()
110%%
111%% ---------------------------------------------------------------------
112
113encode(SDP) ->
114    case (catch do_encode(SDP)) of
115	{ok, _} = OK ->
116	    OK;
117	{error, _} = ERR ->
118	    ERR;
119	{'EXIT', Reason} ->
120	    {error, {exit, Reason}};
121	CRAP ->
122	    {error, {crap, CRAP}}
123    end.
124
125do_encode(SDP) when is_tuple(SDP) ->
126    {ok, encode_PropertyParm(SDP)};
127do_encode([SDP|_] = PG) when is_tuple(SDP) ->
128    encode_PropertyGroup(PG);
129do_encode([H|_] = PGs) when is_list(H) ->
130    encode_PropertyGroups(PGs);
131do_encode(asn1_NOVALUE = V) ->
132    {ok, V}.
133
134
135%%-----------------------------------------------------------------
136%% Generate sdp records from PropertyParm records
137%%-----------------------------------------------------------------
138
139decode_PropertyGroups(PGs) ->
140    decode_PropertyGroups(PGs, []).
141
142decode_PropertyGroups([], DecodedPGs) ->
143    {ok, lists:reverse(DecodedPGs)};
144
145decode_PropertyGroups([PG | PGs], DecodedPGs) ->
146    {ok, DecodedPG} = decode_PropertyGroup(PG),
147    decode_PropertyGroups(PGs, [DecodedPG | DecodedPGs]).
148
149
150decode_PropertyGroup(PG) ->
151    {ok, decode_PropertyGroup(PG, [])}.
152
153decode_PropertyGroup([], Acc) ->
154    lists:reverse(Acc);
155
156decode_PropertyGroup([PP | PG], Acc) ->
157    case (catch decode_PropertyParm(PP)) of
158	{ok, PP2} ->
159	    decode_PropertyGroup(PG, [PP2 | Acc]);
160	{error, Reason} ->
161	    decode_PropertyGroup(PG, [{PP, Reason} | Acc]);
162	Error ->
163	    decode_PropertyGroup(PG, [{PP, Error} | Acc])
164    end.
165
166
167%% ===== Protocol Version =====
168%%
169decode_PropertyParm(#'PropertyParm'{name      = "v",
170				    value     = [V],
171				    extraInfo = asn1_NOVALUE}) ->
172    decode_pp_protocol_version(V);
173
174
175%% ===== Origin =====
176%%
177decode_PropertyParm(#'PropertyParm'{name      = "o",
178				    value     = [V],
179				    extraInfo = asn1_NOVALUE}) ->
180    decode_pp_origin(V);
181
182
183%% ===== Session Name =====
184%%
185decode_PropertyParm(#'PropertyParm'{name      = "s",
186				    value     = [V],
187				    extraInfo = asn1_NOVALUE}) ->
188    decode_pp_session_name(V);
189
190
191%% ===== Session and Media Information =====
192%%
193decode_PropertyParm(#'PropertyParm'{name      = "i",
194				    value     = [V],
195				    extraInfo = asn1_NOVALUE}) ->
196    decode_pp_session_media_id(V);
197
198
199%% ===== URI =====
200%%
201decode_PropertyParm(#'PropertyParm'{name      = "u",
202				    value     = [V],
203				    extraInfo = asn1_NOVALUE}) ->
204    decode_pp_uri(V);
205
206
207%% ===== Email Address and Phone Number =====
208%%
209decode_PropertyParm(#'PropertyParm'{name      = "e",
210				    value     = [V],
211				    extraInfo = asn1_NOVALUE}) ->
212    decode_pp_email(V);
213
214decode_PropertyParm(#'PropertyParm'{name      = "p",
215				    value     = [V],
216				    extraInfo = asn1_NOVALUE}) ->
217    decode_pp_phone(V);
218
219
220%% ===== Connection Data =====
221%%
222decode_PropertyParm(#'PropertyParm'{name      = "c",
223				    value     = [V],
224				    extraInfo = asn1_NOVALUE}) ->
225    decode_pp_connection_data(V);
226
227
228%% ===== Bandwidth =====
229%%
230decode_PropertyParm(#'PropertyParm'{name      = "b",
231				    value     = [V],
232				    extraInfo = asn1_NOVALUE}) ->
233    decode_pp_bandwidth(V);
234
235
236%% ===== Times, Repeat Times and Time Zones =====
237%%
238decode_PropertyParm(#'PropertyParm'{name      = "t",
239				    value     = [V],
240				    extraInfo = asn1_NOVALUE }) ->
241    decode_pp_times(V);
242decode_PropertyParm(#'PropertyParm'{name      = "r",
243				    value     = [V],
244				    extraInfo = asn1_NOVALUE}) ->
245    decode_pp_rtimes(V);
246decode_PropertyParm(#'PropertyParm'{name      = "z",
247				    value     = [V],
248				    extraInfo = asn1_NOVALUE}) ->
249    decode_pp_tzones(V);
250
251
252%% ===== Encryption Keys =====
253%%
254decode_PropertyParm(#'PropertyParm'{name      = "k",
255				    value     = [V],
256				    extraInfo = asn1_NOVALUE}) ->
257    decode_pp_encryption_keys(V);
258
259
260%% ===== Attributes =====
261%%
262decode_PropertyParm(#'PropertyParm'{name      = "a",
263				    value     = [V],
264				    extraInfo =  asn1_NOVALUE}) ->
265    decode_pp_attribute(V);
266
267
268%% ===== Media Announcements =====
269%%
270decode_PropertyParm(#'PropertyParm'{name      = "m",
271				    value     = [V],
272				    extraInfo = asn1_NOVALUE}) ->
273    decode_pp_media_announcement(V);
274
275
276decode_PropertyParm(_PP) ->
277    ?d("decode_PropertyParm -> entry with"
278       "~n   _PP: ~p", [_PP]),
279    {error, undefined_PropertyParm}.
280
281
282%%-----------------------------------------------------------------
283%% Generate PropertyParm records from sdp records
284%%-----------------------------------------------------------------
285
286encode_PropertyGroups(PGs) ->
287    encode_PropertyGroups(PGs, []).
288
289
290encode_PropertyGroups([], Acc) ->
291    {ok, lists:reverse(Acc)};
292encode_PropertyGroups([PG | PGs], Acc) ->
293    {ok, EncodedPG} = encode_PropertyGroup(PG),
294    encode_PropertyGroups(PGs, [EncodedPG | Acc]).
295
296
297encode_PropertyGroup(PG) ->
298    encode_PropertyGroup(PG, []).
299
300encode_PropertyGroup([], Acc) ->
301    {ok, lists:reverse(Acc)};
302encode_PropertyGroup([PP | PG], Acc) ->
303    EncodedPP = encode_PropertyParm(PP),
304    encode_PropertyGroup(PG, [EncodedPP | Acc]).
305
306
307%% ===== Protocol Version =====
308%%
309encode_PropertyParm(#megaco_sdp_v{version = Version}) ->
310    encode_pp_protocol_version(Version);
311
312
313%% ===== Origin =====
314%%
315encode_PropertyParm(#megaco_sdp_o{user_name    = User,
316				  session_id   = SessionId,
317				  version      = Version,
318				  network_type = Network,
319				  address_type = AddrType,
320				  address      = Addr}) ->
321    encode_pp_origin(User, SessionId, Version, Network, AddrType, Addr);
322
323
324%% ===== Session Name =====
325%%
326encode_PropertyParm(#megaco_sdp_s{name = Name}) ->
327    encode_pp_session_name(Name);
328
329
330%% ===== Session and Media Information =====
331%%
332encode_PropertyParm(#megaco_sdp_i{session_descriptor = SD}) ->
333    encode_pp_session_media_id(SD);
334
335
336%% ===== URI =====
337%%
338encode_PropertyParm(#megaco_sdp_u{uri = URI}) ->
339    encode_pp_uri(URI);
340
341
342%% ===== Email Address and Phone Number =====
343%%
344encode_PropertyParm(#megaco_sdp_e{email = Email}) ->
345    encode_pp_email(Email);
346
347encode_PropertyParm(#megaco_sdp_p{phone_number = Num}) ->
348    encode_pp_phone(Num);
349
350
351%% ===== Connection Data =====
352%%
353encode_PropertyParm(#megaco_sdp_c{network_type    = NetType,
354				  address_type    = AddressType,
355				  connection_addr = ConnectionAddr}) ->
356    encode_pp_connection_data(NetType, AddressType, ConnectionAddr);
357
358
359%% ===== Bandwidth =====
360%%
361encode_PropertyParm(#megaco_sdp_b{bwtype    = BwType,
362				  bandwidth = Bandwidth}) ->
363    encode_pp_bandwidth(BwType, Bandwidth);
364
365
366%% ===== Times, Repeat Times and Time Zones =====
367%%
368encode_PropertyParm(#megaco_sdp_t{start = Start, stop = Stop}) ->
369    encode_pp_times(Start, Stop);
370
371encode_PropertyParm(#megaco_sdp_r{repeat_interval = Repeat,
372				  active_duration = Duration,
373				  list_of_offsets = ListOfOffsets}) ->
374    encode_pp_rtimes(Repeat, Duration, ListOfOffsets);
375
376encode_PropertyParm(#megaco_sdp_z{list_of_adjustments = LOA}) ->
377    encode_pp_tzones(LOA);
378
379
380%% ===== Encryption Keys =====
381%%
382encode_PropertyParm(#megaco_sdp_k{method         = Method,
383				  encryption_key = EncryptionKey}) ->
384    encode_pp_encryption_keys(Method, EncryptionKey);
385
386
387%% ===== Attributes =====
388%%
389encode_PropertyParm(#megaco_sdp_a_cat{category = Category}) ->
390    encode_pp_attribute_cat(Category);
391
392encode_PropertyParm(#megaco_sdp_a_keywds{keywords = Keywords}) ->
393    encode_pp_attribute_keywds(Keywords);
394
395encode_PropertyParm(#megaco_sdp_a_tool{name_and_version = NameAndVersion}) ->
396    encode_pp_attribute_tool(NameAndVersion);
397
398encode_PropertyParm(#megaco_sdp_a_ptime{packet_time = PacketTime}) ->
399    encode_pp_attribute_ptime(PacketTime);
400
401encode_PropertyParm(
402  #megaco_sdp_a_maxptime{maximum_packet_time = MaxPacketTime}) ->
403    encode_pp_attribute_maxptime(MaxPacketTime);
404
405encode_PropertyParm(#megaco_sdp_a_rtpmap{payload_type   = Payload,
406					 encoding_name  = EncName,
407					 clock_rate     = ClockRate,
408					 encoding_parms = EncPar}) ->
409    encode_pp_attribute_rtpmap(Payload, EncName, ClockRate, EncPar);
410
411encode_PropertyParm(#megaco_sdp_a_orient{orientation = Orientation}) ->
412    encode_pp_attribute_orient(Orientation);
413
414encode_PropertyParm(#megaco_sdp_a_type{conf_type = CType}) ->
415    encode_pp_attribute_type(CType);
416
417encode_PropertyParm(#megaco_sdp_a_charset{char_set = CharSet}) ->
418    encode_pp_attribute_charset(CharSet);
419
420encode_PropertyParm(#megaco_sdp_a_sdplang{tag = Tag}) ->
421    encode_pp_attribute_sdplang(Tag);
422
423encode_PropertyParm(#megaco_sdp_a_lang{tag = Tag}) ->
424    encode_pp_attribute_lang(Tag);
425
426encode_PropertyParm(#megaco_sdp_a_framerate{frame_rate = FrameRate}) ->
427    encode_pp_attribute_framerate(FrameRate);
428
429encode_PropertyParm(#megaco_sdp_a_quality{quality = Quality}) ->
430    encode_pp_attribute_quality(Quality);
431
432encode_PropertyParm(#megaco_sdp_a_fmtp{format = Fmt, param = Params}) ->
433    encode_pp_attribute_fmtp(Fmt, Params);
434
435encode_PropertyParm(#megaco_sdp_a{attribute = Attr, value = Value}) ->
436    encode_pp_attribute(Attr, Value);
437
438
439%% ===== Media Announcements =====
440%%
441encode_PropertyParm(#megaco_sdp_m{media     = Media,
442				  port      = Port,
443				  num_ports = NOP,
444				  transport = Transport,
445				  fmt_list  = FMT}) ->
446    encode_pp_media_announcement(Media, Port, NOP, Transport, FMT);
447
448
449%% This is a "manually" encoded PropertyParm, leave it as is.
450%%
451encode_PropertyParm(PP) when is_record(PP, 'PropertyParm') ->
452    PP;
453
454
455%% Bad data
456encode_PropertyParm(SDP) ->
457    error({unknown_sdp, SDP}).
458
459
460%%-----------------------------------------------------------------
461%% Func: get_sdp_record_from_PropertGroup/2
462%% Description: Get all sdp records of a certain type from a
463%%              property group
464%%-----------------------------------------------------------------
465
466get_sdp_record_from_PropertyGroup(Type, PG)
467  when is_atom(Type) and is_list(PG) ->
468    F = fun(R) -> not is_pg_record(Type, R) end,
469    lists:filter(F, PG).
470
471is_pg_record(v, R) when is_record(R, megaco_sdp_v)        -> true;
472is_pg_record(c, R) when is_record(R, megaco_sdp_c)        -> true;
473is_pg_record(m, R) when is_record(R, megaco_sdp_m)        -> true;
474is_pg_record(o, R) when is_record(R, megaco_sdp_o)        -> true;
475is_pg_record(a, R) when is_record(R, megaco_sdp_a)        -> true;
476is_pg_record(a, R) when is_record(R, megaco_sdp_a_ptime)  -> true;
477is_pg_record(a, R) when is_record(R, megaco_sdp_a_rtpmap) -> true;
478is_pg_record(b, R) when is_record(R, megaco_sdp_b)        -> true;
479is_pg_record(t, R) when is_record(R, megaco_sdp_t)        -> true;
480is_pg_record(r, R) when is_record(R, megaco_sdp_r)        -> true;
481is_pg_record(z, R) when is_record(R, megaco_sdp_z)        -> true;
482is_pg_record(k, R) when is_record(R, megaco_sdp_k)        -> true;
483is_pg_record(s, R) when is_record(R, megaco_sdp_s)        -> true;
484is_pg_record(i, R) when is_record(R, megaco_sdp_i)        -> true;
485is_pg_record(u, R) when is_record(R, megaco_sdp_u)        -> true;
486is_pg_record(e, R) when is_record(R, megaco_sdp_e)        -> true;
487is_pg_record(p, R) when is_record(R, megaco_sdp_p)        -> true;
488is_pg_record(_, _)                                        -> false.
489
490
491%%======================================================================
492%% Internal functions
493%%======================================================================
494
495%% ===== Protocol Version =====
496%%
497decode_pp_protocol_version(Value) when is_list(Value) ->
498    ?d("decode_pp_protocol_version -> entry with"
499       "~n   Value: ~p", [Value]),
500    Version = s2i(Value, invalid_protocol_version),
501    ?d("decode_pp_protocol_version -> entry with"
502       "~n   Version: ~w", [Version]),
503    decode_pp_protocol_version(Version);
504decode_pp_protocol_version(Version) when is_integer(Version) ->
505    ?d("decode_pp_protocol_version -> entry with"
506       "~n   Version: ~w", [Version]),
507    {ok, #megaco_sdp_v{version = Version}}.
508
509encode_pp_protocol_version(Version) when is_integer(Version) ->
510    ?d("encode_pp_protocol_version -> entry with"
511       "~n   Version: ~w", [Version]),
512    #'PropertyParm'{name  =  "v",
513		    value = [integer_to_list(Version)]};
514encode_pp_protocol_version(Version) ->
515    error({invalid_protocol_version, Version}).
516
517
518%% ===== Origin =====
519%%
520decode_pp_origin(Value) ->
521    ?d("decode_pp_origin -> entry with"
522       "~n   Value: ~p", [Value]),
523    case string:tokens(Value, " \t") of
524	[User, SessId, SessVersion, NetType, AddrType, UnicastAddress] ->
525	    ?d("decode_pp_origin -> entry with"
526	       "~n   User:           ~p"
527	       "~n   SessionId:      ~p"
528	       "~n   Version:        ~p"
529	       "~n   NetType:        ~p"
530	       "~n   AddrType:       ~p"
531	       "~n   UnicastAddress: ~p",
532	       [User, SessId, SessVersion, NetType, AddrType, UnicastAddress]),
533	    F = fun(X, R) ->
534			case (catch list_to_integer(X)) of
535			    I when is_integer(I) ->
536				I;
537			    _ ->
538				error({invalid_origin, {R, X}})
539			end
540		end,
541	    SID = F(SessId,      sess_id),
542	    V   = F(SessVersion, sess_version),
543	    SDP = #megaco_sdp_o{user_name    = User,
544				session_id   = SID,
545				version      = V,
546				network_type = decode_network_type(NetType),
547				address_type = decode_address_type(AddrType),
548				address      = UnicastAddress},
549	    {ok, SDP};
550	Err ->
551	    ?d("decode_pp_origin -> "
552	       "~n   Err: ~p", [Err]),
553	    invalid_pp(origin, Value, Err)
554    end.
555
556encode_pp_origin(User0, SessionId0, SessVersion0,
557		 NetType0, AddrType0, UnicastAddr0) ->
558    ?d("do_encode_pp_origin -> entry with"
559       "~n   User0:        ~p"
560       "~n   SessionId0:   ~p"
561       "~n   SessVersion0: ~p"
562       "~n   NetType0:     ~p"
563       "~n   AddrType0:    ~p"
564       "~n   UnicastAddr0: ~p",
565       [User0, SessionId0, SessVersion0, NetType0, AddrType0, UnicastAddr0]),
566    User        = encode_origin_user(User0),
567    SessionId   = encode_origin_session_id(SessionId0),
568    SessVersion = encode_origin_sess_version(SessVersion0),
569    NetType     = encode_origin_network_type(NetType0),
570    AddrType    = encode_origin_address_type(AddrType0),
571    UnicastAddr = encode_origin_unicast_address(UnicastAddr0),
572    #'PropertyParm'{name  = "o",
573		    value = [
574			     User        ++ " " ++
575			     SessionId   ++ " " ++
576			     SessVersion ++ " " ++
577			     NetType     ++ " " ++
578			     AddrType    ++ " " ++
579			     UnicastAddr
580			    ]}.
581
582encode_origin_user(User) when is_list(User) ->
583    User;
584encode_origin_user(BadUser) ->
585    error({invalid_origin_user, BadUser}).
586
587encode_origin_session_id(SID) when is_integer(SID) ->
588    integer_to_list(SID);
589encode_origin_session_id(BadSID) ->
590    error({invalid_origin_session_id, BadSID}).
591
592encode_origin_sess_version(SessVersion) when is_integer(SessVersion) ->
593    integer_to_list(SessVersion);
594encode_origin_sess_version(BadSessVersion) ->
595    error({invalid_origin_sess_version, BadSessVersion}).
596
597encode_origin_network_type(NT) ->
598    case (catch encode_network_type(NT)) of
599	{error, _} ->
600	    error({invalid_origin_network_type, NT});
601	Val ->
602	    Val
603    end.
604
605encode_origin_address_type(AT) ->
606    case (catch encode_address_type(AT)) of
607	{error, _} ->
608	    error({invalid_origin_address_type, AT});
609	Val ->
610	    Val
611    end.
612
613encode_origin_unicast_address(UnicastAddr) when is_list(UnicastAddr) ->
614    UnicastAddr;
615encode_origin_unicast_address(BadUnicastAddr) ->
616    error({invalid_origin_unicast_address, BadUnicastAddr}).
617
618
619%% ===== Session Name =====
620%%
621decode_pp_session_name(Value) ->
622    ?d("decode_pp_session_name -> entry with"
623       "~n   Value: ~p", [Value]),
624    {ok, #megaco_sdp_s{name = Value}}.
625
626encode_pp_session_name(Name) when is_list(Name) ->
627    ?d("encode_pp_session_name -> entry with"
628       "~n   Name: '~s'", [Name]),
629    #'PropertyParm'{name  = "s",
630		    value = [Name]};
631encode_pp_session_name(BadName) ->
632    error({invalid_session_name, BadName}).
633
634
635%% ===== Session and Media Information =====
636%%
637decode_pp_session_media_id(Value) ->
638    ?d("decode_pp_session_media_id -> entry with"
639       "~n   Value: ~p", [Value]),
640    {ok, #megaco_sdp_i{session_descriptor = Value}}.
641
642encode_pp_session_media_id(SD) when is_list(SD) ->
643    ?d("encode_pp_session_media_id -> entry with"
644       "~n   SD: '~s'", [SD]),
645    #'PropertyParm'{name  = "i",
646		    value = [SD]};
647encode_pp_session_media_id(BadSD) ->
648    error({invalid_session_media_id, BadSD}).
649
650
651%% ===== URI =====
652%%
653decode_pp_uri(Value) ->
654    ?d("decode_pp_uri -> entry with"
655       "~n   Value: ~p", [Value]),
656    {ok, #megaco_sdp_u{uri = Value}}.
657
658encode_pp_uri(URI) when is_list(URI) ->
659    ?d("encode_pp_uri -> entry with"
660       "~n   URI: ~p", [URI]),
661    #'PropertyParm'{name  = "u",
662		    value = [URI]};
663encode_pp_uri(BadUri) ->
664    error({invalid_uri, BadUri}).
665
666
667%% ===== Email Address and Phone Number =====
668%%
669decode_pp_email(Value) ->
670    ?d("decode_pp_email -> entry with"
671       "~n   Value: ~p", [Value]),
672    {ok, #megaco_sdp_e{email = Value}}.
673
674encode_pp_email(Email) when is_list(Email) ->
675    ?d("encode_pp_email -> entry with"
676       "~n   Email: ~p", [Email]),
677    #'PropertyParm'{name  = "e",
678		    value = [Email]};
679encode_pp_email(BadEmail) ->
680    error({invalid_email, BadEmail}).
681
682decode_pp_phone(Value) ->
683    ?d("decode_pp_phone -> entry with"
684       "~n   Value: ~p", [Value]),
685    {ok, #megaco_sdp_p{phone_number = Value}}.
686
687encode_pp_phone(Phone) when is_list(Phone) ->
688    ?d("encode_pp_phone -> entry with"
689       "~n   Phone: ~p", [Phone]),
690    #'PropertyParm'{name  = "p",
691		    value = [Phone]};
692encode_pp_phone(BadPhone) ->
693    error({invalid_phone, BadPhone}).
694
695
696%% ===== Connection Data =====
697%%
698decode_pp_connection_data(Value) ->
699    ?d("decode_pp_connection_data -> entry with"
700       "~n   Value: ~p", [Value]),
701    case string:tokens(Value, " \t") of
702	[NetType, AddrType, ConnectionAddr] ->
703	    ?d("decode_pp_connection_data -> "
704	       "~n   NetType:        ~p"
705	       "~n   AddrType:       ~p"
706	       "~n   ConnectionAddr: ~p", [NetType, AddrType, ConnectionAddr]),
707	    NT = decode_network_type(NetType),
708	    AT = decode_address_type(AddrType),
709	    case AT of
710		ip4 ->
711		    ?d("decode_pp_connection_data -> ip4", []),
712		    ConnAddr =
713			case string:tokens(ConnectionAddr, "\/") of
714			    [Base, TtlStr, NumOfAddrs] ->
715				?d("decode_pp_connection_data -> "
716				   "~n   Base:      ~p"
717				   "~n   TtlStr:    ~p"
718				   "~n   NumOfAddrs:~p",
719				   [Base, TtlStr, NumOfAddrs]),
720				TTL = s2i(TtlStr,
721					  invalid_connection_data_ttl),
722				?d("decode_pp_connection_data -> TTL: ~p",
723				   [TTL]),
724				NOA =
725				    s2i(NumOfAddrs,
726					invalid_connection_data_conn_addr_num_of),
727				?d("decode_pp_connection_data -> NOA: ~p",
728				   [NOA]),
729				#megaco_sdp_c_conn_addr{base   = Base,
730							ttl    = TTL,
731							num_of = NOA};
732			    [Base, TtlStr] ->
733				?d("decode_pp_connection_data -> "
734				   "~n   Base:   ~p"
735				   "~n   TtlStr: ~p",
736				   [Base, TtlStr]),
737				TTL =
738				    s2i(TtlStr,
739					invalid_connection_data_conn_addr_ttl),
740				?d("decode_pp_connection_data -> TTL: ~p",
741				   [TTL]),
742				#megaco_sdp_c_conn_addr{base = Base,
743							ttl  = TTL};
744			    [Base] ->
745				Base
746			end,
747		    ?d("decode_pp_connection_data -> "
748		       "~n   ConnAddr: ~p", [ConnAddr]),
749		    SDP = #megaco_sdp_c{network_type    = NT,
750					address_type    = AT,
751					connection_addr = ConnAddr},
752		    {ok, SDP};
753		ip6 ->
754		    ?d("decode_pp_connection_data -> ip6", []),
755		    SDP = #megaco_sdp_c{network_type    = NT,
756					address_type    = AT,
757					connection_addr = ConnectionAddr},
758		    {ok, SDP};
759		_ ->
760		    SDP = #megaco_sdp_c{network_type    = NT,
761					address_type    = AT,
762					connection_addr = ConnectionAddr},
763		    {ok, SDP}
764	    end;
765	Err ->
766	    invalid_pp(connection_data, Value, Err)
767    end.
768
769encode_pp_connection_data(NetType0, AddrType0, ConnAddr0) ->
770    ?d("encode_pp_connection_data -> entry with"
771       "~n   NetType0:  ~p"
772       "~n   AddrType0: ~p"
773       "~n   ConnAddr0: ~p", [NetType0, AddrType0, ConnAddr0]),
774    NetType  = encode_conn_data_network_type(NetType0),
775    AddrType = encode_conn_data_address_type(AddrType0),
776    ConnAddr = encode_conn_data_conn_addr(AddrType0, ConnAddr0),
777    Val      = NetType ++ " " ++ AddrType ++ " " ++ ConnAddr,
778    #'PropertyParm'{name  = "c",
779		    value = [Val]}.
780
781encode_conn_data_network_type(NT) ->
782    case (catch encode_network_type(NT)) of
783	{error, _} ->
784	    error({invalid_connection_data_network_type, NT});
785	Val ->
786	    Val
787    end.
788
789encode_conn_data_address_type(AT) ->
790    case (catch encode_address_type(AT)) of
791	{error, _} ->
792	    error({invalid_connection_data_address_type, AT});
793	Val ->
794	    Val
795    end.
796
797encode_conn_data_conn_addr(_, CA) when is_list(CA) ->
798    CA;
799encode_conn_data_conn_addr(ip4, CA)
800  when is_record(CA, megaco_sdp_c_conn_addr) ->
801    encode_conn_data_conn_addr(CA);
802encode_conn_data_conn_addr(AT, CA)
803  when is_list(AT) and is_record(CA, megaco_sdp_c_conn_addr) ->
804    case tolower(AT) of
805	"ip4" ->
806	    encode_conn_data_conn_addr(CA);
807	_ ->
808	    error({invalid_connection_data_conn_addr, {AT, CA}})
809    end;
810encode_conn_data_conn_addr(_, BadCA) ->
811    error({invalid_connection_data_conn_addr, BadCA}).
812
813encode_conn_data_conn_addr(#megaco_sdp_c_conn_addr{base   = Base0,
814						   ttl    = TTL0,
815						   num_of = undefined}) ->
816    Base = encode_conn_data_conn_addr_base(Base0),
817    TTL  = encode_conn_data_conn_addr_ttl(TTL0),
818    Base ++ "/" ++ TTL;
819encode_conn_data_conn_addr(#megaco_sdp_c_conn_addr{base   = Base0,
820						   ttl    = TTL0,
821						   num_of = NumOf0}) ->
822    Base  = encode_conn_data_conn_addr_base(Base0),
823    TTL   = encode_conn_data_conn_addr_ttl(TTL0),
824    NumOf = encode_conn_data_conn_addr_num_of(NumOf0),
825    Base ++ "/" ++ TTL ++ "/" ++ NumOf.
826
827encode_conn_data_conn_addr_base(Base) when is_list(Base) ->
828    Base;
829encode_conn_data_conn_addr_base(BadBase) ->
830    error({invalid_connection_data_conn_addr_base, BadBase}).
831
832encode_conn_data_conn_addr_ttl(TTL) when is_integer(TTL) ->
833    integer_to_list(TTL);
834encode_conn_data_conn_addr_ttl(BadTTL) ->
835    error({invalid_connection_data_conn_addr_ttl, BadTTL}).
836
837encode_conn_data_conn_addr_num_of(NumOf) when is_integer(NumOf) ->
838    integer_to_list(NumOf);
839encode_conn_data_conn_addr_num_of(BadNumOf) ->
840    error({invalid_connection_data_conn_addr_num_of, BadNumOf}).
841
842
843%% ===== Bandwidth =====
844%%
845decode_pp_bandwidth(Value) ->
846    ?d("decode_pp_bandwidth -> entry with"
847       "~n   Value: ~p", [Value]),
848    case string:tokens(Value, ":") of
849	[BwTypeStr, BandwidthStr] ->
850	    ?d("decode_pp_bandwidth -> "
851	       "~n   BwTypeStr:    ~p"
852	       "~n   BandwidthStr: ~p", [BwTypeStr, BandwidthStr]),
853	    BwType    = decode_bandwidth_bwt(BwTypeStr),
854	    ?d("decode_pp_bandwidth -> "
855	       "~n   BwType: ~w", [BwType]),
856	    Bandwidth = decode_bandwidth_bw(BandwidthStr),
857	    ?d("decode_pp_bandwidth -> "
858	       "~n   Bandwidth: ~w", [Bandwidth]),
859	    SDP = #megaco_sdp_b{bwtype    = BwType,
860				bandwidth = Bandwidth},
861	    {ok, SDP};
862	Err ->
863	    invalid_pp(bandwidth_info, Value, Err)
864    end.
865
866encode_pp_bandwidth(BwType0, Bandwidth0) ->
867    ?d("encode_pp_bandwidth -> entry with"
868       "~n   BwType0:    ~p"
869       "~n   Bandwidth0: ~p", [BwType0, Bandwidth0]),
870    BwType    = encode_bandwidth_bwt(BwType0),
871    Bandwidth = encode_bandwidth_bw(Bandwidth0),
872    Val       = BwType ++ ":" ++ Bandwidth,
873    #'PropertyParm'{name  = "b",
874		    value = [Val]}.
875
876decode_bandwidth_bwt("CT") ->
877    ct;
878decode_bandwidth_bwt("AS") ->
879    as;
880decode_bandwidth_bwt(BwType) when is_list(BwType) ->
881    BwType.
882
883encode_bandwidth_bwt(ct) ->
884    "CT";
885encode_bandwidth_bwt(as) ->
886    "AS";
887encode_bandwidth_bwt(BwType) when is_list(BwType) ->
888    BwType;
889encode_bandwidth_bwt(BadBwType) ->
890    error({invalid_bandwidth_bwtype, BadBwType}).
891
892decode_bandwidth_bw(Bandwidth) ->
893    s2i(Bandwidth, invalid_bandwidth_bandwidth).
894
895encode_bandwidth_bw(Bandwidth) when is_integer(Bandwidth) ->
896    integer_to_list(Bandwidth);
897encode_bandwidth_bw(BadBandwidth) ->
898    error({invalid_bandwidth_bandwidth, BadBandwidth}).
899
900
901%% ===== Times =====
902%%
903decode_pp_times(Value) ->
904    ?d("decode_pp_times -> entry with"
905       "~n   Value: ~p", [Value]),
906    case string:tokens(Value, " \t") of
907	[StartStr, StopStr] ->
908	    ?d("decode_pp_times -> "
909	       "~n   StartStr: ~p"
910	       "~n   StopStr:  ~p", [StartStr, StopStr]),
911	    Start = decode_times_start(StartStr),
912	    ?d("decode_pp_times -> entry with"
913	       "~n   Stop: ~w", [Start]),
914	    Stop  = decode_times_stop(StopStr),
915	    ?d("decode_pp_times -> entry with"
916	       "~n   Stop:  ~w", [Stop]),
917	    SDP = #megaco_sdp_t{start = Start,
918				stop  = Stop},
919	    {ok, SDP};
920	Err ->
921	    invalid_pp(times, Value, Err)
922    end.
923
924encode_pp_times(Start0, Stop0) ->
925    ?d("encode_pp_times -> entry with"
926       "~n   Start0:  ~p"
927       "~n   Stop0:   ~p", [Start0, Stop0]),
928    Start = encode_times_start(Start0),
929    Stop  = encode_times_stop(Stop0),
930    Val = Start ++ " " ++ Stop,
931    #'PropertyParm'{name  = "t",
932		    value = [Val]}.
933
934decode_times_start(Time) ->
935    s2i(Time, invalid_times_start).
936
937encode_times_start(Time) when is_integer(Time) ->
938    integer_to_list(Time);
939encode_times_start(BadTime) ->
940    error({invalid_times_start, BadTime}).
941
942decode_times_stop(Time) ->
943    s2i(Time, invalid_times_stop).
944
945encode_times_stop(Time) when is_integer(Time) ->
946    integer_to_list(Time);
947encode_times_stop(BadTime) ->
948    error({invalid_times_stop, BadTime}).
949
950
951%% ===== Repeat Times =====
952%%
953decode_pp_rtimes(Value) ->
954    ?d("decode_pp_rtimes -> entry with"
955       "~n   Value: ~p", [Value]),
956    case string:tokens(Value, " \t") of
957	[Repeat, Duration | ListOfOffsets] ->
958	    ?d("decode_pp_rtimes -> "
959	       "~n   Repeat:        ~p"
960	       "~n   Duration:      ~p"
961	       "~n   ListOfOffsets: ~p", [Repeat, Duration, ListOfOffsets]),
962	    SDP = #megaco_sdp_r{repeat_interval = Repeat,
963				active_duration = Duration,
964				list_of_offsets = ListOfOffsets},
965	    {ok, SDP};
966	Err ->
967	    invalid_pp(repeat_times, Value, Err)
968    end.
969
970encode_pp_rtimes(Repeat0, Duration0, ListOfOffsets0) ->
971    ?d("encode_pp_rtimes -> entry with"
972       "~n   Repeat0:        ~p"
973       "~n   Duration0:      ~p"
974       "~n   ListOfOffsets0: ~p", [Repeat0, Duration0, ListOfOffsets0]),
975    Repeat        = encode_rtimes_repeat(Repeat0),
976    Duration      = encode_rtimes_duration(Duration0),
977    ListOfOffsets = encode_rtimes_list_of_offsets(ListOfOffsets0),
978    Val = Repeat ++ " " ++ Duration ++ ListOfOffsets,
979    #'PropertyParm'{name  = "r",
980		    value = [Val]}.
981
982encode_rtimes_repeat(Repeat) when is_list(Repeat) ->
983    Repeat;
984encode_rtimes_repeat(BadRepeat) ->
985    error({invalid_rtimes_repeat, BadRepeat}).
986
987encode_rtimes_duration(Duration) when is_list(Duration) ->
988    Duration;
989encode_rtimes_duration(BadDuration) ->
990    error({invalid_rtimes_duration, BadDuration}).
991
992encode_rtimes_list_of_offsets(LOO) when is_list(LOO) ->
993    F = fun(Off, Acc) when is_list(Off) ->
994		Acc ++ " " ++ Off;
995	   (BadOff, Acc) ->
996		error({invalid_rtimes_list_of_offsets, {BadOff, Acc}})
997	end,
998    lists:foldl(F, [], LOO);
999encode_rtimes_list_of_offsets(BadLoo) ->
1000    error({invalid_rtimes_list_of_offsets, BadLoo}).
1001
1002
1003%% ===== Time Zones =====
1004%%
1005decode_pp_tzones(Value) when is_list(Value) and (length(Value) > 0) ->
1006    ?d("decode_pp_ztimes -> entry with"
1007       "~n   Value: ~p", [Value]),
1008    LOA = decode_tzones_list_of_adjustments(string:tokens(Value, " \t"), []),
1009    {ok, #megaco_sdp_z{list_of_adjustments = LOA}};
1010decode_pp_tzones(BadValue) ->
1011    error({invalid_tzones_list_of_adjustments, BadValue}).
1012
1013encode_pp_tzones(LOA) ->
1014    ?d("encode_pp_ztimes -> entry with"
1015       "~n   LOA: ~p", [LOA]),
1016    Val = encode_tzones_list_of_adjustments(LOA),
1017    #'PropertyParm'{name  = "z",
1018		    value = [Val]}.
1019
1020decode_tzones_list_of_adjustments([], Acc) ->
1021    lists:reverse(Acc);
1022decode_tzones_list_of_adjustments([Adj], Acc) ->
1023    error({invalid_tzones_list_of_adjustments, Adj, lists:reverse(Acc)});
1024decode_tzones_list_of_adjustments([Time, Offset | LOA], Acc) ->
1025    Adj = #megaco_sdp_z_adjustement{time = Time, offset = Offset},
1026    decode_tzones_list_of_adjustments(LOA, [Adj | Acc]).
1027
1028encode_tzones_list_of_adjustments([H|_] = LOA)
1029  when is_record(H, megaco_sdp_z_adjustement) ->
1030    F = fun(#megaco_sdp_z_adjustement{time = T, offset = O}, Acc) ->
1031		Acc ++ " " ++ T ++ " " ++ O;
1032	   (BadAdjustment, Acc) ->
1033		error({invalid_tzones_list_of_adjustments,
1034		       {BadAdjustment, Acc}})
1035	end,
1036    lists:foldl(F, [], LOA);
1037encode_tzones_list_of_adjustments(LOA) ->
1038    error({invalid_tzones_list_of_adjustments, LOA}).
1039
1040
1041%% ===== Encryption Keys =====
1042%%
1043decode_pp_encryption_keys(Value) ->
1044    ?d("decode_pp_encryption_keys -> entry with"
1045       "~n   Value: ~p", [Value]),
1046    {M, E} =
1047	case string:tokens(Value, ":") of
1048	    [Method, EncryptionKey] ->
1049		?d("decode_pp_encryption_keys -> "
1050		   "~n   Method:        ~p"
1051		   "~n   EncryptionKey: ~p", [Method, EncryptionKey]),
1052		{Method, EncryptionKey};
1053	    [Method] ->
1054		?d("decode_pp_encryption_keys -> "
1055		   "~n   Method: ~p", [Method]),
1056		{Method, undefined};
1057	    Err ->
1058		invalid_pp(encryption_key, Value, Err)
1059	end,
1060    M2 =
1061	case tolower(M) of
1062	    "clear" ->
1063		clear;
1064	    "base64" ->
1065		base64;
1066	    "uri" ->
1067		uri;
1068	    "prompt" ->
1069		prompt;
1070	    _ ->
1071		M
1072	end,
1073    ?d("decode_pp_encryption_keys -> "
1074       "~n   M2: ~p", [M2]),
1075    SDP = #megaco_sdp_k{method         = M2,
1076			encryption_key = E},
1077    {ok, SDP}.
1078
1079encode_pp_encryption_keys(prompt = _Method, undefined) ->
1080    ?d("encode_pp_encryption_keys(prompt) -> entry", []),
1081    #'PropertyParm'{name  = "k",
1082		    value = ["prompt"]};
1083encode_pp_encryption_keys(clear = _Method, EncryptionKey)
1084  when is_list(EncryptionKey) ->
1085    ?d("encode_pp_encryption_keys(clear) -> entry with"
1086       "~n   EncryptionKey: ~p", [EncryptionKey]),
1087    #'PropertyParm'{name  = "k",
1088		    value = ["clear:" ++ EncryptionKey]};
1089encode_pp_encryption_keys(base64 = _Method, EncryptionKey)
1090  when is_list(EncryptionKey) ->
1091    ?d("encode_pp_encryption_keys(base64) -> entry with"
1092       "~n   EncryptionKey: ~p", [EncryptionKey]),
1093    #'PropertyParm'{name  = "k",
1094		    value = ["base64:" ++ EncryptionKey]};
1095encode_pp_encryption_keys(uri = _Method, EncryptionKey)
1096  when is_list(EncryptionKey) ->
1097    ?d("encode_pp_encryption_keys(uri) -> entry with"
1098       "~n   EncryptionKey: ~p", [EncryptionKey]),
1099    #'PropertyParm'{name  = "k",
1100		    value = ["uri:" ++ EncryptionKey]};
1101encode_pp_encryption_keys(Method, EncryptionKey)
1102  when is_list(Method) and is_list(EncryptionKey) ->
1103    ?d("encode_pp_encryption_keys -> entry with"
1104       "~n   Method:        ~p"
1105       "~n   EncryptionKey: ~p", [Method, EncryptionKey]),
1106    #'PropertyParm'{name  = "k",
1107		    value = [Method ++ ":" ++ EncryptionKey]};
1108encode_pp_encryption_keys(BadMethod, BadEK) ->
1109    error({invalid_encryption_keys, {BadMethod, BadEK}}).
1110
1111
1112%% ===== Attributes =====
1113%%
1114decode_pp_attribute(Value) ->
1115    ?d("decode_pp_attribute -> entry with"
1116       "~n   Value: ~p", [Value]),
1117    First = string:chr(Value, $:),
1118    if
1119	(First > 0) and (First < length(Value)) ->
1120	    ?d("decode_pp_attribute -> value attribute", []),
1121	    Attr      = string:substr(Value, 1, First -1),
1122	    AttrValue = string:substr(Value, First + 1),
1123	    ?d("decode_pp_attribute -> "
1124	       "~n   Attr:      ~p"
1125	       "~n   AttrValue: ~p", [Attr, AttrValue]),
1126	    decode_pp_attribute_value(Attr, AttrValue);
1127
1128	First > 0 ->
1129	    ?d("decode_pp_attribute -> value attribute (empty)", []),
1130	    Attr = string:substr(Value, 1, First -1),
1131	    ?d("decode_pp_attribute -> "
1132	       "~n   Attr: ~p", [Attr]),
1133	    decode_pp_attribute_value(Attr, []);
1134
1135	true ->
1136	    ?d("decode_pp_attribute -> binary attribute", []),
1137	    {ok, #megaco_sdp_a{attribute = Value}}
1138
1139    end.
1140
1141decode_pp_attribute_value("cat", AttrValue) ->
1142    ?d("decode_pp_attribute -> cat", []),
1143    SDP = #megaco_sdp_a_cat{category = AttrValue},
1144    {ok, SDP};
1145
1146decode_pp_attribute_value("keywds", AttrValue) ->
1147    ?d("decode_pp_attribute -> keywds", []),
1148    SDP = #megaco_sdp_a_keywds{keywords = AttrValue},
1149    {ok, SDP};
1150
1151decode_pp_attribute_value("tool", AttrValue) ->
1152    ?d("decode_pp_attribute -> tool", []),
1153    SDP = #megaco_sdp_a_tool{name_and_version = AttrValue},
1154    {ok, SDP};
1155
1156decode_pp_attribute_value("ptime", AttrValue) ->
1157    ?d("decode_pp_attribute -> ptime", []),
1158    PacketTimeStr = string:strip(AttrValue, both, $ ),
1159    PacketTime =
1160	s2i(PacketTimeStr, invalid_ptime_packet_time),
1161    ?d("decode_pp_attribute -> PacketTime: ~w", [PacketTime]),
1162    SDP = #megaco_sdp_a_ptime{packet_time = PacketTime},
1163    {ok, SDP};
1164
1165decode_pp_attribute_value("maxptime", AttrValue) ->
1166    ?d("decode_pp_attribute -> maxptime", []),
1167    MaxPacketTimeStr = string:strip(AttrValue, both, $ ),
1168    MaxPacketTime =
1169	s2i(MaxPacketTimeStr, invalid_maxptime_maximum_packet_time),
1170    ?d("decode_pp_attribute -> MaxPacketTime: ~w", [MaxPacketTime]),
1171    SDP = #megaco_sdp_a_maxptime{maximum_packet_time = MaxPacketTime},
1172    {ok, SDP};
1173
1174decode_pp_attribute_value("rtpmap", AttrValue) ->
1175    ?d("decode_pp_attribute -> rtpmap", []),
1176    case string:tokens(AttrValue, "\/ \t") of
1177	[PayloadStr, EncName, ClockRateStr | EncPar] ->
1178	    ?d("decode_pp_attribute -> "
1179	       "~n   PayloadStr:   ~p"
1180	       "~n   EncName:      ~p"
1181	       "~n   ClockRateStr: ~p"
1182	       "~n   EncPar:       ~p",
1183	       [PayloadStr, EncName, ClockRateStr, EncPar]),
1184		    Payload   =
1185		s2i(PayloadStr, invalid_rtpmap_payload),
1186	    ?d("decode_pp_attribute -> Payload: ~w",
1187	       [Payload]),
1188	    ClockRate =
1189		s2i(ClockRateStr, invalid_rtpmap_payload),
1190	    ?d("decode_pp_attribute -> ClockRate: ~w",
1191	       [ClockRate]),
1192	    SDP =
1193		#megaco_sdp_a_rtpmap{payload_type   = Payload,
1194				     encoding_name  = EncName,
1195				     clock_rate     = ClockRate,
1196				     encoding_parms = EncPar},
1197	    {ok, SDP};
1198	_ ->
1199	    error({invalid_rtpmap, AttrValue})
1200    end;
1201
1202decode_pp_attribute_value("orient", AttrValue) ->
1203    ?d("decode_pp_attribute -> orient", []),
1204    Orientation = decode_attribute_orientation(AttrValue),
1205    SDP = #megaco_sdp_a_orient{orientation = Orientation},
1206    {ok, SDP};
1207
1208decode_pp_attribute_value("type", AttrValue) ->
1209    ?d("decode_pp_attribute -> type", []),
1210    SDP = #megaco_sdp_a_type{conf_type = AttrValue},
1211    {ok, SDP};
1212
1213decode_pp_attribute_value("charset", AttrValue) ->
1214    ?d("decode_pp_attribute -> charset", []),
1215    SDP = #megaco_sdp_a_charset{char_set = AttrValue},
1216    {ok, SDP};
1217
1218decode_pp_attribute_value("sdplang", AttrValue) ->
1219    ?d("decode_pp_attribute -> sdplang", []),
1220    SDP = #megaco_sdp_a_sdplang{tag = AttrValue},
1221    {ok, SDP};
1222
1223decode_pp_attribute_value("lang", AttrValue) ->
1224    ?d("decode_pp_attribute -> lang", []),
1225    SDP = #megaco_sdp_a_lang{tag = AttrValue},
1226    {ok, SDP};
1227
1228decode_pp_attribute_value("framerate", AttrValue) ->
1229    ?d("decode_pp_attribute -> framerate", []),
1230    SDP = #megaco_sdp_a_framerate{frame_rate = AttrValue},
1231    {ok, SDP};
1232
1233decode_pp_attribute_value("quality", AttrValue) ->
1234    ?d("decode_pp_attribute -> quality", []),
1235    QualityStr = AttrValue,
1236    Quality = s2i(QualityStr, invalid_quality_quality),
1237    ?d("decode_pp_attribute -> Quality: ~w", [Quality]),
1238    SDP = #megaco_sdp_a_quality{quality = Quality},
1239    {ok, SDP};
1240
1241decode_pp_attribute_value("fmtp", AttrValue) ->
1242    ?d("decode_pp_attribute -> fmtp", []),
1243    FMTP = AttrValue,
1244    First = string:chr(FMTP, $ ),
1245    if
1246	(First > 0) and (First < length(FMTP)) ->
1247	    ?d("decode_pp_attribute_value -> valid fmtp with params", []),
1248	    Format = string:substr(FMTP, 1, First - 1),
1249	    Params = string:substr(FMTP, First + 1),
1250	    ?d("decode_pp_attribute_value -> "
1251	       "~n   Format: ~p"
1252	       "~n   Params: ~p", [Format, Params]),
1253	    SDP = #megaco_sdp_a_fmtp{format = Format,
1254				     param  = Params},
1255	    {ok, SDP};
1256
1257	First > 0 ->
1258	    ?d("decode_pp_attribute -> valid fmtp", []),
1259	    Format = string:substr(FMTP, 1, First - 1),
1260	    ?d("decode_pp_attribute -> "
1261	       "~n   Format: ~p", [Format]),
1262	    {ok, #megaco_sdp_a_fmtp{format = Format, param = []}};
1263
1264	true ->
1265	    ?d("decode_pp_attribute_value -> no params", []),
1266	    {ok, #megaco_sdp_a_fmtp{format = FMTP, param = []}}
1267
1268    end;
1269
1270decode_pp_attribute_value(Attr, AttrValue) ->
1271    ?d("decode_pp_attribute -> unknown value attribute", []),
1272    {ok, #megaco_sdp_a{attribute = Attr, value = AttrValue}}.
1273
1274decode_attribute_orientation("portrait") ->
1275    portrait;
1276decode_attribute_orientation("landscape") ->
1277    landscape;
1278decode_attribute_orientation("seascape") ->
1279    seascape;
1280decode_attribute_orientation(BadOrientation) ->
1281    error({invalid_orient_orientation, BadOrientation}).
1282
1283encode_attribute_orientation(portrait) ->
1284    "portrait";
1285encode_attribute_orientation(landscape) ->
1286    "landscape";
1287encode_attribute_orientation(seascape) ->
1288    "seascape";
1289encode_attribute_orientation(BadOrientation) ->
1290    error({invalid_orient_orientation, BadOrientation}).
1291
1292
1293encode_pp_attribute_cat(Cat) when is_list(Cat) ->
1294    ?d("encode_pp_attribute_cat -> entry with"
1295       "~n   Cat: ~p", [Cat]),
1296    #'PropertyParm'{name  = "a",
1297		    value = ["cat:" ++ Cat]};
1298encode_pp_attribute_cat(BadCat) ->
1299    error({invalid_cat_category, BadCat}).
1300
1301
1302encode_pp_attribute_keywds(Keywords) when is_list(Keywords) ->
1303    ?d("encode_pp_attribute_keywds -> entry with"
1304       "~n   Keywords: ~p", [Keywords]),
1305    #'PropertyParm'{name  = "a",
1306		    value = ["keywds:" ++ Keywords]};
1307encode_pp_attribute_keywds(BadKeywords) ->
1308    error({invalid_keywds_keywords, BadKeywords}).
1309
1310
1311encode_pp_attribute_tool(NameAndVersion) when is_list(NameAndVersion) ->
1312    ?d("encode_pp_attribute_tool -> entry with"
1313       "~n   NameAndVersion: ~p", [NameAndVersion]),
1314    #'PropertyParm'{name  = "a",
1315		    value = ["tool:" ++ NameAndVersion]};
1316encode_pp_attribute_tool(BadNameAndVersion) ->
1317    error({invalid_tool_name_and_version, BadNameAndVersion}).
1318
1319
1320encode_pp_attribute_ptime(PacketTime) when is_integer(PacketTime) ->
1321    ?d("encode_pp_attribute_ptime -> entry with"
1322       "~n   PacketTime: ~w", [PacketTime]),
1323    #'PropertyParm'{name  = "a",
1324		    value = ["ptime:" ++ integer_to_list(PacketTime)]};
1325encode_pp_attribute_ptime(BadPT) ->
1326    error({invalid_ptime_packet_time, BadPT}).
1327
1328
1329encode_pp_attribute_maxptime(MaxPacketTime) when is_integer(MaxPacketTime) ->
1330    ?d("encode_pp_attribute_maxptime -> entry with"
1331       "~n   MaxPacketTime: ~w", [MaxPacketTime]),
1332    #'PropertyParm'{name  = "a",
1333		    value = ["maxptime:" ++ integer_to_list(MaxPacketTime)]};
1334encode_pp_attribute_maxptime(BadMPT) ->
1335    error({invalid_maxptime_maximum_packet_time, BadMPT}).
1336
1337
1338encode_pp_attribute_rtpmap(Payload0, EncName0, ClockRate0, EncPar0) ->
1339    ?d("encode_pp_attribute_rtpmap -> entry with"
1340       "~n   Payload0:   ~p"
1341       "~n   EncName0:   ~p"
1342       "~n   ClockRate0: ~p"
1343       "~n   EncPar0:    ~p", [Payload0, EncName0, ClockRate0, EncPar0]),
1344    Payload   = encode_rtpmap_payload(Payload0),
1345    EncName   = encode_rtpmap_encoding_name(EncName0),
1346    ClockRate = encode_rtpmap_clockrate(ClockRate0),
1347    EncPar    = encode_rtpmap_encoding_parms(EncPar0),
1348    Val = "rtpmap:" ++ Payload ++ " " ++
1349	EncName ++ "/" ++ ClockRate ++ EncPar,
1350    #'PropertyParm'{name  = "a",
1351		    value = [Val]}.
1352
1353encode_rtpmap_payload(Payload) when is_integer(Payload) ->
1354    integer_to_list(Payload);
1355encode_rtpmap_payload(BadPayload) ->
1356    error({invalid_rtpmap_payload, BadPayload}).
1357
1358encode_rtpmap_encoding_name(EncName) when is_list(EncName) ->
1359    EncName;
1360encode_rtpmap_encoding_name(BadEncName) ->
1361    error({invalid_rtpmap_encoding_name, BadEncName}).
1362
1363encode_rtpmap_clockrate(ClockRate) when is_integer(ClockRate) ->
1364    integer_to_list(ClockRate);
1365encode_rtpmap_clockrate(BadClockRate) ->
1366    error({invalid_rtpmap_clockrate, BadClockRate}).
1367
1368encode_rtpmap_encoding_parms(EncPar) when is_list(EncPar) ->
1369    F = fun(EP, Acc) when is_list(EP) ->
1370		Acc ++ "/" ++ EP;
1371	   (BadEP, Acc) ->
1372		error({invalid_rtpmap_encoding_parms, {BadEP, Acc}})
1373	end,
1374    lists:foldl(F, [], EncPar);
1375encode_rtpmap_encoding_parms(BadEncPar) ->
1376    error({invalid_rtpmap_encoding_parms, BadEncPar}).
1377
1378
1379encode_pp_attribute_orient(Orientation0) ->
1380    ?d("encode_pp_attribute_orient -> entry with"
1381       "~n   Orientation0: ~w", [Orientation0]),
1382    Orientation = encode_attribute_orientation(Orientation0),
1383    #'PropertyParm'{name  = "a",
1384		    value = ["orient:" ++ Orientation]}.
1385
1386
1387encode_pp_attribute_type(CType) when is_list(CType) ->
1388    ?d("encode_pp_attribute_type -> entry with"
1389       "~n   CType: ~p", [CType]),
1390    #'PropertyParm'{name  = "a",
1391		    value = ["type:" ++ CType]};
1392encode_pp_attribute_type(BadCType) ->
1393    error({invalid_type_conf_type, BadCType}).
1394
1395
1396encode_pp_attribute_charset(CharSet) when is_list(CharSet) ->
1397    ?d("encode_pp_attribute_charset -> entry with"
1398       "~n   CharSet: ~p", [CharSet]),
1399    #'PropertyParm'{name  = "a",
1400		    value = ["charset:" ++ CharSet]};
1401encode_pp_attribute_charset(BadCharSet) ->
1402    error({invalid_charset_char_set, BadCharSet}).
1403
1404
1405encode_pp_attribute_sdplang(SdpLang) when is_list(SdpLang) ->
1406    ?d("encode_pp_attribute_sdplang -> entry with"
1407       "~n   SdpLang: ~p", [SdpLang]),
1408    #'PropertyParm'{name  = "a",
1409		    value = ["sdplang:" ++ SdpLang]};
1410encode_pp_attribute_sdplang(BadSdpLang) ->
1411    error({invalid_sdplang_tag, BadSdpLang}).
1412
1413
1414encode_pp_attribute_lang(Lang) when is_list(Lang) ->
1415    ?d("encode_pp_attribute_lang -> entry with"
1416       "~n   Lang: ~p", [Lang]),
1417    #'PropertyParm'{name  = "a",
1418		    value = ["lang:" ++ Lang]};
1419encode_pp_attribute_lang(BadLang) ->
1420    error({invalid_lang_tag, BadLang}).
1421
1422
1423encode_pp_attribute_framerate(FrameRate) when is_list(FrameRate) ->
1424    ?d("encode_pp_attribute_framerate -> entry with"
1425       "~n   FrameRate: ~p", [FrameRate]),
1426    #'PropertyParm'{name  = "a",
1427		    value = ["framerate:" ++ FrameRate]};
1428encode_pp_attribute_framerate(BadFrameRate) ->
1429    error({invalid_framerate_frame_rate, BadFrameRate}).
1430
1431
1432encode_pp_attribute_quality(Quality) when is_integer(Quality) ->
1433    ?d("encode_pp_attribute_quality -> entry with"
1434       "~n   Quality: ~w", [Quality]),
1435    #'PropertyParm'{name  = "a",
1436		    value = ["quality:" ++ integer_to_list(Quality)]};
1437encode_pp_attribute_quality(BadQ) ->
1438    error({invalid_quality_quality, BadQ}).
1439
1440
1441encode_pp_attribute_fmtp(Fmt0, Params0) ->
1442    ?d("encode_pp_attribute_rtpmap -> entry with"
1443       "~n   Fmt0:    ~p"
1444       "~n   Params0: ~p", [Fmt0, Params0]),
1445    Fmt    = encode_fmtp_format(Fmt0),
1446    Params = encode_fmtp_param(Params0),
1447    Val    = "fmtp:" ++ Fmt ++ " " ++ Params,
1448    #'PropertyParm'{name  = "a",
1449		    value = [Val]}.
1450
1451encode_fmtp_format(Fmt) when is_list(Fmt) ->
1452    Fmt;
1453encode_fmtp_format(BadFmt) ->
1454    error({invalid_fmtp_format, BadFmt}).
1455
1456encode_fmtp_param(Param) when is_list(Param) ->
1457    Param;
1458encode_fmtp_param(BadParam) ->
1459    error({invalid_fmtp_param, BadParam}).
1460
1461
1462encode_pp_attribute(Attr, undefined) when is_list(Attr) ->
1463    ?d("encode_pp_attribute_rtpmap -> entry with"
1464       "~n   Attr: ~p", [Attr]),
1465    #'PropertyParm'{name  = "a",
1466		    value = [Attr]};
1467encode_pp_attribute(Attr, Value) when is_list(Attr) and is_list(Value) ->
1468    ?d("encode_pp_attribute_rtpmap -> entry with"
1469       "~n   Attr:  ~p"
1470       "~n   Value: ~p", [Attr, Value]),
1471    #'PropertyParm'{name  = "a",
1472		    value = [Attr ++ ":" ++ Value]};
1473encode_pp_attribute(BadAttr, BadAttrValue) ->
1474    error({invalid_attribute, {BadAttr, BadAttrValue}}).
1475
1476
1477%% ===== Media Announcements =====
1478%%
1479decode_pp_media_announcement(Value) ->
1480    case string:tokens(Value, " \t") of
1481	[Media0, PortInfo, Transport | FMT] ->
1482	    Media =
1483		case tolower(Media0) of
1484		    "audio"       -> audio;
1485		    "video"       -> video;
1486		    "application" -> application;
1487		    "data"        -> data;
1488		    "control"     -> control;
1489		    _             -> Media0
1490		end,
1491	    {Port, NoOfPorts} =
1492		case string:tokens(PortInfo, "/") of
1493		    [P1, NP] ->
1494			{s2i(P1, invalid_media_announcement_port),
1495			 s2i(NP, invalid_media_announcement_nof_ports)};
1496		    [P2] ->
1497			{s2i(P2, invalid_media_announcement_port),
1498			 undefined};
1499		    Err ->
1500			invalid_pp(mnta_port_info, Value, Err)
1501		end,
1502	    SDP = #megaco_sdp_m{media     = Media,
1503				port      = Port,
1504				num_ports = NoOfPorts,
1505				transport = Transport,
1506				fmt_list  = FMT},
1507	    {ok, SDP};
1508	Err ->
1509	    invalid_pp(media_name_transp_addr, Value, Err)
1510    end.
1511
1512
1513encode_pp_media_announcement(Media0, Port0, undefined, Transport0, FMT0) ->
1514    ?d("encode_pp_media_announcement -> entry with"
1515       "~n   Media0:     ~p"
1516       "~n   Port0:      ~p"
1517       "~n   Transport0: ~p"
1518       "~n   FMT0:       ~p", [Media0, Port0, Transport0, FMT0]),
1519    Media     = encode_media_announcement_media(Media0),
1520    Port      = encode_media_announcement_port(Port0),
1521    Transport = encode_media_announcement_transport(Transport0),
1522    FMT       = encode_media_announcement_fmt_list(FMT0),
1523    do_encode_pp_media_announcement(Media, Port, "", Transport, FMT);
1524encode_pp_media_announcement(Media0, Port0, NumPorts0, Transport0, FMT0) ->
1525    ?d("encode_pp_media_announcement -> entry with"
1526       "~n   Media0:     ~p"
1527       "~n   Port0:      ~p"
1528       "~n   NumPorts0:  ~p"
1529       "~n   Transport0: ~p"
1530       "~n   FMT0:       ~p", [Media0, Port0, NumPorts0, Transport0, FMT0]),
1531    Media     = encode_media_announcement_media(Media0),
1532    Port      = encode_media_announcement_port(Port0),
1533    NumPorts  = encode_media_announcement_num_port(NumPorts0),
1534    Transport = encode_media_announcement_transport(Transport0),
1535    FMT       = encode_media_announcement_fmt_list(FMT0),
1536    do_encode_pp_media_announcement(Media, Port, NumPorts, Transport, FMT).
1537
1538do_encode_pp_media_announcement(Media, Port, NumOfPorts, Transport, FMT) ->
1539    Val = Media ++ " " ++ Port ++ NumOfPorts ++ " " ++ Transport ++ FMT,
1540    #'PropertyParm'{name  = "m",
1541		    value = [Val]}.
1542
1543encode_media_announcement_media(Media) when is_atom(Media) ->
1544    MaMedia = [audio, video, application, data, control],
1545    case lists:member(Media, MaMedia) of
1546	true ->
1547	    atom_to_list(Media);
1548	false ->
1549	    error({invalid_media_announcement_media, Media})
1550    end;
1551encode_media_announcement_media(Media) when is_list(Media) ->
1552    Media;
1553encode_media_announcement_media(BadMedia) ->
1554    error({invalid_media_announcement_media, BadMedia}).
1555
1556encode_media_announcement_port(Port) when is_integer(Port) ->
1557    integer_to_list(Port);
1558encode_media_announcement_port(BadPort) ->
1559    error({invalid_media_announcement_port, BadPort}).
1560
1561encode_media_announcement_num_port(NumPort) when is_integer(NumPort) ->
1562    "/" ++ integer_to_list(NumPort);
1563encode_media_announcement_num_port(BadNumPort) ->
1564    error({invalid_media_announcement_num_port, BadNumPort}).
1565
1566encode_media_announcement_transport(Transport) when is_list(Transport) ->
1567    Transport;
1568encode_media_announcement_transport(BadTransport) ->
1569    error({invalid_media_announcement_transport, BadTransport}).
1570
1571encode_media_announcement_fmt_list(FmtList) when is_list(FmtList) ->
1572    F = fun(FMT, Acc) when is_list(FMT) ->
1573		Acc ++ " " ++ FMT;
1574	   (BadFMT, Acc) ->
1575		error({invalid_media_announcement_fmt_list, {BadFMT, Acc}})
1576	end,
1577    lists:foldl(F, [], FmtList);
1578encode_media_announcement_fmt_list(BadFmtList) ->
1579    error({invalid_media_announcement_fmt_list, BadFmtList}).
1580
1581
1582decode_network_type(NT) when is_list(NT) ->
1583    case tolower(NT) of
1584	"in" -> in;
1585	_    -> NT
1586    end.
1587
1588encode_network_type(in)                  -> "IN";
1589encode_network_type(NT) when is_list(NT) -> NT;
1590encode_network_type(Bad) ->
1591    {error, {invalid_network_type, Bad}}.
1592
1593
1594decode_address_type(AT) when is_list(AT) ->
1595    case tolower(AT) of
1596	"ip4" -> ip4;
1597	"ip6" -> ip6;
1598	_     -> AT
1599    end.
1600
1601encode_address_type(ip4)                 -> "IP4";
1602encode_address_type(ip6)                 -> "IP6";
1603encode_address_type(AT) when is_list(AT) ->
1604    case toupper(AT) of
1605	"IP4" -> "IP4";
1606	"IP6" -> "IP6";
1607	_     -> AT
1608    end;
1609encode_address_type(Crap) ->
1610    {error, {invalid_address_type, Crap}}.
1611
1612
1613s2i(S, E) ->
1614    case (catch list_to_integer(S)) of
1615	I when is_integer(I) ->
1616	    I;
1617	_ ->
1618	    error({E, S})
1619    end.
1620
1621-define(LOWER(Char),
1622        if
1623            Char >= $A, Char =< $Z ->
1624                Char - ($A - $a);
1625            true ->
1626                Char
1627        end).
1628tolower(Chars) ->
1629    [?LOWER(Char) || Char <- Chars].
1630
1631-define(UPPER(Char),
1632        if
1633            Char >= $a, Char =< $z ->
1634                Char + ($A - $a);
1635            true ->
1636                Char
1637        end).
1638toupper(Chars) ->
1639    [?UPPER(Char) || Char <- Chars].
1640
1641invalid_pp(What, Value, Error) ->
1642    {error, {invalid_PropertyParm, {What, Value, Error}}}.
1643
1644error(Reason) ->
1645    throw({error, Reason}).
1646
1647