Added/storing auth provider id

This commit is contained in:
M66B 2019-12-21 16:03:57 +01:00
parent e80bc630e1
commit 02dfb75542
14 changed files with 2271 additions and 198 deletions

File diff suppressed because it is too large Load Diff

View File

@ -56,7 +56,7 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory;
// https://developer.android.com/topic/libraries/architecture/room.html
@Database(
version = 123,
version = 124,
entities = {
EntityIdentity.class,
EntityAccount.class,
@ -1196,6 +1196,14 @@ public abstract class DB extends RoomDatabase {
db.execSQL("ALTER TABLE `identity` ADD COLUMN `fingerprint` TEXT");
}
})
.addMigrations(new Migration(123, 124) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `account` ADD COLUMN `provider` TEXT");
db.execSQL("ALTER TABLE `identity` ADD COLUMN `provider` TEXT");
}
})
.build();
}

View File

@ -36,6 +36,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
@ -57,6 +58,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
public class EmailProvider {
public String id;
public String name;
public List<String> domain;
public int order;
@ -107,6 +109,7 @@ public class EmailProvider {
result = new ArrayList<>();
else if ("provider".equals(name)) {
provider = new EmailProvider();
provider.id = xml.getAttributeValue(null, "id");
provider.name = xml.getAttributeValue(null, "name");
String domain = xml.getAttributeValue(null, "domain");
if (domain != null)
@ -170,6 +173,14 @@ public class EmailProvider {
return result;
}
static EmailProvider getProvider(Context context, String id) throws FileNotFoundException {
for (EmailProvider provider : loadProfiles(context))
if (id.equals(provider.id))
return provider;
throw new FileNotFoundException("provider id=" + id);
}
@NonNull
static EmailProvider fromDomain(Context context, String domain, Discover discover) throws IOException {
return fromEmail(context, domain, discover);

View File

@ -72,6 +72,7 @@ public class EntityAccount extends EntityOrder implements Serializable {
public Integer port;
@NonNull
public Integer auth_type; // immutable
public String provider;
@NonNull
public String user;
@NonNull

View File

@ -72,6 +72,7 @@ public class EntityIdentity {
public Integer port;
@NonNull
public Integer auth_type;
public String provider;
@NonNull
public String user;
@NonNull

View File

@ -143,6 +143,7 @@ public class FragmentAccount extends FragmentBase {
private long id = -1;
private long copy = -1;
private int auth = MailService.AUTH_TYPE_PASSWORD;
private String provider = null;
private boolean saving = false;
private static final int REQUEST_COLOR = 1;
@ -500,6 +501,7 @@ public class FragmentAccount extends FragmentBase {
args.putBoolean("insecure", cbInsecure.isChecked());
args.putString("port", etPort.getText().toString());
args.putInt("auth", auth);
args.putString("provider", provider);
args.putString("user", etUser.getText().toString());
args.putString("password", tilPassword.getEditText().getText().toString());
args.putString("realm", etRealm.getText().toString());
@ -537,6 +539,7 @@ public class FragmentAccount extends FragmentBase {
boolean insecure = args.getBoolean("insecure");
String port = args.getString("port");
int auth = args.getInt("auth");
String provider = args.getString("provider");
String user = args.getString("user");
String password = args.getString("password");
String realm = args.getString("realm");
@ -568,7 +571,7 @@ public class FragmentAccount extends FragmentBase {
// Check IMAP server / get folders
String protocol = "imap" + (starttls ? "" : "s");
try (MailService iservice = new MailService(context, protocol, realm, insecure, true, true)) {
iservice.connect(host, Integer.parseInt(port), auth, user, password, fingerprint);
iservice.connect(host, Integer.parseInt(port), auth, provider, user, password, fingerprint);
result.idle = iservice.hasCapability("IDLE");
@ -707,6 +710,7 @@ public class FragmentAccount extends FragmentBase {
args.putBoolean("insecure", cbInsecure.isChecked());
args.putString("port", etPort.getText().toString());
args.putInt("auth", auth);
args.putString("provider", provider);
args.putString("user", etUser.getText().toString());
args.putString("password", tilPassword.getEditText().getText().toString());
args.putString("realm", etRealm.getText().toString());
@ -767,6 +771,7 @@ public class FragmentAccount extends FragmentBase {
boolean insecure = args.getBoolean("insecure");
String port = args.getString("port");
int auth = args.getInt("auth");
String provider = args.getString("provider");
String user = args.getString("user").trim();
String password = args.getString("password");
String realm = args.getString("realm");
@ -942,7 +947,7 @@ public class FragmentAccount extends FragmentBase {
if (check) {
String protocol = "imap" + (starttls ? "" : "s");
try (MailService iservice = new MailService(context, protocol, realm, insecure, true, true)) {
iservice.connect(host, Integer.parseInt(port), auth, user, password, fingerprint);
iservice.connect(host, Integer.parseInt(port), auth, provider, user, password, fingerprint);
for (Folder ifolder : iservice.getStore().getDefaultFolder().list("*")) {
// Check folder attributes
@ -989,6 +994,7 @@ public class FragmentAccount extends FragmentBase {
account.user = user;
account.password = password;
}
account.provider = provider;
account.realm = realm;
account.fingerprint = fingerprint;
@ -1213,6 +1219,7 @@ public class FragmentAccount extends FragmentBase {
outState.putString("fair:password", tilPassword.getEditText().getText().toString());
outState.putInt("fair:advanced", grpAdvanced.getVisibility());
outState.putInt("fair:auth", auth);
outState.putString("fair:authprovider", provider);
super.onSaveInstanceState(outState);
}
@ -1300,6 +1307,7 @@ public class FragmentAccount extends FragmentBase {
cbUseDate.setChecked(account == null ? false : account.use_date);
auth = (account == null ? MailService.AUTH_TYPE_PASSWORD : account.auth_type);
provider = (account == null ? null : account.provider);
new SimpleTask<EntityAccount>() {
@Override
@ -1319,13 +1327,14 @@ public class FragmentAccount extends FragmentBase {
}
}.execute(FragmentAccount.this, new Bundle(), "account:primary");
} else {
int provider = savedInstanceState.getInt("fair:provider");
spProvider.setTag(provider);
spProvider.setSelection(provider);
int p = savedInstanceState.getInt("fair:provider");
spProvider.setTag(p);
spProvider.setSelection(p);
tilPassword.getEditText().setText(savedInstanceState.getString("fair:password"));
grpAdvanced.setVisibility(savedInstanceState.getInt("fair:advanced"));
auth = savedInstanceState.getInt("fair:auth");
provider = savedInstanceState.getString("fair:authprovider");
}
Helper.setViewsEnabled(view, true);

View File

@ -284,7 +284,7 @@ public class FragmentGmail extends FragmentBase {
String aprotocol = provider.imap.starttls ? "imap" : "imaps";
try (MailService iservice = new MailService(context, aprotocol, null, false, true, true)) {
iservice.connect(provider.imap.host, provider.imap.port, MailService.AUTH_TYPE_GMAIL, user, password, null);
iservice.connect(provider.imap.host, provider.imap.port, MailService.AUTH_TYPE_GMAIL, null, user, password, null);
folders = iservice.getFolders();
@ -294,7 +294,7 @@ public class FragmentGmail extends FragmentBase {
String iprotocol = provider.smtp.starttls ? "smtp" : "smtps";
try (MailService iservice = new MailService(context, iprotocol, null, false, true, true)) {
iservice.connect(provider.smtp.host, provider.smtp.port, MailService.AUTH_TYPE_GMAIL, user, password, null);
iservice.connect(provider.smtp.host, provider.smtp.port, MailService.AUTH_TYPE_GMAIL, null, user, password, null);
}
DB db = DB.getInstance(context);

View File

@ -125,6 +125,7 @@ public class FragmentIdentity extends FragmentBase {
private long copy = -1;
private long account = -1;
private int auth = MailService.AUTH_TYPE_PASSWORD;
private String provider = null;
private boolean saving = false;
private static final int REQUEST_COLOR = 1;
@ -253,7 +254,8 @@ public class FragmentIdentity extends FragmentBase {
}
// Copy account credentials
auth = (account.auth_type == null ? MailService.AUTH_TYPE_PASSWORD : account.auth_type);
auth = account.auth_type;
provider = account.provider;
etEmail.setText(account.user);
etUser.setText(account.user);
tilPassword.getEditText().setText(account.password);
@ -519,6 +521,7 @@ public class FragmentIdentity extends FragmentBase {
args.putBoolean("insecure", cbInsecure.isChecked());
args.putString("port", etPort.getText().toString());
args.putInt("auth", auth);
args.putString("provider", provider);
args.putString("user", etUser.getText().toString());
args.putString("password", tilPassword.getEditText().getText().toString());
args.putString("realm", etRealm.getText().toString());
@ -567,6 +570,7 @@ public class FragmentIdentity extends FragmentBase {
boolean insecure = args.getBoolean("insecure");
String port = args.getString("port");
int auth = args.getInt("auth");
String provider = args.getString("provider");
String user = args.getString("user").trim();
String password = args.getString("password");
String realm = args.getString("realm");
@ -741,7 +745,7 @@ public class FragmentIdentity extends FragmentBase {
String protocol = (starttls ? "smtp" : "smtps");
try (MailService iservice = new MailService(context, protocol, realm, insecure, true, true)) {
iservice.setUseIp(use_ip);
iservice.connect(host, Integer.parseInt(port), auth, user, password, fingerprint);
iservice.connect(host, Integer.parseInt(port), auth, provider, user, password, fingerprint);
}
}
@ -775,6 +779,7 @@ public class FragmentIdentity extends FragmentBase {
identity.user = user;
identity.password = password;
}
identity.provider = provider;
identity.realm = realm;
identity.fingerprint = fingerprint;
identity.use_ip = use_ip;
@ -881,6 +886,7 @@ public class FragmentIdentity extends FragmentBase {
outState.putString("fair:password", tilPassword.getEditText().getText().toString());
outState.putInt("fair:advanced", grpAdvanced.getVisibility());
outState.putInt("fair:auth", auth);
outState.putString("fair:authprovider", provider);
outState.putString("fair:html", (String) etSignature.getTag());
super.onSaveInstanceState(outState);
}
@ -941,6 +947,7 @@ public class FragmentIdentity extends FragmentBase {
etBcc.setText(identity == null ? null : identity.bcc);
auth = (identity == null ? MailService.AUTH_TYPE_PASSWORD : identity.auth_type);
provider = (identity == null ? null : identity.provider);
if (identity == null || copy > 0)
new SimpleTask<Integer>() {
@ -963,6 +970,7 @@ public class FragmentIdentity extends FragmentBase {
tilPassword.getEditText().setText(savedInstanceState.getString("fair:password"));
grpAdvanced.setVisibility(savedInstanceState.getInt("fair:advanced"));
auth = savedInstanceState.getInt("fair:auth");
provider = savedInstanceState.getString("fair:authprovider");
etSignature.setTag(savedInstanceState.getString("fair:html"));
}

View File

@ -75,6 +75,7 @@ import java.util.Map;
import static android.app.Activity.RESULT_OK;
public class FragmentOAuth extends FragmentBase {
private String id;
private String name;
private ViewGroup view;
@ -97,6 +98,7 @@ public class FragmentOAuth extends FragmentBase {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
id = args.getString("id");
name = args.getString("name");
}
@ -133,7 +135,7 @@ public class FragmentOAuth extends FragmentBase {
tvGrantHint.setText(getString(R.string.title_setup_oauth_rationale, name));
pbOAuth.setVisibility(View.GONE);
tvAuthorized.setVisibility(View.GONE);
tvGmailHint.setVisibility("Gmail".equals(name) ? View.VISIBLE : View.GONE);
tvGmailHint.setVisibility("gmail".equals(id) ? View.VISIBLE : View.GONE);
hideError();
return view;
@ -186,61 +188,56 @@ public class FragmentOAuth extends FragmentBase {
pbOAuth.setVisibility(View.VISIBLE);
hideError();
for (EmailProvider provider : EmailProvider.loadProfiles(getContext()))
if (provider.name.equals(name) && provider.oauth != null) {
AppAuthConfiguration appAuthConfig = new AppAuthConfiguration.Builder()
.setBrowserMatcher(new BrowserMatcher() {
@Override
public boolean matches(@NonNull BrowserDescriptor descriptor) {
BrowserMatcher sbrowser = new VersionedBrowserMatcher(
Browsers.SBrowser.PACKAGE_NAME,
Browsers.SBrowser.SIGNATURE_SET,
true,
VersionRange.atMost("5.3"));
return !sbrowser.matches(descriptor);
}
})
.build();
EmailProvider provider = EmailProvider.getProvider(getContext(), id);
AuthorizationService authService = new AuthorizationService(getContext(), appAuthConfig);
AppAuthConfiguration appAuthConfig = new AppAuthConfiguration.Builder()
.setBrowserMatcher(new BrowserMatcher() {
@Override
public boolean matches(@NonNull BrowserDescriptor descriptor) {
BrowserMatcher sbrowser = new VersionedBrowserMatcher(
Browsers.SBrowser.PACKAGE_NAME,
Browsers.SBrowser.SIGNATURE_SET,
true,
VersionRange.atMost("5.3"));
return !sbrowser.matches(descriptor);
}
})
.build();
AuthorizationServiceConfiguration serviceConfig = new AuthorizationServiceConfiguration(
Uri.parse(provider.oauth.authorizationEndpoint),
Uri.parse(provider.oauth.tokenEndpoint));
AuthorizationService authService = new AuthorizationService(getContext(), appAuthConfig);
AuthState authState = new AuthState(serviceConfig);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
prefs.edit().putString("oauth." + provider.name, authState.jsonSerializeString()).apply();
AuthorizationServiceConfiguration serviceConfig = new AuthorizationServiceConfiguration(
Uri.parse(provider.oauth.authorizationEndpoint),
Uri.parse(provider.oauth.tokenEndpoint));
Map<String, String> params = new HashMap<>();
if ("Gmail".equals(provider.name))
params.put("access_type", "offline");
AuthState authState = new AuthState(serviceConfig);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
prefs.edit().putString("oauth." + provider.id, authState.jsonSerializeString()).apply();
AuthorizationRequest.Builder authRequestBuilder =
new AuthorizationRequest.Builder(
serviceConfig,
provider.oauth.clientId,
ResponseTypeValues.CODE,
Uri.parse(provider.oauth.redirectUri))
.setScopes(provider.oauth.scopes)
.setState(provider.name)
.setAdditionalParameters(params);
Map<String, String> params = new HashMap<>();
if ("gmail".equals(provider.id))
params.put("access_type", "offline");
if ("Gmail".equals(provider.name) && BuildConfig.DEBUG)
authRequestBuilder.setPrompt("consent");
AuthorizationRequest.Builder authRequestBuilder =
new AuthorizationRequest.Builder(
serviceConfig,
provider.oauth.clientId,
ResponseTypeValues.CODE,
Uri.parse(provider.oauth.redirectUri))
.setScopes(provider.oauth.scopes)
.setState(provider.id)
.setAdditionalParameters(params);
AuthorizationRequest authRequest = authRequestBuilder.build();
if ("gmail".equals(provider.id) && BuildConfig.DEBUG)
authRequestBuilder.setPrompt("consent");
Log.i("OAuth request provider=" + provider.name);
if (BuildConfig.DEBUG)
Log.i("OAuth uri=" + authRequest.toUri());
Intent authIntent = authService.getAuthorizationRequestIntent(authRequest);
startActivityForResult(authIntent, ActivitySetup.REQUEST_OAUTH);
AuthorizationRequest authRequest = authRequestBuilder.build();
return;
}
throw new IllegalArgumentException("Unknown provider=" + name);
Log.i("OAuth request provider=" + provider.id);
if (BuildConfig.DEBUG)
Log.i("OAuth uri=" + authRequest.toUri());
Intent authIntent = authService.getAuthorizationRequestIntent(authRequest);
startActivityForResult(authIntent, ActivitySetup.REQUEST_OAUTH);
} catch (Throwable ex) {
showError(ex);
btnOAuth.setEnabled(true);
@ -256,54 +253,51 @@ public class FragmentOAuth extends FragmentBase {
tvAuthorized.setVisibility(View.VISIBLE);
for (final EmailProvider provider : EmailProvider.loadProfiles(getContext()))
if (provider.name.equals(auth.state)) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
final AuthState authState = AuthState.jsonDeserialize(prefs.getString("oauth." + provider.name, null));
prefs.edit().remove("oauth." + provider.name).apply();
final EmailProvider provider = EmailProvider.getProvider(getContext(), auth.state);
Log.i("OAuth get token provider=" + provider.name);
authState.update(auth, null);
if (BuildConfig.DEBUG)
Log.i("OAuth response=" + authState.jsonSerializeString());
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
String json = prefs.getString("oauth." + provider.id, null);
prefs.edit().remove("oauth." + provider.id).apply();
AuthorizationService authService = new AuthorizationService(getContext());
final AuthState authState = AuthState.jsonDeserialize(json);
ClientAuthentication clientAuth;
if (provider.oauth.clientSecret == null)
clientAuth = NoClientAuthentication.INSTANCE;
else
clientAuth = new ClientSecretPost(provider.oauth.clientSecret);
Log.i("OAuth get token provider=" + provider.id);
authState.update(auth, null);
if (BuildConfig.DEBUG)
Log.i("OAuth response=" + authState.jsonSerializeString());
authService.performTokenRequest(
auth.createTokenExchangeRequest(),
clientAuth,
new AuthorizationService.TokenResponseCallback() {
@Override
public void onTokenRequestCompleted(TokenResponse access, AuthorizationException error) {
try {
if (access == null)
throw error;
AuthorizationService authService = new AuthorizationService(getContext());
Log.i("OAuth got token provider=" + provider.name);
authState.update(access, null);
if (BuildConfig.DEBUG)
Log.i("OAuth response=" + authState.jsonSerializeString());
ClientAuthentication clientAuth;
if (provider.oauth.clientSecret == null)
clientAuth = NoClientAuthentication.INSTANCE;
else
clientAuth = new ClientSecretPost(provider.oauth.clientSecret);
if (TextUtils.isEmpty(access.refreshToken))
throw new IllegalStateException("No refresh token");
authService.performTokenRequest(
auth.createTokenExchangeRequest(),
clientAuth,
new AuthorizationService.TokenResponseCallback() {
@Override
public void onTokenRequestCompleted(TokenResponse access, AuthorizationException error) {
try {
if (access == null)
throw error;
onOAuthorized(access.accessToken, authState);
} catch (Throwable ex) {
showError(ex);
}
}
});
Log.i("OAuth got token provider=" + provider.id);
authState.update(access, null);
if (BuildConfig.DEBUG)
Log.i("OAuth response=" + authState.jsonSerializeString());
return;
}
if (TextUtils.isEmpty(access.refreshToken))
throw new IllegalStateException("No refresh token");
throw new IllegalArgumentException("Unknown state=" + auth.state);
onOAuthorized(access.accessToken, authState);
} catch (Throwable ex) {
showError(ex);
}
}
});
} catch (Throwable ex) {
showError(ex);
btnOAuth.setEnabled(true);
@ -313,6 +307,7 @@ public class FragmentOAuth extends FragmentBase {
private void onOAuthorized(String accessToken, AuthState state) {
Bundle args = new Bundle();
args.putString("id", id);
args.putString("name", name);
args.putString("token", accessToken);
args.putString("state", state.jsonSerializeString());
@ -320,6 +315,7 @@ public class FragmentOAuth extends FragmentBase {
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) throws Throwable {
String id = args.getString("id");
String name = args.getString("name");
String token = args.getString("token");
String state = args.getString("state");
@ -327,7 +323,7 @@ public class FragmentOAuth extends FragmentBase {
String primaryEmail = null;
List<Pair<String, String>> identities = new ArrayList<>();
if ("Gmail".equals(name)) {
if ("gmail".equals(id)) {
// https://developers.google.com/gmail/api/v1/reference/users/getProfile
URL url = new URL("https://www.googleapis.com/gmail/v1/users/me/settings/sendAs");
Log.i("Fetching " + url);
@ -366,7 +362,7 @@ public class FragmentOAuth extends FragmentBase {
}
}
} else if ("Outlook/Office365".equals(name)) {
} else if ("outlook".equals(id)) {
// https://docs.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0&tabs=http#http-request
URL url = new URL("https://graph.microsoft.com/v1.0/me?$select=displayName,otherMails");
Log.i("Fetching " + url);
@ -403,7 +399,7 @@ public class FragmentOAuth extends FragmentBase {
}
}
} else
throw new IllegalArgumentException("Unknown provider=" + name);
throw new IllegalArgumentException("Unknown provider=" + id);
if (TextUtils.isEmpty(primaryEmail) || identities.size() == 0)
throw new IllegalArgumentException("Primary email address not found");
@ -412,102 +408,102 @@ public class FragmentOAuth extends FragmentBase {
for (Pair<String, String> identity : identities)
Log.i("OAuth identity=" + identity.first + "/" + identity.second);
for (EmailProvider provider : EmailProvider.loadProfiles(context))
if (provider.name.equals(name)) {
EmailProvider provider = EmailProvider.getProvider(context, id);
List<EntityFolder> folders;
List<EntityFolder> folders;
Log.i("OAuth checking IMAP provider=" + provider.name);
String aprotocol = provider.imap.starttls ? "imap" : "imaps";
try (MailService iservice = new MailService(context, aprotocol, null, false, true, true)) {
iservice.connect(provider.imap.host, provider.imap.port, MailService.AUTH_TYPE_OAUTH, primaryEmail, state, null);
Log.i("OAuth checking IMAP provider=" + provider.id);
String aprotocol = provider.imap.starttls ? "imap" : "imaps";
try (MailService iservice = new MailService(context, aprotocol, null, false, true, true)) {
iservice.connect(provider.imap.host, provider.imap.port, MailService.AUTH_TYPE_OAUTH, provider.id, primaryEmail, state, null);
folders = iservice.getFolders();
folders = iservice.getFolders();
if (folders == null)
throw new IllegalArgumentException(context.getString(R.string.title_setup_no_system_folders));
}
if (folders == null)
throw new IllegalArgumentException(context.getString(R.string.title_setup_no_system_folders));
}
Log.i("OAuth checking SMTP provider=" + provider.name);
String iprotocol = provider.smtp.starttls ? "smtp" : "smtps";
try (MailService iservice = new MailService(context, iprotocol, null, false, true, true)) {
iservice.connect(provider.smtp.host, provider.smtp.port, MailService.AUTH_TYPE_OAUTH, primaryEmail, state, null);
}
Log.i("OAuth checking SMTP provider=" + provider.id);
String iprotocol = provider.smtp.starttls ? "smtp" : "smtps";
try (MailService iservice = new MailService(context, iprotocol, null, false, true, true)) {
iservice.connect(provider.smtp.host, provider.smtp.port, MailService.AUTH_TYPE_OAUTH, provider.id, primaryEmail, state, null);
}
Log.i("OAuth passed provider=" + provider.name);
Log.i("OAuth passed provider=" + provider.id);
DB db = DB.getInstance(context);
try {
db.beginTransaction();
DB db = DB.getInstance(context);
try {
db.beginTransaction();
EntityAccount primary = db.account().getPrimaryAccount();
EntityAccount primary = db.account().getPrimaryAccount();
// Create account
EntityAccount account = new EntityAccount();
// Create account
EntityAccount account = new EntityAccount();
account.host = provider.imap.host;
account.starttls = provider.imap.starttls;
account.port = provider.imap.port;
account.auth_type = MailService.AUTH_TYPE_OAUTH;
account.user = primaryEmail;
account.password = state;
account.host = provider.imap.host;
account.starttls = provider.imap.starttls;
account.port = provider.imap.port;
account.auth_type = MailService.AUTH_TYPE_OAUTH;
account.provider = provider.id;
account.user = primaryEmail;
account.password = state;
account.name = provider.name;
account.name = provider.name;
account.synchronize = true;
account.primary = (primary == null);
account.synchronize = true;
account.primary = (primary == null);
account.created = new Date().getTime();
account.last_connected = account.created;
account.created = new Date().getTime();
account.last_connected = account.created;
account.id = db.account().insertAccount(account);
args.putLong("account", account.id);
EntityLog.log(context, "OAuth account=" + account.name);
account.id = db.account().insertAccount(account);
args.putLong("account", account.id);
EntityLog.log(context, "OAuth account=" + account.name);
// Create folders
for (EntityFolder folder : folders) {
folder.account = account.id;
folder.id = db.folder().insertFolder(folder);
EntityLog.log(context, "OAuth folder=" + folder.name + " type=" + folder.type);
}
// Set swipe left/right folder
for (EntityFolder folder : folders)
if (EntityFolder.TRASH.equals(folder.type))
account.swipe_left = folder.id;
else if (EntityFolder.ARCHIVE.equals(folder.type))
account.swipe_right = folder.id;
db.account().updateAccount(account);
// Create identities
for (Pair<String, String> identity : identities) {
EntityIdentity ident = new EntityIdentity();
ident.name = identity.second;
ident.email = identity.first;
ident.account = account.id;
ident.host = provider.smtp.host;
ident.starttls = provider.smtp.starttls;
ident.port = provider.smtp.port;
ident.auth_type = MailService.AUTH_TYPE_OAUTH;
ident.user = primaryEmail;
ident.password = state;
ident.synchronize = true;
ident.primary = ident.user.equals(ident.email);
ident.id = db.identity().insertIdentity(ident);
EntityLog.log(context, "OAuth identity=" + ident.name + " email=" + ident.email);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
ServiceSynchronize.eval(context, "OAuth");
// Create folders
for (EntityFolder folder : folders) {
folder.account = account.id;
folder.id = db.folder().insertFolder(folder);
EntityLog.log(context, "OAuth folder=" + folder.name + " type=" + folder.type);
}
// Set swipe left/right folder
for (EntityFolder folder : folders)
if (EntityFolder.TRASH.equals(folder.type))
account.swipe_left = folder.id;
else if (EntityFolder.ARCHIVE.equals(folder.type))
account.swipe_right = folder.id;
db.account().updateAccount(account);
// Create identities
for (Pair<String, String> identity : identities) {
EntityIdentity ident = new EntityIdentity();
ident.name = identity.second;
ident.email = identity.first;
ident.account = account.id;
ident.host = provider.smtp.host;
ident.starttls = provider.smtp.starttls;
ident.port = provider.smtp.port;
ident.auth_type = MailService.AUTH_TYPE_OAUTH;
ident.provider = provider.id;
ident.user = primaryEmail;
ident.password = state;
ident.synchronize = true;
ident.primary = ident.user.equals(ident.email);
ident.id = db.identity().insertIdentity(ident);
EntityLog.log(context, "OAuth identity=" + ident.name + " email=" + ident.email);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
ServiceSynchronize.eval(context, "OAuth");
return null;
}
@ -542,7 +538,7 @@ public class FragmentOAuth extends FragmentBase {
grpError.setVisibility(View.VISIBLE);
if ("Gmail".equals(name))
if ("gmail".equals(id))
tvGmailDraftsHint.setVisibility(View.VISIBLE);
new Handler().post(new Runnable() {

View File

@ -278,7 +278,7 @@ public class FragmentPop extends FragmentBase {
if (check) {
String protocol = "pop3" + (starttls ? "" : "s");
try (MailService iservice = new MailService(context, protocol, null, insecure, true, true)) {
iservice.connect(host, Integer.parseInt(port), MailService.AUTH_TYPE_PASSWORD, user, password, null);
iservice.connect(host, Integer.parseInt(port), MailService.AUTH_TYPE_PASSWORD, null, user, password, null);
}
}

View File

@ -250,13 +250,13 @@ public class FragmentQuickSetup extends FragmentBase {
String aprotocol = provider.imap.starttls ? "imap" : "imaps";
try (MailService iservice = new MailService(context, aprotocol, null, false, true, true)) {
try {
iservice.connect(provider.imap.host, provider.imap.port, MailService.AUTH_TYPE_PASSWORD, user, password, null);
iservice.connect(provider.imap.host, provider.imap.port, MailService.AUTH_TYPE_PASSWORD, null, user, password, null);
} catch (AuthenticationFailedException ex) {
if (!user.equals(username)) {
Log.w(ex);
user = username;
Log.i("Retry with user=" + user);
iservice.connect(provider.imap.host, provider.imap.port, MailService.AUTH_TYPE_PASSWORD, user, password, null);
iservice.connect(provider.imap.host, provider.imap.port, MailService.AUTH_TYPE_PASSWORD, null, user, password, null);
} else
throw ex;
}
@ -270,7 +270,7 @@ public class FragmentQuickSetup extends FragmentBase {
String iprotocol = provider.smtp.starttls ? "smtp" : "smtps";
try (MailService iservice = new MailService(context, iprotocol, null, false, true, true)) {
iservice.setUseIp(provider.useip);
iservice.connect(provider.smtp.host, provider.smtp.port, MailService.AUTH_TYPE_PASSWORD, user, password, null);
iservice.connect(provider.smtp.host, provider.smtp.port, MailService.AUTH_TYPE_PASSWORD, null, user, password, null);
}
if (check)

View File

@ -174,6 +174,7 @@ public class FragmentSetup extends FragmentBase {
popupMenu.getMenu()
.add(Menu.NONE, -1, order++, getString(R.string.title_setup_oauth, provider.name))
.setIntent(new Intent(ActivitySetup.ACTION_QUICK_OAUTH)
.putExtra("id", provider.id)
.putExtra("name", provider.name));
popupMenu.getMenu().add(Menu.NONE, R.string.title_setup_activesync, order++, R.string.title_setup_activesync);

View File

@ -17,6 +17,9 @@ import com.sun.mail.util.MailConnectException;
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.x509.GeneralName;
import org.jetbrains.annotations.NotNull;
@ -200,7 +203,7 @@ public class MailService implements AutoCloseable {
}
public void connect(EntityAccount account) throws MessagingException {
String password = connect(account.host, account.port, account.auth_type, account.user, account.password, account.fingerprint);
String password = connect(account.host, account.port, account.auth_type, account.provider, account.user, account.password, account.fingerprint);
if (password != null) {
DB db = DB.getInstance(context);
int count = db.account().setAccountPassword(account.id, account.password);
@ -209,7 +212,7 @@ public class MailService implements AutoCloseable {
}
public void connect(EntityIdentity identity) throws MessagingException {
String password = connect(identity.host, identity.port, identity.auth_type, identity.user, identity.password, identity.fingerprint);
String password = connect(identity.host, identity.port, identity.auth_type, identity.provider, identity.user, identity.password, identity.fingerprint);
if (password != null) {
DB db = DB.getInstance(context);
int count = db.identity().setIdentityPassword(identity.id, identity.password);
@ -217,7 +220,7 @@ public class MailService implements AutoCloseable {
}
}
public String connect(String host, int port, int auth, String user, String password, String fingerprint) throws MessagingException {
public String connect(String host, int port, int auth, String provider, String user, String password, String fingerprint) throws MessagingException {
SSLSocketFactoryService factory = null;
try {
factory = new SSLSocketFactoryService(host, insecure, fingerprint);
@ -240,7 +243,7 @@ public class MailService implements AutoCloseable {
// new SocketConnectException("Debug", new Exception("Test"), host, port, 0));
if (auth == AUTH_TYPE_OAUTH) {
AuthState authState = OAuthRefresh(context, password);
AuthState authState = OAuthRefresh(context, provider, password);
_connect(context, host, port, user, authState.getAccessToken(), factory);
return authState.jsonSerializeString();
} else {
@ -272,7 +275,7 @@ public class MailService implements AutoCloseable {
throw new AuthenticationFailedException(ex.getMessage(), ex1);
}
else if (auth == AUTH_TYPE_OAUTH) {
AuthState authState = OAuthRefresh(context, password);
AuthState authState = OAuthRefresh(context, provider, password);
_connect(context, host, port, user, authState.getAccessToken(), factory);
return authState.jsonSerializeString();
} else
@ -377,24 +380,33 @@ public class MailService implements AutoCloseable {
AuthorizationException error;
}
static AuthState OAuthRefresh(Context context, String json) throws MessagingException {
private static AuthState OAuthRefresh(Context context, String id, String json) throws MessagingException {
try {
AuthState authState = AuthState.jsonDeserialize(json);
Semaphore semaphore = new Semaphore(0);
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, new AuthState.AuthStateAction() {
@Override
public void execute(String accessToken, String idToken, AuthorizationException error) {
if (error != null)
holder.error = error;
semaphore.release();
}
});
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");

View File

@ -3,6 +3,7 @@
<provider
name="Gmail"
domain="gmail\\.com"
id="gmail"
link="https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq6"
order="1"
type="com.google">
@ -28,6 +29,7 @@
<provider
name="Outlook/Office365"
domain="outlook\\..*,live\\..*,hotmail\\..*"
id="outlook"
link="https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq14"
order="2"
partial="false">