/* * Overchan Android (Meta Imageboard Client) * Copyright (C) 2014-2016 miku-nyan <https://github.com/miku-nyan> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package nya.miku.wishmaster.api; import java.io.OutputStream; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; import android.preference.CheckBoxPreference; import android.preference.PreferenceGroup; import cz.msebera.android.httpclient.cookie.Cookie; import cz.msebera.android.httpclient.impl.cookie.BasicClientCookie; import nya.miku.wishmaster.R; import nya.miku.wishmaster.api.interfaces.CancellableTask; import nya.miku.wishmaster.api.interfaces.ProgressListener; import nya.miku.wishmaster.api.util.LazyPreferences; import nya.miku.wishmaster.http.cloudflare.CloudflareException; import nya.miku.wishmaster.http.streamer.HttpRequestModel; import nya.miku.wishmaster.http.streamer.HttpStreamer; import nya.miku.wishmaster.http.streamer.HttpWrongStatusCodeException; import nya.miku.wishmaster.lib.org_json.JSONArray; import nya.miku.wishmaster.lib.org_json.JSONObject; public abstract class CloudflareChanModule extends AbstractChanModule { protected static final String PREF_KEY_CLOUDFLARE_RECAPTCHA_FALLBACK = "PREF_KEY_CLOUDFLARE_RECAPTCHA_FALLBACK"; protected static final String PREF_KEY_CLOUDFLARE_COOKIE_VALUE = "PREF_KEY_CLOUDFLARE_COOKIE"; protected static final String PREF_KEY_CLOUDFLARE_COOKIE_DOMAIN = "PREF_KEY_CLOUDFLARE_COOKIE_DOMAIN"; protected static final String CLOUDFLARE_COOKIE_NAME = "cf_clearance"; protected static final String CLOUDFLARE_RECAPTCHA_KEY = "6LfOYgoTAAAAAInWDVTLSc8Yibqp-c9DaLimzNGM"; public CloudflareChanModule(SharedPreferences preferences, Resources resources) { super(preferences, resources); } protected boolean canCloudflare() { return true; } protected String getCloudflareCookieDomain() { return preferences.getString(getSharedKey(PREF_KEY_CLOUDFLARE_COOKIE_DOMAIN), null); } @Override protected void initHttpClient() { if (canCloudflare()) { String cloudflareCookieValue = preferences.getString(getSharedKey(PREF_KEY_CLOUDFLARE_COOKIE_VALUE), null); String cloudflareCookieDomain = getCloudflareCookieDomain(); if (cloudflareCookieValue != null && cloudflareCookieDomain != null) { BasicClientCookie c = new BasicClientCookie(CLOUDFLARE_COOKIE_NAME, cloudflareCookieValue); c.setDomain(cloudflareCookieDomain); httpClient.getCookieStore().addCookie(c); } } } @Override public void saveCookie(Cookie cookie) { super.saveCookie(cookie); if (cookie != null) { if (canCloudflare() && cookie.getName().equals(CLOUDFLARE_COOKIE_NAME)) { preferences.edit(). putString(getSharedKey(PREF_KEY_CLOUDFLARE_COOKIE_VALUE), cookie.getValue()). putString(getSharedKey(PREF_KEY_CLOUDFLARE_COOKIE_DOMAIN), cookie.getDomain()).commit(); } } } protected void checkCloudflareError(HttpWrongStatusCodeException e, String url) throws CloudflareException { if (e.getStatusCode() == 403) { String html = e.getHtmlString(); if (html != null && html.contains("CAPTCHA")) { throw CloudflareException.withRecaptcha(url, getChanName(), html, cloudflareRecaptchaFallback()); } } else if (e.getStatusCode() == 503) { String html = e.getHtmlString(); if (html != null && html.contains("Just a moment...")) { throw CloudflareException.antiDDOS(url, getChanName()); } } } @Override public void addPreferencesOnScreen(PreferenceGroup preferenceGroup) { super.addPreferencesOnScreen(preferenceGroup); addCloudflareRecaptchaFallbackPreference(preferenceGroup); } protected void addCloudflareRecaptchaFallbackPreference(PreferenceGroup preferenceGroup) { if (canCloudflare()) { Context context = preferenceGroup.getContext(); CheckBoxPreference fallbackPref = new LazyPreferences.CheckBoxPreference(context); fallbackPref.setTitle(R.string.pref_cf_recaptcha_fallback); fallbackPref.setSummary(R.string.pref_cf_recaptcha_fallback_summary); fallbackPref.setKey(getSharedKey(PREF_KEY_CLOUDFLARE_RECAPTCHA_FALLBACK)); fallbackPref.setDefaultValue(false); preferenceGroup.addPreference(fallbackPref); } } protected boolean cloudflareRecaptchaFallback() { return preferences.getBoolean(getSharedKey(PREF_KEY_CLOUDFLARE_RECAPTCHA_FALLBACK), false); } @Override public void downloadFile(String url, OutputStream out, ProgressListener listener, CancellableTask task) throws Exception { String fixedUrl = fixRelativeUrl(url); try { HttpRequestModel rqModel = HttpRequestModel.DEFAULT_GET; HttpStreamer.getInstance().downloadFileFromUrl(fixedUrl, out, rqModel, httpClient, listener, task, canCloudflare()); } catch (HttpWrongStatusCodeException e) { if (canCloudflare()) checkCloudflareError(e, fixedUrl); throw e; } } @Override protected JSONObject downloadJSONObject(String url, boolean checkIfModidied, ProgressListener listener, CancellableTask task) throws Exception { try { HttpRequestModel rqModel = HttpRequestModel.builder().setGET().setCheckIfModified(checkIfModidied).build(); JSONObject object = HttpStreamer.getInstance().getJSONObjectFromUrl(url, rqModel, httpClient, listener, task, canCloudflare()); if (task != null && task.isCancelled()) throw new Exception("interrupted"); if (listener != null) listener.setIndeterminate(); return object; } catch (HttpWrongStatusCodeException e) { if (canCloudflare()) checkCloudflareError(e, url); throw e; } } @Override protected JSONArray downloadJSONArray(String url, boolean checkIfModidied, ProgressListener listener, CancellableTask task) throws Exception { try { HttpRequestModel rqModel = HttpRequestModel.builder().setGET().setCheckIfModified(checkIfModidied).build(); JSONArray array = HttpStreamer.getInstance().getJSONArrayFromUrl(url, rqModel, httpClient, listener, task, canCloudflare()); if (task != null && task.isCancelled()) throw new Exception("interrupted"); if (listener != null) listener.setIndeterminate(); return array; } catch (HttpWrongStatusCodeException e) { if (canCloudflare()) checkCloudflareError(e, url); throw e; } } }