terminate method

void terminate([
  1. Map<String, dynamic>? options
])

Terminate the call.

Implementation

void terminate([Map<String, dynamic>? options]) {
  logger.debug('terminate()');

  options = options ?? <String, dynamic>{};

  Object cause = options['cause'] ?? dart_sip_c.CausesType.BYE;

  List<dynamic> extraHeaders = options['extraHeaders'] != null
      ? utils.cloneArray(options['extraHeaders'])
      : <dynamic>[];
  Object? body = options['body'];

  String? cancel_reason;
  int? status_code = options['status_code'] as int?;
  String? reason_phrase = options['reason_phrase'] as String?;

  // Check Session Status.
  if (_status == C.statusTerminated) {
    throw exceptions.InvalidStateError(_status);
  }

  switch (_status) {
    // - UAC -
    case C.statusNull:
    case C.statusInviteSent:
    case C.status1xxReceived:
      logger.debug('canceling session');

      if (status_code != null && (status_code < 200 || status_code >= 700)) {
        throw exceptions.TypeError('Invalid status_code: $status_code');
      } else if (status_code != null) {
        reason_phrase =
            reason_phrase ?? dart_sip_c.REASON_PHRASE[status_code];
        cancel_reason = 'SIP ;cause=$status_code ;text="$reason_phrase"';
      }

      // Check Session Status.
      if (_status == C.statusNull || _status == C.statusInviteSent) {
        _is_canceled = true;
        _cancel_reason = cancel_reason;
      } else if (_status == C.status1xxReceived) {
        _request.cancel(cancel_reason ?? '');
      }

      _status = C.statusCanceled;
      cancel_reason = cancel_reason ?? 'Canceled by local';
      status_code = status_code ?? 100;
      _failed('local', null, null, null, status_code,
          dart_sip_c.CausesType.CANCELED, cancel_reason);
      break;

    // - UAS -
    case C.statusWaitingForAnswer:
    case C.statusAnswered:
      logger.debug('rejecting session');

      status_code = status_code ?? 480;

      if (status_code < 300 || status_code >= 700) {
        throw exceptions.InvalidStateError(
            'Invalid status_code: $status_code');
      }

      _request.reply(status_code, reason_phrase, extraHeaders, body);
      _failed('local', null, null, null, status_code,
          dart_sip_c.CausesType.REJECTED, reason_phrase);
      break;

    case C.statusWaitingForAck:
    case C.statusConfirmed:
      logger.debug('terminating session');

      reason_phrase = options['reason_phrase'] as String? ??
          dart_sip_c.REASON_PHRASE[status_code ?? 0];

      if (status_code != null && (status_code < 200 || status_code >= 700)) {
        throw exceptions.InvalidStateError(
            'Invalid status_code: $status_code');
      } else if (status_code != null) {
        extraHeaders
            .add('Reason: SIP ;case=$status_code; text="$reason_phrase"');
      }

      /* RFC 3261 section 15 (Terminating a session):
        *
        * "...the callee's UA MUST NOT send a BYE on a confirmed dialog
        * until it has received an ACK for its 2xx response or until the server
        * transaction times out."
        */
      if (_status == C.statusWaitingForAck &&
          _direction == 'incoming' &&
          _request.server_transaction.state != TransactionState.TERMINATED) {
        /// Save the dialog for later restoration.
        Dialog dialog = _dialog!;

        // Send the BYE as soon as the ACK is received...
        receiveRequest = (IncomingMessage request) {
          if (request.method == SipMethod.ACK) {
            sendRequest(SipMethod.BYE, <String, dynamic>{
              'extraHeaders': extraHeaders,
              'body': body
            });
            dialog.terminate();
          }
        };

        // .., or when the INVITE transaction times out
        _request.server_transaction.on(EventStateChanged(),
            (EventStateChanged state) {
          if (_request.server_transaction.state ==
              TransactionState.TERMINATED) {
            sendRequest(SipMethod.BYE, <String, dynamic>{
              'extraHeaders': extraHeaders,
              'body': body
            });
            dialog.terminate();
          }
        });

        _ended(
            'local',
            null,
            ErrorCause(
                cause: cause as String?,
                status_code: status_code,
                reason_phrase: reason_phrase));

        // Restore the dialog into 'this' in order to be able to send the in-dialog BYE :-).
        _dialog = dialog;

        // Restore the dialog into 'ua' so the ACK can reach 'this' session.
        _ua!.newDialog(dialog);
      } else {
        sendRequest(SipMethod.BYE,
            <String, dynamic>{'extraHeaders': extraHeaders, 'body': body});
        reason_phrase = reason_phrase ?? 'Terminated by local';
        status_code = status_code ?? 200;
        _ended(
            'local',
            null,
            ErrorCause(
                cause: cause as String?,
                status_code: status_code,
                reason_phrase: reason_phrase));
      }
  }
}