com.nttec.everychan.chans.ponyach.PonyachModule.java Source code

Java tutorial

Introduction

Here is the source code for com.nttec.everychan.chans.ponyach.PonyachModule.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.chans.ponyach;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.tuple.Pair;
import cz.msebera.android.httpclient.Header;
import cz.msebera.android.httpclient.HttpHeaders;
import cz.msebera.android.httpclient.client.HttpClient;
import cz.msebera.android.httpclient.client.entity.UrlEncodedFormEntity;
import cz.msebera.android.httpclient.cookie.Cookie;
import cz.msebera.android.httpclient.impl.cookie.BasicClientCookie;
import cz.msebera.android.httpclient.message.BasicNameValuePair;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.support.v4.content.res.ResourcesCompat;
import android.text.InputFilter;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.Toast;
import com.nttec.everychan.R;
import com.nttec.everychan.api.AbstractWakabaModule;
import com.nttec.everychan.api.interfaces.CancellableTask;
import com.nttec.everychan.api.interfaces.ProgressListener;
import com.nttec.everychan.api.models.AttachmentModel;
import com.nttec.everychan.api.models.BoardModel;
import com.nttec.everychan.api.models.PostModel;
import com.nttec.everychan.api.models.SendPostModel;
import com.nttec.everychan.api.models.SimpleBoardModel;
import com.nttec.everychan.api.models.UrlPageModel;
import com.nttec.everychan.api.util.ChanModels;
import com.nttec.everychan.api.util.LazyPreferences;
import com.nttec.everychan.api.util.WakabaReader;
import com.nttec.everychan.common.Async;
import com.nttec.everychan.common.IOUtils;
import com.nttec.everychan.common.Logger;
import com.nttec.everychan.common.MainApplication;
import com.nttec.everychan.http.ExtendedMultipartBuilder;
import com.nttec.everychan.http.interactive.InteractiveException;
import com.nttec.everychan.http.streamer.HttpRequestException;
import com.nttec.everychan.http.streamer.HttpRequestModel;
import com.nttec.everychan.http.streamer.HttpResponseModel;
import com.nttec.everychan.http.streamer.HttpStreamer;

@SuppressLint("SimpleDateFormat")
public class PonyachModule extends AbstractWakabaModule {
    private static final String TAG = "PonyachModule";
    private static final String CHAN_NAME = "ponyach";
    private static final String DEFAULT_DOMAIN = "ponyach.ru";
    private static final String[] DOMAINS = new String[] { DEFAULT_DOMAIN, "ponychan.ru", "ponya.ch", "ponyach.cf",
            "ponyach.ga", "ponyach.ml" };

    private static final DateFormat DATE_FORMAT;
    static {
        DateFormatSymbols symbols = new DateFormatSymbols();
        symbols.setMonths(new String[] { "", "", "", "?", "", "", "",
                "?", "", "", "??", "" });
        DATE_FORMAT = new SimpleDateFormat("dd MMMM yyyy HH:mm:ss", symbols);
        DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+3"));
    }

    private static final SimpleBoardModel[] BOARDS = new SimpleBoardModel[] {
            ChanModels.obtainSimpleBoardModel(CHAN_NAME, "b", "/b/ - was never good", "", true),
            ChanModels.obtainSimpleBoardModel(CHAN_NAME, "d", "  ?", "", false),
            ChanModels.obtainSimpleBoardModel(CHAN_NAME, "tea", "? ", "", false),
            ChanModels.obtainSimpleBoardModel(CHAN_NAME, "test", "", "", false),
            ChanModels.obtainSimpleBoardModel(CHAN_NAME, "vg", "", "", false),
            ChanModels.obtainSimpleBoardModel(CHAN_NAME, "oc", " ", "", false),
            //ChanModels.obtainSimpleBoardModel(CHAN_NAME, "r34", "r34", "", true),
            ChanModels.obtainSimpleBoardModel(CHAN_NAME, "rf", "", "", true), };

    private static final String PREF_KEY_DOMAIN = "PREF_KEY_DOMAIN";
    private static final String PREF_KEY_CAPTCHA_LEVEL = "PREF_KEY_CAPTCHA_LEVEL";
    private static final String PREF_KEY_PHPSESSION_COOKIE = "PREF_KEY_PHPSESSION_COOKIE";
    private static final String PHPSESSION_COOKIE_NAME = "PHPSESSID";
    private static final String CAPTCHATYPE_COOKIE_NAME = "captcha_type";

    private static final Pattern ERROR_PATTERN = Pattern.compile("<h2[^>]*>(.*?)</h2>", Pattern.DOTALL);

    public PonyachModule(SharedPreferences preferences, Resources resources) {
        super(preferences, resources);
    }

    @Override
    public String getChanName() {
        return CHAN_NAME;
    }

    @Override
    public String getDisplayingName() {
        return "?.";
    }

    @Override
    public Drawable getChanFavicon() {
        return ResourcesCompat.getDrawable(resources, R.drawable.favicon_ponyach, null);
    }

    @Override
    protected String[] getAllDomains() {
        return DOMAINS;
    }

    @Override
    protected boolean canHttps() {
        return true;
    }

    @Override
    protected String getUsingDomain() {
        String domain = preferences.getString(getSharedKey(PREF_KEY_DOMAIN), DEFAULT_DOMAIN);
        return TextUtils.isEmpty(domain) ? DEFAULT_DOMAIN : domain;
    }

    @Override
    protected void initHttpClient() {
        super.initHttpClient();
        loadPhpCookies();
    }

    private void loadPhpCookies() {
        loadPhpCookies(getUsingDomain());
    }

    private void loadPhpCookies(String usingDomain) {
        String phpSessionCookie = preferences.getString(getSharedKey(PREF_KEY_PHPSESSION_COOKIE), null);
        if (phpSessionCookie != null) {
            BasicClientCookie c = new BasicClientCookie(PHPSESSION_COOKIE_NAME, phpSessionCookie);
            c.setDomain(usingDomain);
            httpClient.getCookieStore().addCookie(c);
        }
    }

    private void savePhpCookies() {
        for (Cookie cookie : httpClient.getCookieStore().getCookies()) {
            if (cookie.getName().equalsIgnoreCase(PHPSESSION_COOKIE_NAME)
                    && cookie.getDomain().contains(getUsingDomain())) {
                preferences.edit().putString(getSharedKey(PREF_KEY_PHPSESSION_COOKIE), cookie.getValue()).commit();
            }
        }
    }

    @Override
    public void addPreferencesOnScreen(PreferenceGroup preferenceGroup) {
        final Context context = preferenceGroup.getContext();
        ListPreference captchaLevel = new LazyPreferences.ListPreference(context);
        captchaLevel.setTitle(R.string.ponyach_prefs_captcha);
        captchaLevel.setDialogTitle(R.string.ponyach_prefs_captcha);
        captchaLevel.setKey(getSharedKey(PREF_KEY_CAPTCHA_LEVEL));
        captchaLevel.setEntryValues(new String[] { "3", "2", "1" });
        captchaLevel.setEntries(new String[] { "Easy", "Easy++", "Medium" });
        captchaLevel.setDefaultValue("1");
        preferenceGroup.addPreference(captchaLevel);

        EditTextPreference passcodePref = new EditTextPreference(context);
        passcodePref.setTitle(R.string.ponyach_prefs_passcode);
        passcodePref.setDialogTitle(R.string.ponyach_prefs_passcode);
        passcodePref.getEditText().setFilters(new InputFilter[] { new InputFilter.LengthFilter(6) });
        passcodePref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                final String newPasscode = (String) newValue;
                final CancellableTask passAuthTask = new CancellableTask.BaseCancellableTask();
                final ProgressDialog passAuthProgressDialog = new ProgressDialog(context);
                passAuthProgressDialog.setMessage("Logging in");
                passAuthProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialog) {
                        passAuthTask.cancel();
                    }
                });
                passAuthProgressDialog.setCanceledOnTouchOutside(false);
                passAuthProgressDialog.show();
                Async.runAsync(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            if (passAuthTask.isCancelled())
                                return;
                            String url = getUsingUrl() + "passcode.php";
                            List<BasicNameValuePair> pairs = Collections
                                    .singletonList(new BasicNameValuePair("passcode_just_set", newPasscode));
                            HttpRequestModel request = HttpRequestModel.builder()
                                    .setPOST(new UrlEncodedFormEntity(pairs, "UTF-8")).build();
                            HttpStreamer.getInstance().getStringFromUrl(url, request, httpClient, null,
                                    passAuthTask, false);
                            savePhpCookies();
                        } catch (final Exception e) {
                            if (context instanceof Activity) {
                                ((Activity) context).runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        String message = e.getMessage() == null
                                                ? resources.getString(R.string.error_unknown)
                                                : e.getMessage();
                                        Toast.makeText(context, message, Toast.LENGTH_LONG).show();
                                    }
                                });
                            }
                        } finally {
                            passAuthProgressDialog.dismiss();
                        }
                    }
                });
                return false;
            }
        });
        preferenceGroup.addPreference(passcodePref);

        ListPreference domainPref = new LazyPreferences.ListPreference(context);
        domainPref.setTitle(R.string.pref_domain);
        domainPref.setDialogTitle(R.string.pref_domain);
        domainPref.setKey(getSharedKey(PREF_KEY_DOMAIN));
        domainPref.setEntryValues(DOMAINS);
        domainPref.setEntries(DOMAINS);
        domainPref.setDefaultValue(DEFAULT_DOMAIN);
        domainPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                loadPhpCookies((String) newValue);
                return true;
            }
        });
        preferenceGroup.addPreference(domainPref);

        addHttpsPreference(preferenceGroup, useHttpsDefaultValue());
        addProxyPreferences(preferenceGroup);
        captchaLevel.setSummary(captchaLevel.getEntry());
        domainPref.setSummary(domainPref.getEntry());
    }

    @Override
    protected WakabaReader getWakabaReader(InputStream stream, UrlPageModel urlModel) {
        return new WakabaReader(stream, DATE_FORMAT, canCloudflare()) {
            private final Pattern aHrefPattern = Pattern.compile("<a\\s+href=\"(.*?)\"", Pattern.DOTALL);
            private final Pattern attachmentSizePattern = Pattern.compile("([\\d\\.]+)[KM]B");
            private final Pattern attachmentPxSizePattern = Pattern.compile("(\\d+)x(\\d+)");
            private final char[] dateFilter = "<span class=\"mobile_date dast-date\">".toCharArray();
            private final char[] attachmentFilter = "<span class=\"filesize fs_".toCharArray();
            private ArrayList<AttachmentModel> myAttachments = new ArrayList<>();
            private int curDatePos = 0;
            private int curAttachmentPos = 0;

            @Override
            protected void customFilters(int ch) throws IOException {
                if (ch == dateFilter[curDatePos]) {
                    ++curDatePos;
                    if (curDatePos == dateFilter.length) {
                        parseDate(readUntilSequence("</span>".toCharArray()).trim());
                        curDatePos = 0;
                    }
                } else {
                    if (curDatePos != 0)
                        curDatePos = ch == dateFilter[0] ? 1 : 0;
                }

                if (ch == attachmentFilter[curAttachmentPos]) {
                    ++curAttachmentPos;
                    if (curAttachmentPos == attachmentFilter.length) {
                        skipUntilSequence(">".toCharArray());
                        myParseAttachment(readUntilSequence("</span>".toCharArray()));
                        curAttachmentPos = 0;
                    }
                } else {
                    if (curAttachmentPos != 0)
                        curAttachmentPos = ch == attachmentFilter[0] ? 1 : 0;
                }
            }

            @Override
            protected void parseDate(String date) {
                date = date.substring(date.indexOf(' ') + 1);
                super.parseDate(date);
            }

            private void myParseAttachment(String html) {
                Matcher aHrefMatcher = aHrefPattern.matcher(html);
                if (aHrefMatcher.find()) {
                    AttachmentModel attachment = new AttachmentModel();
                    attachment.path = aHrefMatcher.group(1);
                    attachment.thumbnail = attachment.path.replaceAll("/src/(\\d+)/(?:.*?)\\.(.*?)$",
                            "/thumb/$1s.$2");
                    if (attachment.thumbnail.equals(attachment.path)) {
                        attachment.thumbnail = null;
                    } else {
                        attachment.thumbnail = attachment.thumbnail.replace(".webm", ".png");
                    }

                    String ext = attachment.path.substring(attachment.path.lastIndexOf('.') + 1);
                    switch (ext) {
                    case "jpg":
                    case "jpeg":
                    case "png":
                        attachment.type = AttachmentModel.TYPE_IMAGE_STATIC;
                        break;
                    case "gif":
                        attachment.type = AttachmentModel.TYPE_IMAGE_GIF;
                        break;
                    case "svg":
                    case "svgz":
                        attachment.type = AttachmentModel.TYPE_IMAGE_SVG;
                        break;
                    case "webm":
                    case "mp4":
                        attachment.type = AttachmentModel.TYPE_VIDEO;
                        break;
                    default:
                        attachment.type = AttachmentModel.TYPE_OTHER_FILE;
                    }

                    Matcher sizeMatcher = attachmentSizePattern.matcher(html);
                    if (sizeMatcher.find()) {
                        try {
                            int mul = sizeMatcher.group(0).endsWith("MB") ? 1024 : 1;
                            attachment.size = Math.round(Float.parseFloat(sizeMatcher.group(1)) * mul);
                        } catch (Exception e) {
                            attachment.size = -1;
                        }
                        try {
                            Matcher pxSizeMatcher = attachmentPxSizePattern.matcher(html);
                            if (!pxSizeMatcher.find(sizeMatcher.end()))
                                throw new Exception();
                            attachment.width = Integer.parseInt(pxSizeMatcher.group(1));
                            attachment.height = Integer.parseInt(pxSizeMatcher.group(2));
                        } catch (Exception e) {
                            attachment.width = -1;
                            attachment.height = -1;
                        }
                    } else {
                        attachment.size = -1;
                        attachment.width = -1;
                        attachment.height = -1;
                    }

                    myAttachments.add(attachment);
                }
            }

            @Override
            protected void postprocessPost(PostModel post) {
                post.attachments = myAttachments.toArray(new AttachmentModel[myAttachments.size()]);
                myAttachments.clear();
            }
        };
    }

    @Override
    protected SimpleBoardModel[] getBoardsList() {
        return BOARDS;
    }

    @Override
    public BoardModel getBoard(String shortName, ProgressListener listener, CancellableTask task) throws Exception {
        BoardModel board = super.getBoard(shortName, listener, task);
        board.timeZoneId = "GMT+3";
        board.defaultUserName = "?";
        board.uniqueAttachmentNames = false;
        board.readonlyBoard = false;
        board.requiredFileForNewThread = true;
        board.allowDeletePosts = false;
        board.allowDeleteFiles = false;
        board.allowReport = BoardModel.REPORT_NOT_ALLOWED;
        board.allowNames = true;
        board.allowSubjects = true;
        board.allowSage = true;
        board.allowEmails = true;
        board.ignoreEmailIfSage = true;
        board.allowCustomMark = false;
        board.allowRandomHash = true;
        board.allowIcons = false;
        board.attachmentsMaxCount = 5;
        board.markType = BoardModel.MARK_BBCODE;
        return board;
    }

    @Override
    public String sendPost(SendPostModel model, ProgressListener listener, CancellableTask task) throws Exception {
        if (HttpStreamer.getInstance().getStringFromUrl(getUsingUrl() + "haikaptcha.php?m=isndn",
                HttpRequestModel.DEFAULT_GET, httpClient, null, task, false).equals("1")) {
            throw new HaikuCaptchaException();
        }

        String url = getUsingUrl() + "board.php";
        ExtendedMultipartBuilder postEntityBuilder = ExtendedMultipartBuilder.create().setDelegates(listener, task)
                .addString("board", model.boardName)
                .addString("replythread", model.threadNumber == null ? "0" : model.threadNumber)
                .addString("name", model.name).addString("em", model.sage ? "sage" : model.email)
                .addString("subject", model.subject).addString("message", model.comment);

        int filesCount = model.attachments != null ? model.attachments.length : 0;
        for (int i = 0; i < filesCount; ++i)
            postEntityBuilder.addFile("upload[]", model.attachments[i], model.randomHash);

        HttpRequestModel request = HttpRequestModel.builder().setPOST(postEntityBuilder.build()).setNoRedirect(true)
                .build();
        HttpResponseModel response = null;
        try {
            response = HttpStreamer.getInstance().getFromUrl(url, request, httpClient, null, task);
            if (response.statusCode == 302) {
                for (Header header : response.headers) {
                    if (header != null && HttpHeaders.LOCATION.equalsIgnoreCase(header.getName())) {
                        return fixRelativeUrl(
                                header.getValue().replaceAll("res/0[05]{2}(\\d+)\\.html", "res/$1\\.html"));
                    }
                }
            } else if (response.statusCode == 200) {
                ByteArrayOutputStream output = new ByteArrayOutputStream(1024);
                IOUtils.copyStream(response.stream, output);
                String htmlResponse = output.toString("UTF-8");
                Matcher errorMatcher = ERROR_PATTERN.matcher(htmlResponse);
                if (errorMatcher.find())
                    throw new Exception(errorMatcher.group(1));
                if (htmlResponse.contains("<strong> </strong>"))
                    throw new Exception(" ");
            }
            throw new Exception(response.statusCode + " - " + response.statusReason);
        } finally {
            if (response != null)
                response.release();
            savePhpCookies();
        }
    }

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

        private static final Pattern IMG_PATTERN = Pattern.compile("<img(.*?)>");
        private static final Pattern SRC_PATTERN = Pattern.compile("src=\"(.*?)\"");
        private static final Pattern HAIKU_PATTERN = Pattern.compile("onclick=\"haiku\\('(.*?)'\\);\"");

        @Override
        public String getServiceName() {
            return "Haiku Captcha";
        }

        private Bitmap getBitmap(String url, CancellableTask task) throws HttpRequestException {
            PonyachModule thisModule = ((PonyachModule) MainApplication.getInstance().getChanModule(CHAN_NAME));
            String baseUrl = thisModule.getUsingUrl();
            if (url.startsWith("/"))
                url = baseUrl + url.substring(1);
            HttpClient httpClient = thisModule.httpClient;
            HttpRequestModel requestModel = HttpRequestModel.DEFAULT_GET;
            HttpResponseModel responseModel = HttpStreamer.getInstance().getFromUrl(url, requestModel, httpClient,
                    null, task);
            try {
                InputStream imageStream = responseModel.stream;
                return BitmapFactory.decodeStream(imageStream);
            } finally {
                responseModel.release();
            }
        }

        private void setCaptchaTypeCookie() {
            PonyachModule thisModule = ((PonyachModule) MainApplication.getInstance().getChanModule(CHAN_NAME));
            String level = thisModule.preferences.getString(thisModule.getSharedKey(PREF_KEY_CAPTCHA_LEVEL), "1");
            BasicClientCookie cookie = new BasicClientCookie(CAPTCHATYPE_COOKIE_NAME, level);
            cookie.setDomain(thisModule.getUsingDomain());
            thisModule.httpClient.getCookieStore().addCookie(cookie);
        }

        @Override
        public void handle(final Activity activity, final CancellableTask task, final Callback callback) {
            try {
                setCaptchaTypeCookie();
                String haiku = HttpStreamer.getInstance().getStringFromUrl(
                        ((PonyachModule) MainApplication.getInstance().getChanModule(CHAN_NAME)).getUsingUrl()
                                + "haikaptcha.php?m=get",
                        HttpRequestModel.DEFAULT_GET,
                        ((PonyachModule) MainApplication.getInstance().getChanModule(CHAN_NAME)).httpClient, null,
                        task, false);
                //Logger.d(TAG, "haiku response: " + haiku);
                if (task.isCancelled())
                    throw new Exception();
                String captcha = null;
                final List<Pair<String, String>> answers = new ArrayList<>();
                Matcher imgMatcher = IMG_PATTERN.matcher(haiku);
                while (imgMatcher.find()) {
                    String img = imgMatcher.group(1);
                    Matcher srcMatcher = SRC_PATTERN.matcher(img);
                    if (srcMatcher.find()) {
                        String src = srcMatcher.group(1);
                        if (img.contains("onclick=\"haiku()\"")) {
                            captcha = src;
                        } else {
                            Matcher haikuMatcher = HAIKU_PATTERN.matcher(img);
                            if (haikuMatcher.find()) {
                                answers.add(Pair.of(src, haikuMatcher.group(1)));
                            }
                        }
                    }
                }
                if (captcha == null || answers.isEmpty())
                    throw new Exception();

                final Bitmap captchaBmp = getBitmap(captcha, task);
                if (task.isCancelled())
                    throw new Exception();
                final Bitmap[] answersBmp = new Bitmap[answers.size()];
                for (int i = 0; i < answersBmp.length; ++i) {
                    answersBmp[i] = getBitmap(answers.get(i).getLeft(), task);
                    if (task.isCancelled())
                        throw new Exception();
                }

                activity.runOnUiThread(new Runnable() {
                    @SuppressLint("InlinedApi")
                    @Override
                    public void run() {
                        final Dialog dialog = new Dialog(activity);

                        LinearLayout mainLayout = new LinearLayout(activity);
                        mainLayout.setOrientation(LinearLayout.VERTICAL);
                        final ImageView captchaView = new ImageView(activity);
                        captchaView.setLayoutParams(new LinearLayout.LayoutParams(
                                LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
                        captchaView.setImageBitmap(captchaBmp);
                        captchaView.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                dialog.dismiss();
                                if (task.isCancelled())
                                    return;
                                Async.runAsync(new Runnable() {
                                    @Override
                                    public void run() {
                                        handle(activity, task, callback);
                                    }
                                });
                            }
                        });
                        mainLayout.addView(captchaView);

                        LinearLayout answersLayout = new LinearLayout(activity);
                        answersLayout.setOrientation(LinearLayout.HORIZONTAL);
                        answersLayout.setWeightSum(answersBmp.length);
                        answersLayout.setLayoutParams(new LinearLayout.LayoutParams(
                                LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
                        for (int i = 0; i < answersBmp.length; ++i) {
                            ImageView answer = new ImageView(activity);
                            answer.setLayoutParams(
                                    new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1));
                            answer.setBackgroundColor(Color.WHITE);
                            answer.setImageBitmap(answersBmp[i]);
                            answer.setTag(answers.get(i).getRight());
                            answer.setOnClickListener(new View.OnClickListener() {
                                @Override
                                public void onClick(final View v) {
                                    dialog.dismiss();
                                    if (task.isCancelled())
                                        return;
                                    Async.runAsync(new Runnable() {
                                        @Override
                                        public void run() {
                                            String checkUrl = ((PonyachModule) MainApplication.getInstance()
                                                    .getChanModule(CHAN_NAME)).getUsingUrl()
                                                    + "haikaptcha.php?m=chk&a=" + (String) v.getTag();
                                            HttpRequestModel request = HttpRequestModel.DEFAULT_GET;
                                            String response = null;
                                            try {
                                                response = HttpStreamer.getInstance().getStringFromUrl(checkUrl,
                                                        request,
                                                        ((PonyachModule) MainApplication.getInstance()
                                                                .getChanModule(CHAN_NAME)).httpClient,
                                                        null, task, false);
                                            } catch (Exception e) {
                                                Logger.e(TAG, e);
                                            }
                                            if (task.isCancelled())
                                                return;
                                            try {
                                                if (response != null) {
                                                    Matcher m = Pattern.compile("haiku_wait\\((\\d+)\\)")
                                                            .matcher(response);
                                                    if (m.find()) {
                                                        Integer time = Integer.parseInt(m.group(1));
                                                        Logger.d(TAG, "incorrect");
                                                        Logger.d(TAG, "response: " + response);
                                                        Logger.d(TAG, "waiting " + time + " seconds");
                                                        Thread.sleep(time * 1000);
                                                        throw new Exception("try again");
                                                    }
                                                    ((PonyachModule) MainApplication.getInstance()
                                                            .getChanModule(CHAN_NAME)).savePhpCookies();
                                                    activity.runOnUiThread(new Runnable() {
                                                        @Override
                                                        public void run() {
                                                            callback.onSuccess();
                                                        }
                                                    });
                                                } else
                                                    throw new Exception("response == null");
                                            } catch (Exception e) {
                                                Logger.e(TAG, e);
                                                if (task.isCancelled())
                                                    return;
                                                handle(activity, task, callback);
                                            }
                                        }
                                    });
                                }
                            });
                            answersLayout.addView(answer);
                        }
                        mainLayout.addView(answersLayout);

                        ScrollView dlgLayout = new ScrollView(activity);
                        dlgLayout.addView(mainLayout);

                        dialog.setTitle("Haiku Captcha");
                        dialog.setContentView(dlgLayout);
                        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.MATCH_PARENT);
                        dialog.show();
                    }
                });

            } catch (Exception e) {
                Logger.e(TAG, e);
                activity.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        callback.onError("Error");
                    }
                });
            }

        }

    }
}