diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fae3e6f..32fd7d05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,11 @@ 3.8.3 ======================== * Fixed Streamate -* Fixed favorites tab for Cam4; kind of, because only the online tab works. I currently don't see - a way to retrieve the offline favorites +* Fixed favorites tab for Cam4; kind of, because only the online tab works. + I currently don't see a way to retrieve the offline favorites +* Fixed favorites tab for CamSoda +* Fixed CamSoda recordings +* Added external login dialog to Stripchat to support the captcha 3.8.2 diff --git a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatElectronLoginDialog.java b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatElectronLoginDialog.java new file mode 100644 index 00000000..7eb49cc5 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatElectronLoginDialog.java @@ -0,0 +1,124 @@ +package ctbrec.ui.sites.stripchat; + +import java.io.IOException; +import java.util.Collections; +import java.util.function.Consumer; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ctbrec.Config; +import ctbrec.sites.stripchat.Stripchat; +import ctbrec.ui.ExternalBrowser; +import okhttp3.Cookie; +import okhttp3.Cookie.Builder; +import okhttp3.CookieJar; +import okhttp3.HttpUrl; + +public class StripchatElectronLoginDialog { + + private static final transient Logger LOG = LoggerFactory.getLogger(StripchatElectronLoginDialog.class); + public static final String DOMAIN = "stripchat.com"; + public static final String URL = Stripchat.BASE_URI; + private CookieJar cookieJar; + private ExternalBrowser browser; + + public StripchatElectronLoginDialog(CookieJar cookieJar) throws IOException { + this.cookieJar = cookieJar; + browser = ExternalBrowser.getInstance(); + try { + JSONObject config = new JSONObject(); + config.put("url", URL); + config.put("w", 640); + config.put("h", 640); + JSONObject msg = new JSONObject(); + msg.put("config", config); + browser.run(msg, msgHandler); + } catch (InterruptedException e) { + throw new IOException("Couldn't wait for login dialog", e); + } finally { + browser.close(); + } + } + + private Consumer msgHandler = (line) -> { + if(!line.startsWith("{")) { + System.err.println(line); + } else { + JSONObject json = new JSONObject(line); + if(json.has("url")) { + String url = json.getString("url"); + + if(url.endsWith(DOMAIN) || url.endsWith(DOMAIN + '/')) { + try { + browser.executeJavaScript("document.querySelector('button[class~=\"btn-visitors-agreement-accept\"]').click();"); + browser.executeJavaScript("document.querySelector('div[class~=\"header-dropdown\"] a[class~=\"dropdown-link\"]').click();"); + browser.executeJavaScript("document.querySelector('a[class~=\"btn\"][href*=\"login\"]').click();"); + String username = Config.getInstance().getSettings().stripchatUsername; + if (username != null && !username.trim().isEmpty()) { + browser.executeJavaScript("document.querySelector('#login_login_or_email').value = '" + username + "';"); + } + String password = Config.getInstance().getSettings().stripchatPassword; + if (password != null && !password.trim().isEmpty()) { + browser.executeJavaScript("document.querySelector('#login_password').value = '" + password + "';"); + } + browser.executeJavaScript("document.querySelector('#recaptcha-checkbox-border').click();"); + browser.executeJavaScript("document.querySelector('*[class~=btn-login]').addEventListener('click', function() {window.setTimeout(function() {location.reload()}, 2000)});"); + } catch(Exception e) { + LOG.warn("Couldn't auto fill username and password for Stripchat", e); + } + } + + if (json.has("cookies")) { + JSONArray _cookies = json.getJSONArray("cookies"); + boolean sessionCookieFound = false; + for (int i = 0; i < _cookies.length(); i++) { + JSONObject cookie = _cookies.getJSONObject(i); + if (cookie.getString("domain").contains(DOMAIN)) { + String domain = cookie.getString("domain"); + if (domain.startsWith(".")) { + domain = domain.substring(1); + } + Cookie c = createCookie(domain, cookie); + cookieJar.saveFromResponse(HttpUrl.parse(url), Collections.singletonList(c)); + c = createCookie(DOMAIN, cookie); + cookieJar.saveFromResponse(HttpUrl.parse(Stripchat.BASE_URI), Collections.singletonList(c)); + if (c.name().equals("stripchat_com_sessionId")) { + sessionCookieFound = true; + } + } + } + + if(sessionCookieFound) { + try { + browser.close(); + } catch (IOException e) { + LOG.error("Couldn't send close request to browser", e); + } + } + } + } + } + }; + + private Cookie createCookie(String domain, JSONObject cookie) { + Builder b = new Cookie.Builder() + .path(cookie.getString("path")) + .domain(domain) + .name(cookie.getString("name")) + .value(cookie.getString("value")) + .expiresAt(Double.valueOf(cookie.optDouble("expirationDate")).longValue()); + if(cookie.optBoolean("hostOnly")) { + b.hostOnlyDomain(domain); + } + if(cookie.optBoolean("httpOnly")) { + b.httpOnly(); + } + if(cookie.optBoolean("secure")) { + b.secure(); + } + return b.build(); + } +} diff --git a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatFollowedUpdateService.java b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatFollowedUpdateService.java index 2dcc28c2..938be470 100644 --- a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatFollowedUpdateService.java +++ b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatFollowedUpdateService.java @@ -79,6 +79,7 @@ public class StripchatFollowedUpdateService extends PaginatedScheduledService { private JSONArray loadFavoriteModelIds() throws IOException { SiteUiFactory.getUi(stripchat).login(); + stripchat.getHttpClient().login(); long userId = ((StripchatHttpClient) stripchat.getHttpClient()).getUserId(); String url = stripchat.getBaseUrl() + "/api/front/users/" + userId + "/favorites"; Request request = new Request.Builder() diff --git a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatSiteUi.java b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatSiteUi.java index a7462903..af898505 100644 --- a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatSiteUi.java +++ b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatSiteUi.java @@ -1,20 +1,30 @@ package ctbrec.ui.sites.stripchat; import java.io.IOException; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import ctbrec.sites.stripchat.Stripchat; +import ctbrec.sites.stripchat.StripchatHttpClient; +import ctbrec.ui.controls.Dialogs; import ctbrec.ui.sites.AbstractSiteUi; import ctbrec.ui.sites.ConfigUI; import ctbrec.ui.tabs.TabProvider; +import javafx.application.Platform; public class StripchatSiteUi extends AbstractSiteUi { + private static final Logger LOG = LoggerFactory.getLogger(StripchatSiteUi.class); + private StripchatTabProvider tabProvider; private StripchatConfigUI configUi; - private Stripchat stripchat; + private Stripchat site; public StripchatSiteUi(Stripchat stripchat) { - this.stripchat = stripchat; + this.site = stripchat; tabProvider = new StripchatTabProvider(stripchat); configUi = new StripchatConfigUI(stripchat); } @@ -31,7 +41,40 @@ public class StripchatSiteUi extends AbstractSiteUi { @Override public synchronized boolean login() throws IOException { - boolean automaticLogin = stripchat.login(); - return automaticLogin; + boolean automaticLogin = site.login(); + if (automaticLogin) { + return true; + } else { + + BlockingQueue queue = new LinkedBlockingQueue<>(); + + Runnable showDialog = () -> { + // login with external browser + try { + new StripchatElectronLoginDialog(site.getHttpClient().getCookieJar()); + } catch (Exception e1) { + LOG.error("Error logging in with external browser", e1); + Dialogs.showError("Login error", "Couldn't login to " + site.getName(), e1); + } + + try { + queue.put(true); + } catch (InterruptedException e) { + LOG.error("Error while signaling termination", e); + } + }; + + Platform.runLater(showDialog); + try { + queue.take(); + } catch (InterruptedException e) { + LOG.error("Error while waiting for login dialog to close", e); + throw new IOException(e); + } + + StripchatHttpClient httpClient = (StripchatHttpClient) site.getHttpClient(); + boolean loggedIn = httpClient.checkLoginSuccess(); + return loggedIn; + } } } diff --git a/common/src/main/java/ctbrec/sites/stripchat/StripchatHttpClient.java b/common/src/main/java/ctbrec/sites/stripchat/StripchatHttpClient.java index 71561a15..94beb70f 100644 --- a/common/src/main/java/ctbrec/sites/stripchat/StripchatHttpClient.java +++ b/common/src/main/java/ctbrec/sites/stripchat/StripchatHttpClient.java @@ -4,6 +4,7 @@ import static ctbrec.io.HttpConstants.*; import java.io.IOException; +import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +37,7 @@ public class StripchatHttpClient extends HttpClient { } // persisted cookies might let us log in - if(checkLoginSuccess()) { + if (checkLoginSuccess()) { loggedIn = true; LOG.debug("Logged in with cookies"); return true; @@ -75,7 +76,8 @@ public class StripchatHttpClient extends HttpClient { return false; } } else { - throw new HttpException(response.code(), response.message()); + LOG.info("Auto-Login failed: {} {} {}", response.code(), response.message(), url); + return false; } } } @@ -107,11 +109,48 @@ public class StripchatHttpClient extends HttpClient { * check, if the login worked * @throws IOException */ - public boolean checkLoginSuccess() { - return userId > 0; + public boolean checkLoginSuccess() throws IOException { + long userId = getUserId(); + String url = Stripchat.BASE_URI + "/api/front/users/" + userId + "/favorites"; + Request request = new Request.Builder() + .url(url) + .header(ACCEPT, MIMETYPE_APPLICATION_JSON) + .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) + .header(ORIGIN, Stripchat.BASE_URI) + .header(REFERER, Stripchat.BASE_URI + "/favorites") + .header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON) + .build(); + try (Response response = execute(request)) { + if (response.isSuccessful()) { + return true; + } + } catch (Exception e) { + LOG.info("Login check returned unsuccessful: {}", e.getLocalizedMessage()); + } + return false; } - public long getUserId() { + public long getUserId() throws JSONException, IOException { + if (userId == 0) { + String url = Stripchat.BASE_URI + "/api/front/users/username/" + Config.getInstance().getSettings().stripchatUsername; + Request request = new Request.Builder() + .url(url) + .header(ACCEPT, MIMETYPE_APPLICATION_JSON) + .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) + .header(ORIGIN, Stripchat.BASE_URI) + .header(REFERER, Stripchat.BASE_URI) + .header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON) + .build(); + try (Response response = execute(request)) { + if (response.isSuccessful()) { + JSONObject resp = new JSONObject(response.body().string()); + JSONObject user = resp.getJSONObject("user"); + userId = user.optLong("id"); + } else { + throw new HttpException(url, response.code(), response.message()); + } + } + } return userId; }