# negotiate method

1. @override

Future <Result<NegotiatorState, NegotiatorError>> negotiate(

1. XMLNode nonza

)
override

Called with the currently received nonza `nonza` when the negotiator is active. If the negotiator is just elected to be the next one, then `nonza` is equal to the <stream:features /> nonza.

Returns the next state of the negotiator. If done or retryLater is selected, then negotiator won't be called again. If retryLater is returned, then the negotiator must switch some internal state to prevent getting matched immediately again. If ready is returned, then the negotiator indicates that it is not done with negotiation.

## Implementation

    @override
    Future<Result<NegotiatorState, NegotiatorError>> negotiate(
      XMLNode nonza,
    ) async {
      switch (_sasl2State) {
        case Sasl2State.idle:
          _sasl2Data = nonza.firstTag('authentication', xmlns: sasl2Xmlns);
          final mechanisms = XMLNode.xmlns(
            tag: 'mechanisms',
            xmlns: saslXmlns,
            children:
                _sasl2Data!.children.where((c) => c.tag == 'mechanism').toList(),
          );
          for (final negotiator in _saslNegotiators) {
            if (negotiator.matchesFeature([mechanisms])) {
              _currentSaslNegotiator = negotiator;
              _currentSaslNegotiator!.pickForSasl2();
              break;
            }
          }

          // We must have a SASL negotiator by now
          if (_currentSaslNegotiator == null) {
            return Result(NoSASLMechanismSelectedError());
          }

          // Collect additional data by interested negotiators
          final inline = _sasl2Data!.firstTag('inline');
          final children = List<XMLNode>.empty(growable: true);
          if (inline != null && inline.children.isNotEmpty) {
            for (final negotiator in _featureNegotiators) {
              if (negotiator.canInlineFeature(inline.children)) {
                _activeSasl2Negotiators.add(negotiator.id);
                children.addAll(
                  await negotiator.onSasl2FeaturesReceived(_sasl2Data!),
                );
              }
            }
          }

          // Build the authenticate nonza
          final authenticate = XMLNode.xmlns(
            tag: 'authenticate',
            xmlns: sasl2Xmlns,
            attributes: {
              'mechanism': _currentSaslNegotiator!.mechanismName,
            },
            children: [
              XMLNode(
                tag: 'initial-response',
                text: await _currentSaslNegotiator!.getRawStep(''),
              ),
              if (userAgent != null) userAgent!.toXml(),
              ...children,
            ],
          );

          _sasl2State = Sasl2State.authenticateSent;
          attributes.sendNonza(authenticate);
          return const Result(NegotiatorState.ready);
        case Sasl2State.authenticateSent:
          if (nonza.tag == 'success') {
            // Tell the dependent negotiators about the result
            final negotiators = _featureNegotiators
                .where(
                  (negotiator) => _activeSasl2Negotiators.contains(negotiator.id),
                )
                .toList()
              ..add(_currentSaslNegotiator!);
            for (final negotiator in negotiators) {
              final result = await negotiator.onSasl2Success(nonza);
              if (!result.isType<bool>()) {
                return Result(result.get<NegotiatorError>());
              }
            }

            // We're done
            attributes.setAuthenticated();
            attributes.removeNegotiatingFeature(saslXmlns);

            // Check if we also received a resource with the SASL2 success
            final jid = JID.fromString(
              nonza.firstTag('authorization-identifier')!.innerText(),
            );
            if (!jid.isBare()) {
              attributes.setResource(jid.resource);
            }

            return const Result(NegotiatorState.done);
          } else if (nonza.tag == 'challenge') {
            // We still have to negotiate
            final challenge = nonza.innerText();
            final response = XMLNode.xmlns(
              tag: 'response',
              xmlns: sasl2Xmlns,
              text: await _currentSaslNegotiator!.getRawStep(challenge),
            );
            attributes.sendNonza(response);
          } else if (nonza.tag == 'failure') {
            final negotiators = _featureNegotiators
                .where(
                  (negotiator) => _activeSasl2Negotiators.contains(negotiator.id),
                )
                .toList()
              ..add(_currentSaslNegotiator!);
            for (final negotiator in negotiators) {
              await negotiator.onSasl2Failure(nonza);
            }

            // Check if we should retry and, if we should, reset the current
            // negotiator, this negotiator, and retry.
            if (_currentSaslNegotiator!.shouldRetrySasl()) {
              _currentSaslNegotiator!.reset();
              reset();
              return const Result(
                NegotiatorState.retryLater,
              );
            }

            return Result(
              SaslError.fromFailure(nonza),
            );
          }
      }

      return const Result(NegotiatorState.ready);
    }