AntennaPod/net/sync/gpoddernet/src/main/java/de/danoeh/antennapod/net/sync/nextcloud/NextcloudLoginFlow.java

140 lines
5.3 KiB
Java

package de.danoeh.antennapod.net.sync.nextcloud;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import de.danoeh.antennapod.net.sync.HostnameParser;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
public class NextcloudLoginFlow {
private static final String TAG = "NextcloudLoginFlow";
private final OkHttpClient httpClient;
private final HostnameParser hostname;
private final String rawHostUrl;
private final Context context;
private final AuthenticationCallback callback;
private String token;
private String endpoint;
private Disposable startDisposable;
private Disposable pollDisposable;
public NextcloudLoginFlow(OkHttpClient httpClient, String hostUrl, Context context,
AuthenticationCallback callback) {
this.httpClient = httpClient;
this.rawHostUrl = hostUrl;
this.hostname = new HostnameParser(hostUrl);
this.context = context;
this.callback = callback;
}
public static NextcloudLoginFlow fromInstanceState(OkHttpClient httpClient, Context context,
AuthenticationCallback callback, ArrayList<String> instanceState) {
NextcloudLoginFlow flow = new NextcloudLoginFlow(httpClient, instanceState.get(0), context, callback);
flow.token = instanceState.get(1);
flow.endpoint = instanceState.get(2);
return flow;
}
public ArrayList<String> saveInstanceState() {
ArrayList<String> state = new ArrayList<>();
state.add(rawHostUrl);
state.add(token);
state.add(endpoint);
return state;
}
public void start() {
if (token != null) {
poll();
return;
}
startDisposable = Observable.fromCallable(() -> {
URL url = new URI(hostname.scheme, null, hostname.host, hostname.port,
hostname.subfolder + "/index.php/login/v2", null, null).toURL();
JSONObject result = doRequest(url, "");
String loginUrl = result.getString("login");
this.token = result.getJSONObject("poll").getString("token");
this.endpoint = result.getJSONObject("poll").getString("endpoint");
return loginUrl;
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
result -> {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(result));
context.startActivity(browserIntent);
poll();
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
this.token = null;
this.endpoint = null;
callback.onNextcloudAuthError(error.getLocalizedMessage());
});
}
private void poll() {
pollDisposable = Observable.fromCallable(() -> doRequest(URI.create(endpoint).toURL(), "token=" + token))
.retryWhen(t -> t.delay(1, TimeUnit.SECONDS))
.timeout(5, TimeUnit.MINUTES)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> callback.onNextcloudAuthenticated(
result.getString("server"), result.getString("loginName"), result.getString("appPassword")),
error -> {
this.token = null;
this.endpoint = null;
callback.onNextcloudAuthError(error.getLocalizedMessage());
});
}
public void cancel() {
if (startDisposable != null) {
startDisposable.dispose();
}
if (pollDisposable != null) {
pollDisposable.dispose();
}
}
private JSONObject doRequest(URL url, String bodyContent) throws IOException, JSONException {
RequestBody requestBody = RequestBody.create(
bodyContent, MediaType.get("application/x-www-form-urlencoded"));
Request request = new Request.Builder().url(url).method("POST", requestBody).build();
Response response = httpClient.newCall(request).execute();
if (response.code() != 200) {
response.close();
throw new IOException("Return code " + response.code());
}
ResponseBody body = response.body();
if (body == null) {
throw new IOException("Empty response");
}
return new JSONObject(body.string());
}
public interface AuthenticationCallback {
void onNextcloudAuthenticated(String server, String username, String password);
void onNextcloudAuthError(String errorMessage);
}
}