mirror of https://github.com/M66B/FairEmail.git
Added/storing auth provider id
This commit is contained in:
parent
e80bc630e1
commit
02dfb75542
File diff suppressed because it is too large
Load Diff
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -72,6 +72,7 @@ public class EntityIdentity {
|
|||
public Integer port;
|
||||
@NonNull
|
||||
public Integer auth_type;
|
||||
public String provider;
|
||||
@NonNull
|
||||
public String user;
|
||||
@NonNull
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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">
|
||||
|
|
Loading…
Reference in New Issue