nu.nethome.home.items.web.proxy.HomeCloudConnection.java Source code

Java tutorial

Introduction

Here is the source code for nu.nethome.home.items.web.proxy.HomeCloudConnection.java

Source

/**
 * Copyright (C) 2005-2016, Stefan Strmberg <stefangs@nethome.nu>
 *
 * This file is part of OpenNetHome  (http://www.nethome.nu)
 *
 * OpenNetHome 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.
 *
 * OpenNetHome 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 nu.nethome.home.items.web.proxy;

import nu.nethome.home.item.HomeItem;
import nu.nethome.home.item.HomeItemAdapter;
import nu.nethome.home.item.HomeItemType;
import nu.nethome.util.plugin.Plugin;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.http.util.ByteArrayBuffer;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.logging.Logger;

@SuppressWarnings("UnusedDeclaration")
@Plugin
@HomeItemType("Ports")
public class HomeCloudConnection extends HomeItemAdapter implements Runnable, HomeItem {
    // TODO: Verify session id
    // TODO: Generate dynamic challenge
    // TODO: Handle POST and DELETE
    // TODO: Transfer error codes
    private static final String MODEL = ("<?xml version = \"1.0\"?> \n"
            + "<HomeItem Class=\"HttpReverseProxy\" Category=\"Ports\" >"
            + "  <Attribute Name=\"State\" Type=\"String\" Get=\"getState\" Default=\"true\" />"
            + "  <Attribute Name=\"ServiceURL\" Type=\"String\" Get=\"getServiceURL\" Set=\"setServiceURL\" />"
            + "  <Attribute Name=\"LocalURL\" Type=\"String\" Get=\"getLocalURL\" Set=\"setLocalURL\" />"
            + "  <Attribute Name=\"SystemId\" Type=\"String\" Get=\"getSystemId\" Set=\"setSystemId\" />"
            + "  <Attribute Name=\"SystemPassword\" Type=\"Password\" Get=\"getSystemPassword\" Set=\"setSystemPassword\" />"
            + "  <Attribute Name=\"UserPassword\" Type=\"Password\" Get=\"getPassword\" Set=\"setPassword\" />"
            + "  <Attribute Name=\"MessageCount\" Type=\"String\" Get=\"getMessageCount\" />" + "</HomeItem> ");

    private static final String CHALLENGE = "challenge";
    private static final String POLL_RESOURCE = "poll";
    private static final int RETRY_INTERVAL_MS = 5000;
    private static final String LOGIN_RESOURCE = "serverLogin";

    protected String serviceURL = "https://cloud.opennethome.org/";
    protected String localURL = "http://127.0.0.1:8020/";
    protected String password = "";
    protected String systemId = "0";
    private String systemPassword = "";
    protected int messageCount = 0;
    private boolean systemPasswordIsBad = false;

    private boolean connected = false;
    /*
     * Internal attributes
     */
    private static Logger logger = Logger.getLogger(HomeCloudConnection.class.getName());
    protected Thread listenThread;
    protected boolean isRunning = false;
    private JsonRestClient jsonRestClient;

    public HomeCloudConnection() {
        systemId = Integer.toString(new Random().nextInt(10000));
    }

    public String getModel() {
        return MODEL;
    }

    public void activate() {
        jsonRestClient = new JsonRestClient();
        isRunning = true;
        listenThread = new Thread(this, "ProxyListenThread");
        listenThread.start();
    }

    public void stop() {
        isRunning = false;
        super.stop();
    }

    public void setServiceURL(String serviceURL) {
        this.serviceURL = serviceURL;
    }

    public String getServiceURL() {
        return serviceURL;
    }

    public void setLocalURL(String localURL) {
        this.localURL = localURL;
    }

    public String getLocalURL() {
        return localURL;
    }

    String charset = java.nio.charset.StandardCharsets.UTF_8.name();

    public void run() {
        HttpResponse noResponse = new HttpResponse(systemId, "", new String[0], CHALLENGE);
        try {
            HttpResponse lastHttpResponse = noResponse;
            while (isRunning) {
                try {
                    if (systemPasswordIsBad) {
                        Thread.sleep(RETRY_INTERVAL_MS);
                        continue;
                    }
                    final LoginResponse loginResponse = loginToCloud(new LoginRequest(systemId, systemPassword));
                    if (loginResponse.sesssionId.isEmpty()) {
                        systemPasswordIsBad = true;
                    } else {
                        connected = true;
                        while (isRunning) {
                            lastHttpResponse = proxyHttpRequest(noResponse, lastHttpResponse);
                        }
                    }
                } catch (Exception e) {
                    connected = false;
                    if (isRunning) {
                        logger.fine("Failed connecting to cloud " + e);
                        lastHttpResponse = noResponse;
                        Thread.sleep(RETRY_INTERVAL_MS);
                    }
                }
            }
        } catch (Exception e) {
            logger.warning("Failed creating socket in UDPListener " + e);
        }
    }

    private HttpResponse proxyHttpRequest(HttpResponse noResponse, HttpResponse httpResponse)
            throws IOException, NoSuchAlgorithmException {
        final HttpRequest request = postResponseAndFetchNewRequest(httpResponse);
        if (request.url.isEmpty()) {
            final String loginCredential = request.loginCredential;
            if (!loginCredential.isEmpty()) {
                httpResponse = verifyLoginRequest(noResponse, loginCredential);
            } else {
                httpResponse = noResponse;
            }
        } else {
            httpResponse = performLocalRequest(request);
            messageCount++;
        }
        return httpResponse;
    }

    private HttpResponse verifyLoginRequest(HttpResponse noResponse, String loginCredential)
            throws NoSuchAlgorithmException {
        HttpResponse httpResponse;
        String expectedCredential = this.systemId + this.password + CHALLENGE;
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hash = digest.digest(expectedCredential.getBytes(StandardCharsets.UTF_8));
        String expectedHashString = Hex.encodeHexString(hash);
        if (expectedHashString.equals(loginCredential)) {
            String sessionId = UUID.randomUUID().toString();
            httpResponse = new HttpResponse(systemId, "", new String[0], CHALLENGE, sessionId);
        } else {
            httpResponse = noResponse;
        }
        return httpResponse;
    }

    private LoginResponse loginToCloud(LoginRequest loginRequest) throws IOException {
        final JSONData result = jsonRestClient.post(serviceURL, LOGIN_RESOURCE, loginRequest.toJson());
        return new LoginResponse(result.getObject());
    }

    private HttpRequest postResponseAndFetchNewRequest(HttpResponse httpResponse) throws IOException {
        final JSONData result = jsonRestClient.post(serviceURL, POLL_RESOURCE, httpResponse.toJson());
        return new HttpRequest(result.getObject());
    }

    private HttpResponse performLocalRequest(HttpRequest request) throws IOException {
        HttpResponse httpResponse;
        HttpURLConnection connection = (HttpURLConnection) new URL(localURL + request.url).openConnection();
        for (String header : request.headers) {
            String parts[] = header.split(":");
            connection.setRequestProperty(parts[0].trim(), parts[1].trim());
        }
        ByteArrayBuffer baf = new ByteArrayBuffer(50);
        try (InputStream response = connection.getInputStream()) {
            BufferedInputStream bis = new BufferedInputStream(response);
            int read;
            int bufSize = 512;
            byte[] buffer = new byte[bufSize];
            while (true) {
                read = bis.read(buffer);
                if (read == -1) {
                    break;
                }
                baf.append(buffer, 0, read);
            }
        } catch (IOException e) {
            return new HttpResponse(systemId, "", new String[0], CHALLENGE);
        }

        Map<String, List<String>> map = connection.getHeaderFields();
        String headers[] = new String[map.size()];
        int i = 0;
        for (Map.Entry<String, List<String>> entry : map.entrySet()) {
            System.out.println("Key : " + entry.getKey() + " ,Value : " + entry.getValue());
            headers[i++] = entry.getKey() + ":" + entry.getValue().get(0);
        }
        httpResponse = new HttpResponse(systemId, new String(Base64.encodeBase64(baf.toByteArray())), headers,
                CHALLENGE);
        return httpResponse;
    }

    public String getMessageCount() {
        return String.valueOf(messageCount);
    }

    public String getSystemId() {
        return systemId;
    }

    public void setSystemId(String systemId) {
        this.systemId = systemId;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getState() {
        return connected ? "Connected" : "Not Connected";
    }

    public String getSystemPassword() {
        return systemPassword;
    }

    public void setSystemPassword(String systemPassword) {
        this.systemPassword = systemPassword;
        systemPasswordIsBad = false;
    }
}