mirror of https://github.com/M66B/FairEmail.git
Use service authenticator
This commit is contained in:
parent
b5443dd4f9
commit
480eaa18f6
2
FAQ.md
2
FAQ.md
|
@ -1080,7 +1080,7 @@ This requires contact/account permissions and internet connectivity.
|
|||
|
||||
The error *... Authentication failed ... Account not found ...* means that a previously authorized Gmail account was removed from the device.
|
||||
|
||||
The errors *... Authentication failed ... No token on refresh ...* means that the Android account manager failed to refresh the authorization of a Gmail account.
|
||||
The errors *... Authentication failed ... No token ...* means that the Android account manager failed to refresh the authorization of a Gmail account.
|
||||
|
||||
The error *... Authentication failed ... Invalid credentials ... network error ...*
|
||||
means that the Android account manager was not able to refresh the authorization of a Gmail account due to problems with the internet connection
|
||||
|
|
|
@ -102,6 +102,9 @@ import javax.crypto.SecretKeyFactory;
|
|||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_GMAIL;
|
||||
import static eu.faircode.email.ServiceAuthenticator.TYPE_GOOGLE;
|
||||
|
||||
public class ActivitySetup extends ActivityBase implements FragmentManager.OnBackStackChangedListener {
|
||||
private View view;
|
||||
private DrawerLayout drawerLayout;
|
||||
|
@ -773,10 +776,10 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
|
|||
JSONObject jaccount = (JSONObject) jaccounts.get(a);
|
||||
EntityAccount account = EntityAccount.fromJSON(jaccount);
|
||||
|
||||
if (account.auth_type == EmailService.AUTH_TYPE_GMAIL) {
|
||||
if (account.auth_type == AUTH_TYPE_GMAIL) {
|
||||
AccountManager am = AccountManager.get(context);
|
||||
boolean found = false;
|
||||
for (Account google : am.getAccountsByType(EmailService.TYPE_GOOGLE))
|
||||
for (Account google : am.getAccountsByType(TYPE_GOOGLE))
|
||||
if (account.user.equals(google.name)) {
|
||||
found = true;
|
||||
break;
|
||||
|
|
|
@ -64,6 +64,8 @@ import java.text.NumberFormat;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_PASSWORD;
|
||||
|
||||
public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHolder> {
|
||||
private Fragment parentFragment;
|
||||
private boolean settings;
|
||||
|
@ -152,7 +154,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
|
|||
ivSync.setContentDescription(context.getString(account.synchronize ? R.string.title_legend_synchronize_on : R.string.title_legend_synchronize_off));
|
||||
|
||||
ivOAuth.setVisibility(
|
||||
settings && account.auth_type != EmailService.AUTH_TYPE_PASSWORD ? View.VISIBLE : View.GONE);
|
||||
settings && account.auth_type != AUTH_TYPE_PASSWORD ? View.VISIBLE : View.GONE);
|
||||
ivPrimary.setVisibility(account.primary ? View.VISIBLE : View.GONE);
|
||||
ivNotify.setVisibility(account.notify ? View.VISIBLE : View.GONE);
|
||||
|
||||
|
|
|
@ -55,6 +55,8 @@ import java.util.Comparator;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_PASSWORD;
|
||||
|
||||
public class AdapterIdentity extends RecyclerView.Adapter<AdapterIdentity.ViewHolder> {
|
||||
private Fragment parentFragment;
|
||||
private Context context;
|
||||
|
@ -123,7 +125,7 @@ public class AdapterIdentity extends RecyclerView.Adapter<AdapterIdentity.ViewHo
|
|||
ivSync.setImageResource(identity.synchronize ? R.drawable.twotone_sync_24 : R.drawable.twotone_sync_disabled_24);
|
||||
ivSync.setContentDescription(context.getString(identity.synchronize ? R.string.title_legend_synchronize_on : R.string.title_legend_synchronize_off));
|
||||
|
||||
ivOAuth.setVisibility(identity.auth_type == EmailService.AUTH_TYPE_PASSWORD ? View.GONE : View.VISIBLE);
|
||||
ivOAuth.setVisibility(identity.auth_type == AUTH_TYPE_PASSWORD ? View.GONE : View.VISIBLE);
|
||||
ivPrimary.setVisibility(identity.primary ? View.VISIBLE : View.GONE);
|
||||
ivGroup.setVisibility(identity.self ? View.GONE : View.VISIBLE);
|
||||
tvName.setText(identity.getDisplayName());
|
||||
|
|
|
@ -40,6 +40,8 @@ import javax.mail.internet.InternetAddress;
|
|||
import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory;
|
||||
import io.requery.android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_PASSWORD;
|
||||
|
||||
/*
|
||||
This file is part of FairEmail.
|
||||
|
||||
|
@ -1343,8 +1345,8 @@ public abstract class DB extends RoomDatabase {
|
|||
int previous_version = prefs.getInt("previous_version", -1);
|
||||
if (previous_version <= 848 && Helper.isPlayStoreInstall()) {
|
||||
// JavaMail didn't check server certificates
|
||||
db.execSQL("UPDATE account SET insecure = 1 WHERE auth_type = " + EmailService.AUTH_TYPE_PASSWORD);
|
||||
db.execSQL("UPDATE identity SET insecure = 1 WHERE auth_type = " + EmailService.AUTH_TYPE_PASSWORD);
|
||||
db.execSQL("UPDATE account SET insecure = 1 WHERE auth_type = " + AUTH_TYPE_PASSWORD);
|
||||
db.execSQL("UPDATE identity SET insecure = 1 WHERE auth_type = " + AUTH_TYPE_PASSWORD);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,9 +1,24 @@
|
|||
package eu.faircode.email;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
/*
|
||||
This file is part of FairEmail.
|
||||
|
||||
FairEmail is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FairEmail is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2018-2020 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.security.KeyChain;
|
||||
|
@ -19,13 +34,6 @@ import com.sun.mail.smtp.SMTPTransport;
|
|||
import com.sun.mail.util.MailConnectException;
|
||||
import com.sun.mail.util.SocketConnectException;
|
||||
|
||||
import net.openid.appauth.AuthState;
|
||||
import net.openid.appauth.AuthorizationException;
|
||||
import net.openid.appauth.AuthorizationService;
|
||||
import net.openid.appauth.ClientAuthentication;
|
||||
import net.openid.appauth.ClientSecretPost;
|
||||
import net.openid.appauth.NoClientAuthentication;
|
||||
|
||||
import org.bouncycastle.asn1.DEROctetString;
|
||||
import org.bouncycastle.asn1.x509.Extension;
|
||||
import org.bouncycastle.asn1.x509.GeneralName;
|
||||
|
@ -63,10 +71,10 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.mail.AuthenticationFailedException;
|
||||
import javax.mail.Authenticator;
|
||||
import javax.mail.Folder;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.NoSuchProviderException;
|
||||
|
@ -86,6 +94,9 @@ import javax.net.ssl.TrustManager;
|
|||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_GMAIL;
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_OAUTH;
|
||||
|
||||
// IMAP standards: https://imapwiki.org/Specs
|
||||
|
||||
public class EmailService implements AutoCloseable {
|
||||
|
@ -105,12 +116,6 @@ public class EmailService implements AutoCloseable {
|
|||
|
||||
private ExecutorService executor = Helper.getBackgroundExecutor(0, "mail");
|
||||
|
||||
static final String TYPE_GOOGLE = "com.google";
|
||||
|
||||
static final int AUTH_TYPE_PASSWORD = 1;
|
||||
static final int AUTH_TYPE_GMAIL = 2;
|
||||
static final int AUTH_TYPE_OAUTH = 3;
|
||||
|
||||
static final int PURPOSE_CHECK = 1;
|
||||
static final int PURPOSE_USE = 2;
|
||||
static final int PURPOSE_SEARCH = 3;
|
||||
|
@ -278,35 +283,50 @@ public class EmailService implements AutoCloseable {
|
|||
}
|
||||
|
||||
public void connect(EntityAccount account) throws MessagingException {
|
||||
String password = connect(
|
||||
connect(
|
||||
account.host, account.port,
|
||||
account.auth_type, account.provider,
|
||||
account.user, account.password,
|
||||
new ServiceAuthenticator.IAuthenticated() {
|
||||
@Override
|
||||
public void onPasswordChanged(String newPassword) {
|
||||
DB db = DB.getInstance(context);
|
||||
int count = db.account().setAccountPassword(account.id, newPassword);
|
||||
EntityLog.log(context, account.name + " token refreshed=" + count);
|
||||
}
|
||||
},
|
||||
account.certificate_alias, account.fingerprint);
|
||||
if (password != null) {
|
||||
DB db = DB.getInstance(context);
|
||||
int count = db.account().setAccountPassword(account.id, password);
|
||||
Log.i(account.name + " token refreshed=" + count);
|
||||
}
|
||||
}
|
||||
|
||||
public void connect(EntityIdentity identity) throws MessagingException {
|
||||
String password = connect(
|
||||
connect(
|
||||
identity.host, identity.port,
|
||||
identity.auth_type, identity.provider,
|
||||
identity.user, identity.password,
|
||||
new ServiceAuthenticator.IAuthenticated() {
|
||||
@Override
|
||||
public void onPasswordChanged(String newPassword) {
|
||||
DB db = DB.getInstance(context);
|
||||
int count = db.identity().setIdentityPassword(identity.id, newPassword);
|
||||
EntityLog.log(context, identity.email + " token refreshed=" + count);
|
||||
|
||||
}
|
||||
},
|
||||
identity.certificate_alias, identity.fingerprint);
|
||||
if (password != null) {
|
||||
DB db = DB.getInstance(context);
|
||||
int count = db.identity().setIdentityPassword(identity.id, password);
|
||||
Log.i(identity.email + " token refreshed=" + count);
|
||||
}
|
||||
}
|
||||
|
||||
public String connect(
|
||||
public void connect(
|
||||
String host, int port,
|
||||
int auth, String provider, String user, String password,
|
||||
String certificate, String fingerprint) throws MessagingException {
|
||||
connect(host, port, auth, provider, user, password, null, certificate, fingerprint);
|
||||
}
|
||||
|
||||
private void connect(
|
||||
String host, int port,
|
||||
int auth, String provider, String user, String password,
|
||||
ServiceAuthenticator.IAuthenticated intf,
|
||||
String certificate, String fingerprint) throws MessagingException {
|
||||
SSLSocketFactoryService factory = null;
|
||||
try {
|
||||
PrivateKey key = null;
|
||||
|
@ -332,35 +352,27 @@ public class EmailService implements AutoCloseable {
|
|||
Log.e("Trust issues", ex);
|
||||
}
|
||||
|
||||
properties.put("mail." + protocol + ".forcepasswordrefresh", "true");
|
||||
ServiceAuthenticator authenticator = new ServiceAuthenticator(context, auth, provider, user, password, intf);
|
||||
|
||||
try {
|
||||
if (auth == AUTH_TYPE_GMAIL || auth == AUTH_TYPE_OAUTH)
|
||||
properties.put("mail." + protocol + ".auth.mechanisms", "XOAUTH2");
|
||||
|
||||
if (auth == AUTH_TYPE_OAUTH) {
|
||||
if ("imap.mail.yahoo.com".equals(host))
|
||||
properties.put("mail." + protocol + ".yahoo.guid", "FAIRMAIL_V1");
|
||||
AuthState authState = OAuthRefresh(context, provider, password);
|
||||
connect(host, port, auth, user, authState.getAccessToken(), factory);
|
||||
return authState.jsonSerializeString();
|
||||
} else {
|
||||
connect(host, port, auth, user, password, factory);
|
||||
return null;
|
||||
}
|
||||
if (auth == AUTH_TYPE_OAUTH && "imap.mail.yahoo.com".equals(host))
|
||||
properties.put("mail." + protocol + ".yahoo.guid", "FAIRMAIL_V1");
|
||||
|
||||
connect(host, port, auth, user, authenticator, factory);
|
||||
} catch (AuthenticationFailedException ex) {
|
||||
// Refresh token
|
||||
if (auth == AUTH_TYPE_GMAIL)
|
||||
if (auth == AUTH_TYPE_GMAIL || auth == AUTH_TYPE_OAUTH) {
|
||||
try {
|
||||
String token = GmailRefresh(context, user, password);
|
||||
connect(host, port, auth, user, token, factory);
|
||||
return token;
|
||||
authenticator.expire();
|
||||
connect(host, port, auth, user, authenticator, factory);
|
||||
} catch (Exception ex1) {
|
||||
Log.e(ex1);
|
||||
throw new AuthenticationFailedException(ex.getMessage(), ex1);
|
||||
}
|
||||
else if (auth == AUTH_TYPE_OAUTH) {
|
||||
AuthState authState = OAuthRefresh(context, provider, password);
|
||||
connect(host, port, auth, user, authState.getAccessToken(), factory);
|
||||
return authState.jsonSerializeString();
|
||||
} else if (purpose == PURPOSE_CHECK) {
|
||||
String msg = ex.getMessage();
|
||||
if (msg != null)
|
||||
|
@ -407,7 +419,7 @@ public class EmailService implements AutoCloseable {
|
|||
|
||||
private void connect(
|
||||
String host, int port, int auth,
|
||||
String user, String password,
|
||||
String user, Authenticator authenticator,
|
||||
SSLSocketFactoryService factory) throws MessagingException {
|
||||
InetAddress main = null;
|
||||
boolean require_id = (purpose == PURPOSE_CHECK &&
|
||||
|
@ -421,7 +433,7 @@ public class EmailService implements AutoCloseable {
|
|||
|
||||
main = InetAddress.getByName(host);
|
||||
EntityLog.log(context, "Connecting to " + main);
|
||||
_connect(main, port, require_id, user, password, factory);
|
||||
_connect(main, port, require_id, user, authenticator, factory);
|
||||
} catch (UnknownHostException ex) {
|
||||
throw new MessagingException(ex.getMessage(), ex);
|
||||
} catch (MessagingException ex) {
|
||||
|
@ -486,7 +498,7 @@ public class EmailService implements AutoCloseable {
|
|||
|
||||
try {
|
||||
EntityLog.log(context, "Falling back to " + iaddr);
|
||||
_connect(iaddr, port, require_id, user, password, factory);
|
||||
_connect(iaddr, port, require_id, user, authenticator, factory);
|
||||
return;
|
||||
} catch (MessagingException ex1) {
|
||||
ex = ex1;
|
||||
|
@ -505,9 +517,9 @@ public class EmailService implements AutoCloseable {
|
|||
|
||||
private void _connect(
|
||||
InetAddress address, int port, boolean require_id,
|
||||
String user, String password,
|
||||
String user, Authenticator authenticator,
|
||||
SSLSocketFactoryService factory) throws MessagingException {
|
||||
isession = Session.getInstance(properties, null);
|
||||
isession = Session.getInstance(properties, authenticator);
|
||||
|
||||
isession.setDebug(debug || log);
|
||||
if (debug || log)
|
||||
|
@ -538,13 +550,13 @@ public class EmailService implements AutoCloseable {
|
|||
|
||||
if ("pop3".equals(protocol) || "pop3s".equals(protocol)) {
|
||||
iservice = isession.getStore(protocol);
|
||||
iservice.connect(address.getHostAddress(), port, user, password);
|
||||
iservice.connect(address.getHostAddress(), port, user, null);
|
||||
|
||||
} else if ("imap".equals(protocol) || "imaps".equals(protocol) || "gimaps".equals(protocol)) {
|
||||
iservice = isession.getStore(protocol);
|
||||
if (listener != null)
|
||||
((IMAPStore) iservice).addStoreListener(listener);
|
||||
iservice.connect(address.getHostAddress(), port, user, password);
|
||||
iservice.connect(address.getHostAddress(), port, user, null);
|
||||
|
||||
// https://www.ietf.org/rfc/rfc2971.txt
|
||||
IMAPStore istore = (IMAPStore) getStore();
|
||||
|
@ -580,13 +592,13 @@ public class EmailService implements AutoCloseable {
|
|||
|
||||
iservice = isession.getTransport(protocol);
|
||||
try {
|
||||
iservice.connect(address.getHostAddress(), port, user, password);
|
||||
iservice.connect(address.getHostAddress(), port, user, null);
|
||||
} catch (MessagingException ex) {
|
||||
if (ehlo == null && ConnectionHelper.isSyntacticallyInvalid(ex)) {
|
||||
properties.put("mail." + protocol + ".localhost", useip ? hdomain : haddr);
|
||||
Log.i("Fallback localhost=" + properties.getProperty("mail." + protocol + ".localhost"));
|
||||
try {
|
||||
iservice.connect(address.getHostAddress(), port, user, password);
|
||||
iservice.connect(address.getHostAddress(), port, user, null);
|
||||
} catch (MessagingException ex1) {
|
||||
if (ConnectionHelper.isSyntacticallyInvalid(ex1))
|
||||
Log.e("Used localhost=" + haddr + "/" + hdomain);
|
||||
|
@ -605,75 +617,6 @@ public class EmailService implements AutoCloseable {
|
|||
return TextUtils.join(".", c);
|
||||
}
|
||||
|
||||
private static class ErrorHolder {
|
||||
AuthorizationException error;
|
||||
}
|
||||
|
||||
private static String GmailRefresh(Context context, String user, String password) throws AuthenticatorException, OperationCanceledException, IOException {
|
||||
AccountManager am = AccountManager.get(context);
|
||||
Account[] accounts = am.getAccountsByType(TYPE_GOOGLE);
|
||||
for (Account account : accounts)
|
||||
if (user.equals(account.name)) {
|
||||
Log.i("Refreshing token user=" + user);
|
||||
if (password != null)
|
||||
am.invalidateAuthToken(TYPE_GOOGLE, password);
|
||||
String token = am.blockingGetAuthToken(account, getAuthTokenType(TYPE_GOOGLE), true);
|
||||
if (token == null)
|
||||
throw new AuthenticatorException("No token on refresh for " + user);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
throw new AuthenticatorException("Account not found for " + user);
|
||||
}
|
||||
|
||||
private static AuthState OAuthRefresh(Context context, String id, String json) throws MessagingException {
|
||||
try {
|
||||
AuthState authState = AuthState.jsonDeserialize(json);
|
||||
|
||||
ClientAuthentication clientAuth;
|
||||
EmailProvider provider = EmailProvider.getProvider(context, id);
|
||||
if (provider.oauth.clientSecret == null)
|
||||
clientAuth = NoClientAuthentication.INSTANCE;
|
||||
else
|
||||
clientAuth = new ClientSecretPost(provider.oauth.clientSecret);
|
||||
|
||||
ErrorHolder holder = new ErrorHolder();
|
||||
Semaphore semaphore = new Semaphore(0);
|
||||
|
||||
Log.i("OAuth refresh");
|
||||
AuthorizationService authService = new AuthorizationService(context);
|
||||
authState.performActionWithFreshTokens(
|
||||
authService,
|
||||
clientAuth,
|
||||
new AuthState.AuthStateAction() {
|
||||
@Override
|
||||
public void execute(String accessToken, String idToken, AuthorizationException error) {
|
||||
if (error != null)
|
||||
holder.error = error;
|
||||
semaphore.release();
|
||||
}
|
||||
});
|
||||
|
||||
semaphore.acquire();
|
||||
Log.i("OAuth refreshed");
|
||||
|
||||
if (holder.error != null)
|
||||
throw holder.error;
|
||||
|
||||
return authState;
|
||||
} catch (Exception ex) {
|
||||
throw new MessagingException("OAuth refresh", ex);
|
||||
}
|
||||
}
|
||||
|
||||
static String getAuthTokenType(String type) {
|
||||
// https://developers.google.com/gmail/imap/xoauth2-protocol
|
||||
if ("com.google".equals(type))
|
||||
return "oauth2:https://mail.google.com/";
|
||||
return null;
|
||||
}
|
||||
|
||||
List<EntityFolder> getFolders() throws MessagingException {
|
||||
List<EntityFolder> folders = new ArrayList<>();
|
||||
|
||||
|
|
|
@ -19,8 +19,6 @@ package eu.faircode.email;
|
|||
Copyright 2018-2020 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
@ -74,6 +72,9 @@ import javax.mail.Folder;
|
|||
import static android.app.Activity.RESULT_OK;
|
||||
import static com.google.android.material.textfield.TextInputLayout.END_ICON_NONE;
|
||||
import static com.google.android.material.textfield.TextInputLayout.END_ICON_PASSWORD_TOGGLE;
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_GMAIL;
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_OAUTH;
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_PASSWORD;
|
||||
|
||||
public class FragmentAccount extends FragmentBase {
|
||||
private ViewGroup view;
|
||||
|
@ -153,7 +154,7 @@ public class FragmentAccount extends FragmentBase {
|
|||
|
||||
private long id = -1;
|
||||
private long copy = -1;
|
||||
private int auth = EmailService.AUTH_TYPE_PASSWORD;
|
||||
private int auth = AUTH_TYPE_PASSWORD;
|
||||
private String provider = null;
|
||||
private String certificate = null;
|
||||
private boolean saving = false;
|
||||
|
@ -261,7 +262,7 @@ public class FragmentAccount extends FragmentBase {
|
|||
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long itemid) {
|
||||
EmailProvider provider = (EmailProvider) adapterView.getSelectedItem();
|
||||
tvGmailHint.setVisibility(
|
||||
auth == EmailService.AUTH_TYPE_PASSWORD && "gmail".equals(provider.id)
|
||||
auth == AUTH_TYPE_PASSWORD && "gmail".equals(provider.id)
|
||||
? View.VISIBLE : View.GONE);
|
||||
grpServer.setVisibility(position > 0 ? View.VISIBLE : View.GONE);
|
||||
grpAuthorize.setVisibility(position > 0 ? View.VISIBLE : View.GONE);
|
||||
|
@ -1333,16 +1334,7 @@ public class FragmentAccount extends FragmentBase {
|
|||
if (account == null)
|
||||
return null;
|
||||
|
||||
AccountManager am = AccountManager.get(context);
|
||||
Account[] accounts = am.getAccountsByType(EmailService.TYPE_GOOGLE);
|
||||
for (Account google : accounts)
|
||||
if (account.user.equals(google.name))
|
||||
return am.blockingGetAuthToken(
|
||||
google,
|
||||
EmailService.getAuthTokenType(EmailService.TYPE_GOOGLE),
|
||||
true);
|
||||
|
||||
return null;
|
||||
return ServiceAuthenticator.getGmailToken(context, account.user);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1449,7 +1441,7 @@ public class FragmentAccount extends FragmentBase {
|
|||
boolean found = false;
|
||||
for (int pos = 2; pos < providers.size(); pos++) {
|
||||
EmailProvider provider = providers.get(pos);
|
||||
if ((provider.oauth != null) == (account.auth_type == EmailService.AUTH_TYPE_OAUTH) &&
|
||||
if ((provider.oauth != null) == (account.auth_type == AUTH_TYPE_OAUTH) &&
|
||||
provider.imap.host.equals(account.host) &&
|
||||
provider.imap.port == account.port &&
|
||||
provider.imap.starttls == (account.encryption == EmailService.ENCRYPTION_STARTTLS)) {
|
||||
|
@ -1514,7 +1506,7 @@ public class FragmentAccount extends FragmentBase {
|
|||
else
|
||||
rgDate.check(R.id.radio_server_time);
|
||||
|
||||
auth = (account == null ? EmailService.AUTH_TYPE_PASSWORD : account.auth_type);
|
||||
auth = (account == null ? AUTH_TYPE_PASSWORD : account.auth_type);
|
||||
provider = (account == null ? null : account.provider);
|
||||
|
||||
new SimpleTask<EntityAccount>() {
|
||||
|
@ -1550,13 +1542,13 @@ public class FragmentAccount extends FragmentBase {
|
|||
|
||||
Helper.setViewsEnabled(view, true);
|
||||
|
||||
if (auth != EmailService.AUTH_TYPE_PASSWORD) {
|
||||
if (auth != AUTH_TYPE_PASSWORD) {
|
||||
etUser.setEnabled(false);
|
||||
tilPassword.setEnabled(false);
|
||||
btnCertificate.setEnabled(false);
|
||||
}
|
||||
|
||||
if (account == null || account.auth_type != EmailService.AUTH_TYPE_GMAIL)
|
||||
if (account == null || account.auth_type != AUTH_TYPE_GMAIL)
|
||||
Helper.hide((btnOAuth));
|
||||
|
||||
cbOnDemand.setEnabled(cbSynchronize.isChecked());
|
||||
|
|
|
@ -56,6 +56,8 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_PASSWORD;
|
||||
|
||||
public class FragmentAccounts extends FragmentBase {
|
||||
private boolean settings;
|
||||
|
||||
|
@ -267,7 +269,7 @@ public class FragmentAccounts extends FragmentBase {
|
|||
|
||||
boolean authorized = true;
|
||||
for (TupleAccountEx account : accounts)
|
||||
if (account.auth_type != EmailService.AUTH_TYPE_PASSWORD &&
|
||||
if (account.auth_type != AUTH_TYPE_PASSWORD &&
|
||||
!Helper.hasPermissions(getContext(), Helper.getOAuthPermissions())) {
|
||||
authorized = false;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ import java.util.Map;
|
|||
|
||||
import static android.accounts.AccountManager.newChooseAccountIntent;
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_GMAIL;
|
||||
|
||||
public class FragmentGmail extends FragmentBase {
|
||||
private ViewGroup view;
|
||||
|
@ -264,7 +265,7 @@ public class FragmentGmail extends FragmentBase {
|
|||
Log.i("Requesting token name=" + account.name);
|
||||
am.getAuthToken(
|
||||
account,
|
||||
EmailService.getAuthTokenType(type),
|
||||
ServiceAuthenticator.getAuthTokenType(type),
|
||||
new Bundle(),
|
||||
getActivity(),
|
||||
new AccountManagerCallback<Bundle>() {
|
||||
|
@ -369,7 +370,7 @@ public class FragmentGmail extends FragmentBase {
|
|||
EmailService.PURPOSE_CHECK, true)) {
|
||||
iservice.connect(
|
||||
provider.imap.host, provider.imap.port,
|
||||
EmailService.AUTH_TYPE_GMAIL, null,
|
||||
AUTH_TYPE_GMAIL, null,
|
||||
user, password,
|
||||
null, null);
|
||||
|
||||
|
@ -384,7 +385,7 @@ public class FragmentGmail extends FragmentBase {
|
|||
EmailService.PURPOSE_CHECK, true)) {
|
||||
iservice.connect(
|
||||
provider.smtp.host, provider.smtp.port,
|
||||
EmailService.AUTH_TYPE_GMAIL, null,
|
||||
AUTH_TYPE_GMAIL, null,
|
||||
user, password,
|
||||
null, null);
|
||||
max_size = iservice.getMaxSize();
|
||||
|
@ -402,7 +403,7 @@ public class FragmentGmail extends FragmentBase {
|
|||
account.host = provider.imap.host;
|
||||
account.encryption = aencryption;
|
||||
account.port = provider.imap.port;
|
||||
account.auth_type = EmailService.AUTH_TYPE_GMAIL;
|
||||
account.auth_type = AUTH_TYPE_GMAIL;
|
||||
account.user = user;
|
||||
account.password = password;
|
||||
|
||||
|
@ -451,7 +452,7 @@ public class FragmentGmail extends FragmentBase {
|
|||
identity.host = provider.smtp.host;
|
||||
identity.encryption = iencryption;
|
||||
identity.port = provider.smtp.port;
|
||||
identity.auth_type = EmailService.AUTH_TYPE_GMAIL;
|
||||
identity.auth_type = AUTH_TYPE_GMAIL;
|
||||
identity.user = user;
|
||||
identity.password = password;
|
||||
identity.synchronize = true;
|
||||
|
|
|
@ -19,8 +19,6 @@ package eu.faircode.email;
|
|||
Copyright 2018-2020 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
|
@ -68,6 +66,9 @@ import javax.mail.internet.InternetAddress;
|
|||
import static android.app.Activity.RESULT_OK;
|
||||
import static com.google.android.material.textfield.TextInputLayout.END_ICON_NONE;
|
||||
import static com.google.android.material.textfield.TextInputLayout.END_ICON_PASSWORD_TOGGLE;
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_GMAIL;
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_OAUTH;
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_PASSWORD;
|
||||
|
||||
public class FragmentIdentity extends FragmentBase {
|
||||
private ViewGroup view;
|
||||
|
@ -131,7 +132,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
private long id = -1;
|
||||
private long copy = -1;
|
||||
private long account = -1;
|
||||
private int auth = EmailService.AUTH_TYPE_PASSWORD;
|
||||
private int auth = AUTH_TYPE_PASSWORD;
|
||||
private String provider = null;
|
||||
private String certificate = null;
|
||||
private String signature = null;
|
||||
|
@ -498,9 +499,9 @@ public class FragmentIdentity extends FragmentBase {
|
|||
etRealm.setText(account.realm);
|
||||
cbTrust.setChecked(false);
|
||||
|
||||
etUser.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
|
||||
tilPassword.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
|
||||
btnCertificate.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
|
||||
etUser.setEnabled(auth == AUTH_TYPE_PASSWORD);
|
||||
tilPassword.setEnabled(auth == AUTH_TYPE_PASSWORD);
|
||||
btnCertificate.setEnabled(auth == AUTH_TYPE_PASSWORD);
|
||||
}
|
||||
|
||||
private void setProvider(EmailProvider provider) {
|
||||
|
@ -986,16 +987,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
if (identity == null)
|
||||
return null;
|
||||
|
||||
AccountManager am = AccountManager.get(context);
|
||||
Account[] accounts = am.getAccountsByType(EmailService.TYPE_GOOGLE);
|
||||
for (Account google : accounts)
|
||||
if (identity.user.equals(google.name))
|
||||
return am.blockingGetAuthToken(
|
||||
google,
|
||||
EmailService.getAuthTokenType(EmailService.TYPE_GOOGLE),
|
||||
true);
|
||||
|
||||
return null;
|
||||
return ServiceAuthenticator.getGmailToken(context, identity.user);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1137,7 +1129,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
etBcc.setText(identity == null ? null : identity.bcc);
|
||||
cbUnicode.setChecked(identity != null && identity.unicode);
|
||||
|
||||
auth = (identity == null ? EmailService.AUTH_TYPE_PASSWORD : identity.auth_type);
|
||||
auth = (identity == null ? AUTH_TYPE_PASSWORD : identity.auth_type);
|
||||
provider = (identity == null ? null : identity.provider);
|
||||
|
||||
if (identity == null || copy > 0)
|
||||
|
@ -1171,13 +1163,13 @@ public class FragmentIdentity extends FragmentBase {
|
|||
|
||||
Helper.setViewsEnabled(view, true);
|
||||
|
||||
if (auth != EmailService.AUTH_TYPE_PASSWORD) {
|
||||
if (auth != AUTH_TYPE_PASSWORD) {
|
||||
etUser.setEnabled(false);
|
||||
tilPassword.setEnabled(false);
|
||||
btnCertificate.setEnabled(false);
|
||||
}
|
||||
|
||||
if (identity == null || identity.auth_type != EmailService.AUTH_TYPE_GMAIL)
|
||||
if (identity == null || identity.auth_type != AUTH_TYPE_GMAIL)
|
||||
Helper.hide(btnOAuth);
|
||||
|
||||
cbPrimary.setEnabled(cbSynchronize.isChecked());
|
||||
|
@ -1197,7 +1189,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
if (identity != null)
|
||||
for (int pos = 1; pos < providers.size(); pos++) {
|
||||
EmailProvider provider = providers.get(pos);
|
||||
if ((provider.oauth != null) == (identity.auth_type == EmailService.AUTH_TYPE_OAUTH) &&
|
||||
if ((provider.oauth != null) == (identity.auth_type == AUTH_TYPE_OAUTH) &&
|
||||
provider.smtp.host.equals(identity.host) &&
|
||||
provider.smtp.port == identity.port &&
|
||||
provider.smtp.starttls == (identity.encryption == EmailService.ENCRYPTION_STARTTLS)) {
|
||||
|
@ -1226,7 +1218,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
|
||||
EntityAccount unselected = new EntityAccount();
|
||||
unselected.id = -1L;
|
||||
unselected.auth_type = EmailService.AUTH_TYPE_PASSWORD;
|
||||
unselected.auth_type = AUTH_TYPE_PASSWORD;
|
||||
unselected.name = getString(R.string.title_select);
|
||||
unselected.primary = false;
|
||||
accounts.add(0, unselected);
|
||||
|
|
|
@ -77,6 +77,7 @@ import java.util.Map;
|
|||
import javax.mail.AuthenticationFailedException;
|
||||
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_OAUTH;
|
||||
|
||||
public class FragmentOAuth extends FragmentBase {
|
||||
private String id;
|
||||
|
@ -434,7 +435,7 @@ public class FragmentOAuth extends FragmentBase {
|
|||
EmailService.PURPOSE_CHECK, true)) {
|
||||
iservice.connect(
|
||||
provider.imap.host, provider.imap.port,
|
||||
EmailService.AUTH_TYPE_OAUTH, provider.id,
|
||||
AUTH_TYPE_OAUTH, provider.id,
|
||||
unique_name, state,
|
||||
null, null);
|
||||
username = unique_name;
|
||||
|
@ -486,7 +487,7 @@ public class FragmentOAuth extends FragmentBase {
|
|||
EmailService.PURPOSE_CHECK, true)) {
|
||||
iservice.connect(
|
||||
provider.imap.host, provider.imap.port,
|
||||
EmailService.AUTH_TYPE_OAUTH, provider.id,
|
||||
AUTH_TYPE_OAUTH, provider.id,
|
||||
username, state,
|
||||
null, null);
|
||||
|
||||
|
@ -503,7 +504,7 @@ public class FragmentOAuth extends FragmentBase {
|
|||
EmailService.PURPOSE_CHECK, true)) {
|
||||
iservice.connect(
|
||||
provider.smtp.host, provider.smtp.port,
|
||||
EmailService.AUTH_TYPE_OAUTH, provider.id,
|
||||
AUTH_TYPE_OAUTH, provider.id,
|
||||
username, state,
|
||||
null, null);
|
||||
max_size = iservice.getMaxSize();
|
||||
|
@ -523,7 +524,7 @@ public class FragmentOAuth extends FragmentBase {
|
|||
account.host = provider.imap.host;
|
||||
account.encryption = aencryption;
|
||||
account.port = provider.imap.port;
|
||||
account.auth_type = EmailService.AUTH_TYPE_OAUTH;
|
||||
account.auth_type = AUTH_TYPE_OAUTH;
|
||||
account.provider = provider.id;
|
||||
account.user = username;
|
||||
account.password = state;
|
||||
|
@ -579,7 +580,7 @@ public class FragmentOAuth extends FragmentBase {
|
|||
ident.host = provider.smtp.host;
|
||||
ident.encryption = iencryption;
|
||||
ident.port = provider.smtp.port;
|
||||
ident.auth_type = EmailService.AUTH_TYPE_OAUTH;
|
||||
ident.auth_type = AUTH_TYPE_OAUTH;
|
||||
ident.provider = provider.id;
|
||||
ident.user = username;
|
||||
ident.password = state;
|
||||
|
|
|
@ -61,6 +61,7 @@ import java.util.Objects;
|
|||
import static android.app.Activity.RESULT_OK;
|
||||
import static com.google.android.material.textfield.TextInputLayout.END_ICON_NONE;
|
||||
import static com.google.android.material.textfield.TextInputLayout.END_ICON_PASSWORD_TOGGLE;
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_PASSWORD;
|
||||
|
||||
public class FragmentPop extends FragmentBase {
|
||||
private ViewGroup view;
|
||||
|
@ -383,7 +384,7 @@ public class FragmentPop extends FragmentBase {
|
|||
EmailService.PURPOSE_CHECK, true)) {
|
||||
iservice.connect(
|
||||
host, Integer.parseInt(port),
|
||||
EmailService.AUTH_TYPE_PASSWORD, null,
|
||||
AUTH_TYPE_PASSWORD, null,
|
||||
user, password,
|
||||
null, null);
|
||||
}
|
||||
|
@ -408,7 +409,7 @@ public class FragmentPop extends FragmentBase {
|
|||
account.encryption = encryption;
|
||||
account.insecure = insecure;
|
||||
account.port = Integer.parseInt(port);
|
||||
account.auth_type = EmailService.AUTH_TYPE_PASSWORD;
|
||||
account.auth_type = AUTH_TYPE_PASSWORD;
|
||||
account.user = user;
|
||||
account.password = password;
|
||||
|
||||
|
|
|
@ -54,6 +54,8 @@ import java.util.List;
|
|||
|
||||
import javax.mail.AuthenticationFailedException;
|
||||
|
||||
import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_PASSWORD;
|
||||
|
||||
public class FragmentQuickSetup extends FragmentBase {
|
||||
private ViewGroup view;
|
||||
private ScrollView scroll;
|
||||
|
@ -303,7 +305,7 @@ public class FragmentQuickSetup extends FragmentBase {
|
|||
try {
|
||||
iservice.connect(
|
||||
provider.imap.host, provider.imap.port,
|
||||
EmailService.AUTH_TYPE_PASSWORD, null,
|
||||
AUTH_TYPE_PASSWORD, null,
|
||||
user, password,
|
||||
null, imap_fingerprint);
|
||||
} catch (EmailService.UntrustedException ex) {
|
||||
|
@ -311,7 +313,7 @@ public class FragmentQuickSetup extends FragmentBase {
|
|||
imap_fingerprint = ex.getFingerprint();
|
||||
iservice.connect(
|
||||
provider.imap.host, provider.imap.port,
|
||||
EmailService.AUTH_TYPE_PASSWORD, null,
|
||||
AUTH_TYPE_PASSWORD, null,
|
||||
user, password,
|
||||
null, imap_fingerprint);
|
||||
} else
|
||||
|
@ -328,7 +330,7 @@ public class FragmentQuickSetup extends FragmentBase {
|
|||
Log.i("Retry with user=" + user);
|
||||
iservice.connect(
|
||||
provider.imap.host, provider.imap.port,
|
||||
EmailService.AUTH_TYPE_PASSWORD, null,
|
||||
AUTH_TYPE_PASSWORD, null,
|
||||
user, password,
|
||||
null, null);
|
||||
} catch (Throwable ex1) {
|
||||
|
@ -353,7 +355,7 @@ public class FragmentQuickSetup extends FragmentBase {
|
|||
iservice.setUseIp(provider.useip, null);
|
||||
iservice.connect(
|
||||
provider.smtp.host, provider.smtp.port,
|
||||
EmailService.AUTH_TYPE_PASSWORD, null,
|
||||
AUTH_TYPE_PASSWORD, null,
|
||||
user, password,
|
||||
null, smtp_fingerprint);
|
||||
max_size = iservice.getMaxSize();
|
||||
|
@ -382,7 +384,7 @@ public class FragmentQuickSetup extends FragmentBase {
|
|||
account.host = provider.imap.host;
|
||||
account.encryption = aencryption;
|
||||
account.port = provider.imap.port;
|
||||
account.auth_type = EmailService.AUTH_TYPE_PASSWORD;
|
||||
account.auth_type = AUTH_TYPE_PASSWORD;
|
||||
account.user = user;
|
||||
account.password = password;
|
||||
account.fingerprint = imap_fingerprint;
|
||||
|
@ -432,7 +434,7 @@ public class FragmentQuickSetup extends FragmentBase {
|
|||
identity.host = provider.smtp.host;
|
||||
identity.encryption = iencryption;
|
||||
identity.port = provider.smtp.port;
|
||||
identity.auth_type = EmailService.AUTH_TYPE_PASSWORD;
|
||||
identity.auth_type = AUTH_TYPE_PASSWORD;
|
||||
identity.user = user;
|
||||
identity.password = password;
|
||||
identity.fingerprint = smtp_fingerprint;
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
package eu.faircode.email;
|
||||
|
||||
/*
|
||||
This file is part of FairEmail.
|
||||
|
||||
FairEmail is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FairEmail is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2018-2020 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.content.Context;
|
||||
|
||||
import net.openid.appauth.AuthState;
|
||||
import net.openid.appauth.AuthorizationException;
|
||||
import net.openid.appauth.AuthorizationService;
|
||||
import net.openid.appauth.ClientAuthentication;
|
||||
import net.openid.appauth.ClientSecretPost;
|
||||
import net.openid.appauth.NoClientAuthentication;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import javax.mail.Authenticator;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.PasswordAuthentication;
|
||||
|
||||
class ServiceAuthenticator extends Authenticator {
|
||||
private Context context;
|
||||
private int auth;
|
||||
private String provider;
|
||||
private String user;
|
||||
private String password;
|
||||
private IAuthenticated intf;
|
||||
|
||||
static final int AUTH_TYPE_PASSWORD = 1;
|
||||
static final int AUTH_TYPE_GMAIL = 2;
|
||||
static final int AUTH_TYPE_OAUTH = 3;
|
||||
|
||||
static final String TYPE_GOOGLE = "com.google";
|
||||
|
||||
ServiceAuthenticator(
|
||||
Context context,
|
||||
int auth, String provider,
|
||||
String user, String password,
|
||||
IAuthenticated intf) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.auth = auth;
|
||||
this.provider = provider;
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
this.intf = intf;
|
||||
}
|
||||
|
||||
void expire() {
|
||||
if (auth == AUTH_TYPE_GMAIL) {
|
||||
EntityLog.log(context, user + " token expired");
|
||||
expireGmailToken(context, password);
|
||||
password = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
String token = password;
|
||||
try {
|
||||
if (auth == AUTH_TYPE_GMAIL) {
|
||||
String oldToken = password;
|
||||
token = getGmailToken(context, user);
|
||||
password = token;
|
||||
if (intf != null && !Objects.equals(oldToken, token))
|
||||
intf.onPasswordChanged(password);
|
||||
} else if (auth == AUTH_TYPE_OAUTH) {
|
||||
AuthState authState = AuthState.jsonDeserialize(password);
|
||||
String oldToken = authState.getAccessToken();
|
||||
OAuthRefresh(context, provider, authState);
|
||||
token = authState.getAccessToken();
|
||||
password = authState.jsonSerializeString();
|
||||
if (intf != null && !Objects.equals(oldToken, token))
|
||||
intf.onPasswordChanged(password);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
Log.e(ex);
|
||||
}
|
||||
Log.i(user + " returning password");
|
||||
return new PasswordAuthentication(user, token);
|
||||
}
|
||||
|
||||
interface IAuthenticated {
|
||||
void onPasswordChanged(String newPassword);
|
||||
}
|
||||
|
||||
static String getGmailToken(Context context, String user) throws AuthenticatorException, OperationCanceledException, IOException {
|
||||
AccountManager am = AccountManager.get(context);
|
||||
Account[] accounts = am.getAccountsByType(TYPE_GOOGLE);
|
||||
for (Account account : accounts)
|
||||
if (user.equals(account.name)) {
|
||||
Log.i("Getting token user=" + user);
|
||||
String token = am.blockingGetAuthToken(account, getAuthTokenType(TYPE_GOOGLE), true);
|
||||
if (token == null)
|
||||
throw new AuthenticatorException("No token for " + user);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
throw new AuthenticatorException("Account not found for " + user);
|
||||
}
|
||||
|
||||
private static void expireGmailToken(Context context, String token) {
|
||||
try {
|
||||
AccountManager am = AccountManager.get(context);
|
||||
am.invalidateAuthToken(TYPE_GOOGLE, token);
|
||||
} catch (Throwable ex) {
|
||||
Log.e(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OAuthRefresh(Context context, String id, AuthState authState) throws MessagingException {
|
||||
try {
|
||||
ClientAuthentication clientAuth;
|
||||
EmailProvider provider = EmailProvider.getProvider(context, id);
|
||||
if (provider.oauth.clientSecret == null)
|
||||
clientAuth = NoClientAuthentication.INSTANCE;
|
||||
else
|
||||
clientAuth = new ClientSecretPost(provider.oauth.clientSecret);
|
||||
|
||||
ErrorHolder holder = new ErrorHolder();
|
||||
Semaphore semaphore = new Semaphore(0);
|
||||
|
||||
Log.i("OAuth refresh");
|
||||
AuthorizationService authService = new AuthorizationService(context);
|
||||
authState.performActionWithFreshTokens(
|
||||
authService,
|
||||
clientAuth,
|
||||
new AuthState.AuthStateAction() {
|
||||
@Override
|
||||
public void execute(String accessToken, String idToken, AuthorizationException error) {
|
||||
if (error != null)
|
||||
holder.error = error;
|
||||
semaphore.release();
|
||||
}
|
||||
});
|
||||
|
||||
semaphore.acquire();
|
||||
Log.i("OAuth refreshed");
|
||||
|
||||
if (holder.error != null)
|
||||
throw holder.error;
|
||||
} catch (Exception ex) {
|
||||
throw new MessagingException("OAuth refresh", ex);
|
||||
}
|
||||
}
|
||||
|
||||
static String getAuthTokenType(String type) {
|
||||
// https://developers.google.com/gmail/imap/xoauth2-protocol
|
||||
if ("com.google".equals(type))
|
||||
return "oauth2:https://mail.google.com/";
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class ErrorHolder {
|
||||
AuthorizationException error;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue