sippy 2.2.1__tar.gz → 2.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sippy-2.2.3/MANIFEST.in +4 -0
- {sippy-2.2.1/sippy.egg-info → sippy-2.2.3}/PKG-INFO +1 -1
- {sippy-2.2.1 → sippy-2.2.3}/setup.py +2 -4
- sippy-2.2.1/sippy/B2BRoute.py → sippy-2.2.3/sippy/B2B/Route.py +1 -1
- sippy-2.2.3/sippy/B2B/SimpleAPI.py +142 -0
- sippy-2.2.3/sippy/B2B/States.py +36 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/CLIManager.py +39 -13
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Core/EventDispatcher.py +32 -5
- {sippy-2.2.1 → sippy-2.2.3}/sippy/MsgBody.py +1 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/MultipartMixBody.py +20 -5
- {sippy-2.2.1 → sippy-2.2.3}/sippy/MyConfigParser.py +1 -1
- sippy-2.2.1/sippy/Rtp_proxy/Client/stream.py → sippy-2.2.3/sippy/Rtp_proxy/Client/Worker/external.py +14 -104
- sippy-2.2.3/sippy/Rtp_proxy/Client/Worker/internal.py +95 -0
- sippy-2.2.3/sippy/Rtp_proxy/Client/internal.py +67 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/Client/local.py +4 -16
- sippy-2.2.3/sippy/Rtp_proxy/Client/stream.py +106 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/Client/udp.py +11 -19
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/Cmd/sequencer.py +5 -1
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/Session/side.py +5 -5
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/Session/update.py +1 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/client.py +108 -72
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/session.py +29 -25
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipContentType.py +2 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipTransactionManager.py +20 -4
- sippy-2.2.3/sippy/Time/__init__.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UA.py +16 -6
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UaStateDead.py +3 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/b2bua.py +46 -129
- {sippy-2.2.1 → sippy-2.2.3/sippy.egg-info}/PKG-INFO +1 -1
- {sippy-2.2.1 → sippy-2.2.3}/sippy.egg-info/SOURCES.txt +9 -2
- sippy-2.2.3/tests/__init__.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/tests/test_B2BRoute.py +1 -1
- {sippy-2.2.1 → sippy-2.2.3}/tests/test_B2BTransforms.py +1 -1
- sippy-2.2.1/MANIFEST.in +0 -1
- {sippy-2.2.1 → sippy-2.2.3}/LICENSE +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/README.md +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/pyproject.toml +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/requirements.txt +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/setup.cfg +0 -0
- /sippy-2.2.1/sippy/B2BTransforms.py → /sippy-2.2.3/sippy/B2B/Transforms.py +0 -0
- {sippy-2.2.1/sippy/Core → sippy-2.2.3/sippy/B2B}/__init__.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/CCEvents.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Core/Exceptions.py +0 -0
- {sippy-2.2.1/sippy/Exceptions → sippy-2.2.3/sippy/Core}/__init__.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/ESipHeaderCSV.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/ESipHeaderIgnore.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Exceptions/RtpProxyError.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Exceptions/SdpParseError.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Exceptions/SipHandlingError.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Exceptions/SipParseError.py +0 -0
- {sippy-2.2.1/sippy/Math → sippy-2.2.3/sippy/Exceptions}/__init__.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/External_command.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/FakeAccounting.py +0 -0
- {sippy-2.2.1/sippy/Security → sippy-2.2.3/sippy/Math}/__init__.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Math/recfilter.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Network_server.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/RadiusAccounting.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/RadiusAuthorisation.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Radius_client.py +0 -0
- {sippy-2.2.1/sippy/Time → sippy-2.2.3/sippy/Rtp_proxy/Client/Worker}/__init__.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/Client/__init__.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/Client/net.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/Cmd/__init__.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/Session/__init__.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/Session/subcommand.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/Session/subcommand_ice.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/Session/webrtc.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/__init__.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Rtp_proxy/cmd.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SdpBody.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SdpConnecton.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SdpGeneric.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SdpMedia.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SdpMediaDescription.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SdpOrigin.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Security/SipNonce.py +0 -0
- {sippy-2.2.1/tests → sippy-2.2.3/sippy/Security}/__init__.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Signal.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipAddress.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipAddressHF.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipAllow.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipAlso.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipAuthorization.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipCCDiversion.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipCSeq.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipCallId.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipCiscoGUID.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipConf.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipContact.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipContentLength.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipDiversion.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipExpires.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipFrom.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipGenericHF.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipHeader.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipLogger.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipMaxForwards.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipMsg.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipNumericHF.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipPAssertedIdentity.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipPath.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipProxyAuthenticate.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipProxyAuthorization.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipRAck.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipRSeq.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipReason.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipRecordRoute.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipReferTo.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipReferredBy.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipRegistrationAgent.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipReplaces.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipRequest.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipResponse.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipRoute.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipServer.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipSupported.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipTo.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipURL.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipUserAgent.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipVia.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipWWWAuthenticate.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/SipWarning.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/StatefulProxy.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Time/MonoTime.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Time/Timeout.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Time/clock_dtime.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UaStateConnected.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UaStateDisconnected.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UaStateFailed.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UaStateGeneric.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UacStateCancelling.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UacStateIdle.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UacStateRinging.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UacStateTrying.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UacStateUpdating.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UasStateIdle.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UasStateRinging.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UasStateTrying.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/UasStateUpdating.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Udp_server.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/Wss_server.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/XMPP_server.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/__init__.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/b2bua_simple.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/dictionary +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy/misc.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy.egg-info/dependency_links.txt +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy.egg-info/entry_points.txt +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy.egg-info/requires.txt +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/sippy.egg-info/top_level.txt +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/tests/test_Multipart.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/tests/test_SdbBody.py +0 -0
- {sippy-2.2.1 → sippy-2.2.3}/tests/test_Udp_server.py +0 -0
sippy-2.2.3/MANIFEST.in
ADDED
|
@@ -8,13 +8,11 @@ with open("README.md", "r") as fh:
|
|
|
8
8
|
|
|
9
9
|
setup(
|
|
10
10
|
name = "sippy",
|
|
11
|
-
version = "2.2.
|
|
11
|
+
version = "2.2.3",
|
|
12
12
|
packages = find_packages(),
|
|
13
13
|
|
|
14
14
|
install_requires = requirements,
|
|
15
|
-
|
|
16
|
-
'': ['dictionary', '*.md']
|
|
17
|
-
},
|
|
15
|
+
include_package_data=True,
|
|
18
16
|
test_suite = 'tests',
|
|
19
17
|
|
|
20
18
|
entry_points = {
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
|
|
2
|
+
# Copyright (c) 2006-2025 Sippy Software, Inc. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
|
5
|
+
# are permitted provided that the following conditions are met:
|
|
6
|
+
#
|
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
|
8
|
+
# list of conditions and the following disclaimer.
|
|
9
|
+
#
|
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer in the documentation and/or
|
|
12
|
+
# other materials provided with the distribution.
|
|
13
|
+
#
|
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
15
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
16
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
17
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
18
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
19
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
20
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
21
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
22
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
23
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
24
|
+
|
|
25
|
+
from ..CLIManager import CLIConnectionManager, CLIManager
|
|
26
|
+
from ..Time.MonoTime import MonoTime
|
|
27
|
+
from .States import CCStateConnected, CCStateARComplete
|
|
28
|
+
|
|
29
|
+
class B2BSimpleAPI(CLIConnectionManager):
|
|
30
|
+
global_config = None
|
|
31
|
+
|
|
32
|
+
def __init__(self, global_config):
|
|
33
|
+
cmdfile = global_config['b2bua_socket']
|
|
34
|
+
self.global_config = global_config
|
|
35
|
+
if cmdfile.startswith('tcp:'):
|
|
36
|
+
parts = cmdfile[4:].split(':', 1)
|
|
37
|
+
if len(parts) == 1:
|
|
38
|
+
address = (parts[0], 12345)
|
|
39
|
+
else:
|
|
40
|
+
address = (parts[0], int(parts[1]))
|
|
41
|
+
super().__init__(self.recvCommand, address, tcp = True)
|
|
42
|
+
else:
|
|
43
|
+
if cmdfile.startswith('unix:'):
|
|
44
|
+
cmdfile = cmdfile[5:]
|
|
45
|
+
super().__init__(self.recvCommand, cmdfile)
|
|
46
|
+
|
|
47
|
+
def recvCommand(self, clim, cmd):
|
|
48
|
+
ccm = self.global_config['_cmap']
|
|
49
|
+
args = cmd.split()
|
|
50
|
+
cmd = args.pop(0).lower()
|
|
51
|
+
if cmd == 'q':
|
|
52
|
+
clim.close()
|
|
53
|
+
return False
|
|
54
|
+
if cmd == 'l':
|
|
55
|
+
res = 'In-memory calls:\n'
|
|
56
|
+
total = 0
|
|
57
|
+
for (cid, sname), uaast, uaost in ccm.listActiveCalls():
|
|
58
|
+
res += f'{cid}: {sname} ('
|
|
59
|
+
if uaast is not None:
|
|
60
|
+
_s, _t, _h, _p, _cld, _cli = uaast
|
|
61
|
+
res += f'{_s} {_t}:{_h}:{_p} {_cld} {_cli} -> '
|
|
62
|
+
else:
|
|
63
|
+
res += 'N/A -> '
|
|
64
|
+
if uaost is not None:
|
|
65
|
+
_s, _t, _h, _p, _cld, _cli = uaost
|
|
66
|
+
res += f'{_s} {_t}:{_h}:{_p} {_cld} {_cli})\n'
|
|
67
|
+
else:
|
|
68
|
+
res += 'N/A)\n'
|
|
69
|
+
total += 1
|
|
70
|
+
res += 'Total: %d\n' % total
|
|
71
|
+
clim.send(res)
|
|
72
|
+
return False
|
|
73
|
+
if cmd == 'lt':
|
|
74
|
+
res = 'In-memory server transactions:\n'
|
|
75
|
+
for tid, t in self.global_config['_sip_tm'].tserver.iteritems():
|
|
76
|
+
res += '%s %s %s\n' % (tid, t.method, t.state)
|
|
77
|
+
res += 'In-memory client transactions:\n'
|
|
78
|
+
for tid, t in self.global_config['_sip_tm'].tclient.iteritems():
|
|
79
|
+
res += '%s %s %s\n' % (tid, t.method, t.state)
|
|
80
|
+
clim.send(res)
|
|
81
|
+
return False
|
|
82
|
+
if cmd in ('lt', 'llt'):
|
|
83
|
+
if cmd == 'llt':
|
|
84
|
+
mindur = 60.0
|
|
85
|
+
else:
|
|
86
|
+
mindur = 0.0
|
|
87
|
+
ctime = MonoTime()
|
|
88
|
+
res = 'In-memory server transactions:\n'
|
|
89
|
+
for tid, t in self.global_config['_sip_tm'].tserver.iteritems():
|
|
90
|
+
duration = ctime - t.rtime
|
|
91
|
+
if duration < mindur:
|
|
92
|
+
continue
|
|
93
|
+
res += '%s %s %s %s\n' % (tid, t.method, t.state, duration)
|
|
94
|
+
res += 'In-memory client transactions:\n'
|
|
95
|
+
for tid, t in self.global_config['_sip_tm'].tclient.iteritems():
|
|
96
|
+
duration = ctime - t.rtime
|
|
97
|
+
if duration < mindur:
|
|
98
|
+
continue
|
|
99
|
+
res += '%s %s %s %s\n' % (tid, t.method, t.state, duration)
|
|
100
|
+
clim.send(res)
|
|
101
|
+
return False
|
|
102
|
+
if cmd == 'd':
|
|
103
|
+
if len(args) != 1:
|
|
104
|
+
clim.send('ERROR: syntax error: d <call-id>\n')
|
|
105
|
+
return False
|
|
106
|
+
if args[0] == '*':
|
|
107
|
+
self.discAll()
|
|
108
|
+
clim.send('OK\n')
|
|
109
|
+
return False
|
|
110
|
+
dlist = [x for x in ccm.ccmap if str(x.cId) == args[0]]
|
|
111
|
+
if len(dlist) == 0:
|
|
112
|
+
clim.send('ERROR: no call with id of %s has been found\n' % args[0])
|
|
113
|
+
return False
|
|
114
|
+
for cc in dlist:
|
|
115
|
+
cc.disconnect()
|
|
116
|
+
clim.send('OK\n')
|
|
117
|
+
return False
|
|
118
|
+
if cmd == 'r':
|
|
119
|
+
if len(args) != 1:
|
|
120
|
+
clim.send('ERROR: syntax error: r [<id>]\n')
|
|
121
|
+
return False
|
|
122
|
+
idx = int(args[0])
|
|
123
|
+
dlist = [x for x in ccm.ccmap if x.id == idx]
|
|
124
|
+
if len(dlist) == 0:
|
|
125
|
+
clim.send('ERROR: no call with id of %d has been found\n' % idx)
|
|
126
|
+
return False
|
|
127
|
+
for cc in dlist:
|
|
128
|
+
if not cc.proxied:
|
|
129
|
+
continue
|
|
130
|
+
if cc.state == CCStateConnected:
|
|
131
|
+
cc.disconnect(MonoTime().getOffsetCopy(-60), origin = 'media_timeout')
|
|
132
|
+
continue
|
|
133
|
+
if cc.state == CCStateARComplete:
|
|
134
|
+
cc.uaO.disconnect(MonoTime().getOffsetCopy(-60), origin = 'media_timeout')
|
|
135
|
+
continue
|
|
136
|
+
clim.send('OK\n')
|
|
137
|
+
return False
|
|
138
|
+
clim.send('ERROR: unknown command\n')
|
|
139
|
+
return False
|
|
140
|
+
|
|
141
|
+
def set_rtp_io_socket(self, rtpp_nsock, rtpp_nsock_spec):
|
|
142
|
+
CLIManager(rtpp_nsock, self.recvCommand)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
|
|
2
|
+
# Copyright (c) 2006-2025 Sippy Software, Inc. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
|
5
|
+
# are permitted provided that the following conditions are met:
|
|
6
|
+
#
|
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
|
8
|
+
# list of conditions and the following disclaimer.
|
|
9
|
+
#
|
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer in the documentation and/or
|
|
12
|
+
# other materials provided with the distribution.
|
|
13
|
+
#
|
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
15
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
16
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
17
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
18
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
19
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
20
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
21
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
22
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
23
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
24
|
+
|
|
25
|
+
class CCStateIdle(object):
|
|
26
|
+
sname = 'Idle'
|
|
27
|
+
class CCStateWaitRoute(object):
|
|
28
|
+
sname = 'WaitRoute'
|
|
29
|
+
class CCStateARComplete(object):
|
|
30
|
+
sname = 'ARComplete'
|
|
31
|
+
class CCStateConnected(object):
|
|
32
|
+
sname = 'Connected'
|
|
33
|
+
class CCStateDead(object):
|
|
34
|
+
sname = 'Dead'
|
|
35
|
+
class CCStateDisconnecting(object):
|
|
36
|
+
sname = 'Disconnecting'
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
from __future__ import print_function
|
|
29
29
|
|
|
30
|
-
from os import remove, chown, chmod
|
|
30
|
+
from os import remove, chown, chmod, pipe, read, write, close
|
|
31
31
|
import socket
|
|
32
32
|
from errno import EPIPE, ENOTCONN, EBADF, ECONNABORTED
|
|
33
33
|
from select import poll, POLLIN, POLLNVAL
|
|
@@ -48,19 +48,30 @@ class _Acceptor(Thread):
|
|
|
48
48
|
pollobj = None
|
|
49
49
|
fileno = None
|
|
50
50
|
|
|
51
|
-
def __init__(self, clicm):
|
|
51
|
+
def __init__(self, clicm, wakeup_r):
|
|
52
52
|
Thread.__init__(self)
|
|
53
53
|
self.clicm = clicm
|
|
54
54
|
self.pollobj = poll()
|
|
55
55
|
self.fileno = self.clicm.serversock.fileno()
|
|
56
|
+
self.wakeup_r = wakeup_r
|
|
56
57
|
self.pollobj.register(self.fileno, POLLIN)
|
|
58
|
+
self.pollobj.register(self.wakeup_r, POLLIN)
|
|
57
59
|
self.start()
|
|
58
60
|
|
|
59
61
|
def run(self):
|
|
60
62
|
#print(self.run, 'enter')
|
|
61
63
|
while True:
|
|
62
64
|
#print(self.run, 'cycle')
|
|
63
|
-
|
|
65
|
+
revents = dict(self.pollobj.poll())
|
|
66
|
+
if self.wakeup_r in revents and (revents[self.wakeup_r] & POLLIN):
|
|
67
|
+
try:
|
|
68
|
+
# drain the pipe (best-effort)
|
|
69
|
+
read(self.wakeup_r, 1024)
|
|
70
|
+
except OSError:
|
|
71
|
+
dump_exception('CLIConnectionManager: unhandled exception draining wakeup socket')
|
|
72
|
+
pass
|
|
73
|
+
break
|
|
74
|
+
pollret = revents.get(self.fileno, 0)
|
|
64
75
|
if pollret & POLLNVAL != 0:
|
|
65
76
|
break
|
|
66
77
|
if pollret & POLLIN == 0:
|
|
@@ -74,16 +85,18 @@ class _Acceptor(Thread):
|
|
|
74
85
|
break
|
|
75
86
|
else:
|
|
76
87
|
raise
|
|
77
|
-
except Exception
|
|
78
|
-
dump_exception('CLIConnectionManager: unhandled exception
|
|
88
|
+
except Exception:
|
|
89
|
+
dump_exception('CLIConnectionManager: unhandled exception accepting incoming connection')
|
|
79
90
|
break
|
|
80
91
|
#print(self.run, 'handle_accept')
|
|
81
92
|
ED2.callFromThread(self.clicm.handle_accept, clientsock, addr)
|
|
82
93
|
self.clicm = None
|
|
94
|
+
close(self.wakeup_r)
|
|
83
95
|
#print(self.run, 'exit')
|
|
84
96
|
|
|
85
97
|
class CLIConnectionManager(object):
|
|
86
98
|
command_cb = None
|
|
99
|
+
address = None
|
|
87
100
|
tcp = False
|
|
88
101
|
accept_list: list = None
|
|
89
102
|
serversock = None
|
|
@@ -116,7 +129,9 @@ class CLIConnectionManager(object):
|
|
|
116
129
|
if sock_mode != None:
|
|
117
130
|
chmod(address, sock_mode)
|
|
118
131
|
self.serversock.listen(backlog)
|
|
119
|
-
self.
|
|
132
|
+
wakeup_r, self._wakeup_w = pipe()
|
|
133
|
+
self.atr = _Acceptor(self, wakeup_r)
|
|
134
|
+
self.address = address
|
|
120
135
|
|
|
121
136
|
def handle_accept(self, conn, address):
|
|
122
137
|
#print(self.handle_accept)
|
|
@@ -125,16 +140,24 @@ class CLIConnectionManager(object):
|
|
|
125
140
|
conn.close()
|
|
126
141
|
return
|
|
127
142
|
try:
|
|
128
|
-
cm = CLIManager(conn, self.command_cb
|
|
129
|
-
except Exception
|
|
130
|
-
dump_exception('CLIConnectionManager: unhandled exception
|
|
143
|
+
cm = CLIManager(conn, self.command_cb)
|
|
144
|
+
except Exception:
|
|
145
|
+
dump_exception('CLIConnectionManager: unhandled exception setting up incoming connection handler')
|
|
131
146
|
conn.close()
|
|
132
147
|
return
|
|
133
148
|
|
|
134
149
|
def shutdown(self):
|
|
150
|
+
try:
|
|
151
|
+
write(self._wakeup_w, b'\x01')
|
|
152
|
+
except OSError:
|
|
153
|
+
dump_exception('CLIConnectionManager: unhandled exception waking up acceptor')
|
|
154
|
+
pass
|
|
155
|
+
close(self._wakeup_w)
|
|
135
156
|
self.serversock.close()
|
|
136
157
|
self.command_cb = None
|
|
137
158
|
self.atr.join()
|
|
159
|
+
if not self.tcp:
|
|
160
|
+
remove(self.address)
|
|
138
161
|
|
|
139
162
|
class _CLIManager_w(Thread):
|
|
140
163
|
daemon = True
|
|
@@ -174,6 +197,11 @@ class _CLIManager_w(Thread):
|
|
|
174
197
|
except BrokenPipeError:
|
|
175
198
|
self.clim = None
|
|
176
199
|
return
|
|
200
|
+
except socket.error as why:
|
|
201
|
+
if why.errno != EBADF:
|
|
202
|
+
raise
|
|
203
|
+
self.clim = None
|
|
204
|
+
return
|
|
177
205
|
if res == len(wbuffer):
|
|
178
206
|
break
|
|
179
207
|
if res > 0:
|
|
@@ -239,14 +267,12 @@ class CLIManager(object):
|
|
|
239
267
|
clientsock = None
|
|
240
268
|
command_cb = None
|
|
241
269
|
close_pendind = False
|
|
242
|
-
raddr = None
|
|
243
270
|
wthr = None
|
|
244
271
|
rthr = None
|
|
245
272
|
|
|
246
|
-
def __init__(self, clientsock, command_cb
|
|
273
|
+
def __init__(self, clientsock, command_cb):
|
|
247
274
|
self.clientsock = clientsock
|
|
248
275
|
self.command_cb = command_cb
|
|
249
|
-
self.raddr = raddr
|
|
250
276
|
self.wthr = _CLIManager_w(clientsock, self)
|
|
251
277
|
self.rthr = _CLIManager_r(clientsock, self)
|
|
252
278
|
|
|
@@ -254,7 +280,7 @@ class CLIManager(object):
|
|
|
254
280
|
try:
|
|
255
281
|
self.command_cb(self, cmd)
|
|
256
282
|
except:
|
|
257
|
-
dump_exception('CLIManager: unhandled exception
|
|
283
|
+
dump_exception('CLIManager: unhandled exception processing incoming data')
|
|
258
284
|
self.close()
|
|
259
285
|
|
|
260
286
|
def send(self, data):
|
|
@@ -29,12 +29,13 @@ from __future__ import print_function
|
|
|
29
29
|
from functools import partial
|
|
30
30
|
from datetime import datetime
|
|
31
31
|
from heapq import heappush, heappop, heapify
|
|
32
|
-
from threading import Lock
|
|
32
|
+
from threading import Lock, local as t_local
|
|
33
33
|
from random import random
|
|
34
34
|
import sys, traceback, signal
|
|
35
35
|
from _thread import get_ident
|
|
36
36
|
from sippy.Time.MonoTime import MonoTime
|
|
37
37
|
from sippy.Core.Exceptions import dump_exception, StdException
|
|
38
|
+
from queue import Queue
|
|
38
39
|
|
|
39
40
|
from elperiodic.ElPeriodic import ElPeriodic
|
|
40
41
|
|
|
@@ -123,6 +124,8 @@ class EventDispatcher2(Singleton):
|
|
|
123
124
|
ed_inum = 0
|
|
124
125
|
elp = None
|
|
125
126
|
bands = None
|
|
127
|
+
_exception = None
|
|
128
|
+
tloc_data = None
|
|
126
129
|
|
|
127
130
|
def __init__(self, freq = 100.0):
|
|
128
131
|
EventDispatcher2.state_lock.acquire()
|
|
@@ -140,6 +143,7 @@ class EventDispatcher2(Singleton):
|
|
|
140
143
|
self.elp = ElPeriodic(freq)
|
|
141
144
|
self.elp.CFT_enable(signal.SIGURG)
|
|
142
145
|
self.bands = [(freq, 0),]
|
|
146
|
+
self.tloc_data = t_local()
|
|
143
147
|
|
|
144
148
|
def signal(self, signum, frame):
|
|
145
149
|
self.signals_pending.append(signum)
|
|
@@ -233,16 +237,35 @@ class EventDispatcher2(Singleton):
|
|
|
233
237
|
def dispatchThreadCallback(self, thread_cb, cb_params):
|
|
234
238
|
try:
|
|
235
239
|
thread_cb(*cb_params)
|
|
236
|
-
except
|
|
237
|
-
if isinstance(ex, SystemExit):
|
|
238
|
-
|
|
240
|
+
except BaseException as ex:
|
|
241
|
+
if isinstance(ex, (SystemExit, KeyboardInterrupt)):
|
|
242
|
+
self._exception = ex
|
|
243
|
+
return
|
|
239
244
|
dump_exception('EventDispatcher2: unhandled exception when processing from-thread-call')
|
|
240
|
-
|
|
245
|
+
|
|
246
|
+
def dispatchThreadCallbackSync(self, res_cb_q, thread_cb, cb_params):
|
|
247
|
+
try:
|
|
248
|
+
res = thread_cb(*cb_params)
|
|
249
|
+
except BaseException as ex:
|
|
250
|
+
rval = (None, ex)
|
|
251
|
+
else:
|
|
252
|
+
rval = (res, None)
|
|
253
|
+
res_cb_q.put(rval)
|
|
241
254
|
|
|
242
255
|
def callFromThread(self, thread_cb, *cb_params):
|
|
243
256
|
self.elp.call_from_thread(self.dispatchThreadCallback, thread_cb, cb_params)
|
|
244
257
|
#print('EventDispatcher2.callFromThread completed', str(self), thread_cb, cb_params)
|
|
245
258
|
|
|
259
|
+
def callFromThreadSync(self, thread_cb, *cb_params):
|
|
260
|
+
if not hasattr(self.tloc_data, 'res_cb_q'):
|
|
261
|
+
self.tloc_data.res_cb_q = Queue()
|
|
262
|
+
res_cb_q = self.tloc_data.res_cb_q
|
|
263
|
+
self.elp.call_from_thread(self.dispatchThreadCallbackSync, res_cb_q, thread_cb, cb_params)
|
|
264
|
+
res, ex = res_cb_q.get()
|
|
265
|
+
if ex is not None:
|
|
266
|
+
raise ex
|
|
267
|
+
return res
|
|
268
|
+
|
|
246
269
|
def loop(self, timeout = None, freq = None):
|
|
247
270
|
if freq != None and self.bands[0][0] != freq:
|
|
248
271
|
for fb in self.bands:
|
|
@@ -274,6 +297,10 @@ class EventDispatcher2(Singleton):
|
|
|
274
297
|
break
|
|
275
298
|
self.elp.procrastinate()
|
|
276
299
|
self.last_ts = MonoTime()
|
|
300
|
+
if self._exception is not None:
|
|
301
|
+
ex = self._exception
|
|
302
|
+
self._exception = None
|
|
303
|
+
raise ex
|
|
277
304
|
return self.el_rval
|
|
278
305
|
|
|
279
306
|
def breakLoop(self, rval=0):
|
|
@@ -23,14 +23,22 @@
|
|
|
23
23
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
24
24
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
25
25
|
|
|
26
|
+
from secrets import token_hex
|
|
27
|
+
|
|
26
28
|
from sippy.SipHeader import SipHeader
|
|
29
|
+
from sippy.SipContentType import SipContentType
|
|
27
30
|
|
|
28
31
|
class MultipartMixBody():
|
|
29
32
|
parts = None
|
|
30
33
|
boundary = None
|
|
34
|
+
mtype = SipContentType('multipart/mixed')
|
|
35
|
+
mtype.parse()
|
|
31
36
|
|
|
32
37
|
def __init__(self, body = None, ctype = None):
|
|
38
|
+
self.parts = []
|
|
39
|
+
self.part_headers = []
|
|
33
40
|
if body is None:
|
|
41
|
+
self.setBoundary(token_hex(16))
|
|
34
42
|
return
|
|
35
43
|
sep = f'--{ctype.params["boundary"]}'
|
|
36
44
|
bparts = body.split(sep)
|
|
@@ -38,8 +46,6 @@ class MultipartMixBody():
|
|
|
38
46
|
assert len(bparts[0]) == 0
|
|
39
47
|
assert bparts[-1].strip() == '--'
|
|
40
48
|
parts = [p.lstrip() for p in bparts[1:-1]]
|
|
41
|
-
self.parts = []
|
|
42
|
-
self.part_headers = []
|
|
43
49
|
for sect in parts:
|
|
44
50
|
headers = []
|
|
45
51
|
ct = None
|
|
@@ -55,18 +61,24 @@ class MultipartMixBody():
|
|
|
55
61
|
sect = MsgBody(sect, ct)
|
|
56
62
|
self.parts.append(sect)
|
|
57
63
|
self.part_headers.append(headers)
|
|
58
|
-
self.
|
|
64
|
+
self.setBoundary(ctype.params["boundary"])
|
|
65
|
+
|
|
66
|
+
def setBoundary(self, bnd):
|
|
67
|
+
self.boundary = bnd
|
|
68
|
+
mtype = self.mtype.getCopy()
|
|
69
|
+
mtype.params["boundary"] = bnd
|
|
70
|
+
self.mtype = mtype
|
|
59
71
|
|
|
60
72
|
def __str__(self):
|
|
61
73
|
bnd = f'--{self.boundary}\r\n'
|
|
62
|
-
parts = [f'{bnd}Content-Type: {p.mtype}\r\n{p}' for p in self.parts]
|
|
74
|
+
parts = [f'{bnd}Content-Type: {p.mtype}\r\n\r\n{p}' for p in self.parts]
|
|
63
75
|
s = ''.join(parts)
|
|
64
76
|
s += f'{bnd[:-2]}--\r\n'
|
|
65
77
|
return s
|
|
66
78
|
|
|
67
79
|
def localStr(self, local_addr = None):
|
|
68
80
|
bnd = f'--{self.boundary}\r\n'
|
|
69
|
-
parts = [f'{bnd}Content-Type: {p.mtype}\r\n{p.localStr(local_addr)}'
|
|
81
|
+
parts = [f'{bnd}Content-Type: {p.mtype}\r\n\r\n{p.localStr(local_addr)}'
|
|
70
82
|
for p in self.parts]
|
|
71
83
|
s = ''.join(parts)
|
|
72
84
|
s += f'{bnd[:-2]}--\r\n'
|
|
@@ -78,5 +90,8 @@ class MultipartMixBody():
|
|
|
78
90
|
cself.boundary = self.boundary
|
|
79
91
|
return cself
|
|
80
92
|
|
|
93
|
+
def getContentType(self):
|
|
94
|
+
return self.mtype.getCopy()
|
|
95
|
+
|
|
81
96
|
if 'MsgBody' not in globals():
|
|
82
97
|
from sippy.MsgBody import MsgBody
|
|
@@ -33,7 +33,7 @@ except ImportError:
|
|
|
33
33
|
from configparser import RawConfigParser
|
|
34
34
|
_boolean_states = RawConfigParser.BOOLEAN_STATES
|
|
35
35
|
from sippy.SipConf import SipConf
|
|
36
|
-
from sippy.
|
|
36
|
+
from sippy.B2B.Transforms import getTransProc
|
|
37
37
|
|
|
38
38
|
SUPPORTED_OPTIONS = { \
|
|
39
39
|
'acct_enable': ('B', 'enable or disable Radius accounting'), \
|