nya.miku.wishmaster.http.cloudflare.CloudflareChecker.java Source code

Java tutorial

Introduction

Here is the source code for nya.miku.wishmaster.http.cloudflare.CloudflareChecker.java

Source

/*
 * Overchan Android (Meta Imageboard Client)
 * Copyright (C) 2014-2015  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.http.cloudflare;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.Locale;

import nya.miku.wishmaster.api.interfaces.CancellableTask;
import nya.miku.wishmaster.common.CompatibilityImpl;
import nya.miku.wishmaster.common.Logger;
import nya.miku.wishmaster.http.HttpConstants;
import nya.miku.wishmaster.http.client.ExtendedHttpClient;
import nya.miku.wishmaster.http.recaptcha.Recaptcha;
import nya.miku.wishmaster.http.recaptcha.RecaptchaException;
import nya.miku.wishmaster.http.streamer.HttpRequestModel;
import nya.miku.wishmaster.http.streamer.HttpResponseModel;
import nya.miku.wishmaster.http.streamer.HttpStreamer;
import nya.miku.wishmaster.lib.WebViewProxy;

import org.apache.http.HttpHost;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.params.ConnRouteParams;
import org.apache.http.cookie.Cookie;
import org.apache.http.cookie.SetCookie;
import org.apache.http.impl.cookie.BasicClientCookieHC4;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
import android.webkit.WebViewClient;

/**
 *  Cloudflare-
 * @author miku-nyan
 *
 */

/* Google  ? ??  ?  org.apache.http  "deprecated"  API 22 (Android 5.1)
 * ? ?  ??? ? ?? apache-hc httpclient 4.3.5.1-android
 * ?: https://issues.apache.org/jira/browse/HTTPCLIENT-1632 */
@SuppressWarnings("deprecation")

public class CloudflareChecker {
    private static final String TAG = "CloudflareChecker";

    /**   -?   ? */
    public static final long TIMEOUT = 35 * 1000;

    private CloudflareChecker() {
    }

    private static CloudflareChecker instance;

    /**  -? */
    public static synchronized CloudflareChecker getInstance() {
        if (instance == null)
            instance = new CloudflareChecker();
        return instance;
    }

    /**  false, ?    (?-) -?  ??? */
    public boolean isAvaibleAntiDDOS() {
        return !(processing || InterceptingAntiDDOS.getInstance().isProcessing());
    }

    /**
     *  -?  cloudflare
     * @param exception Cloudflare ?
     * @param httpClient HTTP 
     * @param task ?? 
     * @param activity ?,  ?    WebView (webkit)
     * @return ? cookie  null, ?     ,       
     */
    public Cookie checkAntiDDOS(CloudflareException exception, HttpClient httpClient, CancellableTask task,
            Activity activity) {
        if (exception.isRecaptcha())
            throw new IllegalArgumentException();

        HttpHost proxy = null;
        if (httpClient instanceof ExtendedHttpClient) {
            proxy = ((ExtendedHttpClient) httpClient).getProxy();
        } else if (httpClient != null) {
            try {
                proxy = ConnRouteParams.getDefaultProxy(httpClient.getParams());
            } catch (Exception e) {
                /*ignore*/ }
        }
        if (proxy != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            if (httpClient instanceof ExtendedHttpClient) {
                return InterceptingAntiDDOS.getInstance().check(exception, (ExtendedHttpClient) httpClient, task,
                        activity);
            } else {
                throw new IllegalArgumentException(
                        "cannot run anti-DDOS checking with proxy settings; http client is not instance of ExtendedHttpClient");
            }
        } else {
            return checkAntiDDOS(exception, proxy, task, activity);
        }
    }

    private volatile boolean processing = false;
    private volatile boolean processing2 = false;
    private volatile Cookie currentCookie;
    private volatile WebView webView;
    private volatile Context webViewContext;
    private Object lock = new Object();

    private Cookie checkAntiDDOS(final CloudflareException exception, final HttpHost proxy, CancellableTask task,
            final Activity activity) {
        synchronized (lock) {
            if (processing)
                return null;
            processing = true;
        }
        processing2 = true;
        currentCookie = null;

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            CookieSyncManager.createInstance(activity);
            CookieManager.getInstance().removeAllCookie();
        } else {
            CompatibilityImpl.clearCookies(CookieManager.getInstance());
        }

        final ViewGroup layout = (ViewGroup) activity.getWindow().getDecorView().getRootView();
        final WebViewClient client = new WebViewClient() {
            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                handler.proceed();
            }

            @Override
            public void onPageFinished(WebView webView, String url) {
                super.onPageFinished(webView, url);
                Logger.d(TAG, "Got Page: " + url);
                String value = null;
                try {
                    String[] cookies = CookieManager.getInstance().getCookie(url).split("[;]");
                    for (String cookie : cookies) {
                        if ((cookie != null) && (!cookie.trim().equals(""))
                                && (cookie.startsWith(" " + exception.getRequiredCookieName() + "="))) {
                            value = cookie.substring(exception.getRequiredCookieName().length() + 2);
                        }
                    }
                } catch (NullPointerException e) {
                    Logger.e(TAG, e);
                }
                if (value != null) {
                    BasicClientCookieHC4 cf_cookie = new BasicClientCookieHC4(exception.getRequiredCookieName(),
                            value);
                    cf_cookie.setDomain("." + Uri.parse(url).getHost());
                    cf_cookie.setPath("/");
                    currentCookie = cf_cookie;
                    Logger.d(TAG, "Cookie found: " + value);
                    processing2 = false;
                } else {
                    Logger.d(TAG, "Cookie is not found");
                }
            }
        };

        activity.runOnUiThread(new Runnable() {
            @SuppressLint("SetJavaScriptEnabled")
            @Override
            public void run() {
                webView = new WebView(activity);
                webView.setVisibility(View.GONE);
                layout.addView(webView);
                webView.setWebViewClient(client);
                webView.getSettings().setUserAgentString(HttpConstants.USER_AGENT_STRING);
                webView.getSettings().setJavaScriptEnabled(true);
                webViewContext = webView.getContext();
                if (proxy != null)
                    WebViewProxy.setProxy(webViewContext, proxy.getHostName(), proxy.getPort());
                webView.loadUrl(exception.getCheckUrl());
            }
        });

        long startTime = System.currentTimeMillis();
        while (processing2) {
            long time = System.currentTimeMillis() - startTime;
            if ((task != null && task.isCancelled()) || time > TIMEOUT) {
                processing2 = false;
            }
        }

        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                try {
                    layout.removeView(webView);
                    webView.stopLoading();
                    webView.clearCache(true);
                    webView.destroy();
                    webView = null;
                } finally {
                    if (proxy != null)
                        WebViewProxy.setProxy(webViewContext, null, 0);
                    processing = false;
                }
            }
        });

        return currentCookie;
    }

    /**
     *   ?   cloudflare 
     * @param exception Cloudflare ?
     * @param httpClient HTTP 
     * @param task ?? 
     * @return ? 
     */
    public Recaptcha getRecaptcha(CloudflareException exception, HttpClient httpClient, CancellableTask task)
            throws RecaptchaException {
        if (!exception.isRecaptcha())
            throw new IllegalArgumentException();
        String scheme = exception.getCheckCaptchaUrlFormat().startsWith("https") ? "https" : "http";
        return Recaptcha.obtain(exception.getRecaptchaPublicKey(), task, httpClient, scheme);
    }

    /**
     *   cloudflare,  cookie
     * @param exception Cloudflare ?
     * @param httpClient HTTP 
     * @param task ?? 
     * @param challenge challenge 
     * @param recaptchaAnswer   
     * @return ? cookie  null, ?   
     */
    public Cookie checkRecaptcha(CloudflareException exception, ExtendedHttpClient httpClient, CancellableTask task,
            String challenge, String recaptchaAnswer) {
        if (!exception.isRecaptcha())
            throw new IllegalArgumentException("wrong type of CloudflareException");
        try {
            recaptchaAnswer = URLEncoder.encode(recaptchaAnswer, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            Logger.e(TAG, e);
        }
        String url = String.format(Locale.US, exception.getCheckCaptchaUrlFormat(), challenge, recaptchaAnswer);
        HttpResponseModel responseModel = null;
        try {
            HttpRequestModel rqModel = HttpRequestModel.builder().setGET().setNoRedirect(false).build();
            CookieStore cookieStore = httpClient.getCookieStore();
            removeCookie(cookieStore, exception.getRequiredCookieName());
            responseModel = HttpStreamer.getInstance().getFromUrl(url, rqModel, httpClient, null, task);
            for (int i = 0; i < 3 && responseModel.statusCode == 400; ++i) {
                Logger.d(TAG, "HTTP 400");
                responseModel.release();
                responseModel = HttpStreamer.getInstance().getFromUrl(url, rqModel, httpClient, null, task);
            }
            for (Cookie cookie : cookieStore.getCookies()) {
                if (isClearanceCookie(cookie, url, exception.getRequiredCookieName())) {
                    Logger.d(TAG, "Cookie found: " + cookie.getValue());
                    return cookie;
                }
            }
            Logger.d(TAG, "Cookie is not found");
        } catch (Exception e) {
            Logger.e(TAG, e);
        } finally {
            if (responseModel != null) {
                responseModel.release();
            }
        }
        return null;
    }

    static boolean isClearanceCookie(Cookie cookie, String url, String requiredCookieName) {
        try {
            String cookieName = cookie.getName();
            String cookieDomain = cookie.getDomain();
            if (!cookieDomain.startsWith(".")) {
                cookieDomain = "." + cookieDomain;
            }

            String urlCookie = "." + Uri.parse(url).getHost();
            if (cookieName.equals(requiredCookieName) && cookieDomain.equalsIgnoreCase(urlCookie)) {
                return true;
            }
        } catch (Exception e) {
            Logger.e(TAG, e);
        }
        return false;
    }

    static void removeCookie(CookieStore store, String name) {
        boolean flag = false;
        for (Cookie cookie : store.getCookies()) {
            if (cookie.getName().equals(name)) {
                if (cookie instanceof SetCookie) {
                    flag = true;
                    ((SetCookie) cookie).setExpiryDate(new Date(0));
                } else {
                    Logger.e(TAG,
                            "cannot remove cookie (object does not implement SetCookie): " + cookie.toString());
                }
            }
        }
        if (flag)
            store.clearExpired(new Date());
    }
}