├── .github └── workflows │ └── python-wheels.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── apps └── iot │ ├── IoTUAC.py │ ├── IoTUAS.py │ ├── PELIO.py │ ├── RTPGen.py │ ├── pel_collect.py │ ├── pel_read.py │ └── pel_read.sh ├── docker ├── Dockerfile.voiptests ├── install_depends.sh └── set_env.sh ├── documentation ├── Fig1_B2BUA_Architecture.png ├── Fig2_Try_Event.png ├── Fig3_Orig_SIP_UA_generating_INVITE_message_4.png ├── Fig4_Answering_SIP_UA_sending_response_to_originating_end_point.png ├── Fig5_Endpoints_receiving_RTP_media.png ├── Fig6_BYE_message_sent_to_originating_SIP_endpoint.png └── documentation.md ├── pyproject.toml ├── requirements.txt ├── setup.py ├── sippy ├── .gitignore ├── B2B │ ├── Route.py │ ├── SimpleAPI.py │ ├── States.py │ ├── Transforms.py │ └── __init__.py ├── CCEvents.py ├── CLIManager.py ├── Core │ ├── EventDispatcher.py │ ├── Exceptions.py │ └── __init__.py ├── ESipHeaderCSV.py ├── ESipHeaderIgnore.py ├── Exceptions │ ├── RtpProxyError.py │ ├── SdpParseError.py │ ├── SipHandlingError.py │ ├── SipParseError.py │ └── __init__.py ├── External_command.py ├── FakeAccounting.py ├── Math │ ├── __init__.py │ └── recfilter.py ├── MsgBody.py ├── MultipartMixBody.py ├── MyConfigParser.py ├── Network_server.py ├── RadiusAccounting.py ├── RadiusAuthorisation.py ├── Radius_client.py ├── Rtp_proxy │ ├── Client │ │ ├── Worker │ │ │ ├── __init__.py │ │ │ ├── external.py │ │ │ └── internal.py │ │ ├── __init__.py │ │ ├── internal.py │ │ ├── local.py │ │ ├── net.py │ │ ├── stream.py │ │ └── udp.py │ ├── Cmd │ │ ├── __init__.py │ │ └── sequencer.py │ ├── Session │ │ ├── __init__.py │ │ ├── side.py │ │ ├── subcommand.py │ │ ├── subcommand_ice.py │ │ ├── update.py │ │ └── webrtc.py │ ├── __init__.py │ ├── client.py │ ├── cmd.py │ └── session.py ├── SdpBody.py ├── SdpConnecton.py ├── SdpGeneric.py ├── SdpMedia.py ├── SdpMediaDescription.py ├── SdpOrigin.py ├── Security │ ├── SipNonce.py │ └── __init__.py ├── Signal.py ├── SipAddress.py ├── SipAddressHF.py ├── SipAllow.py ├── SipAlso.py ├── SipAuthorization.py ├── SipCCDiversion.py ├── SipCSeq.py ├── SipCallId.py ├── SipCiscoGUID.py ├── SipConf.py ├── SipContact.py ├── SipContentLength.py ├── SipContentType.py ├── SipDiversion.py ├── SipExpires.py ├── SipFrom.py ├── SipGenericHF.py ├── SipHeader.py ├── SipLogger.py ├── SipMaxForwards.py ├── SipMsg.py ├── SipNumericHF.py ├── SipPAssertedIdentity.py ├── SipPath.py ├── SipProxyAuthenticate.py ├── SipProxyAuthorization.py ├── SipRAck.py ├── SipRSeq.py ├── SipReason.py ├── SipRecordRoute.py ├── SipReferTo.py ├── SipReferredBy.py ├── SipRegistrationAgent.py ├── SipReplaces.py ├── SipRequest.py ├── SipResponse.py ├── SipRoute.py ├── SipServer.py ├── SipSupported.py ├── SipTo.py ├── SipTransactionManager.py ├── SipURL.py ├── SipUserAgent.py ├── SipVia.py ├── SipWWWAuthenticate.py ├── SipWarning.py ├── StatefulProxy.py ├── Time │ ├── MonoTime.py │ ├── Timeout.py │ ├── __init__.py │ └── clock_dtime.py ├── UA.py ├── UaStateConnected.py ├── UaStateDead.py ├── UaStateDisconnected.py ├── UaStateFailed.py ├── UaStateGeneric.py ├── UacStateCancelling.py ├── UacStateIdle.py ├── UacStateRinging.py ├── UacStateTrying.py ├── UacStateUpdating.py ├── UasStateIdle.py ├── UasStateRinging.py ├── UasStateTrying.py ├── UasStateUpdating.py ├── Udp_server.py ├── Wss_server.py ├── XMPP_server.py ├── __init__.py ├── b2bua.py ├── b2bua_simple.py ├── dictionary ├── misc.py └── tools │ ├── getmonotime.py │ └── rtpp_query.py └── tests ├── __init__.py ├── test_B2BRoute.py ├── test_B2BTransforms.py ├── test_Multipart.py ├── test_SdbBody.py └── test_Udp_server.py /.github/workflows/python-wheels.yml: -------------------------------------------------------------------------------- 1 | name: Build & Test Python Wheels 2 | 3 | on: 4 | push: 5 | # branches: [ master ] 6 | pull_request: 7 | # branches: [ master ] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | release: 13 | types: [published] 14 | 15 | jobs: 16 | build: 17 | runs-on: ${{ matrix.os }} 18 | 19 | strategy: 20 | matrix: 21 | os: [ubuntu-latest, ubuntu-22.04] 22 | python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13-dev'] 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - name: Set up Python ${{ matrix.python-version }} 28 | uses: actions/setup-python@v5 29 | with: 30 | python-version: ${{ matrix.python-version }} 31 | 32 | - name: Define PYTHON_CMD 33 | run: | 34 | PYTHON_VER="`echo ${{ matrix.python-version }} | sed 's|-dev$||'`" 35 | echo "PYTHON_CMD=python${PYTHON_VER}" >> $GITHUB_ENV 36 | 37 | - name: Install dependencies 38 | run: | 39 | ${PYTHON_CMD} -m pip install --upgrade pip 40 | pip install --upgrade setuptools wheel 41 | pip install -r requirements.txt 42 | 43 | - name: Build, Install & Test 44 | run: | 45 | ${PYTHON_CMD} setup.py build 46 | ${PYTHON_CMD} setup.py sdist 47 | sudo "`which ${PYTHON_CMD}`" setup.py install 48 | pip install dist/sippy*.gz 49 | ${PYTHON_CMD} -m unittest 50 | 51 | set_env: 52 | uses: sippy/cimagic/.github/workflows/GetContainerPlatforms.yml@wip 53 | with: 54 | image: sippylabs/rtpproxy:latest 55 | 56 | voiptests: 57 | runs-on: ubuntu-latest 58 | needs: [set_env, build] 59 | env: 60 | BASE_IMAGE: sippylabs/rtpproxy:latest 61 | TARGETPLATFORM: ${{ matrix.platform }} 62 | strategy: 63 | fail-fast: false 64 | matrix: 65 | platform: ${{ fromJSON(needs.set_env.outputs.build-matrix) }} 66 | steps: 67 | - uses: actions/checkout@v4 68 | 69 | - name: Checkout VoIPTests repo 70 | uses: actions/checkout@v4 71 | with: 72 | repository: 'sippy/voiptests' 73 | path: dist/voiptests 74 | 75 | - name: Checkout RTPProxy repo 76 | uses: actions/checkout@v4 77 | with: 78 | repository: 'sippy/rtpproxy' 79 | path: dist/rtpproxy 80 | 81 | - name: Set up QEMU 82 | if: matrix.platform != 'linux/386' && matrix.platform != 'linux/amd64' 83 | uses: docker/setup-qemu-action@v3 84 | with: 85 | platforms: ${{ env.TARGETPLATFORM }} 86 | 87 | - name: Set up Docker Buildx 88 | uses: docker/setup-buildx-action@v3 89 | 90 | - name: VoIP Tests 91 | uses: docker/build-push-action@v6 92 | with: 93 | context: . 94 | file: ./docker/Dockerfile.voiptests 95 | build-args: | 96 | RTPPC_TYPE=unix 97 | BASE_IMAGE=${{ env.BASE_IMAGE }} 98 | platforms: ${{ env.TARGETPLATFORM }} 99 | 100 | publish_wheels: 101 | needs: [build, voiptests] 102 | runs-on: ubuntu-latest 103 | environment: 104 | name: pypi 105 | url: https://pypi.org/p/sippy 106 | permissions: 107 | id-token: write 108 | steps: 109 | - uses: actions/checkout@v4 110 | 111 | - name: Set up Python 112 | uses: actions/setup-python@v5 113 | with: 114 | python-version: '3.12' 115 | 116 | - name: Install dependencies 117 | run: | 118 | python -m pip install --upgrade pip 119 | pip install --upgrade setuptools wheel 120 | pip install -r requirements.txt 121 | 122 | - name: build 123 | run: python setup.py build sdist 124 | 125 | - name: Publish package distributions to PyPI 126 | if: github.event_name == 'release' && github.event.action == 'published' 127 | uses: pypa/gh-action-pypi-publish@release/v1 128 | 129 | roll_release: 130 | needs: [publish_wheels] 131 | permissions: 132 | contents: write 133 | uses: sippy/cimagic/.github/workflows/RollReleaseDraft.yml@v1 134 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | 56 | *~ 57 | *.swp 58 | 59 | *.orig 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2006-2019, 2003-2005 Maxim Sobolev 4 | Copyright (c) 2006-2019, Sippy Software, Inc., http://www.sippysoft.com 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include requirements.txt 3 | include sippy/dictionary 4 | recursive-include sippy/UI/static * 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 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 | 3 | # Sippy B2BUA 4 | 5 | Sippy B2BUA is a [RFC3261](https://www.ietf.org/rfc/rfc3261.txt)-compliant 6 | Session Initiation Protocol (SIP) stack and 7 | [Back-to-back user agent](http://en.wikipedia.org/wiki/Back-to-back_user_agent) (B2BUA). 8 | 9 | The Sippy B2BUA is a SIP call controlling component. Unlike a SIP proxy server, 10 | which only maintains transaction state, the Sippy B2BUA maintains complete call 11 | state and participates in all call requests. For this reason it can perform 12 | number of functions that are not possible to implement using SIP proxy, such as 13 | for example accurate call accounting, pre-paid rating and billing, fail over 14 | call routing etc. Unlike PBX-type solutions such as Asterisk for example, the 15 | Sippy B2BUA doesn't perform any media relaying or processing, therefore it 16 | doesn't introduce any additional packet loss, delay or jitter into the media 17 | path. 18 | 19 | ## News 20 | 21 | - Support for WebRTC clients has been added via RFC7118-compatible secure 22 | websocket (`wss`) interface. Combined with the functionality provided 23 | by the RTPProxy's `dtls_gw` and `ice_lite` modules allows building 24 | high-capacity WebRTC<->SIP gateways. 25 | 26 | ## Features 27 | 28 | - 5,000-10,000 simultaneous sessions per server 29 | - 150-400 call setups/tear-downs per second 30 | - Real-time calls control and call data records (CDRs) generation 31 | - Optional ability to use [Sippy RTPproxy](https://github.com/sippy/rtpproxy) 32 | for media relaying 33 | - Optional ability to perform Cisco-compatible RADIUS AAA (Authentication, 34 | Authorization and Accounting) 35 | - RFC3261 compliance 36 | - Seamless compatibility with majority of popular SIP software and hardware on 37 | the market today 38 | - Robustness and Resilience 39 | - Simple and clean, yet flexible, internal design making implementing new 40 | features and services easy 41 | - Sippy B2BUA could be easily combined with other Open Source software, such as 42 | SIP Express Router / OpenSIPS to build complete softswitch solution 43 | 44 | ## Installation 45 | 46 | ### Install latest stable version 47 | 48 | `pip install sippy` 49 | 50 | ### Install latest development version 51 | 52 | `pip install git+https://github.com/sippy/b2bua` 53 | 54 | ## Running 55 | 56 | To get started, you can use the b2bua_simple implementation. The following 57 | example will cause the b2bua run in the foreground so you can see the SIP 58 | messaging. If you make a call to the IP address of your host machine, the b2bua 59 | will recieve the call on its UAS side, and it will send a new call leg out its 60 | UAC side to the IP address 192.168.1.1. It is expected that 192.168.1.1 is some 61 | sort of SIP switch or termination gateway. 62 | 63 | `b2bua_simple -f -n 192.168.1.1` 64 | 65 | ## Documentation 66 | 67 | See [documentation/documentation.md](documentation/documentation.md) 68 | 69 | ## Support us on Patreon! 70 | 71 | Sippy B2BUA is available for use at the most liberal Open Source license 72 | there is and we promise to keep it this way. If you like our software, 73 | find it useful and want to support us moving it forward consider 74 | becoming a patron here [Sippy Labs Patreon Page](https://www.patreon.com/sippylabs). 75 | In turn, we would give a priority to bugs and feature requests from our 76 | Patrons. 77 | -------------------------------------------------------------------------------- /apps/iot/pel_collect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.6 2 | # 3 | # Copyright (c) 2015-2018 Sippy Software, Inc. All rights reserved. 4 | # 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without modification, 8 | # are permitted provided that the following conditions are met: 9 | # 10 | # 1. Redistributions of source code must retain the above copyright notice, this 11 | # list of conditions and the following disclaimer. 12 | # 13 | # 2. Redistributions in binary form must reproduce the above copyright notice, 14 | # this list of conditions and the following disclaimer in the documentation and/or 15 | # other materials provided with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 21 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 24 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | from getopt import getopt, GetoptError 29 | import os, sys 30 | 31 | from os.path import dirname, abspath 32 | from inspect import getfile, currentframe 33 | currentdir = dirname(abspath(getfile(currentframe()))) 34 | _parentdir = dirname(currentdir) 35 | parentdir = dirname(_parentdir) 36 | sys.path.insert(0, parentdir) 37 | 38 | #sys.path.append('../..') 39 | 40 | from sippy.misc import daemonize 41 | from sippy.Core.EventDispatcher import ED2 42 | from sippy.SipConf import SipConf 43 | from sippy.SipLogger import SipLogger 44 | 45 | from IoTUAS import IoTUAS 46 | 47 | if __name__ == '__main__': 48 | try: 49 | opts, args = getopt(sys.argv[1:], 'fl:p:n:L:s:u:P:i:') 50 | except GetoptError: 51 | print('usage: pel_collect.py [-l addr] [-p port] [-n addr] [-f] [-L logfile] [-u authname [-P authpass]]\n' \ 52 | ' [-i pidfile]') 53 | sys.exit(1) 54 | laddr = None 55 | lport = None 56 | sdev = None 57 | authname = None 58 | authpass = None 59 | logfile = '/var/log/pel_collect.log' 60 | pidfile = None 61 | global_config = {'nh_addr':['192.168.0.102', 5060]} 62 | foreground = False 63 | for o, a in opts: 64 | if o == '-f': 65 | foreground = True 66 | elif o == '-l': 67 | laddr = a 68 | elif o == '-p': 69 | lport = int(a) 70 | elif o == '-L': 71 | logfile = a 72 | elif o == '-n': 73 | if a.startswith('['): 74 | parts = a.split(']', 1) 75 | global_config['nh_addr'] = [parts[0] + ']', 5060] 76 | parts = parts[1].split(':', 1) 77 | else: 78 | parts = a.split(':', 1) 79 | global_config['nh_addr'] = [parts[0], 5060] 80 | if len(parts) == 2: 81 | global_config['nh_addr'][1] = int(parts[1]) 82 | elif o == '-s': 83 | sdev = a 84 | elif o == '-u': 85 | authname = a 86 | elif o == '-P': 87 | authpass = a 88 | elif o == '-i': 89 | pidfile = a 90 | 91 | if not foreground: 92 | daemonize(logfile) 93 | if logfile == '-': 94 | lfile = sys.stdout 95 | else: 96 | lfile = open(logfile, 'a') 97 | 98 | if pidfile != None: 99 | open(pidfile, 'w').write('%d' % os.getpid()) 100 | 101 | global_config['_sip_address'] = SipConf.my_address 102 | global_config['_sip_port'] = SipConf.my_port 103 | if laddr != None: 104 | global_config['_sip_address'] = laddr 105 | if lport != None: 106 | global_config['_sip_port'] = lport 107 | global_config['_sip_logger'] = SipLogger('pel_collect') 108 | 109 | iua = IoTUAS(global_config, authname, authpass) 110 | #pio = PELIO(lfile) 111 | #if sdev != None: 112 | # pio.sdev = sdev 113 | #pio.sstart_cb = pua.sess_started 114 | #pio.send_cb = pua.sess_ended 115 | #pio.start() 116 | ED2.loop() 117 | -------------------------------------------------------------------------------- /apps/iot/pel_read.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.6 2 | # 3 | # Copyright (c) 2015-2018 Sippy Software, Inc. All rights reserved. 4 | # 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without modification, 8 | # are permitted provided that the following conditions are met: 9 | # 10 | # 1. Redistributions of source code must retain the above copyright notice, this 11 | # list of conditions and the following disclaimer. 12 | # 13 | # 2. Redistributions in binary form must reproduce the above copyright notice, 14 | # this list of conditions and the following disclaimer in the documentation and/or 15 | # other materials provided with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 21 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 24 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | from getopt import getopt, GetoptError 29 | import os, sys 30 | 31 | from os.path import dirname, abspath 32 | from inspect import getfile, currentframe 33 | currentdir = dirname(abspath(getfile(currentframe()))) 34 | _parentdir = dirname(currentdir) 35 | parentdir = dirname(_parentdir) 36 | sys.path.insert(0, parentdir) 37 | 38 | #sys.path.append('../..') 39 | 40 | from sippy.misc import daemonize 41 | from sippy.Core.EventDispatcher import ED2 42 | from sippy.SipConf import SipConf 43 | from sippy.SipLogger import SipLogger 44 | 45 | from PELIO import PELIO 46 | from IoTUAC import IoTUAC 47 | 48 | if __name__ == '__main__': 49 | try: 50 | opts, args = getopt(sys.argv[1:], 'fl:p:n:L:s:u:P:i:') 51 | except GetoptError: 52 | print('usage: pel_read.py [-l addr] [-p port] [-n addr] [-f] [-L logfile] [-s serial] [-u authname [-P authpass]]\n' \ 53 | ' [-i pidfile]') 54 | sys.exit(1) 55 | laddr = None 56 | lport = None 57 | sdev = None 58 | authname = None 59 | authpass = None 60 | logfile = '/var/log/pel_read.log' 61 | pidfile = None 62 | global_config = {'nh_addr':['192.168.0.102', 5060]} 63 | foreground = False 64 | for o, a in opts: 65 | if o == '-f': 66 | foreground = True 67 | elif o == '-l': 68 | laddr = a 69 | elif o == '-p': 70 | lport = int(a) 71 | elif o == '-L': 72 | logfile = a 73 | elif o == '-n': 74 | if a.startswith('['): 75 | parts = a.split(']', 1) 76 | global_config['nh_addr'] = [parts[0] + ']', 5060] 77 | parts = parts[1].split(':', 1) 78 | else: 79 | parts = a.split(':', 1) 80 | global_config['nh_addr'] = [parts[0], 5060] 81 | if len(parts) == 2: 82 | global_config['nh_addr'][1] = int(parts[1]) 83 | elif o == '-s': 84 | sdev = a 85 | elif o == '-u': 86 | authname = a 87 | elif o == '-P': 88 | authpass = a 89 | elif o == '-i': 90 | pidfile = a 91 | 92 | if not foreground: 93 | daemonize(logfile) 94 | if logfile == '-': 95 | lfile = sys.stdout 96 | else: 97 | lfile = open(logfile, 'a') 98 | 99 | if pidfile != None: 100 | open(pidfile, 'w').write('%d' % os.getpid()) 101 | 102 | global_config['_sip_address'] = SipConf.my_address 103 | global_config['_sip_port'] = SipConf.my_port 104 | if laddr != None: 105 | global_config['_sip_address'] = laddr 106 | if lport != None: 107 | global_config['_sip_port'] = lport 108 | global_config['_sip_logger'] = SipLogger('pel_read') 109 | 110 | pua = IoTUAC(global_config) 111 | if authname != None: 112 | pua.authname = authname 113 | pua.authpass = authpass 114 | pio = PELIO(lfile) 115 | if sdev != None: 116 | pio.sdev = sdev 117 | pio.sstart_cb = pua.sess_started 118 | pio.send_cb = pua.sess_ended 119 | pio.start() 120 | ED2.loop() 121 | -------------------------------------------------------------------------------- /apps/iot/pel_read.sh: -------------------------------------------------------------------------------- 1 | # PROVIDE: pel_read 2 | # REQUIRE: DAEMON 3 | # BEFORE: LOGIN 4 | # KEYWORD: nojail shutdown 5 | 6 | pel_read_enable="YES" 7 | cli_arg="${1}" 8 | 9 | . /etc/rc.subr 10 | 11 | load_rc_config pel_read 12 | 13 | unset pel_read_flags 14 | 15 | name=pel_read 16 | rcvar="${name}_enable" 17 | 18 | command="/usr/local/bin/python3.6" 19 | pidfile="/var/run/pel_read.pid" 20 | 21 | command_args="/home/sobomax/b2bua/apps/iot/pel_read.py -s /dev/cuau1 -n 174.36.24.14 -u pel150_uac -P udv1pzs \ 22 | -i ${pidfile}" 23 | 24 | run_rc_command "$cli_arg" 25 | -------------------------------------------------------------------------------- /docker/Dockerfile.voiptests: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1.7-labs 2 | 3 | ARG BASE_IMAGE="sippylabs/rtpproxy:latest" 4 | FROM $BASE_IMAGE AS test 5 | LABEL maintainer="Maksym Sobolyev " 6 | 7 | USER root 8 | 9 | ENV DEBIAN_FRONTEND noninteractive 10 | 11 | WORKDIR /tmp 12 | 13 | COPY docker/set_env.sh docker/install_depends.sh docker/ 14 | 15 | ARG TARGETPLATFORM 16 | ENV SET_ENV="sh /tmp/docker/set_env.sh platformopts" 17 | 18 | RUN env -S "`${SET_ENV}`" sh -x docker/install_depends.sh 19 | 20 | COPY --exclude=.git --exclude=.github . . 21 | 22 | ENV PYTHON_CMD=python3 23 | 24 | RUN ${PYTHON_CMD} setup.py sdist 25 | RUN ${PYTHON_CMD} -m pip install dist/sippy*.gz 26 | RUN env -S "`${SET_ENV}`" \ 27 | ${PYTHON_CMD} -m pip install git+https://github.com/sippy/py-rtpproxy.git@wip 28 | 29 | ENV MM_TYPE=b2bua 30 | ENV MM_BRANCH=master 31 | ENV MM_ROOT=../.. 32 | ENV RTPP_BRANCH=DOCKER 33 | ENV RTPPROXY_DIST=../../dist/rtpproxy 34 | WORKDIR dist/voiptests 35 | 36 | ENV RTPPC_TYPE=unix 37 | RUN env -S "`${SET_ENV}`" sh -x ./test_run.sh 38 | RUN rm -f alice.log bob.log rtpproxy.log b2bua.log 39 | 40 | ENV RTPPC_TYPE=cunix 41 | RUN env -S "`${SET_ENV}`" sh -x ./test_run.sh 42 | RUN rm -f alice.log bob.log rtpproxy.log b2bua.log 43 | 44 | ENV RTPPC_TYPE=udp 45 | RUN env -S "`${SET_ENV}`" sh -x ./test_run.sh 46 | RUN rm -f alice.log bob.log rtpproxy.log b2bua.log 47 | 48 | ENV RTPPC_TYPE=tcp 49 | RUN env -S "`${SET_ENV}`" sh -x ./test_run.sh 50 | RUN rm -f alice.log bob.log rtpproxy.log b2bua.log 51 | 52 | ENV RTPPC_TYPE=rtp.io 53 | RUN env -S "`${SET_ENV}`" sh -x ./test_run.sh 54 | -------------------------------------------------------------------------------- /docker/install_depends.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | apt-get -y update -qq 6 | apt-get -y install gnupg2 ca-certificates 7 | apt-get -y update -qq 8 | 9 | apt-get -y --no-install-recommends install \ 10 | python-is-python3 python3-pip gpp `echo ${EXTRA_PACKAGES}` 11 | 12 | find /usr/lib -type f -name 'EXTERNALLY-MANAGED' -delete 13 | -------------------------------------------------------------------------------- /docker/set_env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | set -x 5 | 6 | platformopts() { 7 | case "${TARGETPLATFORM}" in 8 | linux/arm/v7) 9 | echo "RTPP_VERSION=production" 10 | echo "OPENSSL_CONFIGURE_ARGS=linux-armv4" 11 | ;; 12 | linux/s390x) 13 | echo "RTPP_VERSION=production" 14 | ;; 15 | linux/arm64) 16 | echo "QEMU_CPU=cortex-a53" 17 | ;; 18 | esac 19 | # if [ "${TARGETPLATFORM}" != "linux/amd64" -a "${TARGETPLATFORM}" != "linux/arm64" ] 20 | # then 21 | echo 'EXTRA_PACKAGES="build-essential git python3-dev"' 22 | # fi 23 | 24 | } 25 | 26 | case "${1}" in 27 | platformopts) 28 | shift 29 | platformopts "${@}" 30 | ;; 31 | *) 32 | echo "usage: `basename "${0}"` platformopts [opts]" 2>&1 33 | exit 1 34 | ;; 35 | esac 36 | -------------------------------------------------------------------------------- /documentation/Fig1_B2BUA_Architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/b2bua/e08039df0f48479c836f1f0e28ce6792d68422b3/documentation/Fig1_B2BUA_Architecture.png -------------------------------------------------------------------------------- /documentation/Fig2_Try_Event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/b2bua/e08039df0f48479c836f1f0e28ce6792d68422b3/documentation/Fig2_Try_Event.png -------------------------------------------------------------------------------- /documentation/Fig3_Orig_SIP_UA_generating_INVITE_message_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/b2bua/e08039df0f48479c836f1f0e28ce6792d68422b3/documentation/Fig3_Orig_SIP_UA_generating_INVITE_message_4.png -------------------------------------------------------------------------------- /documentation/Fig4_Answering_SIP_UA_sending_response_to_originating_end_point.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/b2bua/e08039df0f48479c836f1f0e28ce6792d68422b3/documentation/Fig4_Answering_SIP_UA_sending_response_to_originating_end_point.png -------------------------------------------------------------------------------- /documentation/Fig5_Endpoints_receiving_RTP_media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/b2bua/e08039df0f48479c836f1f0e28ce6792d68422b3/documentation/Fig5_Endpoints_receiving_RTP_media.png -------------------------------------------------------------------------------- /documentation/Fig6_BYE_message_sent_to_originating_SIP_endpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/b2bua/e08039df0f48479c836f1f0e28ce6792d68422b3/documentation/Fig6_BYE_message_sent_to_originating_SIP_endpoint.png -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ElPeriodic>=1.1 2 | pycryptodome 3 | websockets 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup, find_packages 3 | 4 | requirements = [x.strip() for x in open("requirements.txt", "r").readlines()] 5 | 6 | with open("README.md", "r") as fh: 7 | long_description = fh.read() 8 | 9 | setup( 10 | name = "sippy", 11 | version = "2.2.1", 12 | packages = find_packages(), 13 | 14 | install_requires = requirements, 15 | include_package_data=True, 16 | test_suite = 'tests', 17 | 18 | entry_points = { 19 | 'console_scripts': [ 20 | 'b2bua_simple = sippy.b2bua_simple:main_func', 21 | 'b2bua_radius = sippy.b2bua:main_func', 22 | 'b2bua = sippy.b2bua:main_func', 23 | ], 24 | }, 25 | 26 | long_description = long_description, 27 | long_description_content_type = "text/markdown", 28 | 29 | # meta-data for upload to PyPi 30 | author = "Sippy Software, Inc.", 31 | author_email = "sobomax@sippysoft.com", 32 | description = "RFC3261 SIP Stack and Back-to-Back User Agent (B2BUA)", 33 | license = "BSD", 34 | keywords = "sip,b2bua,voip,rfc3261,sippy", 35 | url = "http://www.b2bua.org/", 36 | classifiers = [ 37 | 'License :: OSI Approved :: BSD License', 38 | 'Operating System :: POSIX', 39 | 'Programming Language :: Python' 40 | ], 41 | ) 42 | -------------------------------------------------------------------------------- /sippy/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo 3 | -------------------------------------------------------------------------------- /sippy/B2B/States.py: -------------------------------------------------------------------------------- 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' 37 | -------------------------------------------------------------------------------- /sippy/B2B/Transforms.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 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 | import sys 27 | 28 | from sippy.SipRequest import SipRequest 29 | from sippy.CCEvents import CCEventTry 30 | 31 | class HDR2Xattrs(): 32 | # Source: https://github.com/sippy/b2bua/pull/38 33 | # Author: @twmobius 34 | hdr_name:str 35 | def __init__(self, hdr_name:str): 36 | self.hdr_name = hdr_name 37 | 38 | def __call__(self, cc:'CallController', req:SipRequest): 39 | hfs = req.getHFs(self.hdr_name) 40 | 41 | if len(hfs) == 0: 42 | return 43 | 44 | extra_attributes = [] 45 | 46 | for header in hfs: 47 | kvPairs = header.body.body.split(';') 48 | for pair in kvPairs: 49 | [key, _, value] = pair.partition("=") 50 | if value != '': 51 | extra_attributes.append((key, value)) 52 | if len(extra_attributes) == 0: 53 | return 54 | if cc.extra_attributes is None: 55 | cc.extra_attributes = extra_attributes 56 | else: 57 | cc.extra_attributes.extend(extra_attributes) 58 | 59 | class VAL2Xattrs(): 60 | # Source: https://github.com/sippy/b2bua/pull/39 61 | # Author: @twmobius 62 | doO: bool = True 63 | doA: bool = True 64 | radius_parameters: list 65 | def __init__(self, v:str): 66 | radius_parameters = [] 67 | pairs = v.split(',') 68 | for pair in pairs: 69 | [key, _, value] = pair.partition("=") 70 | if value == '': raise ValueError(f'{v}: comma-separated list of key=value pairs is expected') 71 | radius_parameters.append((key, value)) 72 | self.radius_parameters = radius_parameters 73 | 74 | def __call__(self, cc:'CallController', _:CCEventTry): 75 | if self.doO and cc.acctO is not None: 76 | cc.acctO.addAttributes(self.radius_parameters) 77 | if self.doA and cc.acctA is not None: 78 | cc.acctA.addAttributes(self.radius_parameters) 79 | 80 | class Nop(): 81 | def __init__(self, v:str): pass 82 | def __call__(self, *a, **kwa): pass 83 | 84 | class VAL2XattrsA(VAL2Xattrs): doO = False 85 | class VAL2XattrsO(VAL2Xattrs): doA = False 86 | 87 | def getTransProc(value:str): 88 | rparts = value.split('[', 1) 89 | if not len(rparts) == 2 or not value.endswith(']'): 90 | raise ValueError(f'getTransProc: `{value}` should be in the format `function[argument]`') 91 | fname = rparts[0] 92 | bts = sys.modules[__name__] 93 | fclass = getattr(bts, fname) 94 | farg = rparts[1][:-1] 95 | return fclass(farg) 96 | 97 | if __name__ == '__main__': 98 | for t in ('HDR2Xattrs[X-foo-hdr]', 'VAL2Xattrs[foo=bar,baz=xxx]', 'VAL2XattrsA[foo=bar,baz=xxx]', 'VAL2XattrsO[foo=bar,baz=xxx]'): 99 | p = getTransProc(t) 100 | -------------------------------------------------------------------------------- /sippy/B2B/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/b2bua/e08039df0f48479c836f1f0e28ce6792d68422b3/sippy/B2B/__init__.py -------------------------------------------------------------------------------- /sippy/Core/Exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017 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 datetime import datetime 27 | from traceback import print_exception, extract_stack, print_list, format_exception_only 28 | import sys, os 29 | 30 | SEPT = '-' * 70 + '\n' 31 | 32 | class StdException(Exception): 33 | traceback = None 34 | 35 | def __init__(self, *args): 36 | pin_exception(self, 2) 37 | super(StdException, self).__init__(*args) 38 | 39 | def dump_exception(msg, f = sys.stdout, extra = None): 40 | exc_type, exc_value, exc_traceback = sys.exc_info() 41 | if isinstance(exc_value, StdException): 42 | cus_traceback = exc_value.traceback 43 | else: 44 | if hasattr(exc_value, 'traceback'): 45 | exc_traceback = exc_value.traceback 46 | cus_traceback = None 47 | f.write('%s @%s[%d] %s:\n' % (datetime.now(), sys.argv[0], os.getpid(), msg)) 48 | f.write(SEPT) 49 | if cus_traceback != None: 50 | f.write('Traceback (most recent call last):\n') 51 | print_list(cus_traceback, file = f) 52 | f.write(format_exception_only(exc_type, exc_value)[0]) 53 | else: 54 | print_exception(exc_type, exc_value, exc_traceback, file = f) 55 | f.write(SEPT) 56 | if extra != None: 57 | f.write(extra) 58 | f.write(SEPT) 59 | f.flush() 60 | 61 | def pin_exception(exc_value, undepth = 1): 62 | if not hasattr(exc_value, 'traceback'): 63 | exc_value.traceback = sys.exc_info()[2] 64 | elif exc_value.traceback == None: 65 | exc_value.traceback = extract_stack()[:-undepth] 66 | 67 | if __name__ == '__main__': 68 | for f in sys.stdout, sys.stderr: 69 | for etype in Exception, StdException: 70 | try: 71 | raise etype("test: %s" % str(etype)) 72 | except: 73 | dump_exception("test ok", f = f) 74 | try: 75 | try: 76 | raise etype("test: %s" % str(etype)) 77 | except Exception as e: 78 | raise e 79 | except: 80 | dump_exception("test ok", f = f) 81 | try: 82 | try: 83 | raise etype("test: %s" % str(etype)) 84 | except Exception as e: 85 | pin_exception(e) 86 | raise e 87 | except: 88 | dump_exception("test ok", f = f) 89 | -------------------------------------------------------------------------------- /sippy/Core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/b2bua/e08039df0f48479c836f1f0e28ce6792d68422b3/sippy/Core/__init__.py -------------------------------------------------------------------------------- /sippy/ESipHeaderCSV.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 | class ESipHeaderCSV(Exception): 28 | def __init__(self, name, bodys): 29 | Exception.__init__(self) 30 | self.name = name 31 | self.bodys = bodys 32 | -------------------------------------------------------------------------------- /sippy/ESipHeaderIgnore.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 3 | # Copyright (c) 2006-2014 Sippy Software, Inc. All rights reserved. 4 | # 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without modification, 8 | # are permitted provided that the following conditions are met: 9 | # 10 | # 1. Redistributions of source code must retain the above copyright notice, this 11 | # list of conditions and the following disclaimer. 12 | # 13 | # 2. Redistributions in binary form must reproduce the above copyright notice, 14 | # this list of conditions and the following disclaimer in the documentation and/or 15 | # other materials provided with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 21 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 24 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | class ESipHeaderIgnore(Exception): 29 | def __init__(self): 30 | Exception.__init__(self) 31 | -------------------------------------------------------------------------------- /sippy/Exceptions/RtpProxyError.py: -------------------------------------------------------------------------------- 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' 31 | -------------------------------------------------------------------------------- /sippy/Exceptions/SdpParseError.py: -------------------------------------------------------------------------------- 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) 34 | -------------------------------------------------------------------------------- /sippy/Exceptions/SipHandlingError.py: -------------------------------------------------------------------------------- 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 | 28 | class SipHandlingError(Exception): 29 | code: int 30 | msg: str 31 | sip_response = None 32 | arg = None 33 | 34 | def __init__(self, arg): 35 | super().__init__() 36 | self.arg = arg 37 | 38 | def __str__(self): 39 | return str(self.arg) 40 | 41 | def getResponse(self, req): 42 | reason = self.getReason() 43 | resp = req.genResponse(self.code, self.msg, ext_reason = reason) 44 | return resp 45 | 46 | def getReason(self): 47 | if self.arg is not None and len(self.arg) > 0: 48 | return SipReason(protocol='SIP', cause=self.code, 49 | reason=self.arg) 50 | return None 51 | -------------------------------------------------------------------------------- /sippy/Exceptions/SipParseError.py: -------------------------------------------------------------------------------- 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 | 28 | class SipParseError(SipHandlingError): 29 | sip_response = None 30 | 31 | def __init__(self, arg, sip_response = None): 32 | super().__init__(arg) 33 | self.sip_response = sip_response 34 | 35 | def getResponse(self, req): 36 | if self.sip_response is not None: 37 | return self.sip_response 38 | return super().getResponse(req) 39 | -------------------------------------------------------------------------------- /sippy/Exceptions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/b2bua/e08039df0f48479c836f1f0e28ce6792d68422b3/sippy/Exceptions/__init__.py -------------------------------------------------------------------------------- /sippy/FakeAccounting.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 3 | # Copyright (c) 2006-2014 Sippy Software, Inc. All rights reserved. 4 | # 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without modification, 8 | # are permitted provided that the following conditions are met: 9 | # 10 | # 1. Redistributions of source code must retain the above copyright notice, this 11 | # list of conditions and the following disclaimer. 12 | # 13 | # 2. Redistributions in binary form must reproduce the above copyright notice, 14 | # this list of conditions and the following disclaimer in the documentation and/or 15 | # other materials provided with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 21 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 24 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | class FakeAccounting(object): 29 | def __init__(self, *args): 30 | pass 31 | 32 | def conn(self, *args): 33 | pass 34 | 35 | def disc(self, *args): 36 | pass 37 | -------------------------------------------------------------------------------- /sippy/Math/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/b2bua/e08039df0f48479c836f1f0e28ce6792d68422b3/sippy/Math/__init__.py -------------------------------------------------------------------------------- /sippy/Math/recfilter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2006-2014 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 threading import local 27 | from math import exp, pi 28 | 29 | def calc_f_coef(Fs, Fc): 30 | if Fs < Fc * 2.0: 31 | raise ValueError('The cutoff frequency (%f) should be less ' \ 32 | 'than half of the sampling rate (%f)' % (Fc, Fs)) 33 | return exp(-2.0 * pi * Fc / Fs) 34 | 35 | class recfilter(object): 36 | lastval = 0.0 37 | a = None 38 | b = None 39 | 40 | def __init__(self, fcoef, initval): 41 | #print('recfilter::init()') 42 | self.lastval = float(initval) 43 | self.a = 1.0 - float(fcoef) 44 | self.b = float(fcoef) 45 | 46 | def apply(self, x): 47 | self.lastval = self.a * float(x) + self.b * self.lastval 48 | return self.lastval 49 | 50 | class recfilter_ts(local, recfilter): 51 | def __init__(self, *args): 52 | #print('recfilter_ts::init()') 53 | recfilter.__init__(self, *args) 54 | -------------------------------------------------------------------------------- /sippy/MsgBody.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 3 | # Copyright (c) 2006-2014 Sippy Software, Inc. All rights reserved. 4 | # 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without modification, 8 | # are permitted provided that the following conditions are met: 9 | # 10 | # 1. Redistributions of source code must retain the above copyright notice, this 11 | # list of conditions and the following disclaimer. 12 | # 13 | # 2. Redistributions in binary form must reproduce the above copyright notice, 14 | # this list of conditions and the following disclaimer in the documentation and/or 15 | # other materials provided with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 21 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 24 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | from sippy.SdpBody import SdpBody 29 | from sippy.SipContentType import SipContentType 30 | 31 | try: 32 | # Python < 3 33 | str_types = (str, unicode) 34 | except NameError: 35 | str_types = (str,) 36 | 37 | DEFAULT_CTYPE = SipContentType('application/sdp') 38 | DEFAULT_CTYPE.parse() 39 | 40 | class MsgBody(object): 41 | content = None 42 | mtype = None 43 | needs_update = True 44 | parsed = False 45 | 46 | def __init__(self, content = None, mtype = DEFAULT_CTYPE, cself = None): 47 | if content != None: 48 | self.mtype = mtype 49 | self.content = content 50 | self.parsed = type(content) not in str_types 51 | return 52 | if cself is not None: 53 | if type(cself.content) in str_types: 54 | self.content = cself.content 55 | else: 56 | self.content = cself.content.getCopy() 57 | self.mtype = cself.mtype.getCopy() 58 | self.parsed = True 59 | 60 | def parse(self): 61 | b_types = {'application/sdp':SdpBody, 62 | 'multipart/mixed':MultipartMixBody} 63 | if not self.parsed: 64 | mtype = self.getType() 65 | if mtype in b_types: 66 | self.content = b_types[mtype](self.content, ctype=self.mtype) 67 | self.parsed = True 68 | 69 | def __str__(self): 70 | return str(self.content) 71 | 72 | def localStr(self, local_addr = None): 73 | if type(self.content) in str_types: 74 | return self.content 75 | return self.content.localStr(local_addr) 76 | 77 | def getType(self): 78 | return self.mtype.name.lower() 79 | 80 | def getCopy(self): 81 | if not self.parsed: 82 | return MsgBody(self.content) 83 | return MsgBody(cself = self) 84 | 85 | if 'MultipartMixBody' not in globals(): 86 | from sippy.MultipartMixBody import MultipartMixBody 87 | -------------------------------------------------------------------------------- /sippy/MultipartMixBody.py: -------------------------------------------------------------------------------- 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 secrets import token_hex 27 | 28 | from sippy.SipHeader import SipHeader 29 | from sippy.SipContentType import SipContentType 30 | 31 | class MultipartMixBody(): 32 | parts = None 33 | boundary = None 34 | mtype = SipContentType('multipart/mixed') 35 | mtype.parse() 36 | 37 | def __init__(self, body = None, ctype = None): 38 | self.parts = [] 39 | self.part_headers = [] 40 | if body is None: 41 | self.setBoundary(token_hex(16)) 42 | return 43 | sep = f'--{ctype.params["boundary"]}' 44 | bparts = body.split(sep) 45 | assert len(bparts) > 2 46 | assert len(bparts[0]) == 0 47 | assert bparts[-1].strip() == '--' 48 | parts = [p.lstrip() for p in bparts[1:-1]] 49 | for sect in parts: 50 | headers = [] 51 | ct = None 52 | headersect, sect = sect.split('\r\n\r\n', 1) 53 | # parse sub headers 54 | for hl in headersect.split('\r\n'): 55 | h = SipHeader(hl) 56 | if h.name == "content-type": 57 | ct = h.getBody() 58 | else: 59 | headers.append(h) 60 | # add part 61 | sect = MsgBody(sect, ct) 62 | self.parts.append(sect) 63 | self.part_headers.append(headers) 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 71 | 72 | def __str__(self): 73 | bnd = f'--{self.boundary}\r\n' 74 | parts = [f'{bnd}Content-Type: {p.mtype}\r\n\r\n{p}' for p in self.parts] 75 | s = ''.join(parts) 76 | s += f'{bnd[:-2]}--\r\n' 77 | return s 78 | 79 | def localStr(self, local_addr = None): 80 | bnd = f'--{self.boundary}\r\n' 81 | parts = [f'{bnd}Content-Type: {p.mtype}\r\n\r\n{p.localStr(local_addr)}' 82 | for p in self.parts] 83 | s = ''.join(parts) 84 | s += f'{bnd[:-2]}--\r\n' 85 | return s 86 | 87 | def getCopy(self): 88 | cself = MultipartMixBody() 89 | cself.parts = [p.getCopy() for p in self.parts] 90 | cself.boundary = self.boundary 91 | return cself 92 | 93 | def getContentType(self): 94 | return self.mtype.getCopy() 95 | 96 | if 'MsgBody' not in globals(): 97 | from sippy.MsgBody import MsgBody 98 | -------------------------------------------------------------------------------- /sippy/RadiusAuthorisation.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.Radius_client import Radius_client 28 | 29 | from time import time 30 | from functools import reduce 31 | 32 | class RadiusAuthorisation(Radius_client): 33 | def do_auth(self, username, caller, callee, sip_cid, remote_ip, res_cb, \ 34 | realm = None, nonce = None, uri = None, response = None, extra_attributes = None): 35 | sip_cid = str(sip_cid) 36 | attributes = None 37 | if None not in (realm, nonce, uri, response): 38 | attributes = [('User-Name', username), ('Digest-Realm', realm), \ 39 | ('Digest-Nonce', nonce), ('Digest-Method', 'INVITE'), ('Digest-URI', uri), \ 40 | ('Digest-Algorithm', 'MD5'), ('Digest-User-Name', username), ('Digest-Response', response)] 41 | else: 42 | attributes = [('User-Name', remote_ip), ('Password', 'cisco')] 43 | if caller == None: 44 | caller = '' 45 | attributes.extend((('Calling-Station-Id', caller), ('Called-Station-Id', callee), \ 46 | ('call-id', sip_cid), ('h323-remote-address', remote_ip), ('h323-session-protocol', 'sipv2'))) 47 | if extra_attributes != None: 48 | for a, v in extra_attributes: 49 | attributes.append((a, v)) 50 | message = 'sending AAA request:\n' 51 | message += reduce(lambda x, y: x + y, ['%-32s = \'%s\'\n' % (x[0], str(x[1])) for x in attributes]) 52 | self.global_config['_sip_logger'].write(message, call_id = sip_cid) 53 | Radius_client.do_auth(self, attributes, self._process_result, res_cb, sip_cid, time()) 54 | 55 | def _process_result(self, results, res_cb, sip_cid, btime): 56 | delay = time() - btime 57 | rcode = results[1] 58 | if rcode in (0, 1): 59 | if rcode == 0: 60 | message = 'AAA request accepted (delay is %.3f), processing response:\n' % delay 61 | else: 62 | message = 'AAA request rejected (delay is %.3f), processing response:\n' % delay 63 | if len(results[0]) > 0: 64 | message += reduce(lambda x, y: x + y, ['%-32s = \'%s\'\n' % x for x in results[0]]) 65 | else: 66 | message = 'Error sending AAA request (delay is %.3f)\n' % delay 67 | self.global_config['_sip_logger'].write(message, call_id = sip_cid) 68 | res_cb(results) 69 | -------------------------------------------------------------------------------- /sippy/Radius_client.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 3 | # Copyright (c) 2006-2014 Sippy Software, Inc. All rights reserved. 4 | # 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without modification, 8 | # are permitted provided that the following conditions are met: 9 | # 10 | # 1. Redistributions of source code must retain the above copyright notice, this 11 | # list of conditions and the following disclaimer. 12 | # 13 | # 2. Redistributions in binary form must reproduce the above copyright notice, 14 | # this list of conditions and the following disclaimer in the documentation and/or 15 | # other materials provided with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 21 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 24 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | from sippy.External_command import External_command 29 | 30 | class Radius_client(External_command): 31 | global_config = None 32 | _avpair_names = ('call-id', 'h323-session-protocol', 'h323-ivr-out', 'h323-incoming-conf-id', \ 33 | 'release-source', 'alert-timepoint', 'provisional-timepoint') 34 | _cisco_vsa_names = ('h323-remote-address', 'h323-conf-id', 'h323-setup-time', 'h323-call-origin', \ 35 | 'h323-call-type', 'h323-connect-time', 'h323-disconnect-time', 'h323-disconnect-cause', \ 36 | 'h323-voice-quality', 'h323-credit-time', 'h323-return-code', 'h323-redirect-number', \ 37 | 'h323-preferred-lang', 'h323-billing-model', 'h323-currency') 38 | 39 | def __init__(self, global_config = {}): 40 | self.global_config = global_config 41 | command = global_config.getdefault('radiusclient', '/usr/local/sbin/radiusclient') 42 | config = global_config.getdefault('radiusclient.conf', None) 43 | max_workers = global_config.getdefault('max_radiusclients', 20) 44 | if config != None: 45 | External_command.__init__(self, (command, '-f', config, '-s'), max_workers = max_workers) 46 | else: 47 | External_command.__init__(self, (command, '-s'), max_workers = max_workers) 48 | 49 | def _prepare_attributes(self, type, attributes): 50 | data = [type] 51 | for a, v in attributes: 52 | if a in self._avpair_names: 53 | v = '%s=%s' % (str(a), str(v)) 54 | a = 'Cisco-AVPair' 55 | elif a in self._cisco_vsa_names: 56 | v = '%s=%s' % (str(a), str(v)) 57 | data.append('%s="%s"' % (str(a), str(v))) 58 | return data 59 | 60 | def do_auth(self, attributes, result_callback, *callback_parameters): 61 | return External_command.process_command(self, self._prepare_attributes('AUTH', attributes), result_callback, *callback_parameters) 62 | 63 | def do_acct(self, attributes, result_callback = None, *callback_parameters): 64 | External_command.process_command(self, self._prepare_attributes('ACCT', attributes), result_callback, *callback_parameters) 65 | 66 | def process_result(self, result_callback, result, *callback_parameters): 67 | if result_callback == None: 68 | return 69 | nav = [] 70 | for av in result[:-1]: 71 | a, v = [x.strip() for x in av.decode().split(' = ', 1)] 72 | v = v.strip('\'') 73 | if (a == 'Cisco-AVPair' or a in self._cisco_vsa_names): 74 | t = v.split('=', 1) 75 | if len(t) > 1: 76 | a, v = t 77 | elif v.startswith(a + '='): 78 | v = v[len(a) + 1:] 79 | nav.append((a, v)) 80 | External_command.process_result(self, result_callback, (tuple(nav), int(result[-1])), *callback_parameters) 81 | -------------------------------------------------------------------------------- /sippy/Rtp_proxy/Client/Worker/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/b2bua/e08039df0f48479c836f1f0e28ce6792d68422b3/sippy/Rtp_proxy/Client/Worker/__init__.py -------------------------------------------------------------------------------- /sippy/Rtp_proxy/Client/Worker/internal.py: -------------------------------------------------------------------------------- 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 96 | -------------------------------------------------------------------------------- /sippy/Rtp_proxy/Client/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sippy/Rtp_proxy/Client/internal.py: -------------------------------------------------------------------------------- 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 | import socket 28 | 29 | from .stream import Rtp_proxy_client_stream, test 30 | from .Worker.internal import RTPPLWorker_internal as _RTPPLWorker 31 | 32 | try: from rtpproxy import rtpproxy 33 | except ModuleNotFoundError as ex: rtpproxy = ex 34 | 35 | class Rtp_proxy_client_internal(Rtp_proxy_client_stream): 36 | failed = None if not isinstance(rtpproxy, Exception) else rtpproxy 37 | def __init__(self, global_config, nworkers = 1, nsetup_f = None, **kwargs): 38 | if Rtp_proxy_client_internal.failed is not None: 39 | raise Rtp_proxy_client_internal.failed 40 | self.is_local = True 41 | self.worker_class = _RTPPLWorker 42 | if 'extra_args' in kwargs: 43 | extra_args = kwargs['extra_args'] 44 | modules = [x.split('=', 1) for x in extra_args if x.startswith('modules=')] 45 | if len(modules) > 0: 46 | modules = tuple(modules[0][1].split('+')) 47 | kwargs['extra_args'] = [x for x in extra_args if not x.startswith('modules=')] 48 | kwargs['modules'] = modules 49 | self.rtpp = rtpproxy(**kwargs) 50 | if nsetup_f is not None: 51 | nsetup_f(self.rtpp.rtpp_nsock, self.rtpp.rtpp_nsock_spec) 52 | self.notify_socket = self.rtpp.rtpp_nsock_spec 53 | Rtp_proxy_client_stream.__init__(self, global_config = global_config, \ 54 | address = self.rtpp.rtpp_sock, bind_address = None, nworkers = nworkers, \ 55 | family = socket.AF_UNIX) 56 | 57 | def _reconnect(self, *args, **kwargs): 58 | raise RuntimeError('Rtp_proxy_client_internal does not support reconnecting') 59 | 60 | def shutdown(self): 61 | Rtp_proxy_client_stream.shutdown(self) 62 | if self.rtpp: 63 | del self.rtpp 64 | self.rtpp = None 65 | 66 | if __name__ == '__main__': 67 | test(Rtp_proxy_client_internal) 68 | -------------------------------------------------------------------------------- /sippy/Rtp_proxy/Client/local.py: -------------------------------------------------------------------------------- 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 | import socket 28 | 29 | from .stream import Rtp_proxy_client_stream, test 30 | 31 | class Rtp_proxy_client_local(Rtp_proxy_client_stream): 32 | is_local = True 33 | 34 | def __init__(self, global_config, address = '/var/run/rtpproxy.sock', \ 35 | bind_address = None, nworkers = 1): 36 | Rtp_proxy_client_stream.__init__(self, global_config = global_config, \ 37 | address = address, bind_address = bind_address, nworkers = nworkers, \ 38 | family = socket.AF_UNIX) 39 | 40 | if __name__ == '__main__': 41 | test(Rtp_proxy_client_local) 42 | -------------------------------------------------------------------------------- /sippy/Rtp_proxy/Client/net.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 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 socket import getaddrinfo, SOCK_DGRAM, AF_INET, AF_INET6 27 | 28 | class Rtp_proxy_client_net(object): 29 | sock_type = None # Set me in superclass! 30 | 31 | def getdestbyaddr(self, address, af): 32 | #print('getaddrinfo', address[0], address[1], af, self.sock_type) 33 | if af == AF_INET6 and address[0].startswith('['): 34 | return address 35 | ainfo = getaddrinfo(address[0], address[1], af, self.sock_type) 36 | amatch = [x[4] for x in ainfo if x[0] == af] 37 | if len(amatch) == 0: 38 | raise Exception('no match for the %s in AF %s' % (address, af)) 39 | amatch = amatch[0] 40 | if af == AF_INET6: 41 | return (('[%s]' % amatch[0], amatch[1])) 42 | return ((amatch[0], amatch[1])) 43 | -------------------------------------------------------------------------------- /sippy/Rtp_proxy/Client/stream.py: -------------------------------------------------------------------------------- 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 queue import Queue 28 | import socket 29 | 30 | from sippy.Math.recfilter import recfilter 31 | from sippy.Core.EventDispatcher import ED2 32 | 33 | from ...Rtp_proxy.cmd import Rtp_proxy_cmd 34 | from .net import Rtp_proxy_client_net 35 | from .Worker.external import RTPPLWorker_external as _RTPPLWorker 36 | 37 | class Rtp_proxy_client_stream(Rtp_proxy_client_net): 38 | is_local = None 39 | wi = None 40 | nworkers = None 41 | nworkers_act = None 42 | workers = None 43 | delay_flt = None 44 | family = None 45 | sock_type = socket.SOCK_STREAM 46 | worker_class = _RTPPLWorker 47 | 48 | def __init__(self, global_config, address = '/var/run/rtpproxy.sock', \ 49 | bind_address = None, nworkers = 1, family = socket.AF_UNIX): 50 | #print('Rtp_proxy_client_stream.__init__', address, bind_address, nworkers, family) 51 | if family == socket.AF_UNIX: 52 | self.is_local = True 53 | self.address = address 54 | else: 55 | self.is_local = False 56 | self.address = self.getdestbyaddr(address, family) 57 | self.family = family 58 | self.wi = Queue() 59 | self.nworkers = nworkers 60 | self.workers = [] 61 | for i in range(0, self.nworkers): 62 | self.workers.append(self.worker_class(self)) 63 | self.nworkers_act = i + 1 64 | self.delay_flt = recfilter(0.95, 0.25) 65 | 66 | def send_command(self, command, result_callback = None, *callback_parameters): 67 | if self.nworkers_act == 0: 68 | self.rtpp_class._reconnect(self, self.address) 69 | if isinstance(command, Rtp_proxy_cmd): 70 | command = str(command) 71 | elif not command.endswith('\n'): 72 | command += '\n' 73 | self.wi.put((command, result_callback, callback_parameters)) 74 | 75 | def shutdown(self): 76 | self.wi.put(None) 77 | for rworker in self.workers: 78 | rworker.join() 79 | self.workers = None 80 | 81 | def register_delay(self, rtpc_delay): 82 | self.delay_flt.apply(rtpc_delay) 83 | 84 | def get_rtpc_delay(self): 85 | return self.delay_flt.lastval 86 | 87 | def test(class_obj): 88 | class robj(object): 89 | rval = None 90 | r = robj() 91 | def display(res, ro, arg): 92 | print(res, arg) 93 | ro.rval = (res, arg) 94 | ED2.breakLoop() 95 | rc = class_obj({'_sip_address':'1.2.3.4'}) 96 | rc.send_command('VF 123456', display, r, 'abcd') 97 | ED2.loop() 98 | rc.shutdown() 99 | assert(r.rval == (u'0', u'abcd')) 100 | print('passed') 101 | 102 | if __name__ == '__main__': 103 | test(Rtp_proxy_client_stream) 104 | -------------------------------------------------------------------------------- /sippy/Rtp_proxy/Cmd/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sippy/Rtp_proxy/Cmd/sequencer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2022 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 | class Rtp_proxy_cmd_sequencer(object): 28 | rtp_proxy_client = None 29 | comqueue = None 30 | inflight = None 31 | deleted = False 32 | 33 | def __init__(self, rtpp_client): 34 | self.rtp_proxy_client = rtpp_client 35 | self.comqueue = [] 36 | 37 | def send_command(self, command, result_callback = None, *callback_parameters): 38 | if self.rtp_proxy_client == None: 39 | return 40 | if self.inflight != None: 41 | self.comqueue.append((command, result_callback, callback_parameters)) 42 | return 43 | self.inflight = (command, result_callback, callback_parameters) 44 | self.rtp_proxy_client.send_command(command, self.result_callback) 45 | 46 | def result_callback(self, result): 47 | command, result_callback, callback_parameters = self.inflight 48 | self.inflight = None 49 | if self.rtp_proxy_client != None and len(self.comqueue) > 0: 50 | self.inflight = self.comqueue.pop(0) 51 | self.rtp_proxy_client.send_command(self.inflight[0], self.result_callback) 52 | if result_callback != None: 53 | result_callback(result, *callback_parameters) 54 | if self.deleted: 55 | self.delete() 56 | return 57 | 58 | def delete(self): 59 | if self.inflight is not None: 60 | self.deleted = True 61 | return 62 | # break the reference loop 63 | self.rtp_proxy_client = None 64 | -------------------------------------------------------------------------------- /sippy/Rtp_proxy/Session/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sippy/Rtp_proxy/Session/webrtc.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 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.Rtp_proxy.session import Rtp_proxy_session 27 | 28 | class Rtp_proxy_session_webrtc2sip(Rtp_proxy_session): 29 | insert_nortpp = False 30 | def __init__(self, *a, **kwa): 31 | super().__init__(*a, **kwa) 32 | self.caller.deice = True 33 | self.caller.gateway_dtls = 'dtls' 34 | self.callee.gateway_dtls = 'rtp' 35 | 36 | class Rtp_proxy_session_sip2webrtc(Rtp_proxy_session): 37 | insert_nortpp = False 38 | def __init__(self, *a, **kwa): 39 | super().__init__(*a, **kwa) 40 | self.callee.deice = True 41 | self.callee.gateway_dtls = 'dtls' 42 | self.caller.gateway_dtls = 'rtp' 43 | -------------------------------------------------------------------------------- /sippy/Rtp_proxy/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sippy/SdpConnecton.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 3 | # Copyright (c) 2006-2014 Sippy Software, Inc. All rights reserved. 4 | # 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without modification, 8 | # are permitted provided that the following conditions are met: 9 | # 10 | # 1. Redistributions of source code must retain the above copyright notice, this 11 | # list of conditions and the following disclaimer. 12 | # 13 | # 2. Redistributions in binary form must reproduce the above copyright notice, 14 | # this list of conditions and the following disclaimer in the documentation and/or 15 | # other materials provided with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 21 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 24 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | class SdpConnecton(object): 29 | ntype = None 30 | atype = None 31 | addr = None 32 | 33 | def __init__(self, body = None, cself = None): 34 | if body is not None: 35 | self.ntype, self.atype, self.addr = body.split()[:3] 36 | else: 37 | self.ntype = cself.ntype 38 | self.atype = cself.atype 39 | self.addr = cself.addr 40 | 41 | def __str__(self): 42 | return '%s %s %s' % (self.ntype, self.atype, self.addr) 43 | 44 | def localStr(self, local_addr = None): 45 | return '%s %s %s' % (self.ntype, self.atype, self.addr) 46 | 47 | def getCopy(self): 48 | return SdpConnecton(cself = self) 49 | -------------------------------------------------------------------------------- /sippy/SdpGeneric.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 | class SdpGeneric(str): 28 | def localStr(self, local_addr = None): 29 | return str(self) 30 | 31 | def getCopy(self): 32 | return self 33 | -------------------------------------------------------------------------------- /sippy/SdpMedia.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 | class MTAudio(object): 28 | pass 29 | 30 | class MTVideo(object): 31 | pass 32 | 33 | class MTOther(object): 34 | pass 35 | 36 | class SdpMedia(object): 37 | type = None 38 | stype = None 39 | port = None 40 | transport = None 41 | formats = None 42 | 43 | def __init__(self, body = None, cself = None): 44 | if body is not None: 45 | params = body.split() 46 | self.stype = params[0] 47 | lstype = self.stype.lower() 48 | if lstype == 'audio': 49 | self.type = MTAudio 50 | elif lstype == 'video': 51 | self.type = MTVideo 52 | else: 53 | self.type = MTOther 54 | self.port = int(params[1]) 55 | self.transport = params[2] 56 | if self.type in (MTAudio, MTVideo): 57 | self.formats = [int(x) for x in params[3:]] 58 | else: 59 | self.formats = params[3:] 60 | else: 61 | self.type = cself.type 62 | self.stype = cself.stype 63 | self.port = cself.port 64 | self.transport = cself.transport 65 | self.formats = cself.formats[:] 66 | 67 | def __str__(self): 68 | rval = '%s %d %s' % (self.stype, self.port, self.transport) 69 | if self.type in (MTAudio, MTVideo): 70 | for format in self.formats: 71 | rval += ' %d' % format 72 | else: 73 | for format in self.formats: 74 | rval += ' %s' % format 75 | return rval 76 | 77 | def localStr(self, local_addr = None): 78 | return str(self) 79 | 80 | def getCopy(self): 81 | return SdpMedia(cself = self) 82 | -------------------------------------------------------------------------------- /sippy/SdpOrigin.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipConf import SipConf 28 | from time import time 29 | from random import random 30 | 31 | class SdpOrigin(object): 32 | _session_id = int(random() * time() * 1000.0) 33 | username = None 34 | session_id = None 35 | version = None 36 | network_type = None 37 | address_type = None 38 | address = None 39 | session_id = None 40 | 41 | def __init__(self, body = None, cself = None): 42 | if body is not None: 43 | self.username, self.session_id, self.version, self.network_type, self.address_type, self.address = body.split() 44 | elif cself == None: 45 | self.username = '-' 46 | self.session_id = SdpOrigin._session_id 47 | SdpOrigin._session_id += 1 48 | self.version = self.session_id 49 | self.network_type = 'IN' 50 | self.address_type = 'IP4' 51 | self.address = SipConf.my_address 52 | else: 53 | self.username = cself.username 54 | self.session_id = cself.session_id 55 | self.version = cself.version 56 | self.network_type = cself.network_type 57 | self.address_type = cself.address_type 58 | self.address = cself.address 59 | 60 | def __str__(self): 61 | return '%s %s %s %s %s %s' % (self.username, self.session_id, self.version, self.network_type, self.address_type, self.address) 62 | 63 | def localStr(self, local_addr = None): 64 | if local_addr != None and 'my' in dir(self.address): 65 | (local_addr, local_port), local_transport = local_addr 66 | if local_addr.startswith('['): 67 | address_type = 'IP6' 68 | local_addr = local_addr[1:-1] 69 | else: 70 | address_type = 'IP4' 71 | return '%s %s %s %s %s %s' % (self.username, self.session_id, self.version, self.network_type, address_type, local_addr) 72 | return '%s %s %s %s %s %s' % (self.username, self.session_id, self.version, self.network_type, self.address_type, self.address) 73 | 74 | def getCopy(self): 75 | return self.__class__(cself = self) 76 | -------------------------------------------------------------------------------- /sippy/Security/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/b2bua/e08039df0f48479c836f1f0e28ce6792d68422b3/sippy/Security/__init__.py -------------------------------------------------------------------------------- /sippy/Signal.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 signal import signal, SIG_IGN, SIG_DFL 28 | 29 | from sippy.Core.Exceptions import dump_exception 30 | from sippy.Core.EventDispatcher import ED2 31 | 32 | class Signal(object): 33 | callback = None 34 | parameters = None 35 | previous_handler = None 36 | 37 | def __init__(self, signum, callback, *parameters): 38 | self.signum = signum 39 | self.callback = callback 40 | self.parameters = parameters 41 | self.previous_handler = signal(signum, self.signal_handler) 42 | 43 | def signal_handler(self, signum, *frame): 44 | ED2.callFromThread(self.dispatch) 45 | if self.previous_handler not in (SIG_IGN, SIG_DFL): 46 | try: 47 | self.previous_handler(signum, *frame) 48 | except: 49 | dump_exception('Signal: unhandled exception in signal chain') 50 | 51 | def dispatch(self): 52 | try: 53 | self.callback(*self.parameters) 54 | except: 55 | dump_exception('Signal: unhandled exception in signal callback') 56 | 57 | def cancel(self): 58 | signal(self.signum, self.previous_handler) 59 | self.callback = None 60 | self.parameters = None 61 | self.previous_handler = None 62 | 63 | def log_signal(signum, sip_logger, signal_cb, cb_params): 64 | sip_logger.write('Dispatching signal %d to handler %s' % (signum, str(signal_cb))) 65 | return signal_cb(*cb_params) 66 | 67 | def LogSignal(sip_logger, signum, signal_cb, *cb_params): 68 | sip_logger.write('Registering signal %d to handler %s' % (signum, str(signal_cb))) 69 | return Signal(signum, log_signal, signum, sip_logger, signal_cb, cb_params) 70 | 71 | if __name__ == '__main__': 72 | from signal import SIGHUP, SIGURG, SIGTERM 73 | from os import kill, getpid 74 | 75 | def test(arguments): 76 | arguments['test'] = not arguments['test'] 77 | ED2.breakLoop() 78 | 79 | arguments = {'test':False} 80 | s = Signal(SIGURG, test, arguments) 81 | kill(getpid(), SIGURG) 82 | ED2.loop() 83 | assert(arguments['test']) 84 | s.cancel() 85 | Signal(SIGHUP, test, arguments) 86 | kill(getpid(), SIGURG) 87 | kill(getpid(), SIGHUP) 88 | ED2.loop() 89 | assert(not arguments['test']) 90 | from sippy.SipLogger import SipLogger 91 | sip_logger = SipLogger('Signal::selftest') 92 | LogSignal(sip_logger, SIGTERM, test, arguments) 93 | kill(getpid(), SIGTERM) 94 | ED2.loop() 95 | assert(arguments['test']) 96 | -------------------------------------------------------------------------------- /sippy/SipAddressHF.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipGenericHF import SipGenericHF 28 | from sippy.SipAddress import SipAddress 29 | from sippy.ESipHeaderCSV import ESipHeaderCSV 30 | 31 | class SipAddressHF(SipGenericHF): 32 | address = None 33 | relaxedparser = False 34 | 35 | def __init__(self, body = None, address = None): 36 | SipGenericHF.__init__(self, body) 37 | if body is not None: 38 | csvs = [] 39 | pidx = 0 40 | while 1: 41 | idx = body.find(',', pidx) 42 | if idx == -1: 43 | break 44 | onum = body[:idx].count('<') 45 | cnum = body[:idx].count('>') 46 | qnum = body[:idx].count('"') 47 | if (onum == 0 and cnum == 0 and qnum == 0) or (onum > 0 and \ 48 | onum == cnum and (qnum % 2 == 0)): 49 | csvs.append(body[:idx]) 50 | body = body[idx + 1:] 51 | pidx = 0 52 | else: 53 | pidx = idx + 1 54 | if (len(csvs) > 0): 55 | csvs.append(body) 56 | raise ESipHeaderCSV(None, csvs) 57 | else: 58 | self.parsed = True 59 | self.address = address 60 | 61 | def parse(self): 62 | self.address = SipAddress(self.body, relaxedparser = self.relaxedparser) 63 | self.parsed = True 64 | 65 | def __str__(self): 66 | return self.localStr() 67 | 68 | def localStr(self, local_addr = None): 69 | if not self.parsed: 70 | return self.body 71 | return self.address.localStr(local_addr) 72 | 73 | def getCopy(self): 74 | if not self.parsed: 75 | oret = self.__class__(self.body) 76 | else: 77 | oret = self.__class__(address = self.address.getCopy()) 78 | oret.relaxedparser = self.relaxedparser 79 | return oret 80 | 81 | def setBody(self, body): 82 | self.address = body 83 | 84 | def getUri(self): 85 | return self.address 86 | 87 | def getUrl(self): 88 | return self.address.url 89 | -------------------------------------------------------------------------------- /sippy/SipAllow.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipGenericHF import SipGenericHF 28 | 29 | from functools import reduce 30 | 31 | class SipAllow(SipGenericHF): 32 | hf_names = ('allow',) 33 | methods = None 34 | 35 | def __init__(self, body = None, methods = None): 36 | SipGenericHF.__init__(self, body) 37 | if body is None: 38 | self.parsed = True 39 | self.methods = methods[:] 40 | 41 | def parse(self): 42 | self.methods = [x.strip() for x in self.body.split(',')] 43 | self.parsed = True 44 | 45 | def __str__(self): 46 | if not self.parsed: 47 | return self.body 48 | return reduce(lambda x, y: '%s, %s' % (x, y), self.methods) 49 | 50 | def getCopy(self): 51 | if not self.parsed: 52 | return SipAllow(self.body) 53 | return SipAllow(methods = self.methods) 54 | 55 | def getCanName(self, name, compact = False): 56 | return 'Allow' 57 | -------------------------------------------------------------------------------- /sippy/SipAlso.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipAddressHF import SipAddressHF 28 | 29 | class SipAlso(SipAddressHF): 30 | hf_names = ('also',) 31 | -------------------------------------------------------------------------------- /sippy/SipCCDiversion.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipAddressHF import SipAddressHF 28 | 29 | class SipCCDiversion(SipAddressHF): 30 | hf_names = ('cc-diversion',) 31 | 32 | def getCanName(self, name, compact = False): 33 | return 'CC-Diversion' 34 | -------------------------------------------------------------------------------- /sippy/SipCSeq.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipGenericHF import SipGenericHF 28 | 29 | class SipCSeq(SipGenericHF): 30 | hf_names = ('cseq',) 31 | cseq = None 32 | method = None 33 | 34 | def __init__(self, body = None, cseq = None, method = None): 35 | SipGenericHF.__init__(self, body) 36 | if body is None: 37 | self.parsed = True 38 | self.method = method 39 | if cseq != None: 40 | self.cseq = cseq 41 | else: 42 | self.cseq = 1 43 | 44 | def parse(self): 45 | cseq, self.method = self.body.split() 46 | self.cseq = int(cseq) 47 | self.parsed = True 48 | 49 | def __str__(self): 50 | if not self.parsed: 51 | return self.body 52 | return str(self.cseq) + ' ' + self.method 53 | 54 | def getCSeq(self): 55 | return (self.cseq, self.method) 56 | 57 | def getCSeqNum(self): 58 | return self.cseq 59 | 60 | def getCSeqMethod(self): 61 | return self.method 62 | 63 | def getCopy(self): 64 | if not self.parsed: 65 | return SipCSeq(self.body) 66 | return SipCSeq(cseq = self.cseq, method = self.method) 67 | 68 | def getCanName(self, name, compact = False): 69 | return 'CSeq' 70 | 71 | def incCSeqNum(self): 72 | self.cseq += 1 73 | return self.cseq 74 | -------------------------------------------------------------------------------- /sippy/SipCallId.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 random import random 28 | from hashlib import md5 29 | from time import time 30 | from math import floor 31 | from sippy.SipConf import SipConf 32 | from sippy.SipGenericHF import SipGenericHF 33 | 34 | CALL_ID_CHARSET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-.!%*_+`\'~()<>:\\"/[]?{}' 35 | _clen = len(CALL_ID_CHARSET) 36 | DEFAULT_TEST_LEN = (32, 16) 37 | 38 | def gen_test_cid(lens = DEFAULT_TEST_LEN): 39 | r = '' 40 | for j in (0, 1): 41 | for i in range(0, lens[j]): 42 | r += CALL_ID_CHARSET[int(floor(random() * _clen))] 43 | if j == 0: 44 | r += '@' 45 | return r 46 | 47 | class SipCallId(SipGenericHF): 48 | hf_names = ('call-id', 'i') 49 | body = None 50 | 51 | def __init__(self, body = None): 52 | SipGenericHF.__init__(self, body) 53 | self.parsed = True 54 | if body is None: 55 | self.genCallId() 56 | 57 | def __add__(self, other): 58 | return SipCallId(self.body + str(other)) 59 | 60 | def genCallId(self): 61 | salt = str((random() * 1000000000) + time()) 62 | self.body = md5(salt.encode()).hexdigest() 63 | 64 | def getCanName(self, name, compact = False): 65 | if compact: 66 | return 'i' 67 | return 'Call-ID' 68 | -------------------------------------------------------------------------------- /sippy/SipCiscoGUID.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 random import random 28 | from hashlib import md5 29 | from time import time 30 | from sippy.SipGenericHF import SipGenericHF 31 | 32 | class SipCiscoGUID(SipGenericHF): 33 | hf_names = ('cisco-guid', 'h323-conf-id') 34 | ciscoGUID = None 35 | 36 | def __init__(self, body = None, ciscoGUID = None): 37 | SipGenericHF.__init__(self, body) 38 | if body is not None: 39 | return 40 | self.parsed = True 41 | if ciscoGUID != None: 42 | self.ciscoGUID = ciscoGUID 43 | else: 44 | salt = str((random() * 1000000000) + time()) 45 | s = md5(salt.encode()).hexdigest() 46 | self.ciscoGUID = (int(s[0:8], 16), int(s[8:16], 16), int(s[16:24], 16), int(s[24:32], 16)) 47 | 48 | def parse(self): 49 | self.ciscoGUID = tuple([int(x) for x in self.body.split('-', 3)]) 50 | self.parsed = True 51 | 52 | def __str__(self): 53 | if not self.parsed: 54 | return self.body 55 | return '%d-%d-%d-%d' % self.ciscoGUID 56 | 57 | def getCiscoGUID(self): 58 | return self.ciscoGUID 59 | 60 | def hexForm(self): 61 | return '%.8X %.8X %.8X %.8X' % self.ciscoGUID 62 | 63 | def getCanName(self, name, compact = False): 64 | if name.lower() == 'h323-conf-id': 65 | return 'h323-conf-id' 66 | else: 67 | return 'cisco-GUID' 68 | 69 | def getCopy(self): 70 | if not self.parsed: 71 | return SipCiscoGUID(self.body) 72 | return SipCiscoGUID(ciscoGUID = self.ciscoGUID) 73 | -------------------------------------------------------------------------------- /sippy/SipConf.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 socket import gethostbyname, gethostname 28 | 29 | class MyAddress(object): 30 | my = True 31 | 32 | def __str__(self): 33 | try: 34 | return gethostbyname(gethostname()) 35 | except: 36 | return '127.0.0.1' 37 | 38 | class MyPort(object): 39 | my = True 40 | default_port = None 41 | 42 | def __init__(self, default_port): 43 | self.default_port = default_port 44 | 45 | def __str__(self): 46 | return str(self.default_port) 47 | 48 | def __int__(self): 49 | return self.default_port 50 | 51 | class MyTransport(object): 52 | my = True 53 | default = None 54 | 55 | def __init__(self, default_transport): 56 | self.default = default_transport 57 | 58 | def __str__(self): 59 | return str(self.default) 60 | 61 | class SipConf(object): 62 | default_port = 5060 63 | default_transport = 'udp' 64 | 65 | try: my_address 66 | except: my_address = MyAddress() 67 | 68 | try: my_port 69 | except: my_port = MyPort(default_port) 70 | 71 | try: my_transport 72 | except: my_transport = MyTransport(default_transport) 73 | 74 | try: my_uaname 75 | except: my_uaname = 'Sippy' 76 | 77 | try: allow_formats 78 | except: allow_formats = None 79 | 80 | try: autoconvert_tel_url 81 | except: autoconvert_tel_url = False 82 | 83 | @staticmethod 84 | def port_needed(port, transport, local_transport): 85 | return not('my' in dir(transport) and local_transport == SipConf.default_transport \ 86 | and int(port) == SipConf.default_port) 87 | -------------------------------------------------------------------------------- /sippy/SipContact.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipAddressHF import SipAddressHF 28 | from sippy.SipGenericHF import SipGenericHF 29 | from sippy.SipConf import SipConf 30 | from sippy.SipAddress import SipAddress 31 | from sippy.SipURL import SipURL 32 | from sippy.ESipHeaderIgnore import ESipHeaderIgnore 33 | 34 | class SipContact(SipAddressHF): 35 | hf_names = ('contact', 'm') 36 | asterisk = False 37 | 38 | def __init__(self, body = None, address = None): 39 | if body == '*': 40 | SipGenericHF.__init__(self, body) 41 | self.asterisk = True 42 | return 43 | SipAddressHF.__init__(self, body, address) 44 | if body is not None or address is not None: return 45 | curl = SipURL(host = SipConf.my_address, port = SipConf.my_port, 46 | transport = SipConf.my_transport) 47 | self.address = SipAddress(name = 'Anonymous', url = curl) 48 | 49 | def getCanName(self, name, compact = False): 50 | if compact: 51 | return 'm' 52 | return 'Contact' 53 | 54 | def parse(self): 55 | if not self.asterisk: 56 | return SipAddressHF.parse(self) 57 | -------------------------------------------------------------------------------- /sippy/SipContentLength.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipNumericHF import SipNumericHF 28 | 29 | class SipContentLength(SipNumericHF): 30 | hf_names = ('content-length', 'l') 31 | 32 | def getCanName(self, name, compact = False): 33 | if compact: 34 | return 'l' 35 | return 'Content-Length' 36 | -------------------------------------------------------------------------------- /sippy/SipContentType.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipGenericHF import SipGenericHF 28 | 29 | class SipContentType(SipGenericHF): 30 | hf_names = ('content-type', 'c') 31 | name = None 32 | params = None 33 | 34 | def __init__(self, body): 35 | SipGenericHF.__init__(self, body) 36 | 37 | def parse(self): 38 | parts = self.body.split(';') 39 | self.name = parts.pop(0) 40 | params = [] 41 | for part in parts: 42 | param = part.split('=', 1) 43 | params.append(param) 44 | self.params = dict(params) 45 | self.parsed = True 46 | 47 | def getCanName(self, name, compact = False): 48 | if compact: 49 | return 'c' 50 | return 'Content-Type' 51 | 52 | def getCopy(self): 53 | if not self.parsed: 54 | return SipContentType(self.body) 55 | copy = SipContentType(None) 56 | copy.name = self.name 57 | copy.params = self.params.copy() 58 | copy.parsed = True 59 | return copy 60 | 61 | def __str__(self): 62 | if not self.parsed: 63 | return super().__str__() 64 | s = [self.name,] 65 | s += [f'{k}={v}' for k, v in self.params.items()] 66 | return ';'.join(s) 67 | 68 | if __name__ == '__main__': 69 | t = 'multipart/mixed;boundary=OSS-unique-boundary-42' 70 | ct1 = SipContentType(t) 71 | ct1.parse() 72 | assert f'{ct1}' == t 73 | assert ct1.name == 'multipart/mixed' 74 | assert ct1.params['boundary'] == 'OSS-unique-boundary-42' 75 | ct2 = ct1.getCopy() 76 | assert f'{ct2}' == t 77 | ct1.params['some'] = 'value' 78 | assert f'{ct1}' == t + ';some=value' 79 | assert f'{ct2}' == t 80 | -------------------------------------------------------------------------------- /sippy/SipDiversion.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2016 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 sippy.SipAddressHF import SipAddressHF 28 | 29 | class SipDiversion(SipAddressHF): 30 | hf_names = ('diversion',) 31 | 32 | def getCanName(self, name, compact = False): 33 | return 'Diversion' 34 | -------------------------------------------------------------------------------- /sippy/SipExpires.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipNumericHF import SipNumericHF 28 | 29 | class SipExpires(SipNumericHF): 30 | hf_names = ('expires',) 31 | 32 | def __init__(self, body = None, number = 300): 33 | SipNumericHF.__init__(self, body, number) 34 | -------------------------------------------------------------------------------- /sippy/SipFrom.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 random import random 28 | from hashlib import md5 29 | from time import time 30 | from math import floor 31 | from sippy.SipAddressHF import SipAddressHF 32 | from sippy.SipAddress import SipAddress 33 | from sippy.SipURL import SipURL 34 | from sippy.SipConf import SipConf 35 | 36 | TOKEN_CHARSET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-.!%*_+`\'~' 37 | _clen = len(TOKEN_CHARSET) 38 | DEFAULT_TTAG_LEN = 32 39 | 40 | def gen_test_tag(len = DEFAULT_TTAG_LEN): 41 | r = '' 42 | for i in range(0, len): 43 | r += TOKEN_CHARSET[int(floor(random() * _clen))] 44 | return r 45 | 46 | class SipFrom(SipAddressHF): 47 | hf_names = ('from', 'f') 48 | relaxedparser = True 49 | default_name = 'Anonymous' 50 | 51 | def __init__(self, body = None, address = None): 52 | SipAddressHF.__init__(self, body, address) 53 | if body is None and address == None: 54 | self.address = SipAddress(name = self.default_name, url = SipURL(host = SipConf.my_address, port = SipConf.my_port)) 55 | 56 | def getTag(self): 57 | return self.address.getParam('tag') 58 | 59 | def genTag(self): 60 | salt = str((random() * 1000000000) + time()) 61 | self.address.setParam('tag', md5(salt.encode()).hexdigest()) 62 | 63 | def setTag(self, value): 64 | self.address.setParam('tag', value) 65 | 66 | def delTag(self): 67 | self.address.delParam('tag') 68 | 69 | def getCanName(self, name, compact = False): 70 | if compact: 71 | return 'f' 72 | return 'From' 73 | -------------------------------------------------------------------------------- /sippy/SipGenericHF.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 | class SipGenericHF(object): 28 | hf_names = None # Set this in each subclass!! 29 | body = None 30 | parsed = False 31 | 32 | def __init__(self, body, name = None): 33 | self.body = body 34 | if name != None: 35 | self.hf_names = (name.lower(),) 36 | 37 | def parse(self): 38 | pass 39 | 40 | def localStr(self, local_addr = None): 41 | return self.__str__() 42 | 43 | def __str__(self): 44 | return self.body 45 | 46 | def getCopy(self): 47 | return self.__class__(self.body) 48 | 49 | def getCanName(self, name, compact = False): 50 | return name.capitalize() 51 | -------------------------------------------------------------------------------- /sippy/SipMaxForwards.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipNumericHF import SipNumericHF 28 | 29 | class SipMaxForwards(SipNumericHF): 30 | hf_names = ('max-forwards',) 31 | 32 | def __init__(self, body = None, number = 70): 33 | SipNumericHF.__init__(self, body, number) 34 | 35 | def getCanName(self, name, compact = False): 36 | return 'Max-Forwards' 37 | -------------------------------------------------------------------------------- /sippy/SipNumericHF.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipGenericHF import SipGenericHF 28 | 29 | class SipNumericHF(SipGenericHF): 30 | number = None 31 | 32 | def __init__(self, body = None, number = 0): 33 | SipGenericHF.__init__(self, body) 34 | if body is None: 35 | self.parsed = True 36 | self.number = number 37 | 38 | def parse(self): 39 | self.number = int(self.body) 40 | self.parsed = True 41 | 42 | def __str__(self): 43 | if not self.parsed: 44 | return self.body 45 | return str(self.number) 46 | 47 | def getCopy(self): 48 | if not self.parsed: 49 | return self.__class__(body = self.body) 50 | return self.__class__(number = self.number) 51 | 52 | def getNum(self): 53 | return self.number 54 | 55 | def incNum(self, incr:int = 1): 56 | self.number += incr 57 | return self.number 58 | -------------------------------------------------------------------------------- /sippy/SipPAssertedIdentity.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipFrom import SipFrom 28 | 29 | class SipPAssertedIdentity(SipFrom): 30 | hf_names = ('p-asserted-identity',) 31 | 32 | def getCanName(self, name, compact = False): 33 | return 'P-Asserted-Identity' 34 | 35 | def getAddr(self): 36 | return self.address.url.getAddr() 37 | -------------------------------------------------------------------------------- /sippy/SipPath.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2006-2024 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.SipRoute import SipRoute 27 | 28 | class SipPath(SipRoute): 29 | hf_names = ('path',) 30 | 31 | def __init__(self, body = None, address = None): 32 | super().__init__(body, address) 33 | if body is None and address is None: 34 | self.getUrl().lr = True 35 | 36 | def getCanName(self, name, compact = False): 37 | return 'Path' 38 | -------------------------------------------------------------------------------- /sippy/SipProxyAuthenticate.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipWWWAuthenticate import SipWWWAuthenticate 28 | from sippy.SipProxyAuthorization import SipProxyAuthorization 29 | 30 | class SipProxyAuthenticate(SipWWWAuthenticate): 31 | hf_names = ('proxy-authenticate',) 32 | aclass = SipProxyAuthorization 33 | 34 | def getCanName(self, name, compact = False): 35 | return 'Proxy-Authenticate' 36 | -------------------------------------------------------------------------------- /sippy/SipProxyAuthorization.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipAuthorization import SipAuthorization 28 | 29 | class SipProxyAuthorization(SipAuthorization): 30 | hf_names = ('proxy-authorization',) 31 | 32 | def getCanName(self, name, compact = False): 33 | return 'Proxy-Authorization' 34 | -------------------------------------------------------------------------------- /sippy/SipRAck.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 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.SipCSeq import SipCSeq 27 | 28 | class SipRAck(SipCSeq): 29 | hf_names = ('rack',) 30 | rseq = None 31 | 32 | def __init__(self, body = None, rseq = None, cseq = None, method = None): 33 | if body is None: 34 | self.rseq = rseq 35 | SipCSeq.__init__(self, cseq = cseq, method = method) 36 | return 37 | SipCSeq.__init__(self, body) 38 | 39 | def parse(self): 40 | rseq, cseq, self.method = self.body.split(None, 2) 41 | self.rseq = int(rseq) 42 | self.cseq = int(cseq) 43 | self.parsed = True 44 | 45 | def getCopy(self): 46 | if not self.parsed: 47 | return SipRAck(self.body) 48 | return SipRAck(rseq = self.rseq, cseq = self.cseq, method = self.method) 49 | 50 | def getCanName(self, name, compact = False): 51 | return 'RAck' 52 | 53 | def getRSeq(self): 54 | cseq, method = SipCSeq.getCSeq(self) 55 | return (self.rseq, cseq, method) 56 | 57 | def __str__(self): 58 | if not self.parsed: 59 | return self.body 60 | return '%d %d %s' % (self.rseq, self.cseq, self.method) 61 | 62 | if __name__ == '__main__': 63 | ra1 = SipRAck(body = '5 10 INVITE') 64 | ra1.parse() 65 | print(ra1.rseq, ra1.cseq, ra1.method) 66 | ra1.cseq = 100 67 | print(str(ra1)) 68 | -------------------------------------------------------------------------------- /sippy/SipRSeq.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 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.SipNumericHF import SipNumericHF 27 | 28 | class SipRSeq(SipNumericHF): 29 | hf_names = ('rseq',) 30 | 31 | def __init__(self, body = None, number = 1): 32 | SipNumericHF.__init__(self, body, number) 33 | 34 | def getCanName(self, name, compact = False): 35 | return 'RSeq' 36 | 37 | if __name__ == '__main__': 38 | rs = SipRSeq(body = '50') 39 | rs.parse() 40 | print(rs.number) 41 | rs.number = 100 42 | print(str(rs)) 43 | -------------------------------------------------------------------------------- /sippy/SipReason.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipGenericHF import SipGenericHF 28 | 29 | class SipReason(SipGenericHF): 30 | ''' 31 | Class that implements RFC 3326 Reason header field. 32 | ''' 33 | hf_names = ('reason',) 34 | protocol = None 35 | cause = None 36 | reason = None 37 | 38 | def __init__(self, body = None, protocol = None, cause = None, reason = None): 39 | SipGenericHF.__init__(self, body) 40 | if body is None: 41 | self.parsed = True 42 | self.protocol = protocol 43 | self.cause = cause 44 | self.reason = reason 45 | 46 | def parse(self): 47 | protocol, reason_params = self.body.split(';', 1) 48 | self.protocol = protocol.strip() 49 | while len(reason_params:=reason_params.lstrip()) > 0: 50 | rp_name, reason_params = (p:=reason_params.split('=', 1))[0], p[1].lstrip() if len(p) > 1 else '' 51 | assert rp_name in ('cause', 'text') 52 | if rp_name == 'text' and reason_params.startswith('"'): 53 | rp_value, reason_params = (p:=reason_params[1:].split('"', 1))[0], p[1] if len(p) > 1 else '' 54 | reason_params = '' if len(p:=reason_params.split(';', 1)) == 1 else p[1] 55 | else: 56 | rp_value, reason_params = (p:=reason_params.split(';', 1))[0], p[1] if len(p) > 1 else '' 57 | if rp_name == 'cause': 58 | self.cause = int(rp_value) 59 | elif rp_name == 'text': 60 | self.reason = rp_value 61 | assert(self.cause is not None) 62 | self.parsed = True 63 | 64 | def __str__(self): 65 | if not self.parsed: 66 | return self.body 67 | if self.reason == None: 68 | return '%s; cause=%d' % (self.protocol, self.cause) 69 | return '%s; cause=%d; text="%s"' % (self.protocol, self.cause, self.reason) 70 | 71 | def getCopy(self): 72 | if not self.parsed: 73 | return SipReason(self.body) 74 | return SipReason(protocol = self.protocol, cause = self.cause, reason = self.reason) 75 | 76 | if __name__ == '__main__': 77 | ours = 'Reason: Q.850; cause=31; text="db4e8de3-ef1e-4427-b6b8-afc339de9f0d;Callee only had Trouter endpoints registered and PNH got delivery errors from all of them."' 78 | tset = (ours, 'Reason: Q.850;cause=31;text="db4e8de3-ef1e-4427-b6b8-afc339de9f0d;Callee only had Trouter endpoints registered and PNH got delivery errors from all of them."', 79 | 'Reason: Q.850 ; cause= 31; text= "db4e8de3-ef1e-4427-b6b8-afc339de9f0d;Callee only had Trouter endpoints registered and PNH got delivery errors from all of them." ', 80 | 'Reason: Q.850 ; text= "db4e8de3-ef1e-4427-b6b8-afc339de9f0d;Callee only had Trouter endpoints registered and PNH got delivery errors from all of them." ; cause= 31 ') 81 | for r in tset: 82 | s = SipReason(r) 83 | s.parse() 84 | assert str(s) == ours 85 | -------------------------------------------------------------------------------- /sippy/SipRecordRoute.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipFrom import SipFrom 28 | 29 | class SipRecordRoute(SipFrom): 30 | hf_names = ('record-route',) 31 | relaxedparser = False 32 | 33 | def getCanName(self, name, compact = False): 34 | if name == 'record-route': 35 | return 'Record-Route' 36 | else: 37 | return 'Route' 38 | 39 | def getAddr(self): 40 | return self.address.url.getAddr() 41 | 42 | def getTAddr(self): 43 | return self.address.url.getTAddr() 44 | -------------------------------------------------------------------------------- /sippy/SipReferTo.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipAddressHF import SipAddressHF 28 | 29 | class SipReferTo(SipAddressHF): 30 | hf_names = ('refer-to', 'r') 31 | -------------------------------------------------------------------------------- /sippy/SipReferredBy.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipAddressHF import SipAddressHF 28 | 29 | class SipReferredBy(SipAddressHF): 30 | hf_names = ('referred-by',) 31 | -------------------------------------------------------------------------------- /sippy/SipReplaces.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipGenericHF import SipGenericHF 28 | 29 | class SipReplaces(SipGenericHF): 30 | hf_names = ('replaces',) 31 | call_id = None 32 | from_tag = None 33 | to_tag = None 34 | early_only = False 35 | params = None 36 | 37 | def __init__(self, body = None, call_id = None, from_tag = None, to_tag = None, \ 38 | early_only = False, params = None): 39 | SipGenericHF.__init__(self, body) 40 | if body is not None: 41 | return 42 | self.parsed = True 43 | self.params = [] 44 | self.call_id = call_id 45 | self.from_tag = from_tag 46 | self.to_tag = to_tag 47 | self.early_only = early_only 48 | if params != None: 49 | self.params = params[:] 50 | 51 | def parse(self): 52 | self.params = [] 53 | params = self.body.split(';') 54 | self.call_id = params.pop(0) 55 | for param in params: 56 | if param.startswith('from-tag='): 57 | self.from_tag = param[len('from-tag='):] 58 | elif param.startswith('to-tag='): 59 | self.to_tag = param[len('to-tag='):] 60 | elif param == 'early-only': 61 | self.early_only = True 62 | else: 63 | self.params.append(param) 64 | self.parsed = True 65 | 66 | def __str__(self): 67 | if not self.parsed: 68 | return self.body 69 | res = '%s;from-tag=%s;to-tag=%s' % (self.call_id, self.from_tag, self.to_tag) 70 | if self.early_only: 71 | res += ';early-only' 72 | for param in self.params: 73 | res += ';' + param 74 | return res 75 | 76 | def getCopy(self): 77 | if not self.parsed: 78 | return SipReplaces(self.body) 79 | return SipReplaces(call_id = self.call_id, from_tag = self.from_tag, to_tag = self.to_tag, \ 80 | early_only = self.early_only, params = self.params) 81 | -------------------------------------------------------------------------------- /sippy/SipResponse.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipMsg import SipMsg 28 | from sippy.SipHeader import SipHeader 29 | 30 | class SipResponse(SipMsg): 31 | scode = None 32 | reason = None 33 | sipver = None 34 | lossemul = 0 35 | 36 | def __init__(self, buf = None, scode = None, reason = None, sipver = None, to = None, fr0m = None, callid = None, vias = None, 37 | cseq = None, body = None, rrs = (), server = None): 38 | SipMsg.__init__(self, buf) 39 | if buf != None: 40 | if self.scode > 100 and self.scode < 400: 41 | SipMsg.init_body(self) 42 | return 43 | self.scode, self.reason, self.sipver = scode, reason, sipver 44 | if vias != None: 45 | self.appendHeaders([SipHeader(name = 'via', body = x) for x in vias]) 46 | self.appendHeaders([SipHeader(name = 'record-route', body = x) for x in rrs]) 47 | self.appendHeader(SipHeader(name = 'from', body = fr0m)) 48 | self.appendHeader(SipHeader(name = 'to', body = to)) 49 | self.appendHeader(SipHeader(name = 'call-id', body = callid)) 50 | self.appendHeader(SipHeader(name = 'cseq', body = cseq)) 51 | if server != None: 52 | self.appendHeader(SipHeader(name = 'server', bodys = server)) 53 | if body is not None: 54 | self.setBody(body) 55 | 56 | def setSL(self, startline): 57 | sstartline = startline.split(None, 2) 58 | if len(sstartline) == 2: 59 | # Some brain-damaged UAs don't include reason in some cases 60 | self.sipver, scode = sstartline 61 | self.reason = 'Unspecified' 62 | else: 63 | self.sipver, scode, self.reason = startline.split(None, 2) 64 | self.scode = int(scode) 65 | 66 | def setSCode(self, scode, reason): 67 | self.scode = scode 68 | self.reason = reason 69 | 70 | def getSL(self, local_addr = None): 71 | return self.sipver + ' ' + str(self.scode) + ' ' + self.reason 72 | 73 | def getSCode(self): 74 | return (self.scode, self.reason) 75 | 76 | def getCopy(self): 77 | cself = SipMsg.getCopy(self) 78 | cself.scode = self.scode 79 | cself.reason = self.reason 80 | cself.sipver = self.sipver 81 | return cself 82 | -------------------------------------------------------------------------------- /sippy/SipRoute.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipFrom import SipFrom 28 | 29 | class SipRoute(SipFrom): 30 | hf_names = ('route',) 31 | relaxedparser = False 32 | default_name = None 33 | 34 | def getCanName(self, name, compact = False): 35 | if name == 'record-route': 36 | return 'Record-Route' 37 | else: 38 | return 'Route' 39 | 40 | def getAddr(self): 41 | return self.address.url.getAddr() 42 | -------------------------------------------------------------------------------- /sippy/SipServer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipGenericHF import SipGenericHF 28 | from sippy.SipConf import SipConf 29 | 30 | class SipServer(SipGenericHF): 31 | hf_names = ('server',) 32 | name = None 33 | 34 | def __init__(self, body = None, name = None): 35 | SipGenericHF.__init__(self, body) 36 | self.parsed = True 37 | if body is not None: 38 | self.name = body 39 | elif name != None: 40 | self.name = name 41 | else: 42 | self.name = SipConf.my_uaname 43 | 44 | def __str__(self): 45 | return self.name 46 | 47 | def getCopy(self): 48 | return self.__class__(name = self.name) 49 | -------------------------------------------------------------------------------- /sippy/SipSupported.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipGenericHF import SipGenericHF 28 | 29 | from functools import reduce 30 | 31 | class SipSupported(SipGenericHF): 32 | hf_names = ('supported',) 33 | caps = None 34 | 35 | def __init__(self, body = None, caps = None): 36 | SipGenericHF.__init__(self, body) 37 | if body is None: 38 | self.parsed = True 39 | self.caps = caps[:] 40 | 41 | def parse(self): 42 | self.caps = [x.strip() for x in self.body.split(',')] 43 | self.parsed = True 44 | 45 | def __str__(self): 46 | if not self.parsed: 47 | return self.body 48 | return reduce(lambda x,y: '%s,%s' % (x, y), self.caps) 49 | 50 | def getCopy(self): 51 | if not self.parsed: 52 | return SipSupported(self.body) 53 | return SipSupported(caps = self.caps) 54 | 55 | def getCanName(self, name, compact = False): 56 | if compact: 57 | return 'k' 58 | return 'Supported' 59 | -------------------------------------------------------------------------------- /sippy/SipTo.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipFrom import SipFrom 28 | 29 | class SipTo(SipFrom): 30 | hf_names = ('to', 't') 31 | relaxedparser = True 32 | 33 | def getCanName(self, name, compact = False): 34 | if compact: 35 | return 't' 36 | return 'To' 37 | -------------------------------------------------------------------------------- /sippy/SipUserAgent.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipServer import SipServer 28 | 29 | class SipUserAgent(SipServer): 30 | hf_names = ('user-agent',) 31 | 32 | def getCanName(self, name, compact = False): 33 | return 'User-Agent' 34 | -------------------------------------------------------------------------------- /sippy/SipWarning.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipGenericHF import SipGenericHF 28 | 29 | import socket 30 | 31 | class SipWarning(SipGenericHF): 32 | hf_names = ('warning',) 33 | code = 399 34 | agent = socket.gethostname() 35 | text = None 36 | 37 | def __init__(self, body = None, cself = None, code = None, text = None): 38 | SipGenericHF.__init__(self, body) 39 | if body is not None: 40 | return 41 | self.parsed = True 42 | if cself != None: 43 | self.code, self.agent, self.text = \ 44 | cself.code, cself.agent, cself.text 45 | else: 46 | self.agent = socket.gethostname() 47 | if code != None: 48 | self.code = code 49 | self.text = text.replace('"', "'") 50 | 51 | def parse(self): 52 | code, self.agent, text = self.body.split(None, 2) 53 | self.text = text.strip('"') 54 | self.code = int(code) 55 | self.parsed = True 56 | 57 | def __str__(self): 58 | if not self.parsed: 59 | return self.body 60 | return '%d %s "%s"' % (self.code, self.agent, self.text) 61 | 62 | def getCopy(self): 63 | return self.__class__(cself = self) 64 | -------------------------------------------------------------------------------- /sippy/StatefulProxy.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.SipVia import SipVia 28 | from sippy.SipHeader import SipHeader 29 | 30 | class StatefulProxy: 31 | global_config = None 32 | destination = None 33 | 34 | def __init__(self, global_config, destination): 35 | print(destination) 36 | self.global_config = global_config 37 | self.destination = (destination, 'udp') 38 | 39 | def recvRequest(self, req): 40 | try: 41 | max_forwards = req.getHFBody('max-forwards') 42 | mfval = max_forwards.incNum(incr=-1) 43 | if mfval <= 0: 44 | return (req.genResponse(483, 'Too Many Hops'), None, None) 45 | except IndexError: 46 | req.appendHeader(SipHeader(name = 'max-forwards')) 47 | via0 = SipVia() 48 | via0.genBranch() 49 | via1 = req.getHF('via') 50 | if req.getMethod() == 'REGISTER': 51 | self.insertPath(req) 52 | req.insertHeaderBefore(via1, SipHeader(name = 'via', body = via0)) 53 | req.setTarget(self.destination) 54 | print(req) 55 | self.global_config['_sip_tm'].newTransaction(req, self.recvResponse) 56 | return (None, None, None) 57 | 58 | def recvResponse(self, resp): 59 | resp.removeHeader(resp.getHF('via')) 60 | self.global_config['_sip_tm'].sendResponse(resp) 61 | 62 | def insertPath(self, req): 63 | try: 64 | supported = req.getHFBody('supported') 65 | except IndexError: pass 66 | else: 67 | if 'path' in supported.caps: 68 | mypath = SipHeader(name = 'path') 69 | if req.countHFs('path') == 0: 70 | req.appendHeader(mypath) 71 | else: 72 | path1 = req.getHF('path') 73 | req.insertHeaderBefore(path1, mypath) 74 | -------------------------------------------------------------------------------- /sippy/Time/Timeout.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 __future__ import print_function 28 | 29 | #import sys 30 | #sys.path.append('..') 31 | 32 | from sippy.Core.EventDispatcher import ED2 33 | from sippy.Time.MonoTime import MonoTime 34 | 35 | def Timeout(timeout_cb, ival, nticks = 1, *cb_params): 36 | el = ED2.regTimer(timeout_cb, ival, nticks, False, *cb_params) 37 | el.go() 38 | return el 39 | 40 | def TimeoutInact(timeout_cb, ival, nticks = 1, *cb_params): 41 | return ED2.regTimer(timeout_cb, ival, nticks, False, *cb_params) 42 | 43 | def TimeoutAbsMono(timeout_cb, mtime, *cb_params): 44 | if not isinstance(mtime, MonoTime): 45 | raise TypeError('mtime is not MonoTime') 46 | el = ED2.regTimer(timeout_cb, mtime, None, True, *cb_params) 47 | el.go() 48 | return el 49 | 50 | def testTimeout(): 51 | def test1(arguments, testnum): 52 | print(testnum) 53 | arguments['test'] = True 54 | ED2.breakLoop() 55 | 56 | def test2(arguments, testnum): 57 | print(testnum) 58 | arguments['test'] = 'bar' 59 | ED2.breakLoop() 60 | 61 | arguments = {'test':False} 62 | timeout_1 = Timeout(test1, 0, 1, arguments, 'test1') 63 | ED2.loop() 64 | assert(arguments['test']) 65 | timeout_1 = Timeout(test1, 0.1, 1, arguments, 'test2') 66 | timeout_2 = Timeout(test2, 0.2, 1, arguments, 'test3') 67 | timeout_1.cancel() 68 | ED2.loop() 69 | assert(arguments['test'] == 'bar') 70 | 71 | arguments = {'test':False} 72 | timeout_1 = TimeoutAbsMono(test1, MonoTime(), arguments, 'test4') 73 | ED2.loop() 74 | assert(arguments['test']) 75 | 76 | timeout_1 = TimeoutAbsMono(test1, MonoTime().getOffsetCopy(0.1), arguments, 'test5') 77 | timeout_2 = TimeoutAbsMono(test2, MonoTime().getOffsetCopy(0.2), arguments, 'test6') 78 | timeout_1.cancel() 79 | ED2.loop() 80 | assert(arguments['test'] == 'bar') 81 | 82 | def testTimeoutAbsMono(): 83 | def test1(arguments, testnum, mtm): 84 | arguments['delay'] = mtm.offsetFromNow() 85 | print(testnum, arguments['delay']) 86 | arguments['test'] = True 87 | ED2.breakLoop() 88 | 89 | def test2(arguments, testnum, mtm): 90 | arguments['delay'] = mtm.offsetFromNow() 91 | print(testnum, arguments['delay']) 92 | arguments['test'] = 'bar' 93 | ED2.breakLoop() 94 | 95 | mt = MonoTime() 96 | arguments = {'test':False, 'delay':None} 97 | timeout_1 = TimeoutAbsMono(test1, mt, arguments, 'test1', mt) 98 | ED2.loop() 99 | assert(arguments['test']) 100 | assert(arguments['delay'] < 0.1) 101 | mt1 = mt.getOffsetCopy(0.1) 102 | mt2 = mt.getOffsetCopy(0.2) 103 | arguments = {'test':False, 'delay':None} 104 | timeout_1 = TimeoutAbsMono(test1, mt1, arguments, 'test2', mt1) 105 | timeout_2 = TimeoutAbsMono(test2, mt2, arguments, 'test3', mt2) 106 | timeout_1.cancel() 107 | ED2.loop() 108 | assert(arguments['test'] == 'bar') 109 | assert(arguments['delay'] < 0.1) 110 | 111 | if __name__ == '__main__': 112 | testTimeout() 113 | testTimeoutAbsMono() 114 | -------------------------------------------------------------------------------- /sippy/Time/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/b2bua/e08039df0f48479c836f1f0e28ce6792d68422b3/sippy/Time/__init__.py -------------------------------------------------------------------------------- /sippy/UaStateDead.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.UaStateGeneric import UaStateGeneric 28 | 29 | class UaStateDead(UaStateGeneric): 30 | sname = 'Dead' 31 | dead = True 32 | 33 | def __init__(self, ua): 34 | super().__init__(None) 35 | if ua.cId != None: 36 | ua.global_config['_sip_tm'].unregConsumer(ua, str(ua.cId)) 37 | ua.tr = None 38 | ua.event_cb = None 39 | ua.conn_cbs = () 40 | ua.disc_cbs = () 41 | ua.fail_cbs = () 42 | ua.on_local_sdp_change = None 43 | ua.on_remote_sdp_change = None 44 | ua.expire_timer = None 45 | ua.no_progress_timer = None 46 | ua.credit_timer = None 47 | ua.reqs = None 48 | # Keep this at the very end of processing 49 | for callback in ua.dead_cbs: 50 | callback(ua) 51 | ua.dead_cbs = () 52 | ua.cleanup() 53 | # Break cross-ref chain 54 | self.ua = None 55 | -------------------------------------------------------------------------------- /sippy/UaStateDisconnected.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.Time.Timeout import Timeout 28 | from sippy.UaStateGeneric import UaStateGeneric 29 | 30 | class UaStateDisconnected(UaStateGeneric): 31 | sname = 'Disconnected' 32 | 33 | def __init__(self, ua): 34 | super().__init__(ua) 35 | ua.on_local_sdp_change = None 36 | ua.on_remote_sdp_change = None 37 | Timeout(self.goDead, ua.godead_timeout) 38 | 39 | def recvRequest(self, req): 40 | if req.getMethod() == 'BYE': 41 | #print('BYE received in the Disconnected state') 42 | req.sendResponse(200, 'OK') 43 | else: 44 | req.sendResponse(500, 'Disconnected') 45 | return None 46 | 47 | def goDead(self): 48 | #print('Time in Disconnected state expired, going to the Dead state') 49 | self.ua.changeState((self.ua.UaStateDead,)) 50 | -------------------------------------------------------------------------------- /sippy/UaStateFailed.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 sippy.Time.Timeout import Timeout 28 | from sippy.UaStateGeneric import UaStateGeneric 29 | 30 | class UaStateFailed(UaStateGeneric): 31 | sname = 'Failed' 32 | 33 | def __init__(self, ua): 34 | super().__init__(ua) 35 | ua.on_local_sdp_change = None 36 | ua.on_remote_sdp_change = None 37 | Timeout(self.goDead, ua.godead_timeout) 38 | 39 | def goDead(self): 40 | #print('Time in Failed state expired, going to the Dead state') 41 | self.ua.changeState((self.ua.UaStateDead,)) 42 | -------------------------------------------------------------------------------- /sippy/UaStateGeneric.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 2 | # Copyright (c) 2006-2014 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 | class UaStateGeneric(object): 28 | sname = 'Generic' 29 | ua = None 30 | connected = False 31 | dead = False 32 | 33 | def __init__(self, ua): 34 | self.ua = ua 35 | 36 | def recvRequest(self, req): 37 | return None 38 | 39 | def recvResponse(self, resp, tr): 40 | return None 41 | 42 | def recvEvent(self, event): 43 | return None 44 | 45 | def cancel(self, rtime, req): 46 | return None 47 | 48 | def onStateChange(self, newstate): 49 | pass 50 | 51 | def __str__(self): 52 | return self.sname 53 | -------------------------------------------------------------------------------- /sippy/__init__.py: -------------------------------------------------------------------------------- 1 | # Dummy file to make this directory a package. 2 | -------------------------------------------------------------------------------- /sippy/misc.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/python 2 | # 3 | # Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved. 4 | # Copyright (c) 2006-2014 Sippy Software, Inc. All rights reserved. 5 | # 6 | # All rights reserved. 7 | # 8 | # Redistribution and use in source and binary forms, with or without modification, 9 | # are permitted provided that the following conditions are met: 10 | # 11 | # 1. Redistributions of source code must retain the above copyright notice, this 12 | # list of conditions and the following disclaimer. 13 | # 14 | # 2. Redistributions in binary form must reproduce the above copyright notice, 15 | # this list of conditions and the following disclaimer in the documentation and/or 16 | # other materials provided with the distribution. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | import os, sys, socket 30 | 31 | def daemonize(logfile = None): 32 | # Fork once 33 | if os.fork() != 0: 34 | os._exit(0) 35 | # Create new session 36 | os.setsid() 37 | if os.fork() != 0: 38 | os._exit(0) 39 | os.chdir('/') 40 | fd = os.open('/dev/null', os.O_RDWR) 41 | os.dup2(fd, sys.__stdin__.fileno()) 42 | if logfile != None: 43 | fake_stdout = open(logfile, 'a', 1) 44 | sys.stdout = fake_stdout 45 | sys.stderr = fake_stdout 46 | fd = fake_stdout.fileno() 47 | os.dup2(fd, sys.__stdout__.fileno()) 48 | os.dup2(fd, sys.__stderr__.fileno()) 49 | if logfile == None: 50 | os.close(fd) 51 | 52 | def local4remote(lookup_address, family = socket.AF_INET): 53 | skt = socket.socket(family, socket.SOCK_DGRAM) 54 | ai = socket.getaddrinfo(lookup_address, None, family) 55 | if family == socket.AF_INET: 56 | _address = (ai[0][4][0], 12345) 57 | else: 58 | _address = (ai[0][4][0], 12345, ai[0][4][2], ai[0][4][3]) 59 | skt.connect(_address) 60 | if family == socket.AF_INET: 61 | laddress = skt.getsockname()[0] 62 | else: 63 | laddress = '[%s]' % skt.getsockname()[0] 64 | return laddress 65 | -------------------------------------------------------------------------------- /sippy/tools/getmonotime.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015-2018 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 | import getopt, sys 27 | import os.path 28 | 29 | def usage(): 30 | sys.stderr.write('Usage: %s [-r] [-S sippy_path] [-C clock_name]\n' % \ 31 | (os.path.basename(sys.argv[0]))) 32 | sys.exit(1) 33 | 34 | if __name__ == '__main__': 35 | sippy_path = None 36 | 37 | try: 38 | opts, args = getopt.getopt(sys.argv[1:], 'rS:C:') 39 | except getopt.GetoptError: 40 | usage() 41 | 42 | out_realtime = False 43 | clock_name = 'CLOCK_MONOTONIC' 44 | for o, a in opts: 45 | if o == '-S': 46 | sippy_path = a.strip() 47 | continue 48 | if o == '-r': 49 | out_realtime = True 50 | continue 51 | if o == '-C': 52 | clock_name = a.strip() 53 | continue 54 | 55 | if sippy_path != None: 56 | sys.path.insert(0, sippy_path) 57 | 58 | exec('from sippy.Time.clock_dtime import clock_getdtime, %s' % clock_name) 59 | if not out_realtime: 60 | print(clock_getdtime(eval(clock_name))) 61 | else: 62 | from sippy.Time.clock_dtime import CLOCK_REALTIME 63 | print("%f %f" % (clock_getdtime(eval(clock_name)), clock_getdtime(CLOCK_REALTIME))) 64 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sippy/b2bua/e08039df0f48479c836f1f0e28ce6792d68422b3/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_B2BRoute.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from sippy.B2B.Route import B2BRoute 3 | 4 | class TestB2BRoute(unittest.TestCase): 5 | test_route = '200110508667@b2bua.org;cli=16046288900;rid=-1;expires=30;np_expires=5;ash=Name%3AValue' 6 | test_po = f'{test_route};po_proc=VAL2Xattrs[x-attr=val1,y-attr=val2]' 7 | 8 | def test_B2BRoute_basic(self): 9 | route = B2BRoute(self.test_route) 10 | self.assertIsInstance(route, B2BRoute) 11 | 12 | def test_B2BRoute_with_po_proc(self): 13 | route = B2BRoute(self.test_po) 14 | self.assertIsInstance(route, B2BRoute) 15 | 16 | def test_B2BRoute_parameters(self): 17 | route = B2BRoute(self.test_po) 18 | self.assertEqual(route.params['po_proc'].radius_parameters, [('x-attr', 'val1'), ('y-attr', 'val2')]) 19 | self.assertEqual(route.cli, '16046288900') 20 | self.assertEqual(route.cld, '200110508667') 21 | self.assertEqual(route.params['rid'], '-1') 22 | self.assertEqual(route.expires, 30) 23 | self.assertEqual(route.no_progress_expires, 5) 24 | self.assertEqual(str(route.extra_headers[0]), 'Name: Value') 25 | 26 | if __name__ == '__main__': 27 | unittest.main() 28 | -------------------------------------------------------------------------------- /tests/test_B2BTransforms.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from sippy.B2B.Transforms import getTransProc 3 | 4 | class FakeRequest(): 5 | test: unittest.TestCase 6 | def __init__(self, test): self.test = test 7 | def getHFs(self, name): 8 | got = name 9 | want = 'X-foo-hdr' 10 | self.test.assertEqual(want, got) 11 | return tuple() 12 | class FakeEvent(): pass 13 | class FakeAccounting(): 14 | test: unittest.TestCase 15 | def __init__(self, test): self.test = test 16 | def addAttributes(self, attrs): 17 | got = len(attrs) 18 | want = 2 19 | self.test.assertEqual(want, got) 20 | for a in ('foo', 'bar'), ('baz', 'xxx'): 21 | with self.test.subTest(a=a): 22 | got = a in attrs 23 | want = True 24 | self.test.assertEqual(want, got) 25 | 26 | class FakeCC(): 27 | def __init__(self, acctA = None, acctO = None): self.acctA, self.acctO = acctA, acctO 28 | 29 | class TestB2BTransforms(unittest.TestCase): 30 | 31 | def test_getTransProc(self): 32 | transformations = [ 33 | ('HDR2Xattrs[X-foo-hdr]', (FakeCC(), FakeRequest(self))), 34 | ('VAL2Xattrs[foo=bar,baz=xxx]', (FakeCC(acctA = FakeAccounting(self), 35 | acctO = FakeAccounting(self)), FakeEvent())), 36 | ('VAL2XattrsA[foo=bar,baz=xxx]', (FakeCC(acctA = FakeAccounting(self)), FakeEvent())), 37 | ('VAL2XattrsO[foo=bar,baz=xxx]', (FakeCC(acctO = FakeAccounting(self)), FakeEvent())), 38 | ('Nop[]', (None, None)), 39 | ] 40 | 41 | for t, args in transformations: 42 | with self.subTest(t=t, args=args): 43 | p = getTransProc(t) 44 | self.assertIsNotNone(p, f"getTransProc({t}) returned None") 45 | self.assertIsNone(p(*args), f"__call__({args}) returned not None") 46 | 47 | if __name__ == '__main__': 48 | unittest.main() 49 | -------------------------------------------------------------------------------- /tests/test_Multipart.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from sippy.MsgBody import MsgBody 3 | from sippy.MultipartMixBody import MultipartMixBody 4 | from sippy.SipContentType import SipContentType 5 | 6 | # body from RFC 8147 https://datatracker.ietf.org/doc/html/rfc8147 7 | ecall_body1 = ( 8 | "--boundaryZZZ\r\n" 9 | "Content-Disposition: by-reference\r\n" 10 | "Content-Type: application/EmergencyCallData.Control+xml\r\n" 11 | "Content-ID: <3456789012@atlanta.example.com>\r\n" 12 | "\r\n" 13 | '\r\n' 14 | '\r\n' 15 | '\r\n' 16 | "\r\n" 17 | "--boundaryZZZ--\r\n" 18 | ) 19 | ecall_part0 = ( 20 | '\r\n' 21 | '\r\n' 22 | '\r\n' 23 | "\r\n" 24 | ) 25 | 26 | 27 | class TestMultipart(unittest.TestCase): 28 | def test_multipart_unsorted_headers(self): 29 | ct = SipContentType("multipart/mixed;boundary=boundaryZZZ") 30 | ct.parse() 31 | got = MsgBody(ecall_body1, mtype=ct) 32 | got.parse() 33 | mp = got.content 34 | self.assertEqual(MultipartMixBody, type(mp)) 35 | self.assertEqual(1, len(mp.parts)) 36 | p0 = mp.parts[0] 37 | self.assertEqual(MsgBody, type(p0)) 38 | #print(p0.mtype) 39 | self.assertEqual( 40 | "application/EmergencyCallData.Control+xml", p0.mtype.localStr()) 41 | h0 = mp.part_headers[0] 42 | self.assertEqual("content-disposition", h0[0].name) 43 | self.assertEqual("content-id", h0[1].name) 44 | # check that part body is without headers 45 | self.assertEqual(ecall_part0, p0.content) 46 | -------------------------------------------------------------------------------- /tests/test_SdbBody.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from sippy.SdpBody import SdpBody 4 | 5 | # TODO Rename test names so each one represents clearly what it is 6 | # testing for. 7 | 8 | # Test data... 9 | sdp_multi_stream = """v=0 10 | o=LifeSize 1366021474 2 IN IP4 192.168.190.101 11 | s=LifeSize SIP Call 12 | i=LifeSize Icon_600/LS_RM3_1.1.0 (16) 13 | c=IN IP4 192.168.190.101 14 | b=CT:2500 15 | t=0 0 16 | m=audio 60022 RTP/AVP 113 123 122 121 9 0 8 101 17 | a=rtpmap:113 MP4A-LATM/32000 18 | a=fmtp:113 profile-level-id=2;object=2;bitrate=96000 19 | a=rtpmap:123 G7221/32000 20 | a=fmtp:123 bitrate=48000 21 | a=rtpmap:122 G7221/32000 22 | a=fmtp:122 bitrate=32000 23 | a=rtpmap:121 G7221/32000 24 | a=fmtp:121 bitrate=24000 25 | a=rtpmap:9 G722/8000 26 | a=rtpmap:0 PCMU/8000 27 | a=rtpmap:8 PCMA/8000 28 | a=rtpmap:101 telephone-event/8000 29 | m=video 60024 RTP/AVP 96 97 30 | b=TIAS:2500000 31 | a=rtpmap:96 H264/90000 32 | a=fmtp:96 profile-level-id=42802a;max-mbps=490000;max-fs=8192;packetization-mode=1 33 | a=rtpmap:97 H263-1998/90000 34 | a=fmtp:97 CIF4=1;CIF=1;QCIF=1 35 | a=content:main 36 | a=rtcp-fb:* ccm fir 37 | a=rtcp-fb:* nack pli 38 | a=rtcp-fb:* ccm tmmbr 39 | m=application 60026 RTP/AVP 100 40 | a=rtpmap:100 H224/4800 41 | m=control 60028 RTP/AVP 96 42 | b=TIAS:2500000 43 | a=rtpmap:96 H264/90000 44 | a=fmtp:96 profile-level-id=428029;max-mbps=245000;max-fs=8192;packetization-mode=1 45 | a=label:3 46 | a=rtcp-fb:* ccm fir 47 | a=rtcp-fb:* nack pli 48 | a=rtcp-fb:* ccm tmmbr 49 | a=inactive 50 | m=application 60032 TCP/BFCP * 51 | a=floorctrl:c-only 52 | a=setup:active 53 | a=connection:new 54 | """ 55 | 56 | 57 | sdp_h323 = """v=0 58 | o=H.323 45678 901234 IN IP4 192.168.1.2 59 | s=H.323 SIP Call 60 | c=IN IP4 224.5.6.7 61 | t=0 0 62 | a=type:H332 63 | m=audio 49230 RTP/AVP 0 64 | m=video 49232 RTP/AVP 31 65 | m=application 12349 udp wb 66 | m=control 49234 H323 mc 67 | c=IN IP4 134.134.157.81 68 | """ 69 | 70 | 71 | sdp_single_audio = """v=0 72 | o=- 12333 12333 IN IP4 192.168.1.42 73 | s=- 74 | c=IN IP4 192.168.1.42 75 | t=0 0 76 | m=audio 16442 RTP/AVP 18 0 2 4 8 96 97 98 101 77 | a=rtpmap:18 G729a/8000 78 | a=rtpmap:0 PCMU/8000 79 | a=rtpmap:2 G726-32/8000 80 | a=rtpmap:4 G723/8000 81 | a=rtpmap:8 PCMA/8000 82 | a=rtpmap:96 G726-40/8000 83 | a=rtpmap:97 G726-24/8000 84 | a=rtpmap:98 G726-16/8000 85 | a=rtpmap:101 telephone-event/8000 86 | a=fmtp:101 0-15 87 | a=ptime:30 88 | a=sendrecv 89 | """ 90 | 91 | class TestSdpBodyFunctions(unittest.TestCase): 92 | 93 | def test_localStr(self): 94 | laddr = (('1.2.3.4', 12345), 'udp') 95 | got = SdpBody(sdp_multi_stream).localStr(laddr) 96 | want = sdp_multi_stream.replace('\n','\r\n') 97 | self.assertEqual(want, got) 98 | 99 | def test_str_override_multiple_stremas(self): 100 | got = SdpBody(sdp_multi_stream) 101 | want = sdp_multi_stream.replace('\n','\r\n') 102 | self.assertEqual(want, str(got)) 103 | 104 | def test_str_override_h323_sdp(self): 105 | got = SdpBody(sdp_h323) 106 | want = sdp_h323.replace('\n','\r\n') 107 | self.assertEqual(want, str(got)) 108 | 109 | def test_str_override_single_audio(self): 110 | got = SdpBody(sdp_single_audio) 111 | want = sdp_single_audio.replace('\n','\r\n') 112 | self.assertEqual(want, str(got)) 113 | 114 | def test_getPTbyName(self): 115 | got = SdpBody(sdp_single_audio).sections[0].getPTbyName('G726-40/8000') 116 | want = 96 117 | self.assertEqual(want, got) 118 | 119 | if __name__ == '__main__': 120 | unittest.main() 121 | -------------------------------------------------------------------------------- /tests/test_Udp_server.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from sippy.Udp_server import self_test 3 | 4 | class TestUdp_server(unittest.TestCase): 5 | def test_run(self): 6 | # Create an instance of the self_test class 7 | test_instance = self_test() 8 | 9 | # Run the existing logic 10 | test_instance.run() 11 | 12 | # You can add assertions here if needed to check the final state or outcomes. 13 | # For example, you might want to check if npongs reached 0. 14 | self.assertEqual(test_instance.npongs, 0) 15 | 16 | if __name__ == '__main__': 17 | unittest.main() 18 | --------------------------------------------------------------------------------