Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7e4d034a authored by daquexian's avatar daquexian
Browse files

Delete invalid refresh token when it is revoked during setting checking. Updated settings dialog

parent 70bc6f51
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -3,6 +3,8 @@ package com.fsck.k9.mail;

public class AuthenticationFailedException extends MessagingException {
    public static final long serialVersionUID = -1;
    public static final String OAUTH2_ERROR_INVALID_REFRESH_TOKEN = "oauth2-invalid refresh token";
    public static final String OAUTH2_ERROR_UNKNOWN = "oauth2-unknown";

    public AuthenticationFailedException(String message) {
        super(message);
+29 −5
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ package com.fsck.k9.account;
import com.fsck.k9.mail.AuthenticationFailedException;
import com.fsck.k9.mail.oauth.OAuth2AuthorizationCodeFlowTokenProvider;
import com.fsck.k9.mail.oauth.SpecificOAuth2TokenProvider;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;

import java.io.IOException;
@@ -42,16 +43,39 @@ abstract class AndroidSpecificOAuth2TokenProvider extends SpecificOAuth2TokenPro

    public String refreshToken(String username, String refreshToken) throws AuthenticationFailedException {
        Call<RefreshResponse> call = getRefreshTokenCall(refreshToken);
        RefreshResponse response;
        Response<RefreshResponse> response;
        RefreshResponse refreshResponse;
        try {
            response = call.execute().body();
            response = call.execute();
            refreshResponse = response.body();
        } catch (IOException e) {
            throw new AuthenticationFailedException(e.getMessage());
        }
        if (response == null) {
            throw new AuthenticationFailedException("Error when getting refresh token");
        if (refreshResponse == null) {
            if (response.errorBody() != null) {
                try {
                    String errorBody = response.errorBody().string();
                    OAuth2Error oAuth2Error = new Gson().fromJson(errorBody, OAuth2Error.class);
                    switch (oAuth2Error.error) {
                        case "invalid_grant":
                            throw new AuthenticationFailedException(AuthenticationFailedException.OAUTH2_ERROR_INVALID_REFRESH_TOKEN);
                        default:
                            throw new AuthenticationFailedException(AuthenticationFailedException.OAUTH2_ERROR_UNKNOWN);
                    }
                } catch (IOException e) {
                    throw new AuthenticationFailedException(AuthenticationFailedException.OAUTH2_ERROR_UNKNOWN);
                }
            } else {
                throw new AuthenticationFailedException(AuthenticationFailedException.OAUTH2_ERROR_UNKNOWN);
            }
        }
        return response.accessToken;
        return refreshResponse.accessToken;
    }

    protected static class OAuth2Error {
        String error;
        @SerializedName("error_description")
        String errorDescription;
    }

    protected static class ExchangeResponse {
+46 −2
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import android.widget.Spinner;
import android.widget.TextView;

import com.fsck.k9.mail.ServerSettings.Type;
import com.fsck.k9.service.StorageGoneReceiver;
import com.fsck.k9.view.ClientCertificateSpinner;

import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
@@ -92,10 +93,12 @@ public class AccountSetupActivity extends AppCompatActivity implements AccountSe

    private ClientCertificateSpinner clientCertificateSpinner;
    private TextView clientCertificateLabelView;
    private TextInputLayout usernameViewLayout;
    private TextInputLayout passwordViewLayout;
    private TextInputLayout serverViewLayout;
    private TextInputEditText serverView;
    private TextInputEditText portView;
    private TextView securityTypeLabelView;
    private Spinner securityTypeView;
    private Spinner authTypeView;
    private CheckBox imapAutoDetectNamespaceView;
@@ -758,6 +761,7 @@ public class AccountSetupActivity extends AppCompatActivity implements AccountSe
        View incomingView = findViewById(R.id.account_setup_incoming);
        coordinatorLayout = (CoordinatorLayout) findViewById(R.id.incoming_coordinator_layout);
        usernameView = (EditText) incomingView.findViewById(R.id.incoming_account_username);
        usernameViewLayout = (TextInputLayout) incomingView.findViewById(R.id.incoming_account_username_layout);
        passwordView = (EditText) incomingView.findViewById(R.id.incoming_account_password);
        clientCertificateSpinner = (ClientCertificateSpinner) incomingView.findViewById(R.id.incoming_account_client_certificate_spinner);
        clientCertificateLabelView = (TextView) incomingView.findViewById(R.id.account_client_certificate_label);
@@ -765,6 +769,7 @@ public class AccountSetupActivity extends AppCompatActivity implements AccountSe
        serverViewLayout = (TextInputLayout)  incomingView.findViewById(R.id.incoming_account_server_layout);
        serverView = (TextInputEditText) incomingView.findViewById(R.id.incoming_account_server);
        portView = (TextInputEditText) incomingView.findViewById(R.id.incoming_account_port);
        securityTypeLabelView = (TextView) incomingView.findViewById(R.id.account_setup_incoming_security_label);
        securityTypeView = (Spinner) incomingView.findViewById(R.id.incoming_account_security_type);
        authTypeView = (Spinner) incomingView.findViewById(R.id.incoming_account_auth_type);
        imapAutoDetectNamespaceView = (CheckBox) incomingView.findViewById(R.id.imap_autodetect_namespace);
@@ -849,24 +854,38 @@ public class AccountSetupActivity extends AppCompatActivity implements AccountSe

    @Override
    public void setViewNotExternalInIncoming() {
        passwordView.setVisibility(View.VISIBLE);
        passwordViewLayout.setVisibility(View.VISIBLE);
        clientCertificateLabelView.setVisibility(View.GONE);
        clientCertificateSpinner.setVisibility(View.GONE);
        imapAutoDetectNamespaceView.setEnabled(true);
        passwordViewLayout.setEnabled(true);
        securityTypeView.setEnabled(true);
        portView.setEnabled(true);

        passwordView.requestFocus();
    }

    @Override
    public void setViewExternalInIncoming() {
        passwordView.setVisibility(View.GONE);
        passwordViewLayout.setVisibility(View.GONE);
        clientCertificateLabelView.setVisibility(View.VISIBLE);
        clientCertificateSpinner.setVisibility(View.VISIBLE);
        imapAutoDetectNamespaceView.setEnabled(true);
        passwordViewLayout.setEnabled(true);
        securityTypeView.setEnabled(true);
        portView.setEnabled(true);

        clientCertificateSpinner.chooseCertificate();
    }

    @Override
    public void setViewOAuth2InIncoming() {
        imapAutoDetectNamespaceView.setEnabled(false);
        passwordViewLayout.setEnabled(false);
        securityTypeView.setEnabled(false);
        portView.setEnabled(false);
    }

    @Override
    public void showFailureToast(Exception use) {
        failure(use);
@@ -902,6 +921,17 @@ public class AccountSetupActivity extends AppCompatActivity implements AccountSe
        Toast.makeText(this, toastText, Toast.LENGTH_LONG).show();
    }

    @Override
    public void showInvalidOAuthError() {
        usernameViewLayout.setErrorEnabled(true);
        usernameViewLayout.setError(getString(R.string.OAuth2_not_supported));
    }

    @Override
    public void clearInvalidOAuthError() {
        usernameViewLayout.setError("");
    }

    // names

    public void namesStart() {
@@ -1068,6 +1098,7 @@ public class AccountSetupActivity extends AppCompatActivity implements AccountSe
        final View outgoingView = findViewById(R.id.account_setup_outgoing);
        coordinatorLayout = (CoordinatorLayout) outgoingView.findViewById(R.id.outgoing_coordinator_layout);
        usernameView = (EditText) outgoingView.findViewById(R.id.outgoing_account_username);
        usernameViewLayout = (TextInputLayout) outgoingView.findViewById(R.id.outgoing_account_username_layout);
        passwordView = (EditText) outgoingView.findViewById(R.id.outgoing_account_password);
        passwordViewLayout = (TextInputLayout) outgoingView.findViewById(R.id.outgoing_account_password_layout);
        clientCertificateSpinner = (ClientCertificateSpinner) outgoingView.findViewById(R.id.outgoing_account_client_certificate_spinner);
@@ -1182,6 +1213,9 @@ public class AccountSetupActivity extends AppCompatActivity implements AccountSe
        passwordViewLayout.setVisibility(View.VISIBLE);
        clientCertificateLabelView.setVisibility(View.GONE);
        clientCertificateSpinner.setVisibility(View.GONE);
        passwordViewLayout.setEnabled(true);
        securityTypeView.setEnabled(true);
        portView.setEnabled(true);

        passwordView.requestFocus();
    }
@@ -1192,11 +1226,21 @@ public class AccountSetupActivity extends AppCompatActivity implements AccountSe
        passwordViewLayout.setVisibility(View.GONE);
        clientCertificateLabelView.setVisibility(View.VISIBLE);
        clientCertificateSpinner.setVisibility(View.VISIBLE);
        passwordViewLayout.setEnabled(true);
        securityTypeView.setEnabled(true);
        portView.setEnabled(true);

        // This may again invoke onInputChangedInOutgoing()
        clientCertificateSpinner.chooseCertificate();
    }

    @Override
    public void setViewOAuth2InOutgoing() {
        passwordViewLayout.setEnabled(false);
        securityTypeView.setEnabled(false);
        portView.setEnabled(false);
    }

    public static void actionEditIncomingSettings(Activity context, Account account) {
        context.startActivity(intentActionEditIncomingSettings(context, account));
    }
+6 −0
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ interface AccountSetupContract {

        void setViewNotExternalInIncoming();
        void setViewExternalInIncoming();
        void setViewOAuth2InIncoming();

        void showFailureToast(Exception use);

@@ -88,6 +89,8 @@ interface AccountSetupContract {
        void setSubscribedFoldersOnly(boolean subscribedFoldersOnly);

        void showInvalidSettingsToast();
        void showInvalidOAuthError();
        void clearInvalidOAuthError();

        /* --Names-- */
        void setDoneButtonInNamesEnabled(boolean enabled);
@@ -112,10 +115,13 @@ interface AccountSetupContract {

        void setViewNotExternalInOutgoing();
        void setViewExternalInOutgoing();
        void setViewOAuth2InOutgoing();

        void goToOutgoingChecking();

        void goToAccountNames();


        // ---
        void goBack();
        void end();
+47 −42
Original line number Diff line number Diff line
@@ -67,7 +67,7 @@ import java.util.Locale;
import java.util.Map;

import com.fsck.k9.setup.ServerNameSuggester;
import org.xbill.DNS.TextParseException;

import timber.log.Timber;

import static com.fsck.k9.mail.ServerSettings.Type.IMAP;
@@ -496,14 +496,6 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,


    private class CheckOutgoingTask extends CheckAccountTask {
        private CheckOutgoingTask(Account account) {
            super(account);
        }

        private CheckOutgoingTask(Account account, CheckSettingsSuccessCallback callback) {
            super(account, callback);
        }

        private CheckOutgoingTask(AccountConfig accountConfig) {
            super(accountConfig);
        }
@@ -537,14 +529,6 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,
    }

    private class CheckIncomingTask extends CheckAccountTask {
        private CheckIncomingTask(Account account) {
            super(account);
        }

        private CheckIncomingTask(Account account, CheckSettingsSuccessCallback callback) {
            super(account, callback);
        }

        private CheckIncomingTask(AccountConfig accountConfig) {
            super(accountConfig);
        }
@@ -593,25 +577,13 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,
     */
    private abstract class CheckAccountTask extends AsyncTask<CheckDirection, Integer, Boolean> {
        private final AccountConfig accountConfig;
        private final Account account;
        private CheckSettingsSuccessCallback callback;

        private CheckAccountTask(Account account) {
            this(account, null);
        }

        private CheckAccountTask(Account account, CheckSettingsSuccessCallback callback) {
            this.account = account;
            this.accountConfig = null;
            this.callback = callback;
        }

        private CheckAccountTask(AccountConfig accountConfig) {
            this(accountConfig, null);
        }

        private CheckAccountTask(AccountConfig accountConfig, CheckSettingsSuccessCallback callback) {
            this.account = null;
            this.accountConfig = accountConfig;
            this.callback = callback;
        }
@@ -628,6 +600,10 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,
            } catch (OAuth2NeedUserPromptException ignored) {
            } catch (final AuthenticationFailedException afe) {
                Timber.e(afe, "Error while testing settings");
                if (afe.getMessage().equals(AuthenticationFailedException.OAUTH2_ERROR_INVALID_REFRESH_TOKEN)) {
                    Globals.getOAuth2TokenProvider().disconnectEmailWithK9(accountConfig.getEmail());
                    replayChecking();
                } else {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
@@ -635,6 +611,7 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,
                            view.showErrorDialog(R.string.account_setup_failed_auth_message);
                        }
                    });
                }
            } catch (CertificateValidationException cve) {
                handleCertificateValidationException(cve);
            } catch (final Exception e) {
@@ -670,7 +647,8 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,

        void clearCertificateErrorNotifications(CheckDirection direction) {
            final MessagingController ctrl = MessagingController.getInstance(context);
            ctrl.clearCertificateErrorNotifications(account, direction);
            ctrl.clearCertificateErrorNotifications((Account) accountConfig, direction);

        }

        @Override
@@ -1191,7 +1169,6 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,
    public void onInputChangedInIncoming(String certificateAlias, String server, String port,
            String username, String password, AuthType authType,
            ConnectionSecurity connectionSecurity) {

        revokeInvalidSettingsAndUpdateViewInIncoming(authType, connectionSecurity, port);
        validateFieldInIncoming(certificateAlias, server, currentIncomingPort, username, password,
                currentIncomingAuthType,
@@ -1246,7 +1223,6 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,
            ConnectionSecurity connectionSecurity,
            String port) {
        boolean isAuthTypeExternal = (AuthType.EXTERNAL == authType);

        boolean hasConnectionSecurity = (connectionSecurity != ConnectionSecurity.NONE);

        if (isAuthTypeExternal && !hasConnectionSecurity) {
@@ -1293,6 +1269,8 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,

    private void validateFieldInIncoming(String certificateAlias, String server, String port,
            String username, String password, AuthType authType, ConnectionSecurity connectionSecurity) {
        boolean isAuthTypeOAuth = (AuthType.XOAUTH2 == authType);
        boolean isOAuthValid = canOAuth2(username);
        boolean isAuthTypeExternal = (AuthType.EXTERNAL == authType);
        boolean hasConnectionSecurity = (connectionSecurity != ConnectionSecurity.NONE);

@@ -1300,7 +1278,7 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,
        boolean hasValidUserName = Utility.requiredFieldValid(username);

        boolean hasValidPasswordSettings = hasValidUserName
                && !isAuthTypeExternal
                && !isAuthTypeExternal && !isAuthTypeOAuth
                && Utility.requiredFieldValid(password);

        boolean hasValidExternalAuthSettings = hasValidUserName
@@ -1308,13 +1286,27 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,
                && hasConnectionSecurity
                && hasValidCertificateAlias;

        boolean hasValidOAuth2Settings = hasValidUserName
                && isAuthTypeOAuth
                && isOAuthValid;

        final boolean enabled = Utility.domainFieldValid(server)
                && Utility.requiredFieldValid(port)
                && (hasValidPasswordSettings || hasValidExternalAuthSettings);
                && (hasValidPasswordSettings || hasValidExternalAuthSettings
                || hasValidOAuth2Settings);

        checkInvalidOAuthError(isAuthTypeOAuth, isOAuthValid);
        view.setNextButtonInIncomingEnabled(enabled);
    }

    private void checkInvalidOAuthError(boolean isAuthTypeOAuth, boolean isOAuthValid) {
        if (isAuthTypeOAuth && !isOAuthValid) {
            view.showInvalidOAuthError();
        } else {
            view.clearInvalidOAuthError();
        }
    }

    private void updateAccount() {
        Account account = (Account) accountConfig;

@@ -1334,6 +1326,8 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,
    private void updateViewFromAuthTypeInIncoming(AuthType authType) {
        if (authType == AuthType.EXTERNAL) {
            view.setViewExternalInIncoming();
        } else if (authType == AuthType.XOAUTH2) {
            view.setViewOAuth2InIncoming();
        } else {
            view.setViewNotExternalInIncoming();
        }
@@ -1521,6 +1515,8 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,
            ConnectionSecurity connectionSecurity,
            boolean requireLogin) {

        boolean isAuthTypeOAuth = (AuthType.XOAUTH2 == authType);
        boolean isOAuthValid = canOAuth2(username);
        boolean isAuthTypeExternal = (AuthType.EXTERNAL == authType);
        boolean hasConnectionSecurity = (connectionSecurity != ConnectionSecurity.NONE);

@@ -1536,10 +1532,17 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,
                && hasConnectionSecurity
                && hasValidCertificateAlias;

        boolean hasValidOAuth2Settings = hasValidUserName
                && isAuthTypeOAuth
                && isOAuthValid;

        boolean enabled = Utility.domainFieldValid(server)
                && Utility.requiredFieldValid(port)
                && (!requireLogin
                || hasValidPasswordSettings || hasValidExternalAuthSettings);
                || hasValidPasswordSettings || hasValidExternalAuthSettings
                || hasValidOAuth2Settings);

        checkInvalidOAuthError(isAuthTypeOAuth, isOAuthValid);

        view.setNextButtonInOutgoingEnabled(enabled);
    }
@@ -1579,6 +1582,8 @@ public class AccountSetupPresenter implements AccountSetupContract.Presenter,
    private void updateViewFromAuthTypeInOutgoing(AuthType authType) {
        if (authType == AuthType.EXTERNAL) {
            view.setViewExternalInOutgoing();
        } else if (authType == AuthType.XOAUTH2) {
            view.setViewOAuth2InOutgoing();
        } else {
            view.setViewNotExternalInOutgoing();
        }
Loading