/** * Copyright (C) 2010-2017 Structr GmbH * * This file is part of Structr <http://structr.org>. * * Structr 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. * * Structr 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 Structr. If not, see <http://www.gnu.org/licenses/>. */ package org.structr.net; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Paths; import java.security.KeyPair; import java.util.Base64; import java.util.Base64.Decoder; import java.util.Base64.Encoder; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.structr.net.common.KeyHelper; import org.structr.net.peer.Peer; import org.structr.net.protocol.BroadcastMessage; import org.structr.net.protocol.DirectMessage; import org.structr.net.repository.DefaultRepository; /** * The main class that starts the peer, with an optional interactive * flag and additional configuration to select the local bind address * and the initial peer to which the discovery packet will be * broadcasted (default is 255.255.255.255). */ public class Main { private static final Logger logger = LoggerFactory.getLogger(Main.class.getName()); public static void main(final String[] args) { String uuid = UUID.randomUUID().toString().replaceAll("\\-", ""); String initialPeer = "255.255.255.255"; String bindAddress = "0.0.0.0"; String privateKeyFile = null; String publicKeyFile = null; boolean printKeys = false; boolean isInteractive = false; boolean verbose = false; for (int i=0 ;i<args.length; i++) { switch (args[i]) { case "-i": System.out.println("Interactive mode enabled."); isInteractive = true; break; case "-b": bindAddress = args[i+1]; System.out.println("Bind address set to " + bindAddress); break; case "-p": initialPeer = args[i+1]; System.out.println("Initial peer set to " + initialPeer); break; case "-v": verbose = true; System.out.println("Verbose mode enabled."); break; case "-u": uuid = args[i+1]; System.out.println("Client UUID set to " + uuid); break; case "--print-keys": printKeys = true; System.out.println("Printing peer keys."); break; case "--private-key-file": privateKeyFile = args[i+1]; System.out.println("Using private key from " + privateKeyFile); break; case "--public-key-file": publicKeyFile = args[i+1]; System.out.println("Using public key from " + publicKeyFile); break; case "-h": printHelp(); System.exit(0); break; } } final DefaultRepository repo = new DefaultRepository(uuid); KeyPair keyPair = null; if (privateKeyFile != null && publicKeyFile != null) { try { final Decoder decoder = Base64.getDecoder(); final byte[] privateKey = decoder.decode(readBase64(privateKeyFile)); final byte[] publicKey = decoder.decode(readBase64(publicKeyFile)); keyPair = KeyHelper.fromBytes("RSA", privateKey, publicKey); } catch (IOException ioex) { logger.warn("", ioex); } } else { keyPair = KeyHelper.getOrCreateKeyPair("RSA", 2048); } final Peer peer = new Peer(keyPair, repo, bindAddress, initialPeer); if (printKeys && keyPair != null) { final Encoder encoder = Base64.getEncoder(); System.out.println("Private key (BASE64 encoded): " + encoder.encodeToString(keyPair.getPrivate().getEncoded())); System.out.println("Public key (BASE64 encoded): " + encoder.encodeToString(keyPair.getPublic().getEncoded())); } repo.setPeer(peer); peer.setVerbose(verbose); peer.initializeServer(); peer.start(); if (isInteractive) { final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String line = null; do { try { System.out.print(peer.getLocalPort() + "> "); line = reader.readLine(); final String[] parts = line.split("[ ]+"); final String cmd = parts[0]; switch (cmd) { case "exit": peer.stop(); break; case "i": peer.printInfo(); break; case "kill": peer.broadcast(new BroadcastMessage(peer.getUuid(), "kill")); break; case "info": peer.broadcast(new BroadcastMessage(peer.getUuid(), "info")); break; case "span": peer.broadcast(new BroadcastMessage(peer.getUuid(), "span")); break; case "msg": if (parts.length < 3) { System.out.println("usage: msg <peer> <message>"); break; } peer.broadcast(new DirectMessage(peer.getUuid(), parts[1], parts[2])); break; case "broadcast": if (parts.length < 2) { System.out.println("usage: broadcast <message>"); break; } peer.broadcast(new BroadcastMessage(peer.getUuid(), parts[1])); break; case "ping": if (parts.length < 2) { System.out.println("usage: ping <peer>"); break; } peer.broadcast(new DirectMessage(peer.getUuid(), parts[1], "ping")); break; case "new": if (parts.length < 3) { System.out.println("usage: new <type> <owner> [<key> <value>]..."); break; } final Map<String, Object> map = new HashMap<>(); String key = null; for (int i=3; i<parts.length; i++) { if (key == null) { key = parts[i]; } else { System.out.println(key + " = " + parts[i]); map.put(key, parts[i]); key = null; } } repo.create(UUID.randomUUID().toString().replaceAll("\\-", ""), parts[1], peer.getUuid(), parts[2], peer.getPseudoTemporalEnvironment().next(), map); break; case "get": if (parts.length < 3) { System.out.println("usage: get <id> <key>"); break; } peer.get(parts[1], parts[2]); break; case "set": if (parts.length < 4) { System.out.println("usage: set <id> <key> <value>"); break; } peer.set(parts[1], parts[2], parts[3]); break; } } catch (Throwable t) { logger.warn("", t); } } while (peer.isRunning() && !line.equals("quit")); peer.stop(); } } private static void printHelp() { System.out.println("structr-net Client command line options"); System.out.println(" -b <addr> - set bind address"); System.out.println(" -h - show this help message "); System.out.println(" -i - enable interactive mode"); System.out.println(" -p <addr> - set initial peer"); System.out.println(" -u <uuid> - set peer UUID "); System.out.println(" -v - enable verbose mode"); } private static String readBase64(final String fileName) throws IOException { return getKey(Files.readAllLines(Paths.get(fileName))); } private static String getKey(final List<String> lines) { // return the first non-empty line that does not begin with a comment char for (final String line : lines) { if (line.contains("#")) { final String cleanedLine = line.substring(0, line.indexOf("#")); if (!cleanedLine.isEmpty()) { return cleanedLine; } } else if (!line.isEmpty()) { return line; } } return null; } }