summaryrefslogtreecommitdiffstats
path: root/EssentialsUpdate/src/f00f/net/irc
diff options
context:
space:
mode:
Diffstat (limited to 'EssentialsUpdate/src/f00f/net/irc')
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/ClientStateMonitor.java67
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/Command.java18
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/CommandObserver.java5
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/CommandRegister.java118
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/CommandSender.java14
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/CronManager.java80
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/ForwardObservable.java66
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/GenericAutoService.java54
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/GenericCommandAutoService.java79
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/IRCConnection.java1163
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/InCommand.java99
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/InputHandler.java130
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/Mode.java103
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/OutCommand.java24
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/State.java31
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/StateObserver.java8
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/TimerTaskCommand.java57
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/clientstate/Channel.java373
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/clientstate/ClientState.java193
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/clientstate/Member.java114
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/AbstractCommand.java32
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/AbstractInCommand.java174
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/ActionCtcp.java12
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/ChannelModeCommand.java189
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/CtcpMessage.java129
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/CtcpNotice.java131
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/InviteCommand.java90
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/IsonCommand.java144
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/JoinCommand.java131
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/KickCommand.java110
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/MessageCommand.java127
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/ModeCommand.java237
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/NamesCommand.java77
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/NickCommand.java97
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/NoticeCommand.java129
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/PartCommand.java126
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/PassCommand.java33
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/PingCommand.java67
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/PongCommand.java39
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/QuitCommand.java132
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/RawCommand.java58
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/TopicCommand.java80
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/UnknownCommand.java38
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/UserCommand.java46
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/UserModeCommand.java99
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/WelcomeCommand.java125
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/commands/WhoisCommand.java40
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/AlreadyRegisteredError.java40
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/CannotSendToChanError.java47
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/CantKillServerError.java40
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChanOPrivsNeededError.java48
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelBannedError.java49
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelInviteOnlyError.java48
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelLimitError.java50
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelWrongKeyError.java50
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/ErroneusNicknameError.java47
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/FileErrorError.java39
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/GenericError.java14
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/GenericJoinError.java67
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/KeySetError.java45
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/LoadTooHighError.java49
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NeedMoreParamsError.java47
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NickCollisionError.java48
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NickInUseError.java63
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoAdminInfoError.java47
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoLoginError.java48
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoMotdError.java39
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoNicknameGivenError.java39
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoOperHostError.java40
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoOriginError.java40
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoPermForHostError.java40
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoPrivilegesError.java40
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoRecipientError.java38
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoSuchChannelError.java46
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoSuchNickError.java47
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoSuchServerError.java46
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoTextToSendError.java41
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoTopLevelError.java48
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NotOnChannelError.java47
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/NotRegisteredError.java40
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/PasswdMismatchError.java40
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/SummonDisabledError.java40
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/TooManyChannelsError.java46
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/TooManyTargetsError.java47
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/UModeUnknownFlagError.java40
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/UnknownCommandError.java46
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/UnknownError.java69
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/UnknownModeError.java45
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/UserNotInChannelError.java54
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/UserOnChannelError.java54
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/UsersDisabledError.java40
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/UsersDontMatchError.java39
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/WasNoSuchNickError.java47
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/WildTopLevelError.java48
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/errors/YoureBannedCreepError.java40
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/GenericMode.java77
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/README8
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/AnonChannelMode.java44
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/BanMode.java38
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/ExceptionMode.java38
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/GenericChannelMask.java16
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/GenericChannelMode.java23
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/InviteMaskMode.java30
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/InviteOnlyMode.java32
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/KeyMode.java31
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/LimitMode.java31
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/ModeratedMode.java31
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/NoExtMsgMode.java30
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/OperMode.java31
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/PrivateMode.java50
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/SecretMode.java50
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/TopicLockMode.java27
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/VoiceMode.java34
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/user/GenericUserMode.java24
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/modes/user/InvisibleMode.java22
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/AbstractWhoisReply.java58
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/AwayReply.java49
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/ChannelCreationReply.java78
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/GenericReply.java16
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/GenericStringReply.java23
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/LUserClientReply.java28
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/LUserMeReply.java28
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/LUserOpReply.java36
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/ListEndReply.java31
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/ListReply.java63
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/ListStartReply.java31
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/ModeReply.java54
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/NamesEndReply.java47
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/NamesReply.java89
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/NowAwayReply.java43
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/TopicInfoReply.java80
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/UnAwayReply.java43
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/UnknownReply.java66
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisChannelsReply.java70
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisEndReply.java37
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisIdleReply.java68
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisServerReply.java61
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisUserReply.java54
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoJoin.java157
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoReconnect.java271
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoRegister.java289
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoResponder.java82
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/util/CtcpUtil.java99
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/util/FullNick.java159
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/util/IRCStringUtils.java90
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/util/ParameterIterator.java124
146 files changed, 10751 insertions, 0 deletions
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/ClientStateMonitor.java b/EssentialsUpdate/src/f00f/net/irc/martyr/ClientStateMonitor.java
new file mode 100644
index 000000000..0b7303956
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/ClientStateMonitor.java
@@ -0,0 +1,67 @@
+package f00f.net.irc.martyr;
+
+import java.util.Observable;
+import java.util.Observer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * ClientStateMonitor asks commands to update the client state.
+ */
+public class ClientStateMonitor implements Observer
+{
+
+ static Logger log = Logger.getLogger(ClientStateMonitor.class.getName());
+
+ private IRCConnection connection;
+
+ private boolean enabled = false;
+
+ /**
+ * This should only be called by the IRCConnection itself.
+ *
+ * @param connection Connection we are associated with
+ */
+ ClientStateMonitor( IRCConnection connection )
+ {
+ this.connection = connection;
+
+ enable();
+ }
+
+ public void enable()
+ {
+ if( enabled )
+ return;
+ enabled = true;
+
+ connection.addCommandObserver( this );
+ }
+
+ public void disable()
+ {
+ if( !enabled )
+ return;
+ connection.removeCommandObserver( this );
+ enabled = false;
+ }
+
+ public void update( Observable observable, Object command_o )
+ {
+ InCommand command = (InCommand)command_o;
+
+ try
+ {
+ /*if( */command.updateClientState( connection.getClientState() );// )
+ //log.debug("ClientStateMonnitor: Client state updated");
+ }
+ catch( Throwable e )
+ {
+ log.log(Level.SEVERE,"ClientStateMonitor: Client state update failed.", e);
+ }
+
+ }
+
+ // ===== END ClientStateMonitor
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/Command.java b/EssentialsUpdate/src/f00f/net/irc/martyr/Command.java
new file mode 100644
index 000000000..ff8f168cd
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/Command.java
@@ -0,0 +1,18 @@
+package f00f.net.irc.martyr;
+
+/**
+ * Defines an object which is a command, either incoming or outgoing.
+ */
+public interface Command
+{
+ /**
+ * Returns the string IRC uses to identify this command. Examples:
+ * NICK, PING, KILL, 332. Not strictly required for OutCommands
+ * as the irc identifier is expected to be part of the reder()
+ * result.
+ *
+ * @return The IRC identifier string
+ */
+ String getIrcIdentifier();
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/CommandObserver.java b/EssentialsUpdate/src/f00f/net/irc/martyr/CommandObserver.java
new file mode 100644
index 000000000..5f988c0c3
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/CommandObserver.java
@@ -0,0 +1,5 @@
+package f00f.net.irc.martyr;
+
+public class CommandObserver extends StateObserver
+{
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/CommandRegister.java b/EssentialsUpdate/src/f00f/net/irc/martyr/CommandRegister.java
new file mode 100644
index 000000000..2c7dca1dc
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/CommandRegister.java
@@ -0,0 +1,118 @@
+package f00f.net.irc.martyr;
+
+import java.util.Hashtable;
+
+import f00f.net.irc.martyr.commands.*;
+import f00f.net.irc.martyr.errors.*;
+import f00f.net.irc.martyr.replies.*;
+
+/**
+ * CommandRegister is basically a big hashtable that maps IRC
+ * identifiers to command objects that can be used as factories to
+ * do self-parsing. CommandRegister is also the central list of
+ * commands.
+ */
+public class CommandRegister
+{
+
+ private Hashtable<String,InCommand> commands;
+ public CommandRegister()
+ {
+ commands = new Hashtable<String,InCommand>();
+
+ // Note that currently, we only have to register commands that
+ // can be received from the server.
+ new InviteCommand().selfRegister( this );
+ new JoinCommand().selfRegister( this );
+ new KickCommand().selfRegister( this );
+ new MessageCommand().selfRegister( this );
+ new ModeCommand().selfRegister( this );
+ new IsonCommand().selfRegister( this );
+ new NickCommand().selfRegister( this );
+ new NoticeCommand().selfRegister( this );
+ new PartCommand().selfRegister( this );
+ new PingCommand().selfRegister( this );
+ new QuitCommand().selfRegister( this );
+ new TopicCommand().selfRegister( this );
+ new WelcomeCommand().selfRegister( this );
+
+ // Register errors
+ new AlreadyRegisteredError().selfRegister( this );
+ new CannotSendToChanError().selfRegister( this );
+ new CantKillServerError().selfRegister( this );
+ new ChannelBannedError().selfRegister( this );
+ new ChannelInviteOnlyError().selfRegister( this );
+ new ChannelLimitError().selfRegister( this );
+ new ChannelWrongKeyError().selfRegister( this );
+ new ChanOPrivsNeededError().selfRegister( this );
+ new ErroneusNicknameError().selfRegister( this );
+ new FileErrorError().selfRegister( this );
+ new KeySetError().selfRegister( this );
+ new LoadTooHighError().selfRegister( this );
+ new NeedMoreParamsError().selfRegister( this );
+ new NickCollisionError().selfRegister( this );
+ new NickInUseError().selfRegister( this );
+ new NoAdminInfoError().selfRegister( this );
+ new NoLoginError().selfRegister( this );
+ new NoMotdError().selfRegister( this );
+ new NoNicknameGivenError().selfRegister( this );
+ new NoOperHostError().selfRegister( this );
+ new NoOriginError().selfRegister( this );
+ new NoPermForHostError().selfRegister( this );
+ new NoPrivilegesError().selfRegister( this );
+ new NoRecipientError().selfRegister( this );
+ new NoSuchChannelError().selfRegister( this );
+ new NoSuchNickError().selfRegister( this );
+ new NoSuchServerError().selfRegister( this );
+ new NoTextToSendError().selfRegister( this );
+ new NotOnChannelError().selfRegister( this );
+ new NotRegisteredError().selfRegister( this );
+ new PasswdMismatchError().selfRegister( this );
+ new SummonDisabledError().selfRegister( this );
+ new TooManyChannelsError().selfRegister( this );
+ new TooManyTargetsError().selfRegister( this );
+ new UModeUnknownFlagError().selfRegister( this );
+ new UnknownCommandError().selfRegister( this );
+ new UnknownModeError().selfRegister( this );
+ new UserNotInChannelError().selfRegister( this );
+ new UserOnChannelError().selfRegister( this );
+ new UsersDisabledError().selfRegister( this );
+ new UsersDontMatchError().selfRegister( this );
+ new WasNoSuchNickError().selfRegister( this );
+ new WildTopLevelError().selfRegister( this );
+ new YoureBannedCreepError().selfRegister( this );
+
+ // Register replies
+ new ChannelCreationReply().selfRegister( this );
+ new AwayReply().selfRegister( this );
+ new ListEndReply().selfRegister( this );
+ new ListReply().selfRegister( this );
+ new ListStartReply().selfRegister( this );
+ new LUserClientReply().selfRegister( this );
+ new LUserMeReply().selfRegister( this );
+ new LUserOpReply().selfRegister( this );
+ new ModeReply().selfRegister( this );
+ new NamesEndReply().selfRegister( this );
+ new NamesReply().selfRegister( this );
+ new NowAwayReply().selfRegister( this );
+ new TopicInfoReply().selfRegister( this );
+ new UnAwayReply().selfRegister( this );
+ new WhoisChannelsReply().selfRegister( this );
+ new WhoisEndReply().selfRegister( this );
+ new WhoisIdleReply().selfRegister( this );
+ new WhoisServerReply().selfRegister( this );
+ new WhoisUserReply().selfRegister( this );
+ }
+
+ public void addCommand( String ident, InCommand command )
+ {
+ commands.put( ident, command );
+ }
+
+ public InCommand getCommand( String ident )
+ {
+ return commands.get( ident );
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/CommandSender.java b/EssentialsUpdate/src/f00f/net/irc/martyr/CommandSender.java
new file mode 100644
index 000000000..a5a748e86
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/CommandSender.java
@@ -0,0 +1,14 @@
+package f00f.net.irc.martyr;
+
+/**
+ * A CommandSender can accept an OutCommand and do something with it
+ * (such as send it to the server, or send it on to another
+ * CommandSender). The idea is to create a chain of CommandSenders,
+ * with the last object in the chain the default CommandSender,
+ * created by IRCConnection.
+ * */
+public interface CommandSender
+{
+ CommandSender getNextCommandSender();
+ void sendCommand( OutCommand command );
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/CronManager.java b/EssentialsUpdate/src/f00f/net/irc/martyr/CronManager.java
new file mode 100644
index 000000000..3c5d8b196
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/CronManager.java
@@ -0,0 +1,80 @@
+package f00f.net.irc.martyr;
+
+import java.util.Date;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * @since 0.3.2
+ * */
+public class CronManager
+{
+ private Timer timer;
+
+ public CronManager()
+ {
+ timer = new Timer();
+ }
+
+ /**
+ * @param task TimerTask to schedule
+ * @param time When to schedule task
+ */
+ public void schedule(TimerTask task, Date time)
+ {
+ timer.schedule(task, time);
+ }
+
+ /**
+ * @param task TimerTask to schedule
+ * @param firstTime When to run first
+ * @param period How often to run
+ */
+ public void schedule(TimerTask task, Date firstTime, long period)
+ {
+ timer.schedule(task, firstTime, period);
+ }
+
+ /**
+ * @param task TimerTask to schedule
+ * @param delay How long to wait before running
+ */
+ public void schedule(TimerTask task, long delay)
+ {
+ timer.schedule(task, delay);
+ }
+
+ /**
+ * @param task TimerTask to schedule
+ * @param delay How long to wait before running
+ * @param period How often to run
+ */
+ public void schedule(TimerTask task, long delay, long period)
+ {
+ timer.schedule(task, delay, period);
+ }
+
+ /**
+ * @param task TimerTask to schedule
+ * @param firstTime When first to run
+ * @param period How often to run
+ */
+ public void scheduleAtFixedRate(
+ TimerTask task,
+ Date firstTime,
+ long period)
+ {
+ timer.scheduleAtFixedRate(task, firstTime, period);
+ }
+
+ /**
+ * @param task TimerTask to schedule
+ * @param delay When first to run
+ * @param period How often to run
+ */
+ public void scheduleAtFixedRate(TimerTask task, long delay, long period)
+ {
+ timer.scheduleAtFixedRate(task, delay, period);
+ }
+
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/ForwardObservable.java b/EssentialsUpdate/src/f00f/net/irc/martyr/ForwardObservable.java
new file mode 100644
index 000000000..7f4d8d89a
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/ForwardObservable.java
@@ -0,0 +1,66 @@
+package f00f.net.irc.martyr;
+
+import java.util.Observer;
+import java.util.Observable;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Does notifications in the order they are added.
+ * */
+public class ForwardObservable extends Observable
+{
+ private boolean changed = true;
+ private List<Observer> obs = new LinkedList<Observer>();
+ private final Object localMonitor = new Object();
+
+
+ public void setChanged()
+ {
+ synchronized(localMonitor)
+ {
+ changed = true;
+ }
+ }
+
+ protected void clearChanged()
+ {
+ synchronized(localMonitor)
+ {
+ changed = false;
+ }
+ }
+
+ public void addObserver( Observer o )
+ {
+ synchronized(localMonitor)
+ {
+ obs.add( o );
+ }
+ }
+
+ public void deleteObserver( Observer o )
+ {
+ synchronized(localMonitor)
+ {
+ obs.remove( o );
+ }
+ }
+
+ public void notifyObservers(Object arg)
+ {
+ synchronized(localMonitor)
+ {
+ if (!changed)
+ return;
+ clearChanged();
+
+ for (Observer ob : obs) {
+ ob.update(this, arg);
+ }
+ }
+ }
+
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/GenericAutoService.java b/EssentialsUpdate/src/f00f/net/irc/martyr/GenericAutoService.java
new file mode 100644
index 000000000..0b3546231
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/GenericAutoService.java
@@ -0,0 +1,54 @@
+package f00f.net.irc.martyr;
+
+import java.util.Observable;
+
+/**
+ * Provides a framework for an auto service. Does enable by default.
+ * Splits the 'update' method into two, 'updateState' and 'updateCommand'.
+ * Also provides thread safety on all methods.
+ */
+public abstract class GenericAutoService extends GenericCommandAutoService
+{
+
+protected GenericAutoService( IRCConnection connection )
+{
+ super( connection );
+}
+
+public synchronized void enable()
+{
+ if( enabled )
+ return;
+
+ connection.addStateObserver( this );
+
+ super.enable();
+}
+
+public synchronized void disable()
+{
+ if( !enabled )
+ return;
+
+ connection.removeStateObserver( this );
+
+ super.disable();
+}
+
+public synchronized void update( Observable observer, Object updated )
+{
+ if( !enabled )
+ throw new IllegalStateException("This observer is not enabled." );
+ if( updated instanceof State )
+ updateState( (State)updated );
+ else
+ super.update( observer, updated );
+}
+
+protected abstract void updateState( State state );
+
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/GenericCommandAutoService.java b/EssentialsUpdate/src/f00f/net/irc/martyr/GenericCommandAutoService.java
new file mode 100644
index 000000000..b9c1d5ede
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/GenericCommandAutoService.java
@@ -0,0 +1,79 @@
+package f00f.net.irc.martyr;
+
+import java.util.Observable;
+import java.util.Observer;
+
+/**
+ * Provides a framework for an auto service that operates with
+ * InCommands. Does enable by default. Splits the 'update' method
+ * into two, 'updateState' and 'updateCommand'. Also provides thread
+ * safety on all methods.
+ */
+public abstract class GenericCommandAutoService implements Observer
+{
+
+protected boolean enabled = false;
+protected IRCConnection connection;
+
+protected GenericCommandAutoService( IRCConnection connection )
+{
+ this.connection = connection;
+
+ enable();
+}
+
+public synchronized void enable()
+{
+ if( enabled )
+ return;
+
+ connection.addCommandObserver( this );
+ enabled = true;
+}
+
+public synchronized void disable()
+{
+ if( !enabled )
+ return;
+
+ connection.removeCommandObserver( this );
+ enabled = false;
+}
+
+public synchronized void update( Observable observer, Object updated )
+{
+ if( !enabled )
+ throw new IllegalStateException("This observer is not enabled." );
+ if( updated instanceof State )
+ {
+ throw new IllegalArgumentException("This is not a state observer." );
+ }
+ else if( updated instanceof InCommand )
+ {
+ updateCommand( (InCommand)updated );
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unknown object given to update.");
+ }
+}
+
+protected IRCConnection getConnection()
+{
+ return connection;
+}
+
+protected synchronized boolean isEnabled()
+{
+ return enabled;
+}
+
+protected abstract void updateCommand( InCommand command );
+
+
+// END AutoRegister
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/IRCConnection.java b/EssentialsUpdate/src/f00f/net/irc/martyr/IRCConnection.java
new file mode 100644
index 000000000..159f533cd
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/IRCConnection.java
@@ -0,0 +1,1163 @@
+/*
+ * IRCConnection.java
+ *
+ * Copyright (C) 2000, 2001, 2002, 2003, 2004 Ben Damm
+ *
+ * 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.
+ *
+ * See: http://www.fsf.org/copyleft/lesser.txt
+ */
+
+package f00f.net.irc.martyr;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.LinkedList;
+import java.util.Observer;
+import java.util.StringTokenizer;
+
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.commands.UnknownCommand;
+import f00f.net.irc.martyr.errors.UnknownError;
+import f00f.net.irc.martyr.replies.UnknownReply;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+// TODO:
+//
+// Add synchronous disconnect.
+//
+/**
+ * <p><code>IRCConnection</code> is the core class for Martyr.
+ * <code>IRCConnection</code> manages the socket, giving commands to the server
+ * and passing results to the parse engine. It manages passing information out
+ * to the application via the command listeners and state listeners.
+ * <code>IRCConnection</code> has no IRC intelligence of its own, that is left
+ * up to the classes on the command and state listener lists. A number of
+ * listeners that do various tasks are provided as part of the framework.</p>
+ *
+ * <p><b>Please read this entirely before using the framework.</b> Or
+ * what the heck, try out the example below and see if it works for ya.</p>
+ *
+ * <h2>States and State Listeners</h2>
+ * <p><code>IRCConnection</code> is always in one of three states.
+ * <code>UNCONNECTED</code>, <code>UNREGISTERED</code> or
+ * <code>REGISTERED</code>. It keeps a list of listeners that
+ * would like to know when a state change occurs. When a state change
+ * occurs each listener in the list, in the order they were added, is
+ * notified. If a listener early up on the list causes something to happen
+ * that changes the state before your listener gets notified, you will be
+ * notified of the state change even though the state has changed. You
+ * will be notified again of the new state. That is, state change
+ * notifications will always be in order, but they may not always reflect
+ * the "current" state.</p>
+ *
+ * <h2>Commands and Command Listeners</h2>
+ * <p><code>IRCConnection</code> also keeps a list of listeners for
+ * when a command arrives from the server. When a command arrives, it
+ * is first parsed into an object. That object is then passed around
+ * to all the listeners, again, in order. Commands can be received
+ * and the socket closed before the commands are actually send to the
+ * listeners, so beware that even though you receive a command, you
+ * may not always be guaranteed to have an open socket to send a
+ * response back on. A consumer of the command should never modify
+ * the command object. If you try to send a command to a closed
+ * socket, <code>IRCConnection</code> will silently ignore your
+ * command. Commands should always be received in order by all
+ * listeners, even if a listener higher up in the list sends a
+ * response to the server which elicits a response back from the
+ * server before you've been told of the first command.</p>
+ *
+ * <h2>Connecting and staying connected</h2>
+ * <p>The AutoReconnect class can connect you and will try to stay
+ * connected. Using AutoReconnect to connect the
+ * first time is recommended, use the <code>go(server,port)</code> method once
+ * you are ready to start.</p>
+ *
+ * <h2>Registration On The Network</h2>
+ * <p>The AutoRegister class can register you automatically on the
+ * network. Otherwise, registration is left up to the consumer.
+ * Registration should occur any time the state changes to
+ * <code>UNREGISTERED</code>. The consumer will know this because it
+ * has registered some class as a state observer.
+ * </p>
+ *
+ * <h2>Auto Response</h2>
+ * <p>Some commands, such as Ping require an automatic response.
+ * Commands that fall into this category can be handled by the
+ * <code>AutoResponder</code> class. For a list of what commands
+ * <code>AutoResponder</code> auto responds to, see the source.</p>
+ *
+ * <h2>Joining and Staying Joined</h2>
+ * <p>You can use the <code>AutoJoin</code> class to join a channel
+ * and stay there. <code>AutoJoin</code> will try to re-join if
+ * kicked or if the connection is lost and the server re-connects.
+ * <code>AutoJoin</code> can be used any time a join is desired. If
+ * the server is not connected, it will wait until the server
+ * connects. If the server is connected, it will try to join right
+ * away.</p>
+ *
+ * <h2>Example Usage</h2>
+ * <p>You will probably want to at least use the
+ * <code>AutoRegister</code> and <code>AutoResponder</code> classes.
+ * Example:</p>
+ *
+ * <p>Note that in the example, the first line is optional.
+ * <code>IRCConnection</code> can be called with its default
+ * constructor. See note below about why this is done.
+ * <code>IRCConnection</code> will instantiate its own
+ * <code>ClientState</code> object if you do not provide one.</p>
+ *
+ * <pre>
+ * ClientState clientState = new MyAppClientState();
+ * IRCConnection connection = new IRCConnection( clientState );
+ *
+ * // AutoRegister and AutoResponder both add themselves to the
+ * // appropriate observerables. Both will remove themselves with the
+ * // disable() method.
+ *
+ * AutoRegister autoReg
+ * = new AutoRegister( "repp", "bdamm", "Ben Damm", connection );
+ * AutoReconnect autoRecon = new AutoReconnect( connection );
+ * AutoResponder autoRes = new AutoResponder( connection );
+ *
+ * // Very important that the objects above get added before the connect.
+ * // If done otherwise, AutoRegister will throw an
+ * // IllegalStateException, as AutoRegister can't catch the
+ * // state switch to UNREGISTERED from UNCONNECTED.
+ *
+ * autoRecon.go( server, port );
+ * </pre>
+ *
+ * <h2>Client State</h2>
+ * <p>The <code>ClientStateMonitor</code> class tells commands to
+ * change the client state when they are received.
+ * <code>ClientStateMonitor</code> is automatically added to the
+ * command queue before any other command, so that you can be
+ * guaranteed that the <code>ClientState</code> is updated before any
+ * observer sees a command.</p>
+ *
+ * <p>So, how does an application know when a channel has been joined,
+ * a user has joined a channel we are already on, etc? How does the
+ * application get fine-grained access to client state change info?
+ * This is a tricky question, and the current solution is to sublcass
+ * the <code>clientstate.ClientState</code> and
+ * <code>clientstate.Channel</code> classes with your own, overriding
+ * the <code>setXxxxXxxx</code> methods. Each method would call
+ * <code>super.setXxxXxxx</code> and then proceed to change the
+ * application as required.</p>
+ *
+ * <h2>Startup</h2>
+ * <p>IRCConnection starts in the <code>UNCONNECTED</code> state and
+ * makes no attempt to connect until the <code>connect(...)</code>
+ * method is called.</p>
+ *
+ * <p><code>IRCConnection</code> starts a single thread at
+ * construction time. This thread simply waits for events. An event
+ * is a disconnection request or an incoming message. Events are
+ * dealt with by this thread. If connect is called, a second thread
+ * is created to listen for input from the server (InputHandler).
+ *
+ * @see f00f.net.irc.martyr.A_FAQ
+ * @see f00f.net.irc.martyr.clientstate.ClientState
+ * @see f00f.net.irc.martyr.services.AutoRegister
+ * @see f00f.net.irc.martyr.services.AutoResponder
+ * @see f00f.net.irc.martyr.State
+ *
+ */
+/*
+ * Event handling re-org
+ *
+ * - A message is an event
+ * - A disconnect request is an event, placed on the queue?
+ * -- Off I go to do other stuff.
+ */
+public class IRCConnection {
+ static Logger log = Logger.getLogger(IRCConnection.class.getName());
+
+ public IRCConnection()
+ {
+ this( new ClientState() );
+ }
+
+ public IRCConnection( ClientState clientState )
+ {
+ // State observers are notified of state changes.
+ // Command observers are sent a copy of each message that arrives.
+ stateObservers = new StateObserver();
+ commandObservers = new CommandObserver();
+ this.clientState = clientState;
+ stateQueue = new LinkedList<State>();
+
+ commandRegister = new CommandRegister();
+ commandSender = new DefaultCommandSender();
+
+ setState( State.UNCONNECTED );
+
+ new ClientStateMonitor( this );
+
+ localEventQueue = new LinkedList<String>();
+
+ eventThread = new EventThread();
+ eventThread.setDaemon( true );
+ startEventThread();
+ }
+
+ /**
+ * This method exists so that subclasses may perform operations before
+ * the event thread starts, but overriding this method.
+ * */
+ protected void startEventThread()
+ {
+ eventThread.start();
+ }
+
+ /**
+ * In the event you want to stop martyr, call this. This asks the
+ * event thread to finish the current event, then die.
+ * */
+ public void stop()
+ {
+ eventThread.doStop();
+ }
+
+ /**
+ * Performs a standard connection to the server and port. If we are already
+ * connected, this just returns.
+ *
+ * @param server Server to connect to
+ * @param port Port to connect to
+ * @throws IOException if we could not connect
+ */
+ public void connect( String server, int port )
+ throws IOException
+ {
+ synchronized( connectMonitor )
+ {
+ //log.debug("IRCConnection: Connecting to " + server + ":" + port);
+ if( connected )
+ {
+ log.severe("IRCConnection: Connect requested, but we are already connected!");
+ return;
+ }
+
+ connectUnsafe( new Socket( server, port ), server );
+ }
+ }
+
+ /**
+ * This allows the developer to provide a pre-connected socket, ready for use.
+ * This is so that any options that the developer wants to set on the socket
+ * can be set. The server parameter is passed in, rather than using the
+ * customSocket.getInetAddr() because a DNS lookup may be undesirable. Thus,
+ * the canonical server name, whatever that is, should be provided. This is
+ * then passed on to the client state.
+ *
+ * @param customSocket Custom socket that we will connect over
+ * @param server Server to connect to
+ * @throws IOException if we could not connect
+ * @throws IllegalStateException if we are already connected.
+ */
+ public void connect( Socket customSocket, String server )
+ throws IOException, IllegalStateException
+ {
+ synchronized( connectMonitor )
+ {
+ if( connected )
+ {
+ throw new IllegalStateException( "Connect requested, but we are already connected!" );
+ }
+
+ connectUnsafe( customSocket, server );
+ }
+
+ }
+
+ /**
+ * <p>Orders the socket to disconnect. This doesn't actually disconnect, it
+ * merely schedules an event to disconnect. This way, pending incoming
+ * messages may be processed before a disconnect actually occurs.</p>
+ * <p>No errors are possible from the disconnect. If you try to disconnect an
+ * unconnected socket, your disconnect request will be silently ignored.</p>
+ */
+ public void disconnect()
+ {
+ synchronized( eventMonitor )
+ {
+ disconnectPending = true;
+ eventMonitor.notifyAll();
+ }
+ }
+
+ /**
+ * Sets the daemon status on the threads that <code>IRCConnection</code>
+ * creates. Default is true, that is, new InputHandler threads are
+ * daemon threads, although the event thread is always a daemon. The
+ * result is that as long as there is an active connection, the
+ * program will keep running.
+ *
+ * @param daemon Set if we are to be treated like a daemon
+ */
+ public void setDaemon( boolean daemon )
+ {
+ this.daemon = daemon;
+ }
+
+ /**
+ * Signal threads to stop, and wait for them to do so.
+ * @param timeout *2 msec to wait at most for stop.
+ *
+ * */
+ public void shutdown(long timeout)
+ {
+ // Note: UNTESTED!
+ try
+ {
+ // 1) shut down the input thread.
+ synchronized( inputHandlerMonitor )
+ {
+ if( inputHandler != null )
+ {
+ inputHandler.signalShutdown();
+ }
+ synchronized( socketMonitor )
+ {
+ if( socket != null )
+ {
+ try
+ {
+ socket.close();
+ }
+ catch (IOException e)
+ {
+ // surprising?
+ }
+ }
+ }
+ if( inputHandler != null )
+ {
+ inputHandler.join(timeout);
+ }
+ }
+
+ // 2) shut down the event thread.
+ eventThread.shutdown();
+ eventThread.join(timeout);
+ }
+ catch( InterruptedException ie )
+ {
+ // We got interrupted - while waiting for death.
+ // Shame that.
+ }
+ }
+
+ public String toString()
+ {
+ return "IRCConnection";
+ }
+
+ public void addStateObserver( Observer observer )
+ {
+ //log.debug("IRCConnection: Added state observer " + observer);
+ stateObservers.addObserver( observer );
+ }
+
+ public void removeStateObserver( Observer observer )
+ {
+ //log.debug("IRCConnection: Removed state observer " + observer);
+ stateObservers.deleteObserver( observer );
+ }
+
+ public void addCommandObserver( Observer observer )
+ {
+ //log.debug("IRCConnection: Added command observer " + observer);
+ commandObservers.addObserver( observer );
+ }
+
+ public void removeCommandObserver( Observer observer )
+ {
+ //log.debug("IRCConnection: Removed command observer " + observer);
+ commandObservers.deleteObserver( observer );
+ }
+
+
+ public State getState()
+ {
+ return state;
+ }
+
+ public ClientState getClientState()
+ {
+ return clientState;
+ }
+
+ /**
+ * Accepts a command to be sent. Sends the command to the
+ * CommandSender.
+ *
+ * @param command Command we will send
+ * */
+ public void sendCommand( OutCommand command )
+ {
+ commandSender.sendCommand( command );
+ }
+
+ /**
+ * @return the first class in a chain of OutCommandProcessors.
+ * */
+ public CommandSender getCommandSender()
+ {
+ return commandSender;
+ }
+
+ /**
+ * @param sender sets the class that is responsible for sending commands.
+ * */
+ public void setCommandSender( CommandSender sender )
+ {
+ this.commandSender = sender;
+ }
+
+ /**
+ * @return the local address to which the socket is bound.
+ * */
+ public InetAddress getLocalAddress()
+ {
+ return socket.getLocalAddress();
+ }
+
+ public String getRemotehost()
+ {
+ return clientState.getServer();
+ }
+
+ /**
+ * Sets the time in milliseconds we wait after each command is sent.
+ *
+ * @param sleepTime Length of time to sleep between commands
+ * */
+ public void setSendDelay( int sleepTime )
+ {
+ this.sendDelay = sleepTime;
+ }
+
+ /**
+ * @since 0.3.2
+ * @return a class that can schedule timer tasks.
+ * */
+ public CronManager getCronManager()
+ {
+ if( cronManager == null )
+ cronManager = new CronManager();
+ return cronManager;
+ }
+
+ /**
+ * Inserts into the event queue a command that was not directly
+ * received from the server.
+ *
+ * @param fakeCommand Fake command to inject into incoming queue
+ * */
+ public void injectCommand( String fakeCommand )
+ {
+ synchronized( eventMonitor )
+ {
+ localEventQueue.add( fakeCommand );
+ eventMonitor.notifyAll();
+ }
+ }
+
+ // ===== package methods =============================================
+
+ void socketError( IOException ioe )
+ {
+ //log.debug("Socket error called.");
+ //log.debug("IRCConnection: The stack of the exception:", ioe);
+
+ //log.log(Level.SEVERE, "Socket error", ioe);
+ disconnect();
+ }
+
+ /**
+ * Splits a raw IRC command into three parts, the prefix, identifier,
+ * and parameters.
+ * @param wholeString String to be parsed
+ * @return a String array with 3 components, {prefix,ident,params}.
+ * */
+ public static String[] parseRawString( String wholeString )
+ {
+ String prefix = "";
+ String identifier;
+ String params = "";
+
+ StringTokenizer tokens = new StringTokenizer( wholeString, " " );
+
+ if( wholeString.charAt(0) == ':' )
+ {
+ prefix = tokens.nextToken();
+ prefix = prefix.substring( 1, prefix.length() );
+ }
+
+ identifier = tokens.nextToken();
+
+ if( tokens.hasMoreTokens() )
+ {
+ // The rest of the string
+ params = tokens.nextToken("");
+ }
+
+ String[] result = new String[3];
+ result[0] = prefix;
+ result[1] = identifier;
+ result[2] = params;
+
+ return result;
+ }
+
+ /**
+ * Given the three parts of an IRC command, generates an object to
+ * represent that command.
+ *
+ * @param prefix Prefix of command object
+ * @param identifier ID of command
+ * @param params Params of command
+ * @return An InCommand object for the given command object
+ * */
+ protected InCommand getCommandObject( String prefix, String identifier, String params )
+ {
+ InCommand command;
+
+ // Remember that commands are also factories.
+ InCommand commandFactory = commandRegister.getCommand( identifier );
+ if( commandFactory == null )
+ {
+ if( UnknownError.isError( identifier ) )
+ {
+ command = new UnknownError( identifier );
+ log.warning("IRCConnection: Using " + command);
+ }
+ else if( UnknownReply.isReply( identifier ) )
+ {
+ command = new UnknownReply( identifier );
+ //log.warning("IRCConnection: Using " + command);
+ }
+ else
+ {
+ // The identifier doesn't map to a command.
+ log.warning("IRCConnection: Unknown command");
+ command = new UnknownCommand();
+ }
+ }
+ else
+ {
+ command = commandFactory.parse( prefix, identifier, params);
+
+ if( command == null )
+ {
+ log.severe("IRCConnection: CommandFactory[" + commandFactory + "] returned NULL");
+ return null;
+ }
+ //log.debug("IRCConnection: Using " + command);
+ }
+
+ return command;
+ }
+
+
+
+ /**
+ * Executed by the event thread.
+ *
+ * @param wholeString String to be parsed and handled
+ * */
+ void incomingCommand( String wholeString )
+ {
+ //log.info("IRCConnection: RCV = " + wholeString);
+
+ // 1) Parse out the command
+ String cmdBits[];
+
+ try
+ {
+ cmdBits = parseRawString( wholeString );
+ }
+ catch( Exception e )
+ {
+ // So.. we can't process the command.
+ // So we call the error handler.
+ handleUnparsableCommand( wholeString, e );
+ return;
+ }
+
+ String prefix = cmdBits[0];
+ String identifier = cmdBits[1];
+ String params = cmdBits[2];
+
+ // 2) Fetch command from factory
+ InCommand command = getCommandObject( prefix, identifier, params );
+ command.setSourceString( wholeString );
+
+ // Update the state and send out to commandObservers
+ localCommandUpdate( command );
+ }
+
+ protected void handleUnparsableCommand( String wholeString, Exception e )
+ {
+ log.log(Level.SEVERE, "Unable to parse server message.", e );
+ }
+
+ /**
+ * Called only in the event thread.
+ *
+ * @param command Command to update
+ * */
+ private void localCommandUpdate( InCommand command )
+ {
+ // 3) Change the connection state if required
+ // This allows us to change from UNREGISTERED to REGISTERED and
+ // possibly back.
+ State cmdState = command.getState();
+ if( cmdState != State.UNKNOWN && cmdState != getState() )
+ setState( cmdState );
+
+ // TODO: Bug here?
+
+ // 4) Notify command observers
+ try
+ {
+ commandObservers.setChanged();
+ commandObservers.notifyObservers( command );
+ }
+ catch( Throwable e )
+ {
+ log.log(Level.SEVERE, "IRCConnection: Command notify failed.", e);
+ }
+
+ }
+
+ // ===== private variables ===========================================
+
+ /** Object used to send commands. */
+ private CommandSender commandSender;
+
+ private CronManager cronManager;
+
+ /** State of the session. */
+ private State state;
+
+ /**
+ * Client state (our name, nick, groups, etc). Stored here mainly
+ * because there isn't anywhere else to stick it.
+ */
+ private ClientState clientState;
+
+ /**
+ * Maintains a list of classes observing the state and notifies them
+ * when it changes.
+ */
+ private StateObserver stateObservers;
+
+ /**
+ * Maintains a list of classes observing commands when they come in.
+ */
+ private CommandObserver commandObservers;
+
+ /**
+ * The actual socket used for communication.
+ */
+ private Socket socket;
+
+ /**
+ * Monitor access to socket.
+ * */
+ private final Object socketMonitor = new Object();
+
+ /**
+ * We want to prevent connecting and disconnecting at the same time.
+ */
+ private final Object connectMonitor = new Object();
+
+ /**
+ * This object should be notified if we want the main thread to check for
+ * events. An event is either an incoming message or a disconnect request.
+ * Sending commands to the server is synchronized by the eventMonitor.
+ */
+ private final Object eventMonitor = new Object();
+
+ /**
+ * This tells the processEvents() method to check if we should disconnect
+ * after processing all incoming messages.
+ */
+ // Protected by:
+ // inputHandlerMonitor
+ // eventMonitor
+ // connectMonitor
+ private boolean disconnectPending = false;
+
+ /**
+ * The writer to use for output.
+ */
+ private BufferedWriter socketWriter;
+
+ /**
+ * Command register, contains a list of commands that can be received
+ * by the server and have matching Command objects.
+ */
+ private CommandRegister commandRegister;
+
+ /**
+ * Maintains a handle on the input handler.
+ */
+ private InputHandler inputHandler;
+
+ /**
+ * Access control for the input handler.
+ */
+ private final Object inputHandlerMonitor = new Object();
+
+ /**
+ * State queue keeps a queue of requests to switch state.
+ */
+ private LinkedList<State> stateQueue;
+
+ /**
+ * localEventQueue allows events not received from the server to be
+ * processed.
+ * */
+ private LinkedList<String> localEventQueue;
+
+ /**
+ * Setting state prevents setState from recursing in an uncontrolled
+ * manner.
+ */
+ private boolean settingState = false;
+
+ /**
+ * Event thread waits for events and executes them.
+ */
+ private EventThread eventThread;
+
+ /**
+ * Determines the time to sleep every time we send a message. We do this
+ * so that the server doesn't boot us for flooding.
+ */
+ private int sendDelay = 300;
+
+ /**
+ * connected just keeps track of whether we are connected or not.
+ */
+ private boolean connected = false;
+
+ /**
+ * Are we set to be a daemon thread?
+ */
+ private boolean daemon = false;
+
+ // ===== private methods =============================================
+
+ /**
+ * Unsafe, because this method can only be called by a method that has a lock
+ * on connectMonitor.
+ *
+ * @param socket Socket to connect over
+ * @param server Server to connect to
+ * @throws IOException if connection fails
+ */
+ private void connectUnsafe( Socket socket, String server )
+ throws IOException
+ {
+ synchronized(socketMonitor)
+ {
+ this.socket = socket;
+ }
+
+ socketWriter =
+ new BufferedWriter( new OutputStreamWriter( socket.getOutputStream() ) );
+
+ /**
+ * The reader to use for input. Managed by the InputHandler.
+ */
+ BufferedReader socketReader =
+ new BufferedReader( new InputStreamReader( socket.getInputStream() ) );
+
+ // A simple thread that waits for input from the server and passes
+ // it off to the IRCConnection class.
+ //if( inputHandler != null )
+ //{
+ // log.fatal("IRCConnection: Non-null input handler on connect!!");
+ // return;
+ //}
+
+ synchronized( inputHandlerMonitor )
+ {
+ // Pending events get processed after a disconnect call, and there
+ // shouldn't be any events generated while disconnected, so it makes
+ // sense to test for this condition.
+ if( inputHandler != null && inputHandler.pendingMessages() )
+ {
+ log.severe("IRCConnection: Tried to connect, but there are pending messages!");
+ return;
+ }
+
+ if( inputHandler != null && inputHandler.isAlive() )
+ {
+ log.severe("IRCConnection: Tried to connect, but the input handler is still alive!");
+ return;
+ }
+
+ clientState.setServer( server );
+ clientState.setPort( socket.getPort() );
+
+ connected = true;
+
+ inputHandler = new InputHandler( socketReader, this, eventMonitor );
+ inputHandler.setDaemon( daemon );
+ inputHandler.start();
+ }
+ setState( State.UNREGISTERED );
+ }
+
+ private class EventThread extends Thread
+ {
+ private boolean doShutdown = false;
+
+ public EventThread()
+ {
+ super("EventThread");
+ }
+
+ public void run()
+ {
+ handleEvents();
+ }
+
+ public void shutdown()
+ {
+ synchronized(eventMonitor)
+ {
+ this.doShutdown = true;
+ eventMonitor.notifyAll();
+ }
+ }
+
+ private void handleEvents()
+ {
+ try
+ {
+ while( true )
+ {
+ // Process all events in the event queue.
+ //log.debug("IRCConnection: Processing events");
+ while( processEvents() ) { }
+
+ // We can't process events while synchronized on the
+ // eventMonitor because we may end up in deadlock.
+ synchronized( eventMonitor )
+ {
+ if( !doShutdown && !pendingEvents() )
+ {
+ eventMonitor.wait();
+ }
+
+ if( doShutdown )
+ {
+ return;
+ }
+ }
+ }
+ }
+ catch( InterruptedException ie )
+ {
+ log.log(Level.WARNING, "Interrupted while handling events.", ie );
+ // And we do what?
+ // We die, that's what we do.
+ }
+ }
+
+ public void doStop()
+ {
+ shutdown();
+ }
+
+ public String toString()
+ {
+ return "EventThread";
+ }
+ }
+
+ /**
+ * This method synchronizes on the inputHandlerMonitor. Note that if
+ * additional event types are processed, they also need to be added to
+ * pendingEvents().
+ * @return true if events were processed, false if there were no events to
+ * process.
+ */
+ private boolean processEvents()
+ {
+ boolean events = false;
+
+ // the inputHandlerMonitor here serves two purposes: To protect
+ // from inputHandler changes and to ensure only one thread is
+ // operating in processEvents.
+ //
+ // Perhaps a different monitor should be used?
+ synchronized( inputHandlerMonitor )
+ {
+ while( inputHandler != null && inputHandler.pendingMessages() )
+ {
+ String msg = inputHandler.getMessage();
+ incomingCommand( msg );
+ events = true;
+ }
+
+ while( localEventQueue != null && !localEventQueue.isEmpty() )
+ {
+ String msg = localEventQueue.removeFirst();
+ incomingCommand( msg );
+ events = true;
+ }
+
+ if( disconnectPending )
+ {
+ //log.debug("IRCConnection: Process events: Disconnect pending.");
+ doDisconnect();
+ events = true;
+ }
+ }
+
+ return events;
+ }
+
+ /**
+ * Does no synchronization on its own. This does not synchronize on
+ * any of the IRCConnection monitors or objects and returns after making a
+ * minimum of method calls.
+ * @return true if there are pending events that need processing.
+ */
+ private boolean pendingEvents()
+ {
+ if( inputHandler != null && inputHandler.pendingMessages() )
+ return true;
+ if( disconnectPending )
+ return true;
+ if( localEventQueue != null && !localEventQueue.isEmpty() )
+ return true;
+
+ return false;
+ }
+
+ // Synchronized by inputHandlerMonitor, called only from processEvents.
+ private void doDisconnect()
+ {
+ synchronized( connectMonitor )
+ {
+ disconnectPending = false;
+
+ if( !connected )
+ {
+ return;
+ }
+ connected = false;
+
+ try
+ {
+ final long startTime = System.currentTimeMillis();
+ final long sleepTime = 1000;
+ final long stopTime = startTime + sleepTime;
+ //log.debug("IRCConnection: Sleeping for a bit ("
+ // + sleepTime + ")..");
+ // Slow things down a bit so the server doesn't kill us
+ // Also, we want to give a second to let any pending messages
+ // get processed and any pending disconnect() calls to be made.
+ // It is important that we use wait instead of sleep!
+ while( stopTime - System.currentTimeMillis() > 0 )
+ {
+ connectMonitor.wait( stopTime - System.currentTimeMillis() );
+ }
+ }
+ catch( InterruptedException ie )
+ {
+ // Ignore
+ }
+
+ //log.debug("IRCConnection: Stopping input handler.");
+ // Deprecated?
+ // inputHandler.stop();
+ // inputHandler = null;
+
+ //log.debug("IRCConnection: Closing socket.");
+ try
+ {
+ socket.close();
+ }
+ catch( IOException ioe )
+ {
+ // And we are supposed to do what?
+ // This probably means we've called disconnect on a closed
+ // socket.
+ handleSocketCloseException( ioe );
+ return;
+ }
+ finally
+ {
+ connected = false;
+ }
+ }
+
+ // The input handler should die, because we closed the socket.
+ // We'll wait for it to die.
+ synchronized( inputHandlerMonitor )
+ {
+ //log.debug("IRCConnection: Waiting for the input handler to die..");
+ try
+ {
+ // log.debug("IRCConnection: Stack:");
+
+ if( inputHandler.isAlive() )
+ inputHandler.join();
+ else
+ {
+ //log.debug("IRCConnection: No waiting required, input hander is already dead.");
+ }
+ }
+ catch( InterruptedException ie )
+ {
+ //log.debug("IRCConnection: Error in join(): " + ie);
+ }
+ //log.debug("IRCConnection: Done waiting for the input handler to die.");
+ }
+
+
+ // There may be pending messages that we should process before we
+ // actually notify all the state listeners.
+ processEvents();
+
+ // It is important that the state be switched last. One of the
+ // state listeners may try to re-connect us.
+ setState( State.UNCONNECTED );
+
+ }
+
+ protected void handleSocketCloseException( IOException ioe )
+ {
+ log.log(Level.WARNING, "Error closing socket.", ioe );
+ }
+
+ /**
+ * Signals to trigger a state change. Won't actually send a state change
+ * until a previous attempt at changing the state finishes. This is
+ * important if one of the state listeners affects the state (ie tries to
+ * reconnect if we disconnect, etc).
+ *
+ * @param newState New state to set connection to.
+ */
+ private void setState( State newState )
+ {
+ if( settingState )
+ {
+ // We are already setting the state. We want to complete changing
+ // to one state before changing to another, so that we don't have
+ // out-of-order state change signals.
+ stateQueue.addLast( newState );
+ return;
+ }
+
+ settingState = true;
+
+ if( state == newState )
+ return;
+
+ while( true )
+ {
+ state = newState;
+
+ //log.debug("IRCConnection: State switch: " + state);
+
+ try
+ {
+ stateObservers.setChanged();
+ stateObservers.notifyObservers( newState );
+ }
+ catch( Throwable e )
+ {
+ log.log(Level.SEVERE, "IRCConnection: State update failed.", e);
+ }
+
+ if( stateQueue.isEmpty() )
+ break;
+ newState = stateQueue.removeFirst();
+ }
+
+ settingState = false;
+ }
+
+ private class DefaultCommandSender implements CommandSender
+ {
+ public CommandSender getNextCommandSender()
+ {
+ return null;
+ }
+
+ public void sendCommand( OutCommand oc )
+ {
+ finalSendCommand( oc.render() );
+ }
+ }
+
+ /**
+ * Sends the command down the socket, with the required 'CRLF' on the
+ * end. Waits for a little bit after sending the command so that we don't
+ * accidentally flood the server.
+ *
+ * @param str String to send
+ */
+ private void finalSendCommand( String str )
+ {
+ try
+ {
+ synchronized( eventMonitor )
+ {
+ //log.info("IRCConnection: SEND= " + str);
+
+ if( disconnectPending )
+ {
+ //log.debug("IRCConnection: Send cancelled, disconnect pending.");
+ return;
+ }
+
+ socketWriter.write( str + "\r\n" );
+ socketWriter.flush();
+
+ try
+ {
+ // Slow things down a bit so the server doesn't kill us
+ // We do this after the send so that if the send fails the
+ // exception is handled right away.
+ Thread.sleep( sendDelay );
+ }
+ catch( InterruptedException ie )
+ {
+ // Ignore
+ }
+ }
+ }
+ catch( IOException ioe )
+ {
+ socketError( ioe );
+ }
+ }
+ // ----- END IRCConnection -------------------------------------------
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/InCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/InCommand.java
new file mode 100644
index 000000000..a1564d9ec
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/InCommand.java
@@ -0,0 +1,99 @@
+package f00f.net.irc.martyr;
+
+import java.util.Iterator;
+
+import f00f.net.irc.martyr.clientstate.ClientState;
+
+/**
+ * Defines commands that come from the server. Errors and replies are
+ * incoming commands.
+ *
+ * @see f00f.net.irc.martyr.errors.GenericError
+ * @see f00f.net.irc.martyr.replies.GenericReply
+ */
+public interface InCommand extends Command
+{
+
+ /**
+ * Some commands, when received by the server, can only occur in one
+ * state. Thus, when this command is received, the protocol should
+ * assume that it is in that state, and a state change may be
+ * triggered. A command can use the 'unknown' state to indicate it
+ * can be received in any state (for example, ping).
+ *
+ * @return State associated with command
+ */
+ State getState();
+
+ /**
+ * Every incoming command should know how to register itself with the
+ * command register.
+ *
+ * @param commandRegister Command register we want to register with
+ */
+ void selfRegister( CommandRegister commandRegister );
+
+ /**
+ * Parses a string and produces a formed command object, if it can.
+ * Should return null if it cannot form the command object. The
+ * identifier is usually ignored, except in the special case where
+ * commands can be identified by multiple identifiers. In that case,
+ * the behaviour of the command may change in sublte ways.
+ *
+ * @param prefix Prefix of the command
+ * @param identifier ID of the command
+ * @param params Parameters of the command
+ * @return InCommand instance for parsed command
+ */
+ InCommand parse( String prefix, String identifier, String params );
+
+ /**
+ * Gives the command a copy of the raw string from the server. Called
+ * by IRCConnection after the command is parsed.
+ *
+ * @param str Sets the source string to be parsed
+ */
+ void setSourceString( String str );
+
+ /**
+ * Allows a third party to receive a copy of the raw string.
+ *
+ * @return The original source string from the server
+ */
+ String getSourceString();
+
+ /**
+ * Asks the command to ensure that information it knows about the
+ * state the server thinks the client is in matches what we have.
+ * Returns true if state changes were made.
+ *
+ * @param state Client state to be updated
+ * @return True or false if changes were made
+ */
+ boolean updateClientState( ClientState state );
+
+
+ /**
+ * Returns an iterator of String objects over the attribute names
+ * for this command. Warning: Still new, support for this is not
+ * yet widespread. Should return all possible attribute keys, not just
+ * those that have a value in the current context.
+ *
+ * @return Iterator of attribute keys
+ */
+ Iterator getAttributeKeys();
+
+ /**
+ * Returns the attribute, or null if the attribute does not exist,
+ * or is not defined.
+ *
+ * @param key Attribute to get value of
+ * @return Attribute value or null if attribute doesn't exist
+ */
+ String getAttribute( String key );
+
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/InputHandler.java b/EssentialsUpdate/src/f00f/net/irc/martyr/InputHandler.java
new file mode 100644
index 000000000..a3830afa9
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/InputHandler.java
@@ -0,0 +1,130 @@
+package f00f.net.irc.martyr;
+
+import java.util.LinkedList;
+import java.io.BufferedReader;
+import java.io.IOException;
+
+/**
+ * A simple class to help manage input from the stream.
+ */
+public class InputHandler extends Thread
+{
+ //static Logger log = Logger.getLogger(InputHandler.class);
+
+ private BufferedReader reader;
+ private IRCConnection connection;
+ private final LinkedList<String> messages;
+
+ private final Object eventMonitor;
+
+ private static int serialGen = 0;
+ private int serialNumber = serialGen++;
+ private boolean doShutdown = false;
+
+ public InputHandler( BufferedReader reader,
+ IRCConnection connection,
+ Object eventMonitor )
+ {
+
+ super("InputHandler");
+ this.reader = reader;
+ this.connection = connection;
+ messages = new LinkedList<String>();
+ this.eventMonitor = eventMonitor;
+
+ //log.debug("IRCConnection: New");
+ }
+
+ /**
+ * Set the shutdown flag, so that after next read, or on any error, the thread will just exit.
+ */
+ public void signalShutdown()
+ {
+ synchronized(this)
+ {
+ doShutdown = true;
+ }
+ }
+
+ /**
+ * @return true if there are messages waiting to be processed.
+ */
+ public boolean pendingMessages()
+ {
+ synchronized( messages )
+ {
+ return ! messages.isEmpty();
+ }
+ }
+
+ /**
+ * Gets the message at the top of the message queue and removes it from the
+ * message queue.
+ *
+ * @return Message from top of list.
+ */
+ public String getMessage()
+ {
+ synchronized( messages )
+ {
+ return messages.removeFirst();
+ }
+ }
+
+ /**
+ * Waits for input from the server. When input arrives, it is added to a
+ * queue and eventMonitor.notifyAll() is called.
+ */
+ public void run()
+ {
+ //log.debug("IRCConnection: Running");
+ try{
+
+ String str;
+ while( true )
+ {
+ synchronized(this)
+ {
+ if( doShutdown )
+ {
+ return;
+ }
+ }
+ str = reader.readLine();
+ if( str == null )
+ {
+ connection.socketError( new IOException( "Socket disconnected" ) );
+ return;
+ }
+ synchronized( messages )
+ {
+ messages.addLast( str );
+ }
+ synchronized( eventMonitor )
+ {
+ eventMonitor.notifyAll();
+ }
+ }
+ }
+ catch( IOException ioe )
+ {
+ if( doShutdown )
+ {
+ return;
+ }
+ connection.socketError( ioe );
+ }
+ finally
+ {
+ //log.debug("IRCConnection: Input handler has DIED!");
+ }
+ }
+
+ public String toString()
+ {
+ return "InputHandler[" + serialNumber + "]";
+ }
+
+ // ----- END InputHandler --------------------------------------------
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/Mode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/Mode.java
new file mode 100644
index 000000000..5cc39e1a9
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/Mode.java
@@ -0,0 +1,103 @@
+package f00f.net.irc.martyr;
+
+/**
+ * Any class which is to represent a mode must implement this
+ * interface. They must also implement equals(...) so that if the
+ * parameter for either mode is null they are equal based on the
+ * character, and if both parameters are not null, base the equal
+ * on the character and the parameters being equal.
+ */
+public interface Mode
+{
+ /**
+ * A Mode can be constructed and asked to make copies of itself.
+ *
+ * @return New Mode instance
+ */
+ Mode newInstance();
+
+ /**
+ * The character that represents this mode (ie o for operator)
+ *
+ * @return Character representation of mode
+ */
+ char getChar();
+
+ /**
+ * Should return true if this mode requires a parameter.
+ *
+ * @return True or false if a param is required for mode
+ */
+ boolean requiresParam();
+
+ /**
+ * This mode should be recorded in the list of channel modes. This
+ * would NOT include such things as operator status, as it is recored
+ * with the Member object.
+ *
+ * @return True or false of the mode should be recorded in the list of channels
+ */
+ boolean recordInChannel();
+
+ /**
+ * Determines if there can be multiple versions of this mode in
+ * the channel.
+ *
+ * @return True or false if only one instance of mode can exist per channel
+ */
+ boolean onePerChannel();
+
+ /**
+ * Returns the parameter that was set with setParam(...)
+ *
+ * @return Parameter that was set previously
+ */
+ String getParam();
+
+ /**
+ * Sets the parameter that can be retrieved with getParam()
+ *
+ * @param str Parameter to set on mode
+ */
+ void setParam( String str );
+
+ /**
+ * Sets the sign of the operation. Must be positive (granting),
+ * negative (revoking) or nosign (neutral operation).
+ *
+ * @param sign Sign (+/-) of the mode
+ */
+ void setSign( Sign sign );
+
+ /**
+ * @return the sign of this mode.
+ */
+ Sign getSign();
+
+ /**
+ * Finally, the Sign enumeration.
+ */
+ public class Sign
+ {
+ public static final Sign POSITIVE = new Sign( "positive" );
+ public static final Sign NEGATIVE = new Sign( "negative" );
+ public static final Sign NOSIGN = new Sign( "nosign" );
+
+ private String name;
+ private Sign( String name )
+ {
+ this.name = name;
+ }
+
+ public String toString()
+ {
+ return name;
+ }
+ }
+
+}
+
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/OutCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/OutCommand.java
new file mode 100644
index 000000000..9e25c9ce9
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/OutCommand.java
@@ -0,0 +1,24 @@
+package f00f.net.irc.martyr;
+
+/**
+ * Defines an outgoing command. Outgoing commands are very simple
+ * because all they need to do is be rendered. Outgoing commands do
+ * not change our state.
+ */
+public interface OutCommand extends Command
+{
+
+ /**
+ * Forms a string appropriate to send to the server, if required.
+ * Some commands will have no such string, as they are received and not
+ * sent. The string returned is sent to the server verbatim.
+ *
+ * @return Rendered string
+ */
+ String render();
+
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/State.java b/EssentialsUpdate/src/f00f/net/irc/martyr/State.java
new file mode 100644
index 000000000..e1e60ce9d
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/State.java
@@ -0,0 +1,31 @@
+package f00f.net.irc.martyr;
+
+/**
+ * A simple container for state constants. The state constants here
+ * are used to specify what state the protocol is in. The State
+ * object is both the state representitive and the state container.
+ * This was done so that state could be typesafe and valuesafe.
+ *
+ */
+public class State
+{
+
+ public static final State UNCONNECTED = new State("unconnected");
+ public static final State UNREGISTERED = new State("unregistered");
+ public static final State REGISTERED = new State("registered");
+ public static final State UNKNOWN = new State("unknown/any");
+
+ private String stateName;
+
+ private State( String str )
+ {
+ stateName = str;
+ }
+
+ public String toString()
+ {
+ return stateName;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/StateObserver.java b/EssentialsUpdate/src/f00f/net/irc/martyr/StateObserver.java
new file mode 100644
index 000000000..4b7a6693c
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/StateObserver.java
@@ -0,0 +1,8 @@
+package f00f.net.irc.martyr;
+
+/**
+ * Should the state and state observer be one?
+ */
+public class StateObserver extends ForwardObservable
+{
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/TimerTaskCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/TimerTaskCommand.java
new file mode 100644
index 000000000..00ab56f56
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/TimerTaskCommand.java
@@ -0,0 +1,57 @@
+package f00f.net.irc.martyr;
+
+import java.util.TimerTask;
+
+// TODO: BD: Unit test
+// TODO: BD: synchronization semantics?
+
+/**
+ * This class delays sending a command to the IRC connection.
+ *
+ * @author <a href="mailto:martyr@mog.se">Morgan Christiansson</a>
+ */
+public class TimerTaskCommand extends TimerTask
+{
+
+ private IRCConnection _conn;
+ private OutCommand _cmd;
+ public TimerTaskCommand(IRCConnection conn, OutCommand cmd)
+ {
+ _conn = conn;
+ _cmd = cmd;
+ }
+ /* (non-Javadoc)
+ * @see java.util.TimerTask#run()
+ */
+ public synchronized void run()
+ {
+ if( !isScheduled )
+ return;
+
+ _conn.sendCommand(_cmd);
+ isScheduled = false;
+ }
+
+ private boolean isScheduled = true;
+
+ /* (non-Javadoc)
+ * @see java.util.TimerTask#cancel()
+ */
+ public synchronized boolean cancel()
+ {
+ boolean ret = super.cancel();
+ isScheduled = false;
+ return ret;
+ }
+
+ /**
+ * @return true if the command has yet to run or is running, false
+ * otherwise.
+ */
+ public synchronized boolean isScheduled()
+ {
+ return isScheduled;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/clientstate/Channel.java b/EssentialsUpdate/src/f00f/net/irc/martyr/clientstate/Channel.java
new file mode 100644
index 000000000..d1d39c31d
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/clientstate/Channel.java
@@ -0,0 +1,373 @@
+package f00f.net.irc.martyr.clientstate;
+
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Vector;
+
+import f00f.net.irc.martyr.Command;
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.Mode;
+import f00f.net.irc.martyr.modes.channel.OperMode;
+import f00f.net.irc.martyr.modes.channel.VoiceMode;
+import f00f.net.irc.martyr.util.FullNick;
+//import org.apache.log4j.Logger;
+
+/**
+ * Channel is simply a repository for information about a channel.
+ * Contains channel name, topic, who created the topic, when the topic
+ * was created, who is in the channel, mode, etc.
+ *
+ * <p>If a user of the framework wishes to use their own Member object
+ * (to trap events like setting/removing ops), then subclass
+ * Channel and add a method as follows.</p>
+ *
+ * <pre>
+ * public Member makeMember( String member )
+ * {
+ * return MyMemberSubClass( member ) );
+ * }
+ * </pre>
+ *
+ * <p>Each of the methods in Channel that need to create a Member
+ * object (many are just temporary members, for the enhanced 'equals')
+ * calls makeMember instead of new Member(...). That is why this
+ * version of addMember is protected, so that a part of the framework
+ * won't do something silly like:</p>
+ *
+ * <pre>
+ * ...
+ * channel.addMember( new Member( member ) );
+ * ...
+ * </pre>
+ */
+public class Channel
+{
+
+ //static Logger log = Logger.getLogger(Channel.class);
+
+ private String name = null;
+ private String topic = null;
+ private String topicAuthor = null;
+ private Date topicDate = null;
+ private Date creationDate = null;
+
+ private List<Mode> modes = null;
+
+ /**
+ * Hopefully we can replace this with a more useful data structure.
+ * This is a vector of Member objects.
+ */
+ private Vector<Member> members;
+
+ public Channel( String chanName )
+ {
+ this.name = chanName;
+ members = new Vector<Member>();
+ modes = new LinkedList<Mode>();
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ protected void addMember( Member member )
+ {
+ members.addElement( member );
+ }
+
+ /**
+ * Adds the member to the channel.
+ *
+ * @param member Member to add to the channel
+ * @param why Command that caused the member to be added
+ * @deprecated Use <code>addMember( String, InCommand )</code>
+ * instead.
+ */
+ public void addMember( String member, Command why )
+ {
+ addMember( makeMember( member ) );
+ }
+
+ /**
+ * Adds the member to the channel.
+ *
+ * @param member Nick to add to the channel
+ * @param why Command that caused the member to be added
+ */
+ public void addMember( String member, InCommand why )
+ {
+ addMember( makeMember( member ) );
+ }
+
+
+ /**
+ * @param nick Nick to add to the channel
+ * @param why Command that caused the member to be added
+ * @deprecated Use <code>addMember( FullNick, InCommand )</code> intead.
+ */
+ public void addMember( FullNick nick, Command why )
+ {
+ addMember( makeMember( nick.getNick() ));
+ }
+
+ /**
+ * @param nick Nick to add to the channel
+ * @param why Command that caused the member to be added
+ * Adds the member to the channel. Just calls nick.getNick().
+ */
+ public void addMember( FullNick nick, InCommand why )
+ {
+ addMember( nick.getNick(), why );
+ }
+
+ /**
+ * Removes the user from the channel. Ignores leading @ or + symbols.
+ * This is the cononical version of removeMember.
+ * @param member Nick of the person leaving.
+ * @param why Command issed that caused this action.
+ */
+ public void removeMember( String member, InCommand why )
+ {
+ removeMember( makeMember( member ) );
+ }
+
+ /**
+ * @param member Nick to remove from channel
+ * @param why Command that caused removal
+ * @deprecated Use <code>removeMember( String, InCommand ) instead.</code>
+ * */
+ public void removeMember( String member, Command why )
+ {
+ removeMember( makeMember( member ) );
+ }
+
+ /**
+ * @param member Member to remove from channel
+ * @param why Command that caused removal
+ * @deprecated Use <code>removeMember( FullNick, InCommand ) instead.</code>
+ * */
+ public void removeMember( FullNick member, Command why )
+ {
+ removeMember( member, (InCommand)why );
+ }
+
+ /**
+ * Simply a wrapper to allow FullNicks to be used. Calls the string
+ * version of removeMember with nick.getNick().
+ *
+ * @param nick Nick to remove from channel
+ * @param why Command that caused removal
+ */
+ public void removeMember( FullNick nick, InCommand why )
+ {
+ removeMember( nick.getNick(), why );
+ }
+
+ protected void removeMember( Member compareTo )
+ {
+ for( int i = 0; i < members.size(); ++i )
+ {
+ if( (members.elementAt( i )).equals( compareTo ) )
+ {
+ members.removeElementAt( i );
+ return;
+ }
+ }
+ }
+
+ /**
+ * Informs the channel of a mode change. A mode change might be for the
+ * channel (such as l, n, or t) or for a user on this channel (such as
+ * o).
+ *
+ * @param mode Mode to set on the channel
+ */
+ public void setMode( Mode mode )
+ {
+ // Note that Modes are supposed to be equal if the character is
+ // equal. Thus, we can remove a mode from the set, even though it
+ // is different because its sign or parameters may be different.
+ if( mode.onePerChannel() && modes.contains( mode ) )
+ {
+ modes.remove( mode );
+ }
+
+ if( (mode.getSign() != Mode.Sign.NEGATIVE) && mode.recordInChannel() )
+ {
+ modes.add( mode );
+ }
+
+ if( mode instanceof OperMode )
+ {
+ OperMode oMode = (OperMode)mode;
+ Member member = findMember( oMode.getParam() );
+
+ member.setOps( oMode.getSign() == Mode.Sign.POSITIVE );
+ }
+ else if( mode instanceof VoiceMode )
+ {
+ VoiceMode vMode = (VoiceMode)mode;
+ Member member = findMember( vMode.getParam() );
+
+ member.setVoice( vMode.getSign() == Mode.Sign.POSITIVE );
+ }
+ }
+
+ public Iterator getModes()
+ {
+ return modes.iterator();
+ }
+
+ /**
+ * Returns an enumeration of Member objects, in no particular order.
+ *
+ * @return List of members in the channel
+ */
+ public Enumeration getMembers()
+ {
+ return members.elements();
+ }
+
+ /**
+ * Determines if the nick is in the channel. Nick can be in the form
+ * "@sork" or "+sork" or just "sork", for example.
+ *
+ * @param nick Nick of member to check
+ * @return True or false if the member is in this channel.
+ */
+ public boolean isMemberInChannel( String nick )
+ {
+ return isMemberInChannel( makeMember( nick ) );
+ }
+
+ /**
+ * Determines if the member is in this channel.
+ *
+ * @param member Member to check
+ * @return True or false if the member is in this channel.
+ */
+ protected boolean isMemberInChannel( Member member )
+ {
+ return findMember( member ) != null;
+ }
+
+ /**
+ * Finds the Member object associated with a specific nick. Ignores
+ * prefixed + or @.
+ *
+ * @param nick Nick to check whether it's a member of the channel or not
+ * @return Member object for specified nick, if it exists (null if not)
+ */
+ public Member findMember( String nick )
+ {
+ return findMember( makeMember( nick ) );
+ }
+
+ protected Member findMember( Member member )
+ {
+ Enumeration membersE = getMembers();
+ while( membersE.hasMoreElements() )
+ {
+ Member memberCompare = (Member)membersE.nextElement();
+
+ if( memberCompare.equals( member ) )
+ {
+ return memberCompare;
+ }
+ }
+
+ return null;
+ }
+
+ public void setTopic( String topic )
+ {
+ //log.debug(getName()+": Topic: " + topic);
+ this.topic = topic;
+ }
+
+ public String getTopic()
+ {
+ return topic;
+ }
+
+ public Date getTopicDate()
+ {
+ return topicDate;
+ }
+
+ public void setTopicDate( Date date )
+ {
+ //log.debug(getName()+": Topic date: " + date);
+ this.topicDate = date;
+ }
+
+ public Date getCreationDate()
+ {
+ return creationDate;
+ }
+
+ public void setCreationDate( Date date )
+ {
+ //log.debug(getName()+": Creation date: " + date);
+ this.creationDate = date;
+ }
+
+ public String getTopicAuthor()
+ {
+ return topicAuthor;
+ }
+
+ public void setTopicAuthor( String author )
+ {
+ //log.debug(getName()+": Topic by: " + author);
+ this.topicAuthor = author;
+ }
+
+ /**
+ * To use a customized Member class, override this.
+ *
+ * @param nick Nickname to create a member object for
+ * @return Member object for nick
+ */
+ protected Member makeMember( String nick )
+ {
+ return new Member( nick );
+ }
+
+
+ /**
+ * Determines if the string represents a channel name or not.
+ *
+ * @param str String to test if it's a channel or not
+ * @return True or false if a string looks like a channel
+ */
+ public static boolean isChannel( String str )
+ {
+ return str.charAt(0) == '#' || str.charAt(0) == '!' || str.charAt(0) == '&';
+ }
+
+ /**
+ * Compares the two channel names for equality. Returns false if
+ * either are null.
+ *
+ * @param one Left side of comparison
+ * @param two Right side of comparison
+ * @return True or false whether two channels are equal, false of either are null/
+ */
+ public static boolean areEqual( String one, String two )
+ {
+ if( one == null || two == null )
+ {
+ return false;
+ }
+
+ return one.equalsIgnoreCase( two );
+ }
+}
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/clientstate/ClientState.java b/EssentialsUpdate/src/f00f/net/irc/martyr/clientstate/ClientState.java
new file mode 100644
index 000000000..c508463d9
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/clientstate/ClientState.java
@@ -0,0 +1,193 @@
+/*
+ * Original version: Ben Damm <bdamm@dammfine.com>
+ * Changes by: mog
+ * - Added isOnChannel
+ *
+ */
+package f00f.net.irc.martyr.clientstate;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import f00f.net.irc.martyr.util.FullNick;
+//import org.apache.log4j.Logger;
+
+/**
+ * <p>Maintains a list of client-related facts such as what channels we
+ * are in, who else is in the channels, what our nick is, etc.</p>
+ *
+ * <p>ClientState is a critical part of martyr. To get access to events
+ * that change the client state, the framework user can subclass
+ * ClientState and then pass the subclass to the IRCConnection's
+ * constructor. Then, when a command detects a change in client
+ * state, it will call the corresponding method in the custom
+ * ClientState.</p>
+ *
+ * <p>If a user of the framework wishes to grab client state information
+ * about a channel (when a user joins, when a user leaves, topic
+ * change, etc), the user can do so in a similar manner. Simply
+ * override the 'addChannel(String)' method to instantiate their own
+ * Channel subclass, and call the protected 'addChannel' method. See
+ * the addChannel method for an example.
+ * </p>
+ *
+ */
+public class ClientState
+{
+
+ //static Logger log = Logger.getLogger(ClientState.class);
+
+ private FullNick nick = null;
+ private String user = "";
+ private String name = "";
+ private String pass = null;
+ private String server = "";
+ private int port = -1;
+
+ // Hashtable is threadsafe so we don't have to be.
+ protected Hashtable<String,Channel> channels = new Hashtable<String,Channel>();
+
+ public void setNick( FullNick nick )
+ {
+ if( nick == null )
+ {
+ //log.debug("ClientState: Set nick to null");
+ }
+ else
+ {
+ //log.debug("ClientState: Set nick to \"" + nick + "\"");
+ }
+ this.nick = nick;
+ }
+
+ public FullNick getNick()
+ {
+ return nick;
+ }
+
+ public void setUser( String user )
+ {
+ this.user = user;
+ }
+
+ /**
+ * @return the username that was used to register.
+ * */
+ public String getUser()
+ {
+ return user;
+ }
+
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return the name (any arbitrary string) that was used to register.
+ * */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @return the password that was used to register.
+ */
+ public String getPass()
+ {
+ return pass;
+ }
+
+ public void setPass(String pass)
+ {
+ this.pass = pass;
+ }
+
+ public void setServer( String server )
+ {
+ this.server = server;
+ }
+
+ public String getServer()
+ {
+ return server;
+ }
+
+ public void setPort( int port )
+ {
+ this.port = port;
+ }
+
+ public int getPort()
+ {
+ return port;
+ }
+
+ /**
+ * <p>Adds a channel to the list of channels we know about. If you
+ * want to supply your own Channel object, override this method
+ * with:</p>
+ * <pre>
+ * public void addChannel( String channame )
+ * {
+ * addChannel( new MyChannel( channame ) );
+ * }
+ * </pre>
+ *
+ * @param channame Channel to add to list of channels
+ */
+ public void addChannel( String channame )
+ {
+ addChannel( new Channel( channame ) );
+ }
+
+ protected void addChannel( Channel channel )
+ {
+ //log.debug("ClientState: Channel added: " + channel.getName());
+ channels.put( channel.getName().toLowerCase(), channel );
+ }
+
+ public Channel getChannel( String chanName )
+ {
+ return channels.get( chanName.toLowerCase() );
+ }
+
+ /**
+ * Removes a channel from the state, does nothing if the channel name
+ * is invalid.
+ * Should we throw an exception here?
+ *
+ * @param channel Channel to remove from list
+ */
+ public void removeChannel( String channel )
+ {
+ //log.debug("ClientState: Channel removed: " + channel);
+ channels.remove( channel.toLowerCase() );
+ }
+
+ public boolean isOnChannel( String channel )
+ {
+ for (Enumeration iter = getChannelNames(); iter.hasMoreElements();)
+ {
+ if(channel.equalsIgnoreCase((String) iter.nextElement()))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Enumeration getChannelNames()
+ {
+ return channels.keys();
+ }
+
+ public Enumeration getChannels()
+ {
+ return channels.elements();
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/clientstate/Member.java b/EssentialsUpdate/src/f00f/net/irc/martyr/clientstate/Member.java
new file mode 100644
index 000000000..489f21961
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/clientstate/Member.java
@@ -0,0 +1,114 @@
+package f00f.net.irc.martyr.clientstate;
+
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * <p>This class allows channels to keep track of individual users. Each
+ * user in the channel has a nick and has voice, ops, both, or none.
+ * Note that nicks can change, but the information we have about that
+ * person does not.</p>
+ *
+ * <p>Control over events that happen to this class can be obtained in
+ * a similar fashion to how control for the Channel is taken from
+ * ClientState.</p>
+ */
+public class Member
+{
+
+ private FullNick nick;
+ private boolean hasOpsV = false;
+ private boolean hasVoiceV = false;
+
+ /**
+ * <p>Strips off the leading 'at' or 'plus', sets ops or voice, and
+ * keeps the nick. Calls the methods <code>setVoice(...)</code> and
+ * <code>setOps(...)</code> from the constructor, if those conditions
+ * are true. The nick is set before setVoice or setOps are
+ * called.</p>
+ *
+ * @param nickStr Nick of member
+ */
+ public Member( String nickStr )
+ {
+ char first = nickStr.charAt(0);
+ String shortNick = nickStr.substring(1, nickStr.length() );
+ if( first == '@' )
+ {
+ nick = new FullNick( shortNick );
+ setOps( true );
+ }
+ else if( first == '+' )
+ {
+ nick = new FullNick( shortNick );
+ setVoice( true );
+ }
+ else
+ {
+ nick = new FullNick( nickStr );
+ }
+ }
+
+ /**
+ * Does a nick-wise compare.
+ *
+ * @param member Member to compare against
+ * @return True or false of this member equals the other one
+ */
+ public boolean equals( Member member )
+ {
+ return equals( member.nick );
+ }
+
+ public boolean equals( FullNick fullNick )
+ {
+ return nick.equals( fullNick );
+ }
+
+ public boolean equals( Object o )
+ {
+ if( o instanceof Member )
+ return equals( (Member)o );
+ else if( o instanceof FullNick )
+ return equals( (FullNick)o );
+ else return false;
+ }
+
+ public int hashCode()
+ {
+ return nick.hashCode();
+ }
+
+ public void setOps( boolean ops )
+ {
+ hasOpsV = ops;
+ }
+
+ public void setVoice( boolean voice )
+ {
+ hasVoiceV = voice;
+ }
+
+ public boolean hasOps()
+ {
+ return hasOpsV;
+ }
+
+ public boolean hasVoice()
+ {
+ return hasVoiceV;
+ }
+
+ public void setNick( FullNick nick )
+ {
+ this.nick = nick;
+ }
+
+ public FullNick getNick()
+ {
+ return nick;
+ }
+
+}
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/AbstractCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/AbstractCommand.java
new file mode 100644
index 000000000..a3e3ef2c2
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/AbstractCommand.java
@@ -0,0 +1,32 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.OutCommand;
+
+/**
+ * Defines a generic command. Most commands will simply have to
+ * override the getIrcIdentifier method and implement the parse and
+ * render methods using convenience methods.
+ */
+public abstract class AbstractCommand extends AbstractInCommand implements OutCommand
+{
+
+ /**
+ * Forms a string appropriate to send to the server. All commands can
+ * be sent by the client.
+ */
+ public String render()
+ {
+ // no prefix, since we are sending as a client.
+ return getIrcIdentifier() + " " + renderParams();
+ }
+
+ /**
+ * Renders the parameters of this command.
+ *
+ * @return String of rendered parameters
+ */
+ public abstract String renderParams();
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/AbstractInCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/AbstractInCommand.java
new file mode 100644
index 000000000..97f305241
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/AbstractInCommand.java
@@ -0,0 +1,174 @@
+package f00f.net.irc.martyr.commands;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import f00f.net.irc.martyr.CommandRegister;
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.State;
+import f00f.net.irc.martyr.clientstate.ClientState;
+
+/**
+ * Defines a generic command. Most commands will simply have to
+ * override the getIrcIdentifier method and implement the parse and
+ * render methods using convenience methods.
+ */
+public abstract class AbstractInCommand implements InCommand
+{
+
+ protected Map<String,String> attributes = new HashMap<String,String>();
+
+ protected AbstractInCommand()
+ {
+ }
+
+ protected AbstractInCommand( String[] attributeNames )
+ {
+ for (String attributeName : attributeNames) {
+ attributes.put(attributeName, null);
+ }
+ }
+
+ public String getAttribute( String key )
+ {
+ return attributes.get( key );
+ }
+
+ public Iterator getAttributeKeys()
+ {
+ return Collections.unmodifiableSet( attributes.keySet() ).iterator();
+ }
+
+ protected void setAttribute( String key, String value )
+ {
+ attributes.put( key, value );
+ }
+
+ private String sourceString;
+
+ /**
+ * Some commands, when received by the server, can only occur in one
+ * state. Thus, when this command is received, the protocol should
+ * assume that it is that state. A command can use the 'unknown'
+ * state to indicate it can be received in any state (for example,
+ * ping). Most commands will occur in the REGISTERED state, so for a
+ * few exeptions, commands can leave this alone.
+ */
+ public State getState()
+ {
+ return State.REGISTERED;
+ }
+
+ /**
+ * Every command should know how to register itself (or not) with the
+ * command parsing engine. If a command is available under mutiple
+ * identifiers, then this method can be overridden and the addCommand
+ * method can be called multiple times.
+ */
+ public void selfRegister( CommandRegister commandRegister )
+ {
+ commandRegister.addCommand( getIrcIdentifier(), this );
+ }
+
+ /**
+ * Parses a string and produces a formed command object, if it can.
+ * Should return null if it cannot form the command object.
+ */
+ public abstract InCommand parse( String prefix, String identifier, String params );
+
+ /**
+ * By default, commands do not update the client state.
+ */
+ public boolean updateClientState( ClientState state )
+ {
+ return false;
+ }
+
+ /**
+ * Utility method to make parsing easy. Provides parameter n, where
+ * n=0 is the first parameter. Parses out the : and considers
+ * anything after a : to be one string, the final parameter.
+ *
+ * If the index doesn't exist, returns null. Should it throw
+ * IndexOutOfBoundsException? No, some commands may have optional
+ * fields.
+ *
+ * @param params String with parameters in it
+ * @param num Position number of parameter to be requested
+ * @return Parameter specified by id in params string
+ */
+ public String getParameter( String params, int num )
+ {
+ int colonIndex = params.indexOf( " :" );
+ colonIndex++; // Skip the space, we just needed it to be sure it's really a "rest of line" colon
+ String textParam = null;
+ String spaceParams;
+
+ if( colonIndex < 0 )
+ {
+ spaceParams = params;
+ }
+ else if( colonIndex == 0 )
+ {
+ if( num == 0 )
+ return params.substring( 1, params.length() );
+ else
+ return null;
+ // throw exception?
+ }
+ else
+ {
+ // colon index > 0, so we have at least one parameter before
+ // the final parameter.
+ spaceParams = params.substring( 0, colonIndex ).trim();
+ textParam = params.substring( colonIndex + 1, params.length() );
+ }
+
+ StringTokenizer tokens = new StringTokenizer( spaceParams, " " );
+
+ while( tokens.hasMoreTokens() && num > 0 )
+ {
+ // strip off tokensi
+ --num;
+ tokens.nextToken();
+ }
+
+ if( num == 0 && tokens.hasMoreTokens() )
+ return tokens.nextToken();
+ if( num == 0 && !tokens.hasMoreTokens() )
+ return textParam;
+
+
+ return null;
+ // throw exception?
+ }
+
+ public int getIntParameter( String params, int paramnum, int defaultNum )
+ {
+ try
+ {
+ return Integer.parseInt( getParameter( params, paramnum ) );
+ }
+ catch( NumberFormatException nfe )
+ {
+ return defaultNum;
+ }
+
+ }
+
+ public void setSourceString( String source )
+ {
+ this.sourceString = source;
+ }
+
+ public String getSourceString()
+ {
+ return sourceString;
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/ActionCtcp.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/ActionCtcp.java
new file mode 100644
index 000000000..f599f16b7
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/ActionCtcp.java
@@ -0,0 +1,12 @@
+package f00f.net.irc.martyr.commands;
+
+/**
+ * ActionCtcp allows the application to do a '/me'.
+ */
+public class ActionCtcp extends CtcpMessage
+{
+ public ActionCtcp( String dest, String message )
+ {
+ super( dest, "ACTION " + message );
+ }
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/ChannelModeCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/ChannelModeCommand.java
new file mode 100644
index 000000000..f8305852b
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/ChannelModeCommand.java
@@ -0,0 +1,189 @@
+package f00f.net.irc.martyr.commands;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.Mode;
+import f00f.net.irc.martyr.clientstate.Channel;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.modes.channel.AnonChannelMode;
+import f00f.net.irc.martyr.modes.channel.BanMode;
+import f00f.net.irc.martyr.modes.channel.ExceptionMode;
+import f00f.net.irc.martyr.modes.channel.InviteMaskMode;
+import f00f.net.irc.martyr.modes.channel.InviteOnlyMode;
+import f00f.net.irc.martyr.modes.channel.KeyMode;
+import f00f.net.irc.martyr.modes.channel.LimitMode;
+import f00f.net.irc.martyr.modes.channel.ModeratedMode;
+import f00f.net.irc.martyr.modes.channel.NoExtMsgMode;
+import f00f.net.irc.martyr.modes.channel.OperMode;
+import f00f.net.irc.martyr.modes.channel.PrivateMode;
+import f00f.net.irc.martyr.modes.channel.SecretMode;
+import f00f.net.irc.martyr.modes.channel.TopicLockMode;
+import f00f.net.irc.martyr.modes.channel.VoiceMode;
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * Defines the ChannelMode command. Can be used to send a Channel
+ * mode. For receiving, this defines which channel modes Martyr knows
+ * about and passes them on to the Channel object. Note that the
+ * actual logic of what happens when a mode arrives lies in the
+ * clientstate.Channel object.
+ */
+public class ChannelModeCommand extends ModeCommand
+{
+
+ private String prefix;
+ private String channelName;
+ private FullNick sender;
+
+ private List modes;
+
+ private static HashMap<Character,Mode> modeTypes;
+
+ /**
+ * For receiving a mode command.
+ * @param prefix Currently unused prefix string
+ * @param channelName Channel that the mode change is in reference to
+ * @param params List of params to be parsed
+ */
+ public ChannelModeCommand( String prefix, String channelName,
+ StringTokenizer params )
+ {
+ makeModes();
+
+ this.prefix = prefix;
+ this.channelName = channelName;
+
+ modes = parseModes( modeTypes, params );
+
+ // System.out.println( modes );
+ }
+
+ /**
+ * For sending a mode discovery.
+ *
+ * @param channelName Channel that the mode change is in reference to
+ */
+ public ChannelModeCommand( String channelName )
+ {
+ sender = null;
+ this.channelName = channelName;
+
+ // Empty list, no modes.
+ modes = new LinkedList();
+ }
+
+ public void makeModes()
+ {
+ if( modeTypes == null )
+ {
+ modeTypes = new HashMap<Character,Mode>();
+
+ registerMode( modeTypes, new BanMode() );
+ registerMode( modeTypes, new KeyMode() );
+ registerMode( modeTypes, new OperMode() );
+ registerMode( modeTypes, new VoiceMode() );
+ registerMode( modeTypes, new LimitMode() );
+ // registerMode( modeTypes, new QuietMode() );
+ registerMode( modeTypes, new SecretMode() );
+ registerMode( modeTypes, new PrivateMode() );
+ registerMode( modeTypes, new NoExtMsgMode() );
+ registerMode( modeTypes, new ExceptionMode() );
+ registerMode( modeTypes, new TopicLockMode() );
+ registerMode( modeTypes, new ModeratedMode() );
+ registerMode( modeTypes, new InviteMaskMode() );
+ registerMode( modeTypes, new InviteOnlyMode() );
+ registerMode( modeTypes, new AnonChannelMode() );
+ }
+ }
+
+ /**
+ * Shouldn't be called, as ModeCommand should be responsible for parsing
+ * and creating this class.
+ */
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ throw new IllegalStateException( "Don't call this method!" );
+ }
+
+ public String render()
+ {
+ return "MODE " + channelName + renderParams();
+ }
+
+ public String renderParams()
+ {
+ Iterator modesI = modes.iterator();
+
+ String modes = "";
+ String params = "";
+
+ while( modesI.hasNext() )
+ {
+ Mode mode = (Mode)modesI.next();
+
+ if( mode.getSign() != Mode.Sign.NOSIGN )
+ {
+ modes += (mode.getSign() == Mode.Sign.POSITIVE ? "+" : "-" );
+ }
+ modes += mode.getChar();
+
+ if( mode.getParam() != null )
+ {
+ // Does the parameter list already have params?
+ // If so, stick in a space.
+ if( params.length() > 0 )
+ {
+ params += " ";
+ }
+ params += mode.getParam();
+ }
+ }
+
+ return modes + " " + params;
+ }
+
+ public String getChannel()
+ {
+ return channelName;
+ }
+
+ public FullNick getSender()
+ {
+ return sender;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * Passes the modes on to the clientstate.Channel object.
+ */
+ public boolean updateClientState( ClientState state )
+ {
+ boolean changed = false;
+
+ Iterator modesI = modes.iterator();
+ Channel channel = state.getChannel( channelName );
+
+ while( modesI.hasNext() )
+ {
+ Mode mode = (Mode)modesI.next();
+
+ channel.setMode( mode );
+
+ changed = true;
+ }
+
+ return changed;
+ }
+
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/CtcpMessage.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/CtcpMessage.java
new file mode 100644
index 000000000..6012aa0ab
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/CtcpMessage.java
@@ -0,0 +1,129 @@
+package f00f.net.irc.martyr.commands;
+
+import java.util.StringTokenizer;
+
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * This facilitates the sending and receiving of CTCP messages. Upon
+ * receiving a message, MessageCommand checks to see if it is a CTCP,
+ * and if it is, it instantiates this class instead of a
+ * MessageCommand. You can then use the getAction() and getMessage()
+ * methods to retreive the action and payload, respectively.
+ *
+ * @see MessageCommand
+ */
+public class CtcpMessage extends MessageCommand
+{
+ private String actionStr;
+
+ /**
+ * Use this to send a CTCP message. This simply wraps the string
+ * with the CTCP tags, \001.
+ *
+ * @param dest Target of CTCP message
+ * @param message Actual CTCP message
+ */
+ public CtcpMessage( String dest, String message )
+ {
+ super( dest, "\001" + message + "\001" );
+ }
+
+ public CtcpMessage( String dest, String action, String message )
+ {
+ this( dest, action + " " + message );
+ }
+
+ /**
+ * This is only to be called by MessageCommand, as a way of
+ * receiving a Ctcp message. It strips the \001's off and holds
+ * the message left over.
+ *
+ * @param from Nick that sent the message
+ * @param dest Target of the CTCP message
+ * @param message Actual CTCP message
+ */
+ protected CtcpMessage( FullNick from, String dest, String message )
+ {
+ super( from, dest, getMessageStr( stripCtcpWrapper( message ) ) );
+
+ actionStr = getActionStr( stripCtcpWrapper( message ) );
+ }
+
+ /**
+ * Returns the action of this CTCP. Use getMessage() to retreive
+ * the payload of the action.
+ *
+ * @return The action specified by the CTCP message
+ */
+ public String getAction()
+ {
+ return actionStr;
+ }
+
+ /**
+ * Given a stripped CTCP message, returns the ctcp action string.
+ *
+ * @param msg Message to be parsed into an action
+ * @return Action string from message
+ */
+ public static String getActionStr( String msg )
+ {
+ StringTokenizer tokens = new StringTokenizer( msg );
+ return tokens.nextToken();
+ }
+
+ public static String getMessageStr( String msg )
+ {
+ String acn = getActionStr( msg );
+ return msg.substring( acn.length() ).trim();
+ }
+
+ /**
+ * If the string is wrapped with CTCP signal chars (\001) returns
+ * true.
+ *
+ * @param msg String to check whether it's a CTCP message or not
+ * @return True or false if it's a CTCP message
+ */
+ public static boolean isCtcpString( String msg )
+ {
+ return msg.charAt(0) == '\001' && msg.charAt(msg.length()-1) == '\001';
+ }
+
+ /**
+ * Strips a CTCP wrapper, if there is one.
+ *
+ * @param msg String to be stripped
+ * @return Stripped string
+ */
+ public static String stripCtcpWrapper( String msg )
+ {
+ if( isCtcpString( msg ) )
+ {
+ return msg.substring( 1, msg.length()-1 );
+ }
+ else
+ {
+ return msg;
+ }
+ }
+
+ /**
+ * Dysfunctional. Returns dat immediatly.
+ */
+ /*public static byte[] escapeMsg( byte[] dat )
+ {
+ return dat;
+ }*/
+
+ /**
+ * Dysfunctional. Returns dat immediatly.
+ */
+ /*public static byte[] unEscapeMsg( byte[] dat )
+ {
+ return dat;
+ }*/
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/CtcpNotice.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/CtcpNotice.java
new file mode 100644
index 000000000..e40c0a01a
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/CtcpNotice.java
@@ -0,0 +1,131 @@
+package f00f.net.irc.martyr.commands;
+
+import java.util.StringTokenizer;
+
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * This facilitates the sending and receiving of CTCP messages. Upon
+ * receiving a message, MessageCommand checks to see if it is a CTCP,
+ * and if it is, it instantiates this class instead of a
+ * MessageCommand. You can then use the getAction() and getMessage()
+ * methods to retreive the action and payload, respectively.
+ *
+ * @see NoticeCommand
+ */
+public class CtcpNotice extends NoticeCommand
+{
+ private String actionStr;
+
+ /**
+ * Use this to send a CTCP message. This simply wraps the string
+ * with the CTCP tags, \001.
+ *
+ * @param dest Target of CTCP message
+ * @param message Actual CTCP message
+ */
+ public CtcpNotice( String dest, String message )
+ {
+ super( dest, "\001" + message + "\001" );
+ }
+
+ public CtcpNotice( String dest, String action, String message )
+ {
+ this( dest, action + " " + message );
+
+ actionStr = action;
+ }
+
+ /**
+ * This is only to be called by MessageCommand, as a way of
+ * receiving a Ctcp message. It strips the \001's off and holds
+ * the message left over.
+ *
+ * @param from Nick that sent the message
+ * @param dest Target of the CTCP message
+ * @param message Actual CTCP message
+ */
+ protected CtcpNotice( FullNick from, String dest, String message )
+ {
+ super( from, dest, getMessageStr( stripCtcpWrapper( message ) ) );
+
+ actionStr = getActionStr( stripCtcpWrapper( message ) );
+ }
+
+ /**
+ * Returns the action of this CTCP. Use getMessage() to retreive
+ * the payload of the action.
+ *
+ * @return The action specified by the CTCP message
+ */
+ public String getAction()
+ {
+ return actionStr;
+ }
+
+ /**
+ * Given a stripped CTCP message, returns the ctcp action string.
+ *
+ * @param msg Message to be parsed into an action
+ * @return Action string from message
+ */
+ public static String getActionStr( String msg )
+ {
+ StringTokenizer tokens = new StringTokenizer( msg );
+ return tokens.nextToken();
+ }
+
+ public static String getMessageStr( String msg )
+ {
+ String acn = getActionStr( msg );
+ return msg.substring( acn.length() ).trim();
+ }
+
+ /**
+ * If the string is wrapped with CTCP signal chars (\001) returns
+ * true.
+ *
+ * @param msg String to check whether it's a CTCP message or not
+ * @return True or false if it's a CTCP message
+ */
+ public static boolean isCtcpString( String msg )
+ {
+ return msg.charAt(0) == '\001' && msg.charAt(msg.length()-1) == '\001';
+ }
+
+ /**
+ * Strips a CTCP wrapper, if there is one.
+ *
+ * @param msg String to be stripped
+ * @return Stripped string
+ */
+ public static String stripCtcpWrapper( String msg )
+ {
+ if( isCtcpString( msg ) )
+ {
+ return msg.substring( 1, msg.length()-1 );
+ }
+ else
+ {
+ return msg;
+ }
+ }
+
+ /**
+ * Dysfunctional. Returns dat immediatly.
+ */
+ /*public static byte[] escapeMsg( byte[] dat )
+ {
+ return dat;
+ }*/
+
+ /**
+ * Dysfunctional. Returns dat immediatly.
+ */
+ /*public static byte[] unEscapeMsg( byte[] dat )
+ {
+ return dat;
+ }*/
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/InviteCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/InviteCommand.java
new file mode 100644
index 000000000..9010a4322
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/InviteCommand.java
@@ -0,0 +1,90 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.util.FullNick;
+import f00f.net.irc.martyr.util.ParameterIterator;
+
+/**
+ * @author <a href="mailto:martyr@mog.se">Morgan Christiansson</a>
+ */
+public class InviteCommand extends AbstractCommand {
+
+ private String _channel;
+ private String _nick;
+ private FullNick _user;
+
+ /** For use as a factory */
+ public InviteCommand()
+ {
+ _channel = null;
+ _nick = null;
+ _user = null;
+ }
+
+ private InviteCommand(FullNick user, String nick, String channel)
+ {
+ _user = user;
+ _nick = nick;
+ _channel = channel;
+ }
+
+ public InviteCommand(String nick, String channel)
+ {
+ _nick = nick;
+ _channel = channel;
+ }
+
+ public InviteCommand(FullNick nick, String channel)
+ {
+ this(nick.getNick(), channel);
+ }
+
+ /* (non-Javadoc)
+ * @see f00f.net.irc.martyr.Command#parse(java.lang.String, java.lang.String, java.lang.String)
+ */
+ public InCommand parse(String prefix, String identifier, String params)
+ {
+ ParameterIterator iter = new ParameterIterator(params);
+ return new InviteCommand( new FullNick( prefix ), (String)iter.next(), (String)iter.next() );
+ }
+
+ /* (non-Javadoc)
+ * @see f00f.net.irc.martyr.commands.AbstractCommand#getIrcIdentifier()
+ */
+ public String getIrcIdentifier()
+ {
+ return "INVITE";
+ }
+
+ /* (non-Javadoc)
+ * @see f00f.net.irc.martyr.commands.AbstractCommand#renderParams()
+ */
+ public String renderParams()
+ {
+ return _nick+" "+_channel;
+ }
+
+ /**
+ * @return The channel invited to. */
+ public String getChannel()
+ {
+ return _channel;
+ }
+
+ /**
+ * @return The nick sending the invite.
+ */
+ public String getNick()
+ {
+ return _nick;
+ }
+
+ /**
+ * @return The user the invite is sent to.
+ */
+ public FullNick getUser()
+ {
+ return _user;
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/IsonCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/IsonCommand.java
new file mode 100644
index 000000000..8df571a8e
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/IsonCommand.java
@@ -0,0 +1,144 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.CommandRegister;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Defines the ISON command, which is used to determine if a user or a list of users is online.
+ *
+ * @author Daniel Henninger
+ */
+public class IsonCommand extends AbstractCommand
+{
+
+ public static final String IDENTIFIER_PRIMARY = "ISON";
+ public static final String IDENTIFIER_SECONDARY = "303";
+
+ /* List of nicks that we will check for online status */
+ List<String> nicks = new ArrayList<String>();
+
+ /* Destination nick */
+ String dest = null;
+
+ /**
+ * No parameter passed to the ISON is not valid. This is used for factories.
+ */
+ public IsonCommand()
+ {
+ // Nothing to do
+ }
+
+ /**
+ * Check online status of a single nickname.
+ *
+ * @param nick Nick you want to check the online status of.
+ */
+ public IsonCommand(String nick)
+ {
+ this.nicks.add(nick);
+ }
+
+ public IsonCommand(String dest, String nick) {
+ this.dest = dest;
+ this.nicks.add(nick);
+ }
+
+ /**
+ * Check online status of a number of nicknames.
+ *
+ * @param nicks List of nicks you want to check the online status of.
+ */
+ public IsonCommand(List<String> nicks)
+ {
+ this.nicks.addAll(nicks);
+ }
+
+ public IsonCommand(String dest, List<String> nicks) {
+ this.dest = dest;
+ this.nicks.addAll(nicks);
+ }
+
+ /**
+ * @see AbstractCommand#parse(String, String, String)
+ */
+ public InCommand parse(String prefix, String identifier, String params)
+ {
+ // when the command is used as a reply, the nick is parameter 0 and the rest are parameter 1.
+ if ( identifier.equals( IDENTIFIER_SECONDARY ) ) {
+ String nickParam = getParameter(params, 1);
+ List<String> nicks = Arrays.asList(nickParam.split(" "));
+ return new IsonCommand(getParameter(params, 0), nicks);
+ }
+ else {
+ String nickParam = getParameter(params, 0);
+ List<String> nicks = Arrays.asList(nickParam.split(" "));
+ return new IsonCommand(nicks);
+ }
+ }
+
+ /**
+ * @see f00f.net.irc.martyr.commands.AbstractCommand#renderParams()
+ */
+ public String renderParams()
+ {
+ String ret = "";
+ if (nicks.size() > 0) {
+ Boolean isFirst = true;
+ for (String nick : nicks) {
+ if (isFirst) {
+ ret = ret + nick;
+ isFirst = false;
+ }
+ else {
+ ret = ret + " " + nick;
+ }
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * @see f00f.net.irc.martyr.Command#getIrcIdentifier()
+ */
+ public String getIrcIdentifier()
+ {
+ //
+ // This command uses "ISON" on outgoing, so that is why we use
+ // "ISON" here instead of "303".
+ //
+ return IDENTIFIER_PRIMARY;
+ }
+
+ /**
+ * @see AbstractCommand#selfRegister(f00f.net.irc.martyr.CommandRegister)
+ */
+ public void selfRegister(CommandRegister commandRegister)
+ {
+ commandRegister.addCommand( IDENTIFIER_PRIMARY, this );
+ commandRegister.addCommand( IDENTIFIER_SECONDARY, this );
+ }
+
+ /**
+ * Retrieves the target of the ISON command
+ *
+ * @return Target of command
+ */
+ public String getDest() {
+ return dest;
+ }
+
+ /**
+ * Retrieves the list of nicks that are online after an ISON command
+ *
+ * @return List of online nicks
+ */
+ public List<String> getNicks()
+ {
+ return nicks;
+ }
+
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/JoinCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/JoinCommand.java
new file mode 100644
index 000000000..32f2d1e75
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/JoinCommand.java
@@ -0,0 +1,131 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.clientstate.Channel;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.util.FullNick;
+import java.util.logging.Logger;
+
+
+/**
+ * Defines JOIN command.
+ */
+public class JoinCommand extends AbstractCommand
+{
+
+ static Logger log = Logger.getLogger(JoinCommand.class.getName());
+
+ private String channel;
+ private String secret;
+ private FullNick user;
+
+ /** For use as a factory */
+ public JoinCommand()
+ {
+ this.user = null;
+ this.channel = null;
+ this.secret = null;
+ }
+
+ /**
+ * This constructor is used with an incoming JOIN command from the server.
+ *
+ * @param user User that joined the channel
+ * @param channel Channel that was joined
+ */
+ private JoinCommand( FullNick user, String channel )
+ {
+ this.user = user;
+ this.channel = channel;
+ this.secret = null;
+ }
+
+ /**
+ * This constructor is used to make a request to join a channel that
+ * requires a secret key to join.
+ *
+ * @param channel The channel
+ * @param secret The secret key required to enter the channel, or null of
+ * none.
+ */
+ public JoinCommand( String channel, String secret )
+ {
+ this.secret = secret;
+ this.user = null;
+ this.channel = channel;
+ }
+
+ /**
+ * This constructor is used to make a request to join a channel.
+ *
+ * @param channel Channel that will be joined
+ */
+ public JoinCommand( String channel )
+ {
+ this.secret = null;
+ this.user = null;
+ this.channel = channel;
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new JoinCommand( new FullNick( prefix ), getParameter( params, 0 ) );
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "JOIN";
+ }
+
+ public String renderParams()
+ {
+ if( secret == null )
+ return channel;
+ else
+ return channel + " " + secret;
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public String getSecret()
+ {
+ return secret;
+ }
+
+ public FullNick getUser()
+ {
+ return user;
+ }
+
+ public boolean weJoined( ClientState state )
+ {
+ return user.equals( state.getNick() );
+ }
+
+ public boolean updateClientState( ClientState state )
+ {
+ if( weJoined( state ) )
+ {
+ // We've joined a group.
+ //log.debug("JOIN: We've joined " + channel);
+ state.addChannel( channel );
+ return true;
+ }
+ else
+ {
+ // Someone else joined the group.
+ //log.debug("JOIN: " + user + " joined " + channel);
+ // 1) Grab group
+ Channel channelObj = state.getChannel( channel );
+ // 2) Add user
+ channelObj.addMember( user, this );
+ return true;
+ }
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/KickCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/KickCommand.java
new file mode 100644
index 000000000..96b2731e1
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/KickCommand.java
@@ -0,0 +1,110 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.clientstate.Channel;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.util.FullNick;
+import java.util.logging.Logger;
+
+
+/**
+ * Defines KICK command.
+ */
+public class KickCommand extends AbstractCommand
+{
+
+ static Logger log = Logger.getLogger(KickCommand.class.getName());
+
+ private String channel;
+ private FullNick userKicker;
+ private FullNick userKicked;
+ private String comment;
+
+ /** For use as a factory */
+ public KickCommand()
+ {
+ this( null, null, null, null );
+ }
+
+ public KickCommand( FullNick userKicker, String channel,
+ String userKicked, String comment )
+ {
+ this.userKicker = userKicker;
+ this.channel = channel;
+ this.userKicked = new FullNick( userKicked );
+ this.comment = comment;
+ }
+
+ public KickCommand( String channel, String userToKick, String comment )
+ {
+ this( null, channel, userToKick, comment );
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new KickCommand(
+ new FullNick( prefix ),
+ getParameter( params, 0 ),
+ getParameter( params, 1 ),
+ getParameter( params, 2 )
+ );
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "KICK";
+ }
+
+ public String renderParams()
+ {
+ return channel + " " + userKicked + " :" + comment;
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public FullNick getKicker()
+ {
+ return userKicker;
+ }
+
+ public FullNick getKicked()
+ {
+ return userKicked;
+ }
+
+ public String getComment()
+ {
+ return comment;
+ }
+
+ public boolean kickedUs( ClientState state )
+ {
+ return userKicked.equals( state.getNick() );
+ }
+
+ public boolean updateClientState( ClientState state )
+ {
+ if( kickedUs( state ) )
+ {
+ // We've been kicked.
+ //log.debug("KICK: We've been kicked " + channel);
+ state.removeChannel( channel );
+ return true;
+ }
+ else
+ {
+ // Someone else was kicked.
+ //log.debug("KICK: " + userKicked.getNick() + " kicked " + channel);
+ // 1) Grab group
+ Channel channelObj = state.getChannel( channel );
+ channelObj.removeMember( userKicked, this );
+ return true;
+ }
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/MessageCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/MessageCommand.java
new file mode 100644
index 000000000..3d66f7c21
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/MessageCommand.java
@@ -0,0 +1,127 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.util.FullNick;
+
+
+/**
+ * Defines the PRIVMSG command. Messages can be sent to groups or to users.
+ */
+public class MessageCommand extends AbstractCommand
+{
+
+ private FullNick from;
+ private String dest;
+ private String message;
+
+
+ /** Factory */
+ public MessageCommand()
+ {
+ from = null;
+ dest = null;
+ message = null;
+ }
+
+ /**
+ * Used to send a message.
+ *
+ * @param dest Target for message
+ * @param message Message to be sent
+ */
+ public MessageCommand( String dest, String message )
+ {
+ this( null, dest, message );
+ }
+
+ /**
+ * Used to send a message.
+ *
+ * @param dest Target for message
+ * @param message Message to be sent
+ */
+ public MessageCommand( FullNick dest, String message )
+ {
+ this( dest.getNick(), message );
+ }
+
+ public MessageCommand( FullNick source, String dest, String message )
+ {
+ this.from = source;
+ this.dest = dest;
+ this.message = message;
+ }
+
+ /**
+ * Parses a string and produces a formed command object, if it can.
+ * Should return null if it cannot form the command object.
+ */
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ FullNick from;
+ if( prefix == null || prefix.trim().length() == 0 )
+ {
+ from = null;
+ }
+ else
+ {
+ from = new FullNick( prefix );
+ }
+ String dest = getParameter( params, 0 );
+ String msg = getParameter( params, 1 );
+
+ if( CtcpMessage.isCtcpString( msg ) )
+ {
+ return new CtcpMessage( from, dest, msg );
+ }
+
+ return new MessageCommand( from, dest, msg );
+ }
+
+ /**
+ * Returns the string IRC uses to identify this command. Examples:
+ * NICK, PING, KILL, 332
+ */
+ public String getIrcIdentifier()
+ {
+ return "PRIVMSG";
+ }
+
+ /**
+ * Renders the parameters of this command.
+ */
+ public String renderParams()
+ {
+ return dest + " :" + message;
+ }
+
+ public FullNick getSource()
+ {
+ return from;
+ }
+
+ public String getDest()
+ {
+ return dest;
+ }
+
+ public String getMessage()
+ {
+ return message;
+ }
+
+ /**
+ * Returns true if the message is both private and for us.
+ *
+ * @param state Client state to compare with
+ * @return True or false if this is a private message to us
+ */
+ public boolean isPrivateToUs( ClientState state )
+ {
+ return state.getNick().equals( dest );
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/ModeCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/ModeCommand.java
new file mode 100644
index 000000000..b3c3e19ef
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/ModeCommand.java
@@ -0,0 +1,237 @@
+package f00f.net.irc.martyr.commands;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import f00f.net.irc.martyr.CommandRegister;
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.Mode;
+import f00f.net.irc.martyr.OutCommand;
+import f00f.net.irc.martyr.State;
+import f00f.net.irc.martyr.clientstate.Channel;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import java.util.logging.Logger;
+
+/**
+ * Defines MODE command. Since the MODE command is of two distinct
+ * types, this class is really more of a command mini-factory. It
+ * determines which type of command it is, either a UserModeCommand or
+ * a ChannelModeCommand.
+ *
+ */
+public class ModeCommand implements InCommand, OutCommand
+{
+ static Logger log = Logger.getLogger(ModeCommand.class.getName());
+
+ public static final String IDENTIFIER = "MODE";
+ private String source;
+
+ /** For use as a factory */
+ public ModeCommand()
+ {
+ }
+
+ public Iterator getAttributeKeys()
+ {
+ return new LinkedList().iterator();
+ }
+
+ public String getAttribute( String key )
+ {
+ return null;
+ }
+
+ public static void registerMode( Map<Character,Mode> modes, Mode mode )
+ {
+ Character modeChar = mode.getChar();
+
+ if( modes.get( modeChar ) != null )
+ {
+ log.severe("ModeCommand: Warning: Two modes with same letter: " +
+ modes.get( modeChar ) + " and " + mode);
+ }
+
+ modes.put( modeChar, mode );
+ }
+
+ public State getState()
+ {
+ return State.REGISTERED;
+ }
+
+ public void selfRegister( CommandRegister reg )
+ {
+ reg.addCommand( IDENTIFIER, this );
+ }
+
+ public String getIrcIdentifier()
+ {
+ return IDENTIFIER;
+ }
+
+ // Example
+ // <pre>:repp_!bdamm@dammfine.com MODE #bytesex +oo z * repp_telnet</pre>
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ // there are two kinds of modes. Either a channel mode, or a user
+ // mode. We need to figure out which we are dealing with, and
+ // return that.
+
+ // TODO: Research: Should we specify delimiters other than whitespace?
+ StringTokenizer tokens = new StringTokenizer( params );
+
+ String str = tokens.nextToken();
+
+ //log.debug("ModeCommand: Prefix: " + prefix + " str: " + str
+ // + " total: " + params);
+
+ // Malformed command.
+ if( str == null )
+ return null;
+
+ // Should we check to see if the string is really a channel
+ // that we know about?
+ if( Channel.isChannel( str ) )
+ {
+ return new ChannelModeCommand( prefix, str, tokens );
+ }
+ else
+ {
+ return new UserModeCommand( prefix, str, tokens );
+ }
+ }
+
+ /**
+ * Should not be called, as ModeCommand doesn't actually represent a
+ * command. Use UserModeCommand or ChannelModeCommand instead.
+ */
+ public String render()
+ {
+ throw new IllegalStateException("Don't try to send ModeCommand!");
+ }
+
+ public void setSourceString( String source )
+ {
+ this.source = source;
+ }
+
+ public String getSourceString()
+ {
+ return source;
+ }
+
+ /**
+ * Does nothing, as this is a factory command.
+ */
+ public boolean updateClientState( ClientState cs )
+ {
+ // Nothing here, move on.
+ return false;
+ }
+
+ public String toString()
+ {
+ return "ModeCommand";
+ }
+
+ /** Takes a mode string, such as: '+ooo A B C' or '+o A +o B' or even
+ * '+o-o A B' and returns a List containing Mode objects that
+ * correspond to the modes specified.
+ *
+ * @param modes is a Map of Character to Mode objects.
+ * @param tokens is the sequence of tokens making up the parameters of
+ * the command.
+ * @return List of modes
+ */
+ public List<Mode> parseModes( Map<Character,Mode> modes, StringTokenizer tokens )
+ {
+ LinkedList<Mode> results = new LinkedList<Mode>();
+
+ while( true )
+ {
+ if( tokens.hasMoreTokens() )
+ {
+ parseOneModeSet( modes, tokens, results );
+ }
+ else
+ {
+ return results;
+ }
+ }
+ }
+
+ /**
+ * Parses one group of modes. '+ooo A B C' and not '+o A +o B'. It
+ * will parse the first group it finds and will ignore the rest.
+ *
+ * @param modes Map of character to Mode objects.
+ * @param tokens Sequence of tokens making up the parameters of the command.
+ * @param results List of Mode results to be filled in
+ */
+ private void parseOneModeSet( Map<Character,Mode> modes, StringTokenizer tokens, List<Mode> results )
+ {
+ // A list of modes that we have.
+ LinkedList<Mode> localModes = new LinkedList<Mode>();
+
+ Mode.Sign sign = Mode.Sign.NOSIGN;
+ String chars = tokens.nextToken();
+
+ int stop = chars.length();
+ for( int i = 0; i < stop; ++i )
+ {
+ char lookingAt = chars.charAt( i );
+ if( lookingAt == '+' )
+ sign = Mode.Sign.POSITIVE;
+ else if( lookingAt == '-' )
+ sign = Mode.Sign.NEGATIVE;
+ else if( lookingAt == ':' )
+ // This is to get around a bug in some ircds
+ continue;
+ else
+ {
+ // A real mode character!
+ Mode mode = modes.get( lookingAt );
+ if( mode == null )
+ {
+ //TODO: Is there some way we can figure out if the mode
+ // we don't know anything about needs a parameter?
+ // Things get messy if it does need a parameter, and we
+ // don't eat the string.
+ //log.severe("ModeCommand: Unknown mode: " + lookingAt);
+ }
+ else
+ {
+ mode = mode.newInstance();
+ mode.setSign( sign );
+ localModes.add( mode );
+ }
+ }
+ }
+
+ // Now we know what modes are specified, and whether they are
+ // positive or negative. Now we need to fill in the parameters for
+ // any that require parameters, and place the results in the result
+ // list.
+ for (Mode localMode : localModes) {
+ /*
+ * What we do if the server doesn't pass us a parameter
+ * for a mode is rather undefined - except that we don't
+ * want to run off the end of the tokens. So we just
+ * ignore it. The problem is that we don't always know
+ * when a server is going to send us a parameter or not.
+ * We can only hope that servers don't send ambiguous
+ * masks followed by more modes instead of a parameter.
+ */
+ if (localMode != null && localMode.requiresParam() && tokens.hasMoreTokens()) {
+ localMode.setParam(tokens.nextToken());
+ }
+
+ results.add(localMode);
+ }
+ }
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/NamesCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/NamesCommand.java
new file mode 100644
index 000000000..6f0a9ed5d
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/NamesCommand.java
@@ -0,0 +1,77 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.OutCommand;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Defines the NAMES command, which is used to get the members of certain channels, or all of them.
+ *
+ * @author Daniel Henninger
+ */
+public class NamesCommand implements OutCommand
+{
+
+ /* List of channels we will request membership of. */
+ List<String> channels = new ArrayList<String>();
+
+ /**
+ * No parameter passed to the NAMES command represents a request for all channels.
+ */
+ public NamesCommand()
+ {
+ // Nothing to do
+ }
+
+ /**
+ * Request the membership of a single channel.
+ *
+ * @param channel Channel you want to request membership of.
+ */
+ public NamesCommand(String channel)
+ {
+ this.channels.add(channel);
+ }
+
+ /**
+ * Request the membership of multiple channels.
+ *
+ * @param channels List of channels you want to retrieve the membership list of.
+ */
+ public NamesCommand(List<String> channels)
+ {
+ this.channels.addAll(channels);
+ }
+
+ /**
+ * @see f00f.net.irc.martyr.OutCommand#render()
+ */
+ public String render()
+ {
+ String ret = getIrcIdentifier();
+ if (channels.size() > 0) {
+ ret = ret + " ";
+ Boolean isFirst = true;
+ for (String channel : channels) {
+ if (isFirst) {
+ ret = ret + channel;
+ isFirst = false;
+ }
+ else {
+ ret = ret + "," + channel;
+ }
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * @see f00f.net.irc.martyr.Command#getIrcIdentifier()
+ */
+ public String getIrcIdentifier()
+ {
+ return "NAMES";
+ }
+
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/NickCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/NickCommand.java
new file mode 100644
index 000000000..6cdcb0224
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/NickCommand.java
@@ -0,0 +1,97 @@
+/*
+ * Original version: Ben Damm <bdamm@dammfine.com>
+ * Changes by: Mog
+ * - added getOldNick
+ * */
+package f00f.net.irc.martyr.commands;
+
+import java.util.Enumeration;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.clientstate.Channel;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.clientstate.Member;
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * Defines NICK command.
+ */
+public class NickCommand extends AbstractCommand
+{
+
+ private FullNick oldNick;
+ private FullNick newNick;
+
+ /** For use as a factory */
+ public NickCommand()
+ {
+ this( null, null );
+ }
+
+ public NickCommand( FullNick oldNick, FullNick newNick )
+ {
+ this.oldNick = oldNick;
+ this.newNick = newNick;
+ }
+
+ public NickCommand( String newNick )
+ {
+ this( null, new FullNick( newNick ) );
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NickCommand( new FullNick( prefix ), new FullNick ( getParameter( params, 0 ) ) );
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "NICK";
+ }
+
+ public String renderParams()
+ {
+ return getNick();
+ }
+
+ public String getNick()
+ {
+ return newNick.getNick();
+ }
+
+ public String getOldNick()
+ {
+ return oldNick.getNick();
+ }
+
+ public boolean updateClientState( ClientState state )
+ {
+ // Does this apply to us?
+ if( oldNick.equals( state.getNick() ) )
+ {
+ state.setNick( newNick );
+ return true;
+ }
+ else
+ {
+ // Ok, so we need to change someone's nick.
+ // This needs to occur for each member with that nick in each
+ // channel that we are in. Just use Member.setNick for each
+ // occurance.
+ // Note: I do not believe this code has received a vigorous
+ // test.
+ Enumeration channels = state.getChannels();
+ while( channels.hasMoreElements() )
+ {
+ Channel channel = (Channel)channels.nextElement();
+ Member member = channel.findMember( oldNick.getNick() );
+ if( member != null )
+ member.setNick( newNick );
+ }
+ }
+ return false;
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/NoticeCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/NoticeCommand.java
new file mode 100644
index 000000000..958dcc44f
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/NoticeCommand.java
@@ -0,0 +1,129 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.State;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.util.FullNick;
+
+
+/**
+ * Defines the NOTICE command.
+ */
+public class NoticeCommand extends AbstractCommand
+{
+
+ private FullNick from;
+ private String dest;
+ private String notice;
+
+ /** Factory */
+ public NoticeCommand()
+ {
+ from = null;
+ dest = null;
+ notice = null;
+ }
+
+ public NoticeCommand( String notice )
+ {
+ this.notice = notice;
+ }
+
+ public NoticeCommand( String dest, String notice )
+ {
+ this(null, dest, notice);
+ }
+
+ public NoticeCommand( FullNick dest, String notice )
+ {
+ this(dest.getNick(), notice);
+ }
+
+ public NoticeCommand( FullNick source, String dest, String notice ) {
+ this.from = source;
+ this.dest = dest;
+ this.notice = notice;
+ }
+
+ public State getState()
+ {
+ return State.UNKNOWN;
+ }
+
+ /**
+ * Parses a string and produces a formed command object, if it can.
+ * Should return null if it cannot form the command object.
+ */
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ FullNick from;
+ if( prefix == null || prefix.trim().length() == 0 )
+ {
+ from = null;
+ }
+ else
+ {
+ from = new FullNick( prefix );
+ }
+ String dest = getParameter( params, 0 );
+ String msg = getParameter( params, 1 );
+
+ if( CtcpNotice.isCtcpString( msg ) )
+ {
+ return new CtcpNotice( from, dest, msg );
+ }
+
+ return new NoticeCommand( from, dest, msg );
+ }
+
+ /**
+ * Returns the string IRC uses to identify this command. Examples:
+ * NICK, PING, KILL, 332
+ */
+ public String getIrcIdentifier()
+ {
+ return "NOTICE";
+ }
+
+ /**
+ * Renders the parameters of this command.
+ */
+ public String renderParams()
+ {
+ if (dest != null) {
+ return dest + " :" + notice;
+ }
+ else {
+ return ":" + notice;
+ }
+ }
+
+ public FullNick getFrom()
+ {
+ return from;
+ }
+
+ public String getDest()
+ {
+ return dest;
+ }
+
+ public String getNotice()
+ {
+ return notice;
+ }
+
+ /**
+ * Returns true if the message is both private and for us.
+ *
+ * @param state Client state to compare with
+ * @return True or false if this is a private message to us
+ */
+ public boolean isPrivateToUs( ClientState state )
+ {
+ return state.getNick().equals( dest );
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/PartCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/PartCommand.java
new file mode 100644
index 000000000..b27ed6dd2
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/PartCommand.java
@@ -0,0 +1,126 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.clientstate.Channel;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * <p>Defines PART command. If the part command is from us, we should
+ * remove that channel from the list of channels. If the part command
+ * is from someone else, we should remove that user from the list of
+ * users for that channel.
+ */
+
+public class PartCommand extends AbstractCommand
+{
+
+ private String reason;
+ private String channel;
+ private FullNick user;
+
+ /** For use as a factory */
+ public PartCommand()
+ {
+ this( null, null, null );
+ }
+
+ /**
+ * For use as an incoming command.
+ *
+ * @param user User that is parting
+ * @param channel Channel that the user is parting from
+ * @param reason Reason for part
+ */
+ public PartCommand( FullNick user, String channel, String reason )
+ {
+ this.user = user;
+ this.reason = reason;
+ this.channel = channel;
+ }
+
+ /**
+ * For use as an outgoing command.
+ *
+ * @param channel Channel that we are parting from
+ * @param reason Reason we are parting
+ */
+ public PartCommand( String channel, String reason )
+ {
+ this( null, channel, reason );
+ }
+
+ /**
+ * For use as an outgoing command. Part with no reason.
+ *
+ * @param channel Channel that we are parting from
+ */
+ public PartCommand( String channel )
+ {
+ this( null, channel, null );
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new PartCommand( new FullNick( prefix ), getParameter( params, 0 ), getParameter( params, 1 ) );
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "PART";
+ }
+
+ public String renderParams()
+ {
+ if( reason != null )
+ return channel + " :" + reason;
+ else
+ return channel;
+ }
+
+ public String getReason()
+ {
+ return reason;
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public FullNick getUser()
+ {
+ return user;
+ }
+
+ /** Takes client state action. If we are parting, then remove that
+ * channel from our list of channels. If someone else is parting,
+ * remove them from the channel they are parting from.
+ */
+ public boolean updateClientState( ClientState state )
+ {
+ // We parted
+ if( user.equals( state.getNick() ) )
+ {
+ state.removeChannel( channel );
+ return true;
+ }
+ else
+ {
+ // Someone else parted.
+
+ // 1) Grab channel
+ Channel chanObj = state.getChannel( channel );
+
+ // 2) Remove user
+ chanObj.removeMember( user, this );
+ return true;
+ }
+
+ }
+
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/PassCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/PassCommand.java
new file mode 100644
index 000000000..da1e63cd5
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/PassCommand.java
@@ -0,0 +1,33 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.OutCommand;
+
+/**
+ * Defines PASS command, optional part of the handshake to register on the network.
+ * @author Daniel Henninger
+ */
+public class PassCommand implements OutCommand
+{
+ private String pass;
+
+ public static final String IDENTIFIER = "PASS";
+
+ /**
+ * @param pass the password for the user who is authenticating
+ * */
+ public PassCommand(String pass)
+ {
+ this.pass = pass;
+ }
+
+ public String render()
+ {
+ return IDENTIFIER + " " + pass;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return IDENTIFIER;
+ }
+
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/PingCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/PingCommand.java
new file mode 100644
index 000000000..01c68cb90
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/PingCommand.java
@@ -0,0 +1,67 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.State;
+
+
+/**
+ * Defines the PING command. At this point, PINGs only come in from
+ * the server, so all we need to do is capture the parameters.
+ */
+public class PingCommand extends AbstractCommand
+{
+
+ private String pingSource;
+
+ /** Factory */
+ public PingCommand()
+ {
+ pingSource = null;
+ }
+
+ public PingCommand( String source )
+ {
+ pingSource = source;
+ }
+
+ public State getState()
+ {
+ return State.UNKNOWN;
+ }
+
+ /**
+ * Parses a string and produces a formed command object, if it can.
+ * Should return null if it cannot form the command object.
+ */
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ String str = getParameter( params, 0 );
+ return new PingCommand( str );
+ }
+
+ /**
+ * Returns the string IRC uses to identify this command. Examples:
+ * NICK, PING, KILL, 332
+ */
+ public String getIrcIdentifier()
+ {
+ return "PING";
+ }
+
+ /**
+ * Renders the parameters of this command.
+ */
+ public String renderParams()
+ {
+ return ":" + pingSource;
+ }
+
+ // ===== Ping-specific methods =======================================
+ public String getPingSource()
+ {
+ return pingSource;
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/PongCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/PongCommand.java
new file mode 100644
index 000000000..bfd22531a
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/PongCommand.java
@@ -0,0 +1,39 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.InCommand;
+
+
+/**
+ * Defines the PONG command. At this point, PONGs can only be sent to
+ * the server, so all we need to do is provide render().
+ */
+public class PongCommand extends PingCommand
+{
+
+ public PongCommand( String dest )
+ {
+ super( dest );
+ }
+
+ /**
+ * PONG shouldn't be sent to us.
+ */
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ throw new UnsupportedOperationException("PONG is not an incommand.");
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "PONG";
+ }
+
+ // ===== Pong-specific methods =======================================
+ public String getPongDest()
+ {
+ return getPingSource();
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/QuitCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/QuitCommand.java
new file mode 100644
index 000000000..e12a2520d
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/QuitCommand.java
@@ -0,0 +1,132 @@
+package f00f.net.irc.martyr.commands;
+
+import java.util.Enumeration;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.clientstate.Channel;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * <p>Defines QUIT command. The QUIT command asks the irc server to
+ * disconnect us, and we can optionally give a reason. The QUIT
+ * command is also received by us if someone on a channel we are on
+ * quits.</p>
+ *
+ * <p>What should be done to signal to the framework that the
+ * disconnection that should come from the server is legit, and we
+ * shouldn't try to re-connecet? For now it will be assumed that the
+ * user of the framework will signal all the appropriate classes that
+ * a legit disconnection will happen (ie AutoRegister which will try
+ * to re-connect otherwise).</p>
+ */
+public class QuitCommand extends AbstractCommand
+{
+ //static Logger log = Logger.getLogger(QuitCommand.class);
+
+ private String reason;
+ private FullNick user;
+
+ /** For use as a factory */
+ public QuitCommand()
+ {
+ this( null, null );
+ }
+
+ /**
+ * For use as an incoming command.
+ *
+ * @param user User that has quit
+ * @param reason Specified reason for quitting
+ */
+ public QuitCommand( FullNick user, String reason )
+ {
+ this.user = user;
+ this.reason = reason;
+ }
+
+ /**
+ * For use as an outgoing command.
+ *
+ * @param reason Specified reason for quitting
+ */
+ public QuitCommand( String reason )
+ {
+ this( null, reason );
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new QuitCommand( new FullNick( prefix ), getParameter( params, 0 ) );
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "QUIT";
+ }
+
+ public String renderParams()
+ {
+ return ":" + reason;
+ }
+
+ public String getReason()
+ {
+ return reason;
+ }
+
+ public FullNick getUser()
+ {
+ return user;
+ }
+
+ /**
+ * Returns true if we are the ones quitting.
+ *
+ * @param state Client state we are checking against
+ * @return True or false if the quit is us quitting
+ */
+ public boolean isOurQuit( ClientState state )
+ {
+ return user.equals( state.getNick() );
+ }
+
+ /** If we are quitting, we won't be worrying about our client state.
+ * If someone else is leaving, then remove them from all the groups
+ * they are in.
+ */
+ public boolean updateClientState( ClientState state )
+ {
+ //log.debug( "Nick: " + state.getNick().toString() );
+ if( isOurQuit(state) )
+ {
+ // We've quit
+ //log.debug("QUIT: We've quit: " + reason);
+
+ // What should we do with the client state here?
+ return true;
+ }
+ else
+ {
+ // Someone else quit. We need to remove them from each group
+ // they are in.
+ //log.debug("QUIT: " + user + " quit: " + reason);
+
+ // 1) Grab channels
+ Enumeration channelNames = state.getChannelNames();
+ while( channelNames.hasMoreElements() )
+ {
+ String chanName = channelNames.nextElement().toString();
+
+ // 2) Remove from group.
+ Channel channelObj = state.getChannel( chanName);
+ channelObj.removeMember( user, this );
+ }
+
+ return true;
+ }
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/RawCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/RawCommand.java
new file mode 100644
index 000000000..b7375e92f
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/RawCommand.java
@@ -0,0 +1,58 @@
+package f00f.net.irc.martyr.commands;
+
+import java.util.StringTokenizer;
+
+import f00f.net.irc.martyr.OutCommand;
+
+public class RawCommand implements OutCommand
+{
+
+ private String sourceString;
+ private String ident;
+
+ /**
+ * Tries to use the first "word" in the command as the identifier.
+ * Using this constructor is not recommended.
+ *
+ * @param raw Raw command to send to server
+ */
+ public RawCommand( String raw )
+ {
+ sourceString = raw;
+ StringTokenizer tokens = new StringTokenizer( raw );
+ ident = tokens.nextToken();
+ }
+
+ /**
+ * The rendered command will be <code>identifier + " " +
+ * parameters</code>. This constructure simply allows a correct
+ * response to the <code>getIrcIdentifier</code> method.
+ *
+ * @param identifier Command identifier
+ * @param parameters Parameters to pass
+ */
+ public RawCommand( String identifier, String parameters )
+ {
+ ident = identifier;
+ sourceString = ident + " " + parameters;
+ }
+
+ /**
+ * Returns the identifier, if supplied, or null.
+ */
+ public String getIrcIdentifier()
+ {
+ return ident;
+ }
+
+ /**
+ * Simply returns the string given in the constructor.
+ */
+ public String render()
+ {
+ return sourceString;
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/TopicCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/TopicCommand.java
new file mode 100644
index 000000000..42e3d0421
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/TopicCommand.java
@@ -0,0 +1,80 @@
+package f00f.net.irc.martyr.commands;
+
+import java.util.Date;
+
+import f00f.net.irc.martyr.CommandRegister;
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.clientstate.Channel;
+import f00f.net.irc.martyr.clientstate.ClientState;
+
+public class TopicCommand extends AbstractCommand
+{
+ //static Logger log = Logger.getLogger(TopicCommand.class);
+
+ private String channel;
+ private String topic;
+
+ public static final String IDENTIFIER_PRIMARY = "TOPIC";
+ public static final String IDENTIFIER_SECONDARY = "332";
+
+ public TopicCommand()
+ {
+ this( null, null );
+ }
+
+ public TopicCommand( String channel, String topic )
+ {
+ this.channel = channel;
+ this.topic = topic;
+ }
+
+ public String getIrcIdentifier()
+ {
+ //
+ // This command uses "TOPIC" on outgoing, so that is why we use
+ // "TOPIC" here instead of "332".
+ //
+ return IDENTIFIER_PRIMARY;
+ }
+
+ public void selfRegister( CommandRegister commandRegister )
+ {
+ commandRegister.addCommand( IDENTIFIER_PRIMARY, this );
+ commandRegister.addCommand( IDENTIFIER_SECONDARY, this );
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ // when the command is used as a reply, the nick is parameter 0.
+ if( identifier.equals( IDENTIFIER_SECONDARY ) )
+ return new TopicCommand( getParameter(params, 1), getParameter(params, 2) );
+ else
+ return new TopicCommand( getParameter(params, 0), getParameter(params, 1) );
+ }
+
+ public String renderParams()
+ {
+ return getChannel() + " :" + getTopic();
+ }
+
+ public String getTopic()
+ {
+ return topic;
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public boolean updateClientState( ClientState state )
+ {
+ //log.debug("Topic: Channel: " + channel);
+ Channel chan = state.getChannel( channel );
+ chan.setTopic( topic );
+ chan.setTopicDate( new Date() );
+ return true;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/UnknownCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/UnknownCommand.java
new file mode 100644
index 000000000..6ecd4bc4d
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/UnknownCommand.java
@@ -0,0 +1,38 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.State;
+
+
+/**
+ * Some unknown command, for which there is no factory. This is a
+ * special case command, created by IRCConnection if it can't find a
+ * proper command object.
+ */
+public class UnknownCommand extends AbstractInCommand
+{
+
+ public State getState()
+ {
+ return State.UNKNOWN;
+ }
+
+ /**
+ * Never parsed.
+ */
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ throw new UnsupportedOperationException("UnknownCommand does no parsing.");
+ }
+
+ /**
+ * Unknown, so we don't know what the identifier is ahead of time.
+ */
+ public String getIrcIdentifier()
+ {
+ return null;
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/UserCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/UserCommand.java
new file mode 100644
index 000000000..ac7796f7c
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/UserCommand.java
@@ -0,0 +1,46 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.IRCConnection;
+import f00f.net.irc.martyr.OutCommand;
+/**
+ * Defines USER command, part of the handshake to register on the
+ * network.
+ */
+public class UserCommand implements OutCommand
+{
+
+ private String name;
+ private String user;
+ private String someA; // Might be a mode on some networks
+ private String someB; // might be ignored
+
+ public static final String IDENTIFIER = "USER";
+
+ /**
+ * @param user the login name on the computer the client is on
+ * @param name the purported full name of the user, can be anything.
+ * @param connection the connection the user command is affiliated with
+ * */
+ public UserCommand( String user, String name, IRCConnection connection )
+ {
+ this.name = name;
+ this.user = user;
+ //localhost = connection.getLocalhost();
+ //remotehost = connection.getRemotehost();
+ someA = "0"; // Can be 0|4|8, with 4=+w, 8=+i
+ someB = connection.getRemotehost(); // ignored, apparently
+ }
+
+ public String render()
+ {
+ return IDENTIFIER + " " + user + " " + someA + " " + someB + " :" + name;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return IDENTIFIER;
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/UserModeCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/UserModeCommand.java
new file mode 100644
index 000000000..1190ed64a
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/UserModeCommand.java
@@ -0,0 +1,99 @@
+package f00f.net.irc.martyr.commands;
+
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.Mode;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.modes.user.InvisibleMode;
+import f00f.net.irc.martyr.util.FullNick;
+import java.util.logging.Logger;
+
+/**
+ * Defines a user MODE command.
+ */
+public class UserModeCommand extends ModeCommand
+{
+ static Logger log = Logger.getLogger(ModeCommand.class.getName());
+
+ private FullNick user;
+ private FullNick sender;
+ //private List modes;
+
+ private static HashMap<Character,Mode> modeTypes;
+
+ public UserModeCommand( String prefix, String userStr, StringTokenizer tokens )
+ {
+// System.out.println( prefix );
+ sender = new FullNick( prefix );
+ user = new FullNick( userStr );
+
+ if( !sender.equals( user ) )
+ {
+ log.severe("UserModeCommand: Odd: mode change for a user that isn't us.");
+ return;
+ }
+
+ makeModeTypes();
+
+ //modes = parseModes( modeTypes, tokens );
+
+// System.out.println( modes );
+ }
+
+ private void makeModeTypes()
+ {
+ if( modeTypes == null )
+ {
+ modeTypes = new HashMap<Character,Mode>();
+
+ // Add new mode types here
+ registerMode( modeTypes, new InvisibleMode() );
+ }
+ }
+
+
+ /**
+ * Should not be called, as ModeCommand does the parsing and instantiation
+ * of this class.
+ */
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ throw new IllegalStateException( "Don't call this method!" );
+ }
+
+ public String render()
+ {
+ throw new UnsupportedOperationException("Can't send user modes, yet." );
+ }
+
+ public FullNick getUser()
+ {
+ return user;
+ }
+
+ public FullNick getSender() {
+ return sender;
+ }
+
+ {
+ //log.debug("TODO: UserModeCommand: Can't send");
+ //log.debug("TODO: UserModeCommand: Does not update client state");
+ }
+
+ public boolean updateClientState( ClientState state )
+ {
+ // TODO implement
+ return false;
+ }
+
+ public String toString()
+ {
+ return "UserModeCommand";
+ }
+
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/WelcomeCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/WelcomeCommand.java
new file mode 100644
index 000000000..ecbe9b1ac
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/WelcomeCommand.java
@@ -0,0 +1,125 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.CommandRegister;
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.util.FullNick;
+import f00f.net.irc.martyr.util.ParameterIterator;
+import java.util.logging.Logger;
+
+
+/**
+ * Defines the commands that a server issues to welcome us. These are
+ * identified with 001, 002... etc. These commands are only received
+ * after we register, unlike the NOTICE command.
+ */
+public class WelcomeCommand extends AbstractInCommand
+{
+ static Logger log = Logger.getLogger(WelcomeCommand.class.getName());
+
+ private String notice;
+ private String nick;
+
+ /** Factory */
+ public WelcomeCommand()
+ {
+ this( null, null );
+ }
+
+ /**
+ * Used by parse to create an instance of WelcomeCommand.
+ *
+ * @param nick Nick that send the welcome
+ * @param notice Notice that was sent
+ * */
+ public WelcomeCommand( String nick, String notice )
+ {
+ this.notice = notice;
+ this.nick = nick;
+ //log.debug("WelcomeCommand: Nick is: `" + nick + "'");
+ //log.debug("WelcomeCommand: Notice is: `"+notice+"'");
+ }
+
+ /**
+ * Parses a string and produces a formed command object, if it can.
+ * Should return null if it cannot form the command object.
+ */
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ ParameterIterator pi = new ParameterIterator( params );
+ String nick = pi.next().toString();
+ String notice;
+ if( pi.hasNext() )
+ {
+ // We are looking at a "nick :msg" pair
+ notice = pi.next().toString();
+ }
+ else
+ {
+ // There is only one parameter, a notice.
+ notice = nick;
+ nick = null;
+ }
+ if( pi.hasNext() )
+ {
+ //log.severe("WelcomeCommand: More than two parameters, confused.");
+ }
+
+
+ //String str = getParameter( params, 0 );
+ //
+ return new WelcomeCommand( nick, notice );
+ }
+
+ /**
+ * Sets the nick of the client state, if there is one included with
+ * this command.
+ */
+ public boolean updateClientState( ClientState state )
+ {
+ //log.debug("WelcomeCommand: updated client state with: " + new FullNick( nick ));
+ state.setNick( new FullNick( nick ) );
+
+ return true;
+ }
+
+ /**
+ * Returns the string IRC uses to identify this command. Examples:
+ * NICK, PING, KILL, 332. In our case, there is no one thing.
+ */
+ public String getIrcIdentifier()
+ {
+ return "001";
+ }
+
+ public void selfRegister( CommandRegister commandRegister )
+ {
+ commandRegister.addCommand( "001", this );
+ commandRegister.addCommand( "002", this );
+ commandRegister.addCommand( "003", this );
+ commandRegister.addCommand( "004", this );
+ commandRegister.addCommand( "005", this );
+ }
+
+ public String getNotice()
+ {
+ return notice;
+ }
+
+ /**
+ * @return the nick received with this command, or null if there isn't
+ * one.
+ * */
+ public String getNick()
+ {
+ return nick;
+ }
+
+ public String toString()
+ {
+ return "WelcomeCommand";
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/commands/WhoisCommand.java b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/WhoisCommand.java
new file mode 100644
index 000000000..1c5dea618
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/commands/WhoisCommand.java
@@ -0,0 +1,40 @@
+package f00f.net.irc.martyr.commands;
+
+import f00f.net.irc.martyr.OutCommand;
+
+/**
+ * Implements a WHOIS command, to query details about a user.
+ *
+ */
+public class WhoisCommand implements OutCommand
+{
+ private static final String WHOIS = "WHOIS";
+
+ private String target;
+
+ /**
+ * @param target the nick or mask that you wish to know about.
+ */
+ public WhoisCommand( String target )
+ {
+ this.target = target;
+ }
+
+ /**
+ * @return "WHOIS"
+ */
+ public String getIrcIdentifier()
+ {
+ return WHOIS;
+ }
+
+ /**
+ * Simply returns the string given in the constructor.
+ */
+ public String render()
+ {
+ return WHOIS + " " + target;
+ }
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/AlreadyRegisteredError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/AlreadyRegisteredError.java
new file mode 100644
index 000000000..736b5ebf6
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/AlreadyRegisteredError.java
@@ -0,0 +1,40 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 462 ERR_ALREADYREGISTERED
+ * :You may not reregister
+ * Returned by the server to any link which tries to change part of the registered details (such as
+ * password or user details from second USER message).
+ */
+public class AlreadyRegisteredError extends GenericError
+{
+ private String errorMessage;
+
+ public AlreadyRegisteredError()
+ {
+ }
+
+ public AlreadyRegisteredError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "462";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new AlreadyRegisteredError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/CannotSendToChanError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/CannotSendToChanError.java
new file mode 100644
index 000000000..93793c89a
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/CannotSendToChanError.java
@@ -0,0 +1,47 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 404 ERR_CANNOTSENDTOCHAN
+ * &lt;channel name&gt; :Cannot send to channel
+ * Sent to a user who is either (a) not on a channel which is mode +n or (b) not a chanop (or mode +v)
+ * on a channel which has mode +m set and is trying to send a PRIVMSG message to that channel.
+ */
+public class CannotSendToChanError extends GenericError
+{
+ private String channel;
+ private String errorMessage;
+
+ public CannotSendToChanError()
+ {
+ }
+
+ public CannotSendToChanError(String channel, String errorMessage)
+ {
+ this.channel = channel;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "404";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new CannotSendToChanError(getParameter(params, 1), getParameter(params, 2));
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/CantKillServerError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/CantKillServerError.java
new file mode 100644
index 000000000..246bcbbe7
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/CantKillServerError.java
@@ -0,0 +1,40 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 483 ERR_CANTKILLSERVER
+ * :You can't kill a server!
+ * Any attempts to use the KILL command on a server are to be refused and this
+ * error returned directly to the client.
+ */
+public class CantKillServerError extends GenericError
+{
+ private String errorMessage;
+
+ public CantKillServerError()
+ {
+ }
+
+ public CantKillServerError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "483";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new CantKillServerError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChanOPrivsNeededError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChanOPrivsNeededError.java
new file mode 100644
index 000000000..366dc2b10
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChanOPrivsNeededError.java
@@ -0,0 +1,48 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 482 ERR_CHANOPRIVSNEEDED
+ * &lt;channel&gt; :You're not channel operator
+ * Any command requiring 'chanop' privileges (such as MODE messages) must return
+ * this error if the client making the attempt is not a chanop on the specified
+ * channel.
+ */
+public class ChanOPrivsNeededError extends GenericError
+{
+ private String channel;
+ private String errorMessage;
+
+ public ChanOPrivsNeededError()
+ {
+ }
+
+ public ChanOPrivsNeededError(String channel, String errorMessage)
+ {
+ this.channel = channel;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "482";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new ChanOPrivsNeededError(getParameter(params, 1), getParameter(params, 2));
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelBannedError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelBannedError.java
new file mode 100644
index 000000000..7e3411b70
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelBannedError.java
@@ -0,0 +1,49 @@
+/*
+ * ChannelBannedError.java
+ *
+ * Copyright (C) 2000, 2001, 2002, 2003 Ben Damm
+ *
+ * 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.
+ *
+ * See: http://www.fsf.org/copyleft/lesser.txt
+ */
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 474 ERR_BANNEDFROMCHAN
+ * &lt;channel&gt; :Cannot join channel (+b)
+ * @author <a href="mailto:martyr@mog.se">Morgan Christiansson</a>
+ * @version $Id: ChannelBannedError.java 85 2007-08-02 18:26:59Z jadestorm $
+ * TODO: Should er rename this to BannedFromChanError to match others?
+ */
+public class ChannelBannedError extends GenericJoinError {
+ public ChannelBannedError()
+ {
+ // This one's for registering.
+ }
+
+ protected ChannelBannedError( String chan, String comment )
+ {
+ super(chan, comment);
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "474";
+ }
+
+ protected InCommand create( String channel, String comment)
+ {
+ return new ChannelBannedError( channel, comment );
+ }
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelInviteOnlyError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelInviteOnlyError.java
new file mode 100644
index 000000000..af6322b61
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelInviteOnlyError.java
@@ -0,0 +1,48 @@
+/*
+ * ChannelInviteOnlyError.java
+ *
+ * Copyright (C) 2000, 2001, 2002, 2003 Ben Damm
+ *
+ * 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.
+ *
+ * See: http://www.fsf.org/copyleft/lesser.txt
+ */
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+/**
+ * Code: 473 ERR_INVITEONLYCHAN
+ * &lt;channel&gt; :Cannot join channel (+i)
+ * @version $Id: ChannelInviteOnlyError.java 85 2007-08-02 18:26:59Z jadestorm $
+ * TODO: Should we rename this to InviteOnlyChanError to match others?
+ */
+public class ChannelInviteOnlyError extends GenericJoinError
+{
+ public ChannelInviteOnlyError()
+ {
+ // This one's for registering.
+ }
+
+ protected ChannelInviteOnlyError( String chan, String comment )
+ {
+ super(chan, comment);
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "473";
+ }
+
+ protected InCommand create(String channel, String comment)
+ {
+ return new ChannelInviteOnlyError( channel, comment );
+ }
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelLimitError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelLimitError.java
new file mode 100644
index 000000000..8e7174286
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelLimitError.java
@@ -0,0 +1,50 @@
+/*
+ * ChannelLimitError.java
+ *
+ * Copyright (C) 2000, 2001, 2002, 2003 Ben Damm
+ *
+ * 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.
+ *
+ * See: http://www.fsf.org/copyleft/lesser.txt
+ */
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 471 ERR_CHANNELISFULL
+ * &lt;channel&gt; :Cannot join channel (+l)
+ * @author <a href="mailto:martyr@mog.se">Morgan Christiansson</a>
+ * @version $Id: ChannelLimitError.java 85 2007-08-02 18:26:59Z jadestorm $
+ * TODO: Rename to ChannelIsFullError to match style of others?
+ */
+public class ChannelLimitError extends GenericJoinError
+{
+ public ChannelLimitError()
+ {
+ // This one's for registering.
+ }
+
+ protected ChannelLimitError( String chan, String comment )
+ {
+ super(chan, comment);
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "471";
+ }
+
+ protected InCommand create(String channel, String comment)
+ {
+ return new ChannelLimitError( channel, comment );
+ }
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelWrongKeyError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelWrongKeyError.java
new file mode 100644
index 000000000..099352f9b
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ChannelWrongKeyError.java
@@ -0,0 +1,50 @@
+/*
+ * ChannelWrongKeyError.java
+ *
+ * Copyright (C) 2000, 2001, 2002, 2003 Ben Damm
+ *
+ * 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.
+ *
+ * See: http://www.fsf.org/copyleft/lesser.txt
+ */
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 475 ERR_BADCHANNELKEY
+ * &lt;channel&gt; :Cannot join channel (+k)
+ * @author <a href="mailto:martyr@mog.se">Morgan Christiansson</a>
+ * @version $Id: ChannelWrongKeyError.java 85 2007-08-02 18:26:59Z jadestorm $
+ * TODO: Should we rename to BadChannelKeyError to match others?
+ */
+public class ChannelWrongKeyError extends GenericJoinError
+{
+ public ChannelWrongKeyError()
+ {
+ super();
+ }
+
+ protected ChannelWrongKeyError(String chan, String comment)
+ {
+ super(chan, comment);
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "475";
+ }
+
+ protected InCommand create(String channel, String comment) {
+ return new ChannelWrongKeyError(channel, comment);
+ }
+
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ErroneusNicknameError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ErroneusNicknameError.java
new file mode 100644
index 000000000..4b0bb6bbe
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/ErroneusNicknameError.java
@@ -0,0 +1,47 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * Code: 432 ERR_ERRONEUSNICKNAME
+ * &lt;nick&gt; :Erroneus nickname
+ * Returned after receiving a NICK message which contains characters which do not fall in the defined set.
+ */
+public class ErroneusNicknameError extends GenericError
+{
+ private FullNick nick;
+ private String errorMessage;
+
+ public ErroneusNicknameError()
+ {
+ }
+
+ public ErroneusNicknameError(FullNick nick, String errorMessage)
+ {
+ this.nick = nick;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "432";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new ErroneusNicknameError(new FullNick(getParameter(params, 1)), getParameter(params, 2));
+ }
+
+ public FullNick getNick()
+ {
+ return nick;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/FileErrorError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/FileErrorError.java
new file mode 100644
index 000000000..a41205c7f
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/FileErrorError.java
@@ -0,0 +1,39 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 424 ERR_FILEERROR
+ * :File error doing &lt;file op&gt; on &lt;file&gt;
+ * Generic error message used to report a failed file operation during the processing of a message.
+ */
+public class FileErrorError extends GenericError
+{
+ private String errorMessage;
+
+ public FileErrorError()
+ {
+ }
+
+ public FileErrorError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "424";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new FileErrorError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/GenericError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/GenericError.java
new file mode 100644
index 000000000..5e89fbfd0
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/GenericError.java
@@ -0,0 +1,14 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.commands.AbstractInCommand;
+
+/**
+ * Defines what an error is. All errors are commands.
+ */
+public abstract class GenericError extends AbstractInCommand
+{
+
+}
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/GenericJoinError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/GenericJoinError.java
new file mode 100644
index 000000000..dfabcd3ef
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/GenericJoinError.java
@@ -0,0 +1,67 @@
+/*
+ * GenericJoinError.java
+ *
+ * Copyright (C) 2000, 2001, 2002, 2003 Ben Damm
+ *
+ * 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.
+ *
+ * See: http://www.fsf.org/copyleft/lesser.txt
+ */
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.State;
+import f00f.net.irc.martyr.util.ParameterIterator;
+
+/**
+ * @author <a href="mailto:martyr@mog.se">Morgan Christiansson</a>
+ * @version $Id: GenericJoinError.java 31 2004-04-01 22:02:33Z bdamm $
+ */
+public abstract class GenericJoinError extends GenericError {
+ private String channel;
+ private String comment;
+
+ public GenericJoinError() {
+ }
+
+ protected GenericJoinError(String chan, String comment)
+ {
+ this.channel = chan;
+ this.comment = comment;
+ }
+
+ protected abstract InCommand create(String channel, String comment);
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public String getComment()
+ {
+ return comment;
+ }
+
+ public State getState() {
+ return State.UNKNOWN;
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ ParameterIterator pI = new ParameterIterator( params );
+
+ pI.next(); // We know what our name is.
+ String channel = (String)pI.next();
+ String comment = (String)pI.next();
+
+ return create( channel, comment );
+ }
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/KeySetError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/KeySetError.java
new file mode 100644
index 000000000..0d938263b
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/KeySetError.java
@@ -0,0 +1,45 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 467 ERR_KEYSEY
+ * &lt;channel&gt; :Channel key already set
+ */
+public class KeySetError extends GenericError
+{
+ private String channel;
+ private String errorMessage;
+
+ public KeySetError()
+ {
+ }
+
+ public KeySetError(String channel, String errorMessage)
+ {
+ this.channel = channel;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "467";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new KeySetError(getParameter(params, 1), getParameter(params, 2));
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/LoadTooHighError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/LoadTooHighError.java
new file mode 100644
index 000000000..d6d4411b0
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/LoadTooHighError.java
@@ -0,0 +1,49 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.util.FullNick;
+
+public class LoadTooHighError extends GenericError
+{
+ private FullNick nick;
+ private String command;
+ private String errorMessage;
+
+ public LoadTooHighError()
+ {
+ }
+
+ public LoadTooHighError(FullNick nick, String command, String errorMessage)
+ {
+ this.nick = nick;
+ this.command = command;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "263";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new LoadTooHighError(new FullNick(getParameter(params, 1)), getParameter(params, 2), getParameter(params, 3));
+ }
+
+ public FullNick getNick()
+ {
+ return nick;
+ }
+
+ public String getCommand()
+ {
+ return command;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NeedMoreParamsError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NeedMoreParamsError.java
new file mode 100644
index 000000000..fa80c4dd6
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NeedMoreParamsError.java
@@ -0,0 +1,47 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 461 ERR_NEEDMOREPARAMS
+ * &lt;command&gt; :Not enough parameters
+ * Returned by the server by numerous commands to indicate to the client that it didn't
+ * supply enough parameters.
+ */
+public class NeedMoreParamsError extends GenericError
+{
+ private String command;
+ private String errorMessage;
+
+ public NeedMoreParamsError()
+ {
+ }
+
+ public NeedMoreParamsError(String command, String errorMessage)
+ {
+ this.command = command;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "461";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NeedMoreParamsError(getParameter(params, 1), getParameter(params, 2));
+ }
+
+ public String getCommand()
+ {
+ return command;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NickCollisionError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NickCollisionError.java
new file mode 100644
index 000000000..bf098196c
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NickCollisionError.java
@@ -0,0 +1,48 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * Code: 436 ERR_NICKCOLLISION
+ * &lt;nick&gt; :Nickname collision KILL
+ * Returned by a server to a client when it detects a nickname collision (registered of a NICK that
+ * already exists by another server).
+ */
+public class NickCollisionError extends GenericError
+{
+ private FullNick nick;
+ private String errorMessage;
+
+ public NickCollisionError()
+ {
+ }
+
+ public NickCollisionError(FullNick nick, String errorMessage)
+ {
+ this.nick = nick;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "436";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NickCollisionError(new FullNick(getParameter(params, 1)), getParameter(params, 2));
+ }
+
+ public FullNick getNick()
+ {
+ return nick;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NickInUseError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NickInUseError.java
new file mode 100644
index 000000000..93e37b4a4
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NickInUseError.java
@@ -0,0 +1,63 @@
+/*
+ * Original version: Ben Damm <bdamm@dammfine.com>
+ * Changes by: Mog
+ * - Retains the nick that is in use
+ * */
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.State;
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * Code: 433 ERR_ERRONEUSNICKNAME
+ * &lt;nick&gt; :Nickname is already in use
+ * Returned when a NICK message is processed that result in an attempt to change
+ * to a currently existing nickname.
+ * TODO: Should we rename this to NicknameInUseError for consistency with rest of errors/matching RFC?
+ */
+public class NickInUseError extends GenericError
+{
+ private FullNick _nick;
+ String errorMessage;
+
+ public NickInUseError()
+ {
+ _nick = null;
+ }
+ public NickInUseError(FullNick nick, String errorMessage)
+ {
+ _nick = nick;
+ this.errorMessage = errorMessage;
+ }
+
+ public State getState()
+ {
+ return State.UNKNOWN;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "433";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NickInUseError(new FullNick(getParameter(params, 1)), getParameter(params, 2));
+ }
+
+ /**
+ * @return The nick in use.
+ */
+ public FullNick getNick()
+ {
+ return _nick;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoAdminInfoError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoAdminInfoError.java
new file mode 100644
index 000000000..10b14938b
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoAdminInfoError.java
@@ -0,0 +1,47 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 423 ERR_NOADMININFO
+ * &lt;server name&gt; :No administrative info available
+ * Returned by a server in response to an ADMIN message when there is an error in finding the
+ * appropriate information.
+ */
+public class NoAdminInfoError extends GenericError
+{
+ private String server;
+ private String errorMessage;
+
+ public NoAdminInfoError()
+ {
+ }
+
+ public NoAdminInfoError(String server, String errorMessage)
+ {
+ this.server = server;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "423";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NoAdminInfoError(getParameter(params, 1), getParameter(params, 2));
+ }
+
+ public String getServer()
+ {
+ return server;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoLoginError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoLoginError.java
new file mode 100644
index 000000000..12b2e39f3
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoLoginError.java
@@ -0,0 +1,48 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * Code: 444 ERR_NOLOGIN
+ * &lt;user&gt; :User not logged in
+ * Returned by the summon after a SUMMON command for a user was unable to be performed
+ * since they were not logged in.
+ */
+public class NoLoginError extends GenericError
+{
+ private FullNick nick;
+ private String errorMessage;
+
+ public NoLoginError()
+ {
+ }
+
+ public NoLoginError(FullNick nick, String errorMessage)
+ {
+ this.nick = nick;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "444";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NoLoginError(new FullNick(getParameter(params, 1)), getParameter(params, 2));
+ }
+
+ public FullNick getNick()
+ {
+ return nick;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoMotdError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoMotdError.java
new file mode 100644
index 000000000..0f157a33f
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoMotdError.java
@@ -0,0 +1,39 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 422 ERR_NOMOTD
+ * :MOTD File is missing
+ * Server's MOTD file could not be opened by the server.
+ */
+public class NoMotdError extends GenericError
+{
+ private String errorMessage;
+
+ public NoMotdError()
+ {
+ }
+
+ public NoMotdError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "422";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NoMotdError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoNicknameGivenError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoNicknameGivenError.java
new file mode 100644
index 000000000..7e2502d88
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoNicknameGivenError.java
@@ -0,0 +1,39 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 431 ERR_NONICKNAMEGIVEN
+ * :No nickname given
+ * Returned when a nickname parameter expected for a command and isn't found.
+ */
+public class NoNicknameGivenError extends GenericError
+{
+ private String errorMessage;
+
+ public NoNicknameGivenError()
+ {
+ }
+
+ public NoNicknameGivenError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "431";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NoNicknameGivenError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoOperHostError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoOperHostError.java
new file mode 100644
index 000000000..5ab026987
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoOperHostError.java
@@ -0,0 +1,40 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 491 ERR_NOOPERHOST
+ * :No O-lines for your host
+ * If a client sends an OPER message and the server has not been configured to allow
+ * connections from the client's host as an operator, this error must be returned.
+ */
+public class NoOperHostError extends GenericError
+{
+ private String errorMessage;
+
+ public NoOperHostError()
+ {
+ }
+
+ public NoOperHostError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "491";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NoOperHostError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoOriginError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoOriginError.java
new file mode 100644
index 000000000..3ed8542fb
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoOriginError.java
@@ -0,0 +1,40 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 409 ERR_NOORIGIN
+ * :No origin specified
+ * PING or PONG message missing the originator parameter which is required since these commands must
+ * work without valid prefixes.
+ */
+public class NoOriginError extends GenericError
+{
+ private String errorMessage;
+
+ public NoOriginError()
+ {
+ }
+
+ public NoOriginError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "409";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NoOriginError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoPermForHostError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoPermForHostError.java
new file mode 100644
index 000000000..2cf22c211
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoPermForHostError.java
@@ -0,0 +1,40 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 463 ERR_NOPERMFORHOST
+ * :Your host isn't among the privileged
+ * Returned to a client which attempts to register with a server which does not been setup to allow
+ * connections from the host the attempted connection is tried.
+ */
+public class NoPermForHostError extends GenericError
+{
+ private String errorMessage;
+
+ public NoPermForHostError()
+ {
+ }
+
+ public NoPermForHostError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "463";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NoPermForHostError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoPrivilegesError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoPrivilegesError.java
new file mode 100644
index 000000000..d94df8172
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoPrivilegesError.java
@@ -0,0 +1,40 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 481 ERR_NOPRIVILEGES
+ * :Permission Denied- You're not an IRC operator
+ * Any command requiring operator privileges to operate must return this error to
+ * indicate the attempt was unsuccessful.
+ */
+public class NoPrivilegesError extends GenericError
+{
+ private String errorMessage;
+
+ public NoPrivilegesError()
+ {
+ }
+
+ public NoPrivilegesError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "481";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NoPrivilegesError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoRecipientError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoRecipientError.java
new file mode 100644
index 000000000..e86cefc6c
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoRecipientError.java
@@ -0,0 +1,38 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 411 ERR_NORECIPIENT
+ * :No recipient given (&lt;command&gt;)
+ */
+public class NoRecipientError extends GenericError
+{
+ private String errorMessage;
+
+ public NoRecipientError()
+ {
+ }
+
+ public NoRecipientError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "411";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NoRecipientError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoSuchChannelError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoSuchChannelError.java
new file mode 100644
index 000000000..bf8b3910d
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoSuchChannelError.java
@@ -0,0 +1,46 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 403 ERR_NOSUCHCHANNEL
+ * &lt;channel name&gt; :No such channel
+ * Used to indicate the given channel name is invalid.
+ */
+public class NoSuchChannelError extends GenericError
+{
+ private String channel;
+ private String errorMessage;
+
+ public NoSuchChannelError()
+ {
+ }
+
+ public NoSuchChannelError(String channel, String errorMessage)
+ {
+ this.channel = channel;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "403";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NoSuchChannelError(getParameter(params, 1), getParameter(params, 2));
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoSuchNickError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoSuchNickError.java
new file mode 100644
index 000000000..664a2c9d0
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoSuchNickError.java
@@ -0,0 +1,47 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * Code: 401 ERR_NOSUCHNICK
+ * &lt;nickname&gt; :No such nick/channel
+ * Used to indicated the nickname parameter supplied to a command is currently unused.
+ */
+public class NoSuchNickError extends GenericError
+{
+ private FullNick nick;
+ private String errorMessage;
+
+ public NoSuchNickError()
+ {
+ }
+
+ public NoSuchNickError(FullNick nick, String errorMessage)
+ {
+ this.nick = nick;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "401";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NoSuchNickError(new FullNick(getParameter(params, 1)), getParameter(params, 2));
+ }
+
+ public FullNick getNick()
+ {
+ return nick;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoSuchServerError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoSuchServerError.java
new file mode 100644
index 000000000..cdb0dee90
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoSuchServerError.java
@@ -0,0 +1,46 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 402 ERR_NOSUCHSERVER
+ * &lt;server name&gt; :No such server
+ * Used to indicate the server name given currently doesn't exist.
+ */
+public class NoSuchServerError extends GenericError
+{
+ private String server;
+ private String errorMessage;
+
+ public NoSuchServerError()
+ {
+ }
+
+ public NoSuchServerError(String server, String errorMessage)
+ {
+ this.server = server;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "402";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NoSuchServerError(getParameter(params, 1), getParameter(params, 2));
+ }
+
+ public String getServer()
+ {
+ return server;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoTextToSendError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoTextToSendError.java
new file mode 100644
index 000000000..6e0176295
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoTextToSendError.java
@@ -0,0 +1,41 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 412 ERR_NOTEXTTOSEND
+ * :No text to send
+ * 412 - 414 are returned by PRIVMSG to indicate that the message wasn't delivered for some reason.
+ * ERR_NOTOPLEVEL and ERR_WILDTOPLEVEL are errors that are returned when an invalid use of
+ * "PRIVMSG $&lt;server&gt;" or "PRIVMSG #&lt;host&gt;" is attempted.
+ */
+public class NoTextToSendError extends GenericError
+{
+ private String errorMessage;
+
+ public NoTextToSendError()
+ {
+ }
+
+ public NoTextToSendError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "412";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NoTextToSendError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoTopLevelError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoTopLevelError.java
new file mode 100644
index 000000000..4810ab8a1
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NoTopLevelError.java
@@ -0,0 +1,48 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 413 ERR_NOTOPLEVEL
+ * &lt;mask&gt; :No toplevel domain specified
+ * 412 - 414 are returned by PRIVMSG to indicate that the message wasn't delivered for some reason.
+ * ERR_NOTOPLEVEL and ERR_WILDTOPLEVEL are errors that are returned when an invalid use of
+ * "PRIVMSG $&lt;server&gt;" or "PRIVMSG #&lt;host&gt;" is attempted.
+ */
+public class NoTopLevelError extends GenericError
+{
+ private String mask;
+ private String errorMessage;
+
+ public NoTopLevelError()
+ {
+ }
+
+ public NoTopLevelError(String mask, String errorMessage)
+ {
+ this.mask = mask;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "413";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NoTopLevelError(getParameter(params, 1), getParameter(params, 2));
+ }
+
+ public String getMask()
+ {
+ return mask;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NotOnChannelError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NotOnChannelError.java
new file mode 100644
index 000000000..8c1346012
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NotOnChannelError.java
@@ -0,0 +1,47 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 442 ERR_NOTONCHANNEL
+ * &lt;channel&gt; :You're not on that channel
+ * Returned by the server whenever a client tries to perform a channel effecting command for which the
+ * client isn't a member.
+ */
+public class NotOnChannelError extends GenericError
+{
+ private String channel;
+ private String errorMessage;
+
+ public NotOnChannelError()
+ {
+ }
+
+ public NotOnChannelError(String channel, String errorMessage)
+ {
+ this.channel = channel;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "442";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NotOnChannelError(getParameter(params, 1), getParameter(params, 2));
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NotRegisteredError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NotRegisteredError.java
new file mode 100644
index 000000000..f443901d1
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/NotRegisteredError.java
@@ -0,0 +1,40 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 451 ERR_NOTREGISTERED
+ * :You have not registered
+ * Returned by the server to indicate that the client must be registered before the
+ * server will allow it to be parsed in detail.
+ */
+public class NotRegisteredError extends GenericError
+{
+ private String errorMessage;
+
+ public NotRegisteredError()
+ {
+ }
+
+ public NotRegisteredError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "451";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NotRegisteredError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/PasswdMismatchError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/PasswdMismatchError.java
new file mode 100644
index 000000000..b775b16c4
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/PasswdMismatchError.java
@@ -0,0 +1,40 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 464 ERR_PASSWDMISMATCH
+ * :Password incorrect
+ * Returned to indicate a failed attempt at registering a connection for which a
+ * password was required and was either not given or incorrect.
+ */
+public class PasswdMismatchError extends GenericError
+{
+ private String errorMessage;
+
+ public PasswdMismatchError()
+ {
+ }
+
+ public PasswdMismatchError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "464";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new PasswdMismatchError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/SummonDisabledError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/SummonDisabledError.java
new file mode 100644
index 000000000..0f495fcf4
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/SummonDisabledError.java
@@ -0,0 +1,40 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 445 ERR_SUMMONDISABLED
+ * :SUMMON has been disabled
+ * Returned as a response to the SUMMON command. Must be returned by any server
+ * which does not implement it.
+ */
+public class SummonDisabledError extends GenericError
+{
+ private String errorMessage;
+
+ public SummonDisabledError()
+ {
+ }
+
+ public SummonDisabledError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "445";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new SummonDisabledError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/TooManyChannelsError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/TooManyChannelsError.java
new file mode 100644
index 000000000..a18d92b20
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/TooManyChannelsError.java
@@ -0,0 +1,46 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 405 ERR_TOOMANYCHANNELS
+ * &lt;channel name&gt; :You have joined too many channels
+ * Sent to a user when they have joined the maximum number of allowed channels and they try to join another channel.
+ */
+public class TooManyChannelsError extends GenericError
+{
+ private String channel;
+ private String errorMessage;
+
+ public TooManyChannelsError()
+ {
+ }
+
+ public TooManyChannelsError(String channel, String errorMessage)
+ {
+ this.channel = channel;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "405";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new TooManyChannelsError(getParameter(params, 1), getParameter(params, 2));
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/TooManyTargetsError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/TooManyTargetsError.java
new file mode 100644
index 000000000..594fe4e41
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/TooManyTargetsError.java
@@ -0,0 +1,47 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 407 ERR_TOOMANYTARGETS
+ * &lt;target&gt; :Duplicate recipients. No message delivered
+ * Returned to a client which is attempting to send a PRIVMSG/NOTICE using the user@host destination
+ * format and for a user@host which has several occurrences.
+ */
+public class TooManyTargetsError extends GenericError
+{
+ private String dest;
+ private String errorMessage;
+
+ public TooManyTargetsError()
+ {
+ }
+
+ public TooManyTargetsError(String dest, String errorMessage)
+ {
+ this.dest = dest;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "407";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new TooManyTargetsError(getParameter(params, 1), getParameter(params, 2));
+ }
+
+ public String getDest()
+ {
+ return dest;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UModeUnknownFlagError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UModeUnknownFlagError.java
new file mode 100644
index 000000000..9f112e256
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UModeUnknownFlagError.java
@@ -0,0 +1,40 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 501 ERR_UMODEUNKNOWNFLAG
+ * :Unknown MODE flag
+ * Returned by the server to indicate that a MODE message was sent with a nickname
+ * parameter and that the a mode flag sent was not recognized.
+ */
+public class UModeUnknownFlagError extends GenericError
+{
+ private String errorMessage;
+
+ public UModeUnknownFlagError()
+ {
+ }
+
+ public UModeUnknownFlagError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "501";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new UModeUnknownFlagError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UnknownCommandError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UnknownCommandError.java
new file mode 100644
index 000000000..6fd01f0ad
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UnknownCommandError.java
@@ -0,0 +1,46 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 421 ERR_UNKNOWNCOMMAND
+ * &lt;command&gt; :Unknown command
+ * Returned to a registered client to indicate that the command sent is unknown by the server.
+ */
+public class UnknownCommandError extends GenericError
+{
+ private String command;
+ private String errorMessage;
+
+ public UnknownCommandError()
+ {
+ }
+
+ public UnknownCommandError(String command, String errorMessage)
+ {
+ this.command = command;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "421";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new UnknownCommandError(getParameter(params, 1), getParameter(params, 2));
+ }
+
+ public String getCommand()
+ {
+ return command;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UnknownError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UnknownError.java
new file mode 100644
index 000000000..225361294
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UnknownError.java
@@ -0,0 +1,69 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.State;
+import f00f.net.irc.martyr.commands.UnknownCommand;
+
+
+/**
+ * Some unknown command, for which there is no factory. This is a
+ * special case command, created by IRCConnection if it can't find a
+ * proper command object.
+ */
+public class UnknownError extends UnknownCommand
+{
+
+ private String errorStr;
+ private int errorCode;
+
+ public UnknownError( String ident )
+ {
+ errorStr = ident;
+ errorCode = Integer.parseInt( ident );
+ }
+
+ public int getErrorCode()
+ {
+ return errorCode;
+ }
+
+ public String getError()
+ {
+ return errorStr;
+ }
+
+ public static boolean isError( String ident )
+ {
+ char c = ident.charAt(0);
+ return ( c == '4' || c == '5' );
+ }
+
+ public State getState()
+ {
+ return State.UNKNOWN;
+ }
+
+ /**
+ * Never parsed.
+ */
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ throw new UnsupportedOperationException("UnknownError does no parsing.");
+ }
+
+ /**
+ * Unknown, so we don't know what the identifier is ahead of time.
+ */
+ public String getIrcIdentifier()
+ {
+ return errorStr;
+ }
+
+ public String toString()
+ {
+ return "UnknownError[" + errorStr + "]";
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UnknownModeError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UnknownModeError.java
new file mode 100644
index 000000000..ebd10358d
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UnknownModeError.java
@@ -0,0 +1,45 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 472 ERR_UNKNOWNMODE
+ * &lt;char&gt; :is unknown mode char to me
+ */
+public class UnknownModeError extends GenericError
+{
+ private Character mode;
+ private String errorMessage;
+
+ public UnknownModeError()
+ {
+ }
+
+ public UnknownModeError(Character mode, String errorMessage)
+ {
+ this.mode = mode;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "472";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new UnknownModeError(getParameter(params, 1).charAt(0), getParameter(params, 2));
+ }
+
+ public Character getMode()
+ {
+ return mode;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UserNotInChannelError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UserNotInChannelError.java
new file mode 100644
index 000000000..b1734818f
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UserNotInChannelError.java
@@ -0,0 +1,54 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * Code: 441 ERR_USERNOTINCHANNEL
+ * &lt;user&gt; &lt;channel&gt; :They aren't on that channel
+ * Returned by the server to indicate that the target user of the command is not on the given channel.
+ */
+public class UserNotInChannelError extends GenericError
+{
+ private FullNick nick;
+ private String channel;
+ private String errorMessage;
+
+ public UserNotInChannelError()
+ {
+ }
+
+ public UserNotInChannelError(FullNick nick, String channel, String errorMessage)
+ {
+ this.nick = nick;
+ this.channel = channel;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "441";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new UserNotInChannelError(new FullNick(getParameter(params, 1)), getParameter(params, 2), getParameter(params, 3));
+ }
+
+ public FullNick getNick()
+ {
+ return nick;
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UserOnChannelError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UserOnChannelError.java
new file mode 100644
index 000000000..6d5b53bd2
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UserOnChannelError.java
@@ -0,0 +1,54 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * Code: 443 ERR_USERONCHANNEL
+ * &lt;user&gt; &lt;channel&gt; :is already on channel
+ * Returned when a client tries to invite a user to a channel they are already on.
+ */
+public class UserOnChannelError extends GenericError
+{
+ private FullNick nick;
+ private String channel;
+ private String errorMessage;
+
+ public UserOnChannelError()
+ {
+ }
+
+ public UserOnChannelError(FullNick nick, String channel, String errorMessage)
+ {
+ this.nick = nick;
+ this.channel = channel;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "443";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new UserOnChannelError(new FullNick(getParameter(params, 1)), getParameter(params, 2), getParameter(params, 3));
+ }
+
+ public FullNick getNick()
+ {
+ return nick;
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UsersDisabledError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UsersDisabledError.java
new file mode 100644
index 000000000..b09c0f929
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UsersDisabledError.java
@@ -0,0 +1,40 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 446 ERR_USERSDISABLED
+ * :USERS has been disabled
+ * Returned as a response to the USERS command. Must be returned by any server which
+ * does not implement it.
+ */
+public class UsersDisabledError extends GenericError
+{
+ private String errorMessage;
+
+ public UsersDisabledError()
+ {
+ }
+
+ public UsersDisabledError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "446";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new UsersDisabledError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UsersDontMatchError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UsersDontMatchError.java
new file mode 100644
index 000000000..e8fe565e4
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/UsersDontMatchError.java
@@ -0,0 +1,39 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 502 ERR_USERSDONTMATCH
+ * :Cant change mode for other users
+ * Error sent to any user trying to view or change the user mode for a user other than themselves.
+ */
+public class UsersDontMatchError extends GenericError
+{
+ private String errorMessage;
+
+ public UsersDontMatchError()
+ {
+ }
+
+ public UsersDontMatchError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "502";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new UsersDontMatchError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/WasNoSuchNickError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/WasNoSuchNickError.java
new file mode 100644
index 000000000..82c0545d7
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/WasNoSuchNickError.java
@@ -0,0 +1,47 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.util.FullNick;
+
+/**
+ * Code: 406 ERR_WASNOSUCHNICK
+ * &lt;nickname&gt; :There was no such nickname
+ * Returned by WHOWAS to indicate there is no history information for that nickname.
+ */
+public class WasNoSuchNickError extends GenericError
+{
+ private FullNick nick;
+ private String errorMessage;
+
+ public WasNoSuchNickError()
+ {
+ }
+
+ public WasNoSuchNickError(FullNick nick, String errorMessage)
+ {
+ this.nick = nick;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "406";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new WasNoSuchNickError(new FullNick(getParameter(params, 1)), getParameter(params, 2));
+ }
+
+ public FullNick getNick()
+ {
+ return nick;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/WildTopLevelError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/WildTopLevelError.java
new file mode 100644
index 000000000..2fb641f24
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/WildTopLevelError.java
@@ -0,0 +1,48 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 414 ERR_WILDTOPLEVEL
+ * &lt;mask&gt; :Wildcard in toplevel domain
+ * 412 - 414 are returned by PRIVMSG to indicate that the message wasn't delivered for some reason.
+ * ERR_NOTOPLEVEL and ERR_WILDTOPLEVEL are errors that are returned when an invalid use of
+ * "PRIVMSG $&lt;server&gt;" or "PRIVMSG #&lt;host&gt;" is attempted.
+ */
+public class WildTopLevelError extends GenericError
+{
+ private String mask;
+ private String errorMessage;
+
+ public WildTopLevelError()
+ {
+ }
+
+ public WildTopLevelError(String mask, String errorMessage)
+ {
+ this.mask = mask;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "414";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new WildTopLevelError(getParameter(params, 1), getParameter(params, 2));
+ }
+
+ public String getMask()
+ {
+ return mask;
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/errors/YoureBannedCreepError.java b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/YoureBannedCreepError.java
new file mode 100644
index 000000000..1cea503dc
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/errors/YoureBannedCreepError.java
@@ -0,0 +1,40 @@
+package f00f.net.irc.martyr.errors;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Code: 465 ERR_YOUREBANNEDCREEP
+ * :You are banned from this server
+ * Returned after an attempt to connect and register yourself with a server which has been setup to
+ * explicitly deny connections to you.
+ */
+public class YoureBannedCreepError extends GenericError
+{
+ private String errorMessage;
+
+ public YoureBannedCreepError()
+ {
+ }
+
+ public YoureBannedCreepError(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "465";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new YoureBannedCreepError(getParameter(params, 1));
+ }
+
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/GenericMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/GenericMode.java
new file mode 100644
index 000000000..e1dad6045
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/GenericMode.java
@@ -0,0 +1,77 @@
+package f00f.net.irc.martyr.modes;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * GenericNode uses the character to specify the hash code. Thus, two
+ * mode types are the same, in a hash table, even if they have
+ * different parameters or positive/negative values.
+ */
+public abstract class GenericMode implements Mode
+{
+ private String str;
+ private Mode.Sign sign = Mode.Sign.NOSIGN;
+
+ public void setParam( String str )
+ {
+ this.str = str;
+ }
+
+ public String getParam()
+ {
+ return str;
+ }
+
+ public void setSign( Mode.Sign sign )
+ {
+ this.sign = sign;
+ }
+
+ public Mode.Sign getSign()
+ {
+ return sign;
+ }
+
+ public String toString()
+ {
+ String pString = " ";
+ if( sign != Mode.Sign.NOSIGN )
+ pString += ( sign == Mode.Sign.POSITIVE ? "+" : "-" );
+ String className = this.getClass().getName();
+ className = className.substring( className.indexOf('$')+1 );
+
+ String result = className + pString + getChar();
+ if( requiresParam() )
+ {
+ result += " " + getParam();
+ }
+
+ return result;
+ }
+
+ public boolean equals( Object o )
+ {
+ if( o instanceof Mode )
+ {
+ Mode oMode = (Mode)o;
+
+ if( oMode.getParam() == null || this.getParam() == null )
+ return oMode.getChar() == this.getChar();
+
+ if( oMode.getParam() == null && this.getParam() != null )
+ return false;
+ if( oMode.getParam() == null && this.getParam() == null )
+ return oMode.getChar() == this.getChar();
+
+ return oMode.getChar() == this.getChar() &&
+ oMode.getParam().equals(this.getParam());
+ }
+ return false;
+ }
+
+ public int hashCode()
+ {
+ return (int)getChar();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/README b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/README
new file mode 100644
index 000000000..8d2e25c18
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/README
@@ -0,0 +1,8 @@
+This directory contains modes. Unlike commands, errors, and replies,
+these modes do NOT contain any information about the mode other than
+what it is. The Mode objects know if the mode takes a parameter, what
+character represents the mode, and contain a method to create a new
+instance of itself.
+
+You can get and set parameters using the Mode related commands.
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/AnonChannelMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/AnonChannelMode.java
new file mode 100644
index 000000000..9fd215416
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/AnonChannelMode.java
@@ -0,0 +1,44 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * <p>The channel flag 'a' defines an anonymous channel. This means that
+ * when a message sent to the channel is sent by the server to users,
+ * and the origin is a user, then it MUST be masked. To mask the
+ * message, the origin is changed to "anonymous!anonymous@anonymous."
+ * (e.g., a user with the nickname "anonymous", the username "anonymous"
+ * and from a host called "anonymous."). Because of this, servers MUST
+ * forbid users from using the nickname "anonymous". Servers MUST also
+ * NOT send QUIT messages for users leaving such channels to the other
+ * channel members but generate a PART message instead.</p>
+ *
+ * <p>On channels with the character '&amp;' as prefix, this flag MAY be
+ * toggled by channel operators, but on channels with the character '!'
+ * as prefix, this flag can be set (but SHALL NOT be unset) by the
+ * "channel creator" only. This flag MUST NOT be made available on
+ * other types of channels.</p>
+ *
+ * <p>Replies to the WHOIS, WHO and NAMES commands MUST NOT reveal the
+ * presence of other users on channels for which the anonymous flag is
+ * set.</p>
+ * (From RFC2811)
+ */
+public class AnonChannelMode extends GenericChannelMode
+{
+ public boolean requiresParam()
+ {
+ return false;
+ }
+
+ public char getChar()
+ {
+ return 'a';
+ }
+
+ public Mode newInstance()
+ {
+ return new AnonChannelMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/BanMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/BanMode.java
new file mode 100644
index 000000000..d169370cb
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/BanMode.java
@@ -0,0 +1,38 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * <p>Channel Ban and Exception - When a user requests to join a
+ * channel, his local server checks if the user's address matches
+ * any of the ban masks set for the channel. If a match is found,
+ * the user request is denied unless the address also matches an
+ * exception mask set for the channel.</p>
+ *
+ * <p>Servers MUST NOT allow a channel member who is banned from the
+ * channel to speak on the channel, unless this member is a channel
+ * operator or has voice privilege. (See Section 4.1.3 (Voice
+ * Privilege)).</p>
+ *
+ * <p>A user who is banned from a channel and who carries an invitation
+ * sent by a channel operator is allowed to join the channel.</p>
+ * (From RFC2811)
+ */
+public class BanMode extends GenericChannelMask
+{
+ public boolean requiresParam()
+ {
+ return true;
+ }
+
+ public char getChar()
+ {
+ return 'b';
+ }
+
+ public Mode newInstance()
+ {
+ return new BanMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/ExceptionMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/ExceptionMode.java
new file mode 100644
index 000000000..63f3d03f8
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/ExceptionMode.java
@@ -0,0 +1,38 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * <p>Channel Ban and Exception - When a user requests to join a
+ * channel, his local server checks if the user's address matches
+ * any of the ban masks set for the channel. If a match is found,
+ * the user request is denied unless the address also matches an
+ * exception mask set for the channel.</p>
+ *
+ * <p>Servers MUST NOT allow a channel member who is banned from the
+ * channel to speak on the channel, unless this member is a channel
+ * operator or has voice privilege. (See Section 4.1.3 (Voice
+ * Privilege)).</p>
+ *
+ * <p>A user who is banned from a channel and who carries an invitation
+ * sent by a channel operator is allowed to join the channel.</p>
+ * (From RFC2811)
+*/
+public class ExceptionMode extends GenericChannelMask
+{
+ public boolean requiresParam()
+ {
+ return true;
+ }
+
+ public char getChar()
+ {
+ return 'e';
+ }
+
+ public Mode newInstance()
+ {
+ return new ExceptionMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/GenericChannelMask.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/GenericChannelMask.java
new file mode 100644
index 000000000..13e1fc3de
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/GenericChannelMask.java
@@ -0,0 +1,16 @@
+package f00f.net.irc.martyr.modes.channel;
+
+
+/**
+ * 'Masks' and other modes that can have multiple copies in a channel
+ * at once should subclass this.
+ */
+public abstract class GenericChannelMask extends GenericChannelMode
+{
+ public boolean onePerChannel()
+ {
+ return false;
+ }
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/GenericChannelMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/GenericChannelMode.java
new file mode 100644
index 000000000..d65f850b8
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/GenericChannelMode.java
@@ -0,0 +1,23 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.modes.GenericMode;
+
+/**
+ * A generic channel mode will be recorded in the channel, and there
+ * will be one per channel. Modes that can have multiple copies in
+ * the channel (masks) should subclass GenericChannelMask.
+ */
+public abstract class GenericChannelMode extends GenericMode
+{
+ public boolean recordInChannel()
+ {
+ return true;
+ }
+
+ public boolean onePerChannel()
+ {
+ return true;
+ }
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/InviteMaskMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/InviteMaskMode.java
new file mode 100644
index 000000000..c9faa27fc
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/InviteMaskMode.java
@@ -0,0 +1,30 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * <p>Channel Invitation - For channels which have the invite-only
+ * flag set (See Section 4.2.2 (Invite Only Flag)), users whose
+ * address matches an invitation mask set for the channel are
+ * allowed to join the channel without any
+ * invitation.</p>
+ * (From RFC2811)
+ */
+public class InviteMaskMode extends GenericChannelMask
+{
+ public boolean requiresParam()
+ {
+ return true;
+ }
+
+ public char getChar()
+ {
+ return 'I';
+ }
+
+ public Mode newInstance()
+ {
+ return new InviteMaskMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/InviteOnlyMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/InviteOnlyMode.java
new file mode 100644
index 000000000..45da646c9
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/InviteOnlyMode.java
@@ -0,0 +1,32 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * <p>Invite Only Flag - When the channel flag 'i' is set, new
+ * members are only accepted if their mask matches Invite-list (See
+ * section 4.3.2) or they have been invited by a channel operator.
+ * This flag also restricts the usage of the INVITE command (See
+ * "IRC Client Protocol" [IRC-CLIENT]) to channel operators.</p>
+ * (From RFC2811)
+ *
+ * @see InviteMaskMode
+ */
+public class InviteOnlyMode extends GenericChannelMode
+{
+ public boolean requiresParam()
+ {
+ return false;
+ }
+
+ public char getChar()
+ {
+ return 'i';
+ }
+
+ public Mode newInstance()
+ {
+ return new InviteOnlyMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/KeyMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/KeyMode.java
new file mode 100644
index 000000000..9f29da431
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/KeyMode.java
@@ -0,0 +1,31 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * <p>Channel Key - When a channel key is set (by using the mode
+ * 'k'), servers MUST reject their local users request to join the
+ * channel unless this key is given.</p>
+ *
+ * <p>The channel key MUST only be made visible to the channel members in
+ * the reply sent by the server to a MODE query.</p>
+ * (From RFC2811)
+ */
+public class KeyMode extends GenericChannelMask
+{
+ public boolean requiresParam()
+ {
+ return true;
+ }
+
+ public char getChar()
+ {
+ return 'k';
+ }
+
+ public Mode newInstance()
+ {
+ return new KeyMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/LimitMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/LimitMode.java
new file mode 100644
index 000000000..028739705
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/LimitMode.java
@@ -0,0 +1,31 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * <p>User Limit - A user limit may be set on channels by using the
+ * channel flag 'l'. When the limit is reached, servers MUST
+ * forbid their local users to join the channel.</p>
+ *
+ * <p>The value of the limit MUST only be made available to the channel
+ * members in the reply sent by the server to a MODE query.</p>
+ * (From RFC2811)
+*/
+public class LimitMode extends GenericChannelMode
+{
+ public boolean requiresParam()
+ {
+ return true;
+ }
+
+ public char getChar()
+ {
+ return 'l';
+ }
+
+ public Mode newInstance()
+ {
+ return new LimitMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/ModeratedMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/ModeratedMode.java
new file mode 100644
index 000000000..d0938a565
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/ModeratedMode.java
@@ -0,0 +1,31 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * <p>Moderated Channel Flag - The channel flag 'm' is used to
+ * control who may speak on a channel. When it is set, only
+ * channel operators, and members who have been given the voice
+ * privilege may send messages to the channel.</p>
+ *
+ * <p>This flag only affects users.</p>
+ * (From RFC2811)
+ */
+public class ModeratedMode extends GenericChannelMode
+{
+ public boolean requiresParam()
+ {
+ return false;
+ }
+
+ public char getChar()
+ {
+ return 'm';
+ }
+
+ public Mode newInstance()
+ {
+ return new ModeratedMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/NoExtMsgMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/NoExtMsgMode.java
new file mode 100644
index 000000000..029baf760
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/NoExtMsgMode.java
@@ -0,0 +1,30 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * <p>No Messages To Channel From Clients On The Outside - When the
+ * channel flag 'n' is set, only channel members MAY send messages
+ * to the channel.</p>
+ *
+ * <p>This flag only affects users.</p>
+ * (From RFC2811)
+ */
+public class NoExtMsgMode extends GenericChannelMode
+{
+ public boolean requiresParam()
+ {
+ return false;
+ }
+
+ public char getChar()
+ {
+ return 'n';
+ }
+
+ public Mode newInstance()
+ {
+ return new NoExtMsgMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/OperMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/OperMode.java
new file mode 100644
index 000000000..e8f45f0be
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/OperMode.java
@@ -0,0 +1,31 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * <p>Channel Operator Status - The mode 'o' is used to toggle the
+ * operator status of a channel member.</p> (From RFC2811)
+ *
+ * <p>Note that OperMode is recorded in the channel, but checking the op
+ * status of a member will give you a true list of who is and isn't an
+ * operator. This is because we don't know the entire list of modes
+ * when entering a channel.</p>
+ */
+public class OperMode extends GenericChannelMode
+{
+ public boolean requiresParam()
+ {
+ return true;
+ }
+
+ public char getChar()
+ {
+ return 'o';
+ }
+
+ public Mode newInstance()
+ {
+ return new OperMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/PrivateMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/PrivateMode.java
new file mode 100644
index 000000000..580b957e6
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/PrivateMode.java
@@ -0,0 +1,50 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * <p>Private and Secret Channels - The channel flag 'p' is used to
+ * mark a channel "private" and the channel flag 's' to mark a
+ * channel "secret". Both properties are similar and conceal the
+ * existence of the channel from other users.</p>
+ *
+ * <p>This means that there is no way of getting this channel's name from
+ * the server without being a member. In other words, these channels
+ * MUST be omitted from replies to queries like the WHOIS
+ * command.</p>
+ *
+ * <p>When a channel is "secret", in addition to the restriction above, the
+ * server will act as if the channel does not exist for queries like the
+ * TOPIC, LIST, NAMES commands. Note that there is one exception to
+ * this rule: servers will correctly reply to the MODE command.
+ * Finally, secret channels are not accounted for in the reply to the
+ * LUSERS command (See "Internet Relay Chat: Client Protocol" [IRC-
+ * CLIENT]) when the &lt;mask&gt; parameter is specified.</p>
+ *
+ * <p>The channel flags 'p' and 's' MUST NOT both be set at the same time.
+ * If a MODE message originating from a server sets the flag 'p' and the
+ * flag 's' is already set for the channel, the change is silently
+ * ignored. This should only happen during a split healing phase
+ * (mentioned in the "IRC Server Protocol" document
+ * [IRC-SERVER]).</p>
+ *
+ * (From RFC2811)
+ */
+public class PrivateMode extends GenericChannelMode
+{
+ public boolean requiresParam()
+ {
+ return false;
+ }
+
+ public char getChar()
+ {
+ return 'p';
+ }
+
+ public Mode newInstance()
+ {
+ return new PrivateMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/SecretMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/SecretMode.java
new file mode 100644
index 000000000..33bb13733
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/SecretMode.java
@@ -0,0 +1,50 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * <p>Private and Secret Channels - The channel flag 'p' is used to
+ * mark a channel "private" and the channel flag 's' to mark a
+ * channel "secret". Both properties are similar and conceal the
+ * existence of the channel from other users.</p>
+ *
+ * <p>This means that there is no way of getting this channel's name from
+ * the server without being a member. In other words, these channels
+ * MUST be omitted from replies to queries like the WHOIS
+ * command.</p>
+ *
+ * <p>When a channel is "secret", in addition to the restriction above, the
+ * server will act as if the channel does not exist for queries like the
+ * TOPIC, LIST, NAMES commands. Note that there is one exception to
+ * this rule: servers will correctly reply to the MODE command.
+ * Finally, secret channels are not accounted for in the reply to the
+ * LUSERS command (See "Internet Relay Chat: Client Protocol" [IRC-
+ * CLIENT]) when the &lt;mask&gt; parameter is specified.</p>
+ *
+ * <p>The channel flags 'p' and 's' MUST NOT both be set at the same time.
+ * If a MODE message originating from a server sets the flag 'p' and the
+ * flag 's' is already set for the channel, the change is silently
+ * ignored. This should only happen during a split healing phase
+ * (mentioned in the "IRC Server Protocol" document
+ * [IRC-SERVER]).</p>
+ *
+ * (From RFC2811)
+ */
+public class SecretMode extends GenericChannelMode
+{
+ public boolean requiresParam()
+ {
+ return false;
+ }
+
+ public char getChar()
+ {
+ return 's';
+ }
+
+ public Mode newInstance()
+ {
+ return new SecretMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/TopicLockMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/TopicLockMode.java
new file mode 100644
index 000000000..c6f9b8a81
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/TopicLockMode.java
@@ -0,0 +1,27 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * <p>Topic - The channel flag 't' is used to restrict the usage of the TOPIC
+ * command to channel operators.</p>
+ * (From RFC2811)
+ */
+public class TopicLockMode extends GenericChannelMode
+{
+ public boolean requiresParam()
+ {
+ return false;
+ }
+
+ public char getChar()
+ {
+ return 't';
+ }
+
+ public Mode newInstance()
+ {
+ return new TopicLockMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/VoiceMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/VoiceMode.java
new file mode 100644
index 000000000..8d5faeac3
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/channel/VoiceMode.java
@@ -0,0 +1,34 @@
+package f00f.net.irc.martyr.modes.channel;
+
+import f00f.net.irc.martyr.Mode;
+
+/**
+ * <p>Voice Privilege - The mode 'v' is used to give and take voice
+ * privilege to/from a channel member. Users with this privilege
+ * can talk on moderated channels. (See section 4.2.3 (Moderated
+ * Channel Flag).</p>
+ * (From RFC2811)
+ */
+public class VoiceMode extends GenericChannelMode
+{
+ public boolean requiresParam()
+ {
+ return true;
+ }
+
+ public char getChar()
+ {
+ return 'v';
+ }
+
+ public boolean recordInChannel()
+ {
+ return false;
+ }
+
+ public Mode newInstance()
+ {
+ return new VoiceMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/user/GenericUserMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/user/GenericUserMode.java
new file mode 100644
index 000000000..7a3767d2d
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/user/GenericUserMode.java
@@ -0,0 +1,24 @@
+package f00f.net.irc.martyr.modes.user;
+
+import f00f.net.irc.martyr.modes.GenericMode;
+
+/**
+ *
+ */
+public abstract class GenericUserMode extends GenericMode
+{
+ public boolean recordInChannel()
+ {
+ return false;
+ }
+
+ /**
+ * Well, this is kind of irrelevent isn't it?
+ */
+ public boolean onePerChannel()
+ {
+ return false;
+ }
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/modes/user/InvisibleMode.java b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/user/InvisibleMode.java
new file mode 100644
index 000000000..f86da15a5
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/modes/user/InvisibleMode.java
@@ -0,0 +1,22 @@
+package f00f.net.irc.martyr.modes.user;
+
+import f00f.net.irc.martyr.Mode;
+
+public class InvisibleMode extends GenericUserMode
+{
+ public char getChar()
+ {
+ return 'i';
+ }
+
+ public boolean requiresParam()
+ {
+ return false;
+ }
+
+ public Mode newInstance()
+ {
+ return new InvisibleMode();
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/AbstractWhoisReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/AbstractWhoisReply.java
new file mode 100644
index 000000000..d52e59dcf
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/AbstractWhoisReply.java
@@ -0,0 +1,58 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.util.ParameterIterator;
+
+public abstract class AbstractWhoisReply extends GenericReply
+{
+ //static Logger log = Logger.getLogger(AbstractWhoisReply.class);
+
+ private String target = null;
+
+ /**
+ * Factory constructor.
+ * */
+ public AbstractWhoisReply()
+ {
+ }
+
+ public AbstractWhoisReply( String params )
+ {
+ ParameterIterator pi = getParams( params );
+ parseParams( pi );
+ }
+
+ public abstract String getIrcIdentifier();
+
+ /**
+ * Parse the parameters, but the target param has already been
+ * stripped off.
+ *
+ * @param pi Parameter iterator that will parse the parameters
+ * */
+ protected abstract void parseParams( ParameterIterator pi );
+
+ /**
+ * @return the target of the whois
+ * */
+ public String getTarget()
+ {
+ return target;
+ }
+
+ /**
+ * @param params the params string passed to "parse".
+ * @return a parameter iterator, with the whois target already
+ * stripped off.
+ * */
+ protected ParameterIterator getParams( String params )
+ {
+ ParameterIterator pi = new ParameterIterator(params);
+ pi.next(); // throw away our own nick
+ this.target = (String)pi.next();
+ //log.debug("AbstractWhoisReply: Whois target: " + target);
+
+ return pi;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/AwayReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/AwayReply.java
new file mode 100644
index 000000000..a7105546e
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/AwayReply.java
@@ -0,0 +1,49 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Signals an automated AWAY message received as a response to a PRIVMSG that was sent out.
+ *
+ * @author Daniel Henninger
+ */
+public class AwayReply extends GenericReply
+{
+
+ private String nick;
+ private String message;
+
+ /**
+ * Factory constructor.
+ */
+ public AwayReply()
+ {
+ }
+
+ public AwayReply(String nick, String message)
+ {
+ this.nick = nick;
+ this.message = message;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "301";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new AwayReply(getParameter(params, 1), getParameter(params, 2));
+ }
+
+ public String getNick()
+ {
+ return nick;
+ }
+
+ public String getMessage()
+ {
+ return message;
+ }
+
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ChannelCreationReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ChannelCreationReply.java
new file mode 100644
index 000000000..2dd89e1cd
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ChannelCreationReply.java
@@ -0,0 +1,78 @@
+package f00f.net.irc.martyr.replies;
+
+import java.util.Date;
+import java.util.StringTokenizer;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.clientstate.Channel;
+import f00f.net.irc.martyr.clientstate.ClientState;
+
+/**
+ * ChannelCreationReply sets the creation time of the channel. It is sent
+ * automatically on a MODE discovery request.
+ */
+public class ChannelCreationReply extends GenericReply
+{
+ private String channelName;
+ private Date date;
+
+ /** For use as a factory. */
+ public ChannelCreationReply()
+ {
+ }
+
+ public ChannelCreationReply( String channelName, Date date )
+ {
+ this.channelName = channelName;
+ this.date = date;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "329";
+ }
+
+ /**
+ * This is a factory that passes the command off to a
+ * ChannelModeCommand.
+ */
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ StringTokenizer tokens = new StringTokenizer( params );
+
+ // Our nick. We don't need that, I think.
+ tokens.nextToken();
+
+ // The channel.
+ String chan = tokens.nextToken();
+
+ // The date.
+ Date date;
+ try
+ {
+ date = new Date( Long.parseLong( tokens.nextToken() ) * 1000 );
+ }
+ catch( NumberFormatException nfe )
+ {
+ // riiiight...
+ date = new Date(0);
+ }
+
+ return new ChannelCreationReply( chan, date );
+ }
+
+ /**
+ * This should, theoretically, never be called, because this command is
+ * only ever used as a factory.
+ */
+ public boolean updateClientState( ClientState state )
+ {
+ Channel channel = state.getChannel( channelName );
+ channel.setCreationDate( date );
+ return true;
+ }
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/GenericReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/GenericReply.java
new file mode 100644
index 000000000..eabdf2658
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/GenericReply.java
@@ -0,0 +1,16 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.commands.AbstractInCommand;
+
+/**
+ * Defines what a reply is. A reply is really the same as an error,
+ * it just doesn't signify an error.
+ */
+public abstract class GenericReply extends AbstractInCommand
+{
+
+
+}
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/GenericStringReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/GenericStringReply.java
new file mode 100644
index 000000000..38d609ac6
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/GenericStringReply.java
@@ -0,0 +1,23 @@
+package f00f.net.irc.martyr.replies;
+
+public abstract class GenericStringReply extends GenericReply
+{
+
+ private String string;
+
+ public GenericStringReply()
+ {
+ }
+
+ public GenericStringReply( String string )
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/LUserClientReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/LUserClientReply.java
new file mode 100644
index 000000000..c108fe919
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/LUserClientReply.java
@@ -0,0 +1,28 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+
+public class LUserClientReply extends GenericStringReply
+{
+
+ public LUserClientReply()
+ {
+ }
+
+ public LUserClientReply( String string )
+ {
+ super( string );
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "251";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new LUserClientReply( getParameter( params, 1 ) );
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/LUserMeReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/LUserMeReply.java
new file mode 100644
index 000000000..9083f0a73
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/LUserMeReply.java
@@ -0,0 +1,28 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+
+public class LUserMeReply extends GenericStringReply
+{
+
+ public LUserMeReply()
+ {
+ }
+
+ public LUserMeReply( String string )
+ {
+ super( string );
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "255";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new LUserMeReply( getParameter( params, 1 ) );
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/LUserOpReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/LUserOpReply.java
new file mode 100644
index 000000000..b05efedc1
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/LUserOpReply.java
@@ -0,0 +1,36 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+
+public class LUserOpReply extends GenericStringReply
+{
+
+ private int numOps;
+
+ public LUserOpReply()
+ {
+ }
+
+ public LUserOpReply( int ops, String string )
+ {
+ super( string );
+ this.numOps = ops;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "252";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new LUserOpReply( getIntParameter( params, 1, -1 ), getParameter( params, 2 ) );
+ }
+
+ public int getNumOps()
+ {
+ return numOps;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ListEndReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ListEndReply.java
new file mode 100644
index 000000000..6a0b1ca7f
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ListEndReply.java
@@ -0,0 +1,31 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Signals the end of a LIST response.
+ *
+ * @author Daniel Henninger
+ */
+public class ListEndReply extends GenericReply
+{
+
+ /**
+ * Factory constructor.
+ */
+ public ListEndReply()
+ {
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "323";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new ListEndReply();
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ListReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ListReply.java
new file mode 100644
index 000000000..a814c5a43
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ListReply.java
@@ -0,0 +1,63 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Signals an entry of a LIST response.
+ *
+ * @author Daniel Henninger
+ */
+public class ListReply extends GenericReply
+{
+
+ private String requestor;
+ private String channel;
+ private Integer memberCount;
+ private String topic;
+
+ /**
+ * Factory constructor.
+ */
+ public ListReply()
+ {
+ }
+
+ public ListReply(String requestor, String channel, Integer memberCount, String topic)
+ {
+ this.requestor = requestor;
+ this.channel = channel;
+ this.memberCount = memberCount;
+ this.topic = topic;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "322";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new ListReply(getParameter(params, 0), getParameter(params, 1), Integer.parseInt(getParameter(params, 2)), getParameter(params, 3));
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public Integer getMemberCount()
+ {
+ return memberCount;
+ }
+
+ public String getTopic()
+ {
+ return topic;
+ }
+
+ public String getRequestor()
+ {
+ return requestor;
+ }
+
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ListStartReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ListStartReply.java
new file mode 100644
index 000000000..0f00d6c3f
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ListStartReply.java
@@ -0,0 +1,31 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Signals the beginning of a LIST response.
+ *
+ * @author Daniel Henninger
+ */
+public class ListStartReply extends GenericReply
+{
+
+ /**
+ * Factory constructor.
+ */
+ public ListStartReply()
+ {
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "321";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new ListStartReply();
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ModeReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ModeReply.java
new file mode 100644
index 000000000..d355e45e3
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/ModeReply.java
@@ -0,0 +1,54 @@
+package f00f.net.irc.martyr.replies;
+
+import java.util.StringTokenizer;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.commands.ChannelModeCommand;
+
+/**
+ * ModeReply is really a factory that passes the ModeReply off to a
+ * ChannelModeCommand.
+ */
+public class ModeReply extends GenericReply
+{
+
+ /** For use as a factory. */
+ public ModeReply()
+ {
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "324";
+ }
+
+ /**
+ * This is a factory that passes the command off to a
+ * ChannelModeCommand.
+ */
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ StringTokenizer tokens = new StringTokenizer( params );
+
+ // Our nick. We don't need that, I think.
+ tokens.nextToken();
+
+ String chan = tokens.nextToken();
+
+ return new ChannelModeCommand( prefix, chan, tokens );
+ }
+
+ /**
+ * This should, theoretically, never be called, because this command is
+ * only ever used as a factory.
+ */
+ public boolean updateClientState( ClientState state )
+ {
+ throw new IllegalStateException("This shouldn't be called!" );
+ }
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/NamesEndReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/NamesEndReply.java
new file mode 100644
index 000000000..a0605b5bd
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/NamesEndReply.java
@@ -0,0 +1,47 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+
+public class NamesEndReply extends GenericReply
+{
+
+ private String channel;
+ private String comment;
+
+ /** For use as a factory. */
+ public NamesEndReply()
+ {
+ this( null, null );
+ }
+
+ public NamesEndReply( String channel, String comment )
+ {
+ this.channel = channel;
+ this.comment = comment;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "366";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NamesEndReply( getParameter( params, 1 ), getParameter( params, 2 ) );
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+ public String getComment()
+ {
+ return comment;
+ }
+
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/NamesReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/NamesReply.java
new file mode 100644
index 000000000..acd5045f6
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/NamesReply.java
@@ -0,0 +1,89 @@
+package f00f.net.irc.martyr.replies;
+
+import java.util.List;
+import java.util.Arrays;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.clientstate.Channel;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import java.util.logging.Logger;
+
+public class NamesReply extends GenericReply
+{
+ static Logger log = Logger.getLogger(NamesReply.class.getName());
+
+ private List<String> names;
+ private String channel;
+
+ /** For use as a factory. */
+ public NamesReply()
+ {
+ }
+
+ public NamesReply( String channel, List<String> names )
+ {
+ this.names = names;
+ this.channel = channel;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "353";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NamesReply( getParameter( params, 2 ), Arrays.asList(getParameter( params, 3 ).split(" ")) );
+ }
+
+ /**
+ * Adds the list of names to the client state.
+ */
+ public boolean updateClientState( ClientState state )
+ {
+ boolean stateChanged = false;
+
+ // 1) Get the Channel
+ Channel channelObj = state.getChannel( channel );
+
+ if( channel == null )
+ {
+ log.severe("NamesReply: Channel is null");
+ return false;
+ }
+
+ if( channelObj == null )
+ {
+ log.severe("NamesReply: No channel object for channel: " + channel);
+ return false;
+ }
+
+
+ // 2) Parse out names
+ for (String nick : names) {
+ // 3) Check that the user is not already in the list
+ if( !channelObj.isMemberInChannel( nick ) )
+ {
+ channelObj.addMember( nick, this );
+ stateChanged = true;
+ }
+ }
+
+ return stateChanged;
+ }
+
+ public List<String> getNames()
+ {
+ return names;
+ }
+
+ public String getChannel()
+ {
+ return channel;
+ }
+
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/NowAwayReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/NowAwayReply.java
new file mode 100644
index 000000000..012431112
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/NowAwayReply.java
@@ -0,0 +1,43 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Signals that you were successfully marked un-away.
+ *
+ * @author Daniel Henninger
+ */
+public class NowAwayReply extends GenericReply
+{
+
+ /* Should always be You have been marked as being away */
+ private String message;
+
+ /**
+ * Factory constructor.
+ */
+ public NowAwayReply()
+ {
+ }
+
+ public NowAwayReply(String message)
+ {
+ this.message = message;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "306";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new NowAwayReply(getParameter(params, 0));
+ }
+
+ public String getMessage()
+ {
+ return message;
+ }
+
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/TopicInfoReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/TopicInfoReply.java
new file mode 100644
index 000000000..311e17dc8
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/TopicInfoReply.java
@@ -0,0 +1,80 @@
+package f00f.net.irc.martyr.replies;
+
+import java.util.Date;
+import java.util.StringTokenizer;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.clientstate.Channel;
+import f00f.net.irc.martyr.clientstate.ClientState;
+
+/**
+ * Contains info about the topic, who set it and when.
+ */
+public class TopicInfoReply extends GenericReply
+{
+ private String channelName;
+ private Date date;
+ private String author;
+
+ /** For use as a factory. */
+ public TopicInfoReply()
+ {
+ }
+
+ public TopicInfoReply( String channelName, Date date, String author )
+ {
+ this.channelName = channelName;
+ this.date = date;
+ this.author = author;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "333";
+ }
+
+ public String getChannel()
+ {
+ return this.channelName;
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ StringTokenizer tokens = new StringTokenizer( params );
+
+ // Our nick. We don't need that, I think.
+ tokens.nextToken();
+
+ // The channel.
+ String chan = tokens.nextToken();
+
+ // The author
+ String author = tokens.nextToken();
+
+ // The date.
+ Date date;
+ try
+ {
+ date = new Date( Long.parseLong( tokens.nextToken() ) * 1000 );
+ }
+ catch( NumberFormatException nfe )
+ {
+ // riiiight...
+ date = new Date(0);
+ }
+
+ return new TopicInfoReply( chan, date, author );
+ }
+
+ public boolean updateClientState( ClientState state )
+ {
+ Channel channel = state.getChannel( channelName );
+ channel.setTopicDate( date );
+ channel.setTopicAuthor( author );
+ return true;
+ }
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/UnAwayReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/UnAwayReply.java
new file mode 100644
index 000000000..f4652f0ce
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/UnAwayReply.java
@@ -0,0 +1,43 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+
+/**
+ * Signals that you were successfully marked un-away.
+ *
+ * @author Daniel Henninger
+ */
+public class UnAwayReply extends GenericReply
+{
+
+ /* Should always be You are no longer marked as being away */
+ private String message;
+
+ /**
+ * Factory constructor.
+ */
+ public UnAwayReply()
+ {
+ }
+
+ public UnAwayReply(String message)
+ {
+ this.message = message;
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "305";
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new UnAwayReply(getParameter(params, 0));
+ }
+
+ public String getMessage()
+ {
+ return message;
+ }
+
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/UnknownReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/UnknownReply.java
new file mode 100644
index 000000000..f4ebc14d6
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/UnknownReply.java
@@ -0,0 +1,66 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.State;
+import f00f.net.irc.martyr.commands.UnknownCommand;
+
+
+/**
+ * A container for unknown replies.
+ */
+public class UnknownReply extends UnknownCommand
+{
+ private String replyStr;
+ private int replyCode;
+
+ public UnknownReply( String ident )
+ {
+ replyStr = ident;
+ replyCode = Integer.parseInt( ident );
+ }
+
+ public int getReplyCode()
+ {
+ return replyCode;
+ }
+
+ public String getReply()
+ {
+ return replyStr;
+ }
+
+ public static boolean isReply( String ident )
+ {
+ char c = ident.charAt(0);
+ return ( c == '0' || c == '2' || c == '3' );
+ }
+
+ public State getState()
+ {
+ return State.UNKNOWN;
+ }
+
+ /**
+ * Never parsed.
+ */
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ throw new UnsupportedOperationException("UnknownReply does no parsing.");
+ }
+
+ /**
+ * Unknown, so we don't know what the identifier is ahead of time.
+ */
+ public String getIrcIdentifier()
+ {
+ return replyStr;
+ }
+
+ public String toString()
+ {
+ return "UnknownReply[" + replyStr + "]";
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisChannelsReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisChannelsReply.java
new file mode 100644
index 000000000..0eee5eb2f
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisChannelsReply.java
@@ -0,0 +1,70 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.util.ParameterIterator;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+//import org.apache.log4j.Logger;
+
+public class WhoisChannelsReply extends AbstractWhoisReply
+{
+ //static Logger log = Logger.getLogger(WhoisChannelsReply.class);
+
+ private String channels;
+
+ /**
+ * Factory constructor.
+ * */
+ public WhoisChannelsReply()
+ {
+ }
+
+ public WhoisChannelsReply( String params )
+ {
+ super( params );
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "319";
+ }
+
+ /**
+ * @return a space-delimited list of channels
+ * */
+ public String getChannels()
+ {
+ return channels;
+ }
+
+ /**
+ * @return a set of Strings of channels
+ * */
+ public Set<String> getChannelSet()
+ {
+ StringTokenizer tokens = new StringTokenizer( channels );
+ Set<String> set = new HashSet<String>();
+ while( tokens.hasMoreTokens() )
+ {
+ set.add( tokens.nextToken() );
+ }
+
+ return set;
+ }
+
+ protected void parseParams( ParameterIterator pi )
+ {
+ channels = pi.last(); // Channels
+
+ //log.debug("WhoisChannelsReply: channels: " + channels);
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new WhoisChannelsReply( params );
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisEndReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisEndReply.java
new file mode 100644
index 000000000..ce9def847
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisEndReply.java
@@ -0,0 +1,37 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.util.ParameterIterator;
+
+public class WhoisEndReply extends AbstractWhoisReply
+{
+ /**
+ * Factory constructor.
+ * */
+ public WhoisEndReply()
+ {
+ }
+
+ public WhoisEndReply( String params )
+ {
+ super( params );
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "318";
+ }
+
+
+ protected void parseParams( ParameterIterator pi )
+ {
+ // nothing to do here
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new WhoisEndReply( params );
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisIdleReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisIdleReply.java
new file mode 100644
index 000000000..aba98205d
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisIdleReply.java
@@ -0,0 +1,68 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.util.ParameterIterator;
+import java.util.Date;
+
+//import org.apache.log4j.Logger;
+
+public class WhoisIdleReply extends AbstractWhoisReply
+{
+ //static Logger log = Logger.getLogger(WhoisIdleReply.class);
+
+ private int idleTime;
+ private Date loginTime;
+
+ /**
+ * Factory constructor.
+ * */
+ public WhoisIdleReply()
+ {
+ }
+
+ public WhoisIdleReply( String params )
+ {
+ super( params );
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "317";
+ }
+
+ /**
+ * @return seconds idle
+ * */
+ public int getIdleTime()
+ {
+ return idleTime;
+ }
+
+ /**
+ * @return login time, if provided, null otherwise
+ * */
+ public Date getLoginTime()
+ {
+ return loginTime;
+ }
+
+ protected void parseParams( ParameterIterator pi )
+ {
+ String idleTimeStr = (String)pi.next(); // Idle name
+ idleTime = Integer.parseInt( idleTimeStr );
+ if( pi.hasNext() && ! pi.nextIsLast() )
+ {
+ String loginTimeStr = (String)pi.next(); // Idle description
+ loginTime = new Date( Long.parseLong( loginTimeStr ) * 1000 );
+ }
+ //log.debug("WhoisIdleReply: idle: " + idleTime);
+ //log.debug("WhoisIdleReply: login: " + loginTime);
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new WhoisIdleReply( params );
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisServerReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisServerReply.java
new file mode 100644
index 000000000..d1f4d6344
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisServerReply.java
@@ -0,0 +1,61 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.util.ParameterIterator;
+//import org.apache.log4j.Logger;
+
+public class WhoisServerReply extends AbstractWhoisReply
+{
+ //static Logger log = Logger.getLogger(WhoisServerReply.class);
+
+ private String serverName;
+ private String serverDesc;
+
+ /**
+ * Factory constructor.
+ * */
+ public WhoisServerReply()
+ {
+ }
+
+ public WhoisServerReply( String params )
+ {
+ super( params );
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "312";
+ }
+
+ /**
+ * @return the DNS name of the server
+ * */
+ public String getServerName()
+ {
+ return serverName;
+ }
+
+ /**
+ * @return the free-form description of the server
+ * */
+ public String getServerDescription()
+ {
+ return serverDesc;
+ }
+
+ protected void parseParams( ParameterIterator pi )
+ {
+ serverName = (String)pi.next(); // Server name
+ serverDesc = (String)pi.next(); // Server description
+ //log.debug("WhoisServerReply: server name: " + serverName);
+ //log.debug("WhoisServerReply: server desc: " + serverDesc);
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new WhoisServerReply( params );
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisUserReply.java b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisUserReply.java
new file mode 100644
index 000000000..a896c65e6
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/replies/WhoisUserReply.java
@@ -0,0 +1,54 @@
+package f00f.net.irc.martyr.replies;
+
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.util.ParameterIterator;
+
+public class WhoisUserReply extends AbstractWhoisReply
+{
+
+ private String host;
+ private String name;
+
+ /**
+ * Factory constructor.
+ * */
+ public WhoisUserReply()
+ {
+ }
+
+ public WhoisUserReply( String params )
+ {
+ super( params );
+ }
+
+ public String getIrcIdentifier()
+ {
+ return "311";
+ }
+
+ protected void parseParams( ParameterIterator pi )
+ {
+ pi.next(); // throw away the nick
+ host = (String)pi.next(); // hostmask
+ //log.debug("WhoisUserReply: host: " + host);
+ name = pi.last(); // the "Name"
+ //log.debug("WhoisUserReply: name: " + name);
+ }
+
+ public InCommand parse( String prefix, String identifier, String params )
+ {
+ return new WhoisUserReply( params );
+ }
+
+ public String getHost()
+ {
+ return host;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoJoin.java b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoJoin.java
new file mode 100644
index 000000000..786552a7b
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoJoin.java
@@ -0,0 +1,157 @@
+/*
+ * Original version: Ben Damm <bdamm@dammfine.com>
+ * Changes by: Morgan Christiansson <martyr@mog.se>
+ * - Spotted bugs
+ * - Added timer
+ * - Responds to Invites
+ * - Re-tries a join with a bad key
+ *
+ * Note: Requires Java 1.4
+ */
+package f00f.net.irc.martyr.services;
+
+import f00f.net.irc.martyr.GenericAutoService;
+import f00f.net.irc.martyr.IRCConnection;
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.State;
+import f00f.net.irc.martyr.TimerTaskCommand;
+import f00f.net.irc.martyr.clientstate.Channel;
+import f00f.net.irc.martyr.commands.InviteCommand;
+import f00f.net.irc.martyr.commands.JoinCommand;
+import f00f.net.irc.martyr.commands.KickCommand;
+import f00f.net.irc.martyr.errors.GenericJoinError;
+
+/**
+ * <p>AutoJoin joins a group if the IRCConnection is ready. It will wait until
+ * it is ready if it is not (by waiting for the REGISTERED state change).</p>
+ *
+ * <p>AutoJoin maintains a persistent Join (re-join if kicked).
+ * AutoJoin can cease to be persistent by calling the 'disable'
+ * method.</p>
+ */
+public class AutoJoin extends GenericAutoService
+{
+ //static Logger log = Logger.getLogger(AutoJoin.class);
+
+ private String channel = null;
+ private String key = null;
+ private TimerTaskCommand joinTimerTask = null;
+ private long joinTimerTaskDelay = 10*1000;
+
+ public AutoJoin( IRCConnection connection, String channel )
+ {
+ this( connection, channel, null );
+ }
+
+ public AutoJoin( IRCConnection connection, String channel, String key )
+ {
+ super( connection );
+
+ this.channel = channel;
+ this.key = key;
+
+ enable();
+
+ updateState( connection.getState() );
+ }
+
+ protected void updateState( State state )
+ {
+
+ if( state == State.REGISTERED )
+ performJoin();
+ }
+
+ protected void updateCommand( InCommand command_o )
+ {
+ if( command_o instanceof KickCommand )
+ {
+ KickCommand kickCommand = (KickCommand)command_o;
+
+ if( kickCommand.kickedUs( getConnection().getClientState() ) )
+ {
+ if( Channel.areEqual(kickCommand.getChannel(), channel))
+ {
+ performJoin();
+ }
+ else
+ {
+ // mog: TODO: Should we really join a channel for which we aren't the AutoJoin:er?
+ // BD: You are quite right, this AutoJoin should only worry about itself.
+ // getConnection().sendCommand( new JoinCommand( kickCommand.getChannel() ) );
+ }
+ }
+ }
+ else if(command_o instanceof GenericJoinError )
+ {
+ GenericJoinError joinErr = (GenericJoinError)command_o;
+
+ if( Channel.areEqual( joinErr.getChannel(), channel ) )
+ {
+ //log.debug("AutoJoin: Failed to join channel: "+joinErr.getComment());
+ scheduleJoin();
+ }
+ }
+ else if( command_o instanceof InviteCommand )
+ {
+ InviteCommand invite = (InviteCommand)command_o;
+ if(!getConnection().getClientState().isOnChannel(invite.getChannel()))
+ {
+ performJoin();
+ }
+ }
+ }
+
+ /**
+ * Sets up and sends the join command.
+ * */
+ protected synchronized void performJoin()
+ {
+ setupJoin();
+ sendJoinCommand();
+ }
+
+ /**
+ * Performs various tasks immediatly prior to sending a join command.
+ * Called from performJoin.
+ * */
+ protected void setupJoin()
+ {
+ if(joinTimerTask != null)
+ {
+ joinTimerTask.cancel();
+ joinTimerTask = null;
+ }
+ }
+
+ /**
+ * This method sends the actual command. Called from performJoin.
+ * */
+ protected void sendJoinCommand()
+ {
+ getConnection().sendCommand( new JoinCommand( channel, key ) );
+ }
+
+ protected void scheduleJoin()
+ {
+ if(joinTimerTask == null || !joinTimerTask.isScheduled())
+ {
+ joinTimerTask = new TimerTaskCommand(getConnection(), new JoinCommand(channel, key));
+ //TODO back off delay on repeated retries?
+ getConnection().getCronManager().schedule(joinTimerTask, joinTimerTaskDelay);
+ }
+ }
+
+ public String toString()
+ {
+ if( key == null )
+ return "AutoJoin [" + channel + "]";
+ return "AutoJoin [" + channel + "," + key + "]";
+ }
+
+ // END AutoResponder
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoReconnect.java b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoReconnect.java
new file mode 100644
index 000000000..06ff33f70
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoReconnect.java
@@ -0,0 +1,271 @@
+package f00f.net.irc.martyr.services;
+
+import java.io.IOException;
+
+import f00f.net.irc.martyr.IRCConnection;
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.GenericAutoService;
+import f00f.net.irc.martyr.State;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.commands.QuitCommand;
+
+/**
+ * <p>AutoReconnect performs the job of reconnecting to the server, if
+ * the connection is terminated unexpectedly. AutoReconnect
+ * will try to reconnect 5 times, and then give up. If AutoReconnect
+ * intercepts a QUIT command before the state change that is issued by
+ * us (bounced back from the server) then it will not try to
+ * reconnect.</p>
+ *
+ * <p>Note that AutoReconnect has no play in nick negotiation, and as
+ * such, a failed nick negotiation does not count as a connection
+ * retry.</p>
+ *
+ * <p>Testing note: Does a server send a QUIT before a forceful
+ * removal from the network? Should AutoReconnect not intercept
+ * QUITs? Certainly not all servers send QUITs even when you QUIT on
+ * your own; this class should be put into a command-out chain as well.</p>
+ */
+public class AutoReconnect extends GenericAutoService
+{
+ //static Logger log = Logger.getLogger(AutoReconnect.class);
+
+ private int attempt;
+ private int maxAttempts;
+ private int sleepTime;
+ private boolean disableOnQuit;
+
+ // How many times to try reconnecting?
+ public static final int DEFAULT_MAX_ATTEMPTS = 5;
+ // TODO This is a rather simplistic method, personally I would like to
+ // see a version of this class that implements a backoff algorithm.
+
+ // If we tried to connect, but failed, how long do we wait until we
+ // try again (ms)?
+ public static final int DEFAULT_CONNECT_SLEEPTIME = 1000;
+
+ // If we get a QUIT command from the server notifying us that we have
+ // QUIT, then self-disable so that we don't reconnect.
+ public static final boolean DEFAULT_DISABLE_ON_QUIT = true;
+
+ /**
+ * @param connection The IRCConnection
+ * @param maxAttempts The number of tries to do before giving up
+ * @param sleepBetween Milliseconds to sleep between retries
+ * @param disableOnQuit Automatically disable on quit event
+ */
+ public AutoReconnect( IRCConnection connection, int maxAttempts,
+ int sleepBetween, boolean disableOnQuit )
+ {
+ super( connection );
+
+ this.disableOnQuit = disableOnQuit;
+ this.maxAttempts = maxAttempts;
+ this.sleepTime = sleepBetween;
+ this.attempt = 0;
+
+ enable();
+ }
+
+ public AutoReconnect( IRCConnection connection, int maxAttempts,
+ int sleepBetween )
+ {
+ this( connection, maxAttempts, sleepBetween, DEFAULT_DISABLE_ON_QUIT );
+ }
+
+ /**
+ * Initializes with reasonable defaults.
+ *
+ * @param connection Connection we are associated with
+ */
+ public AutoReconnect( IRCConnection connection )
+ {
+ this( connection, DEFAULT_MAX_ATTEMPTS, DEFAULT_CONNECT_SLEEPTIME );
+ }
+
+ /**
+ * Attempts to connect, returning only when a connection has been made
+ * or repeated connections have failed.
+ *
+ * @param server Server to connect to
+ * @param port Port to connect to
+ * */
+ public void go( String server, int port )
+ {
+ doConnectionLoop( server, port );
+ }
+
+ /**
+ * Attempts a single connection to the server. The connection is done
+ * by asking the client state what server and port we are supposed to
+ * be connected to, and then calling IRCConnection.connect(...).
+ *
+ * @throws IOException if we could not connect successfully
+ * */
+ protected void connect()
+ throws IOException
+ {
+ ClientState cstate = getConnection().getClientState();
+ connect( cstate.getServer(), cstate.getPort() );
+ }
+
+ /**
+ * Attempts a single connection to the server.
+ *
+ * @param server Server to connect to
+ * @param port Port to connect to
+ * @throws IOException if we could not connect successfully
+ * */
+ protected void connect( String server, int port )
+ throws IOException
+ {
+ getConnection().connect( server, port );
+ }
+
+ protected void updateState( State state )
+ {
+ //log.debug("AutoReconnect: Update with state " + state);
+ if( state == State.UNCONNECTED )
+ {
+ // This should only happen if we were connected and then
+ // disconnected. Try connecting.
+
+ // failedToConnect() prevents insane-reconnecting loop if
+ // we never registered, however, it introduces a delay if we
+ // were registered properly previously
+ if (failedToConnect(null))
+ doConnectionLoop();
+ } else if( state == State.REGISTERED ) {
+ this.attempt = 0;
+ }
+
+ //log.debug("AutoReconnect: Returned from " + state);
+ }
+
+ /**
+ * Calls connect() followed by failedToConnect(...) until a connection
+ * is made. Gets the server and port from the client state, implying
+ * that a connection was already attempted before.
+ * */
+ protected void doConnectionLoop()
+ {
+ // Tell called proc to use client state info
+ doConnectionLoop( null, -1 );
+ }
+
+ /**
+ * Calls connect() followed by failedToConnect(...) until a connection
+ * is made, or the service is disabled.
+ *
+ * @param server Server to connect to
+ * @param port Port to connect to
+ * */
+ protected void doConnectionLoop( String server, int port )
+ {
+ boolean keeptrying = true;
+
+ while( keeptrying && enabled )
+ {
+ Exception error = null;
+ try
+ {
+ if( server == null )
+ {
+ // Try getting the info from the client state
+ connect();
+ }
+ else
+ {
+ connect( server, port );
+ }
+ keeptrying = false;
+ }
+ catch( Exception e )
+ {
+ error = e;
+ keeptrying = true;
+ }
+
+ if( keeptrying )
+ {
+ keeptrying = failedToConnect( error );
+ }
+ }
+ }
+
+ /**
+ * Called when the final failure has occurred. Default implementation
+ * does nothing.
+ * */
+ protected void finalFailure()
+ {
+ //log.debug("AutoReconnect: Final failure.");
+ this.attempt = 0;
+ }
+
+ /**
+ * Called when a failure to connect occurs. This method should do
+ * whatever needs to be done between connection attempts; usually this
+ * will be sleeping the current thread and checking if we should stop
+ * trying to reconnect.
+ *
+ * @param error Exception that caused the failure
+ * @return true if another attempt at connecting should occur
+ * */
+ protected boolean failedToConnect( Exception error )
+ {
+ //log.debug("AutoReconnect: Error connecting: " + error);
+
+ this.attempt++;
+
+ // abort if we've tried too many times
+ if( attempt >= maxAttempts )
+ {
+ //log.debug("AutoReconnect: Tried " + attempt + " times, giving up.");
+ finalFailure();
+ return false;
+ }
+
+ // Well, try again damnit!
+ // Sleep for a bit first.
+ try
+ {
+ Thread.sleep( sleepTime );
+ }
+ catch( InterruptedException ie )
+ {
+ // And we're going to do what?
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * AutoReconnect will disable itself if it sees a quit command
+ * generated by returned from the server. This usually happens just
+ * before the server terminates the connection. Note that this would
+ * really be better if we could intercept messages on their way out,
+ * but Martyr doesn't do that.
+ */
+ protected void updateCommand( InCommand command )
+ {
+ if( disableOnQuit
+ && command instanceof QuitCommand
+ && ((QuitCommand)command).isOurQuit(getConnection().getClientState()) )
+ {
+ //log.debug("AutoReconnect: Disabling due to receiving own QUIT.");
+ disable();
+ }
+ }
+
+ public String toString()
+ {
+ return "AutoReconnect [" + attempt + "]";
+ }
+
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoRegister.java b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoRegister.java
new file mode 100644
index 000000000..f0cec08ba
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoRegister.java
@@ -0,0 +1,289 @@
+package f00f.net.irc.martyr.services;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import f00f.net.irc.martyr.IRCConnection;
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.GenericAutoService;
+import f00f.net.irc.martyr.State;
+import f00f.net.irc.martyr.TimerTaskCommand;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.commands.NickCommand;
+import f00f.net.irc.martyr.commands.UserCommand;
+import f00f.net.irc.martyr.commands.PassCommand;
+import f00f.net.irc.martyr.errors.NickInUseError;
+import f00f.net.irc.martyr.util.FullNick;
+import java.util.logging.Logger;
+
+/**
+ * <p>AutoRegister performs the task of registering the user with the server
+ * once connected, including finding an appropriate nickname to use if the
+ * desired one is taken.</p>
+ *
+ * <p>AutoRegister's default behaviour is to send the provided nickname. If it
+ * receives an ERR_NICKNAMEINUSE while unregistered, AutoRegister will try
+ * again, with an _ appended to the nick. If this fails five times,
+ * AutoRegister will ask the IRCConnection to disconnect(). Note that if it
+ * fails to connect it remains enabled, so that if IRCConnection.connect() is
+ * called, it will re-try the same 5 NICKs.</p>
+ *
+ * <p>This default behaviour can be overridden by subclassing AutoRegister and
+ * overriding the getNickIterator( String baseNick ) method. It returns an
+ * instance of the java.util.Iterator interface which supplies nicknames (each
+ * object retreived from the Iterator is presumed to be a String).
+ * AutoRegister will iterate through the nickname list until there are no more
+ * items, at which point it will stop. For simple tasks such as providing a
+ * custom way to form new nicknames, overriding getNickIterator is
+ * sufficient.</p>
+ *
+ * <p>AutoRegister will add itself as a state observer and as a command
+ * observer. It needs to receive the error.NickInUseError command so that
+ * it can re-try the registration, and it needs to detect when we
+ * transition into the UNREGISTERED state.</p>
+ *
+ * <p>AutoRegister should be created before the IRCConnection.connect()
+ * is called. AutoRegister can be disabled by calling the 'disable()'
+ * method at any time. This simply removes AutoRegister as an
+ * observer for the state and commands.</p>
+ *
+ */
+public class AutoRegister extends GenericAutoService
+{
+ static Logger log = Logger.getLogger(AutoRegister.class.getName());
+
+ // I've lost track of why the timer stuff was in here. I think the
+ // original purpose was to make AutoRegister take control of the nick
+ // more *after* registration occurred. This code is now causing so
+ // many problems *before* registration, that I think it may need to be
+ // pulled out. Maybe time to bring a "Manager" service into the
+ // fold?
+ private long nickTimerTaskDelay = 10*1000;
+ private TimerTaskCommand nickTimerTask;
+
+ // Kept so it can be passed to getNickIterator()
+ private String originalNick;
+ // Used to set the client state once we register properly.
+ private String lastTryNick = null;
+ // Passed to the server on login
+ private String user;
+ private String name;
+ private String pass;
+ // Our list of nicks.
+ private Iterator nickIterator = null;
+ // attempt is only used for the debug output.
+ private int attempt = 0;
+
+ public static final int MAX_ATTEMPTS = 5;
+
+ public AutoRegister( IRCConnection connection, String nick,
+ String user, String name )
+ {
+ super( connection );
+
+ this.originalNick = nick;
+ this.user = user;
+ this.name = name;
+ this.pass = null;
+
+ enable();
+ }
+
+ public AutoRegister( IRCConnection connection, String nick,
+ String user, String name, String pass)
+ {
+ super( connection );
+
+ this.originalNick = nick;
+ this.user = user;
+ this.name = name;
+ this.pass = pass;
+
+ enable();
+ }
+
+
+ /**
+ * <p>This method supplies an Iterator that generates nicknames. Each successive
+ * failed attempt to login to the server with a nickname will be met with a new
+ * try using the next nickname in the iterator. When there are no more
+ * nicknames in the Iterator, AutoRegister gives up. Defining the Iterator as
+ * an anonymous class works well.</p>
+ *
+ * <p>The iterator should iterate over String objects.</p>
+ *
+ * @param baseNick The nickname passed into the constructor.
+ * @return Iterator over other attempts of nicks to try
+ */
+ protected Iterator getNickIterator( final String baseNick )
+ {
+ // This is simple and clean.. define the nick generation scheme as an
+ // anonymous class.
+ return new UnderscoreIterator(baseNick);
+ }
+
+ private static class UnderscoreIterator implements Iterator
+ {
+ int count = 1;
+ String nick;
+
+ public UnderscoreIterator( String base )
+ {
+ this.nick = base;
+ }
+
+ public boolean hasNext()
+ {
+ return count <= MAX_ATTEMPTS;
+ }
+
+ public Object next()
+ {
+ if( hasNext() )
+ {
+ String result = nick;
+
+ // Set up the next round
+ nick = nick + "_";
+ ++count;
+
+ // return the value for this round.
+ return result;
+ }
+ else
+ {
+ throw new NoSuchElementException("No more nicknames");
+ }
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ protected void updateState( State state )
+ {
+ //log.debug("AutoRegister: Update with state " + state);
+ if( state == State.UNREGISTERED )
+ {
+ // We need to do some registerin'!
+ nickIterator = getNickIterator( originalNick );
+ attempt = 0;
+ doRegister();
+ }
+ else if( state == State.REGISTERED )
+ {
+ // We need to update the client state.
+ ClientState clientState = getConnection().getClientState();
+ clientState.setNick( new FullNick( lastTryNick ) );
+ clientState.setName( name );
+ clientState.setUser( user );
+ clientState.setPass( pass );
+ }
+
+ //log.debug("AutoRegister: Returned from " + state);
+ }
+
+ protected void updateCommand( InCommand command )
+ {
+ // First, check the state, because if we are already registered
+ // then none of this matters.
+ //if( getConnection().getState() == State.REGISTERED )
+ //{
+ // // We're registered.
+ // // No reason to continue.
+ // return;
+ //}
+
+ if( command instanceof NickInUseError)
+ {
+ // If we get an error, then try another nick
+ NickInUseError nickErr = (NickInUseError)command;
+ if(nickErr.getNick().getNick().equals(originalNick))
+ {
+ cancelNickAttempt(); // We don't want more than one of these
+
+ scheduleNickAttempt();
+ }
+ if(getConnection().getState() == State.UNREGISTERED )
+ {
+ // re-register.
+ doRegister();
+ }
+ }
+ else if( command instanceof NickCommand )
+ {
+ // If we get a nick... then cancel a pending change
+ NickCommand nickCmd = (NickCommand)command;
+ if( nickCmd.getOldNick().equalsIgnoreCase( originalNick ) )
+ {
+ cancelNickAttempt();
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ private void scheduleNickAttempt()
+ {
+ if( getConnection().getState().equals(State.REGISTERED))
+ {
+ // We're already connected.
+ // We're short-circuiting
+ return;
+ }
+ if(nickTimerTask == null || !nickTimerTask.isScheduled())
+ {
+ nickTimerTask = new TimerTaskCommand(getConnection(), new NickCommand(originalNick));
+ //TODO back off delay on repeated retries?
+ getConnection().getCronManager().schedule(nickTimerTask, nickTimerTaskDelay);
+ }
+ }
+
+ private void cancelNickAttempt()
+ {
+ if(nickTimerTask != null && nickTimerTask.isScheduled())
+ {
+ nickTimerTask.cancel();
+ }
+ }
+
+ private void doRegister()
+ {
+ if( getConnection().getState() != State.UNREGISTERED )
+ {
+ log.severe("AutoRegister: Tried to register but we are not UNREGISTERED");
+ return;
+ }
+
+ if( ! nickIterator.hasNext() )
+ {
+ log.info("AutoRegister: Failed to register.");
+ getConnection().disconnect();
+ return;
+ }
+
+ lastTryNick = (String)nickIterator.next();
+ ++attempt;
+ log.info("AutoRegister: Trying to register as " + lastTryNick);
+
+ if (pass != null) {
+ getConnection().sendCommand( new PassCommand( pass ));
+ }
+ getConnection().sendCommand( new NickCommand( lastTryNick ) );
+ getConnection().sendCommand( new UserCommand( user, name, getConnection() ) );
+ }
+
+ public String toString()
+ {
+ return "AutoRegister [" + attempt + "]";
+ }
+
+ // END AutoRegister
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoResponder.java b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoResponder.java
new file mode 100644
index 000000000..08f3a7f29
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoResponder.java
@@ -0,0 +1,82 @@
+package f00f.net.irc.martyr.services;
+
+import java.util.Observable;
+import java.util.Observer;
+
+import f00f.net.irc.martyr.IRCConnection;
+import f00f.net.irc.martyr.commands.ChannelModeCommand;
+import f00f.net.irc.martyr.commands.JoinCommand;
+import f00f.net.irc.martyr.commands.PingCommand;
+import f00f.net.irc.martyr.commands.PongCommand;
+
+/**
+ * AutoResponder is where commands that should be auto-responded (such
+ * as PING-PONG) should go.
+ */
+public class AutoResponder implements Observer
+{
+
+ private IRCConnection connection;
+ private boolean enabled = false;
+
+ public AutoResponder( IRCConnection connection )
+ {
+ this.connection = connection;
+ enable();
+ }
+
+ public void enable()
+ {
+ if( enabled )
+ return;
+
+ connection.addCommandObserver( this );
+ enabled = true;
+ }
+
+ public void disable()
+ {
+ if( !enabled )
+ return;
+
+ connection.removeCommandObserver( this );
+ enabled = false;
+ }
+
+ /**
+ * Does the work of figuring out what to respond to.
+ * If a PING is received, send a PONG. If we JOIN a channel, send a
+ * request for modes.
+ * */
+ public void update( Observable observer, Object updated )
+ {
+
+ if( updated instanceof PingCommand )
+ {
+ // We need to do some pongin'!
+ PingCommand ping = (PingCommand)updated;
+
+ String response = ping.getPingSource();
+
+ connection.sendCommand( new PongCommand( response ) );
+ }
+ else if( updated instanceof JoinCommand )
+ {
+ // Determine if we joined, and if we did, trigger a MODE discovery
+ // request.
+ JoinCommand join = (JoinCommand)updated;
+
+ if( join.weJoined( connection.getClientState() ) )
+ {
+ connection.sendCommand(
+ new ChannelModeCommand( join.getChannel() ) );
+ }
+ }
+ }
+
+ // END AutoResponder
+}
+
+
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/util/CtcpUtil.java b/EssentialsUpdate/src/f00f/net/irc/martyr/util/CtcpUtil.java
new file mode 100644
index 000000000..ed31c46e7
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/util/CtcpUtil.java
@@ -0,0 +1,99 @@
+package f00f.net.irc.martyr.util;
+
+import java.util.NoSuchElementException;
+
+public class CtcpUtil
+{
+ public static final char CTCP_TAG_DELIM = '\001';
+
+ /**
+ * Returns a new string ready for sending via MessageCommand.
+ *
+ * @param action Action string to create
+ * @return Action string ready for sending
+ */
+ public static String makeActionString( String action )
+ {
+ return makeCtcpString( "ACTION " + action );
+ }
+
+ public static String makeCtcpString( String s )
+ {
+ return "" + CTCP_TAG_DELIM + s + CTCP_TAG_DELIM;
+ }
+
+ /**
+ * Parses the string into tokens, where each token is either a
+ * CTCP escaped sequence or not.
+ */
+ public static class CtcpTokenizer
+ {
+ private String str;
+
+ public CtcpTokenizer( String in )
+ {
+ this.str = in;
+ }
+
+ public boolean isNextACtcp()
+ {
+ return str.charAt(0) == CTCP_TAG_DELIM;
+ }
+
+ public boolean hasNext()
+ {
+ return !str.equals("");
+ }
+
+ public String next()
+ {
+ return nextToken();
+ }
+ public String nextToken()
+ {
+ if( !hasNext() )
+ {
+ throw new NoSuchElementException();
+ }
+
+ int pos = str.indexOf( CTCP_TAG_DELIM, 1 );
+ String result;
+ if( isNextACtcp() )
+ {
+ if( pos < 0 )
+ {
+ // Error? Well, whatever, return the rest of the
+ // string.
+ result = str.substring( 1 );
+ str = "";
+ return result;
+ }
+ else
+ {
+ // ^Aour string^A(rest of string)
+ // Lose both ^A
+ result = str.substring( 1, pos );
+ str = str.substring( pos + 1 );
+ return result;
+ }
+ }
+ else
+ {
+ // Not a CTCP
+ if( pos < 0 )
+ {
+ result = str;
+ str = "";
+ return result;
+ }
+ else
+ {
+ result = str.substring( 0, pos );
+ str = str.substring( pos );
+ return result;
+ }
+ }
+ }
+ }
+}
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/util/FullNick.java b/EssentialsUpdate/src/f00f/net/irc/martyr/util/FullNick.java
new file mode 100644
index 000000000..c83cd98d4
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/util/FullNick.java
@@ -0,0 +1,159 @@
+package f00f.net.irc.martyr.util;
+
+import java.util.StringTokenizer;
+
+/**
+ * Parses out a full nick (ex: sork&lt;exclaimation&gt;sork&lt;at&gt;f00f.net) and stores it for
+ * use. It also provides a consistent hashing method.
+ */
+public class FullNick
+{
+
+ private String nick;
+ private String user;
+ private String remotehost;
+
+ private String original;
+
+ public FullNick( String original )
+ {
+ this.original = original;
+ parse( original );
+ }
+
+
+ /**
+ * It can't deal with parameters that have no '!'. When given a parameter with
+ * no '!', it simply places the entire string into the 'nick' field. FullNick
+ * is intended to be immutable.
+ *
+ * TODO: Should this enforce proper nick syntax?
+ * @param original Original nick we will parse
+ */
+ private void parse( String original )
+ {
+ if( original == null )
+ return;
+
+ StringTokenizer tokens = new StringTokenizer( original, "!", false );
+
+ nick = tokens.nextToken();
+
+ if( tokens.hasMoreTokens() )
+ {
+ user = tokens.nextToken("@");
+ if( user.charAt(0) == '!' )
+ user = user.substring(1);
+ }
+
+ if( tokens.hasMoreTokens() )
+ {
+ remotehost = tokens.nextToken("");
+ if( remotehost.charAt(0) == '@' )
+ remotehost = remotehost.substring(1);
+ }
+ }
+
+ public String getNick()
+ {
+ return nick;
+ }
+
+ public String getUser()
+ {
+ return user;
+ }
+
+ public String getHost()
+ {
+ return remotehost;
+ }
+
+ public String getSource()
+ {
+ //return nick+"!"+user+"@"+remotehost;
+ return original;
+ }
+
+
+ public int hashCode()
+ {
+ if( nick == null )
+ return 0;
+
+ return nick.hashCode();
+ }
+
+ /**
+ * Performs case insesitive equals on the nicks only. Does not strip
+ * off any leading @ or +. ({ == [ and ] == } and | == \) It appears
+ * that servers are not RFC complient on this, so we will not as well.
+ *
+ * @param nick Nick to compare this nick with
+ * @return True or false of nick is the same
+ */
+
+ public boolean equals( String nick )
+ {
+ if( nick == null )
+ return false;
+
+ return nick.equalsIgnoreCase( this.nick );
+ }
+
+ public boolean equals( FullNick nick )
+ {
+ if( nick == null )
+ return false;
+ return equals( nick.getNick() );
+ }
+
+ public boolean equals( Object object )
+ {
+ if( object instanceof FullNick )
+ return equals( (FullNick)object );
+ return false;
+ }
+
+ /**
+ * @return the nick part
+ * */
+ public String toString()
+ {
+ return nick;
+ }
+
+ /**
+ * Unit test.
+ *
+ * @param args Args passed to program
+ */
+ public static void main( String args[] )
+ {
+
+ FullNick nick = new FullNick( args[0] );
+
+ System.out.println( nick.getNick() );
+ System.out.println( nick.getUser() );
+ System.out.println( nick.getHost() );
+ System.out.println( nick.getSource() );
+
+ if( args.length > 1 )
+ {
+
+ FullNick nick2 = new FullNick( args[1] );
+
+ System.out.println( "" );
+ System.out.println( nick2.getNick() );
+ System.out.println( nick2.getUser() );
+ System.out.println( nick2.getHost() );
+ System.out.println( nick2.getSource() );
+
+ System.out.println( nick2.equals( nick ) ? "Equal." : "Not equal." );
+
+ }
+ }
+
+}
+
+
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/util/IRCStringUtils.java b/EssentialsUpdate/src/f00f/net/irc/martyr/util/IRCStringUtils.java
new file mode 100644
index 000000000..3b4fa6075
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/util/IRCStringUtils.java
@@ -0,0 +1,90 @@
+package f00f.net.irc.martyr.util;
+
+import java.text.CharacterIterator;
+import java.text.StringCharacterIterator;
+import java.util.ArrayList;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+/**
+ * @author Daniel Henninger
+ */
+public class IRCStringUtils
+{
+
+ /**
+ * Returns the message with all control characters stripped from it.
+ *
+ * @param msg Message to remove control chars from.
+ * @return Stripped form of message.
+ */
+ public static String stripControlChars(String msg)
+ {
+ Pattern pa = Pattern.compile("\u0003\\p{Digit}\\p{Digit}");
+ Matcher ma = pa.matcher(msg);
+ Pattern pb = Pattern.compile("\\p{Cntrl}");
+ Matcher mb = pb.matcher(ma.replaceAll(""));
+ return mb.replaceAll("");
+ }
+
+ /**
+ * Returns the message with all formatting characters converted into associated html characters.
+ *
+ * TODO: Should actually parse colors.
+ * @param msg Message to convert to HTML format.
+ * @return Message in HTML format.
+ */
+ public static String convertToHTML(String msg)
+ {
+ CharacterIterator ci = new StringCharacterIterator(msg);
+ String htmlStr = "";
+ ArrayList<String> formatList = new ArrayList<String>();
+ for (char c = ci.first(); c != CharacterIterator.DONE; c = ci.next()) {
+ if (c == '\u0002') {
+ if (formatList.contains("</b>")) {
+ formatList.remove("</b>");
+ htmlStr += "</b>";
+ }
+ else {
+ formatList.add("</b>");
+ htmlStr += "<b>";
+ }
+ }
+ else if (c == '\u001F') {
+ if (formatList.contains("</u>")) {
+ formatList.remove("</u>");
+ htmlStr += "</u>";
+ }
+ else {
+ formatList.add("</u>");
+ htmlStr += "<u>";
+ }
+ }
+ else if (c == '\u0016') {
+ if (formatList.contains("</i>")) {
+ formatList.remove("</i>");
+ htmlStr += "</i>";
+ }
+ else {
+ formatList.add("</i>");
+ htmlStr += "<i>";
+ }
+ }
+ else if (c == '\u000F' || c == '\u0015') {
+ for (String f : formatList) {
+ htmlStr += f;
+ }
+ formatList.clear();
+ }
+ else {
+ htmlStr += c;
+ }
+ }
+ for (String f : formatList) {
+ htmlStr += f;
+ }
+ formatList.clear();
+ return stripControlChars(htmlStr);
+ }
+
+}
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/util/ParameterIterator.java b/EssentialsUpdate/src/f00f/net/irc/martyr/util/ParameterIterator.java
new file mode 100644
index 000000000..d1e2c8507
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/util/ParameterIterator.java
@@ -0,0 +1,124 @@
+/*
+ * Original author: Ben Damm <bdamm@dammfine.com>
+ * Changes by: Mog
+ * - Fixed bug with substring handling
+ * */
+package f00f.net.irc.martyr.util;
+
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+//TODO: Unit test
+
+/**
+ * This class iterates over the parameter string of an IRC command,
+ * returning each parameter in order as next() is called. This class
+ * also knows about the ":" parameters, which is the large string at
+ * the end of most commands, and treats it specially.
+ */
+public class ParameterIterator implements Iterator
+{
+ //static Logger log = Logger.getLogger(ParameterIterator.class);
+
+ private String paramStr;
+ private int position;
+ private String last = null;
+
+ public ParameterIterator( String paramStr )
+ {
+ //log.debug("ParameterIterator: Params: `" + paramStr + "'");
+ // We don't check for null here because hasNext is the place
+ // to do it, according to the definition for Iterator.
+ // next() should throw an exception.
+ if( paramStr != null )
+ {
+ this.paramStr = paramStr.trim();
+ position = 0;
+ }
+ else
+ {
+ this.paramStr = null;
+ position = -1;
+ }
+ }
+
+ /**
+ * @return true if there are more parameters, and false
+ * otherwise.
+ */
+ public boolean hasNext()
+ {
+ if( paramStr == null )
+ return false;
+
+ return position < paramStr.length();
+ }
+
+ /**
+ * @throws NoSuchElementException if there are no more params
+ * @return true if the next parameter is also the ":" parameter.
+ * */
+ public boolean nextIsLast()
+ {
+ if( ! hasNext() )
+ {
+ throw new NoSuchElementException("No more parameters.");
+ }
+ return paramStr.charAt(position) == ':';
+ }
+
+ /**
+ * @throws NoSuchElementException if there are no more params
+ * */
+ public Object next()
+ {
+ if( ! hasNext() )
+ {
+ throw new NoSuchElementException("No more parameters.");
+ }
+
+ // If : is the first char, the rest of the string is a
+ // parameter.
+ if( paramStr.charAt(position) == ':' )
+ {
+ String result = paramStr.substring(position + 1);
+ position = paramStr.length();
+ last = result;
+ return result;
+ }
+
+ int spaceIndex = paramStr.indexOf( ' ', position );
+ // We can't have a space after the last parameter, it gets
+ // trimmed in the constructor. Also, we can't have only
+ // spaces, so we don't need to check for -1. Finally, we are
+ // guaranteed to have a space before the colon, so we don't
+ // have to do any checking at all!
+
+ String result = paramStr.substring( position, spaceIndex );
+ position = spaceIndex + 1;
+ return result;
+ }
+
+ /**
+ * Forwards the iterator to the last element and returns it. The
+ * "last" parameter should be the ":" parameter.
+ *
+ * @return Last parameter
+ * */
+ public String last()
+ {
+ while( hasNext() )
+ next();
+
+ return last;
+ }
+
+ public void remove()
+ {
+ // hmm, nah. This can be implemented some other time.
+ throw new UnsupportedOperationException( "Remove on the parameters? Why?" );
+ }
+}
+
+