1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2000-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: Handle configuration of Megaco/H.248
24%%----------------------------------------------------------------------
25
26-module(megaco_config).
27
28-behaviour(gen_server).
29
30%% Application internal exports
31-export([
32         start_link/0,
33         stop/0,
34
35         start_user/2,
36         stop_user/1,
37
38         user_info/2,
39         update_user_info/3,
40         conn_info/2,
41         update_conn_info/3,
42         system_info/1,
43
44         %% incr_counter/2,
45         incr_trans_id_counter/1,
46         incr_trans_id_counter/2,
47
48	 %% Verification functions
49         verify_val/2,
50%% 	 verify_strict_uint/1,
51%% 	 verify_strict_int/1, verify_strict_int/2,
52%% 	 verify_uint/1,
53%% 	 verify_int/1, verify_int/2,
54
55
56	 %% Reply limit counter
57	 cre_reply_counter/2,
58	 get_reply_counter/2,
59	 incr_reply_counter/2,
60	 del_reply_counter/2,
61
62	 %% Pending limit counter
63	 cre_pending_counter/3,
64	 get_pending_counter/2,
65	 incr_pending_counter/2,
66	 del_pending_counter/2,
67	 %% Backward compatibillity functions (to be removed in later versions)
68	 cre_pending_counter/1,
69	 get_pending_counter/1,
70	 incr_pending_counter/1,
71	 del_pending_counter/1,
72
73         lookup_local_conn/1,
74         connect/4, finish_connect/4,
75         autoconnect/4,
76         disconnect/1,
77	 connect_remote/3,
78	 disconnect_remote/2,
79	 init_conn_data/4,
80
81	 trans_sender_exit/2
82
83        ]).
84
85
86%% gen_server callbacks
87-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
88         terminate/2, code_change/3]).
89
90-define(SERVER, ?MODULE).
91-record(state, {parent_pid}).
92
93-include_lib("megaco/include/megaco.hrl").
94-include_lib("megaco/src/app/megaco_internal.hrl").
95
96
97-ifdef(MEGACO_TEST_CODE).
98-define(megaco_test_init(),
99	(catch ets:new(megaco_test_data, [set, public, named_table]))).
100-else.
101-define(megaco_test_init(),
102	ok).
103-endif.
104
105-define(TID_CNT(LMID), {LMID, trans_id_counter}).
106
107
108%%%----------------------------------------------------------------------
109%%% API
110%%%----------------------------------------------------------------------
111
112start_link() ->
113    ?d("start_link -> entry", []),
114    gen_server:start_link({local, ?SERVER}, ?MODULE, [self()], []).
115
116stop() ->
117    ?d("stop -> entry", []),
118    call({stop, self()}).
119
120start_user(UserMid, Config) ->
121    call({start_user, UserMid, Config}).
122
123stop_user(UserMid) ->
124    call({stop_user, UserMid}).
125
126user_info(UserMid, all) ->
127    All0 = ets:match_object(megaco_config, {{UserMid, '_'}, '_'}),
128    All1 = [{Item, Val} || {{_, Item}, Val} <- All0, Item /= trans_sender],
129    case lists:keysearch(trans_id_counter, 1, All1) of
130	{value, {_, Val}} ->
131	    lists:keyreplace(trans_id_counter, 1, All1, {trans_id, Val});
132	false when UserMid /= default ->
133	    [{trans_id, undefined_serial}|All1];
134	false ->
135	    All1
136    end;
137user_info(UserMid, receive_handle) ->
138    case call({receive_handle, UserMid}) of
139	{ok, RH} ->
140	    RH;
141	{error, Reason} ->
142	    exit(Reason)
143    end;
144user_info(UserMid, conn_data) ->
145    HandlePat = #megaco_conn_handle{local_mid = UserMid, remote_mid = '_'},
146    Pat = #conn_data{conn_handle      	  = HandlePat,
147                     serial           	  = '_',
148                     max_serial       	  = '_',
149                     request_timer    	  = '_',
150                     long_request_timer   = '_',
151
152                     auto_ack         	  = '_',
153
154                     trans_ack   	  = '_',
155                     trans_ack_maxcount	  = '_',
156
157                     trans_req   	  = '_',
158                     trans_req_maxcount	  = '_',
159                     trans_req_maxsize	  = '_',
160
161                     trans_timer   	  = '_',
162                     trans_sender         = '_',
163
164                     pending_timer     	  = '_',
165                     sent_pending_limit   = '_',
166                     recv_pending_limit   = '_',
167                     reply_timer      	  = '_',
168                     control_pid      	  = '_',
169                     monitor_ref      	  = '_',
170                     send_mod         	  = '_',
171                     send_handle      	  = '_',
172                     encoding_mod     	  = '_',
173                     encoding_config  	  = '_',
174                     protocol_version  	  = '_',
175                     auth_data         	  = '_',
176                     user_mod         	  = '_',
177                     user_args         	  = '_',
178                     reply_action     	  = '_',
179                     reply_data       	  = '_',
180		     threaded       	  = '_',
181		     strict_version   	  = '_',
182		     long_request_resend  = '_',
183		     call_proxy_gc_timeout = '_',
184		     cancel               = '_',
185		     resend_indication    = '_',
186		     segment_reply_ind 	  = '_',
187		     segment_recv_acc 	  = '_',
188		     segment_recv_timer	  = '_',
189		     segment_send   	  = '_',
190		     segment_send_timer	  = '_',
191		     max_pdu_size   	  = '_',
192		     request_keep_alive_timeout = '_'
193		    },
194    %% ok = io:format("PATTERN: ~p~n", [Pat]),
195    ets:match_object(megaco_local_conn, Pat);
196user_info(UserMid, connections) ->
197    [C#conn_data.conn_handle || C <- user_info(UserMid, conn_data)];
198user_info(UserMid, mid) ->
199    ets:lookup_element(megaco_config, {UserMid, mid}, 2);
200user_info(UserMid, orig_pending_limit) ->
201    user_info(UserMid, sent_pending_limit);
202user_info(UserMid, trans_id) ->
203    case (catch user_info(UserMid, trans_id_counter)) of
204	{'EXIT', _} ->
205	    %% There is only two cases where this can occure:
206	    %% 1) The user does not exist
207	    %% 2) Called before the first message is sent, use
208	    %%    undefined_serial, since there is no
209	    %%    "current transaction id"
210	    case (catch user_info(UserMid, mid)) of
211		{'EXIT', _} ->
212		    %% case 1:
213		    exit({no_such_user, UserMid});
214		_ ->
215		    undefined_serial
216	    end;
217	Else ->
218	    Else
219    end;
220user_info(UserMid, Item) ->
221    ets:lookup_element(megaco_config, {UserMid, Item}, 2).
222
223update_user_info(UserMid, orig_pending_limit, Val) ->
224    update_user_info(UserMid, sent_pending_limit, Val);
225update_user_info(UserMid, Item, Val) ->
226    call({update_user_info, UserMid, Item, Val}).
227
228
229conn_info(Data, Item) ->
230    %% The purpose of this is a compiler optimization...
231    %% Args are processed from left to right.
232    do_conn_info(Item, Data).
233
234do_conn_info(mid = _Item, #megaco_conn_handle{local_mid = Mid}) ->
235    Mid;
236do_conn_info(local_mid = _Item, #megaco_conn_handle{local_mid = LMid}) ->
237    LMid;
238do_conn_info(remote_mid = _Item, #megaco_conn_handle{remote_mid = RMid}) ->
239    RMid;
240do_conn_info(conn_handle = _Item, CH) when is_record(CH, megaco_conn_handle) ->
241    CH;
242do_conn_info(conn_data = _Item, CH) when is_record(CH, megaco_conn_handle) ->
243    case lookup_local_conn(CH) of
244	[] ->
245	    exit({no_such_connection, CH});
246	[ConnData] ->
247	    ConnData
248    end;
249do_conn_info(Item, CH) when is_record(CH, megaco_conn_handle) ->
250    case lookup_local_conn(CH) of
251	[] ->
252	    exit({no_such_connection, CH});
253	[ConnData] ->
254	    do_conn_info(Item, ConnData)
255    end;
256
257do_conn_info(cancel = _Item, #conn_data{conn_handle = CH}) ->
258    %% To minimise raise-condition propabillity,
259    %% we always look in the table instead of
260    %% in the record for this one
261    ets:lookup_element(megaco_local_conn, CH, #conn_data.cancel);
262do_conn_info(cancel = _Item, CH) when is_record(CH, megaco_conn_handle) ->
263    %% To minimise raise-condition propabillity,
264    %% we always look in the table instead of
265    %% in the record for this one
266    ets:lookup_element(megaco_local_conn, CH, #conn_data.cancel);
267
268do_conn_info(all = _Item,
269	     #conn_data{conn_handle                = CH,
270			serial                     = TransId,
271			max_serial                 = MaxTransId,
272			request_timer              = ReqTmr,
273			long_request_timer         = LongReqTmr,
274			auto_ack                   = AutoAck,
275			trans_ack                  = TransAck,
276			trans_ack_maxcount         = TransAckMaxCount,
277			trans_req                  = TransReq,
278			trans_req_maxcount         = TransReqMaxCount,
279			trans_req_maxsize          = TransReqMaxSz,
280			trans_timer                = TransTmr,
281			%% trans_sender,
282			pending_timer              = PendingTmr,
283			sent_pending_limit         = SentPendingLimit,
284			recv_pending_limit         = RecvPendingLimit,
285			reply_timer                = ReplyTmr,
286			control_pid                = CtrlPid,
287			monitor_ref                = MonRef,
288			send_mod                   = SendMod,
289			send_handle                = SendHandle,
290			encoding_mod               = EncodingMod,
291			encoding_config            = EncodingConf,
292			protocol_version           = ProtoVersion,
293			auth_data                  = AuthData,
294			user_mod                   = UserMod,
295			user_args                  = UserArgs,
296			reply_action               = ReplyAction,
297			reply_data                 = ReplyData,
298			threaded                   = Threaded,
299			strict_version             = StrictVersion,
300			long_request_resend        = LongReqResend,
301			call_proxy_gc_timeout      = CallProxyGCTimeout,
302			%% cancel,
303			resend_indication          = ResendInd,
304			segment_reply_ind          = SegReplyInd,
305			segment_recv_acc           = SegRecvAcc,
306			segment_recv_timer         = SegRecvTmr,
307			segment_send               = SegSend,
308			segment_send_timer         = SegSendTmr,
309			max_pdu_size               = MaxPduSz,
310			request_keep_alive_timeout = RequestKeepAliveTmr}) ->
311    [{conn_handle,                CH},
312     {trans_id,                   TransId},
313     {max_trans_id,               MaxTransId},
314     {request_timer,              ReqTmr},
315     {long_request_timer,         LongReqTmr},
316     {mid,                        CH#megaco_conn_handle.local_mid},
317     {local_mid,                  CH#megaco_conn_handle.local_mid},
318     {remote_mid,                 CH#megaco_conn_handle.remote_mid},
319     {auto_ack,                   AutoAck},
320     {trans_ack,                  TransAck},
321     {trans_ack_maxcount,         TransAckMaxCount},
322     {trans_req,                  TransReq},
323     {trans_req_maxcount,         TransReqMaxCount},
324     {trans_req_maxsize,          TransReqMaxSz},
325     {trans_timer,                TransTmr},
326     {pending_timer,              PendingTmr},
327     {sent_pending_limit,         SentPendingLimit},
328     {recv_pending_limit,         RecvPendingLimit},
329     {reply_timer,                ReplyTmr},
330     {control_pid,                CtrlPid},
331     {monitor_ref,                MonRef},
332     {send_mod,                   SendMod},
333     {send_handle,                SendHandle},
334     {encoding_mod,               EncodingMod},
335     {encoding_config,            EncodingConf},
336     {protocol_version,           ProtoVersion},
337     {auth_data,                  AuthData},
338     {user_mod,                   UserMod},
339     {user_args,                  UserArgs},
340     {reply_action,               ReplyAction},
341     {reply_data,                 ReplyData},
342     {threaded,                   Threaded},
343     {strict_version,             StrictVersion},
344     {long_request_resend,        LongReqResend},
345     {call_proxy_gc_timeout,      CallProxyGCTimeout},
346     {resend_indication,          ResendInd},
347     {segment_reply_ind,          SegReplyInd},
348     {segment_recv_acc,           SegRecvAcc},
349     {segment_recv_timer,         SegRecvTmr},
350     {segment_send,               SegSend},
351     {segment_send_timer,         SegSendTmr},
352     {max_pdu_size,               MaxPduSz},
353     {request_keep_alive_timeout, RequestKeepAliveTmr}];
354
355do_conn_info(conn_data = _Item, CD) ->
356    CD;
357do_conn_info(conn_handle = _Item, #conn_data{conn_handle = Val}) ->
358    Val;
359do_conn_info(mid = _Item,
360	     #conn_data{conn_handle = #megaco_conn_handle{local_mid = Val}}) ->
361    Val;
362do_conn_info(local_mid = _Item,
363	     #conn_data{conn_handle = #megaco_conn_handle{local_mid = Val}}) ->
364    Val;
365do_conn_info(remote_mid = _Item,
366	     #conn_data{conn_handle = #megaco_conn_handle{remote_mid = Val}}) ->
367    Val;
368do_conn_info(trans_id = _Item,
369	     #conn_data{conn_handle = #megaco_conn_handle{local_mid = LMid},
370			max_serial  = Max}) ->
371    Item2 = {LMid, trans_id_counter},
372    case (catch ets:lookup(megaco_config, Item2)) of
373	{'EXIT', _} ->
374	    undefined_serial;
375	[] ->
376	    user_info(LMid, min_trans_id);
377	[{_, Serial}] ->
378	    if
379		((Max =:= infinity) andalso
380		 is_integer(Serial) andalso
381		 (Serial < 4294967295)) ->
382		    Serial + 1;
383		((Max =:= infinity) andalso
384		 is_integer(Serial) andalso
385		 (Serial =:= 4294967295)) ->
386		    user_info(LMid, min_trans_id);
387		Serial < Max ->
388		    Serial  + 1;
389		Serial =:= Max ->
390		    user_info(LMid, min_trans_id);
391		Serial =:= 4294967295 ->
392		    user_info(LMid, min_trans_id);
393		true ->
394		    undefined_serial
395	    end
396    end;
397do_conn_info(max_trans_id = _Item, #conn_data{max_serial = Val}) ->
398    Val;
399do_conn_info(request_timer = _Item, #conn_data{request_timer = Val}) ->
400    Val;
401do_conn_info(long_request_timer = _Item, #conn_data{long_request_timer = Val}) ->
402    Val;
403do_conn_info(auto_ack = _Item, #conn_data{auto_ack = Val}) ->
404    Val;
405do_conn_info(trans_ack = _Item, #conn_data{trans_ack = Val}) ->
406    Val;
407do_conn_info(trans_ack_maxcount = _Item, #conn_data{trans_ack_maxcount = Val}) ->
408    Val;
409do_conn_info(trans_req = _Item, #conn_data{trans_req = Val}) ->
410    Val;
411do_conn_info(trans_req_maxcount = _Item, #conn_data{trans_req_maxcount = Val}) ->
412    Val;
413do_conn_info(trans_req_maxsize = _Item, #conn_data{trans_req_maxsize = Val}) ->
414    Val;
415do_conn_info(trans_timer = _Item, #conn_data{trans_timer = Val}) ->
416    Val;
417do_conn_info(pending_timer = _Item, #conn_data{pending_timer = Val}) ->
418    Val;
419do_conn_info(orig_pending_limit = _Item, #conn_data{sent_pending_limit = Val}) ->
420    Val;
421do_conn_info(sent_pending_limit = _Item, #conn_data{sent_pending_limit = Val}) ->
422    Val;
423do_conn_info(recv_pending_limit = _Item, #conn_data{recv_pending_limit = Val}) ->
424    Val;
425do_conn_info(reply_timer = _Item, #conn_data{reply_timer = Val}) ->
426    Val;
427do_conn_info(control_pid = _Item, #conn_data{control_pid = Val}) ->
428    Val;
429do_conn_info(send_mod = _Item, #conn_data{send_mod = Val}) ->
430    Val;
431do_conn_info(send_handle = _Item, #conn_data{send_handle = Val}) ->
432    Val;
433do_conn_info(encoding_mod = _Item, #conn_data{encoding_mod = Val}) ->
434    Val;
435do_conn_info(encoding_config = _Item, #conn_data{encoding_config = Val}) ->
436    Val;
437do_conn_info(protocol_version = _Item, #conn_data{protocol_version = Val}) ->
438    Val;
439do_conn_info(auth_data = _Item, #conn_data{auth_data = Val}) ->
440    Val;
441do_conn_info(user_mod = _Item, #conn_data{user_mod = Val}) ->
442    Val;
443do_conn_info(user_args = _Item, #conn_data{user_args = Val}) ->
444    Val;
445do_conn_info(reply_action = _Item, #conn_data{reply_action = Val}) ->
446    Val;
447do_conn_info(reply_data = _Item, #conn_data{reply_data = Val}) ->
448    Val;
449do_conn_info(threaded = _Item, #conn_data{threaded = Val}) ->
450    Val;
451do_conn_info(strict_version = _Item, #conn_data{strict_version = Val}) ->
452    Val;
453do_conn_info(long_request_resend = _Item,
454	     #conn_data{long_request_resend = Val}) ->
455    Val;
456do_conn_info(call_proxy_gc_timeout = _Item,
457	     #conn_data{call_proxy_gc_timeout = Val}) ->
458    Val;
459do_conn_info(resend_indication = _Item, #conn_data{resend_indication = Val}) ->
460    Val;
461do_conn_info(segment_reply_ind = _Item, #conn_data{segment_reply_ind = Val}) ->
462    Val;
463do_conn_info(segment_recv_acc = _Item, #conn_data{segment_recv_acc = Val}) ->
464    Val;
465do_conn_info(segment_recv_timer = _Item,
466	     #conn_data{segment_recv_timer = Val}) ->
467    Val;
468do_conn_info(segment_send = _Item, #conn_data{segment_send = Val}) ->
469    Val;
470do_conn_info(segment_send_timer = _Item,
471	     #conn_data{segment_send_timer = Val}) ->
472    Val;
473do_conn_info(max_pdu_size = _Item, #conn_data{max_pdu_size = Val}) ->
474    Val;
475do_conn_info(request_keep_alive_timeout = _Item,
476	     #conn_data{request_keep_alive_timeout = Val}) ->
477    Val;
478do_conn_info(receive_handle = _Item,
479	     #conn_data{conn_handle = #megaco_conn_handle{local_mid = LMid},
480			encoding_mod    = EM,
481			encoding_config = EC,
482			send_mod        = SM}) ->
483    #megaco_receive_handle{local_mid       = LMid,
484			   encoding_mod    = EM,
485			   encoding_config = EC,
486			   send_mod        = SM};
487do_conn_info(Item, Data)
488  when is_record(Data, conn_data) orelse is_record(Data, megaco_conn_handle) ->
489    exit({no_such_item, Item});
490do_conn_info(_Item, BadData) ->
491    {error, {no_such_connection, BadData}}.
492
493
494%% replace(_, _, []) ->
495%%     [];
496%% replace(Item, WithItem, [Item|List]) ->
497%%     [WithItem|List];
498%% replace(Item, WithItem, [OtherItem|List]) ->
499%%     [OtherItem | replace(Item, WithItem, List)].
500
501
502update_conn_info(#conn_data{conn_handle = CH}, Item, Val) ->
503    do_update_conn_info(CH, Item, Val);
504update_conn_info(CH, Item, Val)
505  when is_record(CH, megaco_conn_handle) andalso (Item /= cancel) ->
506    do_update_conn_info(CH, Item, Val);
507update_conn_info(BadHandle, _Item, _Val) ->
508    {error, {no_such_connection, BadHandle}}.
509
510do_update_conn_info(CH, orig_pending_limit, Val) ->
511    do_update_conn_info(CH, sent_pending_limit, Val);
512do_update_conn_info(CH, Item, Val) ->
513    call({update_conn_data, CH, Item, Val}).
514
515
516system_info(all) ->
517    AllItems = [n_active_requests,
518		n_active_replies,
519		n_active_connections,
520		users,
521		connections,
522		text_config,
523		reply_counters,
524		pending_counters],
525    [{Item, system_info(Item)} || Item <- AllItems];
526system_info(Item) ->
527    case Item of
528        n_active_requests ->
529            ets:info(megaco_requests, size);
530        n_active_replies  ->
531            ets:info(megaco_replies, size);
532        n_active_connections  ->
533            ets:info(megaco_local_conn, size);
534        users ->
535            Pat = {{'_', mid}, '_'},
536            [Mid || {_, Mid} <- ets:match_object(megaco_config, Pat)];
537        connections ->
538            [C#conn_data.conn_handle || C <- ets:tab2list(megaco_local_conn)];
539	text_config ->
540	    case ets:lookup(megaco_config, text_config) of
541		[] ->
542		    [];
543		[{text_config, Conf}] ->
544		    [Conf]
545	    end;
546
547	reply_counters ->
548	    reply_counters();
549
550	pending_counters ->
551	    pending_counters();
552
553	recv_pending_counters ->
554	    pending_counters(recv);
555
556	sent_pending_counters ->
557	    pending_counters(sent);
558
559	BadItem ->
560	    exit({no_such_item, BadItem})
561
562    end.
563
564
565get_env(Env, Default) ->
566    case application:get_env(megaco, Env) of
567        {ok, Val} -> Val;
568        undefined -> Default
569    end.
570
571lookup_local_conn(Handle) ->
572    ets:lookup(megaco_local_conn, Handle).
573
574
575autoconnect(RH, RemoteMid, SendHandle, ControlPid) ->
576    ?d("autoconnect -> entry with "
577	"~n   RH:         ~p"
578	"~n   RemoteMid:  ~p"
579	"~n   SendHandle: ~p"
580	"~n   ControlPid: ~p", [RH, RemoteMid, SendHandle, ControlPid]),
581    case RemoteMid of
582	{MidType, _MidValue} when is_atom(MidType) ->
583	    call({connect, RH, RemoteMid, SendHandle, ControlPid, auto});
584	preliminary_mid ->
585	    call({connect, RH, RemoteMid, SendHandle, ControlPid, auto});
586	BadMid ->
587	    {error, {bad_remote_mid, BadMid}}
588    end.
589
590connect(RH, RemoteMid, SendHandle, ControlPid) ->
591    ?d("connect -> entry with "
592	"~n   RH:         ~p"
593	"~n   RemoteMid:  ~p"
594	"~n   SendHandle: ~p"
595	"~n   ControlPid: ~p", [RH, RemoteMid, SendHandle, ControlPid]),
596    case RemoteMid of
597	{MidType, _MidValue} when is_atom(MidType) ->
598	    call({connect, RH, RemoteMid, SendHandle, ControlPid,
599		  {plain, self()}});
600	preliminary_mid ->
601	    call({connect, RH, RemoteMid, SendHandle, ControlPid,
602		  {plain, self()}});
603	BadMid ->
604	    {error, {bad_remote_mid, BadMid}}
605    end.
606
607finish_connect(ConnHandle, SendHandle, ControlPid, MFA) ->
608    ?d("finish_connect -> entry with "
609	"~n   ConnHandle: ~p"
610	"~n   SendHandle: ~p"
611	"~n   ControlPid: ~p"
612	"~n   MFA:        ~p", [ConnHandle, SendHandle, ControlPid, MFA]),
613    call({finish_connect, ConnHandle, SendHandle, ControlPid, MFA}).
614
615connect_remote(ConnHandle, UserNode, Ref) ->
616    call({connect_remote, ConnHandle, UserNode, Ref}).
617
618disconnect(ConnHandle) ->
619    call({disconnect, ConnHandle}).
620
621disconnect_remote(ConnHandle, UserNode) ->
622    call({disconnect_remote, ConnHandle, UserNode}).
623
624
625incr_counter(Item, Incr) ->
626    try
627	begin
628	    ets:update_counter(megaco_config, Item, Incr)
629	end
630    catch
631	error:_ ->
632	    %% Counter does not exist, so try creat it
633	    try
634		begin
635		    cre_counter(Item, Incr)
636		end
637	    catch
638		exit:_ ->
639		    %% This is a raise condition.
640		    %% When we tried to update the counter above, it
641		    %% did not exist, but now it does...
642		    ets:update_counter(megaco_config, Item, Incr)
643	    end
644    end.
645
646cre_counter(Item, Initial) ->
647    case whereis(?SERVER) =:= self() of
648	false ->
649	    case call({cre_counter, Item, Initial}) of
650		{ok, Value} ->
651		    Value;
652		{error, Reason} ->
653		    exit({failed_creating_counter, Item, Initial, Reason})
654	    end;
655	true ->
656	    %% Check that the counter does not already exists
657	    %% so we don't overwrite an already existing counter
658	    case ets:lookup(megaco_config, Item) of
659		[] ->
660		    ets:insert(megaco_config, {Item, Initial}),
661		    {ok, Initial};
662		[_] ->
663		    %% Possibly a raise condition
664		    {error, already_exists}
665
666		end
667    end.
668
669
670cre_reply_counter(ConnHandle, TransId) ->
671    Counter = {reply_counter, ConnHandle, TransId},
672    Initial = 1,
673    cre_counter(Counter, Initial).
674
675incr_reply_counter(ConnHandle, TransId) ->
676    Counter = {reply_counter, ConnHandle, TransId},
677    incr_counter(Counter, 1).
678
679get_reply_counter(ConnHandle, TransId) ->
680    Counter = {reply_counter, ConnHandle, TransId},
681    [{Counter, Val}] = ets:lookup(megaco_config, Counter),
682    Val.
683
684del_reply_counter(ConnHandle, TransId) ->
685    Counter = {reply_counter, ConnHandle, TransId},
686    ets:delete(megaco_config, Counter).
687
688reply_counters() ->
689    Pattern   = {{reply_counter, '_', '_'}, '_'},
690    Counters1 = ets:match_object(megaco_config, Pattern),
691    [{ConnHandle, TransId, CounterVal} ||
692	{{reply_counter, ConnHandle, TransId}, CounterVal} <- Counters1].
693
694
695cre_pending_counter(TransId) ->
696    cre_pending_counter(sent, TransId, 0).
697
698cre_pending_counter(Direction, TransId, Initial) ->
699    Counter = {pending_counter, Direction, TransId},
700    cre_counter(Counter, Initial).
701
702incr_pending_counter(TransId) ->
703    incr_pending_counter(sent, TransId).
704
705incr_pending_counter(Direction, TransId) ->
706    Counter = {pending_counter, Direction, TransId},
707    incr_counter(Counter, 1).
708
709get_pending_counter(TransId) ->
710    get_pending_counter(sent, TransId).
711
712get_pending_counter(Direction, TransId) ->
713    Counter = {pending_counter, Direction, TransId},
714    [{Counter, Val}] = ets:lookup(megaco_config, Counter),
715    Val.
716
717del_pending_counter(TransId) ->
718    del_pending_counter(sent, TransId).
719
720del_pending_counter(Direction, TransId) ->
721    Counter = {pending_counter, Direction, TransId},
722    ets:delete(megaco_config, Counter).
723
724
725pending_counters() ->
726    Pattern   = {{pending_counter, '_', '_'}, '_'},
727    Counters1 = ets:match_object(megaco_config, Pattern),
728    Counters2 = [{Direction, TransId, CounterVal} ||
729		    {{pending_counter, Direction, TransId}, CounterVal} <-
730			Counters1],
731    RecvCounters = [{TransId, CounterVal} ||
732		       {recv, TransId, CounterVal} <- Counters2],
733    SentCounters = [{TransId, CounterVal} ||
734		       {sent, TransId, CounterVal} <- Counters2],
735    [{recv, RecvCounters}, {sent, SentCounters}].
736
737
738pending_counters(Direction)
739  when ((Direction =:= sent) orelse (Direction =:= recv)) ->
740    Pattern  = {{pending_counter, Direction, '_'}, '_'},
741    Counters = ets:match_object(megaco_config, Pattern),
742    [{TransId, CounterVal} ||
743	{{pending_counter, D, TransId}, CounterVal} <-
744	    Counters, (Direction == D)].
745
746%% A wrapping transaction id counter
747incr_trans_id_counter(ConnHandle) ->
748    incr_trans_id_counter(ConnHandle, 1).
749incr_trans_id_counter(ConnHandle, Incr)
750  when is_integer(Incr) andalso (Incr > 0) ->
751    case megaco_config:lookup_local_conn(ConnHandle) of
752        [] ->
753            {error, {no_such_connection, ConnHandle}};
754        [ConnData] ->
755            LocalMid = ConnHandle#megaco_conn_handle.local_mid,
756	    Min      = user_info(LocalMid, min_trans_id),
757	    Max      =
758		case ConnData#conn_data.max_serial of
759		    infinity ->
760			4294967295;
761		    MS ->
762			MS
763		end,
764	    Item     = ?TID_CNT(LocalMid),
765	    do_incr_trans_id_counter(ConnData, Item, Min, Max, Incr, -1)
766    end.
767
768do_incr_trans_id_counter(ConnData, _Item, _Min, _Max, 0, Serial) ->
769    ConnData2 = ConnData#conn_data{serial = Serial},
770    {ok, ConnData2};
771do_incr_trans_id_counter(ConnData, Item, Min, Max, N, _) ->
772    case (catch ets:update_counter(megaco_config, Item, {2, 1, Max, Min})) of
773	{'EXIT', _} ->
774	    %% This can only happen for the first ever increment,
775	    %% in which case N is equal to (the initial) Incr
776	    ConnHandle = ConnData#conn_data.conn_handle,
777	    init_trans_id_counter(ConnHandle, Item, N);
778	Serial ->
779	    do_incr_trans_id_counter(ConnData, Item, Min, Max, N-1, Serial)
780    end.
781
782init_trans_id_counter(ConnHandle, Item, Incr) ->
783    case whereis(?SERVER) == self() of
784        false ->
785	    call({init_trans_id_counter, ConnHandle, Item, Incr});
786	true ->
787	    do_init_trans_id_counter(ConnHandle, Item, Incr)
788    end.
789
790do_init_trans_id_counter(ConnHandle, Item, Incr) ->
791    case megaco_config:lookup_local_conn(ConnHandle) of
792	[] ->
793	    {error, {no_such_connection, ConnHandle}};
794	[ConnData] ->
795	    %% Make sure that the counter still does not exist
796	    LocalMid = ConnHandle#megaco_conn_handle.local_mid,
797	    Min      = user_info(LocalMid, min_trans_id),
798	    Max      =
799		case ConnData#conn_data.max_serial of
800		    infinity ->
801			4294967295;
802		    MS ->
803			MS
804		end,
805	    Item     = ?TID_CNT(LocalMid),
806	    Incr2    = {2, Incr, Max, Min},
807	    case (catch ets:update_counter(megaco_config, Item, Incr2)) of
808		{'EXIT', _} ->
809		    %% Yep, we are the first one here
810		    Serial1 = Min + (Incr-1),
811		    ets:insert(megaco_config, {Item, Serial1}),
812		    ConnData2 = ConnData#conn_data{serial = Serial1},
813		    {ok, ConnData2};
814		Serial2 ->
815		    %% No, someone got there before we did
816		    ConnData2 = ConnData#conn_data{serial = Serial2},
817		    {ok, ConnData2}
818	    end
819    end.
820
821%% For backward compatibillity (during code upgrade)
822reset_trans_id_counter(ConnHandle, Item, Serial) ->
823    LocalMid = ConnHandle#megaco_conn_handle.local_mid,
824    case megaco_config:lookup_local_conn(ConnHandle) of
825        [] ->
826            {error, {no_such_connection, ConnHandle}};
827        [ConnData] ->
828	    do_reset_trans_id_counter(ConnData, LocalMid,
829				      Item, Serial)
830    end.
831
832do_reset_trans_id_counter(ConnData, LocalMid, Item, Serial)
833  when is_integer(Serial) ->
834    Max = ConnData#conn_data.max_serial,
835    Overflow =
836	if
837	    (Max == infinity) ->
838		Serial - 4294967295;
839
840	    is_integer(Max) ->
841		Serial - Max
842	end,
843    NewSerial = user_info(LocalMid, min_trans_id) + (Overflow-1),
844    ConnData2 = ConnData#conn_data{serial = NewSerial},
845    ets:insert(megaco_config, {Item, NewSerial}),
846    {ok, ConnData2}.
847
848
849trans_sender_exit(Reason, CH) ->
850    ?d("trans_sender_exit -> entry with"
851	"~n   Reason: ~p"
852	"~n   CH: ~p", [Reason, CH]),
853    cast({trans_sender_exit, Reason, CH}).
854
855
856call(Request) ->
857    case (catch gen_server:call(?SERVER, Request, infinity)) of
858	{'EXIT', _} ->
859	    {error, megaco_not_started};
860	Res ->
861	    Res
862    end.
863
864
865cast(Msg) ->
866    case (catch gen_server:cast(?SERVER, Msg)) of
867	{'EXIT', _} ->
868	    {error, megaco_not_started};
869	Res ->
870	    Res
871    end.
872
873
874%%%----------------------------------------------------------------------
875%%% Callback functions from gen_server
876%%%----------------------------------------------------------------------
877
878%%----------------------------------------------------------------------
879%% Func: init/1
880%% Returns: {ok, State}          |
881%%          {ok, State, Timeout} |
882%%          ignore               |
883%%          {stop, Reason}
884%%----------------------------------------------------------------------
885
886init([Parent]) ->
887    ?d("init -> entry with "
888	"~n   Parent: ~p", [Parent]),
889    process_flag(trap_exit, true),
890    case (catch do_init()) of
891	ok ->
892	    ?d("init -> init ok", []),
893	    {ok, #state{parent_pid = Parent}};
894	Else ->
895	    ?d("init -> init error: "
896	       "~n   ~p", [Else]),
897	    {stop, Else}
898    end.
899
900do_init() ->
901    ?megaco_test_init(),
902    ets:new(megaco_config,      [public, named_table, {keypos, 1}]),
903    ets:new(megaco_local_conn,  [public, named_table, {keypos, 2}]),
904    ets:new(megaco_remote_conn, [public, named_table, {keypos, 2}, bag]),
905    megaco_stats:init(megaco_stats, global_snmp_counters()),
906    init_scanner(),
907    init_user_defaults(),
908    init_users().
909
910
911
912init_scanner() ->
913    case get_env(scanner, undefined) of
914	undefined ->
915	    Key  = text_config,
916	    Data = [],
917	    ets:insert(megaco_config, {Key, Data});
918	flex ->
919	    start_scanner(megaco_flex_scanner_handler,
920			  start_link, [], [gen_server]);
921	{flex, Opts} when is_list(Opts) -> % For future use
922	    start_scanner(megaco_flex_scanner_handler,
923			  start_link, [Opts], [gen_server]);
924	{M, F, A, Mods} when is_atom(M) andalso
925			     is_atom(F) andalso
926			     is_list(A) andalso
927			     is_list(Mods) ->
928	    start_scanner(M, F, A, Mods)
929    end.
930
931start_scanner(M, F, A, Mods) ->
932    case megaco_misc_sup:start_permanent_worker(M, F, A, Mods) of
933	{ok, Pid, Conf} when is_pid(Pid) ->
934	    Key  = text_config,
935	    Data = [Conf],
936	    ets:insert(megaco_config, {Key, Data});
937	Else ->
938	    throw({scanner_start_failed, Else})
939    end.
940
941init_user_defaults() ->
942    init_user_default(min_trans_id,         1),
943    init_user_default(max_trans_id,         infinity),
944    init_user_default(request_timer,        #megaco_incr_timer{}),
945    init_user_default(long_request_timer,   timer:seconds(60)),
946
947    init_user_default(auto_ack,             false),
948
949    init_user_default(trans_ack,            false),
950    init_user_default(trans_ack_maxcount,   10),
951
952    init_user_default(trans_req,            false),
953    init_user_default(trans_req_maxcount,   10),
954    init_user_default(trans_req_maxsize,    2048),
955
956    init_user_default(trans_timer,          0),
957    init_user_default(trans_sender,         undefined),
958
959    init_user_default(pending_timer,        timer:seconds(30)),
960    init_user_default(sent_pending_limit,   infinity),
961    init_user_default(recv_pending_limit,   infinity),
962    init_user_default(reply_timer,          timer:seconds(30)),
963    init_user_default(send_mod,             megaco_tcp),
964    init_user_default(encoding_mod,         megaco_pretty_text_encoder),
965    init_user_default(protocol_version,     1),
966    init_user_default(auth_data,            asn1_NOVALUE),
967    init_user_default(encoding_config,      []),
968    init_user_default(user_mod,             megaco_user_default),
969    init_user_default(user_args,            []),
970    init_user_default(reply_data,           undefined),
971    init_user_default(threaded,             false),
972    init_user_default(strict_version,       true),
973    init_user_default(long_request_resend,  false),
974    init_user_default(call_proxy_gc_timeout, timer:seconds(5)),
975    init_user_default(cancel,               false),
976    init_user_default(resend_indication,    false),
977    init_user_default(segment_reply_ind,    false),
978    init_user_default(segment_recv_acc,     false),
979    init_user_default(segment_recv_timer,   timer:seconds(10)),
980    init_user_default(segment_send,         none),
981    init_user_default(segment_send_timer,   timer:seconds(5)),
982    init_user_default(max_pdu_size,         infinity),
983    init_user_default(request_keep_alive_timeout, plain).
984
985init_user_default(Item, Default) when Item /= mid ->
986    Val = get_env(Item, Default),
987    case do_update_user(default, Item, Val) of
988	ok ->
989	    ok;
990	{error, Reason} ->
991	    throw(Reason)
992    end.
993
994init_users() ->
995    Users = get_env(users, []),
996    init_users(Users).
997
998init_users([]) ->
999    ok;
1000init_users([{UserMid, Config} | Rest]) ->
1001    case handle_start_user(UserMid, Config) of
1002        ok ->
1003            init_users(Rest);
1004        Else ->
1005            throw({bad_user, UserMid, Else})
1006    end;
1007init_users(BadConfig) ->
1008    throw({bad_config, users, BadConfig}).
1009
1010%%----------------------------------------------------------------------
1011%% Func: handle_call/3
1012%% Returns: {reply, Reply, State}          |
1013%%          {reply, Reply, State, Timeout} |
1014%%          {noreply, State}               |
1015%%          {noreply, State, Timeout}      |
1016%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
1017%%          {stop, Reason, State}            (terminate/2 is called)
1018%%----------------------------------------------------------------------
1019
1020handle_call({cre_counter, Item, Incr}, _From, S) ->
1021    Reply = cre_counter(Item, Incr),
1022    {reply, Reply, S};
1023
1024handle_call({del_counter, Item, Incr}, _From, S) ->
1025    Reply = cre_counter(Item, Incr),
1026    {reply, Reply, S};
1027
1028%% For backward compatibillity (code upgrade)
1029handle_call({incr_trans_id_counter, ConnHandle}, _From, S) ->
1030    Reply = incr_trans_id_counter(ConnHandle),
1031    {reply, Reply, S};
1032
1033handle_call({init_trans_id_counter, ConnHandle, Item, Incr}, _From, S) ->
1034    Reply = do_init_trans_id_counter(ConnHandle, Item, Incr),
1035    {reply, Reply, S};
1036
1037%% For backward compatibillity (code upgrade)
1038handle_call({reset_trans_id_counter, ConnHandle, Item, Serial}, _From, S) ->
1039    Reply = reset_trans_id_counter(ConnHandle, Item, Serial),
1040    {reply, Reply, S};
1041
1042handle_call({receive_handle, UserMid}, _From, S) ->
1043    case catch make_receive_handle(UserMid) of
1044	{'EXIT', _} ->
1045	    {reply, {error, {no_receive_handle, UserMid}}, S};
1046	RH ->
1047	    {reply, {ok, RH}, S}
1048    end;
1049handle_call({connect, RH, RemoteMid, SendHandle, ControlPid}, _From, S) ->
1050    Reply = handle_connect(RH, RemoteMid, SendHandle, ControlPid, auto),
1051    {reply, Reply, S};
1052handle_call({connect, RH, RemoteMid, SendHandle, ControlPid, Auto}, _From, S) ->
1053    Reply = handle_connect(RH, RemoteMid, SendHandle, ControlPid, Auto),
1054    {reply, Reply, S};
1055
1056handle_call({finish_connect, ConnHandle, SendHandle, ControlPid, MFA},
1057	    _From, S) ->
1058    Reply = handle_finish_connect(ConnHandle, SendHandle, ControlPid, MFA),
1059    {reply, Reply, S};
1060
1061handle_call({connect_remote, CH, UserNode, Ref}, _From, S) ->
1062    Reply = handle_connect_remote(CH, UserNode, Ref),
1063    {reply, Reply, S};
1064
1065handle_call({disconnect, ConnHandle}, _From, S) ->
1066    Reply = handle_disconnect(ConnHandle),
1067    {reply, Reply, S};
1068handle_call({disconnect_remote, CH, UserNode}, _From, S) ->
1069    Reply = handle_disconnect_remote(CH, UserNode),
1070    {reply, Reply, S};
1071
1072handle_call({start_user, UserMid, Config}, _From, S) ->
1073    Reply = handle_start_user(UserMid, Config),
1074    {reply, Reply, S};
1075handle_call({stop_user, UserMid}, _From, S) ->
1076    Reply = handle_stop_user(UserMid),
1077    {reply, Reply, S};
1078handle_call({update_conn_data, CH, Item, Val}, _From, S) ->
1079    case lookup_local_conn(CH) of
1080        [] ->
1081            {reply, {error, {no_such_connection, CH}}, S};
1082        [CD] ->
1083            Reply = handle_update_conn_data(CD, Item, Val),
1084            {reply, Reply, S}
1085    end;
1086handle_call({update_user_info, UserMid, Item, Val}, _From, S) ->
1087    case catch user_info(UserMid, mid) of
1088        {'EXIT', _} ->
1089            {reply, {error, {no_such_user, UserMid}}, S};
1090        _ ->
1091            Reply = do_update_user(UserMid, Item, Val),
1092            {reply, Reply, S}
1093    end;
1094
1095handle_call({stop, ParentPid}, _From, #state{parent_pid = ParentPid} = S) ->
1096    Reason = normal,
1097    Reply  = ok,
1098    {stop, Reason, Reply, S};
1099
1100handle_call(Req, From, S) ->
1101    warning_msg("received unexpected request from ~p: "
1102		"~n~w", [From, Req]),
1103    {reply, {error, {bad_request, Req}}, S}.
1104
1105
1106%%----------------------------------------------------------------------
1107%% Func: handle_cast/2
1108%% Returns: {noreply, State}          |
1109%%          {noreply, State, Timeout} |
1110%%          {stop, Reason, State}            (terminate/2 is called)
1111%%----------------------------------------------------------------------
1112
1113handle_cast({trans_sender_exit, Reason, CH}, S) ->
1114    warning_msg("transaction sender [~p] restarting: "
1115		"~n~p", [CH, Reason]),
1116    case lookup_local_conn(CH) of
1117	[] ->
1118	    error_msg("connection data not found for ~p~n"
1119		      "when restarting transaction sender", [CH]);
1120	[CD] ->
1121	    CD2 = trans_sender_start(CD#conn_data{trans_sender = undefined}),
1122	    ets:insert(megaco_local_conn, CD2)
1123    end,
1124    {noreply, S};
1125
1126handle_cast(Msg, S) ->
1127    warning_msg("received unexpected message: "
1128		"~n~w", [Msg]),
1129    {noreply, S}.
1130
1131
1132
1133%%----------------------------------------------------------------------
1134%% Func: handle_info/2
1135%% Returns: {noreply, State}          |
1136%%          {noreply, State, Timeout} |
1137%%          {stop, Reason, State}            (terminate/2 is called)
1138%%----------------------------------------------------------------------
1139
1140handle_info({'EXIT', Pid, Reason}, S) when Pid =:= S#state.parent_pid ->
1141    {stop, Reason, S};
1142
1143handle_info(Info, S) ->
1144    warning_msg("received unknown info: "
1145		"~n~w", [Info]),
1146    {noreply, S}.
1147
1148
1149
1150%%----------------------------------------------------------------------
1151%% Func: terminate/2
1152%% Purpose: Shutdown the server
1153%% Returns: any (ignored by gen_server)
1154%%----------------------------------------------------------------------
1155
1156terminate(_Reason, _State) ->
1157    ok.
1158
1159
1160%%----------------------------------------------------------------------
1161%% Func: code_change/3
1162%% Purpose: Convert process state when code is changed
1163%% Returns: {ok, NewState}
1164%%----------------------------------------------------------------------
1165
1166code_change(_Vsn, S, upgrade_from_pre_3_12) ->
1167    upgrade_user_info_from_pre_3_12(),
1168    upgrade_conn_data_from_pre_3_12(),
1169    {ok, S};
1170
1171code_change(_Vsn, S, downgrade_to_pre_3_12) ->
1172    downgrade_user_info_to_pre_3_12(),
1173    downgrade_conn_data_to_pre_3_12(),
1174    {ok, S};
1175
1176code_change(_Vsn, S, _Extra) ->
1177    {ok, S}.
1178
1179
1180%% -- Upgrade user info --
1181
1182upgrade_user_info_from_pre_3_12() ->
1183    NewValues = [{request_keep_alive_timeout, plain}],
1184    upgrade_user_info(NewValues).
1185
1186%% upgrade_user_info_from_pre_3_7() ->
1187%%     NewValues = [{segment_reply_ind,  false},
1188%% 		 {segment_recv_acc,   false},
1189%% 		 {segment_recv_timer, #megaco_incr_timer{}},
1190%% 		 {segment_send,       none},
1191%% 		 {segment_send_timer, infinity},
1192%% 		 {max_pdu_size,       infinity}],
1193%%     upgrade_user_info(NewValues).
1194
1195upgrade_user_info(NewValues) ->
1196    Users = [default|system_info(users)],
1197    F = fun({Item, Val}) ->
1198		upgrade_user_info(Users, Item, Val)
1199	end,
1200    lists:foreach(F, NewValues),
1201    ok.
1202
1203upgrade_user_info(Users, Item, Val) ->
1204    F = fun(User) -> do_update_user(User, Item, Val) end,
1205    lists:foreach(F, Users),
1206    ok.
1207
1208
1209%% %% -- Downgrade user info --
1210
1211downgrade_user_info_to_pre_3_12() ->
1212    NewItems = [
1213		request_keep_alive_timeout
1214	       ],
1215    downgrade_user_info(NewItems).
1216
1217%% downgrade_user_info_to_pre_3_7() ->
1218%%     NewItems = [
1219%% 		segment_reply_ind,
1220%% 		segment_recv_acc,
1221%% 		segment_recv_timer,
1222%% 		segment_send,
1223%% 		segment_send_timer,
1224%% 		max_pdu_size
1225%% 	       ],
1226%%     downgrade_user_info(NewItems).
1227
1228downgrade_user_info(NewItems) ->
1229    Users = [default|system_info(users)],
1230    F = fun(Item) ->
1231		downgrade_user_info(Users, Item)
1232	end,
1233    lists:foreach(F, NewItems),
1234    ok.
1235
1236downgrade_user_info(Users, Item) ->
1237    F = fun(User) -> do_downgrade_user_info(User, Item) end,
1238    lists:foreach(F, Users),
1239    ok.
1240
1241do_downgrade_user_info(User, Item) ->
1242    ets:delete(megaco_config, {User, Item}).
1243
1244
1245%% %% -- Upgrade conn data --
1246
1247upgrade_conn_data_from_pre_3_12() ->
1248    Conns    = system_info(connections),
1249    Defaults = [{request_keep_alive_timeout, plain}],
1250    upgrade_conn_data(Conns, Defaults).
1251
1252%% upgrade_conn_data_from_pre_3_7() ->
1253%%     Conns    = system_info(connections),
1254%%     Defaults = [{segment_reply_ind,  false},
1255%% 		{segment_recv_acc,   false},
1256%% 		{segment_recv_timer, #megaco_incr_timer{}},
1257%% 		{segment_send,       false},
1258%% 		{segment_send_timer, #megaco_incr_timer{}},
1259%% 		{max_pdu_size,       infinity}],
1260%%     upgrade_conn_data(Conns, Defaults).
1261
1262upgrade_conn_data(Conns, Defaults) ->
1263    F = fun(CH) ->
1264		case lookup_local_conn(CH) of
1265		    [] ->
1266			ok;
1267		    [CD] ->
1268			do_upgrade_conn_data(CD, Defaults)
1269		end
1270	end,
1271    lists:foreach(F, Conns),
1272    ok.
1273
1274do_upgrade_conn_data(OldStyleCD, Defaults) ->
1275    NewStyleCD = new_conn_data(OldStyleCD, Defaults),
1276    ets:insert(megaco_local_conn, NewStyleCD).
1277
1278%% Pre 3.12
1279new_conn_data({conn_data, CH, Serial, MaxSerial, ReqTmr, LongReqTmr,
1280	       AutoAck,
1281	       TransAck, TransAckMaxCnt,
1282	       TransReq, TransReqMaxCnt, TransReqMaxSz,
1283	       TransTmr, TransSndr,
1284
1285	       PendingTmr,
1286	       SentPendingLimit,
1287	       RecvPendingLimit,
1288	       ReplyTmr, CtrPid, MonRef,
1289	       Sendmod, SendHandle,
1290	       EncodeMod, EncodeConf,
1291	       ProtV, AuthData,
1292	       UserMod, UserArgs, ReplyAction, ReplyData,
1293	       Threaded,
1294	       StrictVersion,
1295	       LongReqResend,
1296	       Cancel,
1297	       ResendIndication,
1298	       SegmentReplyInd,
1299	       SegmentRecvAcc,
1300	       SegmentRecvTimer,
1301	       SegmentSend,
1302	       SegmentSendTimer,
1303	       MaxPDUSize
1304	       %% RequestKeepAliveTimerDefault - New values
1305	      },
1306	      Defaults) ->
1307    #conn_data{conn_handle          = CH,
1308	       serial               = Serial,
1309	       max_serial           = MaxSerial,
1310	       request_timer        = ReqTmr,
1311	       long_request_timer   = LongReqTmr,
1312
1313	       auto_ack             = AutoAck,
1314
1315	       trans_ack            = TransAck,
1316	       trans_ack_maxcount   = TransAckMaxCnt,
1317
1318	       trans_req            = TransReq,
1319	       trans_req_maxcount   = TransReqMaxCnt,
1320	       trans_req_maxsize    = TransReqMaxSz,
1321
1322	       trans_timer          = TransTmr,
1323	       trans_sender         = TransSndr,
1324
1325	       pending_timer        = PendingTmr,
1326	       sent_pending_limit   = SentPendingLimit,
1327	       recv_pending_limit   = RecvPendingLimit,
1328
1329	       reply_timer          = ReplyTmr,
1330	       control_pid          = CtrPid,
1331	       monitor_ref          = MonRef,
1332	       send_mod             = Sendmod,
1333	       send_handle          = SendHandle,
1334	       encoding_mod         = EncodeMod,
1335	       encoding_config      = EncodeConf,
1336	       protocol_version     = ProtV,
1337	       auth_data            = AuthData,
1338	       user_mod             = UserMod,
1339	       user_args            = UserArgs,
1340	       reply_action         = ReplyAction,
1341	       reply_data           = ReplyData,
1342	       threaded             = Threaded,
1343	       strict_version       = StrictVersion,
1344	       long_request_resend  = LongReqResend,
1345	       cancel               = Cancel,
1346	       resend_indication    = ResendIndication,
1347	       segment_reply_ind    = SegmentReplyInd,
1348	       segment_recv_acc     = SegmentRecvAcc,
1349	       segment_recv_timer   = SegmentRecvTimer,
1350	       segment_send         = SegmentSend,
1351	       segment_send_timer   = SegmentSendTimer,
1352	       max_pdu_size         = MaxPDUSize,
1353	       request_keep_alive_timeout = get_default(request_keep_alive_timeout, Defaults)
1354	      }.
1355
1356
1357get_default(Key, Defaults) ->
1358    {value, {Key, Default}} = lists:keysearch(Key, 1, Defaults),
1359    Default.
1360
1361
1362%% %% -- Downgrade conn data --
1363
1364downgrade_conn_data_to_pre_3_12() ->
1365    Conns = system_info(connections),
1366    Downgrade = fun(NewCD) -> old_conn_data_to_pre_3_12(NewCD) end,
1367    downgrade_conn_data(Downgrade, Conns).
1368
1369downgrade_conn_data(Downgrade, Conns) ->
1370    F = fun(CH) ->
1371		case lookup_local_conn(CH) of
1372		    [] ->
1373			ok;
1374		    [CD] ->
1375			do_downgrade_conn_data(Downgrade, CD)
1376		end
1377	end,
1378    lists:foreach(F, Conns).
1379
1380do_downgrade_conn_data(Downgrade, NewStyleCD) ->
1381    OldStyleCD = Downgrade(NewStyleCD),
1382    ets:insert(megaco_local_conn, OldStyleCD).
1383
1384old_conn_data_to_pre_3_12(
1385  #conn_data{conn_handle          = CH,
1386	     serial               = Serial,
1387	     max_serial           = MaxSerial,
1388	     request_timer        = ReqTmr,
1389	     long_request_timer   = LongReqTmr,
1390
1391	     auto_ack             = AutoAck,
1392
1393	     trans_ack            = TransAck,
1394	     trans_ack_maxcount   = TransAckMaxCnt,
1395
1396	     trans_req            = TransReq,
1397	     trans_req_maxcount   = TransReqMaxCnt,
1398	     trans_req_maxsize    = TransReqMaxSz,
1399
1400	     trans_timer          = TransTmr,
1401	     trans_sender         = TransSndr,
1402
1403	     pending_timer        = PendingTmr,
1404	     sent_pending_limit   = SentPendingLimit,
1405	     recv_pending_limit   = RecvPendingLimit,
1406
1407	     reply_timer          = ReplyTmr,
1408	     control_pid          = CtrPid,
1409	     monitor_ref          = MonRef,
1410	     send_mod             = Sendmod,
1411	     send_handle          = SendHandle,
1412	     encoding_mod         = EncodeMod,
1413	     encoding_config      = EncodeConf,
1414	     protocol_version     = ProtV,
1415	     auth_data            = AuthData,
1416	     user_mod             = UserMod,
1417	     user_args            = UserArgs,
1418	     reply_action         = ReplyAction,
1419	     reply_data           = ReplyData,
1420	     threaded             = Threaded,
1421	     strict_version       = StrictVersion,
1422	     long_request_resend  = LongReqResend,
1423	     cancel               = Cancel,
1424	     resend_indication    = ResendIndication,
1425	     segment_reply_ind    = SegmentRecvAcc,
1426	     segment_recv_acc     = SegmentRecvAcc,
1427	     segment_recv_timer   = SegmentRecvTimer,
1428	     segment_send         = SegmentSend,
1429	     segment_send_timer   = SegmentSendTimer,
1430	     max_pdu_size         = MaxPDUSize
1431	     %% request_keep_alive_timeout = RequestKeepAliveTimeout
1432	    }) ->
1433    {conn_data, CH, Serial, MaxSerial, ReqTmr, LongReqTmr,
1434     AutoAck,
1435     TransAck, TransAckMaxCnt,
1436     TransReq, TransReqMaxCnt, TransReqMaxSz,
1437     TransTmr, TransSndr,
1438     PendingTmr,
1439     SentPendingLimit,
1440     RecvPendingLimit,
1441     ReplyTmr, CtrPid, MonRef,
1442     Sendmod, SendHandle,
1443     EncodeMod, EncodeConf,
1444     ProtV, AuthData,
1445     UserMod, UserArgs, ReplyAction, ReplyData,
1446     Threaded,
1447     StrictVersion,
1448     LongReqResend,
1449     Cancel,
1450     ResendIndication,
1451     SegmentRecvAcc,
1452     SegmentRecvAcc,
1453     SegmentRecvTimer,
1454     SegmentSend,
1455     SegmentSendTimer,
1456     MaxPDUSize}.
1457
1458
1459
1460%%%----------------------------------------------------------------------
1461%%% Internal functions
1462%%%----------------------------------------------------------------------
1463
1464handle_start_user(default, _Config) ->
1465    {error, bad_user_mid};
1466handle_start_user(Mid, Config) ->
1467    case catch user_info(Mid, mid) of
1468        {'EXIT', _} ->
1469	    DefaultConfig = user_info(default, all),
1470            do_handle_start_user(Mid, DefaultConfig),
1471	    do_handle_start_user(Mid, Config);
1472        _LocalMid ->
1473            {error, {user_already_exists, Mid}}
1474    end.
1475
1476do_handle_start_user(UserMid, [{Item, Val} | Rest]) ->
1477    case do_update_user(UserMid, Item, Val) of
1478        ok ->
1479            do_handle_start_user(UserMid, Rest);
1480        {error, Reason} ->
1481            ets:match_delete(megaco_config, {{UserMid, '_'}, '_'}),
1482            {error, Reason}
1483    end;
1484do_handle_start_user(UserMid, []) ->
1485    do_update_user(UserMid, mid, UserMid),
1486    ok;
1487do_handle_start_user(UserMid, BadConfig) ->
1488    ets:match_delete(megaco_config, {{UserMid, '_'}, '_'}),
1489    {error, {bad_user_config, UserMid, BadConfig}}.
1490
1491do_update_user(UserMid, Item, Val) ->
1492    case verify_val(Item, Val) of
1493        true  ->
1494            ets:insert(megaco_config, {{UserMid, Item}, Val}),
1495            ok;
1496        false ->
1497            {error, {bad_user_val, UserMid, Item, Val}}
1498    end.
1499
1500verify_val(Item, Val) ->
1501    case Item of
1502        mid                    -> true;
1503        local_mid              -> true;
1504        remote_mid             -> true;
1505        min_trans_id           ->
1506	    megaco_config_misc:verify_strict_uint(Val, 4294967295); % uint32
1507        max_trans_id           ->
1508	    megaco_config_misc:verify_uint(Val, 4294967295);        % uint32
1509        request_timer          -> verify_timer(Val);
1510        long_request_timer     -> verify_timer(Val);
1511
1512        auto_ack               ->
1513	    megaco_config_misc:verify_bool(Val);
1514
1515	trans_ack              ->
1516	    megaco_config_misc:verify_bool(Val);
1517        trans_ack_maxcount     ->
1518	    megaco_config_misc:verify_uint(Val);
1519
1520	trans_req              ->
1521	    megaco_config_misc:verify_bool(Val);
1522        trans_req_maxcount     ->
1523	    megaco_config_misc:verify_uint(Val);
1524        trans_req_maxsize      ->
1525	    megaco_config_misc:verify_uint(Val);
1526
1527        trans_timer            ->
1528	    verify_timer(Val) and (Val >= 0);
1529	trans_sender when Val =:= undefined -> true;
1530
1531        pending_timer                      -> verify_timer(Val);
1532        sent_pending_limit                 ->
1533	    megaco_config_misc:verify_uint(Val) andalso (Val > 0);
1534        recv_pending_limit                 ->
1535	    megaco_config_misc:verify_uint(Val) andalso (Val > 0);
1536        reply_timer                        -> verify_timer(Val);
1537        control_pid      when is_pid(Val)  -> true;
1538        monitor_ref                        -> true; % Internal usage only
1539        send_mod         when is_atom(Val) -> true;
1540        send_handle                        -> true;
1541        encoding_mod     when is_atom(Val) -> true;
1542        encoding_config  when is_list(Val) ->
1543            case Val of
1544                [{version3, V3}|_] when (V3 =/= v3) ->
1545                    error_msg("Encoding Config version3 ~p is "
1546                              "no longer supported!~n"),
1547                    ok;
1548                _ ->
1549                    ok
1550            end,
1551            true;
1552        protocol_version                   ->
1553	    megaco_config_misc:verify_strict_uint(Val);
1554        auth_data                          -> true;
1555        user_mod         when is_atom(Val) -> true;
1556        user_args        when is_list(Val) -> true;
1557        reply_data                         -> true;
1558        threaded                           ->
1559	    megaco_config_misc:verify_bool(Val);
1560        strict_version                     ->
1561	    megaco_config_misc:verify_bool(Val);
1562	long_request_resend                ->
1563	    megaco_config_misc:verify_bool(Val);
1564	call_proxy_gc_timeout              ->
1565	    megaco_config_misc:verify_strict_uint(Val);
1566	cancel                             ->
1567	    megaco_config_misc:verify_bool(Val);
1568	resend_indication                  -> verify_resend_indication(Val);
1569
1570	segment_reply_ind               ->
1571	    megaco_config_misc:verify_bool(Val);
1572	segment_recv_acc                ->
1573	    megaco_config_misc:verify_bool(Val);
1574	segment_recv_timer              -> verify_timer(Val);
1575	segment_send                    -> verify_segmentation_window(Val);
1576	segment_send_timer              -> verify_timer(Val);
1577	max_pdu_size                    ->
1578	    megaco_config_misc:verify_int(Val) andalso (Val > 0);
1579	request_keep_alive_timeout      ->
1580	    (megaco_config_misc:verify_uint(Val) orelse (Val =:= plain));
1581
1582        _                               -> false
1583    end.
1584
1585
1586
1587verify_resend_indication(flag) -> true;
1588verify_resend_indication(Val)  -> megaco_config_misc:verify_bool(Val).
1589
1590verify_timer(Timer) ->
1591    megaco_timer:verify(Timer).
1592
1593verify_segmentation_window(none) ->
1594    true;
1595verify_segmentation_window(K) ->
1596    megaco_config_misc:verify_int(K, 1, infinity).
1597
1598handle_stop_user(UserMid) ->
1599    case catch user_info(UserMid, mid) of
1600        {'EXIT', _} ->
1601	    {error, {no_such_user, UserMid}};
1602	_ ->
1603	    case catch user_info(UserMid, connections) of
1604		[] ->
1605		    ets:match_delete(megaco_config, {{UserMid, '_'}, '_'}),
1606		    ok;
1607		{'EXIT', _} ->
1608		    {error, {no_such_user, UserMid}};
1609		_Else ->
1610		    {error, {active_connections, UserMid}}
1611	    end
1612    end.
1613
1614handle_update_conn_data(CD, Item = receive_handle, RH) ->
1615    UserMid = (CD#conn_data.conn_handle)#megaco_conn_handle.local_mid,
1616    if
1617        is_record(RH, megaco_receive_handle) andalso
1618        is_atom(RH#megaco_receive_handle.encoding_mod) andalso
1619        is_list(RH#megaco_receive_handle.encoding_config) andalso
1620        is_atom(RH#megaco_receive_handle.send_mod) andalso
1621        (RH#megaco_receive_handle.local_mid /= UserMid) ->
1622            CD2 = CD#conn_data{
1623		    encoding_mod    = RH#megaco_receive_handle.encoding_mod,
1624		    encoding_config = RH#megaco_receive_handle.encoding_config,
1625		    send_mod        = RH#megaco_receive_handle.send_mod},
1626            ets:insert(megaco_local_conn, CD2),
1627            ok;
1628        true ->
1629            {error, {bad_user_val, UserMid, Item, RH}}
1630    end;
1631handle_update_conn_data(CD, Item, Val) ->
1632    case verify_val(Item, Val) of
1633        true ->
1634            CD2 = replace_conn_data(CD, Item, Val),
1635            ets:insert(megaco_local_conn, CD2),
1636            ok;
1637        false ->
1638            UserMid = (CD#conn_data.conn_handle)#megaco_conn_handle.local_mid,
1639            {error, {bad_user_val, UserMid, Item, Val}}
1640    end.
1641
1642replace_conn_data(CD, Item, Val) ->
1643    case Item of
1644        trans_id             -> CD#conn_data{serial             = Val};
1645        max_trans_id         -> CD#conn_data{max_serial         = Val};
1646        request_timer        -> CD#conn_data{request_timer      = Val};
1647        long_request_timer   -> CD#conn_data{long_request_timer = Val};
1648
1649	auto_ack             -> update_auto_ack(CD, Val);
1650
1651	%% Accumulate trans ack before sending
1652	trans_ack            -> update_trans_ack(CD, Val);
1653	trans_ack_maxcount   -> update_trans_ack_maxcount(CD, Val);
1654
1655	%% Accumulate trans req before sending
1656	trans_req            -> update_trans_req(CD, Val);
1657	trans_req_maxcount   -> update_trans_req_maxcount(CD, Val);
1658	trans_req_maxsize    -> update_trans_req_maxsize(CD, Val);
1659
1660	trans_timer          -> update_trans_timer(CD, Val);
1661	%% trans_sender      - Automagically updated by
1662	%%                     update_auto_ack & update_trans_timer &
1663	%%                     update_trans_ack & update_trans_req
1664
1665        pending_timer        -> CD#conn_data{pending_timer        = Val};
1666        sent_pending_limit   -> CD#conn_data{sent_pending_limit   = Val};
1667        recv_pending_limit   -> CD#conn_data{recv_pending_limit   = Val};
1668        reply_timer          -> CD#conn_data{reply_timer          = Val};
1669        control_pid          -> CD#conn_data{control_pid          = Val};
1670        monitor_ref          -> CD#conn_data{monitor_ref          = Val};
1671        send_mod             -> CD#conn_data{send_mod             = Val};
1672        send_handle          -> CD#conn_data{send_handle          = Val};
1673        encoding_mod         -> CD#conn_data{encoding_mod         = Val};
1674        encoding_config      -> CD#conn_data{encoding_config      = Val};
1675        protocol_version     -> CD#conn_data{protocol_version     = Val};
1676        auth_data            -> CD#conn_data{auth_data            = Val};
1677        user_mod             -> CD#conn_data{user_mod             = Val};
1678        user_args            -> CD#conn_data{user_args            = Val};
1679        reply_action         -> CD#conn_data{reply_action         = Val};
1680        reply_data           -> CD#conn_data{reply_data           = Val};
1681        threaded             -> CD#conn_data{threaded             = Val};
1682        strict_version       -> CD#conn_data{strict_version       = Val};
1683	long_request_resend  -> CD#conn_data{long_request_resend  = Val};
1684	call_proxy_gc_timeout -> CD#conn_data{call_proxy_gc_timeout = Val};
1685	cancel               -> CD#conn_data{cancel               = Val};
1686	resend_indication    -> CD#conn_data{resend_indication    = Val};
1687        segment_reply_ind    -> CD#conn_data{segment_reply_ind    = Val};
1688        segment_recv_acc     -> CD#conn_data{segment_recv_acc     = Val};
1689        segment_recv_timer   -> CD#conn_data{segment_recv_timer   = Val};
1690        segment_send         -> CD#conn_data{segment_send         = Val};
1691        segment_send_timer   -> CD#conn_data{segment_send_timer   = Val};
1692        max_pdu_size         -> CD#conn_data{max_pdu_size         = Val};
1693        request_keep_alive_timeout -> CD#conn_data{request_keep_alive_timeout = Val}
1694    end.
1695
1696%% update auto_ack
1697update_auto_ack(#conn_data{trans_sender = Pid,
1698			   trans_req    = false} = CD,
1699		false) when is_pid(Pid) ->
1700    megaco_trans_sender:stop(Pid),
1701    CD#conn_data{auto_ack = false, trans_sender = undefined};
1702
1703update_auto_ack(#conn_data{trans_timer  = To,
1704			   trans_ack    = true,
1705			   trans_sender = undefined} = CD,
1706		true) when To > 0 ->
1707    #conn_data{conn_handle        = CH,
1708	       trans_ack_maxcount = AcksMax,
1709	       trans_req_maxcount = ReqsMax,
1710	       trans_req_maxsize  = ReqsMaxSz} = CD,
1711    {ok, Pid} = megaco_trans_sup:start_trans_sender(CH, To, ReqsMaxSz,
1712						    ReqsMax, AcksMax),
1713
1714    %% Make sure we are notified when/if the transaction
1715    %% sender goes down.
1716    %% Do we need to store the ref? Will we ever need to
1717    %% cancel this (apply_at_exit)?
1718    megaco_monitor:apply_at_exit(?MODULE, trans_sender_exit, [CH], Pid),
1719
1720    CD#conn_data{auto_ack = true, trans_sender = Pid};
1721
1722update_auto_ack(CD, Val) ->
1723    ?d("update_auto_ack -> entry with ~p", [Val]),
1724    CD#conn_data{auto_ack = Val}.
1725
1726%% update trans_ack
1727update_trans_ack(#conn_data{auto_ack     = true,
1728			    trans_req    = false,
1729			    trans_sender = Pid} = CD,
1730		      false) when is_pid(Pid) ->
1731    megaco_trans_sender:stop(Pid),
1732    CD#conn_data{trans_ack = false, trans_sender = undefined};
1733
1734update_trans_ack(#conn_data{trans_timer  = To,
1735			    auto_ack     = true,
1736			    trans_sender = undefined} = CD,
1737		      true) when To > 0 ->
1738    #conn_data{conn_handle        = CH,
1739	       trans_ack_maxcount = AcksMax,
1740	       trans_req_maxcount = ReqsMax,
1741	       trans_req_maxsize  = ReqsMaxSz} = CD,
1742    {ok, Pid} = megaco_trans_sup:start_trans_sender(CH, To, ReqsMaxSz,
1743						    ReqsMax, AcksMax),
1744
1745    %% Make sure we are notified when/if the transaction
1746    %% sender goes down.
1747    %% Do we need to store the ref? Will we ever need to
1748    %% cancel this (apply_at_exit)?
1749    megaco_monitor:apply_at_exit(?MODULE, trans_sender_exit, [CH], Pid),
1750
1751    CD#conn_data{trans_ack = true, trans_sender = Pid};
1752
1753update_trans_ack(CD, Val) ->
1754    ?d("update_trans_ack -> entry with ~p", [Val]),
1755    CD#conn_data{trans_ack = Val}.
1756
1757%% update trans_req
1758update_trans_req(#conn_data{trans_ack    = false,
1759			    trans_sender = Pid} = CD,
1760		      false) when is_pid(Pid) ->
1761    megaco_trans_sender:stop(Pid),
1762    CD#conn_data{trans_req = false, trans_sender = undefined};
1763
1764update_trans_req(#conn_data{trans_timer  = To,
1765			    trans_sender = undefined} = CD,
1766		      true) when To > 0 ->
1767    #conn_data{conn_handle        = CH,
1768	       trans_ack_maxcount = AcksMax,
1769	       trans_req_maxcount = ReqsMax,
1770	       trans_req_maxsize  = ReqsMaxSz} = CD,
1771    {ok, Pid} = megaco_trans_sup:start_trans_sender(CH, To, ReqsMaxSz,
1772						    ReqsMax, AcksMax),
1773
1774    %% Make sure we are notified when/if the transaction
1775    %% sender goes down.
1776    %% Do we need to store the ref? Will we ever need to
1777    %% cancel this (apply_at_exit)?
1778    megaco_monitor:apply_at_exit(?MODULE, trans_sender_exit, [CH], Pid),
1779
1780    CD#conn_data{trans_req = true, trans_sender = Pid};
1781
1782update_trans_req(CD, Val) ->
1783    ?d("update_trans_req -> entry with ~p", [Val]),
1784    CD#conn_data{trans_req = Val}.
1785
1786%% update trans_timer
1787update_trans_timer(#conn_data{auto_ack     = true,
1788			      trans_ack    = true,
1789			      trans_sender = undefined} = CD,
1790		   To) when To > 0 ->
1791    #conn_data{conn_handle        = CH,
1792	       trans_ack_maxcount = AcksMax,
1793	       trans_req_maxcount = ReqsMax,
1794	       trans_req_maxsize  = ReqsMaxSz} = CD,
1795    {ok, Pid} = megaco_trans_sup:start_trans_sender(CH, To, ReqsMaxSz,
1796						    ReqsMax, AcksMax),
1797
1798    %% Make sure we are notified when/if the transaction
1799    %% sender goes down.
1800    %% Do we need to store the ref? Will we ever need to
1801    %% cancel this (apply_at_exit)?
1802    megaco_monitor:apply_at_exit(?MODULE, trans_sender_exit, [CH], Pid),
1803
1804    CD#conn_data{trans_timer = To, trans_sender = Pid};
1805
1806update_trans_timer(#conn_data{trans_req    = true,
1807			      trans_sender = undefined} = CD,
1808		   To) when To > 0 ->
1809    #conn_data{conn_handle        = CH,
1810	       trans_ack_maxcount = AcksMax,
1811	       trans_req_maxcount = ReqsMax,
1812	       trans_req_maxsize  = ReqsMaxSz} = CD,
1813    {ok, Pid} = megaco_trans_sup:start_trans_sender(CH, To, ReqsMaxSz,
1814						    ReqsMax, AcksMax),
1815
1816    %% Make sure we are notified when/if the transaction
1817    %% sender goes down.
1818    %% Do we need to store the ref? Will we ever need to
1819    %% cancel this (apply_at_exit)?
1820    megaco_monitor:apply_at_exit(?MODULE, trans_sender_exit, [CH], Pid),
1821
1822    CD#conn_data{trans_timer = To, trans_sender = Pid};
1823
1824update_trans_timer(#conn_data{trans_sender = Pid} = CD, 0) when is_pid(Pid) ->
1825    megaco_trans_sender:stop(Pid),
1826    CD#conn_data{trans_timer = 0, trans_sender = undefined};
1827
1828update_trans_timer(#conn_data{trans_sender = Pid} = CD, To)
1829  when is_pid(Pid) and (To > 0) ->
1830    megaco_trans_sender:timeout(Pid, To),
1831    CD#conn_data{trans_timer = To};
1832
1833update_trans_timer(CD, To) when To > 0 ->
1834    CD#conn_data{trans_timer = To}.
1835
1836%% update trans_ack_maxcount
1837update_trans_ack_maxcount(#conn_data{trans_sender = Pid} = CD, Max)
1838  when is_pid(Pid) and (Max > 0) ->
1839    megaco_trans_sender:ack_maxcount(Pid, Max),
1840    CD#conn_data{trans_ack_maxcount = Max};
1841
1842update_trans_ack_maxcount(CD, Max)
1843  when Max > 0 ->
1844    ?d("update_trans_ack_maxcount -> entry with ~p", [Max]),
1845    CD#conn_data{trans_ack_maxcount = Max}.
1846
1847%% update trans_req_maxcount
1848update_trans_req_maxcount(#conn_data{trans_sender = Pid} = CD, Max)
1849  when is_pid(Pid) and (Max > 0) ->
1850    megaco_trans_sender:req_maxcount(Pid, Max),
1851    CD#conn_data{trans_req_maxcount = Max};
1852
1853update_trans_req_maxcount(CD, Max)
1854  when Max > 0 ->
1855    ?d("update_trans_req_maxcount -> entry with ~p", [Max]),
1856    CD#conn_data{trans_req_maxcount = Max}.
1857
1858%% update trans_req_maxsize
1859update_trans_req_maxsize(#conn_data{trans_sender = Pid} = CD, Max)
1860  when is_pid(Pid) and (Max > 0) ->
1861    megaco_trans_sender:req_maxsize(Pid, Max),
1862    CD#conn_data{trans_req_maxsize = Max};
1863
1864update_trans_req_maxsize(CD, Max)
1865  when Max > 0 ->
1866    ?d("update_trans_req_maxsize -> entry with ~p", [Max]),
1867    CD#conn_data{trans_req_maxsize = Max}.
1868
1869
1870
1871handle_connect(RH, RemoteMid, SendHandle, ControlPid, Auto) ->
1872    LocalMid   = RH#megaco_receive_handle.local_mid,
1873    ConnHandle = #megaco_conn_handle{local_mid  = LocalMid,
1874				     remote_mid = RemoteMid},
1875    ?d("handle_connect -> entry with"
1876	"~n   ConnHandle: ~p", [ConnHandle]),
1877    case ets:lookup(megaco_local_conn, ConnHandle) of
1878        [] ->
1879	    PrelMid = preliminary_mid,
1880	    PrelHandle = ConnHandle#megaco_conn_handle{remote_mid = PrelMid},
1881	    case ets:lookup(megaco_local_conn, PrelHandle) of
1882		[] ->
1883		    case (catch init_conn_data(RH,
1884					       RemoteMid, SendHandle,
1885					       ControlPid, Auto)) of
1886			{'EXIT', _Reason} ->
1887			    ?d("handle_connect -> init conn data failed: "
1888				"~n   ~p",[_Reason]),
1889			    {error, {no_such_user, LocalMid}};
1890			ConnData ->
1891			    ?d("handle_connect -> new connection"
1892				"~n   ConnData: ~p", [ConnData]),
1893			    %% Brand new connection, use
1894			    %% When is the preliminary_mid used?
1895			    create_snmp_counters(ConnHandle),
1896			    %% Maybe start transaction sender
1897			    ConnData2 = trans_sender_start(ConnData),
1898			    ets:insert(megaco_local_conn, ConnData2),
1899			    {ok, ConnData2}
1900		    end;
1901		[PrelData] ->
1902		    ?d("handle_connect -> connection upgrade"
1903			"~n   PrelData: ~p", [PrelData]),
1904		    %% OK, we need to fix the snmp counters. Used
1905		    %% with the temporary (preliminary_mid) conn_handle.
1906		    create_snmp_counters(ConnHandle),
1907		    ConnData = PrelData#conn_data{conn_handle = ConnHandle},
1908		    trans_sender_upgrade(ConnData),
1909		    ets:insert(megaco_local_conn, ConnData),
1910		    ets:delete(megaco_local_conn, PrelHandle),
1911		    update_snmp_counters(ConnHandle, PrelHandle),
1912		    TH = ConnHandle#megaco_conn_handle{local_mid  = PrelMid,
1913						       remote_mid = RemoteMid},
1914		    TD = ConnData#conn_data{conn_handle = TH},
1915 		    ?report_debug(TD,
1916				  "Upgrade preliminary_mid to "
1917				  "actual remote_mid",
1918				  [{preliminary_mid, preliminary_mid},
1919				   {local_mid,       LocalMid},
1920				   {remote_mid,      RemoteMid}]),
1921		    {ok, ConnData}
1922	    end;
1923        [_ConnData] ->
1924            {error, {already_connected, ConnHandle}}
1925    end.
1926
1927handle_finish_connect(ConnHandle, SendHandle, ControlPid, MFA) ->
1928    case (catch ets:lookup(megaco_local_conn, ConnHandle)) of
1929	[#conn_data{monitor_ref = connected} = CD] ->
1930	    {M, F, A} = MFA,
1931	    Ref = megaco_monitor:apply_at_exit(M, F, A, ControlPid),
1932	    ConnData2 = CD#conn_data{monitor_ref = Ref,
1933				     control_pid = ControlPid,
1934				     send_handle = SendHandle},
1935	    ets:insert(megaco_local_conn, ConnData2),
1936	    {ok, Ref};
1937	[#conn_data{monitor_ref = Ref}] ->
1938	    {ok, Ref};
1939	[] ->
1940	    {error, {no_such_connection, ConnHandle}}
1941    end.
1942
1943
1944%% also trans_req == true
1945trans_sender_start(#conn_data{conn_handle        = CH,
1946			      auto_ack           = true,
1947			      trans_ack          = true,
1948			      trans_ack_maxcount = AcksMax,
1949			      trans_req_maxcount = ReqsMax,
1950			      trans_req_maxsize  = ReqsMaxSz,
1951			      trans_timer        = To,
1952			      trans_sender       = undefined} = CD)
1953  when To > 0 ->
1954
1955    ?d("trans_sender_start(ack) -> entry when"
1956	"~n   CH:        ~p"
1957	"~n   To:        ~p"
1958	"~n   AcksMax:   ~p"
1959	"~n   ReqsMax:   ~p"
1960	"~n   ReqsMaxSz: ~p", [CH, To, ReqsMaxSz, ReqsMax, AcksMax]),
1961
1962    {ok, Pid} = megaco_trans_sup:start_trans_sender(CH, To, ReqsMaxSz,
1963						    ReqsMax, AcksMax),
1964
1965    ?d("trans_sender_start(ack) -> Pid: ~p", [Pid]),
1966
1967    %% Make sure we are notified when/if the transaction
1968    %% sender goes down.
1969    %% Do we need to store the ref? Will we ever need to
1970    %% cancel this (apply_at_exit)?
1971    megaco_monitor:apply_at_exit(?MODULE, trans_sender_exit, [CH], Pid),
1972
1973    CD#conn_data{trans_sender = Pid};
1974
1975trans_sender_start(#conn_data{conn_handle        = CH,
1976			      trans_req          = true,
1977			      trans_ack_maxcount = AcksMax,
1978			      trans_req_maxcount = ReqsMax,
1979			      trans_req_maxsize  = ReqsMaxSz,
1980			      trans_timer        = To,
1981			      trans_sender       = undefined} = CD)
1982  when To > 0 ->
1983
1984    ?d("trans_sender_start(req) -> entry when"
1985	"~n   CH:        ~p"
1986	"~n   To:        ~p"
1987	"~n   AcksMax:   ~p"
1988	"~n   ReqsMax:   ~p"
1989	"~n   ReqsMaxSz: ~p", [CH, To, ReqsMaxSz, ReqsMax, AcksMax]),
1990
1991    {ok, Pid} = megaco_trans_sup:start_trans_sender(CH, To, ReqsMaxSz,
1992						    ReqsMax, AcksMax),
1993
1994    ?d("trans_sender_start(req) -> Pid: ~p", [Pid]),
1995
1996    %% Make sure we are notified when/if the transaction
1997    %% sender goes down.
1998    %% Do we need to store the ref? Will we ever need to
1999    %% cancel this (apply_at_exit)?
2000    megaco_monitor:apply_at_exit(?MODULE, trans_sender_exit, [CH], Pid),
2001
2002    CD#conn_data{trans_sender = Pid};
2003
2004trans_sender_start(CD) ->
2005    ?d("trans_sender_start -> undefined", []),
2006    CD#conn_data{trans_sender = undefined}.
2007
2008trans_sender_upgrade(#conn_data{conn_handle  = CH,
2009				trans_sender = Pid})
2010  when is_pid(Pid) ->
2011    ?d("trans_sende_upgrade -> entry when"
2012	"~n   CH:  ~p"
2013	"~n   Pid: ~p", [CH, Pid]),
2014    megaco_trans_sender:upgrade(Pid, CH);
2015trans_sender_upgrade(_CD) ->
2016    ok.
2017
2018
2019handle_connect_remote(ConnHandle, UserNode, Ref) ->
2020    Pat = #remote_conn_data{conn_handle = ConnHandle,
2021			    user_node   = UserNode,
2022			    monitor_ref = '_'},
2023    case ets:match_object(megaco_remote_conn, Pat) of
2024        [] ->
2025	    RCD = #remote_conn_data{conn_handle = ConnHandle,
2026				    user_node   = UserNode,
2027				    monitor_ref = Ref},
2028            ets:insert(megaco_remote_conn, RCD),
2029            ok;
2030        _ ->
2031            {error, {already_connected, ConnHandle, UserNode}}
2032    end.
2033
2034init_conn_data(RH, RemoteMid, SendHandle, ControlPid) ->
2035    init_conn_data(RH, RemoteMid, SendHandle, ControlPid, auto).
2036init_conn_data(RH, RemoteMid, SendHandle, ControlPid, Auto) ->
2037    Mid            = RH#megaco_receive_handle.local_mid,
2038    ConnHandle     = #megaco_conn_handle{local_mid  = Mid,
2039					 remote_mid = RemoteMid},
2040    EncodingMod    = RH#megaco_receive_handle.encoding_mod,
2041    EncodingConfig = RH#megaco_receive_handle.encoding_config,
2042    SendMod        = RH#megaco_receive_handle.send_mod,
2043    MonitorRef     =
2044	case Auto of
2045	    auto ->
2046		undefined_auto_monitor_ref;
2047	    {plain, ConnectorPid} ->
2048		{connecting, ConnectorPid}
2049	end,
2050    #conn_data{conn_handle          = ConnHandle,
2051               serial               = undefined_serial,
2052               max_serial           = user_info(Mid, max_trans_id),
2053               request_timer        = user_info(Mid, request_timer),
2054               long_request_timer   = user_info(Mid, long_request_timer),
2055
2056               auto_ack             = user_info(Mid, auto_ack),
2057               trans_ack            = user_info(Mid, trans_req),
2058               trans_req            = user_info(Mid, trans_req),
2059
2060	       trans_timer          = user_info(Mid, trans_timer),
2061	       trans_req_maxsize    = user_info(Mid, trans_req_maxsize),
2062	       trans_req_maxcount   = user_info(Mid, trans_req_maxcount),
2063	       trans_ack_maxcount   = user_info(Mid, trans_ack_maxcount),
2064
2065               pending_timer        = user_info(Mid, pending_timer),
2066               sent_pending_limit   = user_info(Mid, sent_pending_limit),
2067               recv_pending_limit   = user_info(Mid, recv_pending_limit),
2068               reply_timer          = user_info(Mid, reply_timer),
2069               control_pid          = ControlPid,
2070               monitor_ref          = MonitorRef,
2071               send_mod             = SendMod,
2072               send_handle          = SendHandle,
2073               encoding_mod         = EncodingMod,
2074               encoding_config      = EncodingConfig,
2075               protocol_version     = user_info(Mid, protocol_version),
2076               auth_data            = user_info(Mid, auth_data),
2077               user_mod             = user_info(Mid, user_mod),
2078               user_args            = user_info(Mid, user_args),
2079               reply_action         = undefined,
2080               reply_data           = user_info(Mid, reply_data),
2081	       threaded             = user_info(Mid, threaded),
2082	       strict_version       = user_info(Mid, strict_version),
2083	       long_request_resend  = user_info(Mid, long_request_resend),
2084	       call_proxy_gc_timeout = user_info(Mid, call_proxy_gc_timeout),
2085	       cancel               = false,
2086	       resend_indication    = user_info(Mid, resend_indication),
2087	       segment_reply_ind    = user_info(Mid, segment_reply_ind),
2088	       segment_recv_acc     = user_info(Mid, segment_recv_acc),
2089	       segment_recv_timer   = user_info(Mid, segment_recv_timer),
2090	       segment_send         = user_info(Mid, segment_send),
2091	       segment_send_timer   = user_info(Mid, segment_send_timer),
2092	       max_pdu_size         = user_info(Mid, max_pdu_size),
2093	       request_keep_alive_timeout = user_info(Mid, request_keep_alive_timeout)
2094	      }.
2095
2096handle_disconnect(ConnHandle) when is_record(ConnHandle, megaco_conn_handle) ->
2097    case ets:lookup(megaco_local_conn, ConnHandle) of
2098        [ConnData] ->
2099	    ets:delete(megaco_local_conn, ConnHandle),
2100	    RemoteConnData = handle_disconnect_remote(ConnHandle, '_'),
2101            {ok, ConnData, RemoteConnData};
2102        [] ->
2103            {error, {already_disconnected, ConnHandle}}
2104    end.
2105
2106handle_disconnect_remote(ConnHandle, UserNode) ->
2107    Pat = #remote_conn_data{conn_handle = ConnHandle,
2108			    user_node   = UserNode,
2109			    monitor_ref = '_'},
2110    RemoteConnData = ets:match_object(megaco_remote_conn, Pat),
2111    ets:match_delete(megaco_remote_conn, Pat),
2112    RemoteConnData.
2113
2114make_receive_handle(UserMid) ->
2115    #megaco_receive_handle{local_mid       = UserMid,
2116			   encoding_mod    = user_info(UserMid, encoding_mod),
2117			   encoding_config = user_info(UserMid, encoding_config),
2118			   send_mod        = user_info(UserMid, send_mod)}.
2119
2120
2121%%-----------------------------------------------------------------
2122%% Func: create_snmp_counters/1, update_snmp_counters/2
2123%% Description: create/update all the SNMP statistic counters
2124%%-----------------------------------------------------------------
2125
2126create_snmp_counters(CH) ->
2127    create_snmp_counters(CH, snmp_counters()).
2128
2129% create_snmp_counters(CH, []) ->
2130%     ok;
2131% create_snmp_counters(CH, [Counter|Counters]) ->
2132%     Key = {CH, Counter},
2133%     ets:insert(megaco_stats, {Key, 0}),
2134%     create_snmp_counters(CH, Counters).
2135
2136create_snmp_counters(CH, Counters) ->
2137    F = fun(Counter) ->
2138		Key = {CH, Counter},
2139		ets:insert(megaco_stats, {Key, 0})
2140	end,
2141    lists:foreach(F, Counters).
2142
2143
2144update_snmp_counters(CH, PrelCH) ->
2145    update_snmp_counters(CH, PrelCH, snmp_counters()).
2146
2147update_snmp_counters(_CH, _PrelCH, []) ->
2148    ok;
2149update_snmp_counters(CH, PrelCH, [Counter|Counters]) ->
2150    PrelKey = {PrelCH, Counter},
2151    Key     = {CH, Counter},
2152    [{PrelKey,PrelVal}] = ets:lookup(megaco_stats, PrelKey),
2153    ets:update_counter(megaco_stats, Key, PrelVal),
2154    ets:delete(megaco_stats, PrelKey),
2155    update_snmp_counters(CH, PrelCH, Counters).
2156
2157
2158global_snmp_counters() ->
2159    [medGwyGatewayNumErrors].
2160
2161snmp_counters() ->
2162    [medGwyGatewayNumTimerRecovery,
2163     medGwyGatewayNumErrors].
2164
2165
2166
2167%%-----------------------------------------------------------------
2168
2169%% Time in milli seconds
2170%% t() ->
2171%%     {A,B,C} = erlang:now(),
2172%%     A*1000000000+B*1000+(C div 1000).
2173
2174
2175%%-----------------------------------------------------------------
2176
2177%% warning_msg(F) ->
2178%%     warning_msg(F, []).
2179warning_msg(F, A) ->
2180    ?megaco_warning("Config server: " ++ F, A).
2181
2182error_msg(F) ->
2183    error_msg(F, []).
2184error_msg(F, A) ->
2185    ?megaco_error("Config server: " ++ F, A).
2186
2187