package com.laytonsmith.core.federation; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; /** * This is a wrapper for the lower level medium that communicates between * servers. It may be a {@link java.net.Socket} that carries the underlying * communication, but that information, as well as the security encoding, if * turned on, is hidden from this. */ public class FederationCommunication { private final InputStream socketReader; private final OutputStream socketWriter; private final boolean isEncrypted; public FederationCommunication(InputStream reader, OutputStream writer){ this.socketReader = reader; this.socketWriter = writer; //TODO: Once the security is added, this should be changed. isEncrypted = false; } public void close() throws IOException{ try { socketReader.close(); } finally { socketWriter.close(); } } /** * Writes a line, without ensuring the connection is valid * @param line * @throws java.io.IOException */ public void writeLine(String line) throws IOException { try { socketWriter.write(encode(line.getBytes("UTF-8"))); socketWriter.write("\n".getBytes("UTF-8")); socketWriter.flush(); } catch (UnsupportedEncodingException ex) { throw new Error(ex); } } /** * Reads a line, without ensuring the connection is valid. * * @return * @throws java.io.IOException */ public String readLine() throws IOException { try { List<Byte> bytes = new ArrayList<>(); while(true){ int b = socketReader.read(); if(b == '\n'){ //The newline is never encoded. break; } else { bytes.add((byte)b); } } byte[] ba = new byte[bytes.size()]; for(int i = 0; i < bytes.size(); i++){ ba[i] = bytes.get(i); } ba = decode(ba); return new String(ba, "UTF-8"); } catch (UnsupportedEncodingException ex) { throw new Error(ex); } } /** * Writes raw bytes, without ensuring the connection is valid. * @param bytes * @throws IOException */ public void writeBytes(byte[] bytes) throws IOException { socketWriter.write(encode(bytes)); socketWriter.flush(); } /** * Reads a line, without ensuring the connection is valid. * * @param size * @return * @throws java.io.IOException */ public byte[] readBytes(int size) throws IOException { byte[] bytes = new byte[size]; socketReader.read(bytes); return decode(bytes); } /** * Assumes the input is always unencrypted, but otherwise works like * {@link #readBytes(int)}. * @param size * @return * @throws java.io.IOException */ public byte[] readUnencrypted(int size) throws IOException { byte[] bytes = new byte[size]; socketReader.read(bytes); return bytes; } /** * Never encodes the output, but otherwise works like * {@link #writeBytes(byte[])}. * @param bytes * @throws java.io.IOException */ public void writeUnencrypted(byte[] bytes) throws IOException{ socketWriter.write(bytes); socketWriter.flush(); } /** * Assumes the input is always unencrypted, but otherwise works like * {@link #readLine()}. * @return * @throws java.io.IOException */ public String readUnencryptedLine() throws IOException { try { // Don't put the reader in a try with resources, because we don't actually // want to close the reader when we're done; that would close the actual // InputStream as well. We're just using the BufferedReader as a shortcut // to reading this part of the stream. List<Byte> bytes = new ArrayList<>(); while(true){ int b = socketReader.read(); if(b == '\n'){ //The newline is never encoded. break; } else { bytes.add((byte)b); } } byte[] ba = new byte[bytes.size()]; for(int i = 0; i < bytes.size(); i++){ ba[i] = bytes.get(i); } return new String(ba, "UTF-8"); } catch (UnsupportedEncodingException ex) { throw new Error(ex); } } /** * Never encodes the output, but otherwise works like * {@link #writeLine(byte[])}. * @param line The line to write. * @throws java.io.IOException */ public void writeUnencryptedLine(String line) throws IOException{ socketWriter.write(line.getBytes("UTF-8")); socketWriter.flush(); } /** * If the server's public key is provided, this encodes the data using * it, before sending the data. If the public key isn't provided, then * the bytes are simply returned as is. * @param bytes * @return */ private byte[] encode(byte [] bytes){ //TODO: Add the security bits here return bytes; } /** * If the server's public key is provided, this encodes the data using * it, before sending the data. If the public key isn't provided, then * the bytes are simply returned as is. * @param bytes * @return */ private byte [] decode(byte[] bytes){ //TODO: Add security stuff here return bytes; } }