<commit_before>package org.owntracks.android.services;

import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.util.Base64;

import com.fasterxml.jackson.core.JsonProcessingException;

import org.owntracks.android.App;
import org.owntracks.android.messages.MessageBase;
import org.owntracks.android.messages.MessageClear;
import org.owntracks.android.messages.MessageCmd;
import org.owntracks.android.messages.MessageEvent;
import org.owntracks.android.messages.MessageLocation;
import org.owntracks.android.messages.MessageTransition;
import org.owntracks.android.messages.MessageWaypoint;
import org.owntracks.android.messages.MessageWaypoints;
import org.owntracks.android.support.interfaces.OutgoingMessageProcessor;
import org.owntracks.android.support.Preferences;
import org.owntracks.android.support.SocketFactory;
import org.owntracks.android.support.Parser;
import org.owntracks.android.services.MessageProcessor.EndpointState;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.X509TrustManager;

import okhttp3.ConnectionPool;
import okhttp3.Dns;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import timber.log.Timber;

public class MessageProcessorEndpointHttp implements OutgoingMessageProcessor, Preferences.OnPreferenceChangedListener {
    // Headers according to https://github.com/owntracks/recorder#http-mode
    private static final String HEADER_USERNAME = "X-Limit-U";
    private static final String HEADER_DEVICE = "X-Limit-D";

    private static final String HEADER_AUTHORIZATION = "Authorization";
    private static final String HTTP_BUNDLE_KEY_MESSAGE_PAYLOAD = "HTTP_BUNDLE_KEY_MESSAGE_PAYLOAD";
    private static final String HTTP_BUNDLE_KEY_USERINFO = "HTTP_BUNDLE_KEY_USERINFO";
    private static final String HTTP_BUNDLE_KEY_URL = "HTTP_BUNDLE_KEY_URL";

    private static String headerUsername;
    private static String headerDevice;

    private String endpointUrl;
    private String endpointUserInfo;

    private static OkHttpClient mHttpClient;
    private static final MediaType JSON  = MediaType.parse("application/json; charset=utf-8");

    private static MessageProcessorEndpointHttp instance;
    public static MessageProcessorEndpointHttp getInstance() {
        if(instance == null) {
            instance = new MessageProcessorEndpointHttp();
        }
        return instance;
    }


    private MessageProcessorEndpointHttp() {
        App.getPreferences().registerOnPreferenceChangedListener(this);
        loadEndpointUrl();
        loadHTTPClient();
    }

    @Override
    public void onCreateFromProcessor() {

    }

    private void loadHTTPClient() {
        String tlsCaCrt = App.getPreferences().getTlsCaCrtName();
        String tlsClientCrt = App.getPreferences().getTlsClientCrtName();
        SocketFactory.SocketFactoryOptions socketFactoryOptions = new SocketFactory.SocketFactoryOptions();

        if (tlsCaCrt.length() > 0) {
            try {
                socketFactoryOptions.withCaInputStream(App.getContext().openFileInput(tlsCaCrt));
            } catch (FileNotFoundException e) {
                Timber.e(e);
            }
        }

        if (tlsClientCrt.length() > 0)	{
            try {
                socketFactoryOptions.withClientP12InputStream(App.getContext().openFileInput(tlsClientCrt)).withClientP12Password(App.getPreferences().getTlsClientCrtPassword());
            } catch (FileNotFoundException e1) {
                Timber.e(e1);
            }
        }

        try {
            SocketFactory f = new SocketFactory(socketFactoryOptions);

            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel( HttpLoggingInterceptor.Level.NONE);

            mHttpClient = new OkHttpClient.Builder()
                    .followRedirects(true)
                    .followSslRedirects(true)
                    .connectTimeout(15, TimeUnit.SECONDS)
                    //.dns(new DebugDnsSelector(DebugDnsSelector.Mode.IPV4_FIRST))
                    .connectionPool(new ConnectionPool())
                    .sslSocketFactory(f, (X509TrustManager) f.getTrustManagers()[0])
                    .addInterceptor(logging).build();



        } catch (KeyStoreException | NoSuchAlgorithmException | KeyManagementException | IOException | UnrecoverableKeyException | CertificateException e) {
            //e.printStackTrace();
            Timber.e(e);
        }
        headerUsername = App.getPreferences().getStringOrNull(Preferences.Keys.USERNAME);
        headerDevice = App.getPreferences().getStringOrNull(Preferences.Keys.DEVICE_ID);
    }


    private void loadEndpointUrl() {
        URL endpoint;
        try {
            endpoint = new URL(App.getPreferences().getUrl());
            App.getMessageProcessor().onEndpointStateChanged(EndpointState.IDLE);
        } catch (MalformedURLException e) {
            App.getMessageProcessor().onEndpointStateChanged(EndpointState.ERROR_CONFIGURATION.setError(e));
            return;
        }

        this.endpointUserInfo = endpoint.getUserInfo();

        if (this.endpointUserInfo != null && this.endpointUserInfo.length() > 0) {
            this.endpointUrl = endpoint.toString().replace(endpointUserInfo+"@", "");
        } else {
            this.endpointUrl = endpoint.toString();
        }
        Timber.v("endpointUrl:%s, endpointUserInfo:%s", this.endpointUrl, this.endpointUserInfo );


    }

    int sendMessage(MessageBase message) {
        long messageId = message.getMessageId();
        String body = null;
        try {
            body = App.getParser().toJsonPlain(message);
        } catch (IOException e) {
            getMessageProcessor().onMessageDeliveryFailedFinal(messageId);
        }

        //String url = b.getString(HTTP_BUNDLE_KEY_URL);
        //String userInfo = b.getString(HTTP_BUNDLE_KEY_USERINFO);
        //long messageId = b.getLong(Scheduler.BUNDLE_KEY_MESSAGE_ID);<commit_msg>HTTP Endpoint wasn't symetrically encrypting data<commit_after>package org.owntracks.android.services;

import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.util.Base64;

import com.fasterxml.jackson.core.JsonProcessingException;

import org.owntracks.android.App;
import org.owntracks.android.messages.MessageBase;
import org.owntracks.android.messages.MessageClear;
import org.owntracks.android.messages.MessageCmd;
import org.owntracks.android.messages.MessageEvent;
import org.owntracks.android.messages.MessageLocation;
import org.owntracks.android.messages.MessageTransition;
import org.owntracks.android.messages.MessageWaypoint;
import org.owntracks.android.messages.MessageWaypoints;
import org.owntracks.android.services.MessageProcessor.EndpointState;
import org.owntracks.android.support.Parser;
import org.owntracks.android.support.Preferences;
import org.owntracks.android.support.SocketFactory;
import org.owntracks.android.support.interfaces.OutgoingMessageProcessor;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.X509TrustManager;

import okhttp3.ConnectionPool;
import okhttp3.Dns;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import timber.log.Timber;

public class MessageProcessorEndpointHttp implements OutgoingMessageProcessor, Preferences.OnPreferenceChangedListener {
    // Headers according to https://github.com/owntracks/recorder#http-mode
    private static final String HEADER_USERNAME = "X-Limit-U";
    private static final String HEADER_DEVICE = "X-Limit-D";

    private static final String HEADER_AUTHORIZATION = "Authorization";
    private static final String HTTP_BUNDLE_KEY_MESSAGE_PAYLOAD = "HTTP_BUNDLE_KEY_MESSAGE_PAYLOAD";
    private static final String HTTP_BUNDLE_KEY_USERINFO = "HTTP_BUNDLE_KEY_USERINFO";
    private static final String HTTP_BUNDLE_KEY_URL = "HTTP_BUNDLE_KEY_URL";

    private static String headerUsername;
    private static String headerDevice;

    private String endpointUrl;
    private String endpointUserInfo;

    private static OkHttpClient mHttpClient;
    private static final MediaType JSON  = MediaType.parse("application/json; charset=utf-8");

    private static MessageProcessorEndpointHttp instance;
    public static MessageProcessorEndpointHttp getInstance() {
        if(instance == null) {
            instance = new MessageProcessorEndpointHttp();
        }
        return instance;
    }


    private MessageProcessorEndpointHttp() {
        App.getPreferences().registerOnPreferenceChangedListener(this);
        loadEndpointUrl();
        loadHTTPClient();
    }

    @Override
    public void onCreateFromProcessor() {

    }

    private void loadHTTPClient() {
        String tlsCaCrt = App.getPreferences().getTlsCaCrtName();
        String tlsClientCrt = App.getPreferences().getTlsClientCrtName();
        SocketFactory.SocketFactoryOptions socketFactoryOptions = new SocketFactory.SocketFactoryOptions();

        if (tlsCaCrt.length() > 0) {
            try {
                socketFactoryOptions.withCaInputStream(App.getContext().openFileInput(tlsCaCrt));
            } catch (FileNotFoundException e) {
                Timber.e(e);
            }
        }

        if (tlsClientCrt.length() > 0)	{
            try {
                socketFactoryOptions.withClientP12InputStream(App.getContext().openFileInput(tlsClientCrt)).withClientP12Password(App.getPreferences().getTlsClientCrtPassword());
            } catch (FileNotFoundException e1) {
                Timber.e(e1);
            }
        }

        try {
            SocketFactory f = new SocketFactory(socketFactoryOptions);

            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel( HttpLoggingInterceptor.Level.NONE);

            mHttpClient = new OkHttpClient.Builder()
                    .followRedirects(true)
                    .followSslRedirects(true)
                    .connectTimeout(15, TimeUnit.SECONDS)
                    //.dns(new DebugDnsSelector(DebugDnsSelector.Mode.IPV4_FIRST))
                    .connectionPool(new ConnectionPool())
                    .sslSocketFactory(f, (X509TrustManager) f.getTrustManagers()[0])
                    .addInterceptor(logging).build();



        } catch (KeyStoreException | NoSuchAlgorithmException | KeyManagementException | IOException | UnrecoverableKeyException | CertificateException e) {
            //e.printStackTrace();
            Timber.e(e);
        }
        headerUsername = App.getPreferences().getStringOrNull(Preferences.Keys.USERNAME);
        headerDevice = App.getPreferences().getStringOrNull(Preferences.Keys.DEVICE_ID);
    }


    private void loadEndpointUrl() {
        URL endpoint;
        try {
            endpoint = new URL(App.getPreferences().getUrl());
            App.getMessageProcessor().onEndpointStateChanged(EndpointState.IDLE);
        } catch (MalformedURLException e) {
            App.getMessageProcessor().onEndpointStateChanged(EndpointState.ERROR_CONFIGURATION.setError(e));
            return;
        }

        this.endpointUserInfo = endpoint.getUserInfo();

        if (this.endpointUserInfo != null && this.endpointUserInfo.length() > 0) {
            this.endpointUrl = endpoint.toString().replace(endpointUserInfo+"@", "");
        } else {
            this.endpointUrl = endpoint.toString();
        }
        Timber.v("endpointUrl:%s, endpointUserInfo:%s", this.endpointUrl, this.endpointUserInfo );


    }

    private int sendMessage(MessageBase message) {
        long messageId = message.getMessageId();
        String body = null;
        try {
            body = App.getParser().toJson(message);
        } catch (IOException | Parser.EncryptionException e) {
            getMessageProcessor().onMessageDeliveryFailedFinal(messageId);
        }

        //String url = b.getString(HTTP_BUNDLE_KEY_URL);
        //String userInfo = b.getString(HTTP_BUNDLE_KEY_USERINFO);
        //long messageId = b.getLong(Scheduler.BUNDLE_KEY_MESSAGE_ID);
{'fraction_non_alphanumeric': 0.0915791824882734, 'fraction_numerical': 0.004616186434368253, 'mean_word_length': 3.4683965402528276, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, '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': '1699405', 'n_tokens_mistral': 3703, 'n_tokens_neox': 3565, 'n_words': 640}