string += ";" + ";".join(["%s%s" % (name, ("" if val is None else "="+urllib.quote(val, safe="()[]-_.!~*'/:&+$")))
for name, val in self.parameters.iteritems()])
if self.headers:
string += "?" + "&".join(["%s%s" % (name, ("" if val is None else "="+urllib.quote(val, safe="()[]-_.!~*'/:?+$")))
for name, val in self.headers.iteritems()])
if self.secure:
string = "sips:" + string
else:
string = "sip:" + string
return string
def __richcmp__(self, other, op):
return BaseSIPURI_richcmp(self, other, op)
property transport:
def __get__(self):
return self.parameters.get('transport', 'udp')
def __set__(self, str transport not None):
if isinstance(self, FrozenSIPURI):
raise AttributeError("can't set readonly attribute")
if transport.lower() == 'udp':
self.parameters.pop('transport', None)
else:
self.parameters['transport'] = transport
def matches(self, address):
match = re.match(r'^((?P<scheme>sip|sips):)?(?P<username>.+?)(@(?P<domain>.+?)(:(?P<port>\d+?))?)?(;(?P<parameters>.+?))?(\?(?P<headers>.+?))?$', 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])
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])
cdef int process_incoming_options(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1:
cdef pjsip_tx_data *tdata
cdef pjsip_transaction *initial_tsx
try:
initial_tsx = pjsip_rdata_get_tsx(rdata)
with nogil:
status = pjsip_dlg_create_response(self._dialog, rdata, 200, NULL, &tdata)
if status != 0:
with nogil:
pjsip_tsx_terminate(initial_tsx, 500)
raise PJSIPError("Could not create response for incoming OPTIONS", status)
with nogil:
status = pjsip_dlg_send_response(self._dialog, initial_tsx, tdata)
if status != 0:
raise PJSIPError("Could not send response", status)
except PJSIPError:
pass
def send_invite(self, SIPURI request_uri not None, FromHeader from_header not None, ToHeader to_header not None, RouteHeader route_header not None, ContactHeader contact_header not None,
SDPSession sdp not None, Credentials credentials=None, list extra_headers not None=list(), timeout=None):
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_sdp_session *local_sdp
cdef pjsip_cred_info *cred_info
cdef pjsip_dialog **dialog_address
cdef pjsip_inv_session **invite_session_address
cdef pjsip_replaces_hdr *pj_replaces_hdr
cdef pjsip_route_hdr *route_set
cdef pjsip_tx_data *tdata
cdef PJSIPUA ua
cdef PJSTR contact_uri_str
cdef PJSTR from_header_str
cdef PJSTR to_header_str
cdef PJSTR request_uri_str
ua = _get_ua()
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
dialog_address = &self._dialog
invite_session_address = &self._invite_session
route_set = <pjsip_route_hdr *> &self._route_set
if self.state != None:
raise SIPCoreInvalidStateError('Can only transition to the "outgoing" state from the "None" state, currently in the "%s" state' % self.state)
if timeout is not None and timeout <= 0:
raise ValueError("Timeout value must be positive")
self.transport = route_header.uri.transport
self.direction = "outgoing"
self.credentials = FrozenCredentials.new(credentials) if credentials is not None else None
if isinstance(e, PJSIPError) and e.errno == EADDRNOTAVAIL:
self._invite_session = NULL
pjsip_dlg_dec_lock(self._dialog)
self._dialog = NULL
raise
if self._invite_session != NULL:
pjsip_inv_terminate(self._invite_session, 500, 0)
self._invite_session = NULL
elif self._dialog != NULL:
pjsip_dlg_dec_lock(self._dialog)
self._dialog = NULL
raise
finally:
with nogil:
pj_mutex_unlock(lock)
def send_response(self, int code, str reason=None, BaseContactHeader contact_header=None, BaseSDPSession sdp=None, list extra_headers not None=list()):
cdef int status
cdef int clean_tdata = 0
cdef pj_mutex_t *lock = self._lock
cdef pj_str_t reason_str
cdef pjmedia_sdp_session_ptr_const lsdp = NULL
cdef pjmedia_sdp_session *local_sdp
cdef pjsip_inv_session *invite_session
cdef pjsip_msg_body *body
cdef pjsip_tx_data *tdata
cdef PJSIPUA ua
ua = _get_ua()
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
invite_session = self._invite_session
if reason is not None:
_str_to_pj_str(reason, &reason_str)
if self.state not in ("incoming", "early", "connected"):
raise SIPCoreInvalidStateError('Can only send response from the "incoming", "early" and "connected" states current in the "%s" state.' % self.state)
if self.state == "early" and self.direction != "incoming":
raise SIPCoreInvalidStateError('Cannot send response in the "early" state for an outgoing INVITE')
if self.state == "connected" and self.sub_state not in ("received_proposal", "received_proposal_request"):
raise SIPCoreInvalidStateError('Cannot send response in the "connected" state if a proposal has not been received')
if contact_header is not None:
self._update_contact_header(contact_header)
if 200 <= code < 300 and sdp is None:
raise SIPCoreError("Local SDP needs to be set for a positive response")
if code >= 300 and sdp is not None:
raise SIPCoreError("Local SDP cannot be specified for a negative response")
self.sdp.proposed_local = FrozenSDPSession.new(sdp) if sdp is not None else None
local_sdp = self.sdp.proposed_local.get_sdp_session() if sdp is not None else NULL
if sdp is not None and self.sdp.proposed_remote is None:
# There was no remote proposal, this is a reply with an offer
with nogil:
status = pjmedia_sdp_neg_modify_local_offer(self._dialog.pool, invite_session.neg, <pjmedia_sdp_session_ptr_const>local_sdp);
if status != 0:
raise PJSIPError("Could not modify local SDP offer", status)