Repository: thunderbird/thunderbird-android
File: mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt (Kotlin)

Code before:

    private fun saslAuthCramMD5() {
        val respList = executeCommand("AUTH CRAM-MD5").texts
        if (respList.size != 1) {
            throw MessagingException("Unable to negotiate CRAM-MD5")
        }

        val b64Nonce = respList[0]
        val b64CRAMString = Authentication.computeCramMd5(username, password, b64Nonce)
        try {
            executeSensitiveCommand(b64CRAMString)
        } catch (exception: NegativeSmtpReplyException) {
            handlePossibleAuthenticationFailure("AUTH CRAM-MD5", exception)
        }
    }

    private fun saslOAuth(method: OAuthMethod) {
        Log.d("saslOAuth() called with: method = $method")
        retryOAuthWithNewToken = true
        checkNotNull(oauthTokenProvider) { "No OAuth2TokenProvider available." }

        val users = buildSet {
            // add the given username to the list of users
            add(username)
            // add all the usernames we can fetch from the id_token to be used in case from
            // the given username is fails to authenticate.
            addAll(oauthTokenProvider.usernames)
        }.toMutableSet()
        val negativeResponses = mutableMapOf<String, NegativeSmtpReplyException>()
        val sensitiveLog = "*sensitive*"
        logger.log("users = ${users.takeIf { K9MailLib.isDebugSensitive() } ?: "[${users.size} emails found]"}")

        val iterator = users.iterator()
        while (iterator.hasNext()) {
            val user = iterator.next()
            try {
                logger.log(
                    "trying to authenticate with user '${
                        user.takeIf { K9MailLib.isDebugSensitive() } ?: sensitiveLog
                    }'",
                )
                attempOAuth(method, user)
                negativeResponses.clear()
                break
            } catch (negativeResponse: NegativeSmtpReplyException) {
                Log.w(negativeResponse, "saslOAuth: failed to authenticate.")
                if (negativeResponse.replyCode != SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
                    throw negativeResponse
                }
                iterator.remove()
                negativeResponses += user to negativeResponse

Review comment (bug):
```suggestion
            // add all the usernames we can fetch from the id_token to be used in case
            // the given username fails to authenticate.
```

Code after:

    private fun saslAuthCramMD5() {
        val respList = executeCommand("AUTH CRAM-MD5").texts
        if (respList.size != 1) {
            throw MessagingException("Unable to negotiate CRAM-MD5")
        }

        val b64Nonce = respList[0]
        val b64CRAMString = Authentication.computeCramMd5(username, password, b64Nonce)
        try {
            executeSensitiveCommand(b64CRAMString)
        } catch (exception: NegativeSmtpReplyException) {
            handlePossibleAuthenticationFailure("AUTH CRAM-MD5", exception)
        }
    }

    private fun saslOAuth(method: OAuthMethod) {
        Log.d("saslOAuth() called with: method = $method")
        retryOAuthWithNewToken = true
        checkNotNull(oauthTokenProvider) { "No OAuth2TokenProvider available." }

        val users = buildSet {
            // add the given username to the list of users
            add(username)
            // add all the usernames we can fetch from the id_token to be used in case
            // the given username fails to authenticate.
            addAll(oauthTokenProvider.usernames)
        }.toMutableSet()

        val negativeResponses = authenticateUsers(users, method)

        if (negativeResponses.isNotEmpty()) {
            Log.w("failed to authenticate with all discovered users.")
            val (user, negativeResponse) = negativeResponses[username]?.let { username to it }
                ?: negativeResponses.entries.first().toPair()

            logger.log("invalidating current token")
            oauthTokenProvider.invalidateToken()

            if (!retryOAuthWithNewToken) {
                handlePermanentOAuthFailure(method, negativeResponse)
            } else {
                handleTemporaryOAuthFailure(
                    method = method,
                    username = user,
                    negativeResponseFromOldToken = negativeResponse,
                )
            }
        }
    }