Skip to content

Commit

Permalink
Merge pull request #186 from tayler6000/bugfix/Issue-185
Browse files Browse the repository at this point in the history
Bugfix/Issue-185
  • Loading branch information
tayler6000 authored Nov 24, 2023
2 parents 2ddd5bc + 74174a5 commit 992ef1a
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 22 deletions.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
author = 'Tayler J Porter'

# The full version, including alpha/beta/rc tags
release = '1.6.5'
release = '1.6.6-rc.1'

master_doc = 'index'

Expand Down
82 changes: 73 additions & 9 deletions pyVoIP/SIP.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from enum import Enum, IntEnum
from threading import Timer, Lock
from typing import Any, Callable, Dict, List, Optional, Tuple, TYPE_CHECKING
from pyVoIP.VoIP.status import PhoneStatus
import pyVoIP
import hashlib
import socket
Expand All @@ -13,6 +14,7 @@


if TYPE_CHECKING:
from pyVoIP.VoIP import VoIPPhone
from pyVoIP import RTP


Expand Down Expand Up @@ -801,9 +803,11 @@ def __init__(
port: int,
username: str,
password: str,
phone: "VoIPPhone",
myIP="0.0.0.0",
myPort=5060,
callCallback: Optional[Callable[[SIPMessage], None]] = None,
fatalCallback: Optional[Callable[..., None]] = None,
):
self.NSD = False
self.server = server
Expand All @@ -812,7 +816,10 @@ def __init__(
self.username = username
self.password = password

self.phone = phone

self.callCallback = callCallback
self.fatalCallback = fatalCallback

self.tags: List[str] = []
self.tagLibrary = {"register": self.genTag()}
Expand All @@ -832,6 +839,7 @@ def __init__(
self.urnUUID = self.gen_urn_uuid()

self.registerThread: Optional[Timer] = None
self.registerFailures = 0
self.recvLock = Lock()

def recv(self) -> None:
Expand Down Expand Up @@ -952,6 +960,8 @@ def start(self) -> None:
t.start()

def stop(self) -> None:
if not self.NSD:
return
self.NSD = False
if self.registerThread:
# Only run if registerThread exists
Expand Down Expand Up @@ -1638,7 +1648,24 @@ def bye(self, request: SIPMessage) -> None:
self.out.sendto(message.encode("utf8"), (self.server, self.port))

def deregister(self) -> bool:
try:
deregistered = self.__deregister()
if not deregistered:
debug("DEREGISTERATION FAILED")
return False
else:
self.phone._status = PhoneStatus.INACTIVE

return deregistered
except BaseException as e:
debug(f"DEREGISTERATION ERROR: {e}")
if type(e) is OSError:
raise
return False

def __deregister(self) -> bool:
self.recvLock.acquire()
self.phone._status = PhoneStatus.DEREGISTERING
firstRequest = self.genFirstRequest(deregister=True)
self.out.sendto(firstRequest.encode("utf8"), (self.server, self.port))

Expand All @@ -1648,6 +1675,7 @@ def deregister(self) -> bool:
if ready[0]:
resp = self.s.recv(8192)
else:
self.recvLock.release()
raise TimeoutError("Deregistering on SIP Server timed out")

response = SIPMessage(resp)
Expand All @@ -1667,6 +1695,7 @@ def deregister(self) -> bool:
# At this point, it's reasonable to assume that
# this is caused by invalid credentials.
debug("Unauthorized")
self.recvLock.release()
raise InvalidAccountInfoError(
"Invalid Username or "
+ "Password for SIP server "
Expand All @@ -1680,6 +1709,7 @@ def deregister(self) -> bool:
# with new urn:uuid or reply with expire 0
self._handle_bad_request()
else:
self.recvLock.release()
raise TimeoutError("Deregistering on SIP Server timed out")

if response.status == SIPStatus(500):
Expand All @@ -1694,7 +1724,47 @@ def deregister(self) -> bool:
return False

def register(self) -> bool:
try:
registered = self.__register()
if not registered:
debug("REGISTERATION FAILED")
self.registerFailures += 1
else:
self.phone._status = PhoneStatus.REGISTERED
self.registerFailures = 0

if self.registerFailures >= pyVoIP.REGISTER_FAILURE_THRESHOLD:
debug("Too many registration failures, stopping.")
self.stop()
self.fatalCallback()
return False
self.__start_register_timer()

return registered
except BaseException as e:
debug(f"REGISTERATION ERROR: {e}")
self.registerFailures += 1
if self.registerFailures >= pyVoIP.REGISTER_FAILURE_THRESHOLD:
self.stop()
self.fatalCallback()
return False
self.__start_register_timer(delay=0)

def __start_register_timer(self, delay: Optional[int] = None):
if delay is None:
delay = self.default_expires - 5
if self.NSD:
debug("New register thread")
# self.subscribe(response)
self.registerThread = Timer(delay, self.register)
self.registerThread.name = (
"SIP Register CSeq: " + f"{self.registerCounter.x}"
)
self.registerThread.start()

def __register(self) -> bool:
self.recvLock.acquire()
self.phone._status = PhoneStatus.REGISTERING
firstRequest = self.genFirstRequest()
self.out.sendto(firstRequest.encode("utf8"), (self.server, self.port))

Expand All @@ -1704,6 +1774,7 @@ def register(self) -> bool:
if ready[0]:
resp = self.s.recv(8192)
else:
self.recvLock.release()
raise TimeoutError("Registering on SIP Server timed out")

response = SIPMessage(resp)
Expand Down Expand Up @@ -1742,6 +1813,7 @@ def register(self) -> bool:
debug("\nRECEIVED")
debug(response.summary())
debug("=" * 50)
self.recvLock.release()
raise InvalidAccountInfoError(
"Invalid Username or "
+ "Password for SIP server "
Expand All @@ -1755,6 +1827,7 @@ def register(self) -> bool:
# with new urn:uuid or reply with expire 0
self._handle_bad_request()
else:
self.recvLock.release()
raise TimeoutError("Registering on SIP Server timed out")

if response.status == SIPStatus(407):
Expand Down Expand Up @@ -1782,15 +1855,6 @@ def register(self) -> bool:

self.recvLock.release()
if response.status == SIPStatus.OK:
if self.NSD:
# self.subscribe(response)
self.registerThread = Timer(
self.default_expires - 5, self.register
)
self.registerThread.name = (
"SIP Register CSeq: " + f"{self.registerCounter.x}"
)
self.registerThread.start()
return True
else:
raise InvalidAccountInfoError(
Expand Down
19 changes: 9 additions & 10 deletions pyVoIP/VoIP.py → pyVoIP/VoIP/VoIP.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from enum import Enum
from pyVoIP import SIP, RTP
from pyVoIP.VoIP.status import PhoneStatus
from threading import Timer, Lock
from typing import Any, Callable, Dict, List, Optional
import audioop
Expand Down Expand Up @@ -41,14 +42,6 @@ class CallState(Enum):
ENDED = "ENDED"


class PhoneStatus(Enum):
INACTIVE = "INACTIVE"
REGISTERING = "REGISTERING"
REGISTERED = "REGISTERED"
DEREGISTERING = "DEREGISTERING"
FAILED = "FAILED"


class VoIPCall:
def __init__(
self,
Expand Down Expand Up @@ -521,9 +514,11 @@ def __init__(
port,
username,
password,
phone=self,
myIP=self.myIP,
myPort=sipPort,
callCallback=self.callback,
fatalCallback=self.fatal,
)

def callback(self, request: SIP.SIPMessage) -> None:
Expand Down Expand Up @@ -666,15 +661,14 @@ def start(self) -> None:
self._status = PhoneStatus.REGISTERING
try:
self.sip.start()
self._status = PhoneStatus.REGISTERED
self.NSD = True
except Exception:
self._status = PhoneStatus.FAILED
self.sip.stop()
self.NSD = False
raise

def stop(self) -> None:
def stop(self, failed=False) -> None:
self._status = PhoneStatus.DEREGISTERING
for x in self.calls.copy():
try:
Expand All @@ -683,6 +677,11 @@ def stop(self) -> None:
pass
self.sip.stop()
self._status = PhoneStatus.INACTIVE
if failed:
self._status = PhoneStatus.FAILED

def fatal(self) -> None:
self.stop(failed=True)

def call(self, number: str) -> VoIPCall:
port = self.request_port()
Expand Down
10 changes: 10 additions & 0 deletions pyVoIP/VoIP/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from pyVoIP.VoIP import status
from pyVoIP.VoIP import VoIP

InvalidRangeError = VoIP.InvalidRangeError
InvalidStateError = VoIP.InvalidStateError
NoPortsAvailableError = VoIP.NoPortsAvailableError
CallState = VoIP.CallState
PhoneStatus = status.PhoneStatus
VoIPCall = VoIP.VoIPCall
VoIPPhone = VoIP.VoIPPhone
14 changes: 14 additions & 0 deletions pyVoIP/VoIP/status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from enum import Enum


__all__ = [
"PhoneStatus",
]


class PhoneStatus(Enum):
INACTIVE = "INACTIVE"
REGISTERING = "REGISTERING"
REGISTERED = "REGISTERED"
DEREGISTERING = "DEREGISTERING"
FAILED = "FAILED"
8 changes: 7 additions & 1 deletion pyVoIP/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__all__ = ["SIP", "RTP", "VoIP"]

version_info = (1, 6, "5")
version_info = (1, 6, "6-rc.1")

__version__ = ".".join([str(x) for x in version_info])

Expand All @@ -13,6 +13,12 @@
"""
TRANSMIT_DELAY_REDUCTION = 0.0

"""
If registration fails this many times, VoIPPhone's status will be set to FAILED
and the phone will stop.
"""
REGISTER_FAILURE_THRESHOLD = 3


def debug(s, e=None):
if DEBUG:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

setup(
name="pyVoIP",
version="1.6.5",
version="1.6.6-rc.1",
description="PyVoIP is a pure python VoIP/SIP/RTP library.",
long_description=long_description,
long_description_content_type="text/markdown",
Expand Down

0 comments on commit 992ef1a

Please sign in to comment.