diff --git a/sipsimple/core/_core.referral.pxi b/sipsimple/core/_core.referral.pxi index 80c89437..9b1a4a62 100644 --- a/sipsimple/core/_core.referral.pxi +++ b/sipsimple/core/_core.referral.pxi @@ -1,1003 +1,1003 @@ import re cdef class Referral: expire_warning_time = 30 def __cinit__(self, *args, **kwargs): self.state = "NULL" pj_timer_entry_init(&self._timeout_timer, 0, self, _Referral_cb_timer) self._timeout_timer_active = 0 pj_timer_entry_init(&self._refresh_timer, 1, self, _Referral_cb_timer) self._refresh_timer_active = 0 self.extra_headers = frozenlist() self.peer_address = None self._create_subscription = 1 self.local_contact_header = None self.remote_contact_header = None def __init__(self, SIPURI request_uri not None, FromHeader from_header not None, ToHeader to_header not None, ReferToHeader refer_to_header not None, ContactHeader contact_header not None, RouteHeader route_header not None, Credentials credentials=None): global _refer_cb global _refer_event cdef PJSTR from_header_str cdef PJSTR to_header_str cdef PJSTR contact_str cdef PJSTR request_uri_str cdef pjsip_cred_info *cred_info cdef PJSIPUA ua = _get_ua() cdef int status if self._obj != NULL or self.state != "NULL": raise SIPCoreError("Referral.__init__() was already called") self.local_contact_header = FrozenContactHeader.new(contact_header) 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) from_header_parameters = from_header.parameters.copy() from_header_parameters.pop("tag", None) from_header.parameters = {} from_header_str = PJSTR(from_header.body.encode()) to_header_parameters = to_header.parameters.copy() to_header_parameters.pop("tag", None) to_header.parameters = {} to_header_str = PJSTR(to_header.body.encode()) contact_str = PJSTR(str(contact_header.body).encode()) request_uri_str = PJSTR(str(request_uri).encode()) 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 REFER", 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 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) self.refer_to_header = FrozenReferToHeader.new(refer_to_header) with nogil: status = pjsip_evsub_create_uac(self._dlg, &_refer_cb, &_refer_event.pj_str, PJSIP_EVSUB_NO_EVENT_ID, &self._obj) if status != 0: raise PJSIPError("Could not create REFER", 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 REFER", 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 REFER", 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: with nogil: pjsip_dlg_dec_session(self._dlg, &ua._module) self._dlg = NULL def send_refer(self, int create_subscription=1, list extra_headers not None=list(), object timeout=None): cdef PJSIPUA ua = self._get_ua() with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "NULL": raise SIPCoreError('This method may only be called in the "NULL" state') if timeout is not None: if timeout <= 0: raise ValueError("Timeout value cannot be negative") self._request_timeout.sec = int(timeout) self._request_timeout.msec = (timeout * 1000) % 1000 else: self._request_timeout.sec = 0 self._request_timeout.msec = 0 if extra_headers is not None: self.extra_headers = frozenlist([header.frozen_type.new(header) for header in extra_headers]) self._create_subscription = create_subscription self._send_refer(ua, &self._request_timeout, self.refer_to_header, self.extra_headers) _add_event("SIPReferralWillStart", dict(obj=self)) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def refresh(self, ContactHeader contact_header=None, list extra_headers not None=list(), object timeout=None): cdef PJSIPUA ua = self._get_ua() with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state not in ("ACCEPTED", "ACTIVE", "PENDING"): raise SIPCoreError('This method may only be called in the "ACCEPTED", "ACTIVE" or "PENDING" states') if timeout is not None: if timeout <= 0: raise ValueError("Timeout value cannot be negative") self._request_timeout.sec = int(timeout) self._request_timeout.msec = (timeout * 1000) % 1000 else: self._request_timeout.sec = 0 self._request_timeout.msec = 0 if contact_header is not None: self._update_contact_header(contact_header) if extra_headers is not None: self.extra_headers = frozenlist([header.frozen_type.new(header) for header in extra_headers]) self._send_subscribe(ua, 600, &self._request_timeout, self.extra_headers) 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("SIPReferralWillEnd", dict(obj=self)) try: self._send_subscribe(ua, 0, &end_timeout, frozenlist([])) except PJSIPError, e: self._term_reason = e.args[0] if self._obj != NULL: pjsip_evsub_terminate(self._obj, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) 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 _update_contact_header(self, BaseContactHeader contact_header) except -1: # The PJSIP functions called here don't do much, so there is no need to call them # without the gil. cdef pj_str_t contact_str_pj cdef pjsip_uri *contact contact_str = str(contact_header.uri) if contact_header.display_name: contact_str = "%s <%s>" % (contact_header.display_name, contact_str) pj_strdup2_with_null(self._dlg.pool, &contact_str_pj, contact_str.encode()) contact = pjsip_parse_uri(self._dlg.pool, contact_str_pj.ptr, contact_str_pj.slen, PJSIP_PARSE_URI_AS_NAMEADDR) if contact == NULL: raise SIPCoreError("Not a valid Contact header: %s" % contact_str) self._dlg.local.contact = pjsip_contact_hdr_create(self._dlg.pool) self._dlg.local.contact.uri = contact 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) parameters = contact_header.parameters.copy() parameters.pop("q", None) parameters.pop("expires", None) _dict_to_pjsip_param(parameters, &self._dlg.local.contact.other_param, self._dlg.pool) self.local_contact_header = FrozenContactHeader.new(contact_header) return 0 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_refer(self, PJSIPUA ua, pj_time_val *timeout, FrozenReferToHeader refer_to_header, frozenlist extra_headers) except -1: global _refer_method cdef pjsip_method refer_method cdef pjsip_tx_data *tdata cdef int status pjsip_method_init_np(&refer_method, &_refer_method.pj_str) with nogil: status = pjsip_evsub_initiate(self._obj, &refer_method, -1, &tdata) if status != 0: raise PJSIPError("Could not create REFER message", status) _add_headers_to_tdata(tdata, [refer_to_header, Header('Referred-By', str(self.from_header.uri))]) _add_headers_to_tdata(tdata, extra_headers) if not self._create_subscription: _add_headers_to_tdata(tdata, [Header('Refer-Sub', 'false')]) # We can't remove the Event header or PJSIP will fail to match responses to this request _remove_headers_from_tdata(tdata, [b"Expires"]) with nogil: status = pjsip_evsub_send_request(self._obj, tdata) if status != 0: raise PJSIPError("Could not send REFER message", status) 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 cdef int _send_subscribe(self, PJSIPUA ua, int expires, pj_time_val *timeout, frozenlist extra_headers) except -1: cdef pjsip_tx_data *tdata cdef int status 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) 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 cdef int _cb_state(self, PJSIPUA ua, object state, int code, str reason) except -1: # PJSIP holds the dialog lock when this callback is entered cdef object prev_state = self.state cdef int status self.state = state if state == "ACCEPTED" and prev_state == "SENT": _add_event("SIPReferralDidStart", dict(obj=self)) if not self._create_subscription: # Terminate the subscription self._want_end = 1 _add_event("SIPReferralWillEnd", dict(obj=self)) with nogil: pjsip_evsub_terminate(self._obj, 1) 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("SIPReferralDidEnd", dict(obj=self)) else: if self._term_reason is not None: _add_event("SIPReferralDidFail", dict(obj=self, code=self._term_code, reason=self._term_reason)) elif code/100 == 2: _add_event("SIPReferralDidEnd", dict(obj=self)) else: _add_event("SIPReferralDidFail", dict(obj=self, code=code, reason=reason)) if prev_state != state: _add_event("SIPReferralChangedState", dict(obj=self, prev_state=prev_state, state=state)) cdef int _cb_got_response(self, PJSIPUA ua, pjsip_rx_data *rdata, str method) except -1: # PJSIP holds the dialog lock when this callback is entered global _refer_sub_hdr_name cdef int expires cdef int status cdef dict event_dict = dict() cdef pj_time_val refresh cdef pjsip_generic_int_hdr *expires_hdr cdef pjsip_generic_string_hdr *refer_sub_header self.to_header = FrozenToHeader_create(rdata.msg_info.to_hdr) if self.state != "TERMINATED" and not self._want_end: self._cancel_timers(ua, 1, 0) if method == "REFER": refer_sub_header = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_refer_sub_hdr_name.pj_str, NULL); if not self._create_subscription: if not (refer_sub_header != NULL and _pj_str_to_str(refer_sub_header.hvalue) == "false"): self._create_subscription = 1 elif method == "SUBSCRIBE": # For the REFER method the expires value will be taken from the NOTIFY Subscription-State header expires_hdr = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_EXPIRES, NULL) if expires_hdr != NULL and not self._refresh_timer_active: expires = expires_hdr.ivalue 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 if self.state != "TERMINATED": _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: pass cdef int _cb_notify(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: # PJSIP holds the dialog lock when this callback is entered global _subscription_state_hdr_name cdef pjsip_sub_state_hdr *sub_state_hdr cdef pj_time_val refresh cdef int expires cdef dict event_dict = dict() cdef dict notify_dict = dict(obj=self) sub_state_hdr = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_subscription_state_hdr_name.pj_str, NULL) if self.state != "TERMINATED" and sub_state_hdr != NULL and sub_state_hdr.expires_param > 0 and not self._refresh_timer_active: expires = sub_state_hdr.expires_param 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 _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) if self.state != "TERMINATED": try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: pass 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"] = event_dict["body"] content_type = notify_dict["headers"].get("Content-Type", None) notify_dict["content_type"] = content_type.content_type if content_type else None event = notify_dict["headers"].get("Event", None) notify_dict["event"] = event.event if event else None _add_event("SIPReferralGotNotify", 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, 600, &self._request_timeout, self.extra_headers) 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 IncomingReferral: def __cinit__(self): self.state = None self.peer_address = None self._create_subscription = 1 self.local_contact_header = None self.remote_contact_header = 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) except -1: global _incoming_refer_subs_cb global _event_hdr_name global _refer_event global _refer_to_hdr_name global _refer_sub_hdr_name cdef int status cdef str transport cdef FrozenSIPURI request_uri cdef FrozenContactHeader contact_header cdef PJSTR contact_str cdef dict event_dict cdef pjsip_generic_string_hdr *refer_to_header cdef pjsip_generic_string_hdr *refer_sub_header cdef pjsip_tpselector tp_sel cdef pjsip_event_hdr *event_header refer_to_header = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_refer_to_hdr_name.pj_str, NULL); if refer_to_header == NULL: with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 400, NULL, &self._initial_response) if status != 0: raise PJSIPError("Could not create response", status) with nogil: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, self._initial_response, NULL, NULL) if status != 0: with nogil: pjsip_tx_data_dec_ref(self._initial_response) raise PJSIPError("Could not send response", status) return 0 # If there is a Ref-Sub header and it contains 'false', don't establish a subscription refer_sub_header = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_refer_sub_hdr_name.pj_str, NULL); if refer_sub_header != NULL and _pj_str_to_str(refer_sub_header.hvalue) == "false": self._create_subscription = 0 self._set_state("incoming") self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) event_dict = dict(obj=self, prev_state=self.state, state="incoming") _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: # Contact header is required with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 400, NULL, &self._initial_response) if status != 0: raise PJSIPError("Could not create response", status) with nogil: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, self._initial_response, NULL, NULL) if status != 0: with nogil: pjsip_tx_data_dec_ref(self._initial_response) raise PJSIPError("Could not send response", status) return 0 event_dict["refer_to"] = event_dict["headers"].get("Refer-To") - transport = rdata.tp_info.transport.type_name.lower() + transport = rdata.tp_info.transport.type_name.decode().lower() request_uri = event_dict["request_uri"] if _is_valid_ip(pj_AF_INET(), request_uri.host): self.local_contact_header = FrozenContactHeader(request_uri) else: self.local_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(self.local_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: with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 400, NULL, &self._initial_response) if status != 0: raise PJSIPError("Could not create response", status) with nogil: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, self._initial_response, NULL, NULL) if status != 0: with nogil: pjsip_tx_data_dec_ref(self._initial_response) raise PJSIPError("Could not send response", status) return 0 # 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: pjsip_dlg_dec_lock(self._dlg) raise PJSIPError("Could not increment dialog session count", status) # PJSIP event framework needs an Event header, even if it's not needed for REFER, so we insert a fake one event_header = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_event_hdr_name.pj_str, NULL) if event_header == NULL: event_header = pjsip_event_hdr_create(rdata.tp_info.pool) event_header.event_type = _refer_event.pj_str pjsip_msg_add_hdr(rdata.msg_info.msg, event_header) self._initial_tsx = pjsip_rdata_get_tsx(rdata) with nogil: status = pjsip_evsub_create_uas(self._dlg, &_incoming_refer_subs_cb, rdata, 0, &self._obj) pjsip_dlg_dec_lock(self._dlg) if status != 0: with nogil: pjsip_tsx_terminate(self._initial_tsx, 500) self._initial_tsx = NULL self._dlg = NULL raise PJSIPError("Could not create incoming REFER session", status) pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, self) with nogil: status = pjsip_dlg_create_response(self._dlg, rdata, 500, NULL, &self._initial_response) if status != 0: with nogil: pjsip_tsx_terminate(self._initial_tsx, 500) self._initial_tsx = NULL raise PJSIPError("Could not create response for incoming REFER", status) _add_event("SIPIncomingReferralGotRefer", event_dict) return 0 def accept(self, int code=202, int duration=180): 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 REFER in the "incoming" state, '+ 'object is currently in the "%s" state' % self.state) pjsip_evsub_update_expires(self._obj, duration) self._send_initial_response(code) self._set_state("active") if not self._create_subscription: 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("SIPIncomingReferralDidEnd", dict(obj=self)) else: self._set_content(100, "Trying") self._send_notify() finally: with nogil: pjsip_dlg_dec_lock(self._dlg) 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 REFER 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("SIPIncomingReferralDidEnd", dict(obj=self)) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def send_notify(self, int code, str status=None): cdef PJSIPUA ua = self._get_ua(1) cdef str content with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "active": raise SIPCoreInvalidStateError('Can only send NOTIFY for a REFER session in the "active" state, ' 'object is currently in the "%s" state' % self.state) self._set_content(code, status) self._send_notify() finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def end(self, int code, str status=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 REFER session in the "pending" or '+ '"active" state, object is currently in the "%s" state' % self.state) self._set_content(code, status) self._terminate(ua, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef PJSIPUA _get_ua(self, int raise_exception): cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: self._obj = NULL self._set_state("terminated") if raise_exception: raise else: return None else: return ua cdef int _set_content(self, int code, str reason) except -1: cdef str content if reason is None: try: reason = sip_status_messages[code] except IndexError: reason = "Unknown" content = "SIP/2.0 %d %s\r\n" % (code, reason) self._content = PJSTR(content) 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("SIPIncomingReferralChangedState", dict(obj=self, prev_state=prev_state, state=state)) cdef int _send_initial_response(self, int code) except -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 not self._create_subscription: _add_headers_to_tdata(self._initial_response, [Header('Refer-Sub', 'false')]) 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 cdef int _send_notify(self) except -1: cdef pjsip_evsub_state state cdef pj_str_t *reason_p cdef pjsip_tx_data *tdata cdef int status cdef dict _sipfrag_version = dict(version="2.0") cdef PJSTR _content_type = PJSTR(b"message") cdef PJSTR _content_subtype = PJSTR(b"sipfrag") cdef PJSTR reason = PJSTR(b"noresource") 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 reason_p = &reason.pj_str 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 in ("active", "terminated"): tdata.msg.body = pjsip_msg_body_create(tdata.pool, &_content_type.pj_str, &_content_subtype.pj_str, &self._content.pj_str) _dict_to_pjsip_param(_sipfrag_version, &tdata.msg.body.content_type.param, tdata.pool) 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("SIPIncomingReferralSentNotify", event_dict) return 0 cdef int _terminate(self, PJSIPUA ua, int do_cleanup) except -1: cdef int status self._set_state("terminated") self._send_notify() if do_cleanup: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) self._obj = NULL _add_event("SIPIncomingReferralDidEnd", dict(obj=self)) 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_time.sec = 600 self._expires_time.msec = 0 else: if expires_header.ivalue == 0: _add_event("SIPIncomingReferralGotUnsubscribe", event_dict) # cleanup will be done by _cb_tsx self._terminate(ua, 0) return 200 else: expires = min(expires_header.ivalue, 600) self._expires_time.sec = expires self._expires_time.msec = 0 _add_event("SIPIncomingReferralGotRefreshingSubscribe", event_dict) # Last NOTIFY will be resent self._send_notify() 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 self._terminate(ua, 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) try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: pass _add_event("SIPIncomingReferralNotifyDidSucceed", 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("SIPIncomingReferralNotifyDidFail", 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, 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("SIPIncomingReferralNotifyDidFail", event_dict) self._terminate(ua, 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) == "REFER" 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("SIPIncomingReferralAnsweredRefer", 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 cdef void _Referral_cb_state(pjsip_evsub *sub, pjsip_event *event) with gil: cdef void *referral_void cdef Referral referral cdef object state cdef int code = 0 cdef dict event_dict = dict() cdef str reason = None cdef pjsip_rx_data *rdata = NULL cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_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: reason = "Referral has expired" if event.body.tsx_state.type == PJSIP_EVENT_RX_MSG and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY": # Extract code and reason from the sipfrag payload rdata = event.body.tsx_state.src.rdata if rdata != NULL: _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) if event_dict.get('body', None) is not None: match = sipfrag_re.match(event_dict['body']) if match: code = int(match.group('code')) reason = match.group('reason') referral._cb_state(ua, state, code, reason) except: ua._handle_exception(1) cdef void _Referral_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil: cdef void *referral_void cdef Referral referral cdef pjsip_rx_data *rdata cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_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) in ("REFER", "SUBSCRIBE") and event.body.tsx_state.tsx.status_code/100 == 2): rdata = event.body.tsx_state.src.rdata if rdata != NULL: if referral.peer_address is None: referral.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: referral.peer_address.ip = rdata.pkt_info.src_name referral.peer_address.port = rdata.pkt_info.src_port referral._cb_got_response(ua, rdata, _pj_str_to_str(event.body.tsx_state.tsx.method.name)) except: ua._handle_exception(1) cdef void _Referral_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 *referral_void cdef Referral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_void if rdata != NULL: if referral.peer_address is None: referral.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: referral.peer_address.ip = rdata.pkt_info.src_name referral.peer_address.port = rdata.pkt_info.src_port referral._cb_notify(ua, rdata) except: ua._handle_exception(1) cdef void _Referral_cb_refresh(pjsip_evsub *sub) with gil: # We want to handle the refresh timer oursevles, ignore the PJSIP provided timer pass cdef void _Referral_cb_timer(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) with gil: cdef Referral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: if entry.user_data != NULL: referral = entry.user_data if entry.id == 1: referral._refresh_timer_active = 0 referral._cb_refresh_timer(ua) else: referral._timeout_timer_active = 0 referral._cb_timeout_timer(ua) except: ua._handle_exception(1) cdef void _IncomingReferral_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 *referral_void cdef IncomingReferral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: p_st_code[0] = 481 return referral = referral_void if rdata != NULL: if referral.peer_address is None: referral.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: referral.peer_address.ip = rdata.pkt_info.src_name referral.peer_address.port = rdata.pkt_info.src_port p_st_code[0] = referral._cb_rx_refresh(ua, rdata) except: ua._handle_exception(1) cdef void _IncomingReferral_cb_server_timeout(pjsip_evsub *sub) with gil: cdef void *referral_void cdef IncomingReferral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_void referral._cb_server_timeout(ua) except: ua._handle_exception(1) cdef void _IncomingReferral_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil: cdef void *referral_void cdef IncomingReferral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_void referral._cb_tsx(ua, event) except: ua._handle_exception(1) # Globals # cdef pjsip_evsub_user _refer_cb _refer_cb.on_evsub_state = _Referral_cb_state _refer_cb.on_tsx_state = _Referral_cb_tsx _refer_cb.on_rx_notify = _Referral_cb_notify _refer_cb.on_client_refresh = _Referral_cb_refresh cdef pjsip_evsub_user _incoming_refer_subs_cb _incoming_refer_subs_cb.on_rx_refresh = _IncomingReferral_cb_rx_refresh _incoming_refer_subs_cb.on_server_timeout = _IncomingReferral_cb_server_timeout _incoming_refer_subs_cb.on_tsx_state = _IncomingReferral_cb_tsx sipfrag_re = re.compile(r'^SIP/2\.0\s+(?P\d{3})\s+(?P[ a-zA-Z0-9_-]+)') cdef PJSTR _refer_method = PJSTR(b"REFER") cdef PJSTR _refer_event = PJSTR(b"refer") cdef PJSTR _refer_to_hdr_name = PJSTR(b"Refer-To") cdef PJSTR _refer_sub_hdr_name = PJSTR(b"Refer-Sub") cdef PJSTR _subscription_state_hdr_name = PJSTR(b"Subscription-State") diff --git a/sipsimple/core/_core.util.pxi b/sipsimple/core/_core.util.pxi index 0007723d..5712e1f7 100644 --- a/sipsimple/core/_core.util.pxi +++ b/sipsimple/core/_core.util.pxi @@ -1,439 +1,439 @@ 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 list(self.dict.items()) def iteritems(self): return list(self.dict.items()) def iterkeys(self): return list(self.dict.keys()) def itervalues(self): return list(self.dict.values()) def keys(self): return list(self.dict.keys()) def values(self): return list(self.dict.values()) # functions cdef int _str_to_pj_str(object string, pj_str_t *pj_str) except -1: if type(string) != bytes: pj_str.ptr = PyBytes_AsString(string.encode()) else: pj_str.ptr = PyBytes_AsString(string) pj_str.slen = len(string) cdef object _pj_str_to_bytes(pj_str_t pj_str): return PyBytes_FromStringAndSize(pj_str.ptr, pj_str.slen) cdef object _pj_str_to_str(pj_str_t pj_str): return PyBytes_FromStringAndSize(pj_str.ptr, pj_str.slen).decode() cdef object _pj_buf_len_to_str(object buf, int buf_len): return PyBytes_FromStringAndSize(buf, buf_len) cdef object _buf_to_str(object buf): return PyBytes_FromString(buf).decode() cdef object _str_as_str(object string): if type(string) != bytes: return PyBytes_AsString(string.encode()) else: return PyBytes_AsString(string) cdef object _str_as_size(object string): if type(string) != bytes: return PyBytes_Size(string.encode()) else: 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 name = name if isinstance(name, bytes) else name.encode() _str_to_pj_str(name, ¶m.name) if value is None: param.value.slen = 0 else: value = value if isinstance(value, bytes) else value.encode() _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, 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 = [] if array_header.count < 128: for i from 0 <= i < array_header.count: header_data.append(_pj_str_to_bytes(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 hvalue = _pj_str_to_str(cseq_header.method.name) header_data = (cseq_header.cseq, hvalue) 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"): hvalue = ( header).hvalue header_value = _pj_str_to_str(hvalue) header_data = FrozenHeader(header_name, header_value) 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: - hb = header.name.encode() - bb = header.body.encode() + hb = header.name + bb = header.body _str_to_pj_str(hb, &name_pj) _str_to_pj_str(bb, &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 _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, &pj_header.name_addr.display) _dict_to_pjsip_param(header.parameters, &pj_header.other_param, pool) return 0 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)) if name == 'hide': name = b'hide' elif name == 'tls_name': name = b'tls_name' _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 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()