Add electron login dialog for Chaturbate

This commit is contained in:
0xb00bface 2022-10-22 15:08:29 +02:00
parent d1c5bd4850
commit 09d9c3490e
3 changed files with 185 additions and 39 deletions

View File

@ -0,0 +1,123 @@
package ctbrec.ui.sites.chaturbate;
import ctbrec.Config;
import ctbrec.sites.chaturbate.Chaturbate;
import ctbrec.ui.ExternalBrowser;
import okhttp3.Cookie;
import okhttp3.Cookie.Builder;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Objects;
import java.util.function.Consumer;
public class ChaturbateElectronLoginDialog {
private static final Logger LOG = LoggerFactory.getLogger(ChaturbateElectronLoginDialog.class);
public static final String DOMAIN = "chaturbate.com";
private final Chaturbate site;
private CookieJar cookieJar;
private ExternalBrowser browser;
public ChaturbateElectronLoginDialog(Chaturbate site, CookieJar cookieJar) throws IOException {
this.site = site;
this.cookieJar = cookieJar;
browser = ExternalBrowser.getInstance();
try {
var config = new JSONObject();
config.put("url", site.getBaseUrl() + "/auth/login/");
config.put("w", 640);
config.put("h", 480);
config.put("userAgent", Config.getInstance().getSettings().httpUserAgent);
var msg = new JSONObject();
msg.put("config", config);
browser.run(msg, msgHandler);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Couldn't wait for login dialog", e);
} finally {
browser.close();
}
}
private final Consumer<String> msgHandler = line -> {
if (!line.startsWith("{")) {
LOG.error("Didn't received a JSON object {}", line);
} else {
var json = new JSONObject(line);
if (json.has("url")) {
var url = json.getString("url");
if (url.endsWith("/auth/login/")) {
try {
Thread.sleep(500);
String username = Config.getInstance().getSettings().chaturbateUsername;
if (username != null && !username.trim().isEmpty()) {
browser.executeJavaScript("document.getElementById('id_username').value = '" + username + "'");
}
String password = Config.getInstance().getSettings().chaturbatePassword;
if (password != null && !password.trim().isEmpty()) {
password = password.replace("'", "\\'");
browser.executeJavaScript("document.getElementById('id_password').value = '" + password + "'");
}
var simplify = new String[]{
"$('div#header').css('display','none');",
"$('div#footer-holder').css('display','none')",
};
for (String js : simplify) {
browser.executeJavaScript(js);
}
browser.executeJavaScript("document.querySelector('form[action*=login]').submit()");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.warn("Couldn't auto fill username and password for Chaturbate", e);
} catch (Exception e) {
LOG.warn("Couldn't auto fill username and password for Chaturbate", e);
}
}
if (json.has("cookies")) {
var cookiesFromBrowser = json.getJSONArray("cookies");
for (var i = 0; i < cookiesFromBrowser.length(); i++) {
var cookie = cookiesFromBrowser.getJSONObject(i);
if (cookie.getString("domain").contains(DOMAIN)) {
Builder b = new Builder()
.path(cookie.getString("path"))
.domain(DOMAIN)
.name(cookie.getString("name"))
.value(cookie.getString("value"))
.expiresAt((long) cookie.optDouble("expirationDate"));
if (cookie.optBoolean("hostOnly")) {
b.hostOnlyDomain(DOMAIN);
}
if (cookie.optBoolean("httpOnly")) {
b.httpOnly();
}
if (cookie.optBoolean("secure")) {
b.secure();
}
Cookie c = b.build();
cookieJar.saveFromResponse(HttpUrl.parse(ChaturbateElectronLoginDialog.this.site.getBaseUrl()), Collections.singletonList(c));
}
}
}
try {
if (Objects.equals(new URL(url).getPath(), "/")) {
browser.close();
}
} catch (MalformedURLException e) {
LOG.error("Couldn't parse new url {}", url, e);
} catch (IOException e) {
LOG.error("Couldn't send shutdown request to external browser", e);
}
}
}
};
}

View File

@ -1,14 +1,20 @@
package ctbrec.ui.sites.chaturbate;
import java.io.IOException;
import ctbrec.sites.chaturbate.Chaturbate;
import ctbrec.sites.chaturbate.ChaturbateHttpClient;
import ctbrec.ui.controls.Dialogs;
import ctbrec.ui.sites.AbstractSiteUi;
import ctbrec.ui.sites.ConfigUI;
import ctbrec.ui.tabs.TabProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class ChaturbateSiteUi extends AbstractSiteUi {
private static final Logger LOG = LoggerFactory.getLogger(ChaturbateSiteUi.class);
private final Chaturbate chaturbate;
private ChaturbateTabProvider tabProvider;
private ChaturbateConfigUi configUi;
@ -35,7 +41,26 @@ public class ChaturbateSiteUi extends AbstractSiteUi {
@Override
public synchronized boolean login() throws IOException {
return chaturbate.login();
boolean automaticLogin = false;
try {
automaticLogin = chaturbate.login();
} catch (Exception e) {
LOG.debug("Automatic login failed", e);
}
if (automaticLogin) {
return true;
} else {
// login with external browser window
try {
new ChaturbateElectronLoginDialog(chaturbate, chaturbate.getHttpClient().getCookieJar());
} catch (Exception e1) {
LOG.error("Error logging in with external browser", e1);
Dialogs.showError("Login error", "Couldn't login to " + chaturbate.getName(), e1);
}
ChaturbateHttpClient httpClient = (ChaturbateHttpClient) chaturbate.getHttpClient();
return httpClient.checkLogin();
}
}
}

View File

@ -1,6 +1,12 @@
package ctbrec.sites.chaturbate;
import static ctbrec.io.HttpConstants.*;
import ctbrec.Config;
import ctbrec.io.HtmlParser;
import ctbrec.io.HttpClient;
import okhttp3.*;
import org.jsoup.nodes.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InterruptedIOException;
@ -8,25 +14,16 @@ import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.concurrent.Semaphore;
import org.jsoup.nodes.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ctbrec.Config;
import ctbrec.io.HtmlParser;
import ctbrec.io.HttpClient;
import okhttp3.Cookie;
import okhttp3.FormBody;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import static ctbrec.io.HttpConstants.REFERER;
import static ctbrec.io.HttpConstants.USER_AGENT;
public class ChaturbateHttpClient extends HttpClient {
private static final Logger LOG = LoggerFactory.getLogger(ChaturbateHttpClient.class);
private static final String PATH = "/auth/login/"; // NOSONAR
protected String token;
private static Semaphore requestThrottle = new Semaphore(2, true);
private static final Semaphore requestThrottle = new Semaphore(2, true);
private static long lastRequest = 0;
public ChaturbateHttpClient(Config config) {
@ -37,13 +34,13 @@ public class ChaturbateHttpClient extends HttpClient {
try {
Cookie csrfToken = cookieJar.getCookie(request.url(), "csrftoken");
token = csrfToken.value();
} catch(NoSuchElementException e) {
} catch (NoSuchElementException e) {
LOG.trace("CSRF token not found in cookies");
}
}
public String getToken() throws IOException {
if(token == null) {
if (token == null) {
login();
}
return token;
@ -51,11 +48,11 @@ public class ChaturbateHttpClient extends HttpClient {
@Override
public boolean login() throws IOException {
if(loggedIn) {
if (loggedIn) {
return true;
}
if(checkLogin()) {
if (checkLogin()) {
loggedIn = true;
LOG.debug("Logged in with cookies");
return true;
@ -63,7 +60,7 @@ public class ChaturbateHttpClient extends HttpClient {
try {
Request login = new Request.Builder()
.url(Chaturbate.baseUrl + "/auth/login/")
.url(Chaturbate.baseUrl + PATH)
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
.build();
Response response = client.newCall(login).execute();
@ -78,23 +75,23 @@ public class ChaturbateHttpClient extends HttpClient {
.add("csrfmiddlewaretoken", token)
.build();
login = new Request.Builder()
.url(Chaturbate.baseUrl + "/auth/login/")
.header(REFERER, Chaturbate.baseUrl + "/auth/login/")
.url(Chaturbate.baseUrl + PATH)
.header(REFERER, Chaturbate.baseUrl + PATH)
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
.post(body)
.build();
response = client.newCall(login).execute();
if(response.isSuccessful()) {
if (response.isSuccessful()) {
content = response.body().string();
if(content.contains("Login, Chaturbate login")) {
if (content.contains("Login, Chaturbate login")) {
loggedIn = false;
} else {
loggedIn = true;
extractCsrfToken(login);
}
} else {
if(loginTries++ < 3) {
if (loginTries++ < 3) {
login();
} else {
throw new IOException("Login failed: " + response.code() + " " + response.message());
@ -107,24 +104,25 @@ public class ChaturbateHttpClient extends HttpClient {
return loggedIn;
}
private boolean checkLogin() throws IOException {
public boolean checkLogin() throws IOException {
String url = "https://chaturbate.com/p/" + Config.getInstance().getSettings().chaturbateUsername + "/";
Request req = new Request.Builder()
.url(url)
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
.build();
Response resp = execute(req);
if (resp.isSuccessful()) {
String profilePage = resp.body().string();
try {
Element userIcon = HtmlParser.getTag(profilePage, "img.user_information_header_icon");
return !Objects.equals("Anonymous Icon", userIcon.attr("alt"));
} catch(Exception e) {
LOG.debug("Token tag not found. Login failed");
return false;
try (Response resp = execute(req)) {
if (resp.isSuccessful()) {
String profilePage = resp.body().string();
try {
Element userIcon = HtmlParser.getTag(profilePage, "img.user_information_header_icon");
return !Objects.equals("Anonymous Icon", userIcon.attr("alt"));
} catch (Exception e) {
LOG.debug("Token tag not found. Login failed");
return false;
}
} else {
throw new IOException("HTTP response: " + resp.code() + " - " + resp.message());
}
} else {
throw new IOException("HTTP response: " + resp.code() + " - " + resp.message());
}
}