package ctbrec.sites.chaturbate; import ctbrec.Config; import ctbrec.io.HtmlParser; import ctbrec.io.HttpClient; import okhttp3.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InterruptedIOException; import java.util.NoSuchElementException; import java.util.concurrent.Semaphore; 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 final Semaphore requestThrottle = new Semaphore(2, true); private static long lastRequest = 0; public ChaturbateHttpClient(Config config) { super("chaturbate", config); } private void extractCsrfToken(Request request) { try { Cookie csrfToken = cookieJar.getCookie(request.url(), "csrftoken"); token = csrfToken.value(); } catch (NoSuchElementException e) { LOG.trace("CSRF token not found in cookies"); } } public String getToken() throws IOException { if (token == null) { login(); } return token; } @Override public boolean login() throws IOException { if (loggedIn) { return true; } if (checkLogin()) { loggedIn = true; LOG.debug("Logged in with cookies"); return true; } try { Request login = new Request.Builder() .url(Chaturbate.baseUrl + PATH) .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) .build(); Response response = client.newCall(login).execute(); String content = response.body().string(); token = HtmlParser.getTag(content, "input[name=csrfmiddlewaretoken]").attr("value"); LOG.debug("csrf token is {}", token); RequestBody body = new FormBody.Builder() .add("username", Config.getInstance().getSettings().chaturbateUsername) .add("password", Config.getInstance().getSettings().chaturbatePassword) .add("next", "") .add("csrfmiddlewaretoken", token) .build(); login = new Request.Builder() .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()) { content = response.body().string(); if (content.contains("Login, Chaturbate login")) { loggedIn = false; } else { loggedIn = true; extractCsrfToken(login); } } } catch (Exception ex) { LOG.debug("Login failed: {}", ex.getMessage()); } return loggedIn; } public boolean checkLogin() throws IOException { String url = "https://chaturbate.com/api/ts/chatmessages/pm_users/?offset=0"; Request req = new Request.Builder() .url(url) .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) .build(); try (Response resp = execute(req)) { return (resp.isSuccessful() && !resp.isRedirect()); } catch (Exception ex) { return false; } } @Override public Response execute(Request req) throws IOException { boolean throttled = req.url().host().contains("chaturbate.com"); return executeThrottled(req, throttled); } private Response executeThrottled(Request req, boolean throttle) throws IOException { try { if (throttle) { acquireSlot(); } Response resp = super.execute(req); extractCsrfToken(req); return resp; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new InterruptedIOException("Interrupted during request"); } finally { if (throttle) { releaseSlot(); } } } private static void acquireSlot() throws InterruptedException { long pauseBetweenRequests = Config.getInstance().getSettings().chaturbateMsBetweenRequests; requestThrottle.acquire(); long now = System.currentTimeMillis(); long millisSinceLastRequest = now - lastRequest; if (millisSinceLastRequest < pauseBetweenRequests) { Thread.sleep(pauseBetweenRequests - millisSinceLastRequest); } } private static void releaseSlot() { lastRequest = System.currentTimeMillis(); requestThrottle.release(); } }