listener.Handler.java Source code

Java tutorial

Introduction

Here is the source code for listener.Handler.java

Source

/**
 * This file is part of LukeUtils.
 *
 * LukeUtils is free software: you can redistribute it and/or modify
 * it under the terms of the cc-by-nc-sa (Creative Commons Attribution-
 * NonCommercial-ShareAlike) as released by the Creative Commons
 * organisation, version 3.0.
 *
 * LukeUtils is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY.
 *
 * You should have received a copy of the cc-by-nc-sa-license along
 * with this LukeUtils. If not, see
 * <https://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>.
 *
 * Copyright Lukas Rose 2013 - 2015
 */

package listener;

import java.io.IOException;
import java.util.Arrays;
import java.util.logging.LogManager;

import org.json.JSONException;
import org.json.JSONObject;

import exception.AliasNotFoundException;
import exception.InsufficientPrivilegeException;
import exception.PrivilegeNotFoundException;
import util.FileHandler;
import util.StringTools;
import logging.Logger;
import misc.Account;
import misc.AccountManager;
import misc.AccountOnlineManager;
import misc.AccountPrivileges;
import misc.Chat;
import misc.Message;
import misc.ThreadManager;
import misc.ThreadWrapper;
import misc.User;
import network.MessageHandler;

public class Handler extends Thread {
    private String messageString = "";
    private JSONObject messageObject = null;
    private Notifier notifier;
    private Message message;
    // private boolean parsingNeeded = true;
    private boolean raw = false;
    private boolean waitsForUserInfo = false;
    // private boolean parsedWell = false;
    private int id = -1;
    // private int commandDepthParsed = 0;

    private boolean verbose = true;
    private boolean skipOwn = true;
    private int ownID = 54916622; // Hardcoded, TODO: detect automatically.
    private Account acc = null;
    private AccountOnlineManager acos = null;

    /**
     * Constructor, if incoming message was parsed successfully to JSON by
     * {@link listener.Notifier}.
     * 
     * @param obj
     *            The {@link org.json.JSONObject} that represents the message to
     *            be parsed.
     * @param notifier
     *            The {@link listener.Notifier} that spawned this
     *            {@link Handler}. Needed for back-communication.
     * @param id
     *            The ID of the message this {@link Handler} parses. Used for
     *            identification.
     * @param raw
     *            A boolean value to force RAW-mode even if a
     *            {@link org.json.JSONObject} was passed. This will suppress any
     *            answers sent to telegram, assuming that this was a console
     *            message.
     */
    public Handler(JSONObject obj, Notifier notifier, int id, boolean raw) {
        if (verbose)
            Logger.logMessage('I', this, "New Handler for message " + String.valueOf(id)
                    + " with JSONObject, raw is " + String.valueOf(raw) + ".");
        this.messageObject = obj;
        this.notifier = notifier;
        this.id = id;
        this.raw = raw;
        this.start();
    }

    /**
     * Constructor, if incoming message was parsed successfully to JSON by
     * {@link listener.Notifier}.
     * 
     * @param obj
     *            The {@link org.json.JSONObject} that represents the message to
     *            be parsed.
     * @param notifier
     *            The {@link listener.Notifier} that spawned this
     *            {@link Handler}. Needed for back-communication.
     * @param id
     *            The ID of the message this {@link Handler} parses. Used for
     *            identification.
     */
    public Handler(JSONObject obj, Notifier notifier, int id) {
        this(obj, notifier, id, false);
    }

    /**
     * Constructor, if incoming message couldn't be parsed to JSON by
     * {@link listener.Notifier}. This happens, when the incoming messageString
     * is not in JSON, but in RAW format. A flag is set internally that the
     * {@link misc.Message} handled by this {@link Handler} was in RAW format.
     * 
     * @param message
     *            The messageString to be parsed by this {@link Handler}.
     * @param notifier
     *            The {@link listener.Notifier} that spawned this
     *            {@link Handler}. Needed for back-comminication.
     * @param id
     *            The ID of the message this {@link Handler} parses. Used for
     *            identification.
     */
    public Handler(String message, Notifier notifier, int id) {
        if (verbose)
            Logger.logMessage('I', this,
                    "New Handler for message " + String.valueOf(id) + " with RAW message, raw is true.");
        this.messageString = message;
        this.notifier = notifier;
        this.id = id;
        this.raw = true;
        this.start();
    }

    public void run() {
        if (raw) {
            if (messageObject != null) {
                if (this.parseMessageJSON(messageObject)) {
                    this.handleMessage(this.message.getContents());
                }
            } else if (!messageString.equals("")) {
                if (this.parseMessageRAW(messageString)) {
                    this.handleMessage(this.message.getContents());
                }
            } else {
                Logger.logMessage('E', this, "Neither messageObject nor messageString are set in Handler. Exit.");
            }
        } else {
            if (this.parseMessageJSON(messageObject)) {
                this.handleMessage(this.message.getContents());
            }
        }
    }

    /**
     * This method is used for parsing incoming messages, when they are provided
     * as {@link org.json.JSONObject}. In those cases, incoming messageStrings
     * have successfully been tested as JSON Strings and parsed by the Notifier.
     * 
     * @param obj
     *            The {@link org.json.JSONObject} provided that represents the
     *            message this Handler should handle.
     * @return True when parsing succeeded, false when not.
     */
    private boolean parseMessageJSON(JSONObject obj) {
        if (obj.has("event") ? obj.getString("event").equals("message") : false) {
            message = new Message(obj);
            if (skipOwn) {
                if (message.getFromID() == ownID) {
                    return false;
                }
            }
            if (verbose)
                Logger.logMessage('I', this,
                        "Resulting messageText " + String.valueOf(id) + ": " + message.getText());
            notifier.setSendCommand("msg ");
            return true;
        } else {
            Logger.logMessage('E', this, "Given JSONString " + String.valueOf(id) + " is not a message.");
            return false;
        }
    }

    /**
     * This method is used for parsing incoming messageStrings, which are RAW
     * messages (e.g. from a plain console for debugging purposes instead of
     * telegram cli). These messages are not formatted as JSON, only their
     * message content is provided. This method checks if the incoming
     * messageString really is a RAW message, and then passes it to
     * {@link #parseMessageRAWFinal}.
     * 
     * @param messageString
     *            The messageString to parse.
     * @return True when parsing succeeded, false when not.
     * @see #parseMessageRAWFinal(String)
     */
    private boolean parseMessageRAW(String messageString) {
        if (!messageString.startsWith("{")) { // If it starts with "{", it is probably an JSON-String
            return this.parseMessageRAWFinal(messageString); // Pass it to parsing.
        } else {
            // Try to parse messageString as a JSON-Object. If it fails, it is probably a RAW messageString.
            try {
                return this.parseMessageJSON(new JSONObject(messageString));
            } catch (Exception ex) {
                // Couldn't parse messageString as a JSON-Object. Handle it as a RAW messageString.
                return this.parseMessageRAWFinal(messageString); // Pass it to parsing
            }
        }
    }

    /**
     * This method is just used internally, only after an incoming messageString
     * has been checked for truly being a RAW messageString. It shouldn't be
     * used from any other place than {@link #parseMessageRAW(String)}.
     * 
     * @param messageString
     *            The messageString to be parsed.
     * @return True if parsing succeeded, false when not.
     * @see #parseMessageRAW(String)
     */
    private boolean parseMessageRAWFinal(String messageString) {
        try {
            message = new Message();
            message.setFrom(new User());
            message.setText(messageString);
            message.setFromFirstName("raw console");
            message.getFrom().genPrintName();
            message.setFromChatID(-99);
            return true;
        } catch (Exception ex) {
            return false;
        }
    }

    /**
     * Used internally for the procedure to load the sender's account by his ID.
     * It is saved internally and used by other methods.
     */
    private void detectAccount() {
        if (verbose)
            Logger.logMessage('I', this,
                    "Loading account for sender ID " + String.valueOf(this.message.getFromID()));
        Account dummy = new Account("Dummy", this.message.getFromID());
        acc = AccountManager.getAccount(dummy);
        if (acc == null) {
            Logger.logMessage('W', this, "Account for sender ID " + String.valueOf(this.message.getFromID())
                    + " doesn't exist yet. Creating one!");
            dummy.setAccountName(this.message.getFromPrintName());
            acc = dummy;
            AccountManager.addAccount(acc);
        } else {
            if (verbose)
                Logger.logMessage('I', this, "Account for sender ID " + String.valueOf(this.message.getFromID())
                        + " found with name " + acc.getAccountName() + "!");
        }
        dummy = null;
        // acc.setHandler(this);
    }

    /**
     * Used internally for the procedure of a) sending the User a welcome
     * message if he was offline before, and b) to set an offline timer to the
     * sender's account to set him offline again after a given time. Uses the
     * account loaded by {@link #detectAccount()}, that was saved internally.
     */
    private void dealAccountOnline() {
        if (acc.getAccountState() == Account.STATE_LOGGEDOFF) {
            if (verbose)
                Logger.logMessage('I', this, "Sending welcome back because of message " + String.valueOf(id));
            this.replyMessage("Welcome back, " + acc.getAccountName());
        }

        acos = AccountManager.getAccountOnlineManager(acc);
        if (acos == null) {
            Logger.logMessage('E', this, "No account online manager found for Account " + acc.getAccountName()
                    + ". No online timer set.");
        } else {
            acos.setOnline();
        }
    }

    /**
     * The actual procedure to handle the message. It checks the message's
     * contents for known commands, and passes the message to the procedures to
     * handle the specific context. Further syntax planned.
     * 
     * @param message
     *            The message to be handled.
     */
    private void handleMessage(String[] message) {
        this.detectAccount();
        this.dealAccountOnline();

        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_ACCESS)) {
            if (verbose)
                Logger.logMessage('I', this, "Handling command " + String.valueOf(id) + ": " + message[0]);
            switch (message[0].toLowerCase()) { // make sure everything is lowercase, for case-insensitive matching
            case "ping":
                this.ping(message);
                break;
            case "echo":
                this.echo(message);
                break;
            case "kick":
                this.exit(message);
                break;
            case "kill":
                this.killThread(message);
                break;
            case "killThread":
                this.killThread(message);
                break;
            case "switchon":
                this.switchOn(message);
                break;
            case "switchoff":
                this.switchOff(message);
                break;
            case "switch":
                this.switchPower(message);
                break;
            case "manageswitch":
                this.manageSwitch(message);
                break;
            case "delay":
                this.postpone(message);
                break;
            case "postpone":
                this.postpone(message);
                break;
            case "help":
                this.help(message);
                break;
            case "info":
                this.help(message);
                break;
            case "giveprivilege":
                this.givePrivilege(message);
                break;
            case "giveadmin":
                this.giveAdmin(message);
                break;
            case "listprivileges":
                this.listPrivileges(message);
                break;
            case "repeat":
                this.repeat(message);
            case "for":
                this.repeat(message);
            case "addalias":
                this.addAlias(message);
            case "listalias":
                this.listAlias(message);
            case "removealias":
                this.removeAlias(message);
            case "healthreport":
                this.healthreport(message);
                break;
            case "userinfo":
                this.userInfo(message);
                break;
            case "shutdown":
                this.shutdown(message);
                break;
            case "restart":
                this.restart(message);
                break;
            default:
                if (acc.hasAlias(message[0])) {
                    try {
                        String[] alias = acc.getAlias(message[0]);
                        this.replyMessage("Handling alias " + message[0] + " with contents: "
                                + StringTools.StringArrayToString(alias));
                        this.handleMessage(alias);
                    } catch (AliasNotFoundException e) {
                        Logger.logMessage('E', this, "User " + acc.getAccountName() + " has no alias"
                                + " with name '" + message[0] + "', although I checked before.");
                        this.replyMessage("An error occured: Alias disappeared after checking.");
                    }
                } else {
                    Logger.logMessage('I', this,
                            "User " + acc.toString() + " provided invalid command: " + message[0] + ". Exiting.");
                    this.replyMessage("Sorry: Command not recognized. Try 'help' for a list of available "
                            + "commands or 'listAlias' for a list of available aliases in your profile.");
                }
            }
        } else {
            try {
                Logger.logMessage('I', this,
                        "Account " + acc.getAccountName() + " has no "
                                + AccountPrivileges.getPrivString(AccountPrivileges.PERM_ACCESS)
                                + "-permission. Not handling.");
            } catch (PrivilegeNotFoundException e) {
                Logger.logMessage('E', this,
                        "Error when trying to get PrivilegeString for privilegeID "
                                + String.valueOf(AccountPrivileges.PERM_ACCESS)
                                + " in debug output for handleMessage(String[]).");
                Logger.logMessage('I', this, "Account " + acc.getAccountName() + " has no ID-"
                        + String.valueOf(AccountPrivileges.PERM_ACCESS) + "-permission. Not handling.");
            }
        }
    }

    // ------ Begin of command-related methods ------

    private void ping(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_PING)) {
            this.replyMessage("ping received!");
        } else {
            this.noPermAns("ping");
        }
    }

    private void echo(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_ECHO)) {
            if (message.length > 1) {
                if (verbose)
                    Logger.logMessage('I', this, "Executing echo command");
                this.replyMessage(message[1]);
            } else {
                String error = "usage: echo <message>; see help for more information.";
                Logger.logMessage('E', this, "not enough arguments for echo command. " + error);
                this.replyMessage(error);
            }
        } else {
            this.noPermAns("echo");
        }
    }

    private void exit(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_KICK)) {
            String infoString = "Received Exit-command over network. Exiting.";
            this.replyMessage(infoString);
            Logger.logMessage('I', this, infoString);
            logging.LogManager.saveLogFile("log.txt");
            AccountManager.saveAccounts();
            System.exit(0);
        } else {
            this.noPermAns("exit");
        }
    }

    private void killThread(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_KILL)) {
            if (message.length >= 2) {
                int id = -1;
                boolean err = false;

                try {
                    id = Integer.parseInt(message[1]);
                } catch (Exception ex) {
                    err = true;
                    Logger.logMessage('W', this, "Parsing of ID User " + acc.toString() + " passed to "
                            + "killThread-command failed: " + message[1]);
                    this.replyMessage("Error: Given ID is not numeric: " + message[1] + ". Exiting.");
                }

                if (!err) {
                    ThreadWrapper t = ThreadManager.getThreadWrapperByIndex(id);
                    if (t != null) {
                        if (t.allowKill(acc)) {
                            Logger.logMessage('I', this, "User " + acc.toString() + " is killing his own Thread "
                                    + String.valueOf(id) + ".");
                            this.replyMessage("Killing Thread " + String.valueOf(id));
                            t.getThread().interrupt();
                        } else {
                            if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_KILL_OTHERS)) {
                                Logger.logMessage('I', this,
                                        "User " + acc.toString() + " is killing Thread " + String.valueOf(id)
                                                + " which belongs to " + t.owner().toString()
                                                + ". He is allowed to override this.");
                                this.replyMessage("Killing Thread " + String.valueOf(id) + ", which was"
                                        + " owned by " + t.getOwner().toString());
                                t.getThread().interrupt();
                            } else {
                                Logger.logMessage('W', this,
                                        "User " + acc.toString() + " tried to kill" + " Thread with ID "
                                                + String.valueOf(id) + ", which is owned by " + t.owner().toString()
                                                + ". User has no permission to override this.");
                                this.replyMessage("You're not allowed to kill Thread " + String.valueOf(id));
                            }
                        }
                    } else {
                        Logger.logMessage('W', this, "Thread for ID " + String.valueOf(id) + ", original: "
                                + message[1] + ", as passed by user " + acc.toString() + " couldn't be found.");
                        this.replyMessage("Couldn't find Thread with ID " + String.valueOf(id));
                    }
                }
            } else {
                String usage = "killThread <id>; where ID is the ID of the Thread to be killed.";
                Logger.logMessage('W', this, "User " + acc.toString() + " passed not enough arguments"
                        + " to killThread-command. Exiting.");
                this.replyMessage("Not enough arguments. usage: " + usage);
            }
        } else {
            this.noPermAns("kill");
        }
    }

    private void manageSwitch(String[] message) {

    }

    private void switchPower(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_SWITCHPOWER)) {
            // if (message.length > )
            if (message.length > 3) {
                String infoString = "Executing switch command";
                if (verbose)
                    Logger.logMessage('I', this, infoString);
                this.replyMessage(infoString);

                if (verbose)
                    Logger.logMessage('I', this, "infoString");
                try {
                    Runtime.getRuntime().exec("sudo send " + message[1] + " " + message[2] + " " + message[3]);
                } catch (IOException e) {
                    String error = "Error when trying to execute send command";
                    Logger.logException('E', error, e);
                    this.replyMessage(error + " " + e.getMessage() + System.lineSeparator() + e.getStackTrace());
                }
            } else {
                String error = "usage: switchOn <systemID> <unitID>; switchOff <systemID> <unitID>; switch <systemID> <unitID> <state>; see help for more information.";
                // TODO: Multi-line messages
                Logger.logMessage('E', "not enough arguments for switchOn command. " + error);
                this.replyMessage(error);
            }
        } else {
            this.noPermAns("switchPower");
        }
    }

    private void switchOn(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_SWITCHON)) {
            // if (message.length > 2){
            // if (verbose) Logger.logMessage('I', this,
            // "Executing switchOn command");
            // try {
            // Runtime.getRuntime().exec("sudo send " + message[1] + " " +
            // message[2] + " 1");
            // } catch (IOException e) {
            // String error = "Error when trying to execute send command";
            // Logger.logException('E', error, e);
            // notifier.send(answerCommand + error + " " + e.getMessage() +
            // System.lineSeparator() + e.getStackTrace());
            // }
            String[] messageNew = new String[message.length + 1];
            for (int i = 0; i < message.length; i++) {
                messageNew[i] = message[i];
            }
            messageNew[messageNew.length - 1] = "1";
            // } else {
            // String error =
            // "usage: switchOn <systemID> <unitID>; see help for more information.";
            // Logger.logMessage('E',
            // "not enough arguments for switchOn command. " + error);
            // notifier.send(answerCommand + error);
            // }
        } else {
            this.noPermAns("switchOn");
        }
    }

    private void switchOff(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_SWITCHOFF)) {
            // if (message.length > 2){
            // if (verbose) Logger.logMessage('I', this,
            // "Executing switchOff command");
            // try {
            // Runtime.getRuntime().exec("sudo send " + message[1] + " " +
            // message[2] + " 0");
            // } catch (IOException e) {
            // String error = "Error when trying to execute send command";
            // Logger.logException('E', error, e);
            // notifier.send(answerCommand + error + " " + e.getMessage() +
            // System.lineSeparator() + e.getStackTrace());
            // }
            String[] messageNew = new String[message.length + 1];
            for (int i = 0; i < message.length; i++) {
                messageNew[i] = message[i];
            }
            messageNew[messageNew.length - 1] = "0";
            // } else {
            // String error =
            // "usage: switchOff <systemID> <unitID>; see help for more information.";
            // Logger.logMessage('E',
            // "not enough arguments for switchOn command. " + error);
            // notifier.send(answerCommand + error);
            // }
        } else {
            this.noPermAns("switchOff");
        }
    }

    private void postpone(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_POSTPONE)) {
            // if ((message.getContents().length - commandDepthParsed) > 2){
            if (message.length > 2) {
                String info = "Executing postpone-command... I'm "
                        + String.valueOf(ThreadManager.register(this, acc));
                if (verbose)
                    Logger.logMessage('I', this, info);
                this.replyMessage(info);
                try {
                    long offset = Long.parseLong(message[1]);
                    Thread.sleep(offset);
                    // commandDepthParsed = commandDepthParsed + 2;
                    handleMessage(Arrays.copyOfRange(message, 2, message.length));
                } catch (Exception ex) {
                    // TODO: find possible exceptions
                    String error = "err0r when trying to postpone command execution";
                    Logger.logException(this, error, ex);
                    this.replyMessage(error);
                }
            } else {
                String error = "usage: postpone <offset> <command> [command options]..; see help for more information.";
                Logger.logMessage('E', this, "not enough arguments given for postpone command. " + error);
                this.replyMessage(error);
            }
        } else {
            this.noPermAns("postpone");
        }
    }

    private void givePrivilege(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_GIVEPRIVILEGE)) {
            if (message.length >= 4) { //givePrivilege <user> <privilege> <state>
                boolean err = false;
                int userID = -1;
                int privID = -1;
                boolean state = false;
                try {
                    userID = Integer.parseInt(message[1]);
                } catch (Exception ex) {
                    //TODO: Find possible exceptions
                    err = true;
                    Logger.logMessage('E', this,
                            "User " + acc.getAccountName() + " with ID " + String.valueOf(acc.getAccountID())
                                    + " provided wrong userID '" + message[1] + "' for givePrivilege-command");
                    this.replyMessage("Wrong userID provided: '" + message[1] + "'. Use userInfo-"
                            + "command to get userIDs.");
                }

                try {
                    privID = Integer.parseInt(message[2]);
                } catch (Exception ex) {
                    //TODO: Find possible exceptions
                    err = true;
                    Logger.logMessage('E', this,
                            "User " + acc.getAccountName() + " with ID " + String.valueOf(acc.getAccountID())
                                    + " provided invalid permissionID '" + message[2]
                                    + "' for givePrivilege-command");
                    this.replyMessage("Wrong privilegeID provided: '" + message[2] + "'. Use listPermissions-"
                            + "command to get privilege IDs.");
                }

                switch (message[3].toLowerCase()) {
                case "false":
                    state = false;
                    break;
                case "true":
                    state = true;
                    break;
                case "0":
                    state = false;
                    break;
                case "1":
                    state = true;
                    break;
                case "deny":
                    state = false;
                    break;
                case "allow":
                    state = true;
                    break;
                default:
                    err = true;
                    Logger.logMessage('E', this,
                            "User " + acc.getAccountName() + " with ID " + String.valueOf(acc.getAccountID())
                                    + " provided invalid state '" + message[3] + "' for givePrivilege-command");
                    this.replyMessage("Wrong state provided: '" + message[3] + "'. Values 'true', 'false',"
                            + " '0', '1', 'allow', 'deny' allowed.");
                }

                if (!err) {
                    Account dummy = new Account(userID);
                    Account dest = AccountManager.getAccount(dummy);
                    if (dest != null) {
                        try {
                            dest.setAccountPrivilege(privID, state, acc);
                        } catch (InsufficientPrivilegeException e) {
                            err = true;
                            this.replyMessage(
                                    "You are not allowed to set the " + AccountPrivileges.getPrivStringSafe(privID)
                                            + "-privilege for the account " + dest.getAccountName() + " with ID "
                                            + String.valueOf(dest.getAccountID() + ". No changes made."));
                        } catch (PrivilegeNotFoundException e) {
                            err = true;
                            this.replyMessage(
                                    "Privilege with ID " + String.valueOf(privID) + " not found. No changes made.");
                        }
                    }
                    if (!err) {
                        try {
                            this.replyMessage("Set " + AccountPrivileges.getPrivString(privID)
                                    + "-privilege for Account " + dest.getAccountName() + " with ID "
                                    + String.valueOf(dest.getAccountID()) + " to " + String.valueOf(state) + ".");
                        } catch (PrivilegeNotFoundException e) {
                            this.replyMessage("Set " + "ID-" + String.valueOf(privID) + "-privilege for Account "
                                    + dest.getAccountName() + " with ID " + String.valueOf(dest.getAccountID())
                                    + " to " + String.valueOf(state) + ".");
                        }
                        this.sendMessage(String.valueOf(userID),
                                "You now have the " + AccountPrivileges.getPrivStringSafe(privID) + "-privilege, "
                                        + "granted by " + acc.toString() + ".");
                    }
                }
            } else {
                //Command length is insufficient
                String usage = "usage: givePrivilege <user> <privilegeID> <state>";
                Logger.logMessage('E', this, "User " + acc.toString()
                        + " provided not enough arguments for givePrivilege-command: " + usage);
                this.replyMessage(usage);
            }
        } else {
            //Account has no cmd_givePrivilege-privilege
            this.noPermAns("givePrivilege");
        }
    }

    private void giveAdmin(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_GIVEADMIN)) {
            if (message.length >= 4) {
                boolean err = false;
                int userID = -1;
                int adminID = -1;
                boolean state = false;
                try {
                    userID = Integer.parseInt(message[1]);
                } catch (Exception ex) {
                    //TODO: Find possible exceptions
                    err = true;
                    Logger.logMessage('E', this,
                            "User " + acc.getAccountName() + " with ID " + String.valueOf(acc.getAccountID())
                                    + " provided wrong userID '" + message[1] + "' for giveAdmin-command");
                    this.replyMessage("Wrong userID provided: '" + message[1] + "'. Use userInfo-"
                            + "command to get userIDs.");
                }

                try {
                    adminID = Integer.parseInt(message[2]);
                } catch (Exception ex) {
                    //TODO: Find possible exceptions
                    err = true;
                    Logger.logMessage('E', this,
                            "User " + acc.getAccountName() + " with ID " + String.valueOf(acc.getAccountID())
                                    + " provided invalid permissionID '" + message[2] + "' for giveAdmin-command");
                    this.replyMessage("Wrong privilegeID provided: '" + message[2] + "'. Use listPermissions-"
                            + "command to get privilege IDs.");
                }

                switch (message[3].toLowerCase()) {
                case "false":
                    state = false;
                    break;
                case "true":
                    state = true;
                    break;
                case "0":
                    state = false;
                    break;
                case "1":
                    state = true;
                    break;
                case "deny":
                    state = false;
                    break;
                case "allow":
                    state = true;
                    break;
                default:
                    err = true;
                    Logger.logMessage('E', this,
                            "User " + acc.getAccountName() + " with ID " + String.valueOf(acc.getAccountID())
                                    + " provided invalid state '" + message[3] + "' for giveAdmin-command");
                    this.replyMessage("Wrong state provided: '" + message[3] + "'. Values 'true', 'false',"
                            + " '0', '1', 'allow', 'deny' allowed.");
                }

                if (!err) {
                    Account dummy = new Account(userID);
                    Account dest = AccountManager.getAccount(dummy);
                    if (dest != null) {
                        try {
                            dest.getPriv().setAdmin(adminID, state, acc);
                        } catch (InsufficientPrivilegeException e) {
                            err = true;
                            this.replyMessage(
                                    "You are not allowed to set the " + AccountPrivileges.getPrivStringSafe(adminID)
                                            + "-privilege for the account " + dest.getAccountName() + " with ID "
                                            + String.valueOf(dest.getAccountID() + ". No changes made."));
                        } catch (PrivilegeNotFoundException e) {
                            err = true;
                            this.replyMessage("Admin-level with ID " + String.valueOf(adminID)
                                    + " not found. No changes made.");
                        }
                    } else {
                        err = true;
                    }
                    if (!err) {
                        try {
                            this.replyMessage("Set " + AccountPrivileges.getPrivString(adminID)
                                    + "-privilege for Account " + dest.getAccountName() + " with ID "
                                    + String.valueOf(dest.getAccountID()) + " to " + String.valueOf(state) + ".");
                        } catch (PrivilegeNotFoundException e) {
                            this.replyMessage("Set " + "ID-" + String.valueOf(adminID) + "-privilege for Account "
                                    + dest.getAccountName() + " with ID " + String.valueOf(dest.getAccountID())
                                    + " to " + String.valueOf(state) + ".");
                        }
                        this.sendMessage(String.valueOf(userID),
                                "You now have the " + AccountPrivileges.getPrivStringSafe(adminID) + "-level, "
                                        + "granted by " + acc.toString() + ".");
                    }
                }
            } else {
                //Command length is insufficient
                String usage = "usage: giveAdmin <user> <level> <state>";
                Logger.logMessage('W', this, "User " + acc.toString()
                        + " provided not enough arguments for giveAdmin-Command: " + usage);
                this.replyMessage(usage);
            }
        } else {
            //Account has no cmd_giveAdmin-privilege
            this.noPermAns("giveAdmin");
        }
    }

    private void listPrivileges(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_LISTPRIVILEGES)) {
            String privileges = "";
            for (int i = 0; i < AccountPrivileges.MOST_PERMISSION_ID; i++) {
                if (i != 0) {
                    privileges = privileges + ", ";
                }
                privileges = privileges + String.valueOf(i);
                try {
                    privileges = privileges + ": " + AccountPrivileges.getPrivString(i);
                } catch (PrivilegeNotFoundException e) {
                    // Do nothing
                }
            }
            this.replyMessage(privileges);
        }
    }

    private void addAlias(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_ADDALIAS)) {
            if (message.length >= 3) { //addAlias <aliasName> <command>
                acc.addAlias(message[1], Arrays.copyOfRange(message, 2, message.length));
            } else {
                String usage = "usage: addAlias <aliasName> <command>";
                Logger.logMessage('I', this, "User " + acc.toString() + " provided not enough"
                        + " arguments for addAlias-command. No changes made.");
                this.replyMessage("Not enough argumenets! " + usage);
            }
        } else {
            this.noPermAns("addAlias");
        }
    }

    private void listAlias(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_LISTALIAS)) {
            try {
                this.replyMessage("List of aliases for Account " + acc.toString() + "\n"
                        + StringTools.StringArrayToString(acc.getAllAlias()));
            } catch (AliasNotFoundException e) {
                Logger.logMessage('I', this, "User " + acc.toString() + " does not seem to have"
                        + " any aliases to list in listAlias-command.");
            }
        } else {
            this.noPermAns("listAlias");
        }
    }

    private void removeAlias(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_REMOVEALIAS)) {
            if (message.length >= 2) { //<removeAlias> <aliasName>
                try {
                    acc.removeAlias(message[1]);
                } catch (AliasNotFoundException ex) {
                    Logger.logMessage('I', this, "User " + acc.toString() + " provided invalid"
                            + " alias name for removeAlias-command: " + message[1]);
                    this.replyMessage("Alias not found: " + message[1]);
                }
            } else {
                String usage = "usage: removeAlias <aliasName>";
                Logger.logMessage('I', this, "User " + acc.getAccountName() + " didn't provide enough"
                        + " arguments for removeAlias-command.");
                this.replyMessage("Not enough arguments! " + usage);
            }
        } else {
            this.noPermAns("removeAlias");
        }
    }

    private void help(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_INFO)
                && acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_HELP)) {
            try {
                StringBuilder info = FileHandler.readStringBuilder("information");
                info.append(FileHandler.readStringBuilder("commandHelp"));
                this.replyMessage(info.toString().replace(System.lineSeparator(), " "));
            } catch (Exception ex) {
                // TODO: find possible exceptions
                String error = "Error when reading help file.";
                Logger.logMessage('E', this, error);
                this.replyMessage(error);
            }
        } else {
            this.noPermAns("help");
        }
    }

    @SuppressWarnings("unused")
    private void info(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_INFO)) {
            try {
                StringBuilder info = FileHandler.readStringBuilder("information");
                this.replyMessage(info.toString());
            } catch (Exception ex) {
                // TODO: find possible exceptions
                String error = "Error when reading info file.";
                Logger.logMessage('E', this, error);
                this.replyMessage(error);
            }
        } else {
            this.noPermAns("info");
        }
    }

    private void healthreport(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_HEALTHREPORT)) {
            if (message.length < 1) {
                switch (message[1]) {
                case "now":
                    break; // make a new healthreport now
                }
            } else {
                // send the last healthreport
            }
        } else {
            this.noPermAns("healthreport");
        }
    }

    private void userInfo(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_USERID)) {
            if (message.length > 1) {
                this.waitsForUserInfo = true;
                notifier.enqueueForUserInfo(this);
                notifier.send("user_info " + message[1]);
            } else {
                String error = "usage: userID <user>: returns the user ID of requested user.";
                Logger.logMessage('E', this, "not enough arguments given for userID command. " + error);
                this.replyMessage(error);
            }
        } else {
            this.noPermAns("userID");
        }
    }

    private void repeat(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_REPEAT)) {
            if (message.length >= 5) { //0:repeat 1:<index> 2:<start> 3:<end> 4:<command>
                String indexName = "";
                int start = 0;
                int end = 0;
                boolean err = false;

                indexName = message[1];

                try {
                    start = Integer.parseInt(message[2]);
                } catch (Exception ex) {
                    err = true;
                    Logger.logMessage('W', this, "User " + acc.toString() + " provided wrong startIndex"
                            + " for repeat-command: " + message[2]);
                    this.replyMessage("Wrong startIndex provided: " + message[2] + ". Only numbers allowed.");
                }

                try {
                    end = Integer.parseInt(message[3]);
                } catch (Exception ex) {
                    err = true;
                    Logger.logMessage('W', this, "User " + acc.toString() + " provided wrong endIndex"
                            + " for repeat-command: " + message[3]);
                    this.replyMessage("Wrong endIndex provided: " + message[3] + ". Only numbers allowed.");
                }

                if (!err) {
                    this.replyMessage("Executing repeat-command from " + String.valueOf(start) + " to "
                            + String.valueOf(end) + " with indexName " + indexName);
                    String[] newMessage = Arrays.copyOfRange(message, 4, message.length);
                    String[] workingCopy = new String[newMessage.length];
                    //execute command repeated
                    for (int i = start; i <= end; i++) {
                        //                  System.err.println("ASDF!");
                        //replace occurrences of indexName in all command information
                        for (int j = 0; j < newMessage.length; j++) {
                            workingCopy[j] = newMessage[j].replace(indexName, String.valueOf(i));
                        }
                        //                  this.replyMessage("Executing command: " + StringTools.StringArrayToString(workingCopy));
                        handleMessage(workingCopy);
                    }
                }
            } else {
                String usage = "repeat <indexName> <startIndex> <endIndex> <command> [commandParams]; startIndex is the lowest, endIndex the highest value inserted for indexName";
                Logger.logMessage('E', this,
                        "User " + acc.toString() + " provided not enough arguments for repeat-command.");
                this.replyMessage("Not enough arguments! usage: " + usage);
            }
        } else {
            this.noPermAns("repeat");
        }
    }

    private void shutdown(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_SHUTDOWN)) {

        } else {
            this.noPermAns("shutdown");
        }
    }

    private void restart(String[] message) {
        if (acc.hasAccountPrivilege(AccountPrivileges.PERM_CMD_RESTART)) {

        } else {
            this.noPermAns("restart");
        }
    }

    private void sendMessage(int recipientID, String messageText) {
        this.sendMessage(String.valueOf(recipientID), messageText);
    }

    private void sendMessage(String recipient, String messageText) {
        if (raw || message.getFrom().getID() < 0) {
            Logger.logMessage('W', this, "will not answer message " + String.valueOf(id)
                    + ", because it was internal. Message text: " + messageText);
        } else {
            notifier.send(notifier.getSendCommand() + recipient + " " + messageText);
        }
    }

    private void replyMessage(String messageText) {
        sendMessage(message.getFromPrintName(), messageText);
    }

    private void noPermAns(String command) {
        this.replyMessage("sorry.");
        Logger.logMessage('W', this,
                "User " + acc.getAccountName() + " with ID " + String.valueOf(acc.getAccountID())
                        + " has tried to execute " + command + "-command without sufficient permission.");
        Logger.logMessage('W', this,
                "User " + acc.getAccountName() + " with ID " + String.valueOf(acc.getAccountID())
                        + " has tried to execute " + command + "-command without sufficient permission.",
                "priv");
    }

    public void on_UserInfoArrive(JSONObject obj) {
        String defaultValue = "n.a.";
        String v = "";
        if (this.waitsForUserInfo) { // If an user_info was requested in this Handler
            if (obj.has("type") ? obj.getString("type").equals("user") : false) { // If the JSON-Object really is an user-info.
                notifier.send("UserInfo for ID "
                        + ((v = String.valueOf(obj.getIntSafe("id", -1))).equals("-1") ? defaultValue : v)
                        + ": Username: "
                        + ((v = obj.getStringSafe("username", defaultValue)).equals("") ? defaultValue : v)
                        + ", First Name: "
                        + ((v = obj.getStringSafe("first_name", defaultValue)).equals("") ? defaultValue : v)
                        + ", Last Name: "
                        + ((v = obj.getStringSafe("last_name", defaultValue)).equals("") ? defaultValue : v)
                        + ", Print Name: "
                        + ((v = obj.getStringSafe("print_name", defaultValue)).equals("") ? defaultValue : v)
                        + ", phone: "
                        + ((v = String.valueOf(obj.getIntSafe("phone", -1))).equals("-1") ? defaultValue : v)
                        + ", flags: "
                        + ((v = String.valueOf(obj.getIntSafe("flags", -1))).equals("-1") ? defaultValue : v)
                        + ", type: "
                        + ((v = obj.getStringSafe("type", defaultValue)).equals("") ? defaultValue : v));
            }
        }
    }
}