2323
2424from argparse import ArgumentParser
2525from weakref import WeakSet
26- from functools import partial
26+ from functools import partial , wraps
27+ from secrets import token_hex
28+ from urllib .parse import quote
2729
2830from sippy .UA import UA
2931from sippy .CCEvents import CCEventTry , CCEventConnect , CCEventFail
3941from sippy .SdpBody import SdpBody
4042from sippy .SipReason import SipReason
4143from sippy .UI .Controller import UIController
44+ from sippy .CLIManager import CLIManager
4245
4346class SRSParams :
4447 sippy_c = None
@@ -49,6 +52,7 @@ class SRSParams:
4952 rtpp_r_res = None
5053 rtpp_u_res = None
5154 sess_sdp = None
55+ rname = None
5256 body_tmpl = '\r \n ' .join (('v=0' , f'o={ SdpOrigin ()} ' ,
5357 's=Sippy_SRS' , 't=0 0' ))
5458 def __init__ (self , sippy_c , req ):
@@ -58,6 +62,10 @@ def __init__(self, sippy_c, req):
5862 self .sess_sdp = []
5963 self .rtpp_r_res = []
6064 self .rtpp_u_res = {}
65+ self .rname = str (token_hex (16 ))
66+
67+ def get_rname (self , index ):
68+ return f'{ self .rname } _{ index } '
6169
6270class SRSFailure (CCEventFail ):
6371 c2m = {488 :'Not Acceptable Here' ,
@@ -68,22 +76,37 @@ def __init__(self, reason, code=488):
6876 self .reason = SipReason (protocol = 'SIP' , cause = code ,
6977 reason = reason )
7078
79+ def check_state_required (method ):
80+ @wraps (method )
81+ def wrapper (self , * args , ** kwargs ):
82+ if not self .checkState ():
83+ return
84+ return method (self , * args , ** kwargs )
85+ return wrapper
86+
7187class SippySRSUAS (UA ):
88+ id = 0
7289 _p : SRSParams
7390 cId : 'SipCallId'
7491
7592 def __init__ (self , sippy_c , req , sip_t ):
93+ self .id = SippySRSUAS .id
94+ SippySRSUAS .id += 1
7695 self ._p = SRSParams (sippy_c , req )
7796 super ().__init__ (sippy_c , self .outEvent , disc_cbs = (self .sess_term ,))
7897 super ().recvRequest (req , sip_t )
7998
99+ def checkState (self ):
100+ return isinstance (self .state , (self .UasStateTrying , self .UasStateRinging ))
101+
80102 def sess_term (self , ua , rtime , origin , result = 0 ):
81103 print ('disconnected' )
82104 self ._p .rsess .delete ()
83105 del self ._p .rsess
84106 del self ._p .sippy_c
85107 self ._p = None
86108
109+ @check_state_required
87110 def rtp_legA_created (self , index , result , _ ):
88111 if result is None :
89112 return self .rtp_rec_created (None )
@@ -93,20 +116,24 @@ def rtp_legA_created(self, index, result, _):
93116 up .result_callback = partial (self .rtp_legB_created , index )
94117 self ._p .rsess .callee .update (up )
95118
119+ @check_state_required
96120 def rtp_legB_created (self , index , result , _ ):
97121 if result is None :
98122 return self .rtp_rec_created (None )
99123 self ._p .rtpp_u_res [index ] = (result .rtpproxy_address , result .rtpproxy_port )
100- self ._p .rsess .start_recording (result_callback = self .rtp_rec_created , index = index )
124+ rname = self ._p .get_rname (index )
125+ self ._p .rsess .start_recording (rname , result_callback = self .rtp_rec_created ,
126+ index = index , only_a = True , rflags = 's' )
101127
128+ @check_state_required
102129 def rtp_rec_created (self , result ):
103130 self ._p .rtpp_r_res .append (result )
104131 if len (self ._p .rtpp_r_res ) < len (self ._p .sess_sdp ):
105132 return
106133 nerrs = sum ([1 if r is None or r .startswith ('E' ) else 0
107134 for r in self ._p .rtpp_r_res ])
108135 if nerrs > 0 :
109- fail = SRSFailure (f'Just Can \' t , { nerrs } times' , 502 )
136+ fail = SRSFailure (f'Something broke , { nerrs } times' , 502 )
110137 self .recvEvent (fail )
111138 return
112139 ah_pass = ('label' , 'rtpmap' , 'ptime' )
@@ -124,6 +151,7 @@ def rtp_rec_created(self, result):
124151 event = CCEventConnect ((200 , 'OpenSIPIt Is Great Again! :)' , sdp ))
125152 self .recvEvent (event )
126153
154+ @check_state_required
127155 def outEvent (self , event , ua ):
128156 if not isinstance (event , CCEventTry ):
129157 return
@@ -142,6 +170,7 @@ def outEvent(self, event, ua):
142170 return
143171 #print(type(sdp_body.content), type(sdp_body.content.sections))
144172 rs = Rtp_proxy_session (self ._p .sippy_c , cId , self ._p .from_tag , self ._p .to_tag )
173+ rs .notify_tag = quote (f'r { self .id } ' )
145174 self ._p .rsess = rs
146175 rs .caller .raddress = self ._p .source
147176 for sdp in sdps :
@@ -184,7 +213,8 @@ def __init__(self):
184213 UIController (sippy_c , "Sippy SIP Recording Server" )
185214 udsc , udsoc = SipTransactionManager .model_udp_server
186215 udsoc .nworkers = 1
187- rpc = Rtp_proxy_client (sippy_c , spath = args .rtp_proxy_client )
216+ kwa = {'nsetup_f' : self .set_rtp_io_socket } if args .rtp_proxy_client .startswith ('rtp.io:' ) else {}
217+ rpc = Rtp_proxy_client (sippy_c , spath = args .rtp_proxy_client , ** kwa )
188218 def waitonline (_rpc ):
189219 if _rpc .online :
190220 ED2 .breakLoop ()
@@ -230,5 +260,27 @@ def safeStop(self, signum=None):
230260 self .sippy_c ['_sip_tm' ].shutdown ()
231261 Timeout (ED2 .breakLoop , 0.2 , 1 )
232262
263+ def set_rtp_io_socket (self , rtpp_nsock , rtpp_nsock_spec ):
264+ CLIManager (rtpp_nsock , self .recvCommand )
265+
266+ def recvCommand (self , clim , cmd ):
267+ args = cmd .split ()
268+ cmd = args .pop (0 ).lower ()
269+ if cmd == 'r' :
270+ if len (args ) != 1 :
271+ clim .send ('ERROR: syntax error: r [<id>]\n ' )
272+ return False
273+ idx = int (args [0 ])
274+ dlist = tuple (x for x in self .active_uas if x .id == idx )
275+ if len (dlist ) == 0 :
276+ clim .send ('ERROR: no call with id of %d has been found\n ' % idx )
277+ return False
278+ for cc in dlist :
279+ cc .disconnect ()
280+ clim .send ('OK\n ' )
281+ return False
282+ clim .send ('ERROR: unknown command\n ' )
283+ return False
284+
233285if __name__ == '__main__' :
234286 exit (SippySRS_Control ().run ())
0 commit comments