diff --git a/src/main/java/ctbrec/sites/camsoda/CamsodaHttpClient.java b/src/main/java/ctbrec/sites/camsoda/CamsodaHttpClient.java index 76e3b98b..5381a968 100644 --- a/src/main/java/ctbrec/sites/camsoda/CamsodaHttpClient.java +++ b/src/main/java/ctbrec/sites/camsoda/CamsodaHttpClient.java @@ -1,19 +1,37 @@ package ctbrec.sites.camsoda; import java.io.IOException; +import java.net.HttpCookie; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import ctbrec.Config; import ctbrec.io.HttpClient; +import ctbrec.sites.cam4.Cam4LoginDialog; +import javafx.application.Platform; +import okhttp3.Cookie; import okhttp3.FormBody; +import okhttp3.HttpUrl; import okhttp3.Request; import okhttp3.Response; public class CamsodaHttpClient extends HttpClient { + private static final transient Logger LOG = LoggerFactory.getLogger(CamsodaHttpClient.class); + @Override public boolean login() throws IOException { + if(loggedIn) { + return true; + } + String url = Camsoda.BASE_URI + "/api/v1/auth/login"; FormBody body = new FormBody.Builder() .add("username", Config.getInstance().getSettings().camsodaUsername) @@ -27,7 +45,12 @@ public class CamsodaHttpClient extends HttpClient { if(response.isSuccessful()) { JSONObject resp = new JSONObject(response.body().string()); if(resp.has("error")) { - throw new IOException(resp.getString("error")); + String error = resp.getString("error"); + if(Objects.equals(error, "Please confirm that you are not a robot.")) { + return loginWithDialog(); + } else { + throw new IOException(resp.getString("error")); + } } else { return true; } @@ -35,4 +58,63 @@ public class CamsodaHttpClient extends HttpClient { throw new IOException(response.code() + " " + response.message()); } } + + private boolean loginWithDialog() throws IOException { + BlockingQueue queue = new LinkedBlockingQueue<>(); + + Runnable showDialog = () -> { + // login with javafx WebView + CamsodaLoginDialog loginDialog = new CamsodaLoginDialog(); + + // transfer cookies from WebView to OkHttp cookie jar + transferCookies(loginDialog); + + try { + queue.put(true); + } catch (InterruptedException e) { + LOG.error("Error while signaling termination", e); + } + }; + + if(Platform.isFxApplicationThread()) { + showDialog.run(); + } else { + Platform.runLater(showDialog); + try { + queue.take(); + } catch (InterruptedException e) { + LOG.error("Error while waiting for login dialog to close", e); + throw new IOException(e); + } + } + + loggedIn = checkLoginSuccess(); + return loggedIn; + } + + /** + * check, if the login worked + * @throws IOException + */ + private boolean checkLoginSuccess() throws IOException { + return true; + } + + private void transferCookies(CamsodaLoginDialog loginDialog) { + HttpUrl redirectedUrl = HttpUrl.parse(loginDialog.getUrl()); + List cookies = new ArrayList<>(); + for (HttpCookie webViewCookie : loginDialog.getCookies()) { + Cookie cookie = Cookie.parse(redirectedUrl, webViewCookie.toString()); + cookies.add(cookie); + } + cookieJar.saveFromResponse(redirectedUrl, cookies); + + HttpUrl origUrl = HttpUrl.parse(Cam4LoginDialog.URL); + cookies = new ArrayList<>(); + for (HttpCookie webViewCookie : loginDialog.getCookies()) { + Cookie cookie = Cookie.parse(origUrl, webViewCookie.toString()); + cookies.add(cookie); + } + cookieJar.saveFromResponse(origUrl, cookies); + } } diff --git a/src/main/java/ctbrec/sites/camsoda/CamsodaLoginDialog.java b/src/main/java/ctbrec/sites/camsoda/CamsodaLoginDialog.java new file mode 100644 index 00000000..c0e08a2a --- /dev/null +++ b/src/main/java/ctbrec/sites/camsoda/CamsodaLoginDialog.java @@ -0,0 +1,110 @@ +package ctbrec.sites.camsoda; + +import java.io.File; +import java.io.InputStream; +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.HttpCookie; +import java.util.Base64; +import java.util.List; + +import ctbrec.OS; +import javafx.concurrent.Worker.State; +import javafx.scene.Scene; +import javafx.scene.control.ProgressIndicator; +import javafx.scene.image.Image; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.scene.web.WebEngine; +import javafx.scene.web.WebView; +import javafx.stage.Stage; + +public class CamsodaLoginDialog { + + public static final String URL = Camsoda.BASE_URI; + private List cookies = null; + private String url; + private Region veil; + private ProgressIndicator p; + + public CamsodaLoginDialog() { + Stage stage = new Stage(); + stage.setTitle("CamSoda Login"); + InputStream icon = getClass().getResourceAsStream("/icon.png"); + stage.getIcons().add(new Image(icon)); + CookieManager cookieManager = new CookieManager(); + CookieHandler.setDefault(cookieManager); + WebView webView = createWebView(stage); + + veil = new Region(); + veil.setStyle("-fx-background-color: rgba(1, 1, 1)"); + p = new ProgressIndicator(); + p.setMaxSize(140, 140); + + p.setVisible(true); + veil.visibleProperty().bind(p.visibleProperty()); + + StackPane stackPane = new StackPane(); + stackPane.getChildren().addAll(webView, veil, p); + + stage.setScene(new Scene(stackPane, 400, 358)); + stage.showAndWait(); + cookies = cookieManager.getCookieStore().getCookies(); + } + + private WebView createWebView(Stage stage) { + WebView browser = new WebView(); + WebEngine webEngine = browser.getEngine(); + webEngine.setJavaScriptEnabled(true); + webEngine.locationProperty().addListener((obs, oldV, newV) -> { + // try { + // URL _url = new URL(newV); + // if (Objects.equals(_url.getPath(), "/")) { + // stage.close(); + // } + // } catch (MalformedURLException e) { + // LOG.error("Couldn't parse new url {}", newV, e); + // } + url = newV.toString(); + System.out.println(newV.toString()); + }); + webEngine.getLoadWorker().stateProperty().addListener((observable, oldState, newState) -> { + if (newState == State.SUCCEEDED) { + webEngine.executeScript("document.querySelector('a[ng-click=\"signin();\"]').click()"); + p.setVisible(false); + + // TODO make this work + // String username = Config.getInstance().getSettings().camsodaUsername; + // if (username != null && !username.trim().isEmpty()) { + // webEngine.executeScript("document.querySelector('input[name=\"loginUsername\"]').value = '" + username + "'"); + // } + // String password = Config.getInstance().getSettings().camsodaPassword; + // if (password != null && !password.trim().isEmpty()) { + // webEngine.executeScript("document.querySelector('input[name=\"loginPassword\"]').value = '" + password + "'"); + // } + } else if (newState == State.CANCELLED || newState == State.FAILED) { + p.setVisible(false); + } + }); + + webEngine.setUserStyleSheetLocation("data:text/css;base64," + Base64.getEncoder().encodeToString(CUSTOM_STYLE.getBytes())); + webEngine.setUserDataDirectory(new File(OS.getConfigDir(), "webengine")); + webEngine.load(URL); + return browser; + } + + public List getCookies() { + for (HttpCookie httpCookie : cookies) { + System.out.println(httpCookie); + } + return cookies; + } + + public String getUrl() { + return url; + } + + private static final String CUSTOM_STYLE = "" + + ".ngdialog.ngdialog-theme-custom { padding: 0 !important }" + + ".ngdialog-overlay { background: black !important; }"; +}