/// <summary> /// Starts terminating call. To get when call actually terminates, monitor <b>StateChanged</b> event. /// </summary> /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception> public void Terminate() { lock (m_pLock) { if (m_State == SIP_UA_CallState.Disposed) { throw new ObjectDisposedException(GetType().Name); } if (m_State == SIP_UA_CallState.Terminating || m_State == SIP_UA_CallState.Terminated) { return; } else if (m_State == SIP_UA_CallState.WaitingForStart) { SetState(SIP_UA_CallState.Terminated); } else if (m_State == SIP_UA_CallState.WaitingToAccept) { m_pInitialInviteTransaction.SendResponse( m_pUA.Stack.CreateResponse(SIP_ResponseCodes.x487_Request_Terminated, m_pInitialInviteTransaction.Request)); SetState(SIP_UA_CallState.Terminated); } else if (m_State == SIP_UA_CallState.Active) { m_pDialog.Terminate(); SetState(SIP_UA_CallState.Terminated); } else if (m_pInitialInviteSender != null) { /* RFC 3261 15. * If we are caller and call is not active yet, we must do following actions: *) Send CANCEL, set call Terminating flag. *) If we get non 2xx final response, we are done. (Normally cancel causes '408 Request terminated') *) If we get 2xx response (2xx sent by remote party before our CANCEL reached), we must send BYE to active dialog. */ SetState(SIP_UA_CallState.Terminating); m_pInitialInviteSender.Cancel(); } } }
/// <summary> /// This method is called when SIP stack received new message. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pStack_RequestReceived(object sender, SIP_RequestReceivedEventArgs e) { // TODO: Performance: rise events on thread pool or see if this method called on pool aready, then we may not keep lock for events ? if (e.Request.RequestLine.Method == SIP_Methods.CANCEL) { /* RFC 3261 9.2. * If the UAS did not find a matching transaction for the CANCEL * according to the procedure above, it SHOULD respond to the CANCEL * with a 481 (Call Leg/Transaction Does Not Exist). * * Regardless of the method of the original request, as long as the * CANCEL matched an existing transaction, the UAS answers the CANCEL * request itself with a 200 (OK) response. */ SIP_ServerTransaction trToCancel = m_pStack.TransactionLayer.MatchCancelToTransaction(e.Request); if (trToCancel != null) { trToCancel.Cancel(); e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request)); } else { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist, e.Request)); } } else if (e.Request.RequestLine.Method == SIP_Methods.BYE) { /* RFC 3261 15.1.2. * If the BYE does not match an existing dialog, the UAS core SHOULD generate a 481 * (Call/Transaction Does Not Exist) response and pass that to the server transaction. */ // TODO: SIP_Dialog dialog = m_pStack.TransactionLayer.MatchDialog(e.Request); if (dialog != null) { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request)); dialog.Terminate(); } else { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist, e.Request)); } } else if (e.Request.RequestLine.Method == SIP_Methods.INVITE) { // Supress INVITE retransmissions. e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x100_Trying, e.Request)); // Create call. SIP_UA_Call call = new SIP_UA_Call(this, e.ServerTransaction); call.StateChanged += new EventHandler(Call_StateChanged); m_pCalls.Add(call); OnIncomingCall(call); } else { OnRequestReceived(e); } }