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.
- {sippy-2.0.2/sippy.egg-info → sippy-2.1.0}/PKG-INFO +6 -4
- {sippy-2.0.2 → sippy-2.1.0}/README.md +3 -3
- {sippy-2.0.2 → sippy-2.1.0}/setup.py +2 -2
- {sippy-2.0.2 → sippy-2.1.0}/sippy/CLIManager.py +5 -1
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Core/EventDispatcher.py +10 -10
- sippy-2.1.0/sippy/Exceptions/RtpProxyError.py +38 -0
- sippy-2.1.0/sippy/Exceptions/SipParseError.py +56 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/MsgBody.py +17 -8
- sippy-2.1.0/sippy/MultipartMixBody.py +71 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Rtp_proxy_client.py +1 -1
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Rtp_proxy_client_stream.py +4 -3
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Rtp_proxy_cmd.py +11 -10
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Rtp_proxy_session.py +177 -74
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SdpBody.py +1 -1
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Security/SipNonce.py +3 -13
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipContentType.py +38 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipMsg.py +4 -4
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipRegistrationAgent.py +19 -20
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipRequest.py +2 -2
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipTransactionManager.py +41 -22
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipWWWAuthenticate.py +1 -1
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UA.py +3 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UaStateConnected.py +6 -4
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UacStateIdle.py +3 -1
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UacStateRinging.py +4 -2
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UacStateTrying.py +4 -2
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UacStateUpdating.py +3 -1
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UasStateIdle.py +3 -1
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UasStateRinging.py +4 -3
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UasStateTrying.py +4 -3
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UasStateUpdating.py +4 -3
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Udp_server.py +37 -23
- {sippy-2.0.2 → sippy-2.1.0}/sippy/b2bua_radius.py +2 -2
- {sippy-2.0.2 → sippy-2.1.0}/sippy/b2bua_simple.py +1 -1
- {sippy-2.0.2 → sippy-2.1.0/sippy.egg-info}/PKG-INFO +6 -4
- {sippy-2.0.2 → sippy-2.1.0}/sippy.egg-info/SOURCES.txt +4 -1
- sippy-2.1.0/tests/__init__.py +0 -0
- sippy-2.0.2/sippy/ESipParseException.py +0 -18
- {sippy-2.0.2 → sippy-2.1.0}/LICENSE +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/MANIFEST.in +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/requirements.txt +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/setup.cfg +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/B2BRoute.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/CCEvents.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Core/Exceptions.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Core/__init__.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/ESipHeaderCSV.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/ESipHeaderIgnore.py +0 -0
- {sippy-2.0.2/sippy/Math → sippy-2.1.0/sippy/Exceptions}/__init__.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/External_command.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/FakeAccounting.py +0 -0
- {sippy-2.0.2/sippy/Security → sippy-2.1.0/sippy/Math}/__init__.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Math/recfilter.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/MyConfigParser.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/RadiusAccounting.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/RadiusAuthorisation.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Radius_client.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Rtp_proxy_client_local.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Rtp_proxy_client_net.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Rtp_proxy_client_udp.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SdpConnecton.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SdpGeneric.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SdpMedia.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SdpMediaDescription.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SdpOrigin.py +0 -0
- {sippy-2.0.2/sippy/Time → sippy-2.1.0/sippy/Security}/__init__.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Signal.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipAddress.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipAddressHF.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipAllow.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipAlso.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipAuthorization.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipCCDiversion.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipCSeq.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipCallId.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipCiscoGUID.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipConf.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipContact.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipContentLength.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipDiversion.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipExpires.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipFrom.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipGenericHF.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipHeader.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipLogger.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipMaxForwards.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipNumericHF.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipPAssertedIdentity.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipProxyAuthenticate.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipProxyAuthorization.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipRAck.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipRSeq.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipReason.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipRecordRoute.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipReferTo.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipReferredBy.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipReplaces.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipResponse.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipRoute.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipServer.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipSupported.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipTo.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipURL.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipUserAgent.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipVia.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/SipWarning.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/StatefulProxy.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Time/MonoTime.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Time/Timeout.py +0 -0
- {sippy-2.0.2/tests → sippy-2.1.0/sippy/Time}/__init__.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/Time/clock_dtime.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UaStateDead.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UaStateDisconnected.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UaStateFailed.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UaStateGeneric.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/UacStateCancelling.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/XMPP_server.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/__init__.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/dictionary +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy/misc.py +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy.egg-info/dependency_links.txt +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy.egg-info/entry_points.txt +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy.egg-info/requires.txt +0 -0
- {sippy-2.0.2 → sippy-2.1.0}/sippy.egg-info/top_level.txt +0 -0
- {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
|
|
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
|
[](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
|
|
56
|
+
### Install latest stable version
|
|
55
57
|
|
|
56
|
-
`pip install
|
|
58
|
+
`pip install sippy`
|
|
57
59
|
|
|
58
|
-
### Install
|
|
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
|
|
39
|
+
### Install latest stable version
|
|
40
40
|
|
|
41
|
-
`pip install
|
|
41
|
+
`pip install sippy`
|
|
42
42
|
|
|
43
|
-
### Install
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
269
|
+
break
|
|
272
270
|
self.dispatchTimers()
|
|
273
271
|
if self.endloop:
|
|
274
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
|
106
|
-
s = '%s %s' % (s,
|
|
107
|
-
if
|
|
108
|
-
s = '%s %s' % (s,
|
|
109
|
-
if
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|