<commit_before>package eu.livotov.labs.android.d3s;

import android.content.Context;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * (c) Livotov Labs Ltd. 2013
 * <NAME>, <NAME>
 * <p/>
 * Date: 20/09/2013
 * <p/>
 * <h1>Intro</h1>
 * <p/>
 * <p>This is the 3DSecure WebView component. It can be used to perform 3D-Secure authorizations when processing internet
 * payments in apps. The technology is also named Verified By Visa and MasterCard Secure Code.</p>
 * <p/>
 * <p>The main idea is to route cardholder to the card issuer financial institution where cardholder will be required to
 * answer extra security question or enter one-time sms or token code in order to confirm the card transaction.</p>
 * <p/>
 * <h1>How to use</h1>
 * <p/>
 * <p>Add DDDSView to your layout, set custom (if required) postback url and authorization results listener via the
 * corresponding setters. Then invoke the <b>authorize(...)</b> method to start 3D-Secure authorization. You will need to
 * provide payment data, which will be issued by your card processor in attempt to make a transaction with the 3DS-capable
 * credit card.</p>
 * <p/>
 * <p>Once user completes the authorization, the authorization listener's onAuthorizationCompleted() method will be called
 * with the parameters, came from banking ACS server. Now you can use those parameters in your bckend processing server
 * to finalize the payment.</p>
 */
public class D3SView extends WebView
{

    /**
     * Namespace for JS bridge
     */
    private static String JavaScriptNS = "D3SJS";

    /**
     * Pattern to find the MD value in the ACS server post response
     */
    private static Pattern mdFinder = Pattern.compile(".*?(<input[^<>]* name=\\\"MD\\\"[^<>]*>).*?", 32);

    /**
     * Pattern to find the PaRes value in the ACS server post response
     */
    private static Pattern paresFinder = Pattern.compile(".*?(<input[^<>]* name=\\\"PaRes\\\"[^<>]*>).*?", 32);

    private static Pattern valuePattern = Pattern.compile(".*? value=\\\"(.*?)\\\"", 32);

    /**
     * Internal flag for tracking web page url changes in WebView
     */
    private boolean urlReturned = false;

    /**
     * When set to <b>true</b>, SSL certificate errors and self-signed certificates errors will be ignored.
     */
    private boolean debugMode = false;

    /**
     * Url that will be used by ACS server for posting result data on authorization completion. We will be monitoring
     * this URL in WebView handler to intercept its loading and grabbing the resulting data from POST message instead.
     */
    private String postbackUrl = "https://www.google.com";

    private boolean postbackHandled = false;

    /**
     * Callback to send authorization events to
     */
    private D3SSViewAuthorizationListener authorizationListener = null;


    public D3SView(final Context context)
    {
        super(context);
        initUI();
    }

    private void initUI()
    {
        getSettings().setJavaScriptEnabled(true);
        getSettings().setBuiltInZoomControls(true);
        addJavascriptInterface(new D3SJSInterface(), JavaScriptNS);

        setWebViewClient(new WebViewClient()
        {

            public boolean shouldOverrideUrlLoading(final WebView view, final String url)
            {
                if (!postbackHandled && url.toLowerCase().contains(postbackUrl.toLowerCase()))
                {
                    postbackHandled = true;
                    view.loadUrl(String.format("javascript:window.%s.processHTML(document.getElementsByTagName('html')[0].innerHTML);", JavaScriptNS));
                    return true;
                }
                else
                {
                    return super.shouldOverrideUrlLoading(view, url);
                }
            }

            public void onPageStarted(WebView view, String url, Bitmap icon)
            {
                if (!urlReturned && !postbackHandled)
                {
                    if (url.toLowerCase().contains(postbackUrl.toLowerCase()))
                    {
                        postbackHandled = true;
                        view.loadUrl(String.format("javascript:window.%s.processHTML(document.getElementsByTagName('html')[0].innerHTML);", JavaScriptNS));
                        urlReturned = true;
                    }
                    else
                    {
                        super.onPageStarted(view, url, icon);
                    }
                }
            }

            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
            {
                if (!failingUrl.startsWith(postbackUrl))
                {
                    authorizationListener.onAuthorizationWebPageLoadingError(errorCode, description, failingUrl);
                }
            }

            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
            {
                if (debugMode)
                {
                    handler.proceed();
                }
            }


        });

        setWebChromeClient(new WebChromeClient()
        {

            public void onProgressChanged(WebView view, int newProgress)
            {
                if (authorizationListener != null)
                {
                    authorizationListener.onAuthorizationWebPageLoadingProgressChanged(newProgress);
                }
            }
        });
    }

    public D3SView(final Context context, final AttributeSet attrs)
    {
        super(context, attrs);
        initUI();
    }

    public D3SView(final Context context, final AttributeSet attrs, final int defStyle)
    {
        super(context, attrs, defStyle);
        initUI();
    }

    public D3SView(final Context context, final AttributeSet attrs, final int defStyle, final boolean privateBrowsing)
    {
        super(context, attrs, defStyle);
        initUI();
    }

    private void completeAuthorization(String html)
    {
        String md = "";
        String pares = "";

        Matcher localMatcher1 = mdFinder.matcher(html);
        Matcher localMatcher2 = paresFinder.matcher(html);

        if (localMatcher1.find())
        {
            md = localMatcher1.group(1);
        }

        if (localMatcher2.find())
        {
            pares = localMatcher2.group(1);
        }

        if (!TextUtils.isEmpty(md))
        {
            Matcher valueMatcher = valuePattern.matcher(md);
            if (valueMatcher.find())
            {
                md = valueMatcher.group(1);
            }
        }

        if (!TextUtils.isEmpty(pares))
        {
            Matcher valueMatcher = valuePattern.matcher(pares);
            if (valueMatcher.find())
            {
                pares = valueMatcher.group(1);
            }
        }

        if (authorizationListener != null)
        {
            authorizationListener.onAuthorizationCompleted(md, pares);
        }
    }

    /**
     * Checks if debug mode is on. Note, that you must not turn debug mode for production app !
     *
     * @return
     */
    public boolean isDebugMode()
    {
        return debugMode;
    }

    /**
     * Sets the debug mode state. When set to <b>true</b>, ssl errors will be ignored. Do not turn debug mode ON
     * for production environment !
     *
     * @param debugMode
     */
    public void setDebugMode(final boolean debugMode)
    {
        this.debugMode = debugMode;
    }

    /**
     * Sets the callback to receive authorization events
     *
     * @param authorizationListener
     */
    public void setAuthorizationListener(final D3SSViewAuthorizationListener authorizationListener)
    {
        this.authorizationListener = authorizationListener;
    }

    /**
     * Starts 3DS authorization
     *
     * @param acsUrl ACS server url, returned by the credit card processing gateway
     * @param md     MD parameter, returned by the credit card processing gateway
     * @param paReq  PaReq parameter, returned by the credit card processing gateway
     */
    public void authorize(final String acsUrl, final String md, final String paReq)
    {
        authorize(acsUrl, md, paReq, null);
    }

    /**
     * Starts 3DS authorization
     *
     * @param acsUrl      ACS server url, returned by the credit card processing gateway
     * @param md          MD parameter, returned by the credit card processing gateway
     * @param paReq       PaReq parameter, returned by the credit card processing gateway
     * @param postbackUrl custom postback url for intercepting ACS server result posting. You may use any url you like
     *                    here, if you need, even non existing ones.
     */
    public void authorize(final String acsUrl, final String md, final String paReq, final String postbackUrl)
    {
        if (authorizationListener != null)
        {
            authorizationListener.onAuthorizationStarted(this);
        }

        if (!TextUtils.isEmpty(postbackUrl))
        {
            this.postbackUrl = postbackUrl;
        }

        urlReturned = false;

        List<NameValuePair> params = new LinkedList<NameValuePair>();

        params.add(new BasicNameValuePair("MD", md));
        params.add(new BasicNameValuePair("TermUrl", this.postbackUrl));
        params.add(new BasicNameValuePair("PaReq", paReq));

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try
        {
            new UrlEncodedFormEntity(params, HTTP.UTF_8).writeTo(bos);
        }
        catch (IOException e)
        {
        }

        postUrl(acsUrl, bos.toByteArray());
    }

    class D3SJSInterface
    {

        D3SJSInterface()
        {
        }

        @android.webkit.JavascriptInterface
        public void processHTML(final String paramString)
        {
            completeAuthorization(paramString);
        }
    }
}<commit_msg>Don't use classes from the deprecated Apache HttpClient to encode POST parameters<commit_after>package eu.livotov.labs.android.d3s;

import android.content.Context;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * (c) Livotov Labs Ltd. 2013
 * <NAME>, <NAME>
 * <p/>
 * Date: 20/09/2013
 * <p/>
 * <h1>Intro</h1>
 * <p/>
 * <p>This is the 3DSecure WebView component. It can be used to perform 3D-Secure authorizations when processing internet
 * payments in apps. The technology is also named Verified By Visa and MasterCard Secure Code.</p>
 * <p/>
 * <p>The main idea is to route cardholder to the card issuer financial institution where cardholder will be required to
 * answer extra security question or enter one-time sms or token code in order to confirm the card transaction.</p>
 * <p/>
 * <h1>How to use</h1>
 * <p/>
 * <p>Add DDDSView to your layout, set custom (if required) postback url and authorization results listener via the
 * corresponding setters. Then invoke the <b>authorize(...)</b> method to start 3D-Secure authorization. You will need to
 * provide payment data, which will be issued by your card processor in attempt to make a transaction with the 3DS-capable
 * credit card.</p>
 * <p/>
 * <p>Once user completes the authorization, the authorization listener's onAuthorizationCompleted() method will be called
 * with the parameters, came from banking ACS server. Now you can use those parameters in your bckend processing server
 * to finalize the payment.</p>
 */
public class D3SView extends WebView
{

    /**
     * Namespace for JS bridge
     */
    private static String JavaScriptNS = "D3SJS";

    /**
     * Pattern to find the MD value in the ACS server post response
     */
    private static Pattern mdFinder = Pattern.compile(".*?(<input[^<>]* name=\\\"MD\\\"[^<>]*>).*?", 32);

    /**
     * Pattern to find the PaRes value in the ACS server post response
     */
    private static Pattern paresFinder = Pattern.compile(".*?(<input[^<>]* name=\\\"PaRes\\\"[^<>]*>).*?", 32);

    private static Pattern valuePattern = Pattern.compile(".*? value=\\\"(.*?)\\\"", 32);

    /**
     * Internal flag for tracking web page url changes in WebView
     */
    private boolean urlReturned = false;

    /**
     * When set to <b>true</b>, SSL certificate errors and self-signed certificates errors will be ignored.
     */
    private boolean debugMode = false;

    /**
     * Url that will be used by ACS server for posting result data on authorization completion. We will be monitoring
     * this URL in WebView handler to intercept its loading and grabbing the resulting data from POST message instead.
     */
    private String postbackUrl = "https://www.google.com";

    private boolean postbackHandled = false;

    /**
     * Callback to send authorization events to
     */
    private D3SSViewAuthorizationListener authorizationListener = null;


    public D3SView(final Context context)
    {
        super(context);
        initUI();
    }

    private void initUI()
    {
        getSettings().setJavaScriptEnabled(true);
        getSettings().setBuiltInZoomControls(true);
        addJavascriptInterface(new D3SJSInterface(), JavaScriptNS);

        setWebViewClient(new WebViewClient()
        {

            public boolean shouldOverrideUrlLoading(final WebView view, final String url)
            {
                if (!postbackHandled && url.toLowerCase().contains(postbackUrl.toLowerCase()))
                {
                    postbackHandled = true;
                    view.loadUrl(String.format("javascript:window.%s.processHTML(document.getElementsByTagName('html')[0].innerHTML);", JavaScriptNS));
                    return true;
                }
                else
                {
                    return super.shouldOverrideUrlLoading(view, url);
                }
            }

            public void onPageStarted(WebView view, String url, Bitmap icon)
            {
                if (!urlReturned && !postbackHandled)
                {
                    if (url.toLowerCase().contains(postbackUrl.toLowerCase()))
                    {
                        postbackHandled = true;
                        view.loadUrl(String.format("javascript:window.%s.processHTML(document.getElementsByTagName('html')[0].innerHTML);", JavaScriptNS));
                        urlReturned = true;
                    }
                    else
                    {
                        super.onPageStarted(view, url, icon);
                    }
                }
            }

            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
            {
                if (!failingUrl.startsWith(postbackUrl))
                {
                    authorizationListener.onAuthorizationWebPageLoadingError(errorCode, description, failingUrl);
                }
            }

            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
            {
                if (debugMode)
                {
                    handler.proceed();
                }
            }


        });

        setWebChromeClient(new WebChromeClient()
        {

            public void onProgressChanged(WebView view, int newProgress)
            {
                if (authorizationListener != null)
                {
                    authorizationListener.onAuthorizationWebPageLoadingProgressChanged(newProgress);
                }
            }
        });
    }

    public D3SView(final Context context, final AttributeSet attrs)
    {
        super(context, attrs);
        initUI();
    }

    public D3SView(final Context context, final AttributeSet attrs, final int defStyle)
    {
        super(context, attrs, defStyle);
        initUI();
    }

    public D3SView(final Context context, final AttributeSet attrs, final int defStyle, final boolean privateBrowsing)
    {
        super(context, attrs, defStyle);
        initUI();
    }

    private void completeAuthorization(String html)
    {
        String md = "";
        String pares = "";

        Matcher localMatcher1 = mdFinder.matcher(html);
        Matcher localMatcher2 = paresFinder.matcher(html);

        if (localMatcher1.find())
        {
            md = localMatcher1.group(1);
        }

        if (localMatcher2.find())
        {
            pares = localMatcher2.group(1);
        }

        if (!TextUtils.isEmpty(md))
        {
            Matcher valueMatcher = valuePattern.matcher(md);
            if (valueMatcher.find())
            {
                md = valueMatcher.group(1);
            }
        }

        if (!TextUtils.isEmpty(pares))
        {
            Matcher valueMatcher = valuePattern.matcher(pares);
            if (valueMatcher.find())
            {
                pares = valueMatcher.group(1);
            }
        }

        if (authorizationListener != null)
        {
            authorizationListener.onAuthorizationCompleted(md, pares);
        }
    }

    /**
     * Checks if debug mode is on. Note, that you must not turn debug mode for production app !
     *
     * @return
     */
    public boolean isDebugMode()
    {
        return debugMode;
    }

    /**
     * Sets the debug mode state. When set to <b>true</b>, ssl errors will be ignored. Do not turn debug mode ON
     * for production environment !
     *
     * @param debugMode
     */
    public void setDebugMode(final boolean debugMode)
    {
        this.debugMode = debugMode;
    }

    /**
     * Sets the callback to receive authorization events
     *
     * @param authorizationListener
     */
    public void setAuthorizationListener(final D3SSViewAuthorizationListener authorizationListener)
    {
        this.authorizationListener = authorizationListener;
    }

    /**
     * Starts 3DS authorization
     *
     * @param acsUrl ACS server url, returned by the credit card processing gateway
     * @param md     MD parameter, returned by the credit card processing gateway
     * @param paReq  PaReq parameter, returned by the credit card processing gateway
     */
    public void authorize(final String acsUrl, final String md, final String paReq)
    {
        authorize(acsUrl, md, paReq, null);
    }

    /**
     * Starts 3DS authorization
     *
     * @param acsUrl      ACS server url, returned by the credit card processing gateway
     * @param md          MD parameter, returned by the credit card processing gateway
     * @param paReq       PaReq parameter, returned by the credit card processing gateway
     * @param postbackUrl custom postback url for intercepting ACS server result posting. You may use any url you like
     *                    here, if you need, even non existing ones.
     */
    public void authorize(final String acsUrl, final String md, final String paReq, final String postbackUrl)
    {
        if (authorizationListener != null)
        {
            authorizationListener.onAuthorizationStarted(this);
        }

        if (!TextUtils.isEmpty(postbackUrl))
        {
            this.postbackUrl = postbackUrl;
        }

        urlReturned = false;

        String postParams;
        try
        {
            postParams = String.format(Locale.US, "MD=%1$s&TermUrl=%2$s&PaReq=%3$s",
                    URLEncoder.encode(md, "UTF-8"),
                    URLEncoder.encode(this.postbackUrl, "UTF-8"),
                    URLEncoder.encode(paReq, "UTF-8")
            );
        }
        catch (UnsupportedEncodingException e)
        {
            throw new RuntimeException(e);
        }

        postUrl(acsUrl, postParams.getBytes());
    }

    class D3SJSInterface
    {

        D3SJSInterface()
        {
        }

        @android.webkit.JavascriptInterface
        public void processHTML(final String paramString)
        {
            completeAuthorization(paramString);
        }
    }
}
{'fraction_non_alphanumeric': 0.09114520706074744, 'fraction_numerical': 0.005338848540233755, 'mean_word_length': 2.116306954436451, 'pattern_counts': {'":': 0, '<': 71, '<?xml version=': 0, '>': 71, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3226424', 'n_tokens_mistral': 5605, 'n_tokens_neox': 5317, 'n_words': 1585}