/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library 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 Lesser General Public License for more * details. */ package com.liferay.portal.tools.db.upgrade.client.util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.util.ArrayList; import java.util.List; /** * @author Gregory Amerson */ public class GogoTelnetClient implements AutoCloseable { public GogoTelnetClient() throws IOException { this("localhost", 11311); } public GogoTelnetClient(String host, int port) throws IOException { _socket = new Socket(host, port); _dataInputStream = new DataInputStream(_socket.getInputStream()); _dataOutputStream = new DataOutputStream(_socket.getOutputStream()); _handshake(); } @Override public void close() { try { _socket.close(); _dataInputStream.close(); _dataOutputStream.close(); } catch (IOException ioe) { } } public String send(String command) throws IOException { byte[] bytes = command.getBytes(); int[] codes = new int[bytes.length + 2]; for (int i = 0; i < bytes.length; i++) { codes[i] = bytes[i]; } codes[bytes.length] = '\r'; codes[bytes.length + 1] = '\n'; _sendCommand(codes); return _readUntilNextGogoPrompt(); } private static void _assertCondition(boolean condition) { if (!condition) { throw new AssertionError(); } } private void _handshake() throws IOException { // Gogo server first sends 4 commands _readOneCommand(); _readOneCommand(); _readOneCommand(); _readOneCommand(); // First we negotiate the terminal type // 255 (IAC), 251 (WILL), 24 (terminal type) _sendCommand(255, 251, 24); // Server should respond // 255 (IAC), 250 (SB), 24, 1, 255 (IAC), 240 (SE) _readOneCommand(); // Send the terminal type // 255 (IAC), 250 (SB), 24, 0, 'V', 'T', '2', '2', '0', 255 (IAC), // 240(SE) _sendCommand(255, 250, 24, 0, 'V', 'T', '2', '2', '0', 255, 240); // Read Gogo Shell prompt _readUntilNextGogoPrompt(); } private int[] _readOneCommand() throws IOException { List<Integer> bytes = new ArrayList<>(); int iac = _dataInputStream.read(); _assertCondition(iac == 255); bytes.add(iac); int second = _dataInputStream.read(); bytes.add(second); if (second == 250) { int option = _dataInputStream.read(); bytes.add(option); int code = _dataInputStream.read(); // 1 or 0 _assertCondition(code == 0 || code == 1); bytes.add(code); if (code == 0) { throw new IllegalStateException(); } else if (code == 1) { iac = _dataInputStream.read(); _assertCondition(iac == 255); bytes.add(iac); int se = _dataInputStream.read(); // SE _assertCondition(se == 240); bytes.add(se); } } else { bytes.add(_dataInputStream.read()); } return _toIntArray(bytes); } private String _readUntilNextGogoPrompt() throws IOException { StringBuilder sb = new StringBuilder(); int c = _dataInputStream.read(); while (c != -1) { sb.append((char)c); String s = sb.toString(); if (s.endsWith("g! ")) { break; } c = _dataInputStream.read(); } String output = sb.substring(0, sb.length() - 3); return output.trim(); } private void _sendCommand(int... codes) throws IOException { for (int code : codes) { _dataOutputStream.write(code); } } private int[] _toIntArray(List<Integer> list) { int[] array = new int[list.size()]; int i = 0; for (Integer integer : list) { array[i++] = integer.intValue(); } return array; } private final DataInputStream _dataInputStream; private final DataOutputStream _dataOutputStream; private final Socket _socket; }