diff --git a/sipsimple/core/_core.headers.pxi b/sipsimple/core/_core.headers.pxi index 1ae29ab5..a2be7f76 100644 --- a/sipsimple/core/_core.headers.pxi +++ b/sipsimple/core/_core.headers.pxi @@ -1,1939 +1,1939 @@ # Classes # cdef object BaseHeader_richcmp(object self, object other, int op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseHeader): return NotImplemented if op == 2: return self.name == other.name and self.body == other.body else: return self.name != other.name or self.body != other.body cdef class BaseHeader: normal_type = Header frozen_type = FrozenHeader def __init__(self, *args, **kwargs): raise TypeError("BaseHeader cannot be instantiated directly") def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.name, self.body) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseHeader_richcmp(self, other, op) def Header_new(cls, BaseHeader header): return cls(header.name, header.body) cdef class Header(BaseHeader): def __init__(self, str name not None, str body not None): self.name = name self.body = body property name: def __get__(self): return self._name def __set__(self, str name not None): self._name = name property body: def __get__(self): return self._body def __set__(self, str body not None): self._body = body new = classmethod(Header_new) del Header_new def FrozenHeader_new(cls, BaseHeader header): if isinstance(header, cls): return header return cls(header.name, header.body) cdef class FrozenHeader(BaseHeader): def __init__(self, str name not None, str body not None): self.name = name self.body = body def __hash__(self): return hash((self.name, self.body)) def __richcmp__(self, other, op): return BaseHeader_richcmp(self, other, op) new = classmethod(FrozenHeader_new) del FrozenHeader_new class ContentType(str): def __init__(self, value): if '' in value.partition('/'): raise ValueError('invalid content type') @property def type(self): return self.partition('/')[0] @property def subtype(self): return self.partition('/')[2] cdef object BaseContentTypeHeader_richcmp(object self, object other, object op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseContentTypeHeader): return NotImplemented if op == 2: return self.content_type == other.content_type and self.parameters == other.parameters else: return self.content_type != other.content_type and self.parameters != other.parameters cdef class BaseContentTypeHeader: normal_type = ContentTypeHeader frozen_type = FrozenContentTypeHeader def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.content_type, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __unicode__(self): return unicode(self.__str__(), encoding='utf-8') def __richcmp__(self, other, op): return BaseContentTypeHeader_richcmp(self, other, op) property name: def __get__(self): return "Content-Type" property body: def __get__(self): if self.parameters: parameters = ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) - for name, value in self.parameters.iteritems()]) + for name, value in list(self.parameters.items())]) else: parameters = "" return self.content_type + parameters def ContentTypeHeader_new(cls, BaseContentTypeHeader header): return cls(header.content_type, dict(header.parameters)) cdef class ContentTypeHeader(BaseContentTypeHeader): def __init__(self, str content_type, dict parameters=None): self.content_type = content_type self.parameters = parameters if parameters is not None else {} property content_type: def __get__(self): try: return ContentType(self._content_type) except ValueError: return None def __set__(self, str content_type): self._content_type = content_type property parameters: def __get__(self): return self._parameters def __set__(self, dict parameters not None): self._parameters = parameters new = classmethod(ContentTypeHeader_new) del ContentTypeHeader_new def FrozenContentTypeHeader_new(cls, BaseContentTypeHeader header): if isinstance(header, cls): return header return cls(header.content_type, frozendict(header.parameters)) cdef class FrozenContentTypeHeader(BaseContentTypeHeader): def __init__(self, str content_type, frozendict parameters not None=frozendict()): if not self.initialized: self._content_type = content_type self.parameters = parameters self.initialized = 1 property content_type: def __get__(self): try: return ContentType(self._content_type) except ValueError: return None def __hash__(self): return hash((self.content_type, self.parameters)) def __richcmp__(self, other, op): return BaseContentTypeHeader_richcmp(self, other, op) new = classmethod(FrozenContentTypeHeader_new) del FrozenContentTypeHeader_new cdef object BaseContactHeader_richcmp(object self, object other, object op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseContactHeader): return NotImplemented if op == 2: return self.name == other.name and self.uri == other.uri and self.display_name == other.display_name and self.parameters == other.parameters else: return self.name != other.name or self.uri != other.uri or self.display_name != other.display_name or self.parameters != other.parameters cdef class BaseContactHeader: normal_type = ContactHeader frozen_type = FrozenContactHeader def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r, %r, %r)" % (self.__class__.__name__, self.uri, self.display_name, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __unicode__(self): return unicode(self.__str__(), encoding='utf-8') def __richcmp__(self, other, op): return BaseContactHeader_richcmp(self, other, op) property name: def __get__(self): return "Contact" property body: def __get__(self): if self.uri is None: return "*" if self.parameters: parameters = ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) - for name, value in self.parameters.iteritems()]) + for name, value in list(self.parameters.items())]) else: parameters = "" if self.display_name: return '"%s" <%s>%s' % (self.display_name.encode('utf-8'), self.uri, parameters) else: return '<%s>%s' % (self.uri, parameters) def ContactHeader_new(cls, BaseContactHeader header): return cls(SIPURI.new(header.uri), header.display_name, dict(header.parameters)) cdef class ContactHeader(BaseContactHeader): def __init__(self, SIPURI uri, unicode display_name=None, dict parameters=None): if uri is None and (display_name is not None or parameters not in (None, {})): raise ValueError("uri cannot be None if display_name or parameters are specified") self.uri = uri self.display_name = display_name self.parameters = parameters if parameters is not None else {} property uri: def __get__(self): return self._uri def __set__(self, SIPURI uri): if uri is None and (self.display_name is not None or self.parameters != {}): raise ValueError("uri cannot be None if display_name or parameters are specified") self._uri = uri property display_name: def __get__(self): return self._display_name def __set__(self, unicode display_name): if self.uri is None and display_name is not None: raise ValueError("display_name cannot be specified if uri is None") self._display_name = display_name property parameters: def __get__(self): return self._parameters def __set__(self, dict parameters not None): if self.uri is None and parameters != {}: raise ValueError("parameters cannot be specified if uri is None") self._parameters = parameters property q: def __get__(self): value = self.parameters.get("q", None) if value is not None: value = float(value) return value def __set__(self, object value): if value is None: self.parameters.pop("q", None) else: if self.uri is None: raise ValueError("parameters cannot be specified if uri is None") self.parameters["q"] = str(float(value)) property expires: def __get__(self): value = self.parameters.get("expires", None) if value is not None: value = int(value) return value def __set__(self, object value): if value is None: self.parameters.pop("expires", None) else: if self.uri is None: raise ValueError("parameters cannot be specified if uri is None") self.parameters["expires"] = str(int(value)) new = classmethod(ContactHeader_new) del ContactHeader_new def FrozenContactHeader_new(cls, BaseContactHeader header): if isinstance(header, cls): return header return cls(FrozenSIPURI.new(header.uri), header.display_name, frozendict(header.parameters)) cdef class FrozenContactHeader(BaseContactHeader): def __init__(self, FrozenSIPURI uri, unicode display_name=None, frozendict parameters not None=frozendict()): if not self.initialized: if uri is None and (display_name is not None or parameters not in (None, {})): raise ValueError("uri cannot be None if display_name or parameters are specified") self.uri = uri self.display_name = display_name self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.uri, self.display_name, self.parameters)) def __richcmp__(self, other, op): return BaseContactHeader_richcmp(self, other, op) property q: def __get__(self): value = self.parameters.get("q", None) if value is not None: value = float(value) return value property expires: def __get__(self): value = self.parameters.get("expires", None) if value is not None: value = int(value) return value new = classmethod(FrozenContactHeader_new) del FrozenContactHeader_new cdef object BaseIdentityHeader_richcmp(object self, object other, int op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseIdentityHeader): return NotImplemented if op == 2: return self.name == other.name and self.uri == other.uri and self.display_name == other.display_name and self.parameters == other.parameters else: return self.name != other.name or self.uri != other.uri or self.display_name != other.display_name or self.parameters != other.parameters cdef class BaseIdentityHeader: def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r, %r, %r)" % (self.__class__.__name__, self.uri, self.display_name, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __unicode__(self): return unicode(self.__str__(), encoding='utf-8') def __richcmp__(self, other, op): return BaseIdentityHeader_richcmp(self, other, op) property body: def __get__(self): if self.parameters: parameters = ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) - for name, value in self.parameters.iteritems()]) + for name, value in list(self.parameters.items())]) else: parameters = "" if self.display_name: return '"%s" <%s>%s' % (self.display_name.encode('utf-8'), self.uri, parameters) else: return '<%s>%s' % (self.uri, parameters) def IdentityHeader_new(cls, BaseIdentityHeader contact_header): return cls(SIPURI.new(contact_header.uri), contact_header.display_name, dict(contact_header.parameters)) cdef class IdentityHeader(BaseIdentityHeader): property uri: def __get__(self): return self._uri def __set__(self, SIPURI uri not None): self._uri = uri property parameters: def __get__(self): return self._parameters def __set__(self, dict parameters not None): self._parameters = parameters new = classmethod(IdentityHeader_new) del IdentityHeader_new def FrozenIdentityHeader_new(cls, BaseIdentityHeader contact_header): if isinstance(contact_header, cls): return contact_header return cls(FrozenSIPURI.new(contact_header.uri), contact_header.display_name, frozendict(contact_header.parameters)) cdef class FrozenIdentityHeader(BaseIdentityHeader): def __hash__(self): return hash((self.uri, self.display_name, self.parameters)) def __richcmp__(self, other, op): return BaseIdentityHeader_richcmp(self, other, op) new = classmethod(FrozenIdentityHeader_new) del FrozenIdentityHeader_new cdef class FromHeader(IdentityHeader): normal_type = FromHeader frozen_type = FrozenFromHeader def __init__(self, SIPURI uri not None, unicode display_name=None, dict parameters=None): self.uri = uri self.display_name = display_name self.parameters = parameters if parameters is not None else {} property tag: def __get__(self): return self.parameters.get("tag", None) def __set__(self, str value): if value is None: self.parameters.pop("tag", None) else: self.parameters["tag"] = value property name: def __get__(self): return "From" cdef class FrozenFromHeader(FrozenIdentityHeader): normal_type = FromHeader frozen_type = FrozenFromHeader def __init__(self, FrozenSIPURI uri not None, unicode display_name=None, frozendict parameters not None=frozendict()): if not self.initialized: self.uri = uri self.display_name = display_name self.parameters = parameters self.initialized = 1 property tag: def __get__(self): return self.parameters.get("tag", None) property name: def __get__(self): return "From" cdef class ToHeader(IdentityHeader): normal_type = ToHeader frozen_type = FrozenToHeader def __init__(self, SIPURI uri not None, unicode display_name=None, dict parameters=None): self.uri = uri self.display_name = display_name self.parameters = parameters if parameters is not None else {} property tag: def __get__(self): return self.parameters.get("tag", None) def __set__(self, str value): if value is None: self.parameters.pop("tag", None) else: self.parameters["tag"] = value property name: def __get__(self): return "To" cdef class FrozenToHeader(FrozenIdentityHeader): normal_type = ToHeader frozen_type = FrozenToHeader def __init__(self, FrozenSIPURI uri not None, unicode display_name=None, frozendict parameters not None=frozendict()): if not self.initialized: self.uri = uri self.display_name = display_name self.parameters = parameters self.initialized = 1 property tag: def __get__(self): return self.parameters.get("tag", None) property name: def __get__(self): return "To" cdef class RouteHeader(IdentityHeader): normal_type = RouteHeader frozen_type = FrozenRouteHeader def __init__(self, SIPURI uri not None, unicode display_name=None, dict parameters=None): self.uri = uri self.display_name = display_name self.parameters = parameters if parameters is not None else {} property name: def __get__(self): return "Route" cdef class FrozenRouteHeader(FrozenIdentityHeader): normal_type = RouteHeader frozen_type = FrozenRouteHeader def __init__(self, FrozenSIPURI uri not None, unicode display_name=None, frozendict parameters not None=frozendict()): if not self.initialized: self.uri = uri self.display_name = display_name self.parameters = parameters self.initialized = 1 property name: def __get__(self): return "Route" cdef class RecordRouteHeader(IdentityHeader): normal_type = RecordRouteHeader frozen_type = FrozenRecordRouteHeader def __init__(self, SIPURI uri not None, unicode display_name=None, dict parameters=None): self.uri = uri self.display_name = display_name self.parameters = parameters if parameters is not None else {} property name: def __get__(self): return "Record-Route" cdef class FrozenRecordRouteHeader(FrozenIdentityHeader): normal_type = RecordRouteHeader frozen_type = FrozenRecordRouteHeader def __init__(self, FrozenSIPURI uri not None, unicode display_name=None, frozendict parameters not None=frozendict()): if not self.initialized: self.uri = uri self.display_name = display_name self.parameters = parameters self.initialized = 1 property name: def __get__(self): return "Record-Route" cdef object BaseRetryAfterHeader_richcmp(object self, object other, int op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseRetryAfterHeader): return NotImplemented if op == 2: return self.seconds == other.seconds and self.comment == other.comment and self.parameters == other.parameters else: return self.seconds != other.seconds or self.comment != other.comment or self.parameters != other.parameters cdef class BaseRetryAfterHeader: normal_type = RetryAfterHeader frozen_type = FrozenRetryAfterHeader def __init__(self, *args, **kwargs): raise TypeError("BaseRetryAfterHeader cannot be instantiated directly") def __repr__(self): return "%s(%r, %r, %r)" % (self.__class__.__name__, self.seconds, self.comment, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseRetryAfterHeader_richcmp(self, other, op) property name: def __get__(self): return "Retry-After" property body: def __get__(self): string = str(self.seconds) if self.comment is not None: string += " (%s)" % self.comment if self.parameters: string += ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) - for name, value in self.parameters.iteritems()]) + for name, value in list(self.parameters.items())]) return string def RetryAfterHeader_new(cls, BaseRetryAfterHeader header): return cls(header.seconds, header.comment, dict(header.parameters)) cdef class RetryAfterHeader(BaseRetryAfterHeader): def __init__(self, int seconds, str comment=None, dict parameters=None): self.seconds = seconds self.comment = comment self.parameters = parameters if parameters is not None else {} property parameters: def __get__(self): return self._parameters def __set__(self, dict parameters not None): self._parameters = parameters property duration: def __get__(self): value = self.parameters.get("duration", None) if value is not None: value = int(value) return value def __set__(self, object value): if value is None: self.parameters.pop("duration", None) else: self.parameters["duration"] = str(int(value)) new = classmethod(RetryAfterHeader_new) del RetryAfterHeader_new def FrozenRetryAfterHeader_new(cls, BaseRetryAfterHeader header): if isinstance(header, cls): return header return cls(header.seconds, header.comment, frozendict(header.parameters)) cdef class FrozenRetryAfterHeader(BaseRetryAfterHeader): def __init__(self, int seconds, str comment=None, frozendict parameters not None=frozendict()): if not self.initialized: self.seconds = seconds self.comment = comment self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.seconds, self.comment, self.parameters)) def __richcmp__(self, other, op): return BaseRetryAfterHeader_richcmp(self, other, op) property duration: def __get__(self): value = self.parameters.get("duration", None) if value is not None: value = int(value) return value new = classmethod(FrozenRetryAfterHeader_new) del FrozenRetryAfterHeader_new cdef object BaseViaHeader_richcmp(object self, object other, int op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseViaHeader): return NotImplemented if op == 2: return self.transport == other.transport and self.host == other.host and self.port == other.port and self.parameters == other.parameters else: return self.transport != other.transport or self.host != other.host or self.port != other.port or self.parameters != other.parameters cdef class BaseViaHeader: normal_type = ViaHeader frozen_type = FrozenViaHeader def __init__(self, *args, **kwargs): raise TypeError("BaseViaHeader cannot be instantiated directly") def __repr__(self): return "%s(%r, %r, %r, %r)" % (self.__class__.__name__, self.transport, self.host, self.port, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseViaHeader_richcmp(self, other, op) property name: def __get__(self): return "Via" property body: def __get__(self): string = "SIP/2.0/%s %s:%d" % (self.transport, self.host, self.port) if self.parameters: string += ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) - for name, value in self.parameters.iteritems()]) + for name, value in list(self.parameters.items())]) return string def ViaHeader_new(cls, BaseViaHeader header): return cls(header.transport, header.host, header.port, dict(header.parameters)) cdef class ViaHeader(BaseViaHeader): def __init__(self, str transport not None, str host not None, int port=5060, dict parameters=None): self.transport = transport self.host = host self.port = port self.parameters = parameters if parameters is not None else {} property transport: def __get__(self): return self._transport def __set__(self, str transport not None): self._transport = transport property host: def __get__(self): return self._host def __set__(self, str host not None): self._host = host property port: def __get__(self): return self._port def __set__(self, int port): if not (0 < port <= 65535): raise ValueError("Invalid port: %d" % port) self._port = port property parameters: def __get__(self): return self._parameters def __set__(self, dict parameters not None): self._parameters = parameters property ttl: def __get__(self): value = self.parameters.get("ttl", None) if value is not None: value = int(value) return value def __set__(self, object value): if value is None: self.parameters.pop("ttl", None) else: self.parameters["ttl"] = str(int(value)) property maddr: def __get__(self): return self.parameters.get("maddr", None) def __set__(self, str value): if value is None: self.parameters.pop("maddr", None) else: self.parameters["maddr"] = value property received: def __get__(self): return self.parameters.get("received", None) def __set__(self, str value): if value is None: self.parameters.pop("received", None) else: self.parameters["received"] = value property branch: def __get__(self): return self.parameters.get("branch", None) def __set__(self, str value): if value is None: self.parameters.pop("branch", None) else: self.parameters["branch"] = value property compression: def __get__(self): return self.parameters.get("compression", None) def __set__(self, str value): if value is None: self.parameters.pop("compression", None) else: self.parameters["compression"] = value property rport: def __get__(self): value = self.parameters.get("rport", None) if value is not None: value = int(value) return value def __set__(self, object value): if value is None: self.parameters.pop("rport", None) else: self.parameters["rport"] = str(int(value)) new = classmethod(ViaHeader_new) del ViaHeader_new def FrozenViaHeader_new(cls, BaseViaHeader header): if isinstance(header, cls): return header return cls(header.transport, header.host, header.port, frozendict(header.parameters)) cdef class FrozenViaHeader(BaseViaHeader): def __init__(self, str transport not None, str host not None, int port=5060, frozendict parameters not None=frozendict()): if not self.initialized: if not (0 < port <= 65535): raise ValueError("Invalid port: %d" % port) self.transport = transport self.host = host self.port = port self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.transport, self.host, self.port, self.parameters)) def __richcmp__(self, other, op): return BaseViaHeader_richcmp(self, other, op) property ttl: def __get__(self): value = self.parameters.get("ttl", None) if value is not None: value = int(value) return value property maddr: def __get__(self): return self.parameters.get("maddr", None) property received: def __get__(self): return self.parameters.get("received", None) property branch: def __get__(self): return self.parameters.get("branch", None) property compression: def __get__(self): return self.parameters.get("compression", None) property rport: def __get__(self): value = self.parameters.get("rport", None) if value is not None: value = int(value) return value new = classmethod(FrozenViaHeader_new) del FrozenViaHeader_new cdef object BaseWarningHeader_richcmp(object self, object other, int op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseWarningHeader): return NotImplemented if op == 2: return self.code == other.code and self.agent == other.agent and self.text == other.text else: return self.code != other.code or self.agent != other.agent or self.text != other.text cdef class BaseWarningHeader: normal_type = WarningHeader frozen_type = FrozenWarningHeader def __init__(self, *args, **kwargs): raise TypeError("BaseWarningHeader cannot be instantiated directly") def __repr__(self): return "%s(%r, %r, %r)" % (self.__class__.__name__, self.code, self.agent, self.text) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseWarningHeader_richcmp(self, other, op) property name: def __get__(self): return "Warning" property body: def __get__(self): return '%d %s "%s"' % (self.code, self.agent, self.text) def WarningHeader_new(cls, BaseWarningHeader header): return cls(header.code, header.agent, header.text) cdef class WarningHeader(BaseWarningHeader): def __init__(self, int code, str agent not None, str text not None): self.code = code self.agent = agent self.text = text property code: def __get__(self): return self._code def __set__(self, int code): if code < 100 or code > 999: raise ValueError("code needs to be a 3 digit number") self._code = code property agent: def __get__(self): return self._agent def __set__(self, str agent not None): self._agent = agent property text: def __get__(self): return self._text def __set__(self, str text not None): self._text = text new = classmethod(WarningHeader_new) del WarningHeader_new def FrozenWarningHeader_new(cls, BaseWarningHeader header): if isinstance(header, cls): return header return cls(header.code, header.agent, header.text) cdef class FrozenWarningHeader(BaseWarningHeader): def __init__(self, int code, str agent not None, str text not None): if not self.initialized: if code < 100 or code > 999: raise ValueError("code needs to be a 3 digit number") self.code = code self.agent = agent self.text = text self.initialized = 1 def __hash__(self): return hash((self.code, self.agent, self.text)) def __richcmp__(self, other, op): return BaseWarningHeader_richcmp(self, other, op) new = classmethod(FrozenWarningHeader_new) del FrozenWarningHeader_new cdef object BaseEventHeader_richcmp(object self, object other, object op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseEventHeader): return NotImplemented if op == 2: return self.event == other.event and self.parameters == other.parameters else: return self.event != other.event or self.parameters != other.parameters cdef class BaseEventHeader: normal_type = EventHeader frozen_type = FrozenEventHeader def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.event, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseEventHeader_richcmp(self, other, op) property name: def __get__(self): return "Event" property body: def __get__(self): if self.parameters: parameters = ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) - for name, value in self.parameters.iteritems()]) + for name, value in list(self.parameters.items())]) else: parameters = "" return self.event + parameters def EventHeader_new(cls, BaseEventHeader header): return cls(header.event, dict(header.parameters)) cdef class EventHeader(BaseEventHeader): def __init__(self, str event not None, dict parameters=None): self.event = event self.parameters = parameters if parameters is not None else {} property id: def __get__(self): return self._parameters.get("id", None) def __set__(self, str id): if id is None: self._parameters.pop("id", None) else: self._parameters["id"] = id property parameters: def __get__(self): return self._parameters.copy() def __set__(self, dict parameters not None): self._parameters = parameters new = classmethod(EventHeader_new) del EventHeader_new def FrozenEventHeader_new(cls, BaseEventHeader header): if isinstance(header, cls): return header return cls(header.event, frozendict(header.parameters)) cdef class FrozenEventHeader(BaseEventHeader): def __init__(self, str event not None, frozendict parameters not None=frozendict()): if not self.initialized: self.event = event self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.event, self.parameters)) def __richcmp__(self, other, op): return BaseEventHeader_richcmp(self, other, op) property id: def __get__(self): return self.parameters.get("id", None) new = classmethod(FrozenEventHeader_new) del FrozenEventHeader_new cdef object BaseSubscriptionStateHeader_richcmp(object self, object other, object op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseSubscriptionStateHeader): return NotImplemented if op == 2: return self.state == other.state and self.parameters == other.parameters else: return self.state != other.state or self.parameters != other.parameters cdef class BaseSubscriptionStateHeader: normal_type = SubscriptionStateHeader frozen_type = FrozenSubscriptionStateHeader def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.state, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseSubscriptionStateHeader_richcmp(self, other, op) property name: def __get__(self): return "SubscriptionState" property body: def __get__(self): if self.parameters: parameters = ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) - for name, value in self.parameters.iteritems()]) + for name, value in list(self.parameters.items())]) else: parameters = "" return self.state + parameters def SubscriptionStateHeader_new(cls, BaseSubscriptionStateHeader header): return cls(header.state, dict(header.parameters)) cdef class SubscriptionStateHeader(BaseSubscriptionStateHeader): def __init__(self, str state not None, dict parameters=None): self.state = state self.parameters = parameters if parameters is not None else {} property reason: def __get__(self): return self._parameters.get("reason", None) def __set__(self, str reason): if reason is None: self._parameters.pop("reason", None) else: self._parameters["reason"] = reason property expires: def __get__(self): return int(self._parameters.get("expires", None)) def __set__(self, object expires): cdef int expires_i if expires is None: self._parameters.pop("expires", None) else: expires_i = expires self._parameters["expires"] = str(expires_i) property retry_after: def __get__(self): return int(self._parameters.get("retry-after", None)) def __set__(self, object retry_after): cdef int retry_after_i if retry_after is None: self._parameters.pop("retry-after", None) else: retry_after_i = retry_after self._parameters["retry-after"] = str(retry_after_i) property parameters: def __get__(self): return self._parameters.copy() def __set__(self, dict parameters not None): self._parameters = parameters new = classmethod(SubscriptionStateHeader_new) del SubscriptionStateHeader_new def FrozenSubscriptionStateHeader_new(cls, BaseSubscriptionStateHeader header): if isinstance(header, cls): return header return cls(header.state, frozendict(header.parameters)) cdef class FrozenSubscriptionStateHeader(BaseSubscriptionStateHeader): def __init__(self, str state not None, frozendict parameters not None=frozendict()): if not self.initialized: self.state = state self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.state, self.parameters)) def __richcmp__(self, other, op): return BaseSubscriptionStateHeader_richcmp(self, other, op) property reason: def __get__(self): return self.parameters.get("reason", None) property expires: def __get__(self): expires = self.parameters.get("expires", None) if expires is not None: expires = int(expires) return expires property retry_after: def __get__(self): retry_after = self.parameters.get("retry-after", None) if retry_after is not None: retry_after = int(retry_after) return retry_after new = classmethod(FrozenSubscriptionStateHeader_new) del FrozenSubscriptionStateHeader_new cdef object BaseReasonHeader_richcmp(object self, object other, object op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseSubscriptionStateHeader): return NotImplemented if op == 2: return self.protocol == other.protocol and self.parameters == other.parameters else: return self.protocol != other.protocol or self.parameters != other.parameters cdef class BaseReasonHeader: normal_type = ReasonHeader frozen_type = FrozenReasonHeader def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.protocol, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseReasonHeader_richcmp(self, other, op) property name: def __get__(self): return "Reason" property body: def __get__(self): if self.parameters: parameters = " ;" + " ;".join(["%s%s" % (name, "" if value is None else "=%s" % value) - for name, value in self.parameters.iteritems()]) + for name, value in list(self.parameters.items())]) else: parameters = "" return self.protocol + parameters def ReasonHeader_new(cls, BaseReasonHeader header): return cls(header.state, dict(header.parameters)) cdef class ReasonHeader(BaseReasonHeader): def __init__(self, str protocol not None, dict parameters=None): self.protocol = protocol self.parameters = parameters if parameters is not None else {} property cause: def __get__(self): cause = self.parameters.get("cause", None) if cause is not None: cause = int(cause) return cause def __set__(self, object cause): cdef int cause_i if cause is None: self.parameters.pop("cause", None) else: cause_i = int(cause) self.parameters["cause"] = str(cause_i) property text: def __get__(self): text = self.parameters.get("text", None) if text is not None: text = text.strip().strip('"') return text def __set__(self, str text): if text is None: self.parameters.pop("text", None) else: if not text.startswith('"'): text = '"'+text if not text[1:].endswith('"'): text = text+'"' self.parameters["text"] = text new = classmethod(ReasonHeader_new) del ReasonHeader_new def FrozenReasonHeader_new(cls, BaseReasonHeader header): if isinstance(header, cls): return header return cls(header.protocol, frozendict(header.parameters)) cdef class FrozenReasonHeader(BaseReasonHeader): def __init__(self, str protocol not None, frozendict parameters not None=frozendict()): if not self.initialized: self.protocol = protocol self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.protocol, self.parameters)) def __richcmp__(self, other, op): return BaseReasonHeader_richcmp(self, other, op) property cause: def __get__(self): cause = self.parameters.get("cause", None) if cause is not None: cause = int(cause) return cause property text: def __get__(self): text = self.parameters.get("text", None) if text is not None: text = text.strip().strip('"') return text new = classmethod(FrozenReasonHeader_new) del FrozenReasonHeader_new cdef object BaseReferToHeader_richcmp(object self, object other, object op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseReferToHeader): return NotImplemented if op == 2: return self.uri == other.uri and self.parameters == other.parameters else: return self.uri != other.uri or self.parameters != other.parameters cdef class BaseReferToHeader: normal_type = ReferToHeader frozen_type = FrozenReferToHeader def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.uri, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseReferToHeader_richcmp(self, other, op) property name: def __get__(self): return "Refer-To" property body: def __get__(self): if self.parameters: parameters = ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) - for name, value in self.parameters.iteritems()]) + for name, value in list(self.parameters.items())]) else: parameters = "" return "<%s>%s" % (self.uri, parameters) def ReferToHeader_new(cls, BaseReferToHeader header): return cls(header.uri, dict(header.parameters)) cdef class ReferToHeader(BaseReferToHeader): def __init__(self, str uri not None, dict parameters=None): self.uri = uri self.parameters = parameters if parameters is not None else {} property parameters: def __get__(self): return self._parameters def __set__(self, dict parameters not None): self._parameters = parameters new = classmethod(ReferToHeader_new) del ReferToHeader_new def FrozenReferToHeader_new(cls, BaseReferToHeader header): if isinstance(header, cls): return header return cls(header.uri, frozendict(header.parameters)) cdef class FrozenReferToHeader(BaseReferToHeader): def __init__(self, str uri not None, frozendict parameters not None=frozendict()): if not self.initialized: self.uri = uri self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.uri, self.parameters)) def __richcmp__(self, other, op): return BaseReferToHeader_richcmp(self, other, op) new = classmethod(FrozenReferToHeader_new) del FrozenReferToHeader_new cdef object BaseSubjectHeader_richcmp(object self, object other, int op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseSubjectHeader): return NotImplemented if op == 2: return self.subject == other.subject else: return self.subject != other.subject cdef class BaseSubjectHeader: def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.subject) def __str__(self): return "%s: %s" % (self.name, self.body) def __unicode__(self): return unicode(self.__str__(), encoding='utf-8') def __richcmp__(self, other, op): return BaseSubjectHeader_richcmp(self, other, op) property name: def __get__(self): return "Subject" property body: def __get__(self): return self.subject.encode('utf-8') def SubjectHeader_new(cls, BaseSubjectHeader subject_header): if isinstance(subject_header, cls): return subject_header return cls(subject_header.subject) cdef class SubjectHeader(BaseSubjectHeader): def __init__(self, unicode subject=None): if subject is None: raise ValueError('subject must be specified') self.subject = subject new = classmethod(SubjectHeader_new) del SubjectHeader_new def FrozenSubjectHeader_new(cls, BaseSubjectHeader subject_header): if isinstance(subject_header, cls): return subject_header return cls(subject_header.subject) cdef class FrozenSubjectHeader(BaseSubjectHeader): def __init__(self, unicode subject=None): if not self.initialized: if subject is None: raise ValueError('subject must be specified') self.subject = subject self.initialized = 1 def __hash__(self): return hash((self.subject)) def __richcmp__(self, other, op): return BaseSubjectHeader_richcmp(self, other, op) new = classmethod(FrozenSubjectHeader_new) del FrozenSubjectHeader_new cdef object BaseReplacesHeader_richcmp(object self, object other, int op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseReplacesHeader): return NotImplemented if op == 2: return self.call_id == other.call_id and self.from_tag == other.from_tag and self.to_tag == other.to_tag and self.early_only == other.early_only and self.parameters == other.parameters else: return self.call_id != other.call_id or self.from_tag != other.from_tag or self.to_tag != other.to_tag or self.early_only != other.early_only or self.parameters != other.parameters cdef class BaseReplacesHeader: normal_type = ReplacesHeader frozen_type = FrozenReplacesHeader def __init__(self, *args, **kwargs): raise TypeError("BaseReplacesHeader cannot be instantiated directly") def __repr__(self): return "%s(%r, %r, %r, %r, %r)" % (self.__class__.__name__, self.call_id, self.from_tag, self.to_tag, self.early_only, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseReplacesHeader_richcmp(self, other, op) property name: def __get__(self): return "Replaces" property body: def __get__(self): string = "%s;from-tag=%s;to-tag=%s" % (self.call_id, self.from_tag, self.to_tag) if self.early_only: string += ";early-only" if self.parameters: string += ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) - for name, value in self.parameters.iteritems()]) + for name, value in list(self.parameters.items())]) return string def ReplacesHeader_new(cls, BaseReplacesHeader header): return cls(header.call_id, header.from_tag, header.to_tag, header.early_only, dict(header.parameters)) cdef class ReplacesHeader(BaseReplacesHeader): def __init__(self, str call_id not None, str from_tag not None, str to_tag not None, int early_only=0, dict parameters=None): self.call_id = call_id self.from_tag = from_tag self.to_tag = to_tag self.early_only = early_only self.parameters = parameters if parameters is not None else {} property parameters: def __get__(self): return self._parameters def __set__(self, dict parameters not None): self._parameters = parameters new = classmethod(ReplacesHeader_new) del ReplacesHeader_new def FrozenReplacesHeader_new(cls, BaseReplacesHeader header): if isinstance(header, cls): return header return cls(header.call_id, header.header.from_tag, header.to_tag, header.early_only, frozendict(header.parameters)) cdef class FrozenReplacesHeader(BaseReplacesHeader): def __init__(self, str call_id not None, str from_tag not None, str to_tag not None, int early_only=0, frozendict parameters not None=frozendict()): if not self.initialized: self.call_id = call_id self.from_tag = from_tag self.to_tag = to_tag self.early_only = early_only self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.call_id, self.from_tag, self.to_tag, self.early_only, self.parameters)) def __richcmp__(self, other, op): return BaseReplacesHeader_richcmp(self, other, op) new = classmethod(FrozenReplacesHeader_new) del FrozenReplacesHeader_new # Factory functions # cdef Header Header_create(pjsip_generic_string_hdr *header): return Header(_pj_str_to_str(header.name), _pj_str_to_str(header.hvalue)) cdef FrozenHeader FrozenHeader_create(pjsip_generic_string_hdr *header): return FrozenHeader(_pj_str_to_str(header.name), _pj_str_to_str(header.hvalue)) cdef ContactHeader ContactHeader_create(pjsip_contact_hdr *header): cdef pjsip_name_addr* name_addr if header.star: return ContactHeader(None) else: uri = SIPURI_create(pjsip_uri_get_uri(header.uri)) name_addr = header.uri if name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) if header.q1000 != -1: parameters["q"] = str(float(header.q1000)/1000) if header.expires != -1: parameters["expires"] = str(header.expires) return ContactHeader(uri, display_name, parameters) cdef FrozenContactHeader FrozenContactHeader_create(pjsip_contact_hdr *header): cdef pjsip_name_addr* name_addr if header.star: return FrozenContactHeader(None) else: uri = FrozenSIPURI_create(pjsip_uri_get_uri(header.uri)) name_addr = header.uri if name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) if header.q1000 != -1: parameters["q"] = str(float(header.q1000)/1000) if header.expires != -1: parameters["expires"] = str(header.expires) return FrozenContactHeader(uri, display_name, frozendict(parameters)) cdef ContentTypeHeader ContentTypeHeader_create(pjsip_ctype_hdr *header): type_str = _pj_str_to_str(header.media.type) subtype_str = _pj_str_to_str(header.media.subtype) parameters = _pjsip_param_to_dict(&header.media.param) if subtype_str: content_type = "%s/%s" % (type_str, subtype_str) else: content_type = type_str return ContentTypeHeader(content_type, parameters) cdef FrozenContentTypeHeader FrozenContentTypeHeader_create(pjsip_ctype_hdr *header): type_str = _pj_str_to_str(header.media.type) subtype_str = _pj_str_to_str(header.media.subtype) parameters = _pjsip_param_to_dict(&header.media.param) if subtype_str: content_type = "%s/%s" % (type_str, subtype_str) else: content_type = type_str return FrozenContentTypeHeader(content_type, frozendict(parameters)) cdef FromHeader FromHeader_create(pjsip_fromto_hdr *header): cdef pjsip_name_addr* name_addr uri = SIPURI_create(pjsip_uri_get_uri(header.uri)) name_addr = header.uri if name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) if header.tag.slen > 0: parameters["tag"] = _pj_str_to_str(header.tag) return FromHeader(uri, display_name, parameters) cdef FrozenFromHeader FrozenFromHeader_create(pjsip_fromto_hdr *header): cdef pjsip_name_addr* name_addr uri = FrozenSIPURI_create(pjsip_uri_get_uri(header.uri)) name_addr = header.uri if name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) if header.tag.slen > 0: parameters["tag"] = _pj_str_to_str(header.tag) return FrozenFromHeader(uri, display_name, frozendict(parameters)) cdef ToHeader ToHeader_create(pjsip_fromto_hdr *header): cdef pjsip_name_addr* name_addr uri = SIPURI_create(pjsip_uri_get_uri(header.uri)) name_addr = header.uri if name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) if header.tag.slen > 0: parameters["tag"] = _pj_str_to_str(header.tag) return ToHeader(uri, display_name, parameters) cdef FrozenToHeader FrozenToHeader_create(pjsip_fromto_hdr *header): cdef pjsip_name_addr* name_addr uri = FrozenSIPURI_create(pjsip_uri_get_uri(header.uri)) name_addr = header.uri if name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) if header.tag.slen > 0: parameters["tag"] = _pj_str_to_str(header.tag) return FrozenToHeader(uri, display_name, frozendict(parameters)) cdef RouteHeader RouteHeader_create(pjsip_routing_hdr *header): uri = SIPURI_create(pjsip_uri_get_uri(&header.name_addr)) if header.name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(header.name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) return RouteHeader(uri, display_name, parameters) cdef FrozenRouteHeader FrozenRouteHeader_create(pjsip_routing_hdr *header): uri = FrozenSIPURI_create(pjsip_uri_get_uri(&header.name_addr)) if header.name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(header.name_addr.display), encoding='utf-8') else: display_name = None parameters = frozendict(_pjsip_param_to_dict(&header.other_param)) return FrozenRouteHeader(uri, display_name, parameters) cdef RecordRouteHeader RecordRouteHeader_create(pjsip_routing_hdr *header): uri = SIPURI_create(pjsip_uri_get_uri(&header.name_addr)) if header.name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(header.name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) return RecordRouteHeader(uri, display_name, parameters) cdef FrozenRecordRouteHeader FrozenRecordRouteHeader_create(pjsip_routing_hdr *header): uri = FrozenSIPURI_create(pjsip_uri_get_uri(&header.name_addr)) if header.name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(header.name_addr.display), encoding='utf-8') else: display_name = None parameters = frozendict(_pjsip_param_to_dict(&header.other_param)) return FrozenRecordRouteHeader(uri, display_name, parameters) cdef RetryAfterHeader RetryAfterHeader_create(pjsip_retry_after_hdr *header): seconds = header.ivalue if header.comment.slen > 0: comment = _pj_str_to_str(header.comment) else: comment = None parameters = _pjsip_param_to_dict(&header.param) return RetryAfterHeader(seconds, comment, parameters) cdef FrozenRetryAfterHeader FrozenRetryAfterHeader_create(pjsip_retry_after_hdr *header): seconds = header.ivalue if header.comment.slen > 0: comment = _pj_str_to_str(header.comment) else: comment = None parameters = frozendict(_pjsip_param_to_dict(&header.param)) return FrozenRetryAfterHeader(seconds, comment, parameters) cdef ViaHeader ViaHeader_create(pjsip_via_hdr *header): transport = _pj_str_to_str(header.transport) host = _pj_str_to_str(header.sent_by.host) port = header.sent_by.port or 5060 parameters = _pjsip_param_to_dict(&header.other_param) if header.ttl_param != -1: parameters["ttl"] = header.ttl_param if header.rport_param != -1: parameters["rport"] = header.rport_param if header.maddr_param.slen > 0: parameters["maddr"] = _pj_str_to_str(header.maddr_param) if header.recvd_param.slen > 0: parameters["received"] = _pj_str_to_str(header.recvd_param) if header.branch_param.slen > 0: parameters["branch"] = _pj_str_to_str(header.branch_param) return ViaHeader(transport, host, port, parameters) cdef FrozenViaHeader FrozenViaHeader_create(pjsip_via_hdr *header): transport = _pj_str_to_str(header.transport) host = _pj_str_to_str(header.sent_by.host) port = header.sent_by.port or 5060 parameters = _pjsip_param_to_dict(&header.other_param) if header.ttl_param != -1: parameters["ttl"] = header.ttl_param if header.rport_param != -1: parameters["rport"] = header.rport_param if header.maddr_param.slen > 0: parameters["maddr"] = _pj_str_to_str(header.maddr_param) if header.recvd_param.slen > 0: parameters["received"] = _pj_str_to_str(header.recvd_param) if header.branch_param.slen > 0: parameters["branch"] = _pj_str_to_str(header.branch_param) return FrozenViaHeader(transport, host, port, frozendict(parameters)) cdef EventHeader EventHeader_create(pjsip_event_hdr *header): cdef dict parameters parameters = _pjsip_param_to_dict(&header.other_param) if header.id_param.slen != 0: parameters["id"] = _pj_str_to_str(header.id_param) return EventHeader(_pj_str_to_str(header.event_type), parameters) cdef FrozenEventHeader FrozenEventHeader_create(pjsip_event_hdr *header): cdef dict parameters parameters = _pjsip_param_to_dict(&header.other_param) if header.id_param.slen != 0: parameters["id"] = _pj_str_to_str(header.id_param) return FrozenEventHeader(_pj_str_to_str(header.event_type), frozendict(parameters)) cdef SubscriptionStateHeader SubscriptionStateHeader_create(pjsip_sub_state_hdr *header): cdef dict parameters parameters = _pjsip_param_to_dict(&header.other_param) if header.reason_param.slen != 0: parameters["reason"] = _pj_str_to_str(header.reason_param) if header.expires_param != -1: parameters["expires"] = str(header.expires_param) if header.retry_after != -1: parameters["retry-after"] = str(header.retry_after) return SubscriptionStateHeader(_pj_str_to_str(header.sub_state), parameters) cdef FrozenSubscriptionStateHeader FrozenSubscriptionStateHeader_create(pjsip_sub_state_hdr *header): cdef dict parameters parameters = _pjsip_param_to_dict(&header.other_param) if header.reason_param.slen != 0: parameters["reason"] = _pj_str_to_str(header.reason_param) if header.expires_param != -1: parameters["expires"] = str(header.expires_param) if header.retry_after != -1: parameters["retry-after"] = str(header.retry_after) return FrozenSubscriptionStateHeader(_pj_str_to_str(header.sub_state), frozendict(parameters)) cdef ReferToHeader ReferToHeader_create(pjsip_generic_string_hdr *header): cdef dict parameters cdef str value value = _pj_str_to_str((header).hvalue) uri, sep, params_str = value.partition('>') if sep: uri += '>' parameters = dict([(name, value or None) for name, sep, value in [param.partition('=') for param in params_str.split(';') if param]]) return ReferToHeader(uri, parameters) cdef FrozenReferToHeader FrozenReferToHeader_create(pjsip_generic_string_hdr *header): cdef dict parameters cdef str value value = _pj_str_to_str((header).hvalue) uri, sep, params_str = value.partition('>') if sep: uri += '>' parameters = dict([(name, value or None) for name, sep, value in [param.partition('=') for param in params_str.split(';') if param]]) return FrozenReferToHeader(uri, frozendict(parameters)) cdef SubjectHeader SubjectHeader_create(pjsip_generic_string_hdr *header): subject = unicode(_pj_str_to_str((header).hvalue), encoding='utf-8') return SubjectHeader(subject) cdef FrozenSubjectHeader FrozenSubjectHeader_create(pjsip_generic_string_hdr *header): subject = unicode(_pj_str_to_str((header).hvalue), encoding='utf-8') return FrozenSubjectHeader(subject) cdef ReplacesHeader ReplacesHeader_create(pjsip_replaces_hdr *header): cdef dict parameters = _pjsip_param_to_dict(&header.other_param) call_id = _pj_str_to_str(header.call_id) from_tag = _pj_str_to_str(header.from_tag) to_tag = _pj_str_to_str(header.to_tag) early_only = int(header.early_only) return ReplacesHeader(call_id, from_tag, to_tag, early_only, parameters) cdef FrozenReplacesHeader FrozenReplacesHeader_create(pjsip_replaces_hdr *header): cdef dict parameters = _pjsip_param_to_dict(&header.other_param) call_id = _pj_str_to_str(header.call_id) from_tag = _pj_str_to_str(header.from_tag) to_tag = _pj_str_to_str(header.to_tag) early_only = int(header.early_only) return FrozenReplacesHeader(call_id, from_tag, to_tag, early_only, frozendict(parameters)) diff --git a/sipsimple/core/_core.helper.pxi b/sipsimple/core/_core.helper.pxi index e8dbdb2a..88186900 100644 --- a/sipsimple/core/_core.helper.pxi +++ b/sipsimple/core/_core.helper.pxi @@ -1,347 +1,347 @@ import re import urllib # Classes # cdef class BaseCredentials: def __cinit__(self, *args, **kwargs): global _Credentials_scheme_digest, _Credentials_realm_wildcard self._credentials.scheme = _Credentials_scheme_digest.pj_str def __init__(self, str username not None, str password not None, str realm='*', bint digest=False): if self.__class__ is BaseCredentials: raise TypeError("BaseCredentials cannot be instantiated directly") self.username = username self.realm = realm self.password = password self.digest = digest def __repr__(self): return "%s(username=%r, password=%r, realm=%r, digest=%r)" % (self.__class__.__name__, self.username, self.password, self.realm, self.digest) def __str__(self): return '<%s for "%s@%s">' % (self.__class__.__name__, self.username, self.realm) cdef pjsip_cred_info* get_cred_info(self): return &self._credentials cdef class Credentials(BaseCredentials): property username: def __get__(self): return self._username def __set__(self, str username not None): _str_to_pj_str(username, &self._credentials.username) self._username = username property realm: def __get__(self): return self._realm def __set__(self, str realm not None): _str_to_pj_str(realm, &self._credentials.realm) self._realm = realm property password: def __get__(self): return self._password def __set__(self, str password not None): _str_to_pj_str(password, &self._credentials.data) self._password = password property digest: def __get__(self): return self._digest def __set__(self, bint digest): self._credentials.data_type = PJSIP_CRED_DATA_DIGEST if digest else PJSIP_CRED_DATA_PLAIN_PASSWD self._digest = digest @classmethod def new(cls, BaseCredentials credentials): return cls(credentials.username, credentials.password, credentials.realm, credentials.digest) cdef class FrozenCredentials(BaseCredentials): def __init__(self, str username not None, str password not None, str realm='*', bint digest=False): if not self.initialized: self.username = username self.realm = realm self.password = password self.digest = digest _str_to_pj_str(self.username, &self._credentials.username) _str_to_pj_str(self.realm, &self._credentials.realm) _str_to_pj_str(self.password, &self._credentials.data) self._credentials.data_type = PJSIP_CRED_DATA_DIGEST if digest else PJSIP_CRED_DATA_PLAIN_PASSWD self.initialized = 1 else: raise TypeError("{0.__class__.__name__} is read-only".format(self)) def __hash__(self): return hash((self.username, self.realm, self.password, self.digest)) @classmethod def new(cls, BaseCredentials credentials): if isinstance(credentials, FrozenCredentials): return credentials return cls(credentials.username, credentials.password, credentials.realm, credentials.digest) cdef class BaseSIPURI: def __init__(self, object host not None, object user=None, object password=None, object port=None, bint secure=False, dict parameters=None, dict headers=None): if self.__class__ is BaseSIPURI: raise TypeError("BaseSIPURI cannot be instantiated directly") self.host = host self.user = user self.password = password self.port = port self.secure = secure self.parameters = parameters if parameters is not None else {} self.headers = headers if headers is not None else {} property transport: def __get__(self): return self.parameters.get('transport', 'udp') def __set__(self, str transport not None): if transport.lower() == 'udp': self.parameters.pop('transport', None) else: self.parameters['transport'] = transport def __reduce__(self): return self.__class__, (self.host, self.user, self.password, self.port, self.secure, self.parameters, self.headers), None def __repr__(self): return "%s(%r, %r, %r, %r, %r, %r, %r)" % (self.__class__.__name__, self.host, self.user, self.password, self.port, self.secure, self.parameters, self.headers) def __str__(self): cdef object string = self.host if self.port: string = "%s:%d" % (string, self.port) if self.user is not None: if self.password is not None: string = "%s:%s@%s" % (self.user, self.password, string) else: string = "%s@%s" % (self.user, string) if self.parameters: string += ";" + ";".join(["%s%s" % (name, ("" if val is None else "="+urllib.parse.quote(val, safe="()[]-_.!~*'/:&+$"))) - for name, val in self.parameters.iteritems()]) + for name, val in list(self.parameters.items())]) if self.headers: string += "?" + "&".join(["%s%s" % (name, ("" if val is None else "="+urllib.parse.quote(val, safe="()[]-_.!~*'/:?+$"))) - for name, val in self.headers.iteritems()]) + for name, val in self.headers.items()]) if self.secure: string = "sips:" + string else: string = "sip:" + string return string def __richcmp__(self, other, op): if not isinstance(other, BaseSIPURI): return NotImplemented if op == 2: # 2 is == return all(getattr(self, name) == getattr(other, name) for name in ("user", "password", "host", "port", "secure", "parameters", "headers")) elif op == 3: # 3 is != return any(getattr(self, name) != getattr(other, name) for name in ("user", "password", "host", "port", "secure", "parameters", "headers")) else: operator_map = {0: '<', 1: '<=', 2: '==', 3: '!=', 4: '>', 5: '>='} raise TypeError("unorderable types: {0.__class__.__name__}() {2} {1.__class__.__name__}()".format(self, other, operator_map[op])) def matches(self, address): match = re.match(r'^((?Psip|sips):)?(?P.+?)(@(?P.+?)(:(?P\d+?))?)?(;(?P.+?))?(\?(?P.+?))?$', address) if match is None: return False components = match.groupdict() if components['scheme'] is not None: expected_scheme = 'sips' if self.secure else 'sip' if components['scheme'] != expected_scheme: return False if components['username'] != self.user: return False if components['domain'] is not None and components['domain'] != self.host: return False if components['port'] is not None and int(components['port']) != self.port: return False if components['parameters']: parameters = dict([(name, value) for name, sep, value in [param.partition('=') for param in components['parameters'].split(';')]]) - expected_parameters = dict([(name, str(value) if value is not None else None) for name, value in self.parameters.iteritems() if name in parameters]) + expected_parameters = dict([(name, str(value) if value is not None else None) for name, value in list(self.parameters.items()) if name in parameters]) if parameters != expected_parameters: return False if components['headers']: headers = dict([(name, value) for name, sep, value in [header.partition('=') for header in components['headers'].split('&')]]) - expected_headers = dict([(name, str(value) if value is not None else None) for name, value in self.headers.iteritems() if name in headers]) + expected_headers = dict([(name, str(value) if value is not None else None) for name, value in self.headers.items() if name in headers]) if headers != expected_headers: return False return True cdef class SIPURI(BaseSIPURI): property port: def __get__(self): return self._port def __set__(self, object port): if port is not None: port = int(port) if not (0 < port <= 65535): raise ValueError("Invalid port: %d" % port) self._port = port @classmethod def new(cls, BaseSIPURI sipuri): return cls(user=sipuri.user, password=sipuri.password, host=sipuri.host, port=sipuri.port, secure=sipuri.secure, parameters=dict(sipuri.parameters), headers=dict(sipuri.headers)) @classmethod def parse(cls, object uri_str): if not isinstance(uri_str, basestring): raise TypeError('a string or unicode is required') cdef bytes uri_bytes = uri_str.encode() cdef pjsip_uri *uri = NULL cdef pj_pool_t *pool = NULL cdef pj_str_t tmp cdef char buffer[4096] pool = pj_pool_create_on_buf("SIPURI_parse", buffer, sizeof(buffer)) if pool == NULL: raise SIPCoreError("Could not allocate memory pool") pj_strdup2_with_null(pool, &tmp, uri_bytes) uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0) if uri == NULL: raise SIPCoreError("Not a valid SIP URI: %s" % uri_str) return SIPURI_create(pjsip_uri_get_uri(uri)) cdef class FrozenSIPURI(BaseSIPURI): def __init__(self, object host not None, object user=None, object password=None, object port=None, bint secure=False, frozendict parameters not None=frozendict(), frozendict headers not None=frozendict()): if not self.initialized: if port is not None: port = int(port) if not (0 < port <= 65535): raise ValueError("Invalid port: %d" % port) self.host = host self.user = user self.password = password self.port = port self.secure = secure self.parameters = parameters self.headers = headers self.initialized = 1 else: raise TypeError("{0.__class__.__name__} is read-only".format(self)) property transport: def __get__(self): return self.parameters.get('transport', 'udp') def __hash__(self): return hash((self.user, self.password, self.host, self.port, self.secure, self.parameters, self.headers)) def __richcmp__(self, other, op): # since we define __hash__, __richcmp__ is not inherited (see https://docs.python.org/2/c-api/typeobj.html#c.PyTypeObject.tp_richcompare) if not isinstance(other, BaseSIPURI): return NotImplemented if op == 2: # 2 is == return all(getattr(self, name) == getattr(other, name) for name in ("user", "password", "host", "port", "secure", "parameters", "headers")) elif op == 3: # 3 is != return any(getattr(self, name) != getattr(other, name) for name in ("user", "password", "host", "port", "secure", "parameters", "headers")) else: operator_map = {0: '<', 1: '<=', 2: '==', 3: '!=', 4: '>', 5: '>='} raise TypeError("unorderable types: {0.__class__.__name__}() {2} {1.__class__.__name__}()".format(self, other, operator_map[op])) @classmethod def new(cls, BaseSIPURI sipuri): if isinstance(sipuri, FrozenSIPURI): return sipuri return cls(user=sipuri.user, password=sipuri.password, host=sipuri.host, port=sipuri.port, secure=sipuri.secure, parameters=frozendict(sipuri.parameters), headers=frozendict(sipuri.headers)) @classmethod def parse(cls, object uri_str): if not isinstance(uri_str, basestring): raise TypeError('a string or unicode is required') cdef bytes uri_bytes = str(uri_str) cdef pjsip_uri *uri = NULL cdef pj_pool_t *pool = NULL cdef pj_str_t tmp cdef char buffer[4096] pool = pj_pool_create_on_buf("FrozenSIPURI_parse", buffer, sizeof(buffer)) if pool == NULL: raise SIPCoreError("Could not allocate memory pool") pj_strdup2_with_null(pool, &tmp, uri_bytes) uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0) if uri == NULL: raise SIPCoreError("Not a valid SIP URI: %s" % uri_str) return FrozenSIPURI_create(pjsip_uri_get_uri(uri)) # Factory functions # cdef dict _pj_sipuri_to_dict(pjsip_sip_uri *uri): cdef object scheme cdef pj_str_t *scheme_str cdef pjsip_param *param cdef object parameters = {} cdef object headers = {} cdef object kwargs = dict(parameters=parameters, headers=headers) kwargs["host"] = _pj_str_to_str(uri.host) scheme = _pj_str_to_str(pjsip_uri_get_scheme(uri)[0]) if scheme == "sip": kwargs["secure"] = False elif scheme == "sips": kwargs["secure"] = True else: raise SIPCoreError("Not a sip(s) URI") if uri.user.slen > 0: kwargs["user"] = _pj_str_to_str(uri.user) if uri.passwd.slen > 0: kwargs["password"] = _pj_str_to_str(uri.passwd) if uri.port > 0: kwargs["port"] = uri.port if uri.user_param.slen > 0: parameters["user"] = _pj_str_to_str(uri.user_param) if uri.method_param.slen > 0: parameters["method"] = _pj_str_to_str(uri.method_param) if uri.transport_param.slen > 0: parameters["transport"] = _pj_str_to_str(uri.transport_param) if uri.ttl_param != -1: parameters["ttl"] = uri.ttl_param if uri.lr_param != 0: parameters["lr"] = None if uri.maddr_param.slen > 0: parameters["maddr"] = _pj_str_to_str(uri.maddr_param) param = ( &uri.other_param).next while param != &uri.other_param: if param.value.slen == 0: parameters[_pj_str_to_str(param.name)] = None else: parameters[_pj_str_to_str(param.name)] = _pj_str_to_str(param.value) param = ( param).next param = ( &uri.header_param).next while param != &uri.header_param: if param.value.slen == 0: headers[_pj_str_to_str(param.name)] = None else: headers[_pj_str_to_str(param.name)] = _pj_str_to_str(param.value) param = ( param).next return kwargs cdef SIPURI SIPURI_create(pjsip_sip_uri *uri): cdef dict kwargs = _pj_sipuri_to_dict(uri) return SIPURI(**kwargs) cdef FrozenSIPURI FrozenSIPURI_create(pjsip_sip_uri *uri): cdef dict kwargs = _pj_sipuri_to_dict(uri) kwargs["parameters"] = frozendict(kwargs["parameters"]) kwargs["headers"] = frozendict(kwargs["headers"]) return FrozenSIPURI(**kwargs) # Globals # cdef PJSTR _Credentials_scheme_digest = PJSTR("digest") diff --git a/sipsimple/core/_core.subscription.pxi b/sipsimple/core/_core.subscription.pxi index 34f29945..040c72d8 100644 --- a/sipsimple/core/_core.subscription.pxi +++ b/sipsimple/core/_core.subscription.pxi @@ -1,959 +1,959 @@ import re cdef class Subscription: expire_warning_time = 30 #public methods def __cinit__(self, *args, **kwargs): self.state = "NULL" pj_timer_entry_init(&self._timeout_timer, 0, self, _Subscription_cb_timer) self._timeout_timer_active = 0 pj_timer_entry_init(&self._refresh_timer, 1, self, _Subscription_cb_timer) self._refresh_timer_active = 0 self.extra_headers = frozenlist() self.peer_address = None self.call_id = None def __init__(self, SIPURI request_uri not None, FromHeader from_header not None, ToHeader to_header not None, ContactHeader contact_header not None, object event, RouteHeader route_header not None, Credentials credentials=None, int refresh=300): global _subs_cb cdef PJSTR from_header_str cdef PJSTR to_header_str cdef PJSTR contact_str cdef PJSTR request_uri_str cdef pj_str_t event_pj cdef pjsip_cred_info *cred_info cdef PJSIPUA ua = _get_ua() cdef int status if self._obj != NULL or self.state != "NULL": raise SIPCoreError("Subscription.__init__() was already called") if refresh <= 0: raise ValueError("refresh argument needs to be a non-negative integer") - if event not in ua._events.iterkeys(): + if event not in ua._events.keys(): raise ValueError('Unknown event "%s"' % event) self.contact_header = FrozenContactHeader.new(contact_header) self.event = event self.route_header = FrozenRouteHeader.new(route_header) self.route_header.uri.parameters.dict["lr"] = None # always send lr parameter in Route header self.route_header.uri.parameters.dict["hide"] = None # always hide Route header if credentials is not None: self.credentials = FrozenCredentials.new(credentials) self.refresh = refresh from_header_parameters = from_header.parameters.copy() from_header_parameters.pop("tag", None) from_header.parameters = {} from_header_str = PJSTR(from_header.body) to_header_parameters = to_header.parameters.copy() to_header_parameters.pop("tag", None) to_header.parameters = {} to_header_str = PJSTR(to_header.body) contact_str = PJSTR(str(contact_header.body)) request_uri_str = PJSTR(str(request_uri)) _str_to_pj_str(self.event, &event_pj) with nogil: status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from_header_str.pj_str, &contact_str.pj_str, &to_header_str.pj_str, &request_uri_str.pj_str, &self._dlg) if status != 0: raise PJSIPError("Could not create dialog for SUBSCRIBE", status) # Increment dialog session count so that it's never destroyed by PJSIP with nogil: status = pjsip_dlg_inc_session(self._dlg, &ua._module) if status != 0: raise PJSIPError("Could not increment dialog session count", status) self.call_id = _pj_str_to_str(self._dlg.call_id.id) if contact_header.expires is not None: self._dlg.local.contact.expires = contact_header.expires if contact_header.q is not None: self._dlg.local.contact.q1000 = int(contact_header.q*1000) contact_parameters = contact_header.parameters.copy() contact_parameters.pop("q", None) contact_parameters.pop("expires", None) _dict_to_pjsip_param(contact_parameters, &self._dlg.local.contact.other_param, self._dlg.pool) _dict_to_pjsip_param(from_header_parameters, &self._dlg.local.info.other_param, self._dlg.pool) _dict_to_pjsip_param(to_header_parameters, &self._dlg.remote.info.other_param, self._dlg.pool) self.from_header = FrozenFromHeader_create(self._dlg.local.info) self.to_header = FrozenToHeader.new(to_header) with nogil: status = pjsip_evsub_create_uac(self._dlg, &_subs_cb, &event_pj, PJSIP_EVSUB_NO_EVENT_ID, &self._obj) if status != 0: raise PJSIPError("Could not create SUBSCRIBE", status) pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, self) _BaseRouteHeader_to_pjsip_route_hdr(self.route_header, &self._route_header, self._dlg.pool) pj_list_init( &self._route_set) pj_list_insert_after( &self._route_set, &self._route_header) with nogil: status = pjsip_dlg_set_route_set(self._dlg, &self._route_set) if status != 0: raise PJSIPError("Could not set route on SUBSCRIBE", status) if self.credentials is not None: cred_info = self.credentials.get_cred_info() with nogil: status = pjsip_auth_clt_set_credentials(&self._dlg.auth_sess, 1, cred_info) if status != 0: raise PJSIPError("Could not set credentials for SUBSCRIBE", status) def __dealloc__(self): cdef PJSIPUA ua = self._get_ua() if ua is not None: self._cancel_timers(ua, 1, 1) if self._obj != NULL: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) with nogil: pjsip_evsub_terminate(self._obj, 0) self._obj = NULL if self._dlg != NULL and ua is not None: with nogil: pjsip_dlg_dec_session(self._dlg, &ua._module) self._dlg = NULL def subscribe(self, list extra_headers not None=list(), object content_type=None, object body=None, object timeout=None): cdef object prev_state = self.state cdef PJSIPUA ua = self._get_ua() with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state == "TERMINATED": raise SIPCoreError('This method may not be called in the "TERMINATED" state') if (content_type is not None and body is None) or (content_type is None and body is not None): raise ValueError("Both or none of content_type and body arguments need to be specified") if timeout is not None: if timeout <= 0: raise ValueError("Timeout value cannot be negative") self._subscribe_timeout.sec = int(timeout) self._subscribe_timeout.msec = (timeout * 1000) % 1000 else: self._subscribe_timeout.sec = 0 self._subscribe_timeout.msec = 0 if extra_headers is not None: self.extra_headers = frozenlist([header.frozen_type.new(header) for header in extra_headers]) self.content_type = content_type self.body = body self._send_subscribe(ua, self.refresh, &self._subscribe_timeout, self.extra_headers, content_type, body) self._cancel_timers(ua, 0, 1) if prev_state == "NULL": _add_event("SIPSubscriptionWillStart", dict(obj=self)) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def end(self, object timeout=None): cdef pj_time_val end_timeout cdef PJSIPUA ua = self._get_ua() with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state == "TERMINATED": return if self.state == "NULL": raise SIPCoreError('This method may not be called in the "NULL" state') if timeout is not None: if timeout <= 0: raise ValueError("Timeout value cannot be negative") end_timeout.sec = int(timeout) end_timeout.msec = (timeout * 1000) % 1000 else: end_timeout.sec = 0 end_timeout.msec = 0 self._want_end = 1 self._cancel_timers(ua, 1, 1) _add_event("SIPSubscriptionWillEnd", dict(obj=self)) try: self._send_subscribe(ua, 0, &end_timeout, self.extra_headers, None, None) except PJSIPError, e: self._term_reason = e.args[0] if self._obj != NULL: with nogil: pjsip_evsub_terminate(self._obj, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) # private methods cdef PJSIPUA _get_ua(self): cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: self._obj = NULL self._timeout_timer_active = 0 self._refresh_timer_active = 0 self.state = "TERMINATED" return None else: return ua cdef int _cancel_timers(self, PJSIPUA ua, int cancel_timeout, int cancel_refresh) except -1: if cancel_timeout and self._timeout_timer_active: pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timeout_timer) self._timeout_timer_active = 0 if cancel_refresh and self._refresh_timer_active: pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._refresh_timer) self._refresh_timer_active = 0 cdef int _send_subscribe(self, PJSIPUA ua, int expires, pj_time_val *timeout, object extra_headers, object content_type, object body) except -1: cdef pjsip_tx_data *tdata cdef pj_str_t body_pj cdef object content_type_spl cdef PJSTR content_type_str cdef PJSTR content_subtype_str cdef int status if body is not None: content_type_spl = content_type.split("/") if len(content_type_spl) != 2: raise ValueError('Supplied content_type argument does not contain a "/" character') content_type_str = PJSTR(content_type_spl[0]) content_subtype_str = PJSTR(content_type_spl[1]) _str_to_pj_str(body, &body_pj) with nogil: status = pjsip_evsub_initiate(self._obj, NULL, expires, &tdata) if status != 0: raise PJSIPError("Could not create SUBSCRIBE message", status) _add_headers_to_tdata(tdata, extra_headers) if body is not None: tdata.msg.body = pjsip_msg_body_create(tdata.pool, &content_type_str.pj_str, &content_subtype_str.pj_str, &body_pj) with nogil: status = pjsip_evsub_send_request(self._obj, tdata) if status != 0: raise PJSIPError("Could not send SUBSCRIBE message", status) self._cancel_timers(ua, 1, 0) if timeout.sec or timeout.msec: status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timeout_timer, timeout) if status == 0: self._timeout_timer_active = 1 self._expires = self.refresh # callback methods cdef int _cb_state(self, PJSIPUA ua, object state, int code, object reason, dict headers) except -1: # PJSIP holds the dialog lock when this callback is entered cdef object prev_state = self.state cdef int expires cdef int status cdef pj_time_val end_timeout self.state = state if state == "ACCEPTED" and prev_state == "SENT": try: contact_header = headers['Contact'][0] except LookupError: self._term_code = 1400 self._term_reason = "Contact header missing" with nogil: pjsip_evsub_terminate(self._obj, 1) return 0 _add_event("SIPSubscriptionDidStart", dict(obj=self)) try: expires = int(headers["Expires"]) except (KeyError, ValueError): return 0 if expires == 0: self._want_end = 1 self._cancel_timers(ua, 1, 1) end_timeout.sec = 1 end_timeout.msec = 0 _add_event("SIPSubscriptionWillEnd", dict(obj=self)) try: self._send_subscribe(ua, 0, &end_timeout, self.extra_headers, None, None) except PJSIPError, e: self._term_reason = e.args[0] if self._obj != NULL: with nogil: pjsip_evsub_terminate(self._obj, 1) return 0 elif state == "TERMINATED": pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) self._cancel_timers(ua, 1, 1) self._obj = NULL if self._want_end: _add_event("SIPSubscriptionDidEnd", dict(obj=self)) else: min_expires = headers.get('Min-Expires') if self._term_reason is not None: _add_event("SIPSubscriptionDidFail", dict(obj=self, code=self._term_code, reason=self._term_reason, min_expires=min_expires)) else: subscription_state = headers.get('Subscription-State') if subscription_state is not None and subscription_state.state == 'terminated': reason = subscription_state.reason _add_event("SIPSubscriptionDidFail", dict(obj=self, code=code, reason=reason, min_expires=min_expires)) if prev_state != state: _add_event("SIPSubscriptionChangedState", dict(obj=self, prev_state=prev_state, state=state)) cdef int _cb_got_response(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: # PJSIP holds the dialog lock when this callback is entered cdef dict event_dict = dict() cdef int expires = self._expires cdef int status cdef pj_time_val refresh _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) self.to_header = FrozenToHeader_create(rdata.msg_info.to_hdr) if self.state != "TERMINATED": try: contact_header = event_dict["headers"]["Contact"][0] except LookupError: return 0 try: expires = int(event_dict["headers"]["Expires"]) except (KeyError, ValueError): expires = self._expires if expires == 0: return 0 if self.state != "TERMINATED" and not self._want_end: self._cancel_timers(ua, 1, 0) refresh.sec = max(1, expires - self.expire_warning_time, expires/2) refresh.msec = 0 status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._refresh_timer, &refresh) if status == 0: self._refresh_timer_active = 1 cdef int _cb_notify(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: # PJSIP holds the dialog lock when this callback is entered cdef dict event_dict = dict() cdef dict notify_dict = dict(obj=self) _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) body = event_dict["body"] content_type = event_dict["headers"].get("Content-Type", None) event = event_dict["headers"].get("Event", None) if event is None or event.event != self.event or (body is not None and content_type is not None and content_type.content_type not in ua.events[event.event]): return 0 notify_dict["request_uri"] = event_dict["request_uri"] notify_dict["from_header"] = event_dict["headers"].get("From", None) notify_dict["to_header"] = event_dict["headers"].get("To", None) notify_dict["headers"] = event_dict["headers"] notify_dict["body"] = body notify_dict["content_type"] = content_type.content_type if content_type and body else None notify_dict["event"] = event.event _add_event("SIPSubscriptionGotNotify", notify_dict) cdef int _cb_timeout_timer(self, PJSIPUA ua): # Timer callback, dialog lock is not held by PJSIP global sip_status_messages with nogil: pjsip_dlg_inc_lock(self._dlg) try: self._term_code = PJSIP_SC_TSX_TIMEOUT self._term_reason = sip_status_messages[PJSIP_SC_TSX_TIMEOUT] if self._obj != NULL: with nogil: pjsip_evsub_terminate(self._obj, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef int _cb_refresh_timer(self, PJSIPUA ua): # Timer callback, dialog lock is not held by PJSIP with nogil: pjsip_dlg_inc_lock(self._dlg) try: self._send_subscribe(ua, self.refresh, &self._subscribe_timeout, self.extra_headers, self.content_type, self.body) except PJSIPError, e: self._term_reason = e.args[0] if self._obj != NULL: with nogil: pjsip_evsub_terminate(self._obj, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef class IncomingSubscription: # properties property content_type: def __get__(self): if self._content_type is None: return None return "%s/%s" % (self._content_type.str, self._content_subtype.str) property content: def __get__(self): if self._content is None: return None return self._content.str def __cinit__(self): self.state = None self.peer_address = None self.call_id = None def __dealloc__(self): cdef PJSIPUA ua = self._get_ua(0) self._initial_response = NULL self._initial_tsx = NULL if self._obj != NULL: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) with nogil: pjsip_evsub_terminate(self._obj, 0) self._obj = NULL if self._dlg != NULL and ua is not None: with nogil: pjsip_dlg_dec_session(self._dlg, &ua._module) self._dlg = NULL cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata, str event) except -1: global _incoming_subs_cb cdef int status cdef str transport cdef FrozenSIPURI request_uri cdef FrozenContactHeader contact_header cdef PJSTR contact_str cdef dict event_dict cdef pjsip_expires_hdr *expires_header cdef char *error_message expires_header = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_EXPIRES, NULL) if expires_header == NULL: self._expires = 3600 else: self._expires = min(expires_header.ivalue, 3600) self._set_state("incoming") self.event = event self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) event_dict = dict(obj=self) _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) transport = rdata.tp_info.transport.type_name.lower() request_uri = event_dict["request_uri"] if _is_valid_ip(pj_AF_INET(), request_uri.host): contact_header = FrozenContactHeader(request_uri) else: contact_header = FrozenContactHeader(FrozenSIPURI(host=_pj_str_to_str(rdata.tp_info.transport.local_name.host), user=request_uri.user, port=rdata.tp_info.transport.local_name.port, parameters=(frozendict(transport=transport) if transport != "udp" else frozendict()))) contact_str = PJSTR(str(contact_header.body)) with nogil: status = pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(), rdata, &contact_str.pj_str, &self._dlg) if status != 0: error_message = "Could not create dialog for incoming SUBSCRIBE" else: pjsip_dlg_inc_session(self._dlg, &ua._module) # Increment dialog session count so it's never destroyed by PJSIP # setting the transport to rdata.tp_info.transport doesn't work as the NOTIFY has to be sent to the Contact URI and the transports can conflict if status != 0: raise PJSIPError(error_message, status) self._initial_tsx = pjsip_rdata_get_tsx(rdata) self.call_id = _pj_str_to_str(self._dlg.call_id.id) with nogil: status = pjsip_evsub_create_uas(self._dlg, &_incoming_subs_cb, rdata, 0, &self._obj) pjsip_dlg_dec_lock(self._dlg) if status != 0: pjsip_tsx_terminate(self._initial_tsx, 500) self._initial_tsx = NULL self._dlg = NULL error_message = "Could not create incoming SUBSCRIBE session" else: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, self) status = pjsip_dlg_create_response(self._dlg, rdata, 500, NULL, &self._initial_response) if status != 0: pjsip_tsx_terminate(self._initial_tsx, 500) self._initial_tsx = NULL error_message = "Could not create response for incoming SUBSCRIBE" if status != 0: raise PJSIPError(error_message, status) _add_event("SIPIncomingSubscriptionGotSubscribe", event_dict) return 0 def reject(self, int code): cdef PJSIPUA ua = self._get_ua(1) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "incoming": raise SIPCoreInvalidStateError('Can only reject an incoming SUBSCRIBE in the "incoming" state, '+ 'object is currently in the "%s" state' % self.state) if not (300 <= code < 700): raise ValueError("Invalid negative SIP response code: %d" % code) self._send_initial_response(code) pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) with nogil: pjsip_evsub_terminate(self._obj, 0) self._obj = NULL self._set_state("terminated") _add_event("SIPIncomingSubscriptionDidEnd", dict(obj=self)) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def accept_pending(self): cdef PJSIPUA ua = self._get_ua(1) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "incoming": raise SIPCoreInvalidStateError('Can only accept an incoming SUBSCRIBE as pending in the "incoming" state, '+ 'object is currently in the "%s" state' % self.state) self._send_initial_response(202) self._set_state("pending") if self._expires > 0: self._send_notify() else: # cleanup will be done by _cb_tsx self._terminate(ua, "timeout", 0) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def accept(self, str content_type=None, str content=None): global _re_content_type cdef object content_type_match cdef PJSIPUA ua = self._get_ua(1) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state not in ("incoming", "pending"): raise SIPCoreInvalidStateError('Can only accept an incoming SUBSCRIBE in the "incoming" or "pending" state, object is currently in the "%s" state' % self.state) if (content_type is None and content is not None) or (content_type is not None and content is None): raise ValueError('Either both or neither of the "content_type" and "content" arguments should be specified') if content_type is not None: content_type_match = _re_content_type.match(content_type) if content_type_match is None: raise ValueError("content_type parameter is not properly formatted") self._content_type = PJSTR(content_type_match.group(1)) self._content_subtype = PJSTR(content_type_match.group(2)) self._content = PJSTR(content) if self.state == "incoming": self._send_initial_response(200) self._set_state("active") if self._expires > 0: self._send_notify() else: # cleanup will be done by _cb_tsx self._terminate(ua, "timeout", 0) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def push_content(self, str content_type not None, str content not None): global _re_content_type cdef object content_type_match cdef PJSIPUA ua = self._get_ua(1) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "active": raise SIPCoreInvalidStateError('Can only push the content for a SUBSCRIBE session in the "active" state, ' 'object is currently in the "%s" state' % self.state) content_type_match = _re_content_type.match(content_type) if content_type_match is None: raise ValueError("content_type parameter is not properly formatted") self._content_type = PJSTR(content_type_match.group(1)) self._content_subtype = PJSTR(content_type_match.group(2)) self._content = PJSTR(content) self._send_notify() finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def end(self, reason=None): cdef PJSIPUA ua = self._get_ua(0) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state == "terminated": return if self.state not in ("pending", "active"): raise SIPCoreInvalidStateError('Can only end an incoming SUBSCRIBE session in the "pending" or '+ '"active" state, object is currently in the "%s" state' % self.state) self._terminate(ua, reason, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef int _set_state(self, str state) except -1: cdef str prev_state prev_state = self.state self.state = state if prev_state != state and prev_state is not None: _add_event("SIPIncomingSubscriptionChangedState", dict(obj=self, prev_state=prev_state, state=state)) cdef PJSIPUA _get_ua(self, int raise_exception): cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: self._obj = NULL self._initial_response = NULL self._initial_tsx = NULL self._set_state("terminated") if raise_exception: raise else: return None else: return ua cdef int _send_initial_response(self, int code) except -1: cdef PJSIPUA ua = self._get_ua(1) cdef int status with nogil: status = pjsip_dlg_modify_response(self._dlg, self._initial_response, code, NULL) if status != 0: raise PJSIPError("Could not modify response", status) # pjsip_dlg_modify_response() increases ref count unncessarily with nogil: pjsip_tx_data_dec_ref(self._initial_response) if code / 100 == 2: pjsip_msg_add_hdr(self._initial_response.msg, pjsip_expires_hdr_create(self._initial_response.pool, self._expires)) with nogil: status = pjsip_dlg_send_response(self._dlg, self._initial_tsx, self._initial_response) if status != 0: raise PJSIPError("Could not send response", status) self._initial_response = NULL self._initial_tsx = NULL if self._expires > 0: with nogil: # Start TIMER_TYPE_UAS_TIMEOUT, which PJSIP doesn't do for the initial SUBSCRIBE pjsip_evsub_set_timer(self._obj, 2, self._expires) cdef int _send_notify(self, str reason=None) except -1: cdef pjsip_evsub_state state cdef pj_str_t reason_pj cdef pj_str_t *reason_p cdef pjsip_tx_data *tdata cdef int status reason_p = NULL if self.state == "pending": state = PJSIP_EVSUB_STATE_PENDING elif self.state == "active": state = PJSIP_EVSUB_STATE_ACTIVE else: state = PJSIP_EVSUB_STATE_TERMINATED if reason is not None: _str_to_pj_str(reason, &reason_pj) reason_p = &reason_pj with nogil: status = pjsip_evsub_notify(self._obj, state, NULL, reason_p, &tdata) if status != 0: raise PJSIPError("Could not create NOTIFY request", status) if self.state == "active" and None not in (self._content_type, self._content_subtype, self._content): tdata.msg.body = pjsip_msg_body_create(tdata.pool, &self._content_type.pj_str, &self._content_subtype.pj_str, &self._content.pj_str) with nogil: status = pjsip_evsub_send_request(self._obj, tdata) if status != 0: raise PJSIPError("Could not send NOTIFY request", status) event_dict = dict(obj=self) _pjsip_msg_to_dict(tdata.msg, event_dict) _add_event("SIPIncomingSubscriptionSentNotify", event_dict) return 0 cdef int _terminate(self, PJSIPUA ua, str reason, int do_cleanup) except -1: cdef int status self._set_state("terminated") try: self._send_notify(reason) except SIPCoreError: pass if do_cleanup: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) self._obj = NULL _add_event("SIPIncomingSubscriptionDidEnd", dict(obj=self)) # callback methods cdef int _cb_rx_refresh(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: # PJSIP holds the dialog lock when this callback is entered cdef int status cdef pjsip_expires_hdr *expires_header cdef int expires cdef dict event_dict event_dict = dict(obj=self) _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) expires_header = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_EXPIRES, NULL) if expires_header == NULL: self._expires = 3600 else: if expires_header.ivalue == 0: _add_event("SIPIncomingSubscriptionGotUnsubscribe", event_dict) # cleanup will be done by _cb_tsx self._terminate(ua, None, 0) return 200 else: self._expires = min(expires_header.ivalue, 3600) _add_event("SIPIncomingSubscriptionGotRefreshingSubscribe", event_dict) try: self._send_notify() except SIPCoreError, e: _add_event("SIPIncomingSubscriptionNotifyDidFail", dict(obj=self, code=0, reason=e.args[0])) if self.state == "active": return 200 else: return 202 cdef int _cb_server_timeout(self, PJSIPUA ua) except -1: # PJSIP holds the dialog lock when this callback is entered _add_event("SIPIncomingSubscriptionDidTimeout", dict(obj=self)) self._terminate(ua, "timeout", 1) cdef int _cb_tsx(self, PJSIPUA ua, pjsip_event *event) except -1: # PJSIP holds the dialog lock when this callback is entered cdef pjsip_rx_data *rdata cdef dict event_dict cdef int status_code if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY" and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED): event_dict = dict(obj=self) rdata = event.body.tsx_state.src.rdata if rdata != NULL: if self.peer_address is None: self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: self.peer_address.ip = rdata.pkt_info.src_name self.peer_address.port = rdata.pkt_info.src_port status_code = event.body.tsx_state.tsx.status_code if event.body.tsx_state.type==PJSIP_EVENT_RX_MSG and status_code/100==2: _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) _add_event("SIPIncomingSubscriptionNotifyDidSucceed", event_dict) else: if event.body.tsx_state.type == PJSIP_EVENT_RX_MSG: _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) else: event_dict["code"] = status_code event_dict["reason"] = _pj_str_to_str(event.body.tsx_state.tsx.status_text) _add_event("SIPIncomingSubscriptionNotifyDidFail", event_dict) if status_code in (408, 481) or status_code/100==7: # PJSIP will terminate the subscription and the dialog will be destroyed self._terminate(ua, None, 1) elif (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY" and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_TERMINATED): event_dict = dict(obj=self) status_code = event.body.tsx_state.tsx.status_code if status_code == 408: # Local timeout, PJSIP will terminate the subscription and the dialog will be destroyed event_dict["code"] = status_code event_dict["reason"] = _pj_str_to_str(event.body.tsx_state.tsx.status_text) _add_event("SIPIncomingSubscriptionNotifyDidFail", event_dict) self._terminate(ua, None, 1) elif (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAS and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "SUBSCRIBE" and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED and event.body.tsx_state.type == PJSIP_EVENT_TX_MSG): event_dict = dict(obj=self) _pjsip_msg_to_dict(event.body.tsx_state.src.tdata.msg, event_dict) _add_event("SIPIncomingSubscriptionAnsweredSubscribe", event_dict) if self.state == "terminated" and self._obj != NULL: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) self._obj = NULL # callback functions cdef void _Subscription_cb_state(pjsip_evsub *sub, pjsip_event *event) with gil: cdef void *subscription_void cdef Subscription subscription cdef object state cdef int code = 0 cdef object reason = None cdef pjsip_rx_data *rdata = NULL cdef PJSIPUA ua try: ua = _get_ua() except: return try: subscription_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if subscription_void == NULL: return subscription = subscription_void state = pjsip_evsub_get_state_name(sub) if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and (event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED or event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_TERMINATED)): if state == "TERMINATED": if event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC: code = event.body.tsx_state.tsx.status_code reason = _pj_str_to_str(event.body.tsx_state.tsx.status_text) else: code = 0 reason = None if event.body.tsx_state.type == PJSIP_EVENT_RX_MSG and _pj_str_to_str(event.body.tsx_state.tsx.method.name) in ("SUBSCRIBE", "NOTIFY"): rdata = event.body.tsx_state.src.rdata headers_dict = dict() if rdata != NULL: rdata_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) headers_dict = rdata_dict.get('headers', {}) subscription._cb_state(ua, state, code, reason, headers_dict) except: ua._handle_exception(1) cdef void _Subscription_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil: cdef void *subscription_void cdef Subscription subscription cdef pjsip_rx_data *rdata cdef PJSIPUA ua try: ua = _get_ua() except: return try: subscription_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if subscription_void == NULL: return subscription = subscription_void if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.type == PJSIP_EVENT_RX_MSG and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "SUBSCRIBE" and event.body.tsx_state.tsx.status_code / 100 == 2): rdata = event.body.tsx_state.src.rdata if rdata != NULL: if subscription.peer_address is None: subscription.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: subscription.peer_address.ip = rdata.pkt_info.src_name subscription.peer_address.port = rdata.pkt_info.src_port subscription._cb_got_response(ua, rdata) except: ua._handle_exception(1) cdef void _Subscription_cb_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil: cdef void *subscription_void cdef Subscription subscription cdef PJSIPUA ua try: ua = _get_ua() except: return try: subscription_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if subscription_void == NULL: return subscription = subscription_void if rdata != NULL: if subscription.peer_address is None: subscription.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: subscription.peer_address.ip = rdata.pkt_info.src_name subscription.peer_address.port = rdata.pkt_info.src_port subscription._cb_notify(ua, rdata) except: ua._handle_exception(1) cdef void _Subscription_cb_refresh(pjsip_evsub *sub) with gil: # We want to handle the refresh timer oursevles, ignore the PJSIP provided timer pass cdef void _Subscription_cb_timer(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) with gil: cdef Subscription subscription cdef PJSIPUA ua try: ua = _get_ua() except: return try: if entry.user_data != NULL: subscription = entry.user_data if subscription._dlg == NULL: return if entry.id == 1: subscription._refresh_timer_active = 0 subscription._cb_refresh_timer(ua) else: subscription._timeout_timer_active = 0 subscription._cb_timeout_timer(ua) except: ua._handle_exception(1) cdef void _IncomingSubscription_cb_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil: cdef void *subscription_void cdef IncomingSubscription subscription cdef PJSIPUA ua try: ua = _get_ua() except: return try: subscription_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if subscription_void == NULL: p_st_code[0] = 481 return subscription = subscription_void if rdata != NULL: if subscription.peer_address is None: subscription.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: subscription.peer_address.ip = rdata.pkt_info.src_name subscription.peer_address.port = rdata.pkt_info.src_port p_st_code[0] = subscription._cb_rx_refresh(ua, rdata) except: ua._handle_exception(1) cdef void _IncomingSubscription_cb_server_timeout(pjsip_evsub *sub) with gil: cdef void *subscription_void cdef IncomingSubscription subscription cdef PJSIPUA ua try: ua = _get_ua() except: return try: subscription_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if subscription_void == NULL: return subscription = subscription_void subscription._cb_server_timeout(ua) except: ua._handle_exception(1) cdef void _IncomingSubscription_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil: cdef void *subscription_void cdef IncomingSubscription subscription cdef PJSIPUA ua try: ua = _get_ua() except: return try: subscription_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if subscription_void == NULL: return subscription = subscription_void subscription._cb_tsx(ua, event) except: ua._handle_exception(1) # globals cdef pjsip_evsub_user _subs_cb _subs_cb.on_evsub_state = _Subscription_cb_state _subs_cb.on_tsx_state = _Subscription_cb_tsx _subs_cb.on_rx_notify = _Subscription_cb_notify _subs_cb.on_client_refresh = _Subscription_cb_refresh cdef pjsip_evsub_user _incoming_subs_cb _incoming_subs_cb.on_rx_refresh = _IncomingSubscription_cb_rx_refresh _incoming_subs_cb.on_server_timeout = _IncomingSubscription_cb_server_timeout _incoming_subs_cb.on_tsx_state = _IncomingSubscription_cb_tsx _re_content_type = re.compile("^([a-zA-Z0-9\-.!%*_+`'~]+)\/([a-zA-Z0-9\-.!%*_+`'~]+)$") diff --git a/sipsimple/core/_core.ua.pxi b/sipsimple/core/_core.ua.pxi index 4019a02e..d9b4f757 100644 --- a/sipsimple/core/_core.ua.pxi +++ b/sipsimple/core/_core.ua.pxi @@ -1,1229 +1,1229 @@ import errno import heapq import re import random import sys import time import traceback import os import tempfile cdef class Timer: cdef int schedule(self, float delay, timer_callback callback, object obj) except -1: cdef PJSIPUA ua = _get_ua() if delay < 0: raise ValueError("delay must be a non-negative number") if callback == NULL: raise ValueError("callback must be non-NULL") if self._scheduled: raise RuntimeError("already scheduled") self.schedule_time = PyFloat_AsDouble(time.time() + delay) self.callback = callback self.obj = obj ua._add_timer(self) self._scheduled = 1 return 0 cdef int cancel(self) except -1: cdef PJSIPUA ua = _get_ua() if not self._scheduled: return 0 ua._remove_timer(self) self._scheduled = 0 return 0 cdef int call(self) except -1: self._scheduled = 0 self.callback(self.obj, self) def __richcmp__(self, other, op): cdef double diff if not isinstance(self, Timer) or not isinstance(other, Timer): return NotImplemented diff = (self).schedule_time - (other).schedule_time if op == 0: # < return diff < 0.0 elif op == 1: # <= return diff <= 0.0 elif op == 2: # == return diff == 0.0 elif op == 3: # != return diff != 0.0 elif op == 4: # > return diff > 0.0 elif op == 5: # >= return diff >= 0.0 return cdef class PJSIPUA: def __cinit__(self, *args, **kwargs): global _ua if _ua != NULL: raise SIPCoreError("Can only have one PJSUPUA instance at the same time") _ua = self self._threads = [] self._timers = list() self._events = {} self._incoming_events = set() self._incoming_requests = set() self._sent_messages = set() def __init__(self, event_handler, *args, **kwargs): global _event_queue_lock cdef str event cdef str method cdef list accept_types cdef int status cdef PJSTR message_method = PJSTR("MESSAGE") cdef PJSTR refer_method = PJSTR("REFER") cdef PJSTR str_norefersub = PJSTR("norefersub") cdef PJSTR str_gruu = PJSTR("gruu") self._event_handler = event_handler if kwargs["log_level"] < 0 or kwargs["log_level"] > PJ_LOG_MAX_LEVEL: raise ValueError("Log level should be between 0 and %d" % PJ_LOG_MAX_LEVEL) pj_log_set_level(kwargs["log_level"]) pj_log_set_decor(PJ_LOG_HAS_YEAR | PJ_LOG_HAS_MONTH | PJ_LOG_HAS_DAY_OF_MON | PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_SENDER | PJ_LOG_HAS_INDENT) pj_log_set_log_func(_cb_log) self._pjlib = PJLIB() pj_srand(random.getrandbits(32)) # rely on python seed for now self._caching_pool = PJCachingPool() self._pjmedia_endpoint = PJMEDIAEndpoint(self._caching_pool) self._pjsip_endpoint = PJSIPEndpoint(self._caching_pool, kwargs["ip_address"], kwargs["udp_port"], kwargs["tcp_port"], kwargs["tls_port"], kwargs["tls_verify_server"], kwargs["tls_ca_file"], kwargs["tls_cert_file"], kwargs["tls_privkey_file"], kwargs["tls_timeout"]) status = pj_mutex_create_simple(self._pjsip_endpoint._pool, "event_queue_lock", &_event_queue_lock) if status != 0: raise PJSIPError("Could not initialize event queue mutex", status) self._ip_address = kwargs["ip_address"] self.codecs = kwargs["codecs"] self.video_codecs = kwargs["video_codecs"] self._module_name = PJSTR("mod-core") self._module.name = self._module_name.pj_str self._module.id = -1 self._module.priority = PJSIP_MOD_PRIORITY_APPLICATION self._module.on_rx_request = _PJSIPUA_cb_rx_request self._module.on_tsx_state = _Request_cb_tsx_state status = pjsip_endpt_register_module(self._pjsip_endpoint._obj, &self._module) if status != 0: raise PJSIPError("Could not load application module", status) status = pjsip_endpt_add_capability(self._pjsip_endpoint._obj, &self._module, PJSIP_H_ALLOW, NULL, 1, &message_method.pj_str) if status != 0: raise PJSIPError("Could not add MESSAGE method to supported methods", status) status = pjsip_endpt_add_capability(self._pjsip_endpoint._obj, &self._module, PJSIP_H_ALLOW, NULL, 1, &refer_method.pj_str) if status != 0: raise PJSIPError("Could not add REFER method to supported methods", status) status = pjsip_endpt_add_capability(self._pjsip_endpoint._obj, NULL, PJSIP_H_SUPPORTED, NULL, 1, &str_norefersub.pj_str) if status != 0: raise PJSIPError("Could not add 'norefsub' to Supported header", status) status = pjsip_endpt_add_capability(self._pjsip_endpoint._obj, NULL, PJSIP_H_SUPPORTED, NULL, 1, &str_gruu.pj_str) if status != 0: raise PJSIPError("Could not add 'gruu' to Supported header", status) self._trace_sip = int(bool(kwargs["trace_sip"])) self._detect_sip_loops = int(bool(kwargs["detect_sip_loops"])) self._enable_colorbar_device = int(bool(kwargs["enable_colorbar_device"])) self._opus_fix_module_name = PJSTR("mod-core-opus-fix") self._opus_fix_module.name = self._opus_fix_module_name.pj_str self._opus_fix_module.id = -1 self._opus_fix_module.priority = PJSIP_MOD_PRIORITY_TRANSPORT_LAYER+1 self._opus_fix_module.on_rx_request = _cb_opus_fix_rx self._opus_fix_module.on_rx_response = _cb_opus_fix_rx self._opus_fix_module.on_tx_request = _cb_opus_fix_tx self._opus_fix_module.on_tx_response = _cb_opus_fix_tx status = pjsip_endpt_register_module(self._pjsip_endpoint._obj, &self._opus_fix_module) if status != 0: raise PJSIPError("Could not load opus-fix module", status) self._trace_module_name = PJSTR("mod-core-sip-trace") self._trace_module.name = self._trace_module_name.pj_str self._trace_module.id = -1 self._trace_module.priority = 0 self._trace_module.on_rx_request = _cb_trace_rx self._trace_module.on_rx_response = _cb_trace_rx self._trace_module.on_tx_request = _cb_trace_tx self._trace_module.on_tx_response = _cb_trace_tx status = pjsip_endpt_register_module(self._pjsip_endpoint._obj, &self._trace_module) if status != 0: raise PJSIPError("Could not load sip trace module", status) self._ua_tag_module_name = PJSTR("mod-core-ua-tag") self._ua_tag_module.name = self._ua_tag_module_name.pj_str self._ua_tag_module.id = -1 self._ua_tag_module.priority = PJSIP_MOD_PRIORITY_TRANSPORT_LAYER+1 self._ua_tag_module.on_tx_request = _cb_add_user_agent_hdr self._ua_tag_module.on_tx_response = _cb_add_server_hdr status = pjsip_endpt_register_module(self._pjsip_endpoint._obj, &self._ua_tag_module) if status != 0: raise PJSIPError("Could not load User-Agent/Server header tagging module", status) self._event_module_name = PJSTR("mod-core-events") self._event_module.name = self._event_module_name.pj_str self._event_module.id = -1 self._event_module.priority = PJSIP_MOD_PRIORITY_DIALOG_USAGE status = pjsip_endpt_register_module(self._pjsip_endpoint._obj, &self._event_module) if status != 0: raise PJSIPError("Could not load events module", status) status = pjmedia_aud_dev_set_observer_cb(_cb_audio_dev_process_event); if status != 0: raise PJSIPError("Could not set audio_change callbacks", status) status = pj_rwmutex_create(self._pjsip_endpoint._pool, "ua_audio_change_rwlock", &self.audio_change_rwlock) if status != 0: raise PJSIPError("Could not initialize audio change rwmutex", status) status = pj_mutex_create_recursive(self._pjsip_endpoint._pool, "ua_video_lock", &self.video_lock) if status != 0: raise PJSIPError("Could not initialize video mutex", status) self._user_agent = PJSTR(kwargs["user_agent"]) for event, accept_types in kwargs["events"].iteritems(): self.add_event(event, accept_types) for event in kwargs["incoming_events"]: - if event not in self._events.iterkeys(): + if event not in self._events.keys(): raise ValueError('Event "%s" is not known' % event) self._incoming_events.add(event) for method in kwargs["incoming_requests"]: method = method.upper() if method in ("ACK", "BYE", "INVITE", "REFER", "SUBSCRIBE"): raise ValueError('Handling incoming "%s" requests is not allowed' % method) self._incoming_requests.add(method) self.rtp_port_range = kwargs["rtp_port_range"] self.zrtp_cache = kwargs["zrtp_cache"] pj_stun_config_init(&self._stun_cfg, &self._caching_pool._obj.factory, 0, pjmedia_endpt_get_ioqueue(self._pjmedia_endpoint._obj), pjsip_endpt_get_timer_heap(self._pjsip_endpoint._obj)) property trace_sip: def __get__(self): self._check_self() return bool(self._trace_sip) def __set__(self, value): self._check_self() self._trace_sip = int(bool(value)) property detect_sip_loops: def __get__(self): self._check_self() return bool(self._detect_sip_loops) def __set__(self, value): self._check_self() self._detect_sip_loops = int(bool(value)) property enable_colorbar_device: def __get__(self): self._check_self() return bool(self._enable_colorbar_device) def __set__(self, value): self._check_self() self._enable_colorbar_device = int(bool(value)) self.refresh_video_devices() property events: def __get__(self): self._check_self() return self._events.copy() property ip_address: def __get__(self): self._check_self() return self._ip_address def add_event(self, object event, list accept_types): cdef pj_str_t event_pj cdef pj_str_t accept_types_pj[PJSIP_MAX_ACCEPT_COUNT] cdef int index cdef object accept_type cdef int accept_cnt = len(accept_types) cdef int status self._check_self() if accept_cnt == 0: raise SIPCoreError("Need at least one of accept_types") if accept_cnt > PJSIP_MAX_ACCEPT_COUNT: raise SIPCoreError("Too many accept_types") _str_to_pj_str(event, &event_pj) for index, accept_type in enumerate(accept_types): _str_to_pj_str(accept_type, &accept_types_pj[index]) status = pjsip_evsub_register_pkg(&self._event_module, &event_pj, 3600, accept_cnt, accept_types_pj) if status != 0: raise PJSIPError("Could not register event package", status) self._events[event] = accept_types[:] property incoming_events: def __get__(self): self._check_self() return self._incoming_events.copy() def add_incoming_event(self, str event): self._check_self() - if event not in self._events.iterkeys(): + if event not in self._events.keys(): raise ValueError('Event "%s" is not known' % event) self._incoming_events.add(event) def remove_incoming_event(self, str event): self._check_self() - if event not in self._events.iterkeys(): + if event not in self._events.keys(): raise ValueError('Event "%s" is not known' % event) self._incoming_events.discard(event) property incoming_requests: def __get__(self): self._check_self() return self._incoming_requests.copy() def add_incoming_request(self, object value): cdef str method self._check_self() method = value.upper() if method in ("ACK", "BYE", "INVITE", "REFER", "SUBSCRIBE"): raise ValueError('Handling incoming "%s" requests is not allowed' % method) self._incoming_requests.add(method) def remove_incoming_request(self, object value): cdef str method self._check_self() method = value.upper() if method in ("ACK", "BYE", "INVITE", "REFER", "SUBSCRIBE"): raise ValueError('Handling incoming "%s" requests is not allowed' % method) self._incoming_requests.discard(method) cdef pj_pool_t* create_memory_pool(self, bytes name, int initial_size, int resize_size): cdef pj_pool_t *pool cdef char *c_pool_name cdef pjsip_endpoint *endpoint c_pool_name = name endpoint = self._pjsip_endpoint._obj with nogil: pool = pjsip_endpt_create_pool(endpoint, c_pool_name, initial_size, resize_size) if pool == NULL: raise SIPCoreError("Could not allocate memory pool") return pool cdef void release_memory_pool(self, pj_pool_t* pool): cdef pjsip_endpoint *endpoint endpoint = self._pjsip_endpoint._obj if pool != NULL: with nogil: pjsip_endpt_release_pool(endpoint, pool) cdef void reset_memory_pool(self, pj_pool_t* pool): if pool != NULL: with nogil: pj_pool_reset(pool) cdef object _get_sound_devices(self, int is_output): cdef int count cdef pjmedia_aud_dev_info info cdef list retval = list() cdef int status with nogil: status = pj_rwmutex_lock_read(self.audio_change_rwlock) if status != 0: raise PJSIPError('Could not acquire audio_change_rwlock', status) try: for i in range(pjmedia_aud_dev_count()): with nogil: status = pjmedia_aud_dev_get_info(i, &info) if status != 0: raise PJSIPError("Could not get audio device info", status) if is_output: count = info.output_count else: count = info.input_count if count: retval.append(decode_device_name(info.name)) return retval finally: pj_rwmutex_unlock_read(self.audio_change_rwlock) cdef object _get_default_sound_device(self, int is_output): cdef pjmedia_aud_dev_info info cdef int dev_id cdef int status with nogil: status = pj_rwmutex_lock_read(self.audio_change_rwlock) if status != 0: raise SIPCoreError('Could not acquire audio_change_rwlock', status) try: if is_output: dev_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV else: dev_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV with nogil: status = pjmedia_aud_dev_get_info(dev_id, &info) if status != 0: raise PJSIPError("Could not get audio device info", status) return decode_device_name(info.name) finally: pj_rwmutex_unlock_read(self.audio_change_rwlock) property default_output_device: def __get__(self): self._check_self() return self._get_default_sound_device(1) property default_input_device: def __get__(self): self._check_self() return self._get_default_sound_device(0) property output_devices: def __get__(self): self._check_self() return self._get_sound_devices(1) property input_devices: def __get__(self): self._check_self() return self._get_sound_devices(0) property sound_devices: def __get__(self): self._check_self() cdef int count cdef pjmedia_aud_dev_info info cdef list retval = list() cdef int status with nogil: status = pj_rwmutex_lock_read(self.audio_change_rwlock) if status != 0: raise SIPCoreError('Could not acquire audio_change_rwlock', status) try: for i in range(pjmedia_aud_dev_count()): with nogil: status = pjmedia_aud_dev_get_info(i, &info) if status == 0: retval.append(decode_device_name(info.name)) return retval finally: pj_rwmutex_unlock_read(self.audio_change_rwlock) def refresh_sound_devices(self): self._check_self() cdef int status cdef dict event_dict self.old_devices = self.sound_devices with nogil: status = pj_rwmutex_lock_write(self.audio_change_rwlock) if status != 0: raise SIPCoreError('Could not acquire audio_change_rwlock', status) with nogil: pjmedia_aud_dev_refresh() status = pj_rwmutex_unlock_write(self.audio_change_rwlock) if status != 0: raise SIPCoreError('Could not release audio_change_rwlock', status) event_dict = dict() event_dict["old_devices"] = self.old_devices event_dict["new_devices"] = self.sound_devices _add_event("AudioDevicesDidChange", event_dict) cdef object _get_video_devices(self): cdef pjmedia_vid_dev_info info cdef list retval = list() cdef int direction cdef int status for i in range(pjmedia_vid_dev_count()): with nogil: status = pjmedia_vid_dev_get_info(i, &info) if status != 0: raise PJSIPError("Could not get video device info", status) direction = info.dir if direction in (PJMEDIA_DIR_CAPTURE, PJMEDIA_DIR_CAPTURE_PLAYBACK): if (not self._enable_colorbar_device and bytes(info.driver) == "Colorbar") or bytes(info.driver) == "Null": continue retval.append(decode_device_name(info.name)) return retval cdef object _get_default_video_device(self): cdef pjmedia_vid_dev_info info cdef int status with nogil: status = pjmedia_vid_dev_get_info(PJMEDIA_VID_DEFAULT_CAPTURE_DEV, &info) if status != 0: raise PJSIPError("Could not get default video device info", status) if (not self._enable_colorbar_device and bytes(info.driver) == "Colorbar") or bytes(info.driver) == "Null": raise SIPCoreError("Could not get default video device") return decode_device_name(info.name) def refresh_video_devices(self): self._check_self() cdef int status cdef dict event_dict self.old_video_devices = self.video_devices with nogil: pjmedia_vid_dev_refresh() event_dict = dict() event_dict["old_devices"] = self.old_video_devices event_dict["new_devices"] = self.video_devices _add_event("VideoDevicesDidChange", event_dict) property default_video_device: def __get__(self): self._check_self() return self._get_default_video_device() property video_devices: def __get__(self): self._check_self() return self._get_video_devices() property available_codecs: def __get__(self): self._check_self() return self._pjmedia_endpoint._get_all_codecs() property codecs: def __get__(self): self._check_self() return self._pjmedia_endpoint._get_current_codecs() def __set__(self, value): self._check_self() self._pjmedia_endpoint._set_codecs(value) property available_video_codecs: def __get__(self): self._check_self() return self._pjmedia_endpoint._get_all_video_codecs() property video_codecs: def __get__(self): self._check_self() return self._pjmedia_endpoint._get_current_video_codecs() def __set__(self, value): self._check_self() self._pjmedia_endpoint._set_video_codecs(value) property udp_port: def __get__(self): self._check_self() if self._pjsip_endpoint._udp_transport == NULL: return None return self._pjsip_endpoint._udp_transport.local_name.port def set_udp_port(self, value): cdef int port self._check_self() if value is None: if self._pjsip_endpoint._udp_transport == NULL: return self._pjsip_endpoint._stop_udp_transport() else: port = value if not (0 <= port <= 65535): raise ValueError("Not a valid UDP port: %d" % value) if self._pjsip_endpoint._udp_transport != NULL: if port == self._pjsip_endpoint._udp_transport.local_name.port: return self._pjsip_endpoint._stop_udp_transport() self._pjsip_endpoint._start_udp_transport(port) property tcp_port: def __get__(self): self._check_self() if self._pjsip_endpoint._tcp_transport == NULL: return None return self._pjsip_endpoint._tcp_transport.addr_name.port def set_tcp_port(self, value): cdef int port self._check_self() if value is None: if self._pjsip_endpoint._tcp_transport == NULL: return self._pjsip_endpoint._stop_tcp_transport() else: port = value if not (0 <= port <= 65535): raise ValueError("Not a valid TCP port: %d" % value) if self._pjsip_endpoint._tcp_transport != NULL: if port == self._pjsip_endpoint._tcp_transport.addr_name.port: return self._pjsip_endpoint._stop_tcp_transport() self._pjsip_endpoint._start_tcp_transport(port) property tls_port: def __get__(self): self._check_self() if self._pjsip_endpoint._tls_transport == NULL: return None return self._pjsip_endpoint._tls_transport.addr_name.port property rtp_port_range: def __get__(self): self._check_self() return (self._rtp_port_start, self._rtp_port_start + self._rtp_port_count) def __set__(self, value): cdef int _rtp_port_start cdef int _rtp_port_stop cdef int _rtp_port_count cdef int _rtp_port_usable_count cdef int port self._check_self() for port in value: if not (0 <= port <= 65535): raise SIPCoreError("RTP port range values should be between 0 and 65535") _rtp_port_start, _rtp_port_stop = value _rtp_port_count = _rtp_port_stop - _rtp_port_start _rtp_port_usable_count = _rtp_port_count - _rtp_port_count % 2 # we need an even number of ports, so we won't use the last one if an odd number is provided if _rtp_port_usable_count < 2: raise SIPCoreError("RTP port range should contain at least 2 ports") self._rtp_port_start = _rtp_port_start self._rtp_port_count = _rtp_port_count self._rtp_port_usable_count = _rtp_port_usable_count self._rtp_port_index = 0 property user_agent: def __get__(self): self._check_self() return self._user_agent.str def __set__(self, value): self._check_self() self._user_agent = PJSTR("value") property log_level: def __get__(self): self._check_self() return pj_log_get_level() def __set__(self, value): self._check_self() if value < 0 or value > PJ_LOG_MAX_LEVEL: raise ValueError("Log level should be between 0 and %d" % PJ_LOG_MAX_LEVEL) pj_log_set_level(value) property tls_verify_server: def __get__(self): self._check_self() return bool(self._pjsip_endpoint._tls_verify_server) property tls_ca_file: def __get__(self): self._check_self() if self._pjsip_endpoint._tls_ca_file is None: return None else: return self._pjsip_endpoint._tls_ca_file.str property tls_cert_file: def __get__(self): self._check_self() if self._pjsip_endpoint._tls_cert_file is None: return None else: return self._pjsip_endpoint._tls_cert_file.str property tls_privkey_file: def __get__(self): self._check_self() if self._pjsip_endpoint._tls_privkey_file is None: return None else: return self._pjsip_endpoint._tls_privkey_file.str property tls_timeout: def __get__(self): self._check_self() return self._pjsip_endpoint._tls_timeout def set_tls_options(self, port=None, verify_server=False, ca_file=None, cert_file=None, privkey_file=None, int timeout=3000): cdef int c_port self._check_self() if port is None: if self._pjsip_endpoint._tls_transport == NULL: return self._pjsip_endpoint._stop_tls_transport() else: c_port = port if not (0 <= c_port <= 65535): raise ValueError("Not a valid TCP port: %d" % port) if ca_file is not None and not os.path.isfile(ca_file): raise ValueError("Cannot find the specified CA file: %s" % ca_file) if cert_file is not None and not os.path.isfile(cert_file): raise ValueError("Cannot find the specified certificate file: %s" % cert_file) if privkey_file is not None and not os.path.isfile(privkey_file): raise ValueError("Cannot find the specified private key file: %s" % privkey_file) if timeout < 0: raise ValueError("Invalid TLS timeout value: %d" % timeout) if self._pjsip_endpoint._tls_transport != NULL: self._pjsip_endpoint._stop_tls_transport() self._pjsip_endpoint._tls_verify_server = int(bool(verify_server)) if ca_file is None: self._pjsip_endpoint._tls_ca_file = None else: self._pjsip_endpoint._tls_ca_file = PJSTR(ca_file.encode(sys.getfilesystemencoding())) if cert_file is None: self._pjsip_endpoint._tls_cert_file = None else: self._pjsip_endpoint._tls_cert_file = PJSTR(cert_file.encode(sys.getfilesystemencoding())) if privkey_file is None: self._pjsip_endpoint._tls_privkey_file = None else: self._pjsip_endpoint._tls_privkey_file = PJSTR(privkey_file.encode(sys.getfilesystemencoding())) self._pjsip_endpoint._tls_timeout = timeout self._pjsip_endpoint._start_tls_transport(c_port) def detect_nat_type(self, stun_server_address, stun_server_port=PJ_STUN_PORT, object user_data=None): cdef pj_str_t stun_server_address_pj cdef pj_sockaddr_in stun_server cdef int status self._check_self() if not _is_valid_ip(pj_AF_INET(), stun_server_address): raise ValueError("Not a valid IPv4 address: %s" % stun_server_address) _str_to_pj_str(stun_server_address, &stun_server_address_pj) status = pj_sockaddr_in_init(&stun_server, &stun_server_address_pj, stun_server_port) if status != 0: raise PJSIPError("Could not init STUN server address", status) status = pj_stun_detect_nat_type(&stun_server, &self._stun_cfg, user_data, _cb_detect_nat_type) if status != 0: raise PJSIPError("Could not start NAT type detection", status) Py_INCREF(user_data) def set_nameservers(self, list nameservers): self._check_self() return self._pjsip_endpoint._set_dns_nameservers([n for n in nameservers if _re_ipv4.match(n)]) def set_h264_options(self, profile, level): self._check_self() self._pjmedia_endpoint._set_h264_options(str(profile), int(level.replace('.', ''))) def set_video_options(self, max_resolution, int max_framerate, object max_bitrate): self._check_self() self._pjmedia_endpoint._set_video_options(tuple(max_resolution), max_framerate, max_bitrate or 0.0) property zrtp_cache: def __get__(self): self._check_self() return self._zrtp_cache def __set__(self, value): self._check_self() if value is None: value = os.path.join(tempfile.gettempdir(), 'zrtp_cache_%d.db' % os.getpid()) self._zrtp_cache = value def __dealloc__(self): self.dealloc() def dealloc(self): global _ua, _dealloc_handler_queue, _event_queue_lock if _ua == NULL: return self._check_thread() pjmedia_aud_dev_set_observer_cb(NULL) if self.audio_change_rwlock != NULL: pj_rwmutex_destroy(self.audio_change_rwlock) self.audio_change_rwlock = NULL if self.video_lock != NULL: pj_mutex_destroy(self.video_lock) self.video_lock = NULL _process_handler_queue(self, &_dealloc_handler_queue) if _event_queue_lock != NULL: pj_mutex_lock(_event_queue_lock) pj_mutex_destroy(_event_queue_lock) _event_queue_lock = NULL self._pjsip_endpoint = None self._pjmedia_endpoint = None self._caching_pool = None self._pjlib = None _ua = NULL self._poll_log() cdef int _poll_log(self) except -1: cdef object event_name cdef dict event_params cdef list events events = _get_clear_event_queue() for event_name, event_params in events: self._event_handler(event_name, **event_params) def poll(self): global _post_poll_handler_queue cdef int status cdef double now cdef object retval = None cdef float max_timeout cdef pj_time_val pj_max_timeout cdef list timers cdef Timer timer self._check_self() max_timeout = 0.100 while self._timers: if not (self._timers[0])._scheduled: # timer was cancelled heapq.heappop(self._timers) else: max_timeout = min(max((self._timers[0]).schedule_time - time.time(), 0.0), max_timeout) break pj_max_timeout.sec = int(max_timeout) pj_max_timeout.msec = int(max_timeout * 1000) % 1000 with nogil: status = pjsip_endpt_handle_events(self._pjsip_endpoint._obj, &pj_max_timeout) IF UNAME_SYSNAME == "Darwin": if status not in [0, PJ_ERRNO_START_SYS + errno.EBADF]: raise PJSIPError("Error while handling events", status) ELSE: if status != 0: raise PJSIPError("Error while handling events", status) _process_handler_queue(self, &_post_poll_handler_queue) timers = list() now = time.time() while self._timers: if not (self._timers[0])._scheduled: # timer was cancelled heapq.heappop(self._timers) elif (self._timers[0]).schedule_time <= now: # timer needs to be processed timer = heapq.heappop(self._timers) timers.append(timer) else: break for timer in timers: timer.call() self._poll_log() if self._fatal_error: return True else: return False cdef int _handle_exception(self, int is_fatal) except -1: cdef object exc_type cdef object exc_val cdef object exc_tb exc_type, exc_val, exc_tb = sys.exc_info() if is_fatal: self._fatal_error = is_fatal _add_event("SIPEngineGotException", dict(type=exc_type, value=exc_val, traceback="".join(traceback.format_exception(exc_type, exc_val, exc_tb)))) return 0 cdef int _check_self(self) except -1: global _ua if _ua == NULL: raise SIPCoreError("The PJSIPUA is no longer running") self._check_thread() cdef int _check_thread(self) except -1: if not pj_thread_is_registered(): self._threads.append(PJSIPThread()) return 0 cdef int _add_timer(self, Timer timer) except -1: heapq.heappush(self._timers, timer) return 0 cdef int _remove_timer(self, Timer timer) except -1: # Don't remove it from the heap, just mark it as not scheduled timer._scheduled = 0 return 0 cdef int _cb_rx_request(self, pjsip_rx_data *rdata) except 0: global _event_hdr_name cdef int status cdef int bad_request cdef pjsip_tx_data *tdata = NULL cdef pjsip_hdr_ptr_const hdr_add cdef IncomingRequest request cdef Invitation inv cdef IncomingSubscription sub cdef IncomingReferral ref cdef list extra_headers cdef dict event_dict cdef dict message_params cdef pj_str_t tsx_key cdef pjsip_via_hdr *top_via cdef pjsip_via_hdr *via cdef pjsip_transaction *tsx = NULL cdef unsigned int options = PJSIP_INV_SUPPORT_100REL cdef pjsip_event_hdr *event_hdr cdef object method_name = _pj_str_to_str(rdata.msg_info.msg.line.req.method.name) if method_name != "ACK": if self._detect_sip_loops: # Temporarily trick PJSIP into believing the last Via header is actually the first top_via = via = rdata.msg_info.via while True: rdata.msg_info.via = via via = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_VIA, ( via).next) if via == NULL: break status = pjsip_tsx_create_key(rdata.tp_info.pool, &tsx_key, PJSIP_ROLE_UAC, &rdata.msg_info.msg.line.req.method, rdata) rdata.msg_info.via = top_via if status != 0: raise PJSIPError("Could not generate transaction key for incoming request", status) tsx = pjsip_tsx_layer_find_tsx(&tsx_key, 0) if tsx != NULL: status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 482, NULL, &tdata) if status != 0: raise PJSIPError("Could not create response", status) elif method_name in self._incoming_requests: request = IncomingRequest() request.init(self, rdata) elif method_name == "OPTIONS": status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 200, NULL, &tdata) if status != 0: raise PJSIPError("Could not create response", status) for hdr_type in [PJSIP_H_ALLOW, PJSIP_H_ACCEPT, PJSIP_H_SUPPORTED]: hdr_add = pjsip_endpt_get_capability(self._pjsip_endpoint._obj, hdr_type, NULL) if hdr_add != NULL: pjsip_msg_add_hdr(tdata.msg, pjsip_hdr_clone(tdata.pool, hdr_add)) elif method_name == "INVITE": status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, self._pjsip_endpoint._obj, &tdata) if status == 0: inv = Invitation() inv.init_incoming(self, rdata, options) elif method_name == "SUBSCRIBE": event_hdr = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_event_hdr_name.pj_str, NULL) if event_hdr == NULL or _pj_str_to_str(event_hdr.event_type) not in self._incoming_events: status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 489, NULL, &tdata) if status != 0: raise PJSIPError("Could not create response", status) else: sub = IncomingSubscription() sub.init(self, rdata, _pj_str_to_str(event_hdr.event_type)) elif method_name == "REFER": ref = IncomingReferral() ref.init(self, rdata) elif method_name == "MESSAGE": bad_request = 0 extra_headers = list() message_params = dict() event_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) message_params["request_uri"] = event_dict["request_uri"] message_params["from_header"] = event_dict["headers"].get("From", None) message_params["to_header"] = event_dict["headers"].get("To", None) message_params["headers"] = event_dict["headers"] message_params["body"] = event_dict["body"] content_type = message_params["headers"].get("Content-Type", None) if content_type is not None: message_params["content_type"] = content_type.content_type if message_params["headers"].get("Content-Length", 0) > 0 and message_params["body"] is None: bad_request = 1 extra_headers.append(WarningHeader(399, "local", "Missing body")) else: message_params["content_type"] = None if message_params["headers"].get("Content-Length", 0) > 0 and message_params["body"] is None: bad_request = 1 extra_headers.append(WarningHeader(399, "local", "Missing Content-Type header")) if bad_request: status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 400, NULL, &tdata) if status != 0: raise PJSIPError("Could not create response", status) _add_headers_to_tdata(tdata, extra_headers) else: _add_event("SIPEngineGotMessage", message_params) status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 200, NULL, &tdata) if status != 0: raise PJSIPError("Could not create response", status) elif method_name != "ACK": status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 405, NULL, &tdata) if status != 0: raise PJSIPError("Could not create response", status) if tdata != NULL: status = pjsip_endpt_send_response2(self._pjsip_endpoint._obj, rdata, tdata, NULL, NULL) if status != 0: pjsip_tx_data_dec_ref(tdata) raise PJSIPError("Could not send response", status) return 1 cdef class PJSIPThread: def __cinit__(self): str_id = "python_%d" % id(self) cdef object thread_name = str_id.encode() cdef int status status = pj_thread_register(thread_name, self._thread_desc, &self._obj) if status != 0: raise PJSIPError("Error while registering thread", status) # callback functions cdef void _cb_audio_dev_process_event(pjmedia_aud_dev_event event) with gil: cdef PJSIPUA ua event_dict = dict() try: ua = _get_ua() except: return try: if event in (PJMEDIA_AUD_DEV_DEFAULT_INPUT_CHANGED, PJMEDIA_AUD_DEV_DEFAULT_OUTPUT_CHANGED): event_dict["changed_input"] = event == PJMEDIA_AUD_DEV_DEFAULT_INPUT_CHANGED event_dict["changed_output"] = event == PJMEDIA_AUD_DEV_DEFAULT_OUTPUT_CHANGED _add_event("DefaultAudioDeviceDidChange", event_dict) elif event == PJMEDIA_AUD_DEV_LIST_WILL_REFRESH: ua.old_devices = ua.sound_devices with nogil: status = pj_rwmutex_lock_write(ua.audio_change_rwlock) if status != 0: raise SIPCoreError('Could not acquire audio_change_rwlock for writing', status) elif event == PJMEDIA_AUD_DEV_LIST_DID_REFRESH: with nogil: status = pj_rwmutex_unlock_write(ua.audio_change_rwlock) if status != 0: raise SIPCoreError('Could not release the audio_change_rwlock', status) event_dict["old_devices"] = ua.old_devices event_dict["new_devices"] = ua.sound_devices _add_event("AudioDevicesDidChange", event_dict) except: ua._handle_exception(1) cdef void _cb_detect_nat_type(void *user_data, pj_stun_nat_detect_result_ptr_const res) with gil: cdef PJSIPUA ua cdef dict event_dict cdef object user_data_obj = user_data Py_DECREF(user_data_obj) try: ua = _get_ua() except: return try: event_dict = dict() event_dict["succeeded"] = res.status == 0 event_dict["user_data"] = user_data_obj if res.status == 0: event_dict["nat_type"] = res.nat_type_name else: event_dict["error"] = res.status_text _add_event("SIPEngineDetectedNATType", event_dict) except: ua._handle_exception(0) cdef int _PJSIPUA_cb_rx_request(pjsip_rx_data *rdata) with gil: cdef PJSIPUA ua try: ua = _get_ua() except: return 0 try: return ua._cb_rx_request(rdata) except: ua._handle_exception(0) cdef int _cb_opus_fix_tx(pjsip_tx_data *tdata) with gil: cdef PJSIPUA ua cdef pjsip_msg_body *body cdef pjsip_msg_body *new_body cdef pjmedia_sdp_session *sdp cdef pjmedia_sdp_media *media cdef pjmedia_sdp_attr *attr cdef int i cdef int j cdef pj_str_t new_value try: ua = _get_ua() except: return 0 try: if tdata != NULL and tdata.msg != NULL: body = tdata.msg.body if body != NULL and _pj_str_to_str(body.content_type.type).lower() == "application" and _pj_str_to_str(body.content_type.subtype).lower() == "sdp": new_body = pjsip_msg_body_clone(tdata.pool, body) sdp = new_body.data for i in range(sdp.media_count): media = sdp.media[i] if _pj_str_to_str(media.desc.media).lower() != "audio": continue for j in range(media.attr_count): attr = media.attr[j] if _pj_str_to_str(attr.name).lower() != "rtpmap": continue attr_value = _pj_str_to_str(attr.value).lower() pos = attr_value.find("opus") if pos == -1: continue # this is the opus rtpmap attribute opus_line = attr_value[:pos] + "opus/48000/2" new_value.slen = len(opus_line) new_value.ptr = pj_pool_alloc(tdata.pool, new_value.slen) memcpy(new_value.ptr, PyBytes_AsString(opus_line), new_value.slen) attr.value = new_value break tdata.msg.body = new_body except: ua._handle_exception(0) return 0 cdef int _cb_opus_fix_rx(pjsip_rx_data *rdata) with gil: cdef PJSIPUA ua cdef pjsip_msg_body *body cdef int pos1 cdef int pos2 cdef char *body_ptr try: ua = _get_ua() except: return 0 try: if rdata != NULL and rdata.msg_info.msg != NULL: body = rdata.msg_info.msg.body if body != NULL and _pj_str_to_str(body.content_type.type).lower() == "application" and _pj_str_to_str(body.content_type.subtype).lower() == "sdp": body_ptr = body.data body_str = _pj_buf_len_to_str(body_ptr, body.len).lower() pos1 = body_str.find("opus/48000") if pos1 != -1: pos2 = body_str.find("opus/48000/2") if pos2 != -1: memcpy(body_ptr + pos2 + 11, '1', 1) else: # old opus, we must make it fail memcpy(body_ptr + pos1 + 5, 'XXXXX', 5) except: ua._handle_exception(0) return 0 cdef int _cb_trace_rx(pjsip_rx_data *rdata) with gil: cdef PJSIPUA ua try: ua = _get_ua() except: return 0 try: if ua._trace_sip: _add_event("SIPEngineSIPTrace", dict(received=True, source_ip=rdata.pkt_info.src_name, source_port=rdata.pkt_info.src_port, destination_ip=_pj_str_to_str(rdata.tp_info.transport.local_name.host), destination_port=rdata.tp_info.transport.local_name.port, data=_pj_buf_len_to_str(rdata.pkt_info.packet, rdata.pkt_info.len), transport=rdata.tp_info.transport.type_name)) except: ua._handle_exception(0) return 0 cdef int _cb_trace_tx(pjsip_tx_data *tdata) with gil: cdef PJSIPUA ua try: ua = _get_ua() except: return 0 try: if ua._trace_sip: _add_event("SIPEngineSIPTrace", dict(received=False, source_ip=_pj_str_to_str(tdata.tp_info.transport.local_name.host), source_port=tdata.tp_info.transport.local_name.port, destination_ip=tdata.tp_info.dst_name, destination_port=tdata.tp_info.dst_port, data=_pj_buf_len_to_str(tdata.buf.start, tdata.buf.cur - tdata.buf.start), transport=tdata.tp_info.transport.type_name)) except: ua._handle_exception(0) return 0 cdef int _cb_add_user_agent_hdr(pjsip_tx_data *tdata) with gil: cdef PJSIPUA ua cdef pjsip_hdr *hdr cdef void *found_hdr try: ua = _get_ua() except: return 0 try: found_hdr = pjsip_msg_find_hdr_by_name(tdata.msg, &_user_agent_hdr_name.pj_str, NULL) if found_hdr == NULL: hdr = pjsip_generic_string_hdr_create(tdata.pool, &_user_agent_hdr_name.pj_str, &ua._user_agent.pj_str) if hdr == NULL: raise SIPCoreError('Could not add "User-Agent" header to outgoing request') pjsip_msg_add_hdr(tdata.msg, hdr) except: ua._handle_exception(0) return 0 cdef int _cb_add_server_hdr(pjsip_tx_data *tdata) with gil: cdef PJSIPUA ua cdef pjsip_hdr *hdr cdef void *found_hdr try: ua = _get_ua() except: return 0 try: found_hdr = pjsip_msg_find_hdr_by_name(tdata.msg, &_server_hdr_name.pj_str, NULL) if found_hdr == NULL: hdr = pjsip_generic_string_hdr_create(tdata.pool, &_server_hdr_name.pj_str, &ua._user_agent.pj_str) if hdr == NULL: raise SIPCoreError('Could not add "Server" header to outgoing response') pjsip_msg_add_hdr(tdata.msg, hdr) except: ua._handle_exception(0) return 0 # functions cdef PJSIPUA _get_ua(): global _ua cdef PJSIPUA ua if _ua == NULL: raise SIPCoreError("PJSIPUA is not instantiated") ua = _ua ua._check_thread() return ua cdef int deallocate_weakref(object weak_ref, object timer) except -1 with gil: Py_DECREF(weak_ref) # globals cdef void *_ua = NULL cdef PJSTR _user_agent_hdr_name = PJSTR("User-Agent") cdef PJSTR _server_hdr_name = PJSTR("Server") cdef PJSTR _event_hdr_name = PJSTR("Event") cdef object _re_ipv4 = re.compile(r"^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$") diff --git a/sipsimple/core/_core.util.pxi b/sipsimple/core/_core.util.pxi index b862d7c5..31586f0a 100644 --- a/sipsimple/core/_core.util.pxi +++ b/sipsimple/core/_core.util.pxi @@ -1,422 +1,422 @@ import platform import re import sys from application.version import Version cdef class PJSTR: def __cinit__(self, str): self.str = str _str_to_pj_str(str, &self.pj_str) def __str__(self): return self.str cdef class SIPStatusMessages: cdef object _default_status def __cinit__(self, *args, **kwargs): self._default_status = _pj_str_to_str(pjsip_get_status_text(0)[0]) def __getitem__(self, int val): cdef object _status _status = _pj_str_to_str(pjsip_get_status_text(val)[0]) if _status == self._default_status: raise IndexError("Unknown SIP response code: %d" % val) return _status cdef class frozenlist: def __cinit__(self, *args, **kw): self.list = list() self.initialized = 0 self.hash = 0 def __init__(self, *args, **kw): if not self.initialized: self.list = list(*args, **kw) self.initialized = 1 self.hash = hash(tuple(self.list)) def __reduce__(self): return (self.__class__, (self.list,), None) def __repr__(self): return "frozenlist(%r)" % self.list def __len__(self): return self.list.__len__() def __hash__(self): return self.hash def __iter__(self): return self.list.__iter__() def __cmp__(self, frozenlist other): return self.list.__cmp__(other.list) def __richcmp__(frozenlist self, other, op): if isinstance(other, frozenlist): other = (other).list if op == 0: return self.list.__cmp__(other) < 0 elif op == 1: return self.list.__cmp__(other) <= 0 elif op == 2: return self.list.__eq__(other) elif op == 3: return self.list.__ne__(other) elif op == 4: return self.list.__cmp__(other) > 0 elif op == 5: return self.list.__cmp__(other) >= 0 else: return NotImplemented def __contains__(self, item): return self.list.__contains__(item) def __getitem__(self, key): return self.list.__getitem__(key) def __add__(first, second): if isinstance(first, frozenlist): first = (first).list if isinstance(second, frozenlist): second = (second).list return frozenlist(first+second) def __mul__(first, second): if isinstance(first, frozenlist): first = (first).list if isinstance(second, frozenlist): second = (second).list return frozenlist(first*second) def __reversed__(self): return self.list.__reversed__() def count(self, elem): return self.list.count(elem) def index(self, elem): return self.list.index(elem) cdef class frozendict: def __cinit__(self, *args, **kw): self.dict = dict() self.initialized = 0 def __init__(self, *args, **kw): if not self.initialized: self.dict = dict(*args, **kw) self.initialized = 1 self.hash = hash(tuple(self.dict.iteritems())) def __reduce__(self): return (self.__class__, (self.dict,), None) def __repr__(self): return "frozendict(%r)" % self.dict def __len__(self): return self.dict.__len__() def __hash__(self): return self.hash def __iter__(self): return self.dict.__iter__() def __cmp__(self, frozendict other): return self.dict.__cmp__(other.dict) def __richcmp__(frozendict self, other, op): if isinstance(other, frozendict): other = (other).dict if op == 0: return self.dict.__cmp__(other) < 0 elif op == 1: return self.dict.__cmp__(other) <= 0 elif op == 2: return self.dict.__eq__(other) elif op == 3: return self.dict.__ne__(other) elif op == 4: return self.dict.__cmp__(other) > 0 elif op == 5: return self.dict.__cmp__(other) >= 0 else: return NotImplemented def __contains__(self, item): return self.dict.__contains__(item) def __getitem__(self, key): return self.dict.__getitem__(key) def copy(self): return self def get(self, *args): return self.dict.get(*args) def has_key(self, key): return self.dict.has_key(key) def items(self): - return self.dict.items() + return list(self.dict.items()) def iteritems(self): - return self.dict.iteritems() + return list(self.dict.items()) def iterkeys(self): - return self.dict.iterkeys() + return list(self.dict.keys()) def itervalues(self): - return self.dict.itervalues() + return list(self.dict.values()) def keys(self): - return self.dict.keys() + return list(self.dict.keys()) def values(self): - return self.dict.values() + return list(self.dict.values()) # functions cdef int _str_to_pj_str(object string, pj_str_t *pj_str) except -1: # Feed data from Python to PJSIP # TODO: convert to Python3 bytes_string = string.encode() pj_str.ptr = PyBytes_AsString(bytes_string) pj_str.slen = len(bytes_string) print("Encoded STR %s to PJS %s" % (string, pj_str.ptr)) cdef object _pj_str_to_str(pj_str_t pj_str): # Feed data from PJSIP to the Python bytes_string = PyBytes_FromStringAndSize(pj_str.ptr, pj_str.slen) string = bytes_string.decode() print("Decoded PJS %s to STR %s" % (pj_str.ptr, string)) return string cdef object _pj_buf_len_to_str(object buf, int buf_len): # TODO: convert to Python3 return PyBytes_FromStringAndSize(buf, buf_len) cdef object _buf_to_str(object buf): # TODO: convert to Python3 return PyBytes_FromString(buf) cdef object _str_as_str(object string): # TODO: convert to Python3 return PyBytes_AsString(string) cdef object _str_as_size(object string): # TODO: convert to Python3 return PyBytes_Size(string) cdef object _pj_status_to_str(int status): cdef char buf[PJ_ERR_MSG_SIZE] return _pj_str_to_str(pj_strerror(status, buf, PJ_ERR_MSG_SIZE)) cdef object _pj_status_to_def(int status): return _re_pj_status_str_def.match(_pj_status_to_str(status)).group(1) cdef dict _pjsip_param_to_dict(pjsip_param *param_list): cdef pjsip_param *param cdef dict retval = dict() param = ( param_list).next while param != param_list: if param.value.slen == 0: retval[_pj_str_to_str(param.name)] = None else: retval[_pj_str_to_str(param.name)] = _pj_str_to_str(param.value) param = ( param).next return retval cdef int _dict_to_pjsip_param(object params, pjsip_param *param_list, pj_pool_t *pool): cdef pjsip_param *param = NULL for name, value in params.iteritems(): param = pj_pool_alloc(pool, sizeof(pjsip_param)) if param == NULL: return -1 _str_to_pj_str(name, ¶m.name) if value is None: param.value.slen = 0 else: _str_to_pj_str(value, ¶m.value) pj_list_insert_after( param_list, param) return 0 cdef int _pjsip_msg_to_dict(pjsip_msg *msg, dict info_dict) except -1: cdef pjsip_msg_body *body cdef pjsip_hdr *header cdef pjsip_generic_array_hdr *array_header cdef pjsip_ctype_hdr *ctype_header cdef pjsip_cseq_hdr *cseq_header cdef char *buf cdef int buf_len, i, status headers = {} header = ( &msg.hdr).next while header != &msg.hdr: header_name = _pj_str_to_str(header.name) header_data = None multi_header = False if header_name in ("Accept", "Allow", "Require", "Supported", "Unsupported", "Allow-Events"): array_header = header header_data = [] for i from 0 <= i < array_header.count: header_data.append(_pj_str_to_str(array_header.values[i])) elif header_name == "Contact": multi_header = True header_data = FrozenContactHeader_create( header) elif header_name == "Content-Length": header_data = ( header).len elif header_name == "Content-Type": header_data = FrozenContentTypeHeader_create( header) elif header_name == "CSeq": cseq_header = header header_data = (cseq_header.cseq, _pj_str_to_str(cseq_header.method.name)) elif header_name in ("Expires", "Max-Forwards", "Min-Expires"): header_data = ( header).ivalue elif header_name == "From": header_data = FrozenFromHeader_create( header) elif header_name == "To": header_data = FrozenToHeader_create( header) elif header_name == "Route": multi_header = True header_data = FrozenRouteHeader_create( header) elif header_name == "Reason": value = _pj_str_to_str((header).hvalue) protocol, sep, params_str = value.partition(';') params = frozendict([(name, value or None) for name, sep, value in [param.partition('=') for param in params_str.split(';')]]) header_data = FrozenReasonHeader(protocol, params) elif header_name == "Record-Route": multi_header = True header_data = FrozenRecordRouteHeader_create( header) elif header_name == "Retry-After": header_data = FrozenRetryAfterHeader_create( header) elif header_name == "Via": multi_header = True header_data = FrozenViaHeader_create( header) elif header_name == "Warning": match = _re_warning_hdr.match(_pj_str_to_str((header).hvalue)) if match is not None: warning_params = match.groupdict() warning_params['code'] = int(warning_params['code']) header_data = FrozenWarningHeader(**warning_params) elif header_name == "Event": header_data = FrozenEventHeader_create( header) elif header_name == "Subscription-State": header_data = FrozenSubscriptionStateHeader_create( header) elif header_name == "Refer-To": header_data = FrozenReferToHeader_create( header) elif header_name == "Subject": header_data = FrozenSubjectHeader_create( header) elif header_name == "Replaces": header_data = FrozenReplacesHeader_create( header) # skip the following headers: elif header_name not in ("Authorization", "Proxy-Authenticate", "Proxy-Authorization", "WWW-Authenticate"): header_data = FrozenHeader(header_name, _pj_str_to_str(( header).hvalue)) if header_data is not None: if multi_header: headers.setdefault(header_name, []).append(header_data) else: if header_name not in headers: headers[header_name] = header_data header = ( header).next info_dict["headers"] = headers body = msg.body if body == NULL: info_dict["body"] = None else: status = pjsip_print_body(body, &buf, &buf_len) if status != 0: info_dict["body"] = None else: info_dict["body"] = _pj_buf_len_to_str(buf, buf_len) if msg.type == PJSIP_REQUEST_MSG: info_dict["method"] = _pj_str_to_str(msg.line.req.method.name) # You need to call pjsip_uri_get_uri on the request URI if the message is for transmitting, # but it isn't required if message is one received. Otherwise, a seg fault occurs. Don't ask. info_dict["request_uri"] = FrozenSIPURI_create(pjsip_uri_get_uri(msg.line.req.uri)) else: info_dict["code"] = msg.line.status.code info_dict["reason"] = _pj_str_to_str(msg.line.status.reason) return 0 cdef int _is_valid_ip(int af, object ip) except -1: cdef char buf[16] cdef pj_str_t src cdef int status _str_to_pj_str(ip, &src) status = pj_inet_pton(af, &src, buf) if status == 0: return 1 else: return 0 cdef int _get_ip_version(object ip) except -1: if _is_valid_ip(pj_AF_INET(), ip): return pj_AF_INET() elif _is_valid_ip(pj_AF_INET6(), ip): return pj_AF_INET() else: return 0 cdef int _add_headers_to_tdata(pjsip_tx_data *tdata, object headers) except -1: cdef pj_str_t name_pj, value_pj cdef pjsip_hdr *hdr for header in headers: _str_to_pj_str(header.name, &name_pj) _str_to_pj_str(header.body, &value_pj) hdr = pjsip_generic_string_hdr_create(tdata.pool, &name_pj, &value_pj) pjsip_msg_add_hdr(tdata.msg, hdr) cdef int _remove_headers_from_tdata(pjsip_tx_data *tdata, object headers) except -1: cdef pj_str_t header_name_pj cdef pjsip_hdr *hdr for header in headers: _str_to_pj_str(header, &header_name_pj) hdr = pjsip_msg_find_remove_hdr_by_name(tdata.msg, &header_name_pj, NULL) cdef int _BaseSIPURI_to_pjsip_sip_uri(BaseSIPURI uri, pjsip_sip_uri *pj_uri, pj_pool_t *pool) except -1: cdef pjsip_param *param pjsip_sip_uri_init(pj_uri, uri.secure) if uri.user: _str_to_pj_str(uri.user, &pj_uri.user) if uri.password: _str_to_pj_str(uri.password, &pj_uri.passwd) if uri.host: _str_to_pj_str(uri.host, &pj_uri.host) if uri.port: pj_uri.port = uri.port for name, value in uri.parameters.iteritems(): if name == "lr": pj_uri.lr_param = 1 elif name == "maddr": _str_to_pj_str(value, &pj_uri.maddr_param) elif name == "method": _str_to_pj_str(value, &pj_uri.method_param) elif name == "transport": _str_to_pj_str(value, &pj_uri.transport_param) elif name == "ttl": pj_uri.ttl_param = int(value) elif name == "user": _str_to_pj_str(value, &pj_uri.user_param) else: param = pj_pool_alloc(pool, sizeof(pjsip_param)) _str_to_pj_str(name, ¶m.name) if value is None: param.value.slen = 0 else: _str_to_pj_str(value, ¶m.value) pj_list_insert_after( &pj_uri.other_param, param) _dict_to_pjsip_param(uri.headers, &pj_uri.header_param, pool) return 0 cdef int _BaseRouteHeader_to_pjsip_route_hdr(BaseIdentityHeader header, pjsip_route_hdr *pj_header, pj_pool_t *pool) except -1: cdef pjsip_param *param cdef pjsip_sip_uri *sip_uri pjsip_route_hdr_init(NULL, pj_header) sip_uri = pj_pool_alloc(pool, sizeof(pjsip_sip_uri)) _BaseSIPURI_to_pjsip_sip_uri(header.uri, sip_uri, pool) pj_header.name_addr.uri = sip_uri if header.display_name: _str_to_pj_str(header.display_name.encode('utf-8'), &pj_header.name_addr.display) _dict_to_pjsip_param(header.parameters, &pj_header.other_param, pool) return 0 def _get_device_name_encoding(): if sys.platform == 'win32': encoding = 'mbcs' elif sys.platform.startswith('linux2') and Version.parse(platform.release()) < Version(2,6,31): encoding = 'latin1' else: encoding = 'utf-8' return encoding _device_name_encoding = _get_device_name_encoding() def decode_device_name(device_name): # ignore decoding errors, some systems (I'm looking at you, OSX), seem to misbehave return device_name.decode(_device_name_encoding, 'ignore') # globals cdef object _re_pj_status_str_def = re.compile("^.*\((.*)\)$") cdef object _re_warning_hdr = re.compile('(?P[0-9]{3}) (?P.*?) "(?P.*?)"') sip_status_messages = SIPStatusMessages()