sippy 2.2.1__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 (153) hide show
  1. sippy-2.2.2/MANIFEST.in +4 -0
  2. {sippy-2.2.1/sippy.egg-info → sippy-2.2.2}/PKG-INFO +1 -1
  3. {sippy-2.2.1 → sippy-2.2.2}/setup.py +2 -4
  4. sippy-2.2.1/sippy/B2BRoute.py → sippy-2.2.2/sippy/B2B/Route.py +1 -1
  5. sippy-2.2.2/sippy/B2B/SimpleAPI.py +142 -0
  6. sippy-2.2.2/sippy/B2B/States.py +36 -0
  7. {sippy-2.2.1 → sippy-2.2.2}/sippy/CLIManager.py +7 -4
  8. {sippy-2.2.1 → sippy-2.2.2}/sippy/Core/EventDispatcher.py +32 -5
  9. {sippy-2.2.1 → sippy-2.2.2}/sippy/MsgBody.py +1 -0
  10. {sippy-2.2.1 → sippy-2.2.2}/sippy/MultipartMixBody.py +20 -5
  11. {sippy-2.2.1 → sippy-2.2.2}/sippy/MyConfigParser.py +1 -1
  12. sippy-2.2.1/sippy/Rtp_proxy/Client/stream.py → sippy-2.2.2/sippy/Rtp_proxy/Client/Worker/external.py +6 -98
  13. sippy-2.2.2/sippy/Rtp_proxy/Client/Worker/internal.py +95 -0
  14. sippy-2.2.2/sippy/Rtp_proxy/Client/internal.py +67 -0
  15. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/Client/local.py +4 -16
  16. sippy-2.2.2/sippy/Rtp_proxy/Client/stream.py +103 -0
  17. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/Client/udp.py +9 -19
  18. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/Session/side.py +5 -5
  19. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/Session/update.py +1 -0
  20. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/client.py +102 -68
  21. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/session.py +27 -23
  22. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipContentType.py +2 -0
  23. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipTransactionManager.py +20 -4
  24. sippy-2.2.2/sippy/Time/__init__.py +0 -0
  25. {sippy-2.2.1 → sippy-2.2.2}/sippy/UA.py +16 -6
  26. {sippy-2.2.1 → sippy-2.2.2}/sippy/UaStateDead.py +3 -0
  27. {sippy-2.2.1 → sippy-2.2.2}/sippy/b2bua.py +45 -129
  28. {sippy-2.2.1 → sippy-2.2.2/sippy.egg-info}/PKG-INFO +1 -1
  29. {sippy-2.2.1 → sippy-2.2.2}/sippy.egg-info/SOURCES.txt +9 -2
  30. sippy-2.2.2/tests/__init__.py +0 -0
  31. {sippy-2.2.1 → sippy-2.2.2}/tests/test_B2BRoute.py +1 -1
  32. {sippy-2.2.1 → sippy-2.2.2}/tests/test_B2BTransforms.py +1 -1
  33. sippy-2.2.1/MANIFEST.in +0 -1
  34. {sippy-2.2.1 → sippy-2.2.2}/LICENSE +0 -0
  35. {sippy-2.2.1 → sippy-2.2.2}/README.md +0 -0
  36. {sippy-2.2.1 → sippy-2.2.2}/pyproject.toml +0 -0
  37. {sippy-2.2.1 → sippy-2.2.2}/requirements.txt +0 -0
  38. {sippy-2.2.1 → sippy-2.2.2}/setup.cfg +0 -0
  39. /sippy-2.2.1/sippy/B2BTransforms.py → /sippy-2.2.2/sippy/B2B/Transforms.py +0 -0
  40. {sippy-2.2.1/sippy/Core → sippy-2.2.2/sippy/B2B}/__init__.py +0 -0
  41. {sippy-2.2.1 → sippy-2.2.2}/sippy/CCEvents.py +0 -0
  42. {sippy-2.2.1 → sippy-2.2.2}/sippy/Core/Exceptions.py +0 -0
  43. {sippy-2.2.1/sippy/Exceptions → sippy-2.2.2/sippy/Core}/__init__.py +0 -0
  44. {sippy-2.2.1 → sippy-2.2.2}/sippy/ESipHeaderCSV.py +0 -0
  45. {sippy-2.2.1 → sippy-2.2.2}/sippy/ESipHeaderIgnore.py +0 -0
  46. {sippy-2.2.1 → sippy-2.2.2}/sippy/Exceptions/RtpProxyError.py +0 -0
  47. {sippy-2.2.1 → sippy-2.2.2}/sippy/Exceptions/SdpParseError.py +0 -0
  48. {sippy-2.2.1 → sippy-2.2.2}/sippy/Exceptions/SipHandlingError.py +0 -0
  49. {sippy-2.2.1 → sippy-2.2.2}/sippy/Exceptions/SipParseError.py +0 -0
  50. {sippy-2.2.1/sippy/Math → sippy-2.2.2/sippy/Exceptions}/__init__.py +0 -0
  51. {sippy-2.2.1 → sippy-2.2.2}/sippy/External_command.py +0 -0
  52. {sippy-2.2.1 → sippy-2.2.2}/sippy/FakeAccounting.py +0 -0
  53. {sippy-2.2.1/sippy/Security → sippy-2.2.2/sippy/Math}/__init__.py +0 -0
  54. {sippy-2.2.1 → sippy-2.2.2}/sippy/Math/recfilter.py +0 -0
  55. {sippy-2.2.1 → sippy-2.2.2}/sippy/Network_server.py +0 -0
  56. {sippy-2.2.1 → sippy-2.2.2}/sippy/RadiusAccounting.py +0 -0
  57. {sippy-2.2.1 → sippy-2.2.2}/sippy/RadiusAuthorisation.py +0 -0
  58. {sippy-2.2.1 → sippy-2.2.2}/sippy/Radius_client.py +0 -0
  59. {sippy-2.2.1/sippy/Time → sippy-2.2.2/sippy/Rtp_proxy/Client/Worker}/__init__.py +0 -0
  60. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/Client/__init__.py +0 -0
  61. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/Client/net.py +0 -0
  62. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/Cmd/__init__.py +0 -0
  63. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/Cmd/sequencer.py +0 -0
  64. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/Session/__init__.py +0 -0
  65. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/Session/subcommand.py +0 -0
  66. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/Session/subcommand_ice.py +0 -0
  67. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/Session/webrtc.py +0 -0
  68. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/__init__.py +0 -0
  69. {sippy-2.2.1 → sippy-2.2.2}/sippy/Rtp_proxy/cmd.py +0 -0
  70. {sippy-2.2.1 → sippy-2.2.2}/sippy/SdpBody.py +0 -0
  71. {sippy-2.2.1 → sippy-2.2.2}/sippy/SdpConnecton.py +0 -0
  72. {sippy-2.2.1 → sippy-2.2.2}/sippy/SdpGeneric.py +0 -0
  73. {sippy-2.2.1 → sippy-2.2.2}/sippy/SdpMedia.py +0 -0
  74. {sippy-2.2.1 → sippy-2.2.2}/sippy/SdpMediaDescription.py +0 -0
  75. {sippy-2.2.1 → sippy-2.2.2}/sippy/SdpOrigin.py +0 -0
  76. {sippy-2.2.1 → sippy-2.2.2}/sippy/Security/SipNonce.py +0 -0
  77. {sippy-2.2.1/tests → sippy-2.2.2/sippy/Security}/__init__.py +0 -0
  78. {sippy-2.2.1 → sippy-2.2.2}/sippy/Signal.py +0 -0
  79. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipAddress.py +0 -0
  80. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipAddressHF.py +0 -0
  81. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipAllow.py +0 -0
  82. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipAlso.py +0 -0
  83. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipAuthorization.py +0 -0
  84. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipCCDiversion.py +0 -0
  85. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipCSeq.py +0 -0
  86. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipCallId.py +0 -0
  87. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipCiscoGUID.py +0 -0
  88. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipConf.py +0 -0
  89. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipContact.py +0 -0
  90. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipContentLength.py +0 -0
  91. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipDiversion.py +0 -0
  92. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipExpires.py +0 -0
  93. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipFrom.py +0 -0
  94. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipGenericHF.py +0 -0
  95. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipHeader.py +0 -0
  96. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipLogger.py +0 -0
  97. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipMaxForwards.py +0 -0
  98. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipMsg.py +0 -0
  99. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipNumericHF.py +0 -0
  100. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipPAssertedIdentity.py +0 -0
  101. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipPath.py +0 -0
  102. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipProxyAuthenticate.py +0 -0
  103. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipProxyAuthorization.py +0 -0
  104. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipRAck.py +0 -0
  105. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipRSeq.py +0 -0
  106. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipReason.py +0 -0
  107. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipRecordRoute.py +0 -0
  108. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipReferTo.py +0 -0
  109. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipReferredBy.py +0 -0
  110. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipRegistrationAgent.py +0 -0
  111. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipReplaces.py +0 -0
  112. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipRequest.py +0 -0
  113. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipResponse.py +0 -0
  114. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipRoute.py +0 -0
  115. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipServer.py +0 -0
  116. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipSupported.py +0 -0
  117. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipTo.py +0 -0
  118. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipURL.py +0 -0
  119. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipUserAgent.py +0 -0
  120. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipVia.py +0 -0
  121. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipWWWAuthenticate.py +0 -0
  122. {sippy-2.2.1 → sippy-2.2.2}/sippy/SipWarning.py +0 -0
  123. {sippy-2.2.1 → sippy-2.2.2}/sippy/StatefulProxy.py +0 -0
  124. {sippy-2.2.1 → sippy-2.2.2}/sippy/Time/MonoTime.py +0 -0
  125. {sippy-2.2.1 → sippy-2.2.2}/sippy/Time/Timeout.py +0 -0
  126. {sippy-2.2.1 → sippy-2.2.2}/sippy/Time/clock_dtime.py +0 -0
  127. {sippy-2.2.1 → sippy-2.2.2}/sippy/UaStateConnected.py +0 -0
  128. {sippy-2.2.1 → sippy-2.2.2}/sippy/UaStateDisconnected.py +0 -0
  129. {sippy-2.2.1 → sippy-2.2.2}/sippy/UaStateFailed.py +0 -0
  130. {sippy-2.2.1 → sippy-2.2.2}/sippy/UaStateGeneric.py +0 -0
  131. {sippy-2.2.1 → sippy-2.2.2}/sippy/UacStateCancelling.py +0 -0
  132. {sippy-2.2.1 → sippy-2.2.2}/sippy/UacStateIdle.py +0 -0
  133. {sippy-2.2.1 → sippy-2.2.2}/sippy/UacStateRinging.py +0 -0
  134. {sippy-2.2.1 → sippy-2.2.2}/sippy/UacStateTrying.py +0 -0
  135. {sippy-2.2.1 → sippy-2.2.2}/sippy/UacStateUpdating.py +0 -0
  136. {sippy-2.2.1 → sippy-2.2.2}/sippy/UasStateIdle.py +0 -0
  137. {sippy-2.2.1 → sippy-2.2.2}/sippy/UasStateRinging.py +0 -0
  138. {sippy-2.2.1 → sippy-2.2.2}/sippy/UasStateTrying.py +0 -0
  139. {sippy-2.2.1 → sippy-2.2.2}/sippy/UasStateUpdating.py +0 -0
  140. {sippy-2.2.1 → sippy-2.2.2}/sippy/Udp_server.py +0 -0
  141. {sippy-2.2.1 → sippy-2.2.2}/sippy/Wss_server.py +0 -0
  142. {sippy-2.2.1 → sippy-2.2.2}/sippy/XMPP_server.py +0 -0
  143. {sippy-2.2.1 → sippy-2.2.2}/sippy/__init__.py +0 -0
  144. {sippy-2.2.1 → sippy-2.2.2}/sippy/b2bua_simple.py +0 -0
  145. {sippy-2.2.1 → sippy-2.2.2}/sippy/dictionary +0 -0
  146. {sippy-2.2.1 → sippy-2.2.2}/sippy/misc.py +0 -0
  147. {sippy-2.2.1 → sippy-2.2.2}/sippy.egg-info/dependency_links.txt +0 -0
  148. {sippy-2.2.1 → sippy-2.2.2}/sippy.egg-info/entry_points.txt +0 -0
  149. {sippy-2.2.1 → sippy-2.2.2}/sippy.egg-info/requires.txt +0 -0
  150. {sippy-2.2.1 → sippy-2.2.2}/sippy.egg-info/top_level.txt +0 -0
  151. {sippy-2.2.1 → sippy-2.2.2}/tests/test_Multipart.py +0 -0
  152. {sippy-2.2.1 → sippy-2.2.2}/tests/test_SdbBody.py +0 -0
  153. {sippy-2.2.1 → 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
1
  Metadata-Version: 2.4
2
2
  Name: sippy
3
- Version: 2.2.1
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.
@@ -8,13 +8,11 @@ with open("README.md", "r") as fh:
8
8
 
9
9
  setup(
10
10
  name = "sippy",
11
- version = "2.2.1",
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 = {
@@ -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
@@ -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'
@@ -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()
@@ -174,6 +174,11 @@ class _CLIManager_w(Thread):
174
174
  except BrokenPipeError:
175
175
  self.clim = None
176
176
  return
177
+ except socket.error as why:
178
+ if why.errno != EBADF:
179
+ raise
180
+ self.clim = None
181
+ return
177
182
  if res == len(wbuffer):
178
183
  break
179
184
  if res > 0:
@@ -239,14 +244,12 @@ class CLIManager(object):
239
244
  clientsock = None
240
245
  command_cb = None
241
246
  close_pendind = False
242
- raddr = None
243
247
  wthr = None
244
248
  rthr = None
245
249
 
246
- def __init__(self, clientsock, command_cb, raddr):
250
+ def __init__(self, clientsock, command_cb):
247
251
  self.clientsock = clientsock
248
252
  self.command_cb = command_cb
249
- self.raddr = raddr
250
253
  self.wthr = _CLIManager_w(clientsock, self)
251
254
  self.rthr = _CLIManager_r(clientsock, self)
252
255
 
@@ -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:
@@ -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):
@@ -47,6 +47,7 @@ class MsgBody(object):
47
47
  if content != None:
48
48
  self.mtype = mtype
49
49
  self.content = content
50
+ self.parsed = type(content) not in str_types
50
51
  return
51
52
  if cself is not None:
52
53
  if type(cself.content) in str_types:
@@ -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.boundary = ctype.params["boundary"]
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.B2BTransforms import getTransProc
36
+ from sippy.B2B.Transforms import getTransProc
37
37
 
38
38
  SUPPORTED_OPTIONS = { \
39
39
  'acct_enable': ('B', 'enable or disable Radius accounting'), \
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
2
- # Copyright (c) 2006-2014 Sippy Software, Inc. All rights reserved.
2
+ # Copyright (c) 2006-2025 Sippy Software, Inc. All rights reserved.
3
3
  #
4
4
  # All rights reserved.
5
5
  #
@@ -24,24 +24,17 @@
24
24
  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
25
  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
26
 
27
- from queue import Queue
28
-
29
- from sippy.Time.Timeout import Timeout
30
27
  from threading import Thread
31
28
  from errno import EINTR, EPIPE, ENOTCONN, ECONNRESET
32
- from sippy.Time.MonoTime import MonoTime
33
- from sippy.Math.recfilter import recfilter
34
- from sippy.Rtp_proxy.Client.net import Rtp_proxy_client_net
35
- from sippy.Rtp_proxy.cmd import Rtp_proxy_cmd
36
-
37
29
  import socket
38
30
 
31
+ from sippy.Time.MonoTime import MonoTime
39
32
  from sippy.Core.Exceptions import dump_exception
40
33
  from sippy.Core.EventDispatcher import ED2
41
34
 
42
35
  _MAX_RECURSE = 10
43
36
 
44
- class _RTPPLWorker(Thread):
37
+ class RTPPLWorker_external(Thread):
45
38
  daemon = True
46
39
  userv = None
47
40
  s = None
@@ -84,7 +77,7 @@ class _RTPPLWorker(Thread):
84
77
  rval = self.s.recv(1024)
85
78
  if len(rval) == 0:
86
79
  self.s = None
87
- return self.send_raw(command, _recurse, stime, why)
80
+ return self.send_raw(command, _recurse, stime)
88
81
  rval = rval.decode('ascii').strip()
89
82
  break
90
83
  except socket.error as why:
@@ -113,7 +106,7 @@ class _RTPPLWorker(Thread):
113
106
  if len(data) == 0:
114
107
  data, rtpc_delay = None, None
115
108
  except Exception as e:
116
- dump_exception('Rtp_proxy_client_stream: unhandled exception I/O RTPproxy')
109
+ dump_exception('RTPPLWorker_external: unhandled exception I/O RTPproxy')
117
110
  data, rtpc_delay = None, None
118
111
  if result_callback != None:
119
112
  ED2.callFromThread(self.dispatch, result_callback, data, callback_parameters)
@@ -125,89 +118,4 @@ class _RTPPLWorker(Thread):
125
118
  try:
126
119
  result_callback(data, *callback_parameters)
127
120
  except:
128
- dump_exception('Rtp_proxy_client_stream: unhandled exception when processing RTPproxy reply')
129
-
130
- class Rtp_proxy_client_stream(Rtp_proxy_client_net):
131
- is_local = None
132
- wi = None
133
- nworkers = None
134
- nworkers_act = None
135
- workers = None
136
- delay_flt = None
137
- family = None
138
- sock_type = socket.SOCK_STREAM
139
-
140
- def __init__(self, global_config, address = '/var/run/rtpproxy.sock', \
141
- bind_address = None, nworkers = 1, family = socket.AF_UNIX):
142
- #print('Rtp_proxy_client_stream.__init__', address, bind_address, nworkers, family)
143
- if family == socket.AF_UNIX:
144
- self.is_local = True
145
- self.address = address
146
- else:
147
- self.is_local = False
148
- self.address = self.getdestbyaddr(address, family)
149
- self.family = family
150
- self.wi = Queue()
151
- self.nworkers = nworkers
152
- self.workers = []
153
- for i in range(0, self.nworkers):
154
- try:
155
- self.workers.append(_RTPPLWorker(self))
156
- except:
157
- break
158
- self.nworkers_act = i + 1
159
- self.delay_flt = recfilter(0.95, 0.25)
160
-
161
- def send_command(self, command, result_callback = None, *callback_parameters):
162
- if self.nworkers_act == 0:
163
- self.rtpp_class._reconnect(self, self.address)
164
- if isinstance(command, Rtp_proxy_cmd):
165
- command = str(command)
166
- elif not command.endswith('\n'):
167
- command += '\n'
168
- self.wi.put((command, result_callback, callback_parameters))
169
-
170
- def reconnect(self, address, bind_address = None):
171
- if not self.is_local:
172
- address = self.getdestbyaddr(address, self.family)
173
- self.rtpp_class._reconnect(self, address, bind_address)
174
-
175
- def _reconnect(self, address, bind_address = None):
176
- Rtp_proxy_client_stream.shutdown(self)
177
- self.address = address
178
- self.workers = []
179
- for i in range(0, self.nworkers):
180
- try:
181
- self.workers.append(_RTPPLWorker(self))
182
- except:
183
- break
184
- self.nworkers_act = i + 1
185
- self.delay_flt = recfilter(0.95, 0.25)
186
-
187
- def shutdown(self):
188
- self.wi.put(None)
189
- for rworker in self.workers:
190
- rworker.join()
191
- self.workers = None
192
-
193
- def register_delay(self, rtpc_delay):
194
- self.delay_flt.apply(rtpc_delay)
195
-
196
- def get_rtpc_delay(self):
197
- return self.delay_flt.lastval
198
-
199
- if __name__ == '__main__':
200
- class robj(object):
201
- rval = None
202
- r = robj()
203
- def display(res, ro, arg):
204
- print(res, arg)
205
- ro.rval = (res, arg)
206
- ED2.breakLoop()
207
- r = Rtp_proxy_client_stream({'_sip_address':'1.2.3.4'})
208
- r.send_command('VF 123456', display, r, 'abcd')
209
- ED2.loop()
210
- r.shutdown()
211
- print(r.rval)
212
- assert(r.rval == (u'0', 'abcd'))
213
- print('passed')
121
+ dump_exception('RTPPLWorker_external: unhandled exception when processing RTPproxy reply')
@@ -0,0 +1,95 @@
1
+ # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
2
+ # Copyright (c) 2006-2025 Sippy Software, Inc. All rights reserved.
3
+ #
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without modification,
7
+ # are permitted provided that the following conditions are met:
8
+ #
9
+ # 1. Redistributions of source code must retain the above copyright notice, this
10
+ # list of conditions and the following disclaimer.
11
+ #
12
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ # this list of conditions and the following disclaimer in the documentation and/or
14
+ # other materials provided with the distribution.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
20
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23
+ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+
27
+ from threading import Thread
28
+ from errno import EINTR
29
+ import socket
30
+
31
+ from sippy.Time.MonoTime import MonoTime
32
+ from sippy.Core.Exceptions import dump_exception
33
+ from sippy.Core.EventDispatcher import ED2
34
+
35
+ class RTPPLWorker_internal(Thread):
36
+ daemon = True
37
+ userv = None
38
+ s = None
39
+
40
+ def __init__(self, userv):
41
+ Thread.__init__(self)
42
+ self.userv = userv
43
+ self.s = userv.address
44
+ self.start()
45
+
46
+ def send_raw(self, command, stime = None):
47
+ #print('%s.send_raw(%s)' % (id(self), command))
48
+ if stime == None:
49
+ stime = MonoTime()
50
+ while True:
51
+ try:
52
+ self.s.send(command.encode())
53
+ except socket.error as why:
54
+ if why.errno == EINTR:
55
+ continue
56
+ raise why
57
+ else: break
58
+ while True:
59
+ try:
60
+ rval = self.s.recv(1024)
61
+ if len(rval) == 0:
62
+ raise socket.error('Connection closed')
63
+ rval = rval.decode('ascii').strip()
64
+ except socket.error as why:
65
+ if why.errno == EINTR:
66
+ continue
67
+ raise why
68
+ else: break
69
+ rtpc_delay = stime.offsetFromNow()
70
+ return (rval, rtpc_delay)
71
+
72
+ def run(self):
73
+ #print(self.run, 'enter')
74
+ while True:
75
+ #print(self.run, 'spin')
76
+ wi = self.userv.wi.get()
77
+ if wi == None:
78
+ # Shutdown request, relay it further
79
+ self.userv.wi.put(None)
80
+ if wi == None:
81
+ break
82
+ command, result_callback, callback_parameters = wi
83
+ try:
84
+ data, rtpc_delay = self.send_raw(command)
85
+ if len(data) == 0:
86
+ data, rtpc_delay = None, None
87
+ except Exception as e:
88
+ dump_exception('RTPPLWorker_internal: unhandled exception I/O RTPproxy')
89
+ data, rtpc_delay = None, None
90
+ if result_callback != None:
91
+ ED2.callFromThread(result_callback, data, *callback_parameters)
92
+ if rtpc_delay != None:
93
+ ED2.callFromThread(self.userv.register_delay, rtpc_delay)
94
+ self.userv = None
95
+ self.s = None