diff --git a/Clover/app/build.gradle b/Clover/app/build.gradle
index 8eb4f098..ad8ff1fd 100644
--- a/Clover/app/build.gradle
+++ b/Clover/app/build.gradle
@@ -158,4 +158,6 @@ dependencies {
implementation 'org.codejargon.feather:feather:1.0'
releaseImplementation 'ch.acra:acra-http:5.1.3'
+ testImplementation 'junit:junit:4.12'
+ testImplementation 'org.mockito:mockito-core:2.27.0'
}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java b/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java
index d1789fff..cf2793c8 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java
@@ -163,6 +163,7 @@ public class ChanSettings {
public static final BooleanSetting useNewCaptchaWindow;
public static final BooleanSetting useRealGoogleCookies;
public static final StringSetting googleCookie;
+ public static final LongSetting lastGoogleCookieUpdateTime;
static {
SettingProvider p = new SharedPreferencesSettingProvider(AndroidUtils.getPreferences());
@@ -256,6 +257,7 @@ public class ChanSettings {
useNewCaptchaWindow = new BooleanSetting(p, "use_new_captcha_window", true);
useRealGoogleCookies = new BooleanSetting(p, "use_real_google_cookies", false);
googleCookie = new StringSetting(p, "google_cookie", "");
+ lastGoogleCookieUpdateTime = new LongSetting(p, "last_google_cookie_update_time", 0L);
// Old (but possibly still in some users phone)
// preference_board_view_mode default "list"
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/captcha/v2/CaptchaNoJsHtmlParser.java b/Clover/app/src/main/java/org/floens/chan/ui/captcha/v2/CaptchaNoJsHtmlParser.java
index bc22ae89..bd063398 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/captcha/v2/CaptchaNoJsHtmlParser.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/captcha/v2/CaptchaNoJsHtmlParser.java
@@ -123,7 +123,7 @@ public class CaptchaNoJsHtmlParser {
return token;
}
- private void parseChallengeTitle(
+ public void parseChallengeTitle(
String responseHtml,
CaptchaInfo captchaInfo
) throws CaptchaNoJsV2ParsingError {
@@ -240,7 +240,7 @@ public class CaptchaNoJsHtmlParser {
}
}
- private void parseCParameter(
+ public void parseCParameter(
String responseHtml,
CaptchaInfo captchaInfo
) throws CaptchaNoJsV2ParsingError {
@@ -269,7 +269,7 @@ public class CaptchaNoJsHtmlParser {
captchaInfo.setcParameter(cParameter);
}
- private void parseCheckboxes(
+ public void parseCheckboxes(
String responseHtml,
CaptchaInfo captchaInfo
) throws CaptchaNoJsV2ParsingError {
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/captcha/v2/CaptchaNoJsPresenterV2.java b/Clover/app/src/main/java/org/floens/chan/ui/captcha/v2/CaptchaNoJsPresenterV2.java
index 8f391c1c..256ca2c1 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/captcha/v2/CaptchaNoJsPresenterV2.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/captcha/v2/CaptchaNoJsPresenterV2.java
@@ -31,6 +31,7 @@ import java.net.URLEncoder;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import okhttp3.Headers;
@@ -48,7 +49,6 @@ public class CaptchaNoJsPresenterV2 {
private static final String acceptHeader = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3";
private static final String acceptEncodingHeader = "deflate, br";
private static final String acceptLanguageHeader = "en-US";
-
private static final String recaptchaUrlBase = "https://www.google.com/recaptcha/api/fallback?k=";
private static final String googleBaseUrl = "https://www.google.com/";
private static final String encoding = "UTF-8";
@@ -58,6 +58,7 @@ public class CaptchaNoJsPresenterV2 {
private static final String setCookieHeaderName = "set-cookie";
private static final int SUCCESS_STATUS_CODE = 200;
private static final long CAPTCHA_REQUEST_THROTTLE_MS = 3000L;
+ private static final long THREE_MONTHS = TimeUnit.DAYS.toMillis(90);
// this cookie is taken from dashchan
private static final String defaultGoogleCookies = "NID=87=gkOAkg09AKnvJosKq82kgnDnHj8Om2pLskKhdna02msog8HkdHDlasDf";
@@ -135,6 +136,8 @@ public class CaptchaNoJsPresenterV2 {
String recaptchaUrl = recaptchaUrlBase + siteKey;
RequestBody body = createResponseBody(prevCaptchaInfo, selectedIds);
+ Logger.d(TAG, "Verify called. Current cookie = " + googleCookie);
+
Request request = new Request.Builder()
.url(recaptchaUrl)
.post(body)
@@ -282,11 +285,17 @@ public class CaptchaNoJsPresenterV2 {
return defaultGoogleCookies;
}
- if (!forced && !googleCookie.isEmpty()) {
+ boolean isItTimeToUpdateCookies =
+ ((System.currentTimeMillis() - ChanSettings.lastGoogleCookieUpdateTime.get()) > THREE_MONTHS);
+
+ if (!forced && (!googleCookie.isEmpty() && !isItTimeToUpdateCookies)) {
Logger.d(TAG, "We already have google cookies");
return googleCookie;
}
+ Logger.d(TAG, "Time to update cookies: forced = " + forced + ", isCookieEmpty = " +
+ googleCookie.isEmpty() + ", last cookie expired = " + isItTimeToUpdateCookies);
+
Request request = new Request.Builder()
.url(googleBaseUrl)
.header("User-Agent", userAgentHeader)
@@ -297,9 +306,16 @@ public class CaptchaNoJsPresenterV2 {
try (Response response = okHttpClient.newCall(request).execute()) {
String newCookie = handleGetGoogleCookiesResponse(response);
- ChanSettings.googleCookie.set(newCookie);
+ if (!newCookie.equalsIgnoreCase(defaultGoogleCookies)) {
+ ChanSettings.googleCookie.set(newCookie);
+ ChanSettings.lastGoogleCookieUpdateTime.set(System.currentTimeMillis());
+
+ Logger.d(TAG, "Successfully refreshed google cookies, new cookie = " + newCookie);
+ } else {
+ Logger.d(TAG, "Could not successfully handle google cookie response, " +
+ "using the default google cookies until the next request");
+ }
- Logger.d(TAG, "Successfully refreshed google cookies, new cookie = " + newCookie);
return newCookie;
}
}
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BehaviourSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BehaviourSettingsController.java
index 0a5ff7a9..e2f83be1 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/controller/BehaviourSettingsController.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/BehaviourSettingsController.java
@@ -65,8 +65,11 @@ public class BehaviourSettingsController extends SettingsController {
if (!ChanSettings.useNewCaptchaWindow.get()) {
ChanSettings.useRealGoogleCookies.set(false);
- // reset the old google cookie as well
+ // Reset the old google cookie
ChanSettings.googleCookie.set("");
+
+ // and cookie update time as well
+ ChanSettings.lastGoogleCookieUpdateTime.set(0L);
}
rebuildPreferences();
diff --git a/Clover/app/src/main/res/values/strings.xml b/Clover/app/src/main/res/values/strings.xml
index e568791f..93a1c29a 100644
--- a/Clover/app/src/main/res/values/strings.xml
+++ b/Clover/app/src/main/res/values/strings.xml
@@ -582,7 +582,7 @@ Don't have a 4chan Pass? CaptchaUse new captcha window for no-js captchaUse real google cookies instead of hardcoded ones
- When the real google cookies a GET request to the google.com will be executed to get the google cookies (NID) which will be used for captcha authentication. Why would you need them? Because the hardcoded ones sometimes will make you re-enter the captcha dozens of times. Those cookies will be updated automatically (when posting the first time after the app start). But it will also be possible to update them manually if necessary (for example, when the old ones give too many challenges in a row).
+ When this option is enabled a GET request to the google.com will be executed to get the google cookies (NID) which will be used for captcha authentication. Why would you need them? Because the hardcoded ones sometimes will make you re-enter the captcha dozens of times. Those cookies will be updated automatically (every three months). But it will also be possible to update them manually if necessary (for example, when the old ones give too many challenges in a row).VerifyReload
diff --git a/Clover/app/src/test/java/org/floens/chan/ui/captcha/v2/CaptchaNoJsHtmlParserTest.java b/Clover/app/src/test/java/org/floens/chan/ui/captcha/v2/CaptchaNoJsHtmlParserTest.java
new file mode 100644
index 00000000..37a2f32a
--- /dev/null
+++ b/Clover/app/src/test/java/org/floens/chan/ui/captcha/v2/CaptchaNoJsHtmlParserTest.java
@@ -0,0 +1,104 @@
+package org.floens.chan.ui.captcha.v2;
+
+import android.content.Context;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import okhttp3.OkHttpClient;
+
+import static org.junit.Assert.assertEquals;
+
+public class CaptchaNoJsHtmlParserTest {
+ private List inputDataList = new ArrayList<>();
+ private List> resultCheckboxList = new ArrayList<>();
+ private List resultTitleList = new ArrayList<>();
+ private List resultCParameterList = new ArrayList<>();
+
+ private Context contextMock = Mockito.mock(Context.class);
+ private OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class);
+ private CaptchaNoJsHtmlParser parser = new CaptchaNoJsHtmlParser(contextMock, okHttpClient);
+
+ @Before
+ public void setUp() {
+ inputDataList.add("reCAPTCHA challenge