diff --git a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatElectronLoginDialog.java b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatElectronLoginDialog.java index 6b1e8306..1a896b15 100644 --- a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatElectronLoginDialog.java +++ b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatElectronLoginDialog.java @@ -1,15 +1,15 @@ package ctbrec.ui.sites.stripchat; - -import lombok.extern.slf4j.Slf4j; import ctbrec.Config; import ctbrec.sites.stripchat.Stripchat; import ctbrec.ui.ExternalBrowser; +import lombok.extern.slf4j.Slf4j; import okhttp3.Cookie; import okhttp3.Cookie.Builder; import okhttp3.CookieJar; import okhttp3.HttpUrl; import org.json.JSONObject; + import java.io.IOException; import java.util.Collections; import java.util.function.Consumer; 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 9dad9b66..002692d8 100644 --- a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatSiteUi.java +++ b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatSiteUi.java @@ -1,17 +1,18 @@ package ctbrec.ui.sites.stripchat; - -import lombok.extern.slf4j.Slf4j; import java.io.IOException; + 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 lombok.extern.slf4j.Slf4j; @Slf4j public class StripchatSiteUi extends AbstractSiteUi { + private StripchatTabProvider tabProvider; private StripchatConfigUI configUi; private Stripchat site; diff --git a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatUpdateService.java b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatUpdateService.java index 701a23a7..cfedbb19 100644 --- a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatUpdateService.java +++ b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatUpdateService.java @@ -1,7 +1,5 @@ package ctbrec.ui.sites.stripchat; - -import lombok.extern.slf4j.Slf4j; import ctbrec.Config; import ctbrec.Model; import ctbrec.io.HttpException; @@ -9,8 +7,10 @@ import ctbrec.sites.stripchat.Stripchat; import ctbrec.sites.stripchat.StripchatModel; import ctbrec.ui.SiteUiFactory; import javafx.concurrent.Task; +import lombok.extern.slf4j.Slf4j; import okhttp3.Request; import org.json.JSONObject; + import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -21,6 +21,7 @@ import static ctbrec.io.HttpConstants.*; @Slf4j public class StripchatUpdateService extends AbstractStripchatUpdateService { + private final String url; private final boolean loginRequired; private final Stripchat stripchat; diff --git a/common/src/main/java/ctbrec/sites/stripchat/StripchatHttpClient.java b/common/src/main/java/ctbrec/sites/stripchat/StripchatHttpClient.java index d2d21fbc..c85f99b4 100644 --- a/common/src/main/java/ctbrec/sites/stripchat/StripchatHttpClient.java +++ b/common/src/main/java/ctbrec/sites/stripchat/StripchatHttpClient.java @@ -1,38 +1,35 @@ package ctbrec.sites.stripchat; - -import lombok.extern.slf4j.Slf4j; import ctbrec.Config; import ctbrec.StringUtil; import ctbrec.io.HttpClient; import ctbrec.io.HttpException; -import lombok.Getter; +// import ctbrec.sites.stripchat.Stripchat; import lombok.extern.slf4j.Slf4j; import okhttp3.*; import org.json.JSONException; import org.json.JSONObject; -import java.time.Instant; -import java.util.Base64; -import java.nio.charset.StandardCharsets; import java.io.IOException; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Base64; import static ctbrec.io.HttpConstants.*; import static java.nio.charset.StandardCharsets.UTF_8; @Slf4j public class StripchatHttpClient extends HttpClient { - + Stripchat stripchatInstance = new Stripchat(); public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); - private long userId = 0; + private long userId; + private String csrfToken; - @Getter private String csrfTimestamp; - @Getter private String csrfNotifyTimestamp; - @Getter private String jwtToken; private Instant jwtTokenExp; @@ -42,11 +39,9 @@ public class StripchatHttpClient extends HttpClient { @Override public boolean login() throws IOException { - // refresh token if needed - getCsrfToken(); + getCsrfToken(); // Always load CSRF token (as in v5.0.24) - // already logged in - if (loggedIn && isJwtTokenValid()) { + if (loggedIn && isJwtTokenValid()) { // Ensure JWT is valid as in v5.0.24 return true; } @@ -54,63 +49,75 @@ public class StripchatHttpClient extends HttpClient { if (checkLoginSuccess()) { loggedIn = true; log.debug("Logged in with cookies"); + // Check if CSRF token needs to be loaded after logging in via cookies + if (csrfToken == null) { + loadCsrfToken(); + } return true; } - + // Proceed to the login HTTP request if not already logged in String url = Stripchat.getBaseUri() + "/api/front/auth/login"; - JSONObject requestParams = new JSONObject(); - requestParams.put("loginOrEmail", config.getSettings().stripchatUsername); - requestParams.put("password", config.getSettings().stripchatPassword); - requestParams.put("remember", true); - requestParams.put("csrfToken", csrfToken); - requestParams.put("csrfTimestamp", csrfTimestamp); - requestParams.put("csrfNotifyTimestamp", csrfNotifyTimestamp); + JSONObject requestParams = new JSONObject() + .put("loginOrEmail", config.getSettings().stripchatUsername) + .put("password", config.getSettings().stripchatPassword) + .put("remember", true) + .put("csrfToken", csrfToken) + .put("csrfTimestamp", csrfTimestamp) + .put("csrfNotifyTimestamp", csrfNotifyTimestamp); RequestBody body = RequestBody.Companion.create(requestParams.toString(), JSON); Request request = new Request.Builder() - .url(url) - .header(ACCEPT, MIMETYPE_APPLICATION_JSON) - .header(USER_AGENT, config.getSettings().httpUserAgent) - .header(ORIGIN, Stripchat.getBaseUri()) - .header(REFERER, Stripchat.getBaseUri()) - .header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON) - .post(body) - .build(); + .url(url) + .header(ACCEPT, MIMETYPE_APPLICATION_JSON) + .header(USER_AGENT, config.getSettings().httpUserAgent) + .header(ORIGIN, Stripchat.getBaseUri()) + .header(REFERER, Stripchat.getBaseUri()) + .header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON) + .post(body) + .build(); + try (Response response = execute(request)) { if (response.isSuccessful()) { JSONObject resp = new JSONObject(response.body().string()); if (resp.has("user")) { JSONObject user = resp.getJSONObject("user"); userId = user.optLong("id"); - return true; + return true; // Successful login } else { - return false; + return false; // No user found } } else { log.info("Auto-Login failed: {} {} {}", response.code(), response.message(), url); - return false; + return false; // Request failed } } } private void loadCsrfToken() throws IOException { - String url = Stripchat.getBaseUri() + "/api/front/v3/config/initial?requestPath=%2F&timezoneOffset=0"; - Request request = new Request.Builder() - .url(url) - .header(ACCEPT, MIMETYPE_APPLICATION_JSON) - .header(USER_AGENT, config.getSettings().httpUserAgent) - .header(ORIGIN, Stripchat.getBaseUri()) - .header(REFERER, Stripchat.getBaseUri()) - .header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON) - .build(); + csrfToken = ""; + csrfTimestamp = ""; + csrfNotifyTimestamp = ""; + + String url = Stripchat.getBaseUri() + "/api/front/v3/config/initial?requestPath=%2F&timezoneOffset=0&disableClient=0&uniq=" + stripchatInstance.getUniq(); + Request request = buildJsonGetRequest(url); + try (Response response = execute(request)) { if (response.isSuccessful()) { JSONObject resp = new JSONObject(response.body().string()); - JSONObject data = resp.getJSONObject("initial").getJSONObject("client"); - csrfToken = data.optString("csrfToken"); - csrfTimestamp = data.optString("csrfTimestamp"); - csrfNotifyTimestamp = data.optString("csrfNotifyTimestamp"); - log.debug("Stripchat token CSRF: {} [{}]", csrfToken, csrfNotifyTimestamp); + JSONObject initial = resp.optJSONObject("initial"); + if (initial != null) { + JSONObject client = initial.optJSONObject("client"); + if (client != null) { + csrfToken = client.optString("csrfToken"); + csrfTimestamp = client.optString("csrfTimestamp"); + csrfNotifyTimestamp = client.optString("csrfNotifyTimestamp"); + + log.debug("Stripchat CSRF token (v3): {} [{}]", csrfToken, csrfNotifyTimestamp); + } + } + if (csrfToken.isBlank()) { + log.debug("Stripchat CSRF token not found"); + } } else { throw new HttpException(response.code(), response.message()); } @@ -118,29 +125,30 @@ public class StripchatHttpClient extends HttpClient { } private void loadJwtToken() throws IOException { - String url = Stripchat.getBaseUri() + "/api/front/v3/config/dynamic"; - Request request = new Request.Builder() - .url(url) - .header(ACCEPT, MIMETYPE_APPLICATION_JSON) - .header(USER_AGENT, config.getSettings().httpUserAgent) - .header(ORIGIN, Stripchat.getBaseUri()) - .header(REFERER, Stripchat.getBaseUri()) - .header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON) - .build(); + jwtToken = ""; + + String url = Stripchat.getBaseUri() + "/api/front/v3/config/dynamic?uniq=" + stripchatInstance.getUniq(); + Request request = buildJsonGetRequest(url); + try (Response response = execute(request)) { if (response.isSuccessful()) { JSONObject resp = new JSONObject(response.body().string()); - JSONObject dynamic = resp.getJSONObject("dynamic"); - jwtToken = dynamic.optString("jwtToken"); - if (StringUtil.isNotBlank(jwtToken)) { + JSONObject dynamic = resp.optJSONObject("dynamic"); + if (dynamic != null) { + jwtToken = dynamic.optString("jwtToken"); + } + + if (jwtToken != null && !jwtToken.isBlank()) { String[] parts = jwtToken.split("\\."); if (parts.length > 1) { - String decString = new String(Base64.getDecoder().decode(parts[1]), StandardCharsets.UTF_8); - JSONObject body = new JSONObject(decString); + String payload = new String(Base64.getDecoder().decode(parts[1]), StandardCharsets.UTF_8); + JSONObject body = new JSONObject(payload); jwtTokenExp = Instant.ofEpochSecond(body.optLong("exp")); } + log.debug("Stripchat JWT token (v3): {} [{}]", jwtToken, jwtTokenExp); + } else { + log.debug("Stripchat JWT token not found"); } - log.debug("Stripchat token JWT: {} [{}]", jwtToken, jwtTokenExp); } else { throw new HttpException(response.code(), response.message()); } @@ -152,22 +160,22 @@ public class StripchatHttpClient extends HttpClient { * @throws IOException */ public boolean checkLoginSuccess() throws IOException { - getJwtToken(); - getCsrfToken(); + try { + getJwtToken(); + getCsrfToken(); + } catch (Exception e) { + log.info("Login check returned unsuccessful: {}", e.getLocalizedMessage()); + jwtToken = ""; + csrfToken = ""; + return false; + } return StringUtil.isNotBlank(jwtToken) && StringUtil.isNotBlank(csrfToken); } public long getUserId() throws JSONException, IOException { - if (userId == 0) { + if (userId == 0L) { String url = Stripchat.getBaseUri() + "/api/front/users/username/" + config.getSettings().stripchatUsername; - Request request = new Request.Builder() - .url(url) - .header(ACCEPT, MIMETYPE_APPLICATION_JSON) - .header(USER_AGENT, config.getSettings().httpUserAgent) - .header(ORIGIN, Stripchat.getBaseUri()) - .header(REFERER, Stripchat.getBaseUri()) - .header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON) - .build(); + Request request = buildJsonGetRequest(url); try (Response response = execute(request)) { if (response.isSuccessful()) { JSONObject resp = new JSONObject(response.body().string()); @@ -186,6 +194,7 @@ public class StripchatHttpClient extends HttpClient { Cookie cookie = getCookieJar().getCookie(HttpUrl.parse(Stripchat.getBaseUri()), "baseAmpl"); String json = URLDecoder.decode(cookie.value(), UTF_8); JSONObject ampl = new JSONObject(json); + ampl.put("ep", new JSONObject()); return ampl; } catch (Exception ex) { return new JSONObject(); @@ -203,11 +212,11 @@ public class StripchatHttpClient extends HttpClient { public String getCsrfToken() { try { if (!isTokenValid()) { - loadCsrfToken(); + loadCsrfToken(); // Uses v3 API load } } catch (Exception e) { log.debug("Invalid CSRF Token {}: {}", csrfToken, e.getMessage()); - csrfToken = ""; + csrfToken = ""; // Reset token on failure } return csrfToken; } @@ -215,11 +224,11 @@ public class StripchatHttpClient extends HttpClient { public String getJwtToken() { try { if (!isJwtTokenValid()) { - loadJwtToken(); + loadJwtToken(); // Uses v3 API load } } catch (Exception e) { log.debug("Invalid JWT Token {}: {}", jwtToken, e.getMessage()); - jwtToken = ""; + jwtToken = ""; // Reset token on failure } return jwtToken; } @@ -228,7 +237,7 @@ public class StripchatHttpClient extends HttpClient { if (StringUtil.isBlank(jwtToken) || jwtTokenExp == null) { return false; } - return jwtTokenExp.isAfter(Instant.now()); + return jwtTokenExp.isAfter(Instant.now().minus(3L, ChronoUnit.HOURS)); } private boolean isTokenValid() { @@ -237,9 +246,22 @@ public class StripchatHttpClient extends HttpClient { } try { Instant notifyTime = Instant.parse(csrfNotifyTimestamp); - return notifyTime.isAfter(Instant.now()); - } catch (Exception e) { + return notifyTime.isAfter(Instant.now().minus(3L, ChronoUnit.HOURS)); + } + catch (Exception e) { return false; } } + + private Request buildJsonGetRequest(String url) { + return new Request.Builder() + .url(url) + .header(ACCEPT, MIMETYPE_APPLICATION_JSON) + .header(USER_AGENT, config.getSettings().httpUserAgent) + .header(ORIGIN, Stripchat.getBaseUri()) + .header(REFERER, Stripchat.getBaseUri()) + .header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON) + .build(); + } + }