Page MenuHomePhabricator

No OneTemporary

diff --git a/sipsimple/core/_core.lib.pxi b/sipsimple/core/_core.lib.pxi
index 0c2632d5..fa336155 100644
--- a/sipsimple/core/_core.lib.pxi
+++ b/sipsimple/core/_core.lib.pxi
@@ -1,551 +1,552 @@
import sys
# classes
cdef class PJLIB:
def __cinit__(self):
cdef int status
status = pj_init()
if status != 0:
raise PJSIPError("Could not initialize PJLIB", status)
self._init_done = 1
status = pjlib_util_init()
if status != 0:
raise PJSIPError("Could not initialize PJLIB-UTIL", status)
status = pjnath_init()
if status != 0:
raise PJSIPError("Could not initialize PJNATH", status)
def __dealloc__(self):
if self._init_done:
with nogil:
pj_shutdown()
cdef class PJCachingPool:
def __cinit__(self):
pj_caching_pool_init(&self._obj, &pj_pool_factory_default_policy, 0)
self._init_done = 1
def __dealloc__(self):
if self._init_done:
pj_caching_pool_destroy(&self._obj)
cdef class PJSIPEndpoint:
def __cinit__(self, PJCachingPool caching_pool, ip_address, udp_port, tcp_port, tls_port,
tls_verify_server, tls_ca_file, tls_cert_file, tls_privkey_file, int tls_timeout):
cdef pj_dns_resolver *resolver
cdef pjsip_tpmgr *tpmgr
cdef int status
if ip_address is not None and not _is_valid_ip(pj_AF_INET(), ip_address.encode()):
raise ValueError("Not a valid IPv4 address: %s" % ip_address)
self._local_ip_used = ip_address
status = pjsip_endpt_create(&caching_pool._obj.factory, "core", &self._obj)
if status != 0:
raise PJSIPError("Could not initialize PJSIP endpoint", status)
self._pool = pjsip_endpt_create_pool(self._obj, "PJSIPEndpoint", 4096, 4096)
if self._pool == NULL:
raise SIPCoreError("Could not allocate memory pool")
status = pjsip_tsx_layer_init_module(self._obj)
if status != 0:
raise PJSIPError("Could not initialize transaction layer module", status)
status = pjsip_ua_init_module(self._obj, NULL) # TODO: handle forking
if status != 0:
raise PJSIPError("Could not initialize common dialog layer module", status)
status = pjsip_evsub_init_module(self._obj)
if status != 0:
raise PJSIPError("Could not initialize event subscription module", status)
status = pjsip_100rel_init_module(self._obj)
if status != 0:
raise PJSIPError("Could not initialize 100rel module", status)
status = pjsip_replaces_init_module(self._obj)
if status != 0:
raise PJSIPError("Could not initialize replaces module", status)
status = pjsip_inv_usage_init(self._obj, &_inv_cb)
if status != 0:
raise PJSIPError("Could not initialize invitation module", status)
status = pjsip_endpt_create_resolver(self._obj, &resolver)
if status != 0:
raise PJSIPError("Could not create fake DNS resolver for endpoint", status)
status = pjsip_endpt_set_resolver(self._obj, resolver)
if status != 0:
raise PJSIPError("Could not set fake DNS resolver on endpoint", status)
tpmgr = pjsip_endpt_get_tpmgr(self._obj)
if tpmgr == NULL:
raise SIPCoreError("Could not get the transport manager")
status = pjsip_tpmgr_set_state_cb(tpmgr, _transport_state_cb)
if status != 0:
raise PJSIPError("Could not set transport state callback", status)
if udp_port is not None:
self._start_udp_transport(udp_port)
if tcp_port is not None:
self._start_tcp_transport(tcp_port)
self._tls_verify_server = int(tls_verify_server)
if tls_ca_file is not None:
self._tls_ca_file = PJSTR(tls_ca_file.encode(sys.getfilesystemencoding()))
if tls_cert_file is not None:
self._tls_cert_file = PJSTR(tls_cert_file.encode(sys.getfilesystemencoding()))
if tls_privkey_file is not None:
self._tls_privkey_file = PJSTR(tls_privkey_file.encode(sys.getfilesystemencoding()))
if tls_timeout < 0:
raise ValueError("Invalid TLS timeout value: %d" % tls_timeout)
self._tls_timeout = tls_timeout
if tls_port is not None:
self._start_tls_transport(tls_port)
cdef int _make_local_addr(self, pj_sockaddr_in *local_addr, object ip_address, int port) except -1:
cdef pj_str_t local_ip_pj
cdef pj_str_t *local_ip_p = NULL
cdef int status
if not (0 <= port <= 65535):
raise SIPCoreError("Invalid port: %d" % port)
if ip_address is not None and ip_address is not "0.0.0.0":
local_ip_p = &local_ip_pj
_str_to_pj_str(ip_address.encode(), local_ip_p)
status = pj_sockaddr_in_init(local_addr, local_ip_p, port)
if status != 0:
raise PJSIPError("Could not create local address", status)
return 0
cdef int _start_udp_transport(self, int port) except -1:
cdef pj_sockaddr_in local_addr
self._make_local_addr(&local_addr, self._local_ip_used, port)
status = pjsip_udp_transport_start(self._obj, &local_addr, NULL, 1, &self._udp_transport)
if status != 0:
raise PJSIPError("Could not create UDP transport", status)
return 0
cdef int _stop_udp_transport(self) except -1:
pjsip_transport_shutdown(self._udp_transport)
self._udp_transport = NULL
return 0
cdef int _start_tcp_transport(self, int port) except -1:
cdef pj_sockaddr_in local_addr
self._make_local_addr(&local_addr, self._local_ip_used, port)
status = pjsip_tcp_transport_start2(self._obj, &local_addr, NULL, 1, &self._tcp_transport)
if status != 0:
raise PJSIPError("Could not create TCP transport", status)
return 0
cdef int _stop_tcp_transport(self) except -1:
self._tcp_transport.destroy(self._tcp_transport)
self._tcp_transport = NULL
return 0
cdef int _start_tls_transport(self, port) except -1:
cdef pj_sockaddr_in local_addr
cdef pjsip_tls_setting tls_setting
self._make_local_addr(&local_addr, self._local_ip_used, port)
pjsip_tls_setting_default(&tls_setting)
# The following value needs to be reasonably low, as TLS negotiation hogs the PJSIP polling loop
tls_setting.timeout.sec = self._tls_timeout / 1000
tls_setting.timeout.msec = self._tls_timeout % 1000
if self._tls_ca_file is not None:
tls_setting.ca_list_file = self._tls_ca_file.pj_str
if self._tls_cert_file is not None:
tls_setting.cert_file = self._tls_cert_file.pj_str
if self._tls_privkey_file is not None:
tls_setting.privkey_file = self._tls_privkey_file.pj_str
tls_setting.method = PJSIP_SSLV23_METHOD
tls_setting.verify_server = self._tls_verify_server
status = pjsip_tls_transport_start(self._obj, &tls_setting, &local_addr, NULL, 1, &self._tls_transport)
if status in (PJSIP_TLS_EUNKNOWN, PJSIP_TLS_EINVMETHOD, PJSIP_TLS_ECACERT, PJSIP_TLS_ECERTFILE, PJSIP_TLS_EKEYFILE, PJSIP_TLS_ECIPHER, PJSIP_TLS_ECTX):
raise PJSIPTLSError("Could not create TLS transport", status)
elif status != 0:
raise PJSIPError("Could not create TLS transport", status)
return 0
cdef int _stop_tls_transport(self) except -1:
self._tls_transport.destroy(self._tls_transport)
self._tls_transport = NULL
return 0
cdef int _set_dns_nameservers(self, list servers) except -1:
cdef int num_servers = len(servers)
cdef pj_str_t *pj_servers
cdef int status
cdef pj_dns_resolver *resolver
if num_servers == 0:
return 0
resolver = pjsip_endpt_get_resolver(self._obj)
if resolver == NULL:
raise SIPCoreError("Could not get DNS resolver on endpoint")
pj_servers = <pj_str_t *> malloc(sizeof(pj_str_t)*num_servers)
if pj_servers == NULL:
raise MemoryError()
for i, ns in enumerate(servers):
_str_to_pj_str(ns.encode(), &pj_servers[i])
status = pj_dns_resolver_set_ns(resolver, num_servers, pj_servers, NULL)
free(pj_servers)
if status != 0:
raise PJSIPError("Could not set nameservers on DNS resolver", status)
return 0
def __dealloc__(self):
cdef pjsip_tpmgr *tpmgr
tpmgr = pjsip_endpt_get_tpmgr(self._obj)
if tpmgr != NULL:
pjsip_tpmgr_set_state_cb(tpmgr, NULL)
if self._udp_transport != NULL:
self._stop_udp_transport()
if self._tcp_transport != NULL:
self._stop_tcp_transport()
if self._tls_transport != NULL:
self._stop_tls_transport()
if self._pool != NULL:
pjsip_endpt_release_pool(self._obj, self._pool)
if self._obj != NULL:
with nogil:
pjsip_endpt_destroy(self._obj)
cdef class PJMEDIAEndpoint:
def __cinit__(self, PJCachingPool caching_pool):
cdef int status
status = pjmedia_endpt_create(&caching_pool._obj.factory, NULL, 1, &self._obj)
if status != 0:
raise PJSIPError("Could not create PJMEDIA endpoint", status)
self._pool = pjmedia_endpt_create_pool(self._obj, "PJMEDIAEndpoint", 4096, 4096)
if self._pool == NULL:
raise SIPCoreError("Could not allocate memory pool")
self._audio_subsystem_init(caching_pool)
self._video_subsystem_init(caching_pool)
def __dealloc__(self):
self._audio_subsystem_shutdown()
self._video_subsystem_shutdown()
if self._pool != NULL:
pj_pool_release(self._pool)
if self._obj != NULL:
with nogil:
pjmedia_endpt_destroy(self._obj)
cdef void _audio_subsystem_init(self, PJCachingPool caching_pool):
cdef int status
cdef pjmedia_audio_codec_config audio_codec_cfg
pjmedia_audio_codec_config_default(&audio_codec_cfg)
audio_codec_cfg.speex.option = PJMEDIA_SPEEX_NO_NB
audio_codec_cfg.ilbc.mode = 30
status = pjmedia_codec_register_audio_codecs(self._obj, &audio_codec_cfg)
if status != 0:
raise PJSIPError("Could not initialize audio codecs", status)
self._has_audio_codecs = 1
cdef void _audio_subsystem_shutdown(self):
pass
cdef void _video_subsystem_init(self, PJCachingPool caching_pool):
cdef int status
status = pjmedia_video_format_mgr_create(self._pool, 64, 0, NULL)
if status != 0:
raise PJSIPError("Could not initialize video format manager", status)
status = pjmedia_converter_mgr_create(self._pool, NULL)
if status != 0:
raise PJSIPError("Could not initialize converter manager", status)
status = pjmedia_event_mgr_create(self._pool, 0, NULL)
if status != 0:
raise PJSIPError("Could not initialize event manager", status)
status = pjmedia_vid_codec_mgr_create(self._pool, NULL)
if status != 0:
raise PJSIPError("Could not initialize video codec manager", status)
status = pjmedia_codec_ffmpeg_vid_init(NULL, &caching_pool._obj.factory)
if status != 0:
raise PJSIPError("Could not initialize ffmpeg video codecs", status)
self._has_ffmpeg_video = 1
status = pjmedia_codec_vpx_init(NULL, &caching_pool._obj.factory)
if status != 0:
raise PJSIPError("Could not initialize vpx video codecs", status)
self._has_vpx = 1
status = pjmedia_vid_dev_subsys_init(&caching_pool._obj.factory)
if status != 0:
raise PJSIPError("Could not initialize video subsystem", status)
self._has_video = 1
cdef void _video_subsystem_shutdown(self):
if self._has_video:
pjmedia_vid_dev_subsys_shutdown()
if self._has_ffmpeg_video:
pjmedia_codec_ffmpeg_vid_deinit()
if self._has_vpx:
pjmedia_codec_vpx_deinit()
if pjmedia_vid_codec_mgr_instance() != NULL:
pjmedia_vid_codec_mgr_destroy(NULL)
if pjmedia_event_mgr_instance() != NULL:
pjmedia_event_mgr_destroy(NULL)
if pjmedia_converter_mgr_instance() != NULL:
pjmedia_converter_mgr_destroy(NULL)
if pjmedia_video_format_mgr_instance() != NULL:
pjmedia_video_format_mgr_destroy(NULL)
cdef list _get_codecs(self):
cdef unsigned int count = PJMEDIA_CODEC_MGR_MAX_CODECS
cdef pjmedia_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS]
cdef unsigned int prio[PJMEDIA_CODEC_MGR_MAX_CODECS]
cdef int i
cdef list retval
cdef int status
status = pjmedia_codec_mgr_enum_codecs(pjmedia_endpt_get_codec_mgr(self._obj), &count, info, prio)
if status != 0:
raise PJSIPError("Could not get available codecs", status)
retval = list()
for i from 0 <= i < count:
retval.append((prio[i], _pj_str_to_bytes(info[i].encoding_name), info[i].channel_cnt, info[i].clock_rate))
return retval
cdef list _get_all_codecs(self):
cdef list codecs
cdef tuple codec_data
codecs = self._get_codecs()
return list(set([codec_data[1] for codec_data in codecs]))
cdef list _get_current_codecs(self):
cdef list codecs
cdef tuple codec_data
cdef list retval
codecs = [codec_data for codec_data in self._get_codecs() if codec_data[0] > 0]
codecs.sort(reverse=True)
retval = list(set([codec_data[1] for codec_data in codecs]))
return retval
cdef int _set_codecs(self, list req_codecs) except -1:
cdef object new_codecs
cdef object all_codecs
cdef object codec_set
cdef list codecs
cdef tuple codec_data
cdef object codec
cdef int sample_rate
cdef int channel_count
cdef object codec_name
cdef int prio
cdef list codec_prio
cdef pj_str_t codec_pj
new_codecs = set(req_codecs)
if len(new_codecs) != len(req_codecs):
raise ValueError("Requested codec list contains doubles")
all_codecs = set(self._get_all_codecs())
codec_set = new_codecs.difference(all_codecs)
if len(codec_set) > 0:
raise SIPCoreError("Unknown codec(s): %s" % ", ".join(codec_set))
# reverse the codec data tuples so that we can easily sort on sample rate
# to make sure that bigger sample rates get higher priority
codecs = [list(reversed(codec_data)) for codec_data in self._get_codecs()]
codecs.sort(reverse=True)
codec_prio = list()
for codec in req_codecs:
for sample_rate, channel_count, codec_name, prio in codecs:
if codec == codec_name and channel_count == 1:
codec_prio.append("%s/%d/%d" % (codec_name.decode(), sample_rate, channel_count))
for prio, codec in enumerate(reversed(codec_prio)):
_str_to_pj_str(codec.encode(), &codec_pj)
status = pjmedia_codec_mgr_set_codec_priority(pjmedia_endpt_get_codec_mgr(self._obj), &codec_pj, prio + 1)
if status != 0:
raise PJSIPError("Could not set codec priority", status)
for sample_rate, channel_count, codec_name, prio in codecs:
if codec_name not in req_codecs or channel_count > 1:
codec = "%s/%d/%d" % (codec_name.decode(), sample_rate, channel_count)
_str_to_pj_str(codec.encode(), &codec_pj)
status = pjmedia_codec_mgr_set_codec_priority(pjmedia_endpt_get_codec_mgr(self._obj), &codec_pj, 0)
if status != 0:
raise PJSIPError("Could not set codec priority", status)
return 0
cdef list _get_video_codecs(self):
cdef unsigned int count = PJMEDIA_VID_CODEC_MGR_MAX_CODECS
cdef pjmedia_vid_codec_info info[PJMEDIA_VID_CODEC_MGR_MAX_CODECS]
cdef unsigned int prio[PJMEDIA_VID_CODEC_MGR_MAX_CODECS]
cdef int i
cdef list retval
cdef int status
status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, prio)
if status != 0:
raise PJSIPError("Could not get available video codecs", status)
retval = list()
for i from 0 <= i < count:
if info[i].packings & PJMEDIA_VID_PACKING_PACKETS:
retval.append((prio[i], _pj_str_to_bytes(info[i].encoding_name), info[i].pt))
return retval
cdef list _get_all_video_codecs(self):
cdef list codecs
cdef tuple codec_data
codecs = self._get_video_codecs()
return list(set([codec_data[1] for codec_data in codecs]))
cdef list _get_current_video_codecs(self):
cdef list codecs
cdef tuple codec_data
cdef list retval
codecs = [codec_data for codec_data in self._get_video_codecs() if codec_data[0] > 0]
codecs.sort(reverse=True)
retval = list(set([codec_data[1] for codec_data in codecs]))
return retval
cdef int _set_video_codecs(self, list req_codecs) except -1:
cdef object new_codecs
cdef object codec_set
cdef list codecs
cdef tuple codec_data
cdef object codec
cdef int payload_type
cdef object codec_name
cdef int prio
cdef list codec_prio
cdef pj_str_t codec_pj
new_codecs = set(req_codecs)
if len(new_codecs) != len(req_codecs):
raise ValueError("Requested video codec list contains doubles")
codec_set = new_codecs.difference(set(self._get_all_video_codecs()))
if len(codec_set) > 0:
raise SIPCoreError("Unknown video codec(s): %s" % ", ".join(codec_set))
codecs = self._get_video_codecs()
codec_prio = list()
for codec in req_codecs:
for prio, codec_name, payload_type in codecs:
if codec == codec_name:
codec_prio.append("%s/%d" % (codec_name.decode(), payload_type))
for prio, codec in enumerate(reversed(codec_prio)):
_str_to_pj_str(codec.encode(), &codec_pj)
status = pjmedia_vid_codec_mgr_set_codec_priority(NULL, &codec_pj, prio + 1)
if status != 0:
raise PJSIPError("Could not set video codec priority", status)
for prio, codec_name, payload_type in codecs:
if codec_name not in req_codecs:
codec = "%s/%d" % (codec_name.decode(), payload_type)
_str_to_pj_str(codec.encode(), &codec_pj)
status = pjmedia_vid_codec_mgr_set_codec_priority(NULL, &codec_pj, 0)
if status != 0:
raise PJSIPError("Could not set video codec priority", status)
return 0
cdef void _set_h264_options(self, object profile, int level):
global h264_profiles_map, h264_profile_level_id, h264_packetization_mode
cdef unsigned int count = PJMEDIA_VID_CODEC_MGR_MAX_CODECS
cdef pjmedia_vid_codec_info info[PJMEDIA_VID_CODEC_MGR_MAX_CODECS]
cdef pjmedia_vid_codec_param vparam
cdef unsigned int prio[PJMEDIA_VID_CODEC_MGR_MAX_CODECS]
cdef int i
cdef int status
cdef PJSTR h264_profile_level_id_value
cdef PJSTR h264_packetization_mode_value = PJSTR(b"1") # TODO; make it configurable?
try:
profile_n = h264_profiles_map[profile]
except KeyError:
raise ValueError("invalid profile specified: %s" % profile)
h264_profile_level_id_value = PJSTR(b"%xe0%x" % (profile_n, level)) # use common subset (e0)
status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, prio)
if status != 0:
raise PJSIPError("Could not get available video codecs", status)
for i from 0 <= i < count:
if not (info[i].packings & PJMEDIA_VID_PACKING_PACKETS):
continue
if _pj_str_to_bytes(info[i].encoding_name) != b'H264':
continue
status = pjmedia_vid_codec_mgr_get_default_param(NULL, &info[i], &vparam)
if status != 0:
continue
# 2 format parameters are currently defined for H264: profile-level-id and packetization-mode
vparam.dec_fmtp.param[0].name = h264_profile_level_id.pj_str
vparam.dec_fmtp.param[0].val = h264_profile_level_id_value.pj_str
vparam.dec_fmtp.param[1].name = h264_packetization_mode.pj_str
vparam.dec_fmtp.param[1].val = h264_packetization_mode_value.pj_str
vparam.dec_fmtp.cnt = 2
status = pjmedia_vid_codec_mgr_set_default_param(NULL, &info[i], &vparam)
if status != 0:
raise PJSIPError("Could not set H264 options", status)
cdef void _set_video_options(self, tuple max_resolution, int max_framerate, float max_bitrate):
cdef unsigned int count = PJMEDIA_VID_CODEC_MGR_MAX_CODECS
cdef pjmedia_vid_codec_info info[PJMEDIA_VID_CODEC_MGR_MAX_CODECS]
cdef pjmedia_vid_codec_param vparam
cdef unsigned int prio[PJMEDIA_VID_CODEC_MGR_MAX_CODECS]
cdef int i
cdef int status
max_width, max_height = max_resolution
status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, prio)
if status != 0:
raise PJSIPError("Could not get available video codecs", status)
for i from 0 <= i < count:
if not (info[i].packings & PJMEDIA_VID_PACKING_PACKETS):
continue
status = pjmedia_vid_codec_mgr_get_default_param(NULL, &info[i], &vparam)
if status != 0:
continue
# Max resolution
vparam.enc_fmt.det.vid.size.w = max_width
vparam.enc_fmt.det.vid.size.h = max_height
vparam.dec_fmt.det.vid.size.w = max_width
vparam.dec_fmt.det.vid.size.h = max_height
# Max framerate
vparam.enc_fmt.det.vid.fps.num = max_framerate
vparam.enc_fmt.det.vid.fps.denum = 1
vparam.dec_fmt.det.vid.fps.num = 10
vparam.dec_fmt.det.vid.fps.denum = 1
# Average and max bitrate (set to 0 for 'unlimited')
vparam.enc_fmt.det.vid.avg_bps = int(max_bitrate * 1e6)
vparam.enc_fmt.det.vid.max_bps = int(max_bitrate * 1e6)
vparam.dec_fmt.det.vid.avg_bps = 0
vparam.dec_fmt.det.vid.max_bps = 0
status = pjmedia_vid_codec_mgr_set_default_param(NULL, &info[i], &vparam)
if status != 0:
raise PJSIPError("Could not set video options", status)
cdef void _transport_state_cb(pjsip_transport *tp, pjsip_transport_state state, pjsip_transport_state_info_ptr_const info) with gil:
cdef PJSIPUA ua
cdef str local_address
cdef str remote_address
cdef char buf[PJ_INET6_ADDRSTRLEN]
cdef dict event_dict
try:
ua = _get_ua()
except:
return
if pj_sockaddr_has_addr(&tp.local_addr):
pj_sockaddr_print(&tp.local_addr, buf, 512, 0)
local_address = '%s:%d' % (_buf_to_str(buf), pj_sockaddr_get_port(&tp.local_addr))
else:
local_address = None
+ transport = tp.type_name.decode().lower()
remote_address = '%s:%d' % (_pj_str_to_str(tp.remote_name.host), tp.remote_name.port)
- event_dict = dict(transport=tp.type_name, local_address=local_address, remote_address=remote_address)
+ event_dict = dict(transport=transport, local_address=local_address, remote_address=remote_address)
if state == PJSIP_TP_STATE_CONNECTED:
_add_event("SIPEngineTransportDidConnect", event_dict)
else:
reason = _pj_status_to_str(info.status)
event_dict['reason'] = reason
_add_event("SIPEngineTransportDidDisconnect", event_dict)
# globals
cdef PJSTR h264_profile_level_id = PJSTR(b"profile-level-id")
cdef PJSTR h264_packetization_mode = PJSTR(b"packetization-mode")
cdef dict h264_profiles_map = dict(baseline=66, main=77, high=100)
diff --git a/sipsimple/core/_core.request.pxi b/sipsimple/core/_core.request.pxi
index f6e7597b..2704b827 100644
--- a/sipsimple/core/_core.request.pxi
+++ b/sipsimple/core/_core.request.pxi
@@ -1,505 +1,505 @@
from datetime import datetime, timedelta
cdef class EndpointAddress:
def __init__(self, ip, port):
self.ip = ip
self.port = port
def __repr__(self):
return "%s(%r, %r)" % (self.__class__.__name__, self.ip, self.port)
def __str__(self):
- return "%s:%d" % (self.ip, self.port)
+ return "%s:%d" % (self.ip.decode(), self.port)
cdef class Request:
expire_warning_time = 30
# properties
property method:
def __get__(self):
return self._method.str
property call_id:
def __get__(self):
return self._call_id.str
property content_type:
def __get__(self):
if self._content_type is None:
return None
else:
return "/".join([self._content_type.str, self._content_subtype.str])
property body:
def __get__(self):
if self._body is None:
return None
else:
return self._body.str
property expires_in:
def __get__(self):
cdef object dt
self._get_ua()
if self.state != "EXPIRING" or self._expire_time is None:
return 0
else:
dt = self._expire_time - datetime.now()
return max(0, dt.seconds)
# public methods
def __cinit__(self, *args, **kwargs):
self.state = "INIT"
self.peer_address = None
pj_timer_entry_init(&self._timer, 0, <void *> self, _Request_cb_timer)
self._timer_active = 0
def __init__(self, method, SIPURI request_uri not None, FromHeader from_header not None, ToHeader to_header not None,
RouteHeader route_header not None, Credentials credentials=None, ContactHeader contact_header=None, call_id=None, cseq=None,
object extra_headers=None, content_type=None, body=None):
cdef pjsip_method method_pj
cdef PJSTR from_header_str
cdef PJSTR to_header_str
cdef PJSTR request_uri_str
cdef PJSTR contact_header_str
cdef pj_str_t *contact_header_pj = NULL
cdef pj_str_t *call_id_pj = NULL
cdef object content_type_spl
cdef pjsip_hdr *hdr
cdef pjsip_contact_hdr *contact_hdr
cdef pjsip_cid_hdr *cid_hdr
cdef pjsip_cseq_hdr *cseq_hdr
cdef int status
cdef dict event_dict
cdef PJSIPUA ua = _get_ua()
if self._tsx != NULL or self.state != "INIT":
raise SIPCoreError("Request.__init__() was already called")
if cseq is not None and cseq < 0:
raise ValueError("cseq argument cannot be negative")
if extra_headers is not None:
header_names = set([header.name for header in extra_headers])
if "Route" in header_names:
raise ValueError("Route should be specified with route_header argument, not extra_headers")
if "Content-Type" in header_names:
raise ValueError("Content-Type should be specified with content_type argument, not extra_headers")
else:
header_names = ()
if content_type is not None and body is None:
raise ValueError("Cannot specify a content_type without a body")
if content_type is None and body is not None:
raise ValueError("Cannot specify a body without a content_type")
self._method = PJSTR(method.encode())
pjsip_method_init_np(&method_pj, &self._method.pj_str)
if credentials is not None:
self.credentials = FrozenCredentials.new(credentials)
from_header_str = PJSTR(from_header.body.encode())
self.to_header = FrozenToHeader.new(to_header)
to_header_str = PJSTR(to_header.body.encode())
struri = str(request_uri)
self.request_uri = FrozenSIPURI.new(request_uri)
request_uri_str = PJSTR(struri.encode())
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 contact_header is not None:
self.contact_header = FrozenContactHeader.new(contact_header)
contact_parameters = contact_header.parameters.copy()
contact_parameters.pop("q", None)
contact_parameters.pop("expires", None)
contact_header.parameters = {}
contact_header_str = PJSTR(contact_header.body.encode())
contact_header_pj = &contact_header_str.pj_str
if call_id is not None:
self._call_id = PJSTR(call_id)
call_id_pj = &self._call_id.pj_str
if cseq is None:
self.cseq = -1
else:
self.cseq = cseq
if extra_headers is None:
self.extra_headers = frozenlist()
else:
self.extra_headers = frozenlist([header.frozen_type.new(header) for header in extra_headers])
if body is not None:
content_type_spl = content_type.split("/", 1)
self._content_type = PJSTR(content_type_spl[0].encode())
self._content_subtype = PJSTR(content_type_spl[1].encode())
self._body = PJSTR(body)
status = pjsip_endpt_create_request(ua._pjsip_endpoint._obj, &method_pj, &request_uri_str.pj_str,
&from_header_str.pj_str, &to_header_str.pj_str, contact_header_pj,
call_id_pj, self.cseq, NULL, &self._tdata)
if status != 0:
raise PJSIPError("Could not create request", status)
if body is not None:
self._tdata.msg.body = pjsip_msg_body_create(self._tdata.pool, &self._content_type.pj_str,
&self._content_subtype.pj_str, &self._body.pj_str)
status = _BaseRouteHeader_to_pjsip_route_hdr(self.route_header, &self._route_header, self._tdata.pool)
pjsip_msg_add_hdr(self._tdata.msg, <pjsip_hdr *> &self._route_header)
hdr = <pjsip_hdr *> (<pj_list *> &self._tdata.msg.hdr).next
while hdr != &self._tdata.msg.hdr:
hdr_name = _pj_str_to_str(hdr.name)
if hdr_name in header_names:
raise ValueError("Cannot override %s header value in extra_headers" % _pj_str_to_bytes(hdr.name))
if hdr.type == PJSIP_H_CONTACT:
contact_hdr = <pjsip_contact_hdr *> hdr
_dict_to_pjsip_param(contact_parameters, &contact_hdr.other_param, self._tdata.pool)
elif hdr.type == PJSIP_H_CALL_ID:
cid_hdr = <pjsip_cid_hdr *> hdr
self._call_id = PJSTR(_pj_str_to_bytes(cid_hdr.id))
elif hdr.type == PJSIP_H_CSEQ:
cseq_hdr = <pjsip_cseq_hdr *> hdr
self.cseq = cseq_hdr.cseq
elif hdr.type == PJSIP_H_FROM:
self.from_header = FrozenFromHeader_create(<pjsip_fromto_hdr*> hdr)
else:
pass
hdr = <pjsip_hdr *> (<pj_list *> hdr).next
_add_headers_to_tdata(self._tdata, self.extra_headers)
#event_dict = dict(obj=self)
#_pjsip_msg_to_dict(self._tdata.msg, event_dict)
#print('Request dict %s' % event_dict)
if self.credentials is not None:
status = pjsip_auth_clt_init(&self._auth, ua._pjsip_endpoint._obj, self._tdata.pool, 0)
if status != 0:
raise PJSIPError("Could not init authentication credentials", status)
status = pjsip_auth_clt_set_credentials(&self._auth, 1, self.credentials.get_cred_info())
if status != 0:
raise PJSIPError("Could not set authentication credentials", status)
self._need_auth = 1
else:
self._need_auth = 0
status = pjsip_tsx_create_uac(&ua._module, self._tdata, &self._tsx)
if status != 0:
raise PJSIPError("Could not create transaction for request", status)
self._tsx.mod_data[ua._module.id] = <void *> self
def __dealloc__(self):
cdef PJSIPUA ua = self._get_ua()
if self._tsx != NULL:
self._tsx.mod_data[ua._module.id] = NULL
if self._tsx.state < PJSIP_TSX_STATE_COMPLETED:
pjsip_tsx_terminate(self._tsx, 500)
self._tsx = NULL
if self._tdata != NULL:
pjsip_tx_data_dec_ref(self._tdata)
self._tdata = NULL
if self._timer_active:
pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timer)
self._timer_active = 0
def send(self, timeout=None):
cdef pj_time_val timeout_pj
cdef int status
cdef PJSIPUA ua = self._get_ua()
if self.state != "INIT":
raise SIPCoreError('This method may only be called in the "INIT" state, current state is "%s"' % self.state)
if timeout is not None:
if timeout <= 0:
raise ValueError("Timeout value cannot be negative")
timeout_pj.sec = int(timeout)
timeout_pj.msec = (timeout * 1000) % 1000
self._timeout = timeout
status = pjsip_tsx_send_msg(self._tsx, self._tdata)
if status != 0:
raise PJSIPError("Could not send request", status)
pjsip_tx_data_add_ref(self._tdata)
if timeout:
status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timer, &timeout_pj)
if status == 0:
self._timer_active = 1
self.state = "IN_PROGRESS"
def end(self):
cdef PJSIPUA ua = self._get_ua()
if self.state == "IN_PROGRESS":
pjsip_tsx_terminate(self._tsx, 408)
elif self.state == "EXPIRING":
pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timer)
self._timer_active = 0
self.state = "TERMINATED"
_add_event("SIPRequestDidEnd", dict(obj=self))
# private methods
cdef PJSIPUA _get_ua(self):
cdef PJSIPUA ua
try:
ua = _get_ua()
except SIPCoreError:
self._tsx = NULL
self._tdata = NULL
self._timer_active = 0
self.state = "TERMINATED"
return None
else:
return ua
cdef int _cb_tsx_state(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1:
cdef pjsip_tx_data *tdata_auth
cdef pjsip_transaction *tsx_auth
cdef pjsip_cseq_hdr *cseq
cdef dict event_dict
cdef int expires = -1
cdef SIPURI contact_uri
cdef dict contact_params
cdef pj_time_val timeout_pj
cdef int status
if rdata != NULL:
self.to_header = FrozenToHeader_create(rdata.msg_info.to_hdr)
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
if self._tsx.state == PJSIP_TSX_STATE_PROCEEDING:
if rdata == NULL:
return 0
event_dict = dict(obj=self)
_pjsip_msg_to_dict(rdata.msg_info.msg, event_dict)
_add_event("SIPRequestGotProvisionalResponse", event_dict)
elif self._tsx.state == PJSIP_TSX_STATE_COMPLETED:
if self._timer_active:
pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timer)
self._timer_active = 0
if self._need_auth and self._tsx.status_code in [401, 407]:
self._need_auth = 0
status = pjsip_auth_clt_reinit_req(&self._auth, rdata, self._tdata, &tdata_auth)
if status != 0:
_add_event("SIPRequestDidFail",
dict(obj=self, code=0,
reason="Could not add auth data to request %s" % _pj_status_to_str(status)))
self.state = "TERMINATED"
_add_event("SIPRequestDidEnd", dict(obj=self))
return 0
cseq = <pjsip_cseq_hdr *> pjsip_msg_find_hdr(tdata_auth.msg, PJSIP_H_CSEQ, NULL)
if cseq != NULL:
cseq.cseq += 1
self.cseq = cseq.cseq
status = pjsip_tsx_create_uac(&ua._module, tdata_auth, &tsx_auth)
if status != 0:
pjsip_tx_data_dec_ref(tdata_auth)
_add_event("SIPRequestDidFail",
dict(obj=self, code=0,
reason="Could not create transaction for request with auth %s" %
_pj_status_to_str(status)))
self.state = "TERMINATED"
_add_event("SIPRequestDidEnd", dict(obj=self))
return 0
self._tsx.mod_data[ua._module.id] = NULL
self._tsx = tsx_auth
self._tsx.mod_data[ua._module.id] = <void *> self
status = pjsip_tsx_send_msg(self._tsx, tdata_auth)
if status != 0:
pjsip_tx_data_dec_ref(tdata_auth)
_add_event("SIPRequestDidFail",
dict(obj=self, code=0,
reason="Could not send request with auth %s" % _pj_status_to_str(status)))
self.state = "TERMINATED"
_add_event("SIPRequestDidEnd", dict(obj=self))
return 0
elif self._timeout is not None:
timeout_pj.sec = int(self._timeout)
timeout_pj.msec = (self._timeout * 1000) % 1000
status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timer, &timeout_pj)
if status == 0:
self._timer_active = 1
else:
event_dict = dict(obj=self)
if rdata != NULL:
# This shouldn't happen, but safety fist!
_pjsip_msg_to_dict(rdata.msg_info.msg, event_dict)
if self._tsx.status_code / 100 == 2:
if rdata != NULL:
if "Expires" in event_dict["headers"]:
expires = event_dict["headers"]["Expires"]
elif self.contact_header is not None:
for contact_header in event_dict["headers"].get("Contact", []):
if contact_header.uri == self.contact_header.uri and contact_header.expires is not None:
expires = contact_header.expires
if expires == -1:
expires = 0
for header in self.extra_headers:
if header.name == "Expires":
try:
expires = int(header.body)
except ValueError:
pass
break
event_dict["expires"] = expires
self._expire_time = datetime.now() + timedelta(seconds=expires)
_add_event("SIPRequestDidSucceed", event_dict)
else:
expires = 0
_add_event("SIPRequestDidFail", event_dict)
if expires == 0:
self.state = "TERMINATED"
_add_event("SIPRequestDidEnd", dict(obj=self))
else:
timeout_pj.sec = max(1, expires - self.expire_warning_time, expires/2)
timeout_pj.msec = 0
status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timer, &timeout_pj)
if status == 0:
self._timer_active = 1
self.state = "EXPIRING"
self._expire_rest = max(1, expires - timeout_pj.sec)
else:
self.state = "TERMINATED"
_add_event("SIPRequestDidEnd", dict(obj=self))
elif self._tsx.state == PJSIP_TSX_STATE_TERMINATED:
if self.state == "IN_PROGRESS":
if self._timer_active:
pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timer)
self._timer_active = 0
_add_event("SIPRequestDidFail", dict(obj=self, code=self._tsx.status_code,
reason=_pj_str_to_bytes(self._tsx.status_text)))
self.state = "TERMINATED"
_add_event("SIPRequestDidEnd", dict(obj=self))
self._tsx.mod_data[ua._module.id] = NULL
self._tsx = NULL
else:
pass
cdef int _cb_timer(self, PJSIPUA ua) except -1:
cdef pj_time_val expires
cdef int status
if self.state == "IN_PROGRESS":
pjsip_tsx_terminate(self._tsx, 408)
elif self.state == "EXPIRING":
if self._expire_rest > 0:
_add_event("SIPRequestWillExpire", dict(obj=self, expires=self._expire_rest))
expires.sec = self._expire_rest
expires.msec = 0
self._expire_rest = 0
status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timer, &expires)
if status == 0:
self._timer_active = 1
else:
self.state = "TERMINATED"
_add_event("SIPRequestDidEnd", dict(obj=self))
else:
self.state = "TERMINATED"
_add_event("SIPRequestDidEnd", dict(obj=self))
return 0
cdef class IncomingRequest:
def __cinit__(self, *args, **kwargs):
self.peer_address = None
def __dealloc__(self):
cdef PJSIPUA ua
try:
ua = _get_ua()
except SIPCoreError:
return
if self._tsx != NULL:
pjsip_tsx_terminate(self._tsx, 500)
self._tsx = NULL
if self._tdata != NULL:
pjsip_tx_data_dec_ref(self._tdata)
self._tdata = NULL
def answer(self, int code, str reason=None, object extra_headers=None):
cdef bytes reason_bytes
cdef dict event_dict
cdef int status
cdef PJSIPUA ua = _get_ua()
if self.state != "incoming":
raise SIPCoreInvalidStateError('Can only answer an incoming request in the "incoming" state, '
'object is currently in the "%s" state' % self.state)
if code < 200 or code >= 700:
raise ValueError("Invalid SIP final response code: %d" % code)
self._tdata.msg.line.status.code = code
if reason is None:
self._tdata.msg.line.status.reason = pjsip_get_status_text(code)[0]
else:
pj_strdup2_with_null(self._tdata.pool, &self._tdata.msg.line.status.reason, reason.encode())
if extra_headers is not None:
_add_headers_to_tdata(self._tdata, extra_headers)
event_dict = dict(obj=self)
_pjsip_msg_to_dict(self._tdata.msg, event_dict)
status = pjsip_tsx_send_msg(self._tsx, self._tdata)
if status != 0:
raise PJSIPError("Could not send response", status)
self.state = "answered"
self._tdata = NULL
self._tsx = NULL
_add_event("SIPIncomingRequestSentResponse", event_dict)
cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1:
cdef dict event_dict
cdef int status
status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 500, NULL, &self._tdata)
if status != 0:
raise PJSIPError("Could not create response", status)
status = pjsip_tsx_create_uas(&ua._module, rdata, &self._tsx)
if status != 0:
pjsip_tx_data_dec_ref(self._tdata)
self._tdata = NULL
raise PJSIPError("Could not create transaction for incoming request", status)
pjsip_tsx_recv_msg(self._tsx, rdata)
self.state = "incoming"
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)
_add_event("SIPIncomingRequestGotRequest", event_dict)
# callback functions
cdef void _Request_cb_tsx_state(pjsip_transaction *tsx, pjsip_event *event) with gil:
cdef PJSIPUA ua
cdef void *req_ptr
cdef Request req
cdef pjsip_rx_data *rdata = NULL
try:
ua = _get_ua()
except:
return
try:
req_ptr = tsx.mod_data[ua._module.id]
if req_ptr != NULL:
req = <object> req_ptr
if event.type == PJSIP_EVENT_RX_MSG:
rdata = event.body.rx_msg.rdata
elif event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.type == PJSIP_EVENT_RX_MSG:
rdata = event.body.tsx_state.src.rdata
req._cb_tsx_state(ua, rdata)
except:
ua._handle_exception(1)
cdef void _Request_cb_timer(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) with gil:
cdef PJSIPUA ua
cdef Request req
try:
ua = _get_ua()
except:
return
try:
if entry.user_data != NULL:
req = <object> entry.user_data
req._timer_active = 0
req._cb_timer(ua)
except:
ua._handle_exception(1)

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 5:50 AM (1 d, 3 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3408885
Default Alt Text
(46 KB)

Event Timeline