public class Config {

    public static final boolean LOGD = true;

} public class Constants {
    public static final String TAG = "irondad/";

    public static final String PROJECT_FULL_NAME = "BoD irondad";
    public static final String PROJECT_URL = "https://github.com/BoD/irondad";
    public static final String VERSION_NAME = "v1.11.1"; // xxx When updating this, don't forget to update the version in pom.xml as well
} public abstract class CommandHandler extends BaseHandler {
    protected abstract String getCommand();

    @Override
    public boolean isMessageHandled(String channel, String fromNickname, String text, List<String> textAsList, Message message, HandlerContext handlerContext) {
        String command = getCommand();
        return text.trim().toLowerCase(Locale.getDefault()).startsWith(command.toLowerCase(Locale.getDefault()));
    }
} public class HandlerContext extends HashMap<String, Object> {
    private final HandlerConfig mHandlerConfig;
    private final String mChannelName;
    private Connection mConnection;

    public HandlerContext(HandlerConfig handlerConfig, String channelName) {
        mHandlerConfig = handlerConfig;
        mChannelName = channelName;
    }

    public HandlerConfig getHandlerConfig() {
        return mHandlerConfig;
    }

    public void setConnection(Connection connection) {
        mConnection = connection;
    }

    public Connection getConnection() {
        return mConnection;
    }

    /**
     * @return {@code null} if this is a privmsg context.
     */
    public String getChannelName() {
        return mChannelName;
    }
} public static class Quote {
    public long id;
    public Date date;
    public String text;

    @Override
    public String toString() {
        return "Quote [id=" + id + ", date=" + date + ", text=" + text + "]";
    }
} public class ClientConfig {
    public static class HandlerClassAndConfig {
        public Class<? extends Handler> handlerClass;
        public HandlerConfig handlerConfig;
    }

    private final String mHost;
    private final int mPort;
    private final String mNickname;
    private final String mAdminPassword;
    private final Map<String, HandlerClassAndConfig> mHandlerConfigs = new HashMap<String, HandlerClassAndConfig>();
    private final List<String> mPrivmsgHandlerConfigNames = new ArrayList<String>();
    private final ListMultimap<String, String> mChannelHandlerConfigNames = ArrayListMultimap.create();

    public ClientConfig(String host, int port, String nickname, String adminPassword) {
        mHost = host;
        mPort = port;
        mNickname = nickname;
        mAdminPassword = adminPassword;
    }

    public String getHost() {
        return mHost;
    }

    public int getPort() {
        return mPort;
    }

    public Set<String> getChannels() {
        return Collections.unmodifiableSet(mChannelHandlerConfigNames.keySet());
    }

    public String getNickname() {
        return mNickname;
    }

    public String getAdminPassword() {
        return mAdminPassword;
    }

    public void addHandlerConfig(String configName, Class<? extends Handler> handlerClass, HandlerConfig handlerConfig) {
        HandlerClassAndConfig handlerClassAndConfig = new HandlerClassAndConfig();
        handlerClassAndConfig.handlerClass = handlerClass;
        handlerClassAndConfig.handlerConfig = handlerConfig;
        mHandlerConfigs.put(configName, handlerClassAndConfig);
    }

    public HandlerClassAndConfig getHandlerConfig(String configName) {
        return mHandlerConfigs.get(configName);
    }

    public List<String> getPrivmsgHandlerConfigNames() {
        return Collections.unmodifiableList(mPrivmsgHandlerConfigNames);
    }

    public void addPrivmsgHandlerConfig(String configName) {
        mPrivmsgHandlerConfigNames.add(configName);
    }

    public List<String> getChannelHandlerConfigNames(String channel) {
        return Collections.unmodifiableList(mChannelHandlerConfigNames.get(channel));
    }

    public void addChannelHandlerConfig(String channel, String configName) {
        mChannelHandlerConfigNames.put(channel, configName);
    }
} public enum Command {
    //@formatter:off
    
    /*
     * Messages.
     */
    
    PING,
    PONG,
    NICK,
    USER,
    JOIN,
    PART,
    PRIVMSG,
    NAMES,
    QUIT,
    
    UNKNOWN, 
    
    
    /*
     * Replies.
     */
    
    RPL_WELCOME(1),
    
    RPL_NAMREPLY(353),
    
    ERR_ERRONEUSNICKNAME(432),
    ERR_NICKNAMEINUSE(433),
    ERR_NICKCOLLISION(436),
    
    ;

    //@formatter:on

    private static final HashMap<String, Command> sCommandByName = new HashMap<String, Command>(40);
    private static final HashMap<Integer, Command> sCommandByCode = new HashMap<Integer, Command>(40);

    static {
        for (Command command : values()) {
            sCommandByName.put(command.name(), command);
            if (command.mCode != -1) sCommandByCode.put(command.mCode, command);
        }
    }

    private int mCode;

    private Command() {
        mCode = -1;
    }

    private Command(int code) {
        mCode = code;
    }

    public static Command from(String commandStr) {
        if (StringUtils.isNumeric(commandStr)) {
            // Reply (numeric command)
            return from(Integer.valueOf(commandStr));
        }
        // Message (string command)
        Command res = sCommandByName.get(commandStr);
        if (res == null) return UNKNOWN;
        return res;
    }

    public static Command from(int code) {
        Command res = sCommandByCode.get(code);
        if (res == null) return UNKNOWN;
        return res;
    }
} public class Connection {
    private static final String TAG = Constants.TAG + Connection.class.getSimpleName();
    private static final String CR_LF = "\r\n";

    private Client mClient;
    private final BufferedReader mBufferedReader;
    private final OutputStream mOutputStream;

    public Connection(Client client, Socket socket) throws IOException {
        mClient = client;
        mBufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
        mOutputStream = socket.getOutputStream();
    }

    public void send(String line) throws IOException {
        Log.d(TAG, "SND " + line);
        mOutputStream.write((line + CR_LF).getBytes("utf-8"));
    }

    public void send(Command command, String... params) throws IOException {
        String[] paramsCopy = params.clone();
        if (paramsCopy.length > 0) {
            // Add a colon to the last param if it contains spaces
            if (paramsCopy[paramsCopy.length - 1].contains(" ")) {
                paramsCopy[paramsCopy.length - 1] = ":" + paramsCopy[paramsCopy.length - 1];
            }
            send(command.name() + " " + StringUtils.join(paramsCopy, " "));
        } else {
            send(command.name());
        }
    }

    public void send(Command command, List<String> params) throws IOException {
        String[] paramArray = params.toArray(new String[params.size()]);
        send(command, paramArray);
    }

    public String receiveLine() throws IOException {
        String line = mBufferedReader.readLine();
        Log.i(TAG, "RCV " + line);
        return line;
    }

    public Message receive() throws IOException {
        String line = receiveLine();
        if (line == null) return null;
        Message res = Message.parse(line);
        //        if (Config.LOGD) Log.d(TAG, "receive res=" + res);
        return res;
    }

    public Client getClient() {
        return mClient;
    }
} public class Message {
    /**
     * Origin of the message (can be {@code null}).
     */
    public final Origin origin;
    public final Command command;
    public final ArrayList<String> parameters;

    public Message(Origin origin, Command command, ArrayList<String> parameters) {
        this.origin = origin;
        this.command = command;
        this.parameters = parameters;
    }

    public static Message parse(String line) {
        ArrayList<String> split = split(line);
        Origin origin = null;
        if (split.get(0).startsWith(":")) {
            // Prefix is an optional origin
            String originStr = split.remove(0);
            // Remove colon
            originStr = originStr.substring(1);
            origin = new Origin(originStr);
        }
        String commandStr = split.remove(0);
        Command command = Command.from(commandStr);
        return new Message(origin, command, split);
    }



    private static ArrayList<String> split(String line) {
        ArrayList<String> split = new ArrayList<String>(10);
        int len = line.length();
        StringBuilder currentToken = new StringBuilder(10);
        boolean previousCharIsSpace = false;
        boolean lastParam = false;
        for (int i = 0; i < len; i++) {
            char c = line.charAt(i);
            if (c == ' ' && !lastParam) {
                // Space
                split.add(currentToken.toString());
                currentToken = new StringBuilder(10);
                previousCharIsSpace = true;
            } else if (c == ':' && i != 0 && previousCharIsSpace) {
                // Colon: if at start of token, the remainder is the last param (can have spaces)
                lastParam = true;
                //                currentToken.append(c);
            } else {
                // Other characters
                currentToken.append(c);
                previousCharIsSpace = false;
            }
        }
        split.add(currentToken.toString());
        return split;
    }

    @Override
    public String toString() {
        return "Message [origin=" + origin + ", command=" + command + ", parameters=" + parameters + "]";
    }
} public class Log {
    public static void w(String tag, String msg, Throwable t) {
        System.out.print(getDate() + " W " + tag + " " + msg + "\n" + getStackTraceString(t));
    }

    public static void w(String tag, String msg) {
        System.out.print(getDate() + " W " + tag + " " + msg + "\n");
    }

    public static void e(String tag, String msg, Throwable t) {
        System.out.print(getDate() + " E " + tag + " " + msg + "\n" + getStackTraceString(t));
    }

    public static void e(String tag, String msg) {
        System.out.print(getDate() + " E " + tag + " " + msg + "\n");
    }

    public static void d(String tag, String msg, Throwable t) {
        System.out.print(getDate() + " D " + tag + " " + msg + "\n" + getStackTraceString(t));
    }

    public static void d(String tag, String msg) {
        System.out.print(getDate() + " D " + tag + " " + msg + "\n");
    }

    public static void i(String tag, String msg, Throwable t) {
        System.out.print(getDate() + " I " + tag + " " + msg + "\n" + getStackTraceString(t));
    }

    public static void i(String tag, String msg) {
        System.out.print(getDate() + " I " + tag + " " + msg + "\n");
    }

    private static String getDate() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd/HH:mm:ss");
        return sdf.format(new Date());
    }

    private static String getStackTraceString(Throwable tr) {
        if (tr == null) {
            return "";
        }

        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        tr.printStackTrace(pw);
        return sw.toString();
    }
}
import org.apache.commons.lang3.StringUtils;
import org.jraf.irondad.Config;
import org.jraf.irondad.Constants;
import org.jraf.irondad.handler.CommandHandler;
import org.jraf.irondad.handler.HandlerContext;
import org.jraf.irondad.handler.quote.DbManager.Quote;
import org.jraf.irondad.protocol.ClientConfig;
import org.jraf.irondad.protocol.Command;
import org.jraf.irondad.protocol.Connection;
import org.jraf.irondad.protocol.Message;
import org.jraf.irondad.util.Log;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/*
 * This source is part of the
 *      _____  ___   ____
 *  __ / / _ \/ _ | / __/___  _______ _
 * / // / , _/ __ |/ _/_/ _ \/ __/ _ `/
 * \___/_/|_/_/ |_/_/ (_)___/_/  \_, /
 *                              /___/
 * repository.
 *
 * Copyright (C) 2013 Benoit 'BoD' Lubek (BoD@JRAF.org)
 *
 * 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 3 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.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see
 * <http://www.gnu.org/licenses/>.
 */
package org.jraf.irondad.handler.quote;



public class QuoteHandler extends CommandHandler {
    private static final String TAG = Constants.TAG + QuoteHandler.class.getSimpleName();

    private static final String DATE_FORMAT = "yyyy-MM-dd";
    private static final long MIN_DELAY_BETWEEN_QUOTES = 2 * 60 * 1000;

    private Map<String, DbManager> mDbManagers = new HashMap<String, DbManager>();
    private ClientConfig mClientConfig;