com.swisscom.safeconnect.backend.BackendConnector.java Source code

Java tutorial

Introduction

Here is the source code for com.swisscom.safeconnect.backend.BackendConnector.java

Source

/*
 Swisscom Safe Connect
 Copyright (C) 2014 Swisscom
    
 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 com.swisscom.safeconnect.backend;

import android.content.Context;
import android.os.Build;
import android.util.Base64;
import android.util.Log;

import com.swisscom.safeconnect.BuildConfig;
import com.swisscom.safeconnect.model.IncidentType;
import com.swisscom.safeconnect.model.PlumberAuthResponse;
import com.swisscom.safeconnect.model.PlumberDevicesResponseList;
import com.swisscom.safeconnect.model.PlumberIncidentCountResponse;
import com.swisscom.safeconnect.model.PlumberIncidentsResponseList;
import com.swisscom.safeconnect.model.PlumberLastConnectionLogResponseList;
import com.swisscom.safeconnect.model.PlumberPurchaseResponse;
import com.swisscom.safeconnect.model.PlumberResponse;
import com.swisscom.safeconnect.model.PlumberStatsResponse;
import com.swisscom.safeconnect.model.PlumberSubscriptionResponse;
import com.swisscom.safeconnect.model.PlumberSubscriptionResponseList;
import com.swisscom.safeconnect.model.RawResponse;
import com.swisscom.safeconnect.security.Token;
import com.swisscom.safeconnect.utils.Config;

import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * Created by cianci on 17.04.14.
 */
public class BackendConnector {
    public static interface ResponseCallback<T extends PlumberResponse> {
        public void onRequestComplete(int statusCode, T response);
    }

    /**
     * provides a task which is used to execute HTTP request to Plumber
     */
    public static interface TaskProvider {
        public PlumberTask getTask();
    }

    private TaskProvider taskProvider;

    public BackendConnector(TaskProvider taskProvider) {
        this.taskProvider = taskProvider;
    }

    public BackendConnector(final Context context) {
        this(new TaskProvider() {
            @Override
            public PlumberTask getTask() {
                return new PlumberTask(context);
            }
        });
    }

    /**
     * This method is used the first time for registering a new user
     * Returns the http response code
     *
     * @param phoneNumber
     */
    public void registerUser(final String phoneNumber, final String deviceId, final String language,
            final ResponseCallback<PlumberAuthResponse> callback) {
        PlumberTask task = taskProvider.getTask();
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("auth/register", phoneNumber, "l", language, "d", deviceId);
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                if (response.status == 200) {
                    callback.onRequestComplete(response.status, new PlumberAuthResponse(response.body));
                } else {
                    callback.onRequestComplete(response.status, null);
                }
            }
        };
        task.execute(taskCallback);
    }

    /**
     * This method should be used to authenticate a device which is going to be used
     * to remove other devices if user reached the device registration limit
     */
    public void registerForDeviceRemoval(final String phoneNumber, final String deviceId, final String language,
            final ResponseCallback<PlumberAuthResponse> callback) {
        PlumberTask task = taskProvider.getTask();
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("auth/authrm", phoneNumber, "l", language, "d", deviceId);
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                if (response.status == 200) {
                    callback.onRequestComplete(response.status, new PlumberAuthResponse(response.body));
                } else {
                    callback.onRequestComplete(response.status, null);
                }
            }
        };
        task.execute(taskCallback);
    }

    /**
     * This method is used to validate the SMS Token the user received
     * Returns status, username, password and token in json format
     *
     * @param phoneNumber
     * @param smsToken
     * @return
     */
    public void validateUser(final String phoneNumber, final String deviceId, final String smsToken,
            final int tablet, final String language, final ResponseCallback<PlumberAuthResponse> callback) {
        PlumberTask task = taskProvider.getTask();
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("auth/validate", phoneNumber, "v", smsToken, "tbl", String.valueOf(tablet),
                        "l", language, "d", deviceId);
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                if (response.status == 200) {
                    callback.onRequestComplete(response.status, new PlumberAuthResponse(response.body));
                } else {
                    callback.onRequestComplete(response.status, null);
                }
            }
        };
        task.execute(taskCallback);
    }

    /**
     * This method is used for authenticate a user with his previous authentication token
     * Returns status, username, password and token in json format
     *
     * @param phoneNumber
     * @param authToken
     * @return
     */
    public void authenticateUser(final String phoneNumber, final String deviceId, final String authToken,
            final ResponseCallback<PlumberAuthResponse> callback) {
        PlumberTask task = taskProvider.getTask();
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("auth/auth", phoneNumber, "a", authToken, "d", deviceId);
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                if (response.status == 200) {
                    callback.onRequestComplete(response.status, new PlumberAuthResponse(response.body));
                } else {
                    callback.onRequestComplete(response.status, null);
                }
            }
        };
        task.execute(taskCallback);
    }

    /**
     * will auth user on the caller's thread
     * @param phoneNumber
     * @param authToken
     * @return
     */
    public PlumberAuthResponse authenticateUserSync(final String phoneNumber, final String deviceId,
            final String authToken) {

        String url = buildUrl("auth/auth", phoneNumber, "a", authToken, "d", deviceId);
        RawResponse response = taskProvider.getTask().doSyncRequest(new HttpGet(url));
        PlumberAuthResponse auth = new PlumberAuthResponse(response.body);

        return auth;
    }

    /**
     * will do an HTTP request for user stats on the caller's thread
     * @param phoneNumber phone number
     * @return statistics
     */
    public PlumberStatsResponse getUserStatsSync(String phoneNumber, String deviceId) {
        String url = buildUrl("stats/stat", phoneNumber, "offset", String.valueOf(UtcOffset.get()), "d", deviceId);
        RawResponse response = taskProvider.getTask().doSyncRequest(new HttpGet(url));

        if (response.status != 200) {
            return new PlumberStatsResponse();
        }

        PlumberStatsResponse stats = new PlumberStatsResponse(response.body);

        return stats;
    }

    /**
     * Same as getUserStatsSync, but async
     *
     * @param phoneNumber phone number
     * @return statistics
     */
    public void getUserStatsAsync(final String phoneNumber, final String deviceId,
            final ResponseCallback<PlumberStatsResponse> callback) {
        PlumberTask task = taskProvider.getTask();
        task.setHttpTimeout(2000);

        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("stats/stat", phoneNumber, "offset", String.valueOf(UtcOffset.get()), "d",
                        deviceId);
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                if (response.status == 200) {
                    callback.onRequestComplete(response.status, new PlumberStatsResponse(response.body));
                } else {
                    callback.onRequestComplete(response.status, null);
                }
            }
        };

        task.execute(taskCallback);
    }

    /**
     * gets incidents
     * @param phoneNumber
     * @param deviceId
     * @param type incident type
     * @param limit returns only "limit" number of incidents
     * @param callback
     */
    public void getIncidents(final String phoneNumber, final String deviceId, final IncidentType type,
            final int limit, final Config.StatisticsPeriod statsPeriod,
            final ResponseCallback<PlumberIncidentsResponseList> callback) {

        PlumberTask task = taskProvider.getTask();
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("alert/incident", phoneNumber, "d", deviceId, "l", String.valueOf(limit), "y",
                        type.toUrlParam(), "offset", String.valueOf(UtcOffset.get()), "p",
                        String.valueOf(statsPeriod.ordinal()));

                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                callback.onRequestComplete(response.status, new PlumberIncidentsResponseList(response.body));
            }
        };
        task.execute(taskCallback);
    }

    /**
     * gets number of incidents by type
     * @param phoneNumber
     * @param deviceId
     * @return
     */
    public PlumberIncidentCountResponse getIncidentsCntSync(final String phoneNumber, final String deviceId,
            Config.StatisticsPeriod statsPeriod) {

        String url = buildUrl("alert/incidentcount", phoneNumber, "d", deviceId, "offset",
                String.valueOf(UtcOffset.get()), "p", String.valueOf(statsPeriod.ordinal()));
        RawResponse response = taskProvider.getTask().doSyncRequest(new HttpGet(url));

        if (response.status != 200) {
            return new PlumberIncidentCountResponse("");
        }

        PlumberIncidentCountResponse result = new PlumberIncidentCountResponse(response.body);

        return result;
    }

    /**
     * gets number of incidents by type - Async
     * @param phoneNumber
     * @param deviceId
     * @return
     */
    public void getIncidentsCntASync(final String phoneNumber, final String deviceId,
            final Config.StatisticsPeriod statsPeriod,
            final ResponseCallback<PlumberIncidentCountResponse> callback) {
        PlumberTask task = taskProvider.getTask();

        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("alert/incidentcount", phoneNumber, "d", deviceId, "offset",
                        String.valueOf(UtcOffset.get()), "p", String.valueOf(statsPeriod.ordinal()));

                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                callback.onRequestComplete(response.status, new PlumberIncidentCountResponse(response.body));
            }
        };

        task.execute(taskCallback);
    }

    /**
     * Returns the current subscription of a user
     *
     * @param phoneNumber
     * @return
     */
    public void getOwnSubscription(final String phoneNumber, final String deviceId,
            final ResponseCallback<PlumberSubscriptionResponse> callback) {

        PlumberTask task = taskProvider.getTask();
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("subscription/my", phoneNumber, "d", deviceId);
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                callback.onRequestComplete(response.status, new PlumberSubscriptionResponse(response.body));
            }
        };
        task.execute(taskCallback);
    }

    /**
     * Get all available subscriptions
     *
     * @param phoneNumber
     * @return
     */
    public void getAvailableSubscriptions(final String phoneNumber,
            final ResponseCallback<PlumberSubscriptionResponseList> callback) {

        PlumberTask task = taskProvider.getTask();
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("subscription/list", phoneNumber);
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                callback.onRequestComplete(response.status, new PlumberSubscriptionResponseList(response.body));
            }
        };
        task.execute(taskCallback);
    }

    /**
     * Subscribe to a new subscription
     *
     * @param phoneNumber
     * @param subscriptionId
     * @return
     */
    public void subscribe(final String phoneNumber, final String deviceId, final int subscriptionId,
            final ResponseCallback<PlumberSubscriptionResponse> callback) {

        PlumberTask task = taskProvider.getTask();
        //workaround because the client gets disconnected and does not receive any http response
        task.setHttpTimeout(2000);
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("subscription/subscribe", phoneNumber, "s", String.valueOf(subscriptionId),
                        "d", deviceId);
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                callback.onRequestComplete(response.status, new PlumberSubscriptionResponse(response.body));
            }
        };
        task.execute(taskCallback);
    }

    /**
     * Get all active registered devices for a phone number
     *
     * @param phoneNumber
     * @param deviceId
     * @return
     */
    public void listRegisteredDevices(final String phoneNumber, final String deviceId,
            final ResponseCallback<PlumberDevicesResponseList> callback) {

        PlumberTask task = taskProvider.getTask();
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("auth/listdev", phoneNumber, "d", deviceId);
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                callback.onRequestComplete(response.status, new PlumberDevicesResponseList(response.body));
            }
        };
        task.execute(taskCallback);
    }

    /**
     * removes registered device for a concrete phone number
     *
     * @param phoneNumber phone number
     * @param authToken device token
     * @param deviceId device id from which the request is issued
     * @param targetDeviceId device id to remove
     * @return
     */
    public void removeDevice(final String phoneNumber, final String deviceId, final String authToken,
            final String targetDeviceId, final ResponseCallback<PlumberAuthResponse> callback) {
        PlumberTask task = taskProvider.getTask();
        //workaround because it might happen the client gets disconnected and does not receive any http response
        task.setHttpTimeout(2000);
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("auth/rmdev", phoneNumber, "d", deviceId, "a", authToken, "r",
                        targetDeviceId);
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                if (response.status == 200) {
                    callback.onRequestComplete(response.status, new PlumberAuthResponse(response.body));
                } else {
                    callback.onRequestComplete(response.status, null);
                }
            }
        };
        task.execute(taskCallback);
    }

    /**
     * removes all registered devices for a concrete phone number
     *
     * @param phoneNumber phone number
     * @param authToken device token
     * @param deviceId device id from which the request is issued
     * @return
     */
    public void removeAllDevices(final String phoneNumber, final String deviceId, final String authToken,
            final ResponseCallback<PlumberResponse> callback) {
        PlumberTask task = taskProvider.getTask();
        //workaround because the client gets disconnected and does not receive any http response
        task.setHttpTimeout(2000);
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("auth/rmalldevs", phoneNumber, "d", deviceId, "a", authToken);
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                callback.onRequestComplete(response.status, null);
            }
        };
        task.execute(taskCallback);
    }

    /**
     * This method is used to save the GCM id of a user to the backend
     * so the backend can send push notifications to the users
     *
     * @param phoneNumber
     * @param gcmId
     * @return
     */
    public void saveGcmId(final String phoneNumber, final String deviceId, final String gcmId,
            final ResponseCallback<PlumberResponse> callback) {
        PlumberTask task = taskProvider.getTask();
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("auth/gcm", phoneNumber, "d", deviceId, "g", gcmId);
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                callback.onRequestComplete(response.status, null);
            }
        };
        task.execute(taskCallback);
    }

    /**
     * Get the last connections the vpn user made
     *
     * @param phoneNumber
     * @return
     */
    public void getLastConnections(final String phoneNumber, final String deviceId,
            final ResponseCallback<PlumberLastConnectionLogResponseList> callback) {

        PlumberTask task = taskProvider.getTask();
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("stats/conn", phoneNumber, "d", deviceId);
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                callback.onRequestComplete(response.status,
                        new PlumberLastConnectionLogResponseList(response.body));
            }
        };
        task.execute(taskCallback);
    }

    /**
     * get the last connections synchronously
     * @param phoneNumber
     * @param deviceId
     * @return list of last connections
     */
    public PlumberLastConnectionLogResponseList getLastConnectionsSync(final String phoneNumber,
            final String deviceId) {
        PlumberTask task = taskProvider.getTask();
        task.setHttpTimeout(2000);
        String url = buildUrl("stats/conn", phoneNumber, "d", deviceId);
        RawResponse response = task.doSyncRequest(new HttpGet(url));

        if (response.status != 200) {
            return new PlumberLastConnectionLogResponseList("");
        }

        PlumberLastConnectionLogResponseList list = new PlumberLastConnectionLogResponseList(response.body);

        return list;
    }

    public static String getBase64EncodedString(String string) {
        try {
            String b64EncodedPhoneNumber = Base64.encodeToString(string.getBytes("UTF-8"), Base64.URL_SAFE);
            return URLEncoder.encode(b64EncodedPhoneNumber, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            if (BuildConfig.DEBUG)
                Log.e(Config.TAG, "Exception when encoding url string", e);
            return null;
        }
    }

    /**
     * verifies developer payload for purchases
     *
     * @return PlumberAuthResponse - check only status
     */
    public PlumberAuthResponse verifyPurchaseIdSync(final String phoneNumber, final String uid,
            final int subscriptionId) {

        String url = buildUrl("subscription/check", phoneNumber, "u", uid, "s", String.valueOf(subscriptionId));
        RawResponse response = taskProvider.getTask().doSyncRequest(new HttpGet(url));

        return new PlumberAuthResponse(response.body);
    }

    /**
     * starts subscription purchase process
     * @param phoneNumber
     * @param subscriptionId
     * @param callback
     */
    public void startPurchase(final String phoneNumber, final int subscriptionId,
            final ResponseCallback<PlumberPurchaseResponse> callback) {

        PlumberTask task = taskProvider.getTask();
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("subscription/start", phoneNumber, "s", String.valueOf(subscriptionId));
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                callback.onRequestComplete(response.status, new PlumberPurchaseResponse(response.body));
            }
        };
        task.execute(taskCallback);
    }

    /**
     * activates a voicher
     * @param phoneNumber phone number
     * @param voucherCode voucher code
     * @param callback callback
     */
    public void activateVoucher(final String phoneNumber, final String voucherCode,
            final ResponseCallback<PlumberSubscriptionResponse> callback) {

        PlumberTask task = taskProvider.getTask();
        String code = voucherCode;
        try {
            code = URLEncoder.encode(voucherCode.replaceAll("\\s+", ""), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            if (BuildConfig.DEBUG)
                Log.e(Config.TAG, "failed to urlencode", e);
        }

        final String urlcode = code;
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpGet getHttpRequest() {
                String url = buildUrl("voucher/activate", phoneNumber, "c", urlcode);
                return new HttpGet(url);
            }

            @Override
            public void onRequestComplete() {
                callback.onRequestComplete(response.status, new PlumberSubscriptionResponse(response.body));
            }
        };
        task.execute(taskCallback);
    }

    /**
     * call to end the purchase process and activate the subscription
     * @param phoneNumber
     * @param uid
     * @param signature subscription signature
     * @param callback
     */
    public void finishPurchase(final String phoneNumber, final String uid, final String signature,
            final String purchaseData, final ResponseCallback<PlumberSubscriptionResponse> callback) {

        PlumberTask task = taskProvider.getTask();
        InternalCallback taskCallback = new InternalCallback() {
            @Override
            public HttpRequestBase getHttpRequest() {
                String url = buildUrl("subscription/subscribe_a", phoneNumber, "u", uid);
                HttpPost r = new HttpPost(url);

                r.addHeader("content-type", "application/json");

                JSONObject json = new JSONObject();
                String jsonStr = "";
                try {
                    json.put("data", getBase64EncodedString(purchaseData));
                    json.put("signature", signature);
                    jsonStr = json.toString();
                } catch (JSONException e) {
                    if (BuildConfig.DEBUG)
                        Log.e(Config.TAG, "error constructing json", e);
                }

                StringEntity params = null;
                try {
                    params = new StringEntity(jsonStr);
                } catch (UnsupportedEncodingException e) {
                    if (BuildConfig.DEBUG)
                        Log.e(Config.TAG, "error constructing body", e);
                }
                r.setEntity(params);

                return r;
            }

            @Override
            public void onRequestComplete() {
                callback.onRequestComplete(response.status, new PlumberSubscriptionResponse(response.body));
            }
        };
        task.execute(taskCallback);
    }

    private static String buildUrl(String path, String phoneNumber, String... params) {
        if (params.length % 2 != 0)
            return "";

        StringBuilder sb = new StringBuilder("https://");
        sb.append(Config.PLUMBER_ADDR).append("/");
        sb.append(path).append("/");

        Map<String, String> paramsMap = new HashMap<>();
        for (int i = 0; i < params.length / 2; i++) {
            paramsMap.put(params[i * 2], params[i * 2 + 1]);
        }
        SortedMap<String, String> sortedParams = new TreeMap<>(paramsMap);

        StringBuilder tokenData = new StringBuilder();
        for (String k : sortedParams.keySet()) {
            tokenData.append(sortedParams.get(k));
        }

        String token = Token.generateToken(phoneNumber, tokenData.toString());

        sb.append("?");
        for (String k : paramsMap.keySet()) {
            sb.append(k).append("=").append(paramsMap.get(k)).append("&");
        }

        sb.append("t=").append(token);
        sb.append("&id=").append(BackendConnector.getBase64EncodedString(phoneNumber));

        return sb.toString();
    }
}