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 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;
  }