@Override public CaptchaModel getNewCaptcha( String boardName, String threadNumber, ProgressListener listener, CancellableTask task) throws Exception { HttpRequestModel requestModel = HttpRequestModel.builder().setGET().build(); String html = HttpStreamer.getInstance() .getStringFromUrl( getUsingUrl() + "captcha_update.php", requestModel, httpClient, null, task, false); Matcher matcher = CAPTCHA_KEY.matcher(html); if (matcher.find()) { String captchaUrl = getUsingUrl() + "simple-php-captcha.php?_CAPTCHA&t=" + matcher.group(1); Bitmap captchaBitmap = null; HttpResponseModel responseModel = HttpStreamer.getInstance() .getFromUrl(captchaUrl, requestModel, httpClient, listener, task); try { InputStream imageStream = responseModel.stream; captchaBitmap = BitmapFactory.decodeStream(imageStream); } finally { responseModel.release(); } CaptchaModel captchaModel = new CaptchaModel(); captchaModel.type = CaptchaModel.TYPE_NORMAL_DIGITS; captchaModel.bitmap = captchaBitmap; return captchaModel; } else throw new Exception("Captcha update epic fail"); }
protected ThreadModel[] readWakabaPage( String url, ProgressListener listener, CancellableTask task, boolean checkModified, UrlPageModel urlModel) throws Exception { HttpResponseModel responseModel = null; WakabaReader in = null; HttpRequestModel rqModel = HttpRequestModel.builder() .setGET() .setCheckIfModified(checkModified) .setNoRedirect(wakabaNoRedirect()) .build(); try { responseModel = HttpStreamer.getInstance().getFromUrl(url, rqModel, httpClient, listener, task); if (responseModel.statusCode == 200) { in = getWakabaReader(responseModel.stream, urlModel); if (task != null && task.isCancelled()) throw new Exception("interrupted"); return in.readWakabaPage(); } else { if (responseModel.notModified()) return null; if (canCloudflare()) { String html = null; try { ByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024); IOUtils.copyStream(responseModel.stream, byteStream); html = byteStream.toString("UTF-8"); } catch (Exception e) { } if (html != null) { if (responseModel.statusCode == 403 && html.contains("CAPTCHA")) { throw CloudflareException.withRecaptcha( CLOUDFLARE_RECAPTCHA_KEY, getUsingUrl() + CLOUDFLARE_RECAPTCHA_CHECK_URL_FMT, CLOUDFLARE_COOKIE_NAME, getChanName()); } else if (responseModel.statusCode == 503 && html.contains("Just a moment...")) { throw CloudflareException.antiDDOS(url, CLOUDFLARE_COOKIE_NAME, getChanName()); } } } throw new HttpWrongStatusCodeException( responseModel.statusCode, responseModel.statusCode + " - " + responseModel.statusReason); } } catch (Exception e) { if (responseModel != null) HttpStreamer.getInstance().removeFromModifiedMap(url); throw e; } finally { IOUtils.closeQuietly(in); if (responseModel != null) responseModel.release(); } }
@Override public String deletePost(DeletePostModel model, ProgressListener listener, CancellableTask task) throws Exception { String url = getUsingUrl() + "board.php"; List<NameValuePair> pairs = new ArrayList<NameValuePair>(); pairs.add(new BasicNameValuePair("board", model.boardName)); pairs.add(new BasicNameValuePair("post[]", model.postNumber)); if (model.onlyFiles) pairs.add(new BasicNameValuePair("fileonly", "on")); pairs.add(new BasicNameValuePair("postpassword", model.password)); pairs.add(new BasicNameValuePair("deletepost", "Удалить")); HttpRequestModel request = HttpRequestModel.builder() .setPOST(new UrlEncodedFormEntityHC4(pairs, "UTF-8")) .setNoRedirect(true) .build(); String result = HttpStreamer.getInstance() .getStringFromUrl(url, request, httpClient, listener, task, false); if (result.contains("Неправильный пароль")) throw new Exception("Неправильный пароль"); Matcher errorMatcher = ERROR_POSTING.matcher(result); if (errorMatcher.find()) throw new Exception(errorMatcher.group(1)); return null; }
@Override public String sendPost(SendPostModel model, ProgressListener listener, CancellableTask task) throws Exception { 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("em", model.sage ? "sage" : model.email) .addString("subject", model.subject) .addString("message", model.comment) .addString("recaptcha_response_field", model.captchaAnswer); if (model.attachments != null && model.attachments.length > 0) postEntityBuilder.addFile("imagefile", model.attachments[0], model.randomHash); else if (model.threadNumber == null) postEntityBuilder.addString("nofile", "on"); postEntityBuilder.addString("postpassword", model.password); 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()); } } } else if (response.statusCode == 200) { ByteArrayOutputStream output = new ByteArrayOutputStream(1024); IOUtils.copyStream(response.stream, output); String htmlResponse = output.toString("UTF-8"); Matcher errorMatcher = ERROR_POSTING.matcher(htmlResponse); if (errorMatcher.find()) throw new Exception(errorMatcher.group(1).trim()); } else throw new Exception(response.statusCode + " - " + response.statusReason); } finally { if (response != null) response.release(); } return null; }
@Override public String reportPost(DeletePostModel model, ProgressListener listener, CancellableTask task) throws Exception { String url = getUsingUrl() + "board.php"; List<NameValuePair> pairs = new ArrayList<NameValuePair>(); pairs.add(new BasicNameValuePair("board", model.boardName)); pairs.add(new BasicNameValuePair("post[]", model.postNumber)); if (model.onlyFiles) pairs.add(new BasicNameValuePair("fileonly", "on")); pairs.add(new BasicNameValuePair("reportreason", model.reportReason)); pairs.add(new BasicNameValuePair("reportpost", "Аминь")); HttpRequestModel request = HttpRequestModel.builder() .setPOST(new UrlEncodedFormEntityHC4(pairs, "UTF-8")) .setNoRedirect(true) .build(); String result = HttpStreamer.getInstance() .getStringFromUrl(url, request, httpClient, listener, task, false); if (result.contains("Post successfully reported")) return null; throw new Exception(result); }
@Override public void handle(final Activity activity, final CancellableTask task, final Callback callback) { try { final HttpClient httpClient = MainApplication.getInstance().getChanModule(chanName).getHttpClient(); final String usingURL = scheme + RECAPTCHA_FALLBACK_URL + publicKey; Header[] customHeaders = new Header[] {new BasicHeader(HttpHeaders.REFERER, usingURL)}; String htmlChallenge = HttpStreamer.getInstance() .getStringFromUrl( usingURL, HttpRequestModel.builder().setGET().setCustomHeaders(customHeaders).build(), httpClient, null, task, false); 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().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 = messageMatcher.group(1).replaceAll("<[^>]*>", ""); 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); textView.setTextAppearance(activity, 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; PriorityThreadFactory.LOW_PRIORITY_FACTORY .newThread( 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 UrlEncodedFormEntityHC4(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 { throw new RecaptchaException( "incorrect answer (hash is empty)"); } } catch (final Exception e) { Logger.e(TAG, e); if (task.isCancelled()) return; handle(activity, task, callback); } } }) .start(); } }); } }); } 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()); } }); } } }