com.nttec.everychan.http.recaptcha.Recaptcha2fallback.java Source code

Java tutorial

Introduction

Here is the source code for com.nttec.everychan.http.recaptcha.Recaptcha2fallback.java

Source

/*
 * Everychan 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 com.nttec.everychan.http.recaptcha;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.nttec.everychan.api.HttpChanModule;
import com.nttec.everychan.api.interfaces.CancellableTask;
import com.nttec.everychan.api.util.RegexUtils;
import com.nttec.everychan.common.Async;
import com.nttec.everychan.common.Logger;
import com.nttec.everychan.common.MainApplication;
import com.nttec.everychan.http.interactive.InteractiveException;
import com.nttec.everychan.http.streamer.HttpRequestModel;
import com.nttec.everychan.http.streamer.HttpResponseModel;
import com.nttec.everychan.http.streamer.HttpStreamer;
import com.nttec.everychan.lib.base64.Base64;
import com.nttec.everychan.ui.AppearanceUtils;
import com.nttec.everychan.ui.CompatibilityUtils;

import org.apache.commons.lang3.tuple.Pair;
import cz.msebera.android.httpclient.Header;
import cz.msebera.android.httpclient.HttpHeaders;
import cz.msebera.android.httpclient.NameValuePair;
import cz.msebera.android.httpclient.client.HttpClient;
import cz.msebera.android.httpclient.client.entity.UrlEncodedFormEntity;
import cz.msebera.android.httpclient.message.BasicHeader;
import cz.msebera.android.httpclient.message.BasicNameValuePair;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;

public class Recaptcha2fallback extends InteractiveException {
    private static final long serialVersionUID = 1L;

    private static final String TAG = "Recaptcha2fallback";
    private static Pair<String, String> lastChallenge = null;

    private static final String RECAPTCHA_FALLBACK_URL = "://www.google.com/recaptcha/api/fallback?k=";
    private static final String RECAPTCHA_IMAGE_URL = "://www.google.com/recaptcha/api2/payload?c=";

    private String chanName;
    private String scheme;
    private String baseUrl;
    private String publicKey;
    private String sToken;

    @Override
    public String getServiceName() {
        return "Recaptcha (fallback)";
    }

    /**
     * @param baseUrl URL, ?   ?? 
     * @param publicKey  
     * @param sToken Secure Token
     * @param chanName  ?  (   {@link HttpChanModule})
     */
    public Recaptcha2fallback(String baseUrl, String publicKey, String sToken, String chanName) {
        this.chanName = chanName;
        this.scheme = "https";
        this.baseUrl = baseUrl;
        this.publicKey = publicKey;
        this.sToken = sToken;
    }

    @Override
    public void handle(final Activity activity, final CancellableTask task, final Callback callback) {
        try {
            final HttpClient httpClient = ((HttpChanModule) MainApplication.getInstance().getChanModule(chanName))
                    .getHttpClient();
            final String usingURL = scheme + RECAPTCHA_FALLBACK_URL + publicKey
                    + (sToken != null && sToken.length() > 0 ? ("&stoken=" + sToken) : "");
            String refererURL = baseUrl != null && baseUrl.length() > 0 ? baseUrl : usingURL;
            Header[] customHeaders = new Header[] { new BasicHeader(HttpHeaders.REFERER, refererURL) };
            String htmlChallenge;
            if (lastChallenge != null && lastChallenge.getLeft().equals(usingURL)) {
                htmlChallenge = lastChallenge.getRight();
            } else {
                htmlChallenge = HttpStreamer.getInstance().getStringFromUrl(usingURL,
                        HttpRequestModel.builder().setGET().setCustomHeaders(customHeaders).build(), httpClient,
                        null, task, false);
            }
            lastChallenge = null;

            Matcher challengeMatcher = Pattern.compile("name=\"c\" value=\"([\\w-]+)").matcher(htmlChallenge);
            if (challengeMatcher.find()) {
                final String challenge = challengeMatcher.group(1);
                HttpResponseModel responseModel = HttpStreamer.getInstance().getFromUrl(
                        scheme + RECAPTCHA_IMAGE_URL + challenge + "&k=" + publicKey,
                        HttpRequestModel.builder().setGET().setCustomHeaders(customHeaders).build(), httpClient,
                        null, task);
                try {
                    InputStream imageStream = responseModel.stream;
                    final Bitmap challengeBitmap = BitmapFactory.decodeStream(imageStream);

                    final String message;
                    Matcher messageMatcher = Pattern.compile("imageselect-message(?:.*?)>(.*?)</div>")
                            .matcher(htmlChallenge);
                    if (messageMatcher.find())
                        message = RegexUtils.removeHtmlTags(messageMatcher.group(1));
                    else
                        message = null;

                    final Bitmap candidateBitmap;
                    Matcher candidateMatcher = Pattern
                            .compile("fbc-imageselect-candidates(?:.*?)src=\"data:image/(?:.*?);base64,([^\"]*)\"")
                            .matcher(htmlChallenge);
                    if (candidateMatcher.find()) {
                        Bitmap bmp = null;
                        try {
                            byte[] imgData = Base64.decode(candidateMatcher.group(1), Base64.DEFAULT);
                            bmp = BitmapFactory.decodeByteArray(imgData, 0, imgData.length);
                        } catch (Exception e) {
                        }
                        candidateBitmap = bmp;
                    } else
                        candidateBitmap = null;

                    activity.runOnUiThread(new Runnable() {
                        final int maxX = 3;
                        final int maxY = 3;
                        final boolean[] isSelected = new boolean[maxX * maxY];

                        @SuppressLint("InlinedApi")
                        @Override
                        public void run() {
                            LinearLayout rootLayout = new LinearLayout(activity);
                            rootLayout.setOrientation(LinearLayout.VERTICAL);

                            if (candidateBitmap != null) {
                                ImageView candidateView = new ImageView(activity);
                                candidateView.setImageBitmap(candidateBitmap);
                                int picSize = (int) (activity.getResources().getDisplayMetrics().density * 50
                                        + 0.5f);
                                candidateView.setLayoutParams(new LinearLayout.LayoutParams(picSize, picSize));
                                candidateView.setScaleType(ImageView.ScaleType.FIT_XY);
                                rootLayout.addView(candidateView);
                            }

                            if (message != null) {
                                TextView textView = new TextView(activity);
                                textView.setText(message);
                                CompatibilityUtils.setTextAppearance(textView, android.R.style.TextAppearance);
                                textView.setLayoutParams(
                                        new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                                                LinearLayout.LayoutParams.WRAP_CONTENT));
                                rootLayout.addView(textView);
                            }

                            FrameLayout frame = new FrameLayout(activity);
                            frame.setLayoutParams(
                                    new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                                            LinearLayout.LayoutParams.WRAP_CONTENT));

                            final ImageView imageView = new ImageView(activity);
                            imageView.setLayoutParams(new FrameLayout.LayoutParams(
                                    FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
                            imageView.setScaleType(ImageView.ScaleType.FIT_XY);
                            imageView.setImageBitmap(challengeBitmap);
                            frame.addView(imageView);

                            final LinearLayout selector = new LinearLayout(activity);
                            selector.setLayoutParams(new FrameLayout.LayoutParams(
                                    FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
                            AppearanceUtils.callWhenLoaded(imageView, new Runnable() {
                                @Override
                                public void run() {
                                    selector.setLayoutParams(new FrameLayout.LayoutParams(imageView.getWidth(),
                                            imageView.getHeight()));
                                }
                            });
                            selector.setOrientation(LinearLayout.VERTICAL);
                            selector.setWeightSum(maxY);
                            for (int y = 0; y < maxY; ++y) {
                                LinearLayout subSelector = new LinearLayout(activity);
                                subSelector.setLayoutParams(new LinearLayout.LayoutParams(
                                        LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f));
                                subSelector.setOrientation(LinearLayout.HORIZONTAL);
                                subSelector.setWeightSum(maxX);
                                for (int x = 0; x < maxX; ++x) {
                                    FrameLayout switcher = new FrameLayout(activity);
                                    switcher.setLayoutParams(new LinearLayout.LayoutParams(0,
                                            LinearLayout.LayoutParams.MATCH_PARENT, 1f));
                                    switcher.setTag(new int[] { x, y });
                                    switcher.setOnClickListener(new View.OnClickListener() {
                                        @Override
                                        public void onClick(View v) {
                                            int[] coord = (int[]) v.getTag();
                                            int index = coord[1] * maxX + coord[0];
                                            isSelected[index] = !isSelected[index];
                                            v.setBackgroundColor(isSelected[index] ? Color.argb(128, 0, 255, 0)
                                                    : Color.TRANSPARENT);
                                        }
                                    });
                                    subSelector.addView(switcher);
                                }
                                selector.addView(subSelector);
                            }

                            frame.addView(selector);
                            rootLayout.addView(frame);

                            Button checkButton = new Button(activity);
                            checkButton.setLayoutParams(
                                    new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                                            LinearLayout.LayoutParams.WRAP_CONTENT));
                            checkButton.setText(android.R.string.ok);
                            rootLayout.addView(checkButton);

                            ScrollView dlgView = new ScrollView(activity);
                            dlgView.addView(rootLayout);

                            final Dialog dialog = new Dialog(activity);
                            dialog.setTitle("Recaptcha");
                            dialog.setContentView(dlgView);
                            dialog.setCanceledOnTouchOutside(false);
                            dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
                                @Override
                                public void onCancel(DialogInterface dialog) {
                                    if (!task.isCancelled()) {
                                        callback.onError("Cancelled");
                                    }
                                }
                            });
                            dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
                                    ViewGroup.LayoutParams.WRAP_CONTENT);
                            dialog.show();

                            checkButton.setOnClickListener(new View.OnClickListener() {
                                @Override
                                public void onClick(View v) {
                                    dialog.dismiss();
                                    if (task.isCancelled())
                                        return;
                                    Async.runAsync(new Runnable() {
                                        @Override
                                        public void run() {
                                            try {
                                                List<NameValuePair> pairs = new ArrayList<NameValuePair>();
                                                pairs.add(new BasicNameValuePair("c", challenge));
                                                for (int i = 0; i < isSelected.length; ++i)
                                                    if (isSelected[i])
                                                        pairs.add(new BasicNameValuePair("response",
                                                                Integer.toString(i)));

                                                HttpRequestModel request = HttpRequestModel.builder()
                                                        .setPOST(new UrlEncodedFormEntity(pairs, "UTF-8"))
                                                        .setCustomHeaders(new Header[] {
                                                                new BasicHeader(HttpHeaders.REFERER, usingURL) })
                                                        .build();
                                                String response = HttpStreamer.getInstance().getStringFromUrl(
                                                        usingURL, request, httpClient, null, task, false);
                                                String hash = "";
                                                Matcher matcher = Pattern.compile(
                                                        "fbc-verification-token(?:.*?)<textarea[^>]*>([^<]*)<",
                                                        Pattern.DOTALL).matcher(response);
                                                if (matcher.find())
                                                    hash = matcher.group(1);

                                                if (hash.length() > 0) {
                                                    Recaptcha2solved.push(publicKey, hash);
                                                    activity.runOnUiThread(new Runnable() {
                                                        @Override
                                                        public void run() {
                                                            callback.onSuccess();
                                                        }
                                                    });
                                                } else {
                                                    lastChallenge = Pair.of(usingURL, response);
                                                    throw new RecaptchaException(
                                                            "incorrect answer (hash is empty)");
                                                }
                                            } catch (final Exception e) {
                                                Logger.e(TAG, e);
                                                if (task.isCancelled())
                                                    return;
                                                handle(activity, task, callback);
                                            }
                                        }
                                    });
                                }
                            });
                        }
                    });
                } finally {
                    responseModel.release();
                }
            } else
                throw new Exception("can't parse recaptcha challenge answer");
        } catch (final Exception e) {
            Logger.e(TAG, e);
            if (!task.isCancelled()) {
                activity.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        callback.onError(e.getMessage() != null ? e.getMessage() : e.toString());
                    }
                });
            }
        }
    }

}