sippy 2.0.2__tar.gz → 2.1.0__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 (125) hide show
  1. {sippy-2.0.2/sippy.egg-info → sippy-2.1.0}/PKG-INFO +6 -4
  2. {sippy-2.0.2 → sippy-2.1.0}/README.md +3 -3
  3. {sippy-2.0.2 → sippy-2.1.0}/setup.py +2 -2
  4. {sippy-2.0.2 → sippy-2.1.0}/sippy/CLIManager.py +5 -1
  5. {sippy-2.0.2 → sippy-2.1.0}/sippy/Core/EventDispatcher.py +10 -10
  6. sippy-2.1.0/sippy/Exceptions/RtpProxyError.py +38 -0
  7. sippy-2.1.0/sippy/Exceptions/SipParseError.py +56 -0
  8. {sippy-2.0.2 → sippy-2.1.0}/sippy/MsgBody.py +17 -8
  9. sippy-2.1.0/sippy/MultipartMixBody.py +71 -0
  10. {sippy-2.0.2 → sippy-2.1.0}/sippy/Rtp_proxy_client.py +1 -1
  11. {sippy-2.0.2 → sippy-2.1.0}/sippy/Rtp_proxy_client_stream.py +4 -3
  12. {sippy-2.0.2 → sippy-2.1.0}/sippy/Rtp_proxy_cmd.py +11 -10
  13. {sippy-2.0.2 → sippy-2.1.0}/sippy/Rtp_proxy_session.py +177 -74
  14. {sippy-2.0.2 → sippy-2.1.0}/sippy/SdpBody.py +1 -1
  15. {sippy-2.0.2 → sippy-2.1.0}/sippy/Security/SipNonce.py +3 -13
  16. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipContentType.py +38 -0
  17. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipMsg.py +4 -4
  18. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipRegistrationAgent.py +19 -20
  19. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipRequest.py +2 -2
  20. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipTransactionManager.py +41 -22
  21. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipWWWAuthenticate.py +1 -1
  22. {sippy-2.0.2 → sippy-2.1.0}/sippy/UA.py +3 -0
  23. {sippy-2.0.2 → sippy-2.1.0}/sippy/UaStateConnected.py +6 -4
  24. {sippy-2.0.2 → sippy-2.1.0}/sippy/UacStateIdle.py +3 -1
  25. {sippy-2.0.2 → sippy-2.1.0}/sippy/UacStateRinging.py +4 -2
  26. {sippy-2.0.2 → sippy-2.1.0}/sippy/UacStateTrying.py +4 -2
  27. {sippy-2.0.2 → sippy-2.1.0}/sippy/UacStateUpdating.py +3 -1
  28. {sippy-2.0.2 → sippy-2.1.0}/sippy/UasStateIdle.py +3 -1
  29. {sippy-2.0.2 → sippy-2.1.0}/sippy/UasStateRinging.py +4 -3
  30. {sippy-2.0.2 → sippy-2.1.0}/sippy/UasStateTrying.py +4 -3
  31. {sippy-2.0.2 → sippy-2.1.0}/sippy/UasStateUpdating.py +4 -3
  32. {sippy-2.0.2 → sippy-2.1.0}/sippy/Udp_server.py +37 -23
  33. {sippy-2.0.2 → sippy-2.1.0}/sippy/b2bua_radius.py +2 -2
  34. {sippy-2.0.2 → sippy-2.1.0}/sippy/b2bua_simple.py +1 -1
  35. {sippy-2.0.2 → sippy-2.1.0/sippy.egg-info}/PKG-INFO +6 -4
  36. {sippy-2.0.2 → sippy-2.1.0}/sippy.egg-info/SOURCES.txt +4 -1
  37. sippy-2.1.0/tests/__init__.py +0 -0
  38. sippy-2.0.2/sippy/ESipParseException.py +0 -18
  39. {sippy-2.0.2 → sippy-2.1.0}/LICENSE +0 -0
  40. {sippy-2.0.2 → sippy-2.1.0}/MANIFEST.in +0 -0
  41. {sippy-2.0.2 → sippy-2.1.0}/requirements.txt +0 -0
  42. {sippy-2.0.2 → sippy-2.1.0}/setup.cfg +0 -0
  43. {sippy-2.0.2 → sippy-2.1.0}/sippy/B2BRoute.py +0 -0
  44. {sippy-2.0.2 → sippy-2.1.0}/sippy/CCEvents.py +0 -0
  45. {sippy-2.0.2 → sippy-2.1.0}/sippy/Core/Exceptions.py +0 -0
  46. {sippy-2.0.2 → sippy-2.1.0}/sippy/Core/__init__.py +0 -0
  47. {sippy-2.0.2 → sippy-2.1.0}/sippy/ESipHeaderCSV.py +0 -0
  48. {sippy-2.0.2 → sippy-2.1.0}/sippy/ESipHeaderIgnore.py +0 -0
  49. {sippy-2.0.2/sippy/Math → sippy-2.1.0/sippy/Exceptions}/__init__.py +0 -0
  50. {sippy-2.0.2 → sippy-2.1.0}/sippy/External_command.py +0 -0
  51. {sippy-2.0.2 → sippy-2.1.0}/sippy/FakeAccounting.py +0 -0
  52. {sippy-2.0.2/sippy/Security → sippy-2.1.0/sippy/Math}/__init__.py +0 -0
  53. {sippy-2.0.2 → sippy-2.1.0}/sippy/Math/recfilter.py +0 -0
  54. {sippy-2.0.2 → sippy-2.1.0}/sippy/MyConfigParser.py +0 -0
  55. {sippy-2.0.2 → sippy-2.1.0}/sippy/RadiusAccounting.py +0 -0
  56. {sippy-2.0.2 → sippy-2.1.0}/sippy/RadiusAuthorisation.py +0 -0
  57. {sippy-2.0.2 → sippy-2.1.0}/sippy/Radius_client.py +0 -0
  58. {sippy-2.0.2 → sippy-2.1.0}/sippy/Rtp_proxy_client_local.py +0 -0
  59. {sippy-2.0.2 → sippy-2.1.0}/sippy/Rtp_proxy_client_net.py +0 -0
  60. {sippy-2.0.2 → sippy-2.1.0}/sippy/Rtp_proxy_client_udp.py +0 -0
  61. {sippy-2.0.2 → sippy-2.1.0}/sippy/SdpConnecton.py +0 -0
  62. {sippy-2.0.2 → sippy-2.1.0}/sippy/SdpGeneric.py +0 -0
  63. {sippy-2.0.2 → sippy-2.1.0}/sippy/SdpMedia.py +0 -0
  64. {sippy-2.0.2 → sippy-2.1.0}/sippy/SdpMediaDescription.py +0 -0
  65. {sippy-2.0.2 → sippy-2.1.0}/sippy/SdpOrigin.py +0 -0
  66. {sippy-2.0.2/sippy/Time → sippy-2.1.0/sippy/Security}/__init__.py +0 -0
  67. {sippy-2.0.2 → sippy-2.1.0}/sippy/Signal.py +0 -0
  68. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipAddress.py +0 -0
  69. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipAddressHF.py +0 -0
  70. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipAllow.py +0 -0
  71. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipAlso.py +0 -0
  72. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipAuthorization.py +0 -0
  73. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipCCDiversion.py +0 -0
  74. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipCSeq.py +0 -0
  75. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipCallId.py +0 -0
  76. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipCiscoGUID.py +0 -0
  77. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipConf.py +0 -0
  78. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipContact.py +0 -0
  79. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipContentLength.py +0 -0
  80. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipDiversion.py +0 -0
  81. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipExpires.py +0 -0
  82. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipFrom.py +0 -0
  83. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipGenericHF.py +0 -0
  84. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipHeader.py +0 -0
  85. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipLogger.py +0 -0
  86. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipMaxForwards.py +0 -0
  87. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipNumericHF.py +0 -0
  88. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipPAssertedIdentity.py +0 -0
  89. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipProxyAuthenticate.py +0 -0
  90. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipProxyAuthorization.py +0 -0
  91. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipRAck.py +0 -0
  92. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipRSeq.py +0 -0
  93. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipReason.py +0 -0
  94. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipRecordRoute.py +0 -0
  95. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipReferTo.py +0 -0
  96. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipReferredBy.py +0 -0
  97. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipReplaces.py +0 -0
  98. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipResponse.py +0 -0
  99. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipRoute.py +0 -0
  100. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipServer.py +0 -0
  101. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipSupported.py +0 -0
  102. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipTo.py +0 -0
  103. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipURL.py +0 -0
  104. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipUserAgent.py +0 -0
  105. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipVia.py +0 -0
  106. {sippy-2.0.2 → sippy-2.1.0}/sippy/SipWarning.py +0 -0
  107. {sippy-2.0.2 → sippy-2.1.0}/sippy/StatefulProxy.py +0 -0
  108. {sippy-2.0.2 → sippy-2.1.0}/sippy/Time/MonoTime.py +0 -0
  109. {sippy-2.0.2 → sippy-2.1.0}/sippy/Time/Timeout.py +0 -0
  110. {sippy-2.0.2/tests → sippy-2.1.0/sippy/Time}/__init__.py +0 -0
  111. {sippy-2.0.2 → sippy-2.1.0}/sippy/Time/clock_dtime.py +0 -0
  112. {sippy-2.0.2 → sippy-2.1.0}/sippy/UaStateDead.py +0 -0
  113. {sippy-2.0.2 → sippy-2.1.0}/sippy/UaStateDisconnected.py +0 -0
  114. {sippy-2.0.2 → sippy-2.1.0}/sippy/UaStateFailed.py +0 -0
  115. {sippy-2.0.2 → sippy-2.1.0}/sippy/UaStateGeneric.py +0 -0
  116. {sippy-2.0.2 → sippy-2.1.0}/sippy/UacStateCancelling.py +0 -0
  117. {sippy-2.0.2 → sippy-2.1.0}/sippy/XMPP_server.py +0 -0
  118. {sippy-2.0.2 → sippy-2.1.0}/sippy/__init__.py +0 -0
  119. {sippy-2.0.2 → sippy-2.1.0}/sippy/dictionary +0 -0
  120. {sippy-2.0.2 → sippy-2.1.0}/sippy/misc.py +0 -0
  121. {sippy-2.0.2 → sippy-2.1.0}/sippy.egg-info/dependency_links.txt +0 -0
  122. {sippy-2.0.2 → sippy-2.1.0}/sippy.egg-info/entry_points.txt +0 -0
  123. {sippy-2.0.2 → sippy-2.1.0}/sippy.egg-info/requires.txt +0 -0
  124. {sippy-2.0.2 → sippy-2.1.0}/sippy.egg-info/top_level.txt +0 -0
  125. {sippy-2.0.2 → sippy-2.1.0}/tests/test_SdbBody.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sippy
3
- Version: 2.0.2
3
+ Version: 2.1.0
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.
@@ -12,6 +12,8 @@ Classifier: Operating System :: POSIX
12
12
  Classifier: Programming Language :: Python
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
+ Requires-Dist: ElPeriodic>=1.1
16
+ Requires-Dist: pycryptodome
15
17
 
16
18
  [![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++)
17
19
 
@@ -51,11 +53,11 @@ path.
51
53
 
52
54
  ## Installation
53
55
 
54
- ### Install libelperiodic (if not installed)
56
+ ### Install latest stable version
55
57
 
56
- `pip install ElPeriodic`
58
+ `pip install sippy`
57
59
 
58
- ### Install B2BUA
60
+ ### Install latest development version
59
61
 
60
62
  `pip install git+https://github.com/sippy/b2bua`
61
63
 
@@ -36,11 +36,11 @@ path.
36
36
 
37
37
  ## Installation
38
38
 
39
- ### Install libelperiodic (if not installed)
39
+ ### Install latest stable version
40
40
 
41
- `pip install ElPeriodic`
41
+ `pip install sippy`
42
42
 
43
- ### Install B2BUA
43
+ ### Install latest development version
44
44
 
45
45
  `pip install git+https://github.com/sippy/b2bua`
46
46
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env python2
1
+ #!/usr/bin/env python
2
2
  from setuptools import setup, find_packages
3
3
 
4
4
  requirements = [x.strip() for x in open("requirements.txt", "r").readlines()]
@@ -8,7 +8,7 @@ with open("README.md", "r") as fh:
8
8
 
9
9
  setup(
10
10
  name = "sippy",
11
- version = "2.0.2",
11
+ version = "2.1.0",
12
12
  packages = find_packages(),
13
13
 
14
14
  install_requires = requirements,
@@ -253,6 +253,8 @@ class CLIManager(object):
253
253
  self.close()
254
254
 
255
255
  def send(self, data):
256
+ if self.wthr is None:
257
+ return
256
258
  if not isinstance(data, _uobj):
257
259
  data = data.encode('ascii')
258
260
  self.wthr.send(data)
@@ -261,10 +263,12 @@ class CLIManager(object):
261
263
  return self.send(data)
262
264
 
263
265
  def close(self):
266
+ if self.wthr is None:
267
+ return
264
268
  self.wthr.shutdown(soft = True)
265
269
 
266
270
  def shutdown(self):
267
- if self.wthr == None:
271
+ if self.wthr is None:
268
272
  return
269
273
  self.wthr.shutdown()
270
274
  try:
@@ -26,15 +26,13 @@
26
26
 
27
27
  from __future__ import print_function
28
28
 
29
+ from functools import partial
29
30
  from datetime import datetime
30
31
  from heapq import heappush, heappop, heapify
31
32
  from threading import Lock
32
33
  from random import random
33
34
  import sys, traceback, signal
34
- if sys.version_info[0] < 3:
35
- from thread import get_ident
36
- else:
37
- from _thread import get_ident
35
+ from _thread import get_ident
38
36
  from sippy.Time.MonoTime import MonoTime
39
37
  from sippy.Core.Exceptions import dump_exception, StdException
40
38
 
@@ -67,7 +65,8 @@ class EventListener(object):
67
65
  self.randomize_runs = None
68
66
 
69
67
  def get_randomizer(self, p):
70
- return lambda x: x * (1.0 + p * (1.0 - 2.0 * random()))
68
+ def randomizer(p, x): return x * (1.0 + p * (1.0 - 2.0 * random()))
69
+ return partial(randomizer, p)
71
70
 
72
71
  def spread_runs(self, p):
73
72
  self.randomize_runs = self.get_randomizer(p)
@@ -119,6 +118,7 @@ class EventDispatcher2(Singleton):
119
118
  tlisteners = None
120
119
  slisteners = None
121
120
  endloop = False
121
+ el_rval = None
122
122
  signals_pending = None
123
123
  twasted = 0
124
124
  tcbs_lock = None
@@ -265,13 +265,11 @@ class EventDispatcher2(Singleton):
265
265
  while True:
266
266
  if len(self.signals_pending) > 0:
267
267
  self.dispatchSignals()
268
- if self.endloop:
269
- return
270
268
  if self.endloop:
271
- return
269
+ break
272
270
  self.dispatchTimers()
273
271
  if self.endloop:
274
- return
272
+ break
275
273
  if self.twasted * 2 > len(self.tlisteners):
276
274
  # Clean-up removed timers when their share becomes more than 50%
277
275
  self.tlisteners = [x for x in self.tlisteners if x.cb_func != None]
@@ -282,9 +280,11 @@ class EventDispatcher2(Singleton):
282
280
  break
283
281
  self.elp.procrastinate()
284
282
  self.last_ts = MonoTime()
283
+ return self.el_rval
285
284
 
286
- def breakLoop(self):
285
+ def breakLoop(self, rval=0):
287
286
  self.endloop = True
287
+ self.el_rval = rval
288
288
  #print('breakLoop')
289
289
  #import traceback
290
290
  #import sys
@@ -0,0 +1,38 @@
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 sippy.Exceptions.SipParseError import SdpParseError
27
+
28
+ class RtpProxyError(Exception):
29
+ sip_response = None
30
+ arg = None
31
+ code = 502
32
+ msg = 'Bad Gateway'
33
+ def __init__(self, arg):
34
+ super().__init__()
35
+ self.arg = arg
36
+
37
+ def getResponse(self, req):
38
+ return SdpParseError.getResponse(self, req)
@@ -0,0 +1,56 @@
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 sippy.SipReason import SipReason
27
+ from sippy.SipHeader import SipHeader
28
+
29
+ class SipParseError(Exception):
30
+ sip_response = None
31
+ arg = None
32
+
33
+ def __init__(self, arg, sip_response = None):
34
+ super().__init__()
35
+ self.arg = arg
36
+ self.sip_response = sip_response
37
+
38
+ def __str__(self):
39
+ return str(self.arg)
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
+ 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 self.arg is not None and len(self.arg) > 0:
52
+ reason = SipReason(protocol='SIP', cause=self.code,
53
+ reason=self.arg)
54
+ rsh = SipHeader(body = reason)
55
+ resp.appendHeader(rsh)
56
+ return resp
@@ -26,8 +26,7 @@
26
26
  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
 
28
28
  from sippy.SdpBody import SdpBody
29
-
30
- b_types = {'application/sdp':SdpBody}
29
+ from sippy.SipContentType import SipContentType
31
30
 
32
31
  try:
33
32
  # Python < 3
@@ -35,28 +34,35 @@ try:
35
34
  except NameError:
36
35
  str_types = (str,)
37
36
 
37
+ DEFAULT_CTYPE = SipContentType('application/sdp')
38
+ DEFAULT_CTYPE.parse()
39
+
38
40
  class MsgBody(object):
39
41
  content = None
40
42
  mtype = None
41
43
  needs_update = True
42
44
  parsed = False
43
45
 
44
- def __init__(self, content = None, mtype = 'application/sdp', cself = None):
46
+ def __init__(self, content = None, mtype = DEFAULT_CTYPE, cself = None):
45
47
  if content != None:
46
48
  self.mtype = mtype
47
49
  self.content = content
48
- else:
50
+ return
51
+ if cself is not None:
49
52
  if type(cself.content) in str_types:
50
53
  self.content = cself.content
51
54
  else:
52
55
  self.content = cself.content.getCopy()
53
- self.mtype = cself.mtype
56
+ self.mtype = cself.mtype.getCopy()
54
57
  self.parsed = True
55
58
 
56
59
  def parse(self):
60
+ b_types = {'application/sdp':SdpBody,
61
+ 'multipart/mixed':MultipartMixBody}
57
62
  if not self.parsed:
58
- if self.mtype in b_types:
59
- self.content = b_types[self.mtype](self.content)
63
+ mtype = self.getType()
64
+ if mtype in b_types:
65
+ self.content = b_types[mtype](self.content, ctype=self.mtype)
60
66
  self.parsed = True
61
67
 
62
68
  def __str__(self):
@@ -68,9 +74,12 @@ class MsgBody(object):
68
74
  return self.content.localStr(local_addr, local_port)
69
75
 
70
76
  def getType(self):
71
- return self.mtype
77
+ return self.mtype.name.lower()
72
78
 
73
79
  def getCopy(self):
74
80
  if not self.parsed:
75
81
  return MsgBody(self.content)
76
82
  return MsgBody(cself = self)
83
+
84
+ if not 'MultipartMixBody' in globals():
85
+ from sippy.MultipartMixBody import MultipartMixBody
@@ -0,0 +1,71 @@
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 sippy.SipHeader import SipHeader
27
+
28
+ class MultipartMixBody():
29
+ parts = None
30
+ boundary = None
31
+
32
+ def __init__(self, body = None, ctype = None):
33
+ if body == None:
34
+ return
35
+ sep = f'--{ctype.params["boundary"]}'
36
+ bparts = body.split(sep)
37
+ assert len(bparts) > 2
38
+ assert len(bparts[0]) == 0
39
+ assert bparts[-1].strip() == '--'
40
+ parts = [p.lstrip() for p in bparts[1:-1]]
41
+ self.parts = []
42
+ for sect in parts:
43
+ ct, sect = sect.split('\r\n', 1)
44
+ ct = SipHeader(ct).getBody()
45
+ sect = MsgBody(sect, ct)
46
+ self.parts.append(sect)
47
+ self.boundary = ctype.params["boundary"]
48
+
49
+ def __str__(self):
50
+ bnd = f'--{self.boundary}\r\n'
51
+ parts = [f'{bnd}Content-Type: {p.mtype}\r\n{p}' for p in self.parts]
52
+ s = ''.join(parts)
53
+ s += f'{bnd[:-2]}--\r\n'
54
+ return s
55
+
56
+ def localStr(self, local_addr = None, local_port = None):
57
+ bnd = f'--{self.boundary}\r\n'
58
+ parts = [f'{bnd}Content-Type: {p.mtype}\r\n{p.localStr(local_addr, local_port)}'
59
+ for p in self.parts]
60
+ s = ''.join(parts)
61
+ s += f'{bnd[:-2]}--\r\n'
62
+ return s
63
+
64
+ def getCopy(self):
65
+ cself = MultipartMixBody()
66
+ cself.parts = [p.getCopy() for p in self.parts]
67
+ cself.boundary = self.boundary
68
+ return cself
69
+
70
+ if not 'MsgBody' in globals():
71
+ from sippy.MsgBody import MsgBody
@@ -227,7 +227,7 @@ class Rtp_proxy_client(Rtp_proxy_client_udp, Rtp_proxy_client_stream):
227
227
  return
228
228
  if not self.online:
229
229
  if not self.caps_done:
230
- rtpp_cc = Rtpp_caps_checker(self)
230
+ Rtpp_caps_checker(self)
231
231
  return
232
232
  self.online = True
233
233
  self.heartbeat()
@@ -62,6 +62,7 @@ class _RTPPLWorker(Thread):
62
62
  def send_raw(self, command, _recurse = 0, stime = None):
63
63
  if _recurse > _MAX_RECURSE:
64
64
  raise Exception('Cannot reconnect: %s' % (str(self.userv.address),))
65
+ _recurse += 1
65
66
  if self.s == None:
66
67
  self.connect()
67
68
  #print('%s.send_raw(%s)' % (id(self), command))
@@ -76,14 +77,14 @@ class _RTPPLWorker(Thread):
76
77
  continue
77
78
  elif why.errno in (EPIPE, ENOTCONN, ECONNRESET):
78
79
  self.s = None
79
- return self.send_raw(command, _recurse + 1, stime)
80
+ return self.send_raw(command, _recurse, stime)
80
81
  raise why
81
82
  while True:
82
83
  try:
83
84
  rval = self.s.recv(1024)
84
85
  if len(rval) == 0:
85
86
  self.s = None
86
- return self.send_raw(command, _MAX_RECURSE, stime)
87
+ return self.send_raw(command, _recurse, stime)
87
88
  rval = rval.decode().strip()
88
89
  break
89
90
  except socket.error as why:
@@ -91,7 +92,7 @@ class _RTPPLWorker(Thread):
91
92
  continue
92
93
  elif why.errno in (EPIPE, ENOTCONN, ECONNRESET):
93
94
  self.s = None
94
- return self.send_raw(command, _recurse + 1, stime)
95
+ return self.send_raw(command, _recurse, stime)
95
96
  raise why
96
97
  rtpc_delay = stime.offsetFromNow()
97
98
  return (rval, rtpc_delay)
@@ -63,7 +63,7 @@ class UpdateLookupOpts(object):
63
63
  val = val.strip()
64
64
  if len(val) > 0:
65
65
  self.destination_ip = val
66
- if s[0] == 'L':
66
+ elif s[0] == 'L':
67
67
  val, s = extract_to_next_token(s[1:], ('1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '.'))
68
68
  val = val.strip()
69
69
  if len(val) > 0:
@@ -78,7 +78,7 @@ class UpdateLookupOpts(object):
78
78
  if len(val) > 0:
79
79
  self.otherparams += val
80
80
 
81
- def getstr(self, call_id, swaptags = False):
81
+ def getstr(self, call_id, swaptags = False, skipnotify = False):
82
82
  s = ''
83
83
  if self.destination_ip != None:
84
84
  s += 'R%s' % (self.destination_ip,)
@@ -102,14 +102,15 @@ class UpdateLookupOpts(object):
102
102
  if self.to_tag == None:
103
103
  raise Exception('UpdateLookupOpts::getstr(swaptags = True): to_tag is not set')
104
104
  to_tag, from_tag = (self.from_tag, self.to_tag)
105
- if self.from_tag != None:
106
- s = '%s %s' % (s, self.from_tag)
107
- if self.to_tag != None:
108
- s = '%s %s' % (s, self.to_tag)
109
- if self.notify_socket != None:
110
- s = '%s %s' % (s, self.notify_socket)
111
- if self.notify_tag != None:
112
- s = '%s %s' % (s, self.notify_tag)
105
+ if from_tag != None:
106
+ s = '%s %s' % (s, from_tag)
107
+ if to_tag != None:
108
+ s = '%s %s' % (s, to_tag)
109
+ if not skipnotify:
110
+ if self.notify_socket != None:
111
+ s = '%s %s' % (s, self.notify_socket)
112
+ if self.notify_tag != None:
113
+ s = '%s %s' % (s, self.notify_tag)
113
114
  if self.subargs != None:
114
115
  s = '%s && %s' % (s, self.subargs)
115
116
  return s