/*
* Overchan 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 nya.miku.wishmaster.http.cloudflare;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import nya.miku.wishmaster.api.interfaces.CancellableTask;
import nya.miku.wishmaster.common.IOUtils;
import nya.miku.wishmaster.common.Logger;
import nya.miku.wishmaster.http.HttpConstants;
import nya.miku.wishmaster.http.client.ExtendedHttpClient;
import nya.miku.wishmaster.http.streamer.HttpRequestModel;
import nya.miku.wishmaster.http.streamer.HttpResponseModel;
import nya.miku.wishmaster.http.streamer.HttpStreamer;
import cz.msebera.android.httpclient.client.CookieStore;
import cz.msebera.android.httpclient.cookie.Cookie;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
/**
* Anti-DDOS проверка с перехватом запросов от webview в httpclient (требуется API >= 11)
* @author miku-nyan
*
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
/*package*/ class InterceptingAntiDDOS {
private static final String TAG = "CloudflareChecker_Intercept";
private InterceptingAntiDDOS() {}
private static InterceptingAntiDDOS instanace;
static synchronized InterceptingAntiDDOS getInstance() {
if (instanace == null) instanace = new InterceptingAntiDDOS();
return instanace;
}
private volatile boolean processing = false;
private volatile boolean processing2 = false;
private volatile Cookie currentCookie;
private volatile WebView webView;
private Object lock = new Object();
/*package*/ boolean isProcessing() {
return processing;
}
/** Метод anti-DDOS проверки, все запросы перехватываются из webview в httpclient (для использования с прокси-сервером на API >= 11) */
Cookie check(final CloudflareException exception, final ExtendedHttpClient httpClient, final CancellableTask task, final Activity activity) {
synchronized (lock) {
if (processing) return null;
processing = true;
}
processing2 = true;
currentCookie = null;
final HttpRequestModel rqModel = HttpRequestModel.DEFAULT_GET;
final CookieStore cookieStore = httpClient.getCookieStore();
CloudflareChecker.removeCookie(cookieStore, exception.getRequiredCookieName());
final ViewGroup layout = (ViewGroup)activity.getWindow().getDecorView().getRootView();
final WebViewClient client = new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
HttpResponseModel responseModel = null;
try {
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 (CloudflareChecker.isClearanceCookie(cookie, url, exception.getRequiredCookieName())) {
Logger.d(TAG, "Cookie found: " + cookie.getValue());
currentCookie = cookie;
processing2 = false;
return new WebResourceResponse("text/html", "UTF-8", new ByteArrayInputStream("cookie received".getBytes()));
}
}
BufOutputStream output = new BufOutputStream();
IOUtils.copyStream(responseModel.stream, output);
return new WebResourceResponse(null, null, output.toInputStream());
} catch (Exception e) {
Logger.e(TAG, e);
} finally {
if (responseModel != null) responseModel.release();
}
return new WebResourceResponse("text/html", "UTF-8", new ByteArrayInputStream("something wrong".getBytes()));
}
};
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);
webView.loadUrl(exception.getCheckUrl());
}
});
long startTime = System.currentTimeMillis();
while (processing2) {
long time = System.currentTimeMillis() - startTime;
if ((task != null && task.isCancelled()) || time > CloudflareChecker.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 {
processing = false;
}
}
});
return currentCookie;
}
private static class BufOutputStream extends ByteArrayOutputStream {
public BufOutputStream() {
super(1024);
}
public InputStream toInputStream() {
return new ByteArrayInputStream(buf, 0, count);
}
}
}