sippy 2.2.0__tar.gz → 2.2.2__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.
Files changed (154) hide show
  1. sippy-2.2.2/MANIFEST.in +4 -0
  2. {sippy-2.2.0/sippy.egg-info → sippy-2.2.2}/PKG-INFO +15 -4
  3. {sippy-2.2.0 → sippy-2.2.2}/README.md +1 -1
  4. {sippy-2.2.0 → sippy-2.2.2}/setup.py +4 -5
  5. sippy-2.2.0/sippy/B2BRoute.py → sippy-2.2.2/sippy/B2B/Route.py +3 -3
  6. sippy-2.2.2/sippy/B2B/SimpleAPI.py +142 -0
  7. sippy-2.2.2/sippy/B2B/States.py +36 -0
  8. {sippy-2.2.0 → sippy-2.2.2}/sippy/CCEvents.py +32 -19
  9. {sippy-2.2.0 → sippy-2.2.2}/sippy/CLIManager.py +13 -6
  10. {sippy-2.2.0 → sippy-2.2.2}/sippy/Core/EventDispatcher.py +33 -7
  11. sippy-2.2.2/sippy/Exceptions/RtpProxyError.py +30 -0
  12. sippy-2.2.2/sippy/Exceptions/SdpParseError.py +33 -0
  13. sippy-2.2.0/sippy/Exceptions/SipParseError.py → sippy-2.2.2/sippy/Exceptions/SipHandlingError.py +6 -16
  14. sippy-2.2.0/sippy/Exceptions/RtpProxyError.py → sippy-2.2.2/sippy/Exceptions/SipParseError.py +10 -13
  15. {sippy-2.2.0 → sippy-2.2.2}/sippy/External_command.py +1 -1
  16. {sippy-2.2.0 → sippy-2.2.2}/sippy/Math/recfilter.py +2 -2
  17. {sippy-2.2.0 → sippy-2.2.2}/sippy/MsgBody.py +2 -1
  18. {sippy-2.2.0 → sippy-2.2.2}/sippy/MultipartMixBody.py +33 -7
  19. {sippy-2.2.0 → sippy-2.2.2}/sippy/MyConfigParser.py +1 -1
  20. {sippy-2.2.0 → sippy-2.2.2}/sippy/RadiusAccounting.py +1 -1
  21. {sippy-2.2.0 → sippy-2.2.2}/sippy/Radius_client.py +1 -1
  22. sippy-2.2.2/sippy/Rtp_proxy/Client/Worker/external.py +121 -0
  23. sippy-2.2.2/sippy/Rtp_proxy/Client/Worker/internal.py +95 -0
  24. sippy-2.2.2/sippy/Rtp_proxy/Client/__init__.py +1 -0
  25. sippy-2.2.2/sippy/Rtp_proxy/Client/internal.py +67 -0
  26. sippy-2.2.2/sippy/Rtp_proxy/Client/local.py +41 -0
  27. sippy-2.2.2/sippy/Rtp_proxy/Client/net.py +42 -0
  28. sippy-2.2.2/sippy/Rtp_proxy/Client/stream.py +103 -0
  29. sippy-2.2.2/sippy/Rtp_proxy/Client/udp.py +214 -0
  30. sippy-2.2.2/sippy/Rtp_proxy/Cmd/__init__.py +1 -0
  31. sippy-2.2.2/sippy/Rtp_proxy/Cmd/sequencer.py +63 -0
  32. sippy-2.2.2/sippy/Rtp_proxy/Session/__init__.py +1 -0
  33. sippy-2.2.2/sippy/Rtp_proxy/Session/side.py +263 -0
  34. sippy-2.2.2/sippy/Rtp_proxy/Session/subcommand.py +117 -0
  35. sippy-2.2.2/sippy/Rtp_proxy/Session/subcommand_ice.py +101 -0
  36. sippy-2.2.2/sippy/Rtp_proxy/Session/update.py +122 -0
  37. sippy-2.2.2/sippy/Rtp_proxy/Session/webrtc.py +42 -0
  38. sippy-2.2.2/sippy/Rtp_proxy/__init__.py +1 -0
  39. sippy-2.2.2/sippy/Rtp_proxy/client.py +313 -0
  40. sippy-2.2.2/sippy/Rtp_proxy/cmd.py +224 -0
  41. sippy-2.2.2/sippy/Rtp_proxy/session.py +225 -0
  42. {sippy-2.2.0 → sippy-2.2.2}/sippy/SdpBody.py +5 -4
  43. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipAddressHF.py +1 -1
  44. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipAuthorization.py +0 -1
  45. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipContentType.py +2 -0
  46. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipHeader.py +1 -1
  47. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipMsg.py +1 -1
  48. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipRegistrationAgent.py +4 -2
  49. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipRequest.py +11 -2
  50. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipTransactionManager.py +46 -26
  51. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipWWWAuthenticate.py +2 -2
  52. {sippy-2.2.0 → sippy-2.2.2}/sippy/Time/MonoTime.py +1 -1
  53. sippy-2.2.2/sippy/Time/__init__.py +0 -0
  54. {sippy-2.2.0 → sippy-2.2.2}/sippy/UA.py +79 -46
  55. {sippy-2.2.0 → sippy-2.2.2}/sippy/UaStateConnected.py +38 -54
  56. {sippy-2.2.0 → sippy-2.2.2}/sippy/UaStateDead.py +4 -1
  57. {sippy-2.2.0 → sippy-2.2.2}/sippy/UaStateDisconnected.py +6 -9
  58. {sippy-2.2.0 → sippy-2.2.2}/sippy/UaStateFailed.py +3 -6
  59. {sippy-2.2.0 → sippy-2.2.2}/sippy/UacStateCancelling.py +7 -14
  60. {sippy-2.2.0 → sippy-2.2.2}/sippy/UacStateIdle.py +11 -12
  61. {sippy-2.2.0 → sippy-2.2.2}/sippy/UacStateRinging.py +29 -24
  62. {sippy-2.2.0 → sippy-2.2.2}/sippy/UacStateTrying.py +36 -34
  63. {sippy-2.2.0 → sippy-2.2.2}/sippy/UacStateUpdating.py +30 -32
  64. {sippy-2.2.0 → sippy-2.2.2}/sippy/UasStateIdle.py +4 -7
  65. {sippy-2.2.0 → sippy-2.2.2}/sippy/UasStateRinging.py +21 -38
  66. {sippy-2.2.0 → sippy-2.2.2}/sippy/UasStateTrying.py +18 -39
  67. {sippy-2.2.0 → sippy-2.2.2}/sippy/UasStateUpdating.py +29 -44
  68. {sippy-2.2.0 → sippy-2.2.2}/sippy/Udp_server.py +14 -10
  69. {sippy-2.2.0 → sippy-2.2.2}/sippy/Wss_server.py +1 -1
  70. {sippy-2.2.0 → sippy-2.2.2}/sippy/XMPP_server.py +3 -3
  71. sippy-2.2.0/sippy/b2bua_radius.py → sippy-2.2.2/sippy/b2bua.py +58 -145
  72. {sippy-2.2.0 → sippy-2.2.2}/sippy/b2bua_simple.py +1 -1
  73. {sippy-2.2.0 → sippy-2.2.2/sippy.egg-info}/PKG-INFO +15 -4
  74. {sippy-2.2.0 → sippy-2.2.2}/sippy.egg-info/SOURCES.txt +30 -3
  75. sippy-2.2.2/sippy.egg-info/entry_points.txt +4 -0
  76. sippy-2.2.2/tests/__init__.py +0 -0
  77. {sippy-2.2.0 → sippy-2.2.2}/tests/test_B2BRoute.py +1 -1
  78. {sippy-2.2.0 → sippy-2.2.2}/tests/test_B2BTransforms.py +1 -1
  79. sippy-2.2.2/tests/test_Multipart.py +45 -0
  80. sippy-2.2.0/MANIFEST.in +0 -1
  81. sippy-2.2.0/sippy.egg-info/entry_points.txt +0 -3
  82. {sippy-2.2.0 → sippy-2.2.2}/LICENSE +0 -0
  83. {sippy-2.2.0 → sippy-2.2.2}/pyproject.toml +0 -0
  84. {sippy-2.2.0 → sippy-2.2.2}/requirements.txt +0 -0
  85. {sippy-2.2.0 → sippy-2.2.2}/setup.cfg +0 -0
  86. /sippy-2.2.0/sippy/B2BTransforms.py → /sippy-2.2.2/sippy/B2B/Transforms.py +0 -0
  87. {sippy-2.2.0/sippy/Core → sippy-2.2.2/sippy/B2B}/__init__.py +0 -0
  88. {sippy-2.2.0 → sippy-2.2.2}/sippy/Core/Exceptions.py +0 -0
  89. {sippy-2.2.0/sippy/Exceptions → sippy-2.2.2/sippy/Core}/__init__.py +0 -0
  90. {sippy-2.2.0 → sippy-2.2.2}/sippy/ESipHeaderCSV.py +0 -0
  91. {sippy-2.2.0 → sippy-2.2.2}/sippy/ESipHeaderIgnore.py +0 -0
  92. {sippy-2.2.0/sippy/Math → sippy-2.2.2/sippy/Exceptions}/__init__.py +0 -0
  93. {sippy-2.2.0 → sippy-2.2.2}/sippy/FakeAccounting.py +0 -0
  94. {sippy-2.2.0/sippy/Security → sippy-2.2.2/sippy/Math}/__init__.py +0 -0
  95. {sippy-2.2.0 → sippy-2.2.2}/sippy/Network_server.py +0 -0
  96. {sippy-2.2.0 → sippy-2.2.2}/sippy/RadiusAuthorisation.py +0 -0
  97. {sippy-2.2.0/sippy/Time → sippy-2.2.2/sippy/Rtp_proxy/Client/Worker}/__init__.py +0 -0
  98. {sippy-2.2.0 → sippy-2.2.2}/sippy/SdpConnecton.py +0 -0
  99. {sippy-2.2.0 → sippy-2.2.2}/sippy/SdpGeneric.py +0 -0
  100. {sippy-2.2.0 → sippy-2.2.2}/sippy/SdpMedia.py +0 -0
  101. {sippy-2.2.0 → sippy-2.2.2}/sippy/SdpMediaDescription.py +0 -0
  102. {sippy-2.2.0 → sippy-2.2.2}/sippy/SdpOrigin.py +0 -0
  103. {sippy-2.2.0 → sippy-2.2.2}/sippy/Security/SipNonce.py +0 -0
  104. {sippy-2.2.0/tests → sippy-2.2.2/sippy/Security}/__init__.py +0 -0
  105. {sippy-2.2.0 → sippy-2.2.2}/sippy/Signal.py +0 -0
  106. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipAddress.py +0 -0
  107. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipAllow.py +0 -0
  108. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipAlso.py +0 -0
  109. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipCCDiversion.py +0 -0
  110. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipCSeq.py +0 -0
  111. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipCallId.py +0 -0
  112. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipCiscoGUID.py +0 -0
  113. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipConf.py +0 -0
  114. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipContact.py +0 -0
  115. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipContentLength.py +0 -0
  116. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipDiversion.py +0 -0
  117. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipExpires.py +0 -0
  118. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipFrom.py +0 -0
  119. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipGenericHF.py +0 -0
  120. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipLogger.py +0 -0
  121. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipMaxForwards.py +0 -0
  122. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipNumericHF.py +0 -0
  123. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipPAssertedIdentity.py +0 -0
  124. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipPath.py +0 -0
  125. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipProxyAuthenticate.py +0 -0
  126. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipProxyAuthorization.py +0 -0
  127. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipRAck.py +0 -0
  128. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipRSeq.py +0 -0
  129. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipReason.py +0 -0
  130. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipRecordRoute.py +0 -0
  131. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipReferTo.py +0 -0
  132. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipReferredBy.py +0 -0
  133. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipReplaces.py +0 -0
  134. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipResponse.py +0 -0
  135. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipRoute.py +0 -0
  136. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipServer.py +0 -0
  137. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipSupported.py +0 -0
  138. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipTo.py +0 -0
  139. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipURL.py +0 -0
  140. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipUserAgent.py +0 -0
  141. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipVia.py +0 -0
  142. {sippy-2.2.0 → sippy-2.2.2}/sippy/SipWarning.py +0 -0
  143. {sippy-2.2.0 → sippy-2.2.2}/sippy/StatefulProxy.py +0 -0
  144. {sippy-2.2.0 → sippy-2.2.2}/sippy/Time/Timeout.py +0 -0
  145. {sippy-2.2.0 → sippy-2.2.2}/sippy/Time/clock_dtime.py +0 -0
  146. {sippy-2.2.0 → sippy-2.2.2}/sippy/UaStateGeneric.py +0 -0
  147. {sippy-2.2.0 → sippy-2.2.2}/sippy/__init__.py +0 -0
  148. {sippy-2.2.0 → sippy-2.2.2}/sippy/dictionary +0 -0
  149. {sippy-2.2.0 → sippy-2.2.2}/sippy/misc.py +0 -0
  150. {sippy-2.2.0 → sippy-2.2.2}/sippy.egg-info/dependency_links.txt +0 -0
  151. {sippy-2.2.0 → sippy-2.2.2}/sippy.egg-info/requires.txt +0 -0
  152. {sippy-2.2.0 → sippy-2.2.2}/sippy.egg-info/top_level.txt +0 -0
  153. {sippy-2.2.0 → sippy-2.2.2}/tests/test_SdbBody.py +0 -0
  154. {sippy-2.2.0 → sippy-2.2.2}/tests/test_Udp_server.py +0 -0
@@ -0,0 +1,4 @@
1
+ include README.md
2
+ include requirements.txt
3
+ include sippy/dictionary
4
+ recursive-include sippy/UI/static *
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: sippy
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: RFC3261 SIP Stack and Back-to-Back User Agent (B2BUA)
5
5
  Home-page: http://www.b2bua.org/
6
6
  Author: Sippy Software, Inc.
@@ -15,8 +15,19 @@ License-File: LICENSE
15
15
  Requires-Dist: ElPeriodic>=1.1
16
16
  Requires-Dist: pycryptodome
17
17
  Requires-Dist: websockets
18
-
19
- [![Build Status@GitHub](https://github.com/sippy/b2bua/workflows/Check%20Python%20Wheels/badge.svg?branch=master)](https://github.com/sippy/b2bua/actions?query=branch%3Amaster++)
18
+ Dynamic: author
19
+ Dynamic: author-email
20
+ Dynamic: classifier
21
+ Dynamic: description
22
+ Dynamic: description-content-type
23
+ Dynamic: home-page
24
+ Dynamic: keywords
25
+ Dynamic: license
26
+ Dynamic: license-file
27
+ Dynamic: requires-dist
28
+ Dynamic: summary
29
+
30
+ [![Build Status@GitHub](https://github.com/sippy/b2bua/actions/workflows/python-wheels.yml/badge.svg?branch=master)](https://github.com/sippy/b2bua/actions/workflows/python-wheels.yml?query=branch%3Amaster++)
20
31
 
21
32
  # Sippy B2BUA
22
33
 
@@ -1,4 +1,4 @@
1
- [![Build Status@GitHub](https://github.com/sippy/b2bua/workflows/Check%20Python%20Wheels/badge.svg?branch=master)](https://github.com/sippy/b2bua/actions?query=branch%3Amaster++)
1
+ [![Build Status@GitHub](https://github.com/sippy/b2bua/actions/workflows/python-wheels.yml/badge.svg?branch=master)](https://github.com/sippy/b2bua/actions/workflows/python-wheels.yml?query=branch%3Amaster++)
2
2
 
3
3
  # Sippy B2BUA
4
4
 
@@ -8,19 +8,18 @@ with open("README.md", "r") as fh:
8
8
 
9
9
  setup(
10
10
  name = "sippy",
11
- version = "2.2.0",
11
+ version = "2.2.2",
12
12
  packages = find_packages(),
13
13
 
14
14
  install_requires = requirements,
15
- package_data = {
16
- '': ['dictionary', '*.md']
17
- },
15
+ include_package_data=True,
18
16
  test_suite = 'tests',
19
17
 
20
18
  entry_points = {
21
19
  'console_scripts': [
22
20
  'b2bua_simple = sippy.b2bua_simple:main_func',
23
- 'b2bua_radius = sippy.b2bua_radius:main_func',
21
+ 'b2bua_radius = sippy.b2bua:main_func',
22
+ 'b2bua = sippy.b2bua:main_func',
24
23
  ],
25
24
  },
26
25
 
@@ -26,7 +26,7 @@
26
26
 
27
27
  from sippy.SipHeader import SipHeader
28
28
  from sippy.SipConf import SipConf
29
- from sippy.B2BTransforms import getTransProc
29
+ from sippy.B2B.Transforms import getTransProc
30
30
 
31
31
  try:
32
32
  from urllib import unquote
@@ -82,7 +82,7 @@ class B2BRoute(object):
82
82
  self.cli_set = cself.cli_set
83
83
  self.params = dict(cself.params)
84
84
  self.ainfo = cself.ainfo
85
- if cself.extra_headers != None:
85
+ if cself.extra_headers is not None:
86
86
  self.extra_headers = tuple([x.getCopy() for x in cself.extra_headers])
87
87
  return
88
88
  route = sroute.split(';')
@@ -183,7 +183,7 @@ class B2BRoute(object):
183
183
  if 'gt' in self.params:
184
184
  timeout, skip = self.params['gt'].split(',', 1)
185
185
  self.params['group_timeout'] = (int(timeout), rnum + int(skip))
186
- if self.extra_headers != None:
186
+ if self.extra_headers is not None:
187
187
  self.extra_headers = self.extra_headers + tuple(pass_headers)
188
188
  else:
189
189
  self.extra_headers = tuple(pass_headers)
@@ -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'
@@ -25,19 +25,21 @@
25
25
  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
26
 
27
27
  from sippy.Time.MonoTime import MonoTime
28
+ from sippy.SipHeader import SipHeader
29
+ from sippy.SipWarning import SipWarning
28
30
 
29
31
  class CCEventGeneric(object):
30
32
  data = None
31
33
  name = None
32
34
  rtime = None
33
35
  origin = None
34
- reason = None
36
+ reason_rfc3326 = None
35
37
  extra_headers = None
36
38
  seq = 1
37
39
 
38
40
  def __init__(self, data = None, rtime = None, origin = None):
39
41
  self.data = data
40
- if rtime == None:
42
+ if rtime is None:
41
43
  self.rtime = MonoTime()
42
44
  else:
43
45
  self.rtime = rtime
@@ -50,12 +52,20 @@ class CCEventGeneric(object):
50
52
 
51
53
  def getCopy(self):
52
54
  cself = self.__class__(self.data, self.rtime, self.origin)
53
- if self.reason != None:
54
- cself.reason = self.reason.getCopy()
55
- if self.extra_headers != None:
56
- cself.extra_headers = tuple([x.getCopy() for x in self.extra_headers])
55
+ if self.extra_headers is not None:
56
+ cself.extra_headers = tuple(x.getCopy() for x in self.extra_headers)
57
+ if self.reason_rfc3326 is not None:
58
+ cself.reason_rfc3326 = self.reason_rfc3326.getCopy()
57
59
  return cself
58
60
 
61
+ def getExtraHeaders(self):
62
+ extra_headers = []
63
+ if self.extra_headers is not None:
64
+ extra_headers.extend(self.extra_headers)
65
+ if self.reason_rfc3326 is not None:
66
+ extra_headers.append(SipHeader(body = self.reason_rfc3326))
67
+ return tuple(extra_headers) if len(extra_headers) > 0 else None
68
+
59
69
  def __str__(self):
60
70
  return self.name
61
71
 
@@ -64,7 +74,7 @@ class CCEventTry(CCEventGeneric):
64
74
  max_forwards = None
65
75
 
66
76
  def getCopy(self):
67
- cself = CCEventGeneric.getCopy(self)
77
+ cself = super().getCopy()
68
78
  cself.max_forwards = self.max_forwards
69
79
  return cself
70
80
 
@@ -75,35 +85,27 @@ class CCEventTry(CCEventGeneric):
75
85
 
76
86
  class CCEventRing(CCEventGeneric):
77
87
  name = 'CCEventRing'
78
- pass
79
88
 
80
89
  class CCEventPreConnect(CCEventGeneric):
81
90
  name = 'CCEventPreConnect'
82
- pass
83
91
 
84
92
  class CCEventConnect(CCEventGeneric):
85
93
  name = 'CCEventConnect'
86
- pass
87
94
 
88
95
  class CCEventUpdate(CCEventGeneric):
89
96
  name = 'CCEventUpdate'
90
97
  max_forwards = None
91
98
 
92
99
  def getCopy(self):
93
- cself = CCEventGeneric.getCopy(self)
100
+ cself = super().getCopy()
94
101
  cself.max_forwards = self.max_forwards
95
102
  return cself
96
103
 
97
104
  class CCEventInfo(CCEventGeneric):
98
105
  name = 'CCEventInfo'
99
- pass
100
106
 
101
107
  class CCEventDisconnect(CCEventGeneric):
102
108
  name = 'CCEventDisconnect'
103
- pass
104
-
105
- from sippy.SipHeader import SipHeader
106
- from sippy.SipWarning import SipWarning
107
109
 
108
110
  class CCEventFail(CCEventGeneric):
109
111
  name = 'CCEventFail'
@@ -111,14 +113,25 @@ class CCEventFail(CCEventGeneric):
111
113
  warning = None
112
114
 
113
115
  def getCopy(self):
114
- cself = CCEventGeneric.getCopy(self)
115
- if self.challenges != None:
116
+ cself = super().getCopy()
117
+ if self.challenges is not None:
116
118
  cself.challenges = [x.getCopy() for x in self.challenges]
119
+ if self.warning is not None:
120
+ cself.warning = self.warning.getCopy()
117
121
  return cself
118
122
 
123
+ def getExtraHeaders(self):
124
+ extra_headers = []
125
+ if (eh := super().getExtraHeaders()) is not None:
126
+ extra_headers.extend(eh)
127
+ if self.challenges is not None:
128
+ extra_headers.extend(self.challenges)
129
+ if self.warning is not None:
130
+ extra_headers.append(self.warning)
131
+ return tuple(extra_headers) if len(extra_headers) > 0 else None
132
+
119
133
  def setWarning(self, eistr):
120
134
  self.warning = SipHeader(body = SipWarning(text = eistr))
121
135
 
122
136
  class CCEventRedirect(CCEventGeneric):
123
137
  name = 'CCEventRedirect'
124
- pass
@@ -125,7 +125,7 @@ class CLIConnectionManager(object):
125
125
  conn.close()
126
126
  return
127
127
  try:
128
- cm = CLIManager(conn, self.command_cb, address)
128
+ cm = CLIManager(conn, self.command_cb)
129
129
  except Exception as e:
130
130
  dump_exception('CLIConnectionManager: unhandled exception when setting up incoming connection handler')
131
131
  conn.close()
@@ -169,7 +169,16 @@ class _CLIManager_w(Thread):
169
169
  self.wbuffer = None
170
170
  self.w_available.release()
171
171
  while True:
172
- res = self.clientsock.send(wbuffer)
172
+ try:
173
+ res = self.clientsock.send(wbuffer)
174
+ except BrokenPipeError:
175
+ self.clim = None
176
+ return
177
+ except socket.error as why:
178
+ if why.errno != EBADF:
179
+ raise
180
+ self.clim = None
181
+ return
173
182
  if res == len(wbuffer):
174
183
  break
175
184
  if res > 0:
@@ -235,14 +244,12 @@ class CLIManager(object):
235
244
  clientsock = None
236
245
  command_cb = None
237
246
  close_pendind = False
238
- raddr = None
239
247
  wthr = None
240
248
  rthr = None
241
249
 
242
- def __init__(self, clientsock, command_cb, raddr):
250
+ def __init__(self, clientsock, command_cb):
243
251
  self.clientsock = clientsock
244
252
  self.command_cb = command_cb
245
- self.raddr = raddr
246
253
  self.wthr = _CLIManager_w(clientsock, self)
247
254
  self.rthr = _CLIManager_r(clientsock, self)
248
255
 
@@ -276,7 +283,7 @@ class CLIManager(object):
276
283
  self.clientsock.shutdown(socket.SHUT_RDWR)
277
284
  except Exception as e:
278
285
  if not isinstance(e, socket.error) or e.errno != ENOTCONN: # pylint: disable=no-member
279
- dump_exception('self.clientsock.shutdown(socket.SHUT_RDWR)')
286
+ dump_exception('CLIManager: unhandled exception in self.clientsock.shutdown(socket.SHUT_RDWR)')
280
287
  self.clientsock.close()
281
288
  self.wthr = None
282
289
  self.rthr = None
@@ -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 Exception as ex:
237
- if isinstance(ex, SystemExit):
238
- raise
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
- #print('dispatchThreadCallback dispatched', thread_cb, cb_params)
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:
@@ -255,8 +278,7 @@ class EventDispatcher2(Singleton):
255
278
  self.bands.insert(0, fb)
256
279
  self.endloop = False
257
280
  self.last_ts = MonoTime()
258
- if timeout != None:
259
- etime = self.last_ts.getOffsetCopy(timeout)
281
+ etime = None if timeout is None else self.last_ts.getOffsetCopy(timeout)
260
282
  while True:
261
283
  if len(self.signals_pending) > 0:
262
284
  self.dispatchSignals()
@@ -275,6 +297,10 @@ class EventDispatcher2(Singleton):
275
297
  break
276
298
  self.elp.procrastinate()
277
299
  self.last_ts = MonoTime()
300
+ if self._exception is not None:
301
+ ex = self._exception
302
+ self._exception = None
303
+ raise ex
278
304
  return self.el_rval
279
305
 
280
306
  def breakLoop(self, rval=0):
@@ -0,0 +1,30 @@
1
+ # Copyright (c) 2023 Sippy Software, Inc. All rights reserved.
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without modification,
6
+ # are permitted provided that the following conditions are met:
7
+ #
8
+ # 1. Redistributions of source code must retain the above copyright notice, this
9
+ # list of conditions and the following disclaimer.
10
+ #
11
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ # this list of conditions and the following disclaimer in the documentation and/or
13
+ # other materials provided with the distribution.
14
+ #
15
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
19
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
22
+ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+
26
+ from .SipHandlingError import SipHandlingError
27
+
28
+ class RtpProxyError(SipHandlingError):
29
+ code = 502
30
+ msg = 'Bad Gateway'
@@ -0,0 +1,33 @@
1
+ # Copyright (c) 20012-2023 Sippy Software, Inc. All rights reserved.
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without modification,
6
+ # are permitted provided that the following conditions are met:
7
+ #
8
+ # 1. Redistributions of source code must retain the above copyright notice, this
9
+ # list of conditions and the following disclaimer.
10
+ #
11
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ # this list of conditions and the following disclaimer in the documentation and/or
13
+ # other materials provided with the distribution.
14
+ #
15
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
19
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
22
+ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+
26
+ from .SipHandlingError import SipHandlingError
27
+ from .RtpProxyError import RtpProxyError
28
+
29
+ class SdpParseError(SipHandlingError):
30
+ code = 488
31
+ msg = 'Not Acceptable Here'
32
+
33
+ SdpHandlingErrors = (SdpParseError, RtpProxyError)
@@ -24,33 +24,23 @@
24
24
  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
25
 
26
26
  from sippy.SipReason import SipReason
27
- from sippy.SipHeader import SipHeader
28
27
 
29
- class SipParseError(Exception):
28
+ class SipHandlingError(Exception):
29
+ code: int
30
+ msg: str
30
31
  sip_response = None
31
32
  arg = None
32
33
 
33
- def __init__(self, arg, sip_response = None):
34
+ def __init__(self, arg):
34
35
  super().__init__()
35
36
  self.arg = arg
36
- self.sip_response = sip_response
37
37
 
38
38
  def __str__(self):
39
39
  return str(self.arg)
40
40
 
41
- def getResponse(self, req=None):
42
- return self.sip_response
43
-
44
- class SdpParseError(SipParseError):
45
- code = 488
46
- msg = 'Not Acceptable Here'
47
41
  def getResponse(self, req):
48
- if self.sip_response is not None:
49
- return self.sip_response
50
- resp = req.genResponse(self.code, self.msg)
51
- if (reason:=self.getReason()) is not None:
52
- rsh = SipHeader(body = reason)
53
- resp.appendHeader(rsh)
42
+ reason = self.getReason()
43
+ resp = req.genResponse(self.code, self.msg, ext_reason = reason)
54
44
  return resp
55
45
 
56
46
  def getReason(self):