summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsnowleo <schneeleo@gmail.com>2011-10-12 03:14:07 +0200
committersnowleo <schneeleo@gmail.com>2011-10-12 03:14:26 +0200
commit860d446d28776ec842fa53e8e08538d4e093d6e9 (patch)
tree0c4598eae4eb8c59fd36e8312eab1b27a8018794
parent9ec398b39b0f48392a9d635041b392c7dba2ca0c (diff)
downloadEssentials-860d446d28776ec842fa53e8e08538d4e093d6e9.tar
Essentials-860d446d28776ec842fa53e8e08538d4e093d6e9.tar.gz
Essentials-860d446d28776ec842fa53e8e08538d4e093d6e9.tar.lz
Essentials-860d446d28776ec842fa53e8e08538d4e093d6e9.tar.xz
Essentials-860d446d28776ec842fa53e8e08538d4e093d6e9.zip
EssentialsUpdate WIP
-rw-r--r--.gitignore3
-rw-r--r--EssentialsUpdate/build.xml74
-rw-r--r--EssentialsUpdate/manifest.mf3
-rw-r--r--EssentialsUpdate/nbproject/build-impl.xml1067
-rw-r--r--EssentialsUpdate/nbproject/genfiles.properties8
-rw-r--r--EssentialsUpdate/nbproject/project.properties75
-rw-r--r--EssentialsUpdate/nbproject/project.xml18
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsHelp.java601
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsUpdate.java66
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java112
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/ModuleInfo.java35
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/PastieUpload.java40
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/PostToUrl.java66
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/UpdateCheck.java203
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/UpdateFile.java204
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java128
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java19
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/Version.java173
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java48
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java7
-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
-rw-r--r--EssentialsUpdate/src/plugin.yml21
-rw-r--r--EssentialsUpdate/test/com/earth2me/essentials/update/UploadTest.java27
-rw-r--r--EssentialsUpdate/test/com/earth2me/essentials/update/VersionTest.java87
169 files changed, 13835 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 4d5963e6b..763785783 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,4 +32,5 @@
/YamlAnnotations/
/EssentialsUpdate/nbproject/private/
/EssentialsRelease/
-/EssentialsUpdate/ \ No newline at end of file
+/EssentialsUpdate/dist/
+/EssentialsUpdate/build/ \ No newline at end of file
diff --git a/EssentialsUpdate/build.xml b/EssentialsUpdate/build.xml
new file mode 100644
index 000000000..dedb62afa
--- /dev/null
+++ b/EssentialsUpdate/build.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See commented blocks below for -->
+<!-- some examples of how to customize the build. -->
+<!-- (If you delete it and reopen the project it will be recreated.) -->
+<!-- By default, only the Clean and Build commands use this build script. -->
+<!-- Commands such as Run, Debug, and Test only use this build script if -->
+<!-- the Compile on Save feature is turned off for the project. -->
+<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
+<!-- in the project's Project Properties dialog box.-->
+<project name="EssentialsUpdate" default="default" basedir=".">
+ <description>Builds, tests, and runs the project EssentialsUpdate.</description>
+ <import file="nbproject/build-impl.xml"/>
+ <!--
+
+ There exist several targets which are by default empty and which can be
+ used for execution of your tasks. These targets are usually executed
+ before and after some main targets. They are:
+
+ -pre-init: called before initialization of project properties
+ -post-init: called after initialization of project properties
+ -pre-compile: called before javac compilation
+ -post-compile: called after javac compilation
+ -pre-compile-single: called before javac compilation of single file
+ -post-compile-single: called after javac compilation of single file
+ -pre-compile-test: called before javac compilation of JUnit tests
+ -post-compile-test: called after javac compilation of JUnit tests
+ -pre-compile-test-single: called before javac compilation of single JUnit test
+ -post-compile-test-single: called after javac compilation of single JUunit test
+ -pre-jar: called before JAR building
+ -post-jar: called after JAR building
+ -post-clean: called after cleaning build products
+
+ (Targets beginning with '-' are not intended to be called on their own.)
+
+ Example of inserting an obfuscator after compilation could look like this:
+
+ <target name="-post-compile">
+ <obfuscate>
+ <fileset dir="${build.classes.dir}"/>
+ </obfuscate>
+ </target>
+
+ For list of available properties check the imported
+ nbproject/build-impl.xml file.
+
+
+ Another way to customize the build is by overriding existing main targets.
+ The targets of interest are:
+
+ -init-macrodef-javac: defines macro for javac compilation
+ -init-macrodef-junit: defines macro for junit execution
+ -init-macrodef-debug: defines macro for class debugging
+ -init-macrodef-java: defines macro for class execution
+ -do-jar-with-manifest: JAR building (if you are using a manifest)
+ -do-jar-without-manifest: JAR building (if you are not using a manifest)
+ run: execution of project
+ -javadoc-build: Javadoc generation
+ test-report: JUnit report generation
+
+ An example of overriding the target for project execution could look like this:
+
+ <target name="run" depends="EssentialsUpdate-impl.jar">
+ <exec dir="bin" executable="launcher.exe">
+ <arg file="${dist.jar}"/>
+ </exec>
+ </target>
+
+ Notice that the overridden target depends on the jar target and not only on
+ the compile target as the regular run target does. Again, for a list of available
+ properties which you can use, check the target you are overriding in the
+ nbproject/build-impl.xml file.
+
+ -->
+</project>
diff --git a/EssentialsUpdate/manifest.mf b/EssentialsUpdate/manifest.mf
new file mode 100644
index 000000000..328e8e5bc
--- /dev/null
+++ b/EssentialsUpdate/manifest.mf
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
diff --git a/EssentialsUpdate/nbproject/build-impl.xml b/EssentialsUpdate/nbproject/build-impl.xml
new file mode 100644
index 000000000..7a38ac1d4
--- /dev/null
+++ b/EssentialsUpdate/nbproject/build-impl.xml
@@ -0,0 +1,1067 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+*** GENERATED FROM project.xml - DO NOT EDIT ***
+*** EDIT ../build.xml INSTEAD ***
+
+For the purpose of easier reading the script
+is divided into following sections:
+
+ - initialization
+ - compilation
+ - jar
+ - execution
+ - debugging
+ - javadoc
+ - junit compilation
+ - junit execution
+ - junit debugging
+ - applet
+ - cleanup
+
+ -->
+<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="EssentialsUpdate-impl">
+ <fail message="Please build using Ant 1.7.1 or higher.">
+ <condition>
+ <not>
+ <antversion atleast="1.7.1"/>
+ </not>
+ </condition>
+ </fail>
+ <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
+ <!--
+ ======================
+ INITIALIZATION SECTION
+ ======================
+ -->
+ <target name="-pre-init">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="-pre-init" name="-init-private">
+ <property file="nbproject/private/config.properties"/>
+ <property file="nbproject/private/configs/${config}.properties"/>
+ <property file="nbproject/private/private.properties"/>
+ </target>
+ <target name="-pre-init-libraries">
+ <property location="../lib/nblibraries.properties" name="libraries.path"/>
+ <dirname file="${libraries.path}" property="libraries.dir.nativedirsep"/>
+ <pathconvert dirsep="/" property="libraries.dir">
+ <path path="${libraries.dir.nativedirsep}"/>
+ </pathconvert>
+ <basename file="${libraries.path}" property="libraries.basename" suffix=".properties"/>
+ <available file="${libraries.dir}/${libraries.basename}-private.properties" property="private.properties.available"/>
+ </target>
+ <target depends="-pre-init-libraries" if="private.properties.available" name="-init-private-libraries">
+ <loadproperties encoding="ISO-8859-1" srcfile="${libraries.dir}/${libraries.basename}-private.properties">
+ <filterchain>
+ <replacestring from="$${base}" to="${libraries.dir}"/>
+ <escapeunicode/>
+ </filterchain>
+ </loadproperties>
+ </target>
+ <target depends="-pre-init,-init-private,-init-private-libraries" name="-init-libraries">
+ <loadproperties encoding="ISO-8859-1" srcfile="${libraries.path}">
+ <filterchain>
+ <replacestring from="$${base}" to="${libraries.dir}"/>
+ <escapeunicode/>
+ </filterchain>
+ </loadproperties>
+ </target>
+ <target depends="-pre-init,-init-private,-init-libraries" name="-init-user">
+ <property file="${user.properties.file}"/>
+ <!-- The two properties below are usually overridden -->
+ <!-- by the active platform. Just a fallback. -->
+ <property name="default.javac.source" value="1.4"/>
+ <property name="default.javac.target" value="1.4"/>
+ </target>
+ <target depends="-pre-init,-init-private,-init-libraries,-init-user" name="-init-project">
+ <property file="nbproject/configs/${config}.properties"/>
+ <property file="nbproject/project.properties"/>
+ </target>
+ <target depends="-pre-init,-init-private,-init-libraries,-init-user,-init-project,-init-macrodef-property" name="-do-init">
+ <available file="${manifest.file}" property="manifest.available"/>
+ <condition property="splashscreen.available">
+ <and>
+ <not>
+ <equals arg1="${application.splash}" arg2="" trim="true"/>
+ </not>
+ <available file="${application.splash}"/>
+ </and>
+ </condition>
+ <condition property="main.class.available">
+ <and>
+ <isset property="main.class"/>
+ <not>
+ <equals arg1="${main.class}" arg2="" trim="true"/>
+ </not>
+ </and>
+ </condition>
+ <condition property="manifest.available+main.class">
+ <and>
+ <isset property="manifest.available"/>
+ <isset property="main.class.available"/>
+ </and>
+ </condition>
+ <condition property="do.archive">
+ <not>
+ <istrue value="${jar.archive.disabled}"/>
+ </not>
+ </condition>
+ <condition property="do.mkdist">
+ <and>
+ <isset property="do.archive"/>
+ <isset property="libs.CopyLibs.classpath"/>
+ <not>
+ <istrue value="${mkdist.disabled}"/>
+ </not>
+ </and>
+ </condition>
+ <condition property="manifest.available+main.class+mkdist.available">
+ <and>
+ <istrue value="${manifest.available+main.class}"/>
+ <isset property="do.mkdist"/>
+ </and>
+ </condition>
+ <condition property="do.archive+manifest.available">
+ <and>
+ <isset property="manifest.available"/>
+ <istrue value="${do.archive}"/>
+ </and>
+ </condition>
+ <condition property="do.archive+main.class.available">
+ <and>
+ <isset property="main.class.available"/>
+ <istrue value="${do.archive}"/>
+ </and>
+ </condition>
+ <condition property="do.archive+splashscreen.available">
+ <and>
+ <isset property="splashscreen.available"/>
+ <istrue value="${do.archive}"/>
+ </and>
+ </condition>
+ <condition property="do.archive+manifest.available+main.class">
+ <and>
+ <istrue value="${manifest.available+main.class}"/>
+ <istrue value="${do.archive}"/>
+ </and>
+ </condition>
+ <condition property="manifest.available-mkdist.available">
+ <or>
+ <istrue value="${manifest.available}"/>
+ <isset property="do.mkdist"/>
+ </or>
+ </condition>
+ <condition property="manifest.available+main.class-mkdist.available">
+ <or>
+ <istrue value="${manifest.available+main.class}"/>
+ <isset property="do.mkdist"/>
+ </or>
+ </condition>
+ <condition property="have.tests">
+ <or>
+ <available file="${test.src.dir}"/>
+ </or>
+ </condition>
+ <condition property="have.sources">
+ <or>
+ <available file="${src.dir}"/>
+ </or>
+ </condition>
+ <condition property="netbeans.home+have.tests">
+ <and>
+ <isset property="netbeans.home"/>
+ <isset property="have.tests"/>
+ </and>
+ </condition>
+ <condition property="no.javadoc.preview">
+ <and>
+ <isset property="javadoc.preview"/>
+ <isfalse value="${javadoc.preview}"/>
+ </and>
+ </condition>
+ <property name="run.jvmargs" value=""/>
+ <property name="javac.compilerargs" value=""/>
+ <property name="work.dir" value="${basedir}"/>
+ <condition property="no.deps">
+ <and>
+ <istrue value="${no.dependencies}"/>
+ </and>
+ </condition>
+ <property name="javac.debug" value="true"/>
+ <property name="javadoc.preview" value="true"/>
+ <property name="application.args" value=""/>
+ <property name="source.encoding" value="${file.encoding}"/>
+ <property name="runtime.encoding" value="${source.encoding}"/>
+ <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
+ <and>
+ <isset property="javadoc.encoding"/>
+ <not>
+ <equals arg1="${javadoc.encoding}" arg2=""/>
+ </not>
+ </and>
+ </condition>
+ <property name="javadoc.encoding.used" value="${source.encoding}"/>
+ <property name="includes" value="**"/>
+ <property name="excludes" value=""/>
+ <property name="do.depend" value="false"/>
+ <condition property="do.depend.true">
+ <istrue value="${do.depend}"/>
+ </condition>
+ <path id="endorsed.classpath.path" path="${endorsed.classpath}"/>
+ <condition else="" property="endorsed.classpath.cmd.line.arg" value="-Xbootclasspath/p:'${toString:endorsed.classpath.path}'">
+ <length length="0" string="${endorsed.classpath}" when="greater"/>
+ </condition>
+ <condition else="false" property="jdkBug6558476">
+ <and>
+ <matches pattern="1\.[56]" string="${java.specification.version}"/>
+ <not>
+ <os family="unix"/>
+ </not>
+ </and>
+ </condition>
+ <property name="javac.fork" value="${jdkBug6558476}"/>
+ <property name="jar.index" value="false"/>
+ <property name="jar.index.metainf" value="${jar.index}"/>
+ <available file="${meta.inf.dir}/persistence.xml" property="has.persistence.xml"/>
+ </target>
+ <target name="-post-init">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="-pre-init,-init-private,-init-libraries,-init-user,-init-project,-do-init" name="-init-check">
+ <fail unless="src.dir">Must set src.dir</fail>
+ <fail unless="test.src.dir">Must set test.src.dir</fail>
+ <fail unless="build.dir">Must set build.dir</fail>
+ <fail unless="dist.dir">Must set dist.dir</fail>
+ <fail unless="build.classes.dir">Must set build.classes.dir</fail>
+ <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
+ <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
+ <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
+ <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
+ <fail unless="dist.jar">Must set dist.jar</fail>
+ </target>
+ <target name="-init-macrodef-property">
+ <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute name="name"/>
+ <attribute name="value"/>
+ <sequential>
+ <property name="@{name}" value="${@{value}}"/>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-macrodef-javac-with-processors">
+ <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${src.dir}" name="srcdir"/>
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <attribute default="${javac.classpath}" name="classpath"/>
+ <attribute default="${javac.processorpath}" name="processorpath"/>
+ <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="${javac.debug}" name="debug"/>
+ <attribute default="${empty.dir}" name="sourcepath"/>
+ <attribute default="${empty.dir}" name="gensrcdir"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property location="${build.dir}/empty" name="empty.dir"/>
+ <mkdir dir="${empty.dir}"/>
+ <mkdir dir="@{apgeneratedsrcdir}"/>
+ <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
+ <src>
+ <dirset dir="@{gensrcdir}" erroronmissingdir="false">
+ <include name="*"/>
+ </dirset>
+ </src>
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <compilerarg line="${javac.compilerargs}"/>
+ <compilerarg value="-processorpath"/>
+ <compilerarg path="@{processorpath}:${empty.dir}"/>
+ <compilerarg line="${ap.processors.internal}"/>
+ <compilerarg line="${annotation.processing.processor.options}"/>
+ <compilerarg value="-s"/>
+ <compilerarg path="@{apgeneratedsrcdir}"/>
+ <compilerarg line="${ap.proc.none.internal}"/>
+ <customize/>
+ </javac>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-ap-cmdline-properties" name="-init-macrodef-javac-without-processors" unless="ap.supported.internal">
+ <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${src.dir}" name="srcdir"/>
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <attribute default="${javac.classpath}" name="classpath"/>
+ <attribute default="${javac.processorpath}" name="processorpath"/>
+ <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="${javac.debug}" name="debug"/>
+ <attribute default="${empty.dir}" name="sourcepath"/>
+ <attribute default="${empty.dir}" name="gensrcdir"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property location="${build.dir}/empty" name="empty.dir"/>
+ <mkdir dir="${empty.dir}"/>
+ <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
+ <src>
+ <dirset dir="@{gensrcdir}" erroronmissingdir="false">
+ <include name="*"/>
+ </dirset>
+ </src>
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <compilerarg line="${javac.compilerargs}"/>
+ <customize/>
+ </javac>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-macrodef-javac-with-processors,-init-macrodef-javac-without-processors" name="-init-macrodef-javac">
+ <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${src.dir}" name="srcdir"/>
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <attribute default="${javac.classpath}" name="classpath"/>
+ <sequential>
+ <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ </depend>
+ </sequential>
+ </macrodef>
+ <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <sequential>
+ <fail unless="javac.includes">Must set javac.includes</fail>
+ <pathconvert pathsep="${line.separator}" property="javac.includes.binary">
+ <path>
+ <filelist dir="@{destdir}" files="${javac.includes}"/>
+ </path>
+ <globmapper from="*.java" to="*.class"/>
+ </pathconvert>
+ <tempfile deleteonexit="true" property="javac.includesfile.binary"/>
+ <echo file="${javac.includesfile.binary}" message="${javac.includes.binary}"/>
+ <delete>
+ <files includesfile="${javac.includesfile.binary}"/>
+ </delete>
+ <delete>
+ <fileset file="${javac.includesfile.binary}"/>
+ </delete>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-macrodef-junit">
+ <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <sequential>
+ <property name="junit.forkmode" value="perTest"/>
+ <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+ <batchtest todir="${build.test.results.dir}">
+ <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
+ <filename name="@{testincludes}"/>
+ </fileset>
+ </batchtest>
+ <classpath>
+ <path path="${run.test.classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <formatter type="brief" usefile="false"/>
+ <formatter type="xml"/>
+ <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <jvmarg value="-ea"/>
+ <jvmarg line="${run.jvmargs}"/>
+ </junit>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile, -profile-init-check" name="profile-init"/>
+ <target name="-profile-pre-init">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target name="-profile-post-init">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target name="-profile-init-macrodef-profile">
+ <macrodef name="resolve">
+ <attribute name="name"/>
+ <attribute name="value"/>
+ <sequential>
+ <property name="@{name}" value="${env.@{value}}"/>
+ </sequential>
+ </macrodef>
+ <macrodef name="profile">
+ <attribute default="${main.class}" name="classname"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property environment="env"/>
+ <resolve name="profiler.current.path" value="${profiler.info.pathvar}"/>
+ <java classname="@{classname}" dir="${profiler.info.dir}" fork="true" jvm="${profiler.info.jvm}">
+ <jvmarg value="${profiler.info.jvmargs.agent}"/>
+ <jvmarg line="${profiler.info.jvmargs}"/>
+ <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
+ <arg line="${application.args}"/>
+ <classpath>
+ <path path="${run.classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="run-sys-prop."/>
+ <mapper from="run-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <customize/>
+ </java>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile" name="-profile-init-check">
+ <fail unless="profiler.info.jvm">Must set JVM to use for profiling in profiler.info.jvm</fail>
+ <fail unless="profiler.info.jvmargs.agent">Must set profiler agent JVM arguments in profiler.info.jvmargs.agent</fail>
+ </target>
+ <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
+ <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute default="${main.class}" name="name"/>
+ <attribute default="${debug.classpath}" name="classpath"/>
+ <attribute default="" name="stopclassname"/>
+ <sequential>
+ <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ </nbjpdastart>
+ </sequential>
+ </macrodef>
+ <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute default="${build.classes.dir}" name="dir"/>
+ <sequential>
+ <nbjpdareload>
+ <fileset dir="@{dir}" includes="${fix.classes}">
+ <include name="${fix.includes}*.class"/>
+ </fileset>
+ </nbjpdareload>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-debug-args">
+ <property name="version-output" value="java version &quot;${ant.java.version}"/>
+ <condition property="have-jdk-older-than-1.4">
+ <or>
+ <contains string="${version-output}" substring="java version &quot;1.0"/>
+ <contains string="${version-output}" substring="java version &quot;1.1"/>
+ <contains string="${version-output}" substring="java version &quot;1.2"/>
+ <contains string="${version-output}" substring="java version &quot;1.3"/>
+ </or>
+ </condition>
+ <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
+ <istrue value="${have-jdk-older-than-1.4}"/>
+ </condition>
+ <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
+ <os family="windows"/>
+ </condition>
+ <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
+ <isset property="debug.transport"/>
+ </condition>
+ </target>
+ <target depends="-init-debug-args" name="-init-macrodef-debug">
+ <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${main.class}" name="classname"/>
+ <attribute default="${debug.classpath}" name="classpath"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <java classname="@{classname}" dir="${work.dir}" fork="true">
+ <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <jvmarg line="${debug-args-line}"/>
+ <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
+ <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
+ <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
+ <jvmarg line="${run.jvmargs}"/>
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="run-sys-prop."/>
+ <mapper from="run-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <customize/>
+ </java>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-macrodef-java">
+ <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute default="${main.class}" name="classname"/>
+ <attribute default="${run.classpath}" name="classpath"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <java classname="@{classname}" dir="${work.dir}" fork="true">
+ <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
+ <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
+ <jvmarg line="${run.jvmargs}"/>
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="run-sys-prop."/>
+ <mapper from="run-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <customize/>
+ </java>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-macrodef-copylibs">
+ <macrodef name="copylibs" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${manifest.file}" name="manifest"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+ <pathconvert property="run.classpath.without.build.classes.dir">
+ <path path="${run.classpath}"/>
+ <map from="${build.classes.dir.resolved}" to=""/>
+ </pathconvert>
+ <pathconvert pathsep=" " property="jar.classpath">
+ <path path="${run.classpath.without.build.classes.dir}"/>
+ <chainedmapper>
+ <flattenmapper/>
+ <globmapper from="*" to="lib/*"/>
+ </chainedmapper>
+ </pathconvert>
+ <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
+ <copylibs compress="${jar.compress}" index="${jar.index}" indexMetaInf="${jar.index.metainf}" jarfile="${dist.jar}" manifest="@{manifest}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
+ <fileset dir="${build.classes.dir}"/>
+ <manifest>
+ <attribute name="Class-Path" value="${jar.classpath}"/>
+ <customize/>
+ </manifest>
+ </copylibs>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-presetdef-jar">
+ <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <jar compress="${jar.compress}" index="${jar.index}" jarfile="${dist.jar}">
+ <j2seproject1:fileset dir="${build.classes.dir}"/>
+ </jar>
+ </presetdef>
+ </target>
+ <target name="-init-ap-cmdline-properties">
+ <property name="annotation.processing.enabled" value="true"/>
+ <property name="annotation.processing.processors.list" value=""/>
+ <property name="annotation.processing.processor.options" value=""/>
+ <property name="annotation.processing.run.all.processors" value="true"/>
+ <property name="javac.processorpath" value="${javac.classpath}"/>
+ <property name="javac.test.processorpath" value="${javac.test.classpath}"/>
+ <condition property="ap.supported.internal" value="true">
+ <not>
+ <matches pattern="1\.[0-5](\..*)?" string="${javac.source}"/>
+ </not>
+ </condition>
+ </target>
+ <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-ap-cmdline-supported">
+ <condition else="" property="ap.processors.internal" value="-processor ${annotation.processing.processors.list}">
+ <isfalse value="${annotation.processing.run.all.processors}"/>
+ </condition>
+ <condition else="" property="ap.proc.none.internal" value="-proc:none">
+ <isfalse value="${annotation.processing.enabled}"/>
+ </condition>
+ </target>
+ <target depends="-init-ap-cmdline-properties,-init-ap-cmdline-supported" name="-init-ap-cmdline">
+ <property name="ap.cmd.line.internal" value=""/>
+ </target>
+ <target depends="-pre-init,-init-private,-init-libraries,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar,-init-ap-cmdline" name="init"/>
+ <!--
+ ===================
+ COMPILATION SECTION
+ ===================
+ -->
+ <target name="-deps-jar-init" unless="built-jar.properties">
+ <property location="${build.dir}/built-jar.properties" name="built-jar.properties"/>
+ <delete file="${built-jar.properties}" quiet="true"/>
+ </target>
+ <target if="already.built.jar.${basedir}" name="-warn-already-built-jar">
+ <echo level="warn" message="Cycle detected: EssentialsUpdate was already built"/>
+ </target>
+ <target depends="init,-deps-jar-init" name="deps-jar" unless="no.deps">
+ <mkdir dir="${build.dir}"/>
+ <touch file="${built-jar.properties}" verbose="false"/>
+ <property file="${built-jar.properties}" prefix="already.built.jar."/>
+ <antcall target="-warn-already-built-jar"/>
+ <propertyfile file="${built-jar.properties}">
+ <entry key="${basedir}" value=""/>
+ </propertyfile>
+ </target>
+ <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
+ <target depends="init" name="-check-automatic-build">
+ <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
+ </target>
+ <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
+ <antcall target="clean"/>
+ </target>
+ <target depends="init,deps-jar" name="-pre-pre-compile">
+ <mkdir dir="${build.classes.dir}"/>
+ </target>
+ <target name="-pre-compile">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target if="do.depend.true" name="-compile-depend">
+ <pathconvert property="build.generated.subdirs">
+ <dirset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+ <include name="*"/>
+ </dirset>
+ </pathconvert>
+ <j2seproject3:depend srcdir="${src.dir}:${build.generated.subdirs}"/>
+ </target>
+ <target depends="init,deps-jar,-pre-pre-compile,-pre-compile, -copy-persistence-xml,-compile-depend" if="have.sources" name="-do-compile">
+ <j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/>
+ <copy todir="${build.classes.dir}">
+ <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+ </copy>
+ </target>
+ <target if="has.persistence.xml" name="-copy-persistence-xml">
+ <mkdir dir="${build.classes.dir}/META-INF"/>
+ <copy todir="${build.classes.dir}/META-INF">
+ <fileset dir="${meta.inf.dir}" includes="persistence.xml"/>
+ </copy>
+ </target>
+ <target name="-post-compile">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
+ <target name="-pre-compile-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
+ <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+ <j2seproject3:force-recompile/>
+ <j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.dir}"/>
+ </target>
+ <target name="-post-compile-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
+ <!--
+ ====================
+ JAR BUILDING SECTION
+ ====================
+ -->
+ <target depends="init" name="-pre-pre-jar">
+ <dirname file="${dist.jar}" property="dist.jar.dir"/>
+ <mkdir dir="${dist.jar.dir}"/>
+ </target>
+ <target name="-pre-jar">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive" name="-do-jar-without-manifest" unless="manifest.available-mkdist.available">
+ <j2seproject1:jar/>
+ </target>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive+manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class-mkdist.available">
+ <j2seproject1:jar manifest="${manifest.file}"/>
+ </target>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive+manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
+ <j2seproject1:jar manifest="${manifest.file}">
+ <j2seproject1:manifest>
+ <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
+ </j2seproject1:manifest>
+ </j2seproject1:jar>
+ <echo level="info">To run this application from the command line without Ant, try:</echo>
+ <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+ <property location="${dist.jar}" name="dist.jar.resolved"/>
+ <pathconvert property="run.classpath.with.dist.jar">
+ <path path="${run.classpath}"/>
+ <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
+ </pathconvert>
+ <echo level="info">java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
+ </target>
+ <target depends="init" if="do.archive" name="-do-jar-with-libraries-create-manifest" unless="manifest.available">
+ <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/>
+ <touch file="${tmp.manifest.file}" verbose="false"/>
+ </target>
+ <target depends="init" if="do.archive+manifest.available" name="-do-jar-with-libraries-copy-manifest">
+ <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/>
+ <copy file="${manifest.file}" tofile="${tmp.manifest.file}"/>
+ </target>
+ <target depends="init,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest" if="do.archive+main.class.available" name="-do-jar-with-libraries-set-main">
+ <manifest file="${tmp.manifest.file}" mode="update">
+ <attribute name="Main-Class" value="${main.class}"/>
+ </manifest>
+ </target>
+ <target depends="init,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest" if="do.archive+splashscreen.available" name="-do-jar-with-libraries-set-splashscreen">
+ <basename file="${application.splash}" property="splashscreen.basename"/>
+ <mkdir dir="${build.classes.dir}/META-INF"/>
+ <copy failonerror="false" file="${application.splash}" todir="${build.classes.dir}/META-INF"/>
+ <manifest file="${tmp.manifest.file}" mode="update">
+ <attribute name="SplashScreen-Image" value="META-INF/${splashscreen.basename}"/>
+ </manifest>
+ </target>
+ <target depends="init,-init-macrodef-copylibs,compile,-pre-pre-jar,-pre-jar,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest,-do-jar-with-libraries-set-main,-do-jar-with-libraries-set-splashscreen" if="do.mkdist" name="-do-jar-with-libraries-pack">
+ <j2seproject3:copylibs manifest="${tmp.manifest.file}"/>
+ <echo level="info">To run this application from the command line without Ant, try:</echo>
+ <property location="${dist.jar}" name="dist.jar.resolved"/>
+ <echo level="info">java -jar "${dist.jar.resolved}"</echo>
+ </target>
+ <target depends="-do-jar-with-libraries-pack" if="do.archive" name="-do-jar-with-libraries-delete-manifest">
+ <delete>
+ <fileset file="${tmp.manifest.file}"/>
+ </delete>
+ </target>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest,-do-jar-with-libraries-set-main,-do-jar-with-libraries-set-splashscreen,-do-jar-with-libraries-pack,-do-jar-with-libraries-delete-manifest" name="-do-jar-with-libraries"/>
+ <target name="-post-jar">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
+ <!--
+ =================
+ EXECUTION SECTION
+ =================
+ -->
+ <target depends="init,compile" description="Run a main class." name="run">
+ <j2seproject1:java>
+ <customize>
+ <arg line="${application.args}"/>
+ </customize>
+ </j2seproject1:java>
+ </target>
+ <target name="-do-not-recompile">
+ <property name="javac.includes.binary" value=""/>
+ </target>
+ <target depends="init,compile-single" name="run-single">
+ <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+ <j2seproject1:java classname="${run.class}"/>
+ </target>
+ <target depends="init,compile-test-single" name="run-test-with-main">
+ <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+ <j2seproject1:java classname="${run.class}" classpath="${run.test.classpath}"/>
+ </target>
+ <!--
+ =================
+ DEBUGGING SECTION
+ =================
+ -->
+ <target depends="init" if="netbeans.home" name="-debug-start-debugger">
+ <j2seproject1:nbjpdastart name="${debug.class}"/>
+ </target>
+ <target depends="init" if="netbeans.home" name="-debug-start-debugger-main-test">
+ <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${debug.class}"/>
+ </target>
+ <target depends="init,compile" name="-debug-start-debuggee">
+ <j2seproject3:debug>
+ <customize>
+ <arg line="${application.args}"/>
+ </customize>
+ </j2seproject3:debug>
+ </target>
+ <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
+ <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
+ <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
+ </target>
+ <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
+ <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
+ <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+ <j2seproject3:debug classname="${debug.class}"/>
+ </target>
+ <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
+ <target depends="init,compile-test-single" if="netbeans.home" name="-debug-start-debuggee-main-test">
+ <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+ <j2seproject3:debug classname="${debug.class}" classpath="${debug.test.classpath}"/>
+ </target>
+ <target depends="init,compile-test-single,-debug-start-debugger-main-test,-debug-start-debuggee-main-test" if="netbeans.home" name="debug-test-with-main"/>
+ <target depends="init" name="-pre-debug-fix">
+ <fail unless="fix.includes">Must set fix.includes</fail>
+ <property name="javac.includes" value="${fix.includes}.java"/>
+ </target>
+ <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
+ <j2seproject1:nbjpdareload/>
+ </target>
+ <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
+ <!--
+ =================
+ PROFILING SECTION
+ =================
+ -->
+ <target depends="profile-init,compile" description="Profile a project in the IDE." if="netbeans.home" name="profile">
+ <nbprofiledirect>
+ <classpath>
+ <path path="${run.classpath}"/>
+ </classpath>
+ </nbprofiledirect>
+ <profile/>
+ </target>
+ <target depends="profile-init,compile-single" description="Profile a selected class in the IDE." if="netbeans.home" name="profile-single">
+ <fail unless="profile.class">Must select one file in the IDE or set profile.class</fail>
+ <nbprofiledirect>
+ <classpath>
+ <path path="${run.classpath}"/>
+ </classpath>
+ </nbprofiledirect>
+ <profile classname="${profile.class}"/>
+ </target>
+ <!--
+ =========================
+ APPLET PROFILING SECTION
+ =========================
+ -->
+ <target depends="profile-init,compile-single" if="netbeans.home" name="profile-applet">
+ <nbprofiledirect>
+ <classpath>
+ <path path="${run.classpath}"/>
+ </classpath>
+ </nbprofiledirect>
+ <profile classname="sun.applet.AppletViewer">
+ <customize>
+ <arg value="${applet.url}"/>
+ </customize>
+ </profile>
+ </target>
+ <!--
+ =========================
+ TESTS PROFILING SECTION
+ =========================
+ -->
+ <target depends="profile-init,compile-test-single" if="netbeans.home" name="profile-test-single">
+ <nbprofiledirect>
+ <classpath>
+ <path path="${run.test.classpath}"/>
+ </classpath>
+ </nbprofiledirect>
+ <junit dir="${profiler.info.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" jvm="${profiler.info.jvm}" showoutput="true">
+ <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
+ <jvmarg value="${profiler.info.jvmargs.agent}"/>
+ <jvmarg line="${profiler.info.jvmargs}"/>
+ <test name="${profile.class}"/>
+ <classpath>
+ <path path="${run.test.classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <formatter type="brief" usefile="false"/>
+ <formatter type="xml"/>
+ </junit>
+ </target>
+ <!--
+ ===============
+ JAVADOC SECTION
+ ===============
+ -->
+ <target depends="init" if="have.sources" name="-javadoc-build">
+ <mkdir dir="${dist.javadoc.dir}"/>
+ <javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
+ <classpath>
+ <path path="${javac.classpath}"/>
+ </classpath>
+ <fileset dir="${src.dir}" excludes="*.java,${excludes}" includes="${includes}">
+ <filename name="**/*.java"/>
+ </fileset>
+ <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+ <include name="**/*.java"/>
+ <exclude name="*.java"/>
+ </fileset>
+ </javadoc>
+ <copy todir="${dist.javadoc.dir}">
+ <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
+ <filename name="**/doc-files/**"/>
+ </fileset>
+ <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+ <include name="**/doc-files/**"/>
+ </fileset>
+ </copy>
+ </target>
+ <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
+ <nbbrowse file="${dist.javadoc.dir}/index.html"/>
+ </target>
+ <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
+ <!--
+ =========================
+ JUNIT COMPILATION SECTION
+ =========================
+ -->
+ <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
+ <mkdir dir="${build.test.classes.dir}"/>
+ </target>
+ <target name="-pre-compile-test">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target if="do.depend.true" name="-compile-test-depend">
+ <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
+ </target>
+ <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
+ <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" processorpath="${javac.test.processorpath}" srcdir="${test.src.dir}"/>
+ <copy todir="${build.test.classes.dir}">
+ <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+ </copy>
+ </target>
+ <target name="-post-compile-test">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
+ <target name="-pre-compile-test-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
+ <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+ <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
+ <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" processorpath="${javac.test.processorpath}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
+ <copy todir="${build.test.classes.dir}">
+ <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+ </copy>
+ </target>
+ <target name="-post-compile-test-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
+ <!--
+ =======================
+ JUNIT EXECUTION SECTION
+ =======================
+ -->
+ <target depends="init" if="have.tests" name="-pre-test-run">
+ <mkdir dir="${build.test.results.dir}"/>
+ </target>
+ <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
+ <j2seproject3:junit testincludes="**/*Test.java"/>
+ </target>
+ <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
+ <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+ </target>
+ <target depends="init" if="have.tests" name="test-report"/>
+ <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
+ <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
+ <target depends="init" if="have.tests" name="-pre-test-run-single">
+ <mkdir dir="${build.test.results.dir}"/>
+ </target>
+ <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
+ <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
+ <j2seproject3:junit excludes="" includes="${test.includes}"/>
+ </target>
+ <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
+ <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+ </target>
+ <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
+ <!--
+ =======================
+ JUNIT DEBUGGING SECTION
+ =======================
+ -->
+ <target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
+ <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
+ <property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
+ <delete file="${test.report.file}"/>
+ <mkdir dir="${build.test.results.dir}"/>
+ <j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
+ <customize>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <arg value="${test.class}"/>
+ <arg value="showoutput=true"/>
+ <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
+ <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
+ </customize>
+ </j2seproject3:debug>
+ </target>
+ <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
+ <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
+ </target>
+ <target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
+ <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
+ <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
+ </target>
+ <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
+ <!--
+ =========================
+ APPLET EXECUTION SECTION
+ =========================
+ -->
+ <target depends="init,compile-single" name="run-applet">
+ <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+ <j2seproject1:java classname="sun.applet.AppletViewer">
+ <customize>
+ <arg value="${applet.url}"/>
+ </customize>
+ </j2seproject1:java>
+ </target>
+ <!--
+ =========================
+ APPLET DEBUGGING SECTION
+ =========================
+ -->
+ <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
+ <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+ <j2seproject3:debug classname="sun.applet.AppletViewer">
+ <customize>
+ <arg value="${applet.url}"/>
+ </customize>
+ </j2seproject3:debug>
+ </target>
+ <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
+ <!--
+ ===============
+ CLEANUP SECTION
+ ===============
+ -->
+ <target name="-deps-clean-init" unless="built-clean.properties">
+ <property location="${build.dir}/built-clean.properties" name="built-clean.properties"/>
+ <delete file="${built-clean.properties}" quiet="true"/>
+ </target>
+ <target if="already.built.clean.${basedir}" name="-warn-already-built-clean">
+ <echo level="warn" message="Cycle detected: EssentialsUpdate was already built"/>
+ </target>
+ <target depends="init,-deps-clean-init" name="deps-clean" unless="no.deps">
+ <mkdir dir="${build.dir}"/>
+ <touch file="${built-clean.properties}" verbose="false"/>
+ <property file="${built-clean.properties}" prefix="already.built.clean."/>
+ <antcall target="-warn-already-built-clean"/>
+ <propertyfile file="${built-clean.properties}">
+ <entry key="${basedir}" value=""/>
+ </propertyfile>
+ </target>
+ <target depends="init" name="-do-clean">
+ <delete dir="${build.dir}"/>
+ <delete dir="${dist.dir}" followsymlinks="false" includeemptydirs="true"/>
+ </target>
+ <target name="-post-clean">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
+ <target name="-check-call-dep">
+ <property file="${call.built.properties}" prefix="already.built."/>
+ <condition property="should.call.dep">
+ <not>
+ <isset property="already.built.${call.subproject}"/>
+ </not>
+ </condition>
+ </target>
+ <target depends="-check-call-dep" if="should.call.dep" name="-maybe-call-dep">
+ <ant antfile="${call.script}" inheritall="false" target="${call.target}">
+ <propertyset>
+ <propertyref prefix="transfer."/>
+ <mapper from="transfer.*" to="*" type="glob"/>
+ </propertyset>
+ </ant>
+ </target>
+</project>
diff --git a/EssentialsUpdate/nbproject/genfiles.properties b/EssentialsUpdate/nbproject/genfiles.properties
new file mode 100644
index 000000000..992379672
--- /dev/null
+++ b/EssentialsUpdate/nbproject/genfiles.properties
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=fd4b98a9
+build.xml.script.CRC32=334f342d
+build.xml.stylesheet.CRC32=28e38971@1.44.1.45
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=fd4b98a9
+nbproject/build-impl.xml.script.CRC32=1cab9494
+nbproject/build-impl.xml.stylesheet.CRC32=0ae3a408@1.44.1.45
diff --git a/EssentialsUpdate/nbproject/project.properties b/EssentialsUpdate/nbproject/project.properties
new file mode 100644
index 000000000..fadaa4839
--- /dev/null
+++ b/EssentialsUpdate/nbproject/project.properties
@@ -0,0 +1,75 @@
+annotation.processing.enabled=true
+annotation.processing.enabled.in.editor=false
+annotation.processing.run.all.processors=true
+annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
+application.title=EssentialsUpdate
+application.vendor=essentialsteam
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+# Uncomment to specify the preferred debugger connection transport:
+#debug.transport=dt_socket
+debug.classpath=\
+ ${run.classpath}
+debug.test.classpath=\
+ ${run.test.classpath}
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/EssentialsUpdate.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
+excludes=
+file.reference.bukkit-0.0.1-SNAPSHOT.jar=../lib/bukkit-0.0.1-SNAPSHOT.jar
+includes=**
+jar.compress=true
+javac.classpath=\
+ ${file.reference.bukkit-0.0.1-SNAPSHOT.jar}:\
+ ${libs.junit_4.classpath}
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.processorpath=\
+ ${javac.classpath}
+javac.source=1.6
+javac.target=1.6
+javac.test.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+javac.test.processorpath=\
+ ${javac.test.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+main.class=
+manifest.file=manifest.mf
+meta.inf.dir=${src.dir}/META-INF
+mkdist.disabled=false
+platform.active=default_platform
+run.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+# Space-separated list of JVM arguments used when running the project
+# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
+# or test-sys-prop.name=value to set system properties for unit tests):
+run.jvmargs=
+run.test.classpath=\
+ ${javac.test.classpath}:\
+ ${build.test.classes.dir}
+source.encoding=UTF-8
+src.dir=src
+test.src.dir=test
diff --git a/EssentialsUpdate/nbproject/project.xml b/EssentialsUpdate/nbproject/project.xml
new file mode 100644
index 000000000..6b0efd00f
--- /dev/null
+++ b/EssentialsUpdate/nbproject/project.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+ <type>org.netbeans.modules.java.j2seproject</type>
+ <configuration>
+ <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
+ <name>EssentialsUpdate</name>
+ <source-roots>
+ <root id="src.dir"/>
+ </source-roots>
+ <test-roots>
+ <root id="test.src.dir"/>
+ </test-roots>
+ </data>
+ <libraries xmlns="http://www.netbeans.org/ns/ant-project-libraries/1">
+ <definitions>../lib/nblibraries.properties</definitions>
+ </libraries>
+ </configuration>
+</project>
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsHelp.java b/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsHelp.java
new file mode 100644
index 000000000..5b134b8b8
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsHelp.java
@@ -0,0 +1,601 @@
+package com.earth2me.essentials.update;
+
+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.clientstate.Channel;
+import f00f.net.irc.martyr.clientstate.Member;
+import f00f.net.irc.martyr.commands.InviteCommand;
+import f00f.net.irc.martyr.commands.KickCommand;
+import f00f.net.irc.martyr.commands.MessageCommand;
+import f00f.net.irc.martyr.commands.NoticeCommand;
+import f00f.net.irc.martyr.commands.QuitCommand;
+import f00f.net.irc.martyr.commands.TopicCommand;
+import f00f.net.irc.martyr.errors.GenericJoinError;
+import f00f.net.irc.martyr.services.AutoJoin;
+import f00f.net.irc.martyr.services.AutoReconnect;
+import f00f.net.irc.martyr.services.AutoRegister;
+import f00f.net.irc.martyr.services.AutoResponder;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+import java.util.Enumeration;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.bukkit.Bukkit;
+import org.bukkit.Server;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Event.Priority;
+import org.bukkit.event.Event.Type;
+import org.bukkit.event.player.PlayerChatEvent;
+import org.bukkit.event.player.PlayerListener;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.PluginManager;
+
+
+public class EssentialsHelp extends PlayerListener
+{
+ private transient Player chatUser;
+ private transient IRCConnection connection;
+ private transient AutoReconnect autoReconnect;
+ private transient boolean shouldQuit = false;
+ private final transient Server server;
+ private final transient Plugin plugin;
+ private final static Charset UTF8 = Charset.forName("utf-8");
+
+ public EssentialsHelp(Plugin plugin)
+ {
+ this.plugin = plugin;
+ this.server = plugin.getServer();
+ }
+
+ public void registerEvents()
+ {
+ final PluginManager pluginManager = server.getPluginManager();
+ pluginManager.registerEvent(Type.PLAYER_QUIT, this, Priority.Low, plugin);
+ pluginManager.registerEvent(Type.PLAYER_CHAT, this, Priority.Low, plugin);
+ }
+
+ public void onCommand(CommandSender sender)
+ {
+ if (sender instanceof Player && sender.hasPermission("essentials.helpchat"))
+ {
+ if (chatUser == null)
+ {
+ chatUser = (Player)sender;
+ connection = null;
+ sender.sendMessage("You will be connected to the Essentials Help Chat.");
+ sender.sendMessage("All your chat messages will be forwarded to the channel. You can't chat with other players on your server while in help chat, but you can use commands.");
+ sender.sendMessage("Please be patient, if noone is available, check back later.");
+ sender.sendMessage("Type !help to get a list of all commands.");
+ sender.sendMessage("Type !quit to leave the channel.");
+ sender.sendMessage("Do you want to join the channel now? (yes/no)");
+ }
+ if (!chatUser.equals(sender))
+ {
+ sender.sendMessage("The player " + chatUser.getDisplayName() + " is already using the essentialshelp.");
+ }
+ }
+ else
+ {
+ sender.sendMessage("Please run the command as op from in game.");
+ }
+ }
+
+ public void onDisable()
+ {
+ if (autoReconnect != null && connection != null)
+ {
+ autoReconnect.disable();
+ shouldQuit = true;
+ connection.disconnect();
+ }
+ }
+
+ private void sendChatMessage(final Player player, final String message)
+ {
+ final String messageCleaned = message.trim();
+ if (messageCleaned.isEmpty())
+ {
+ return;
+ }
+ if (connection == null)
+ {
+ if (messageCleaned.equalsIgnoreCase("yes"))
+ {
+ player.sendMessage("Connecting...");
+ connectToIRC(player);
+ }
+ if (messageCleaned.equalsIgnoreCase("no") || message.equalsIgnoreCase("!quit"))
+ {
+ chatUser = null;
+ }
+ }
+ else
+ {
+ final String lowMessage = messageCleaned.toLowerCase();
+ if (lowMessage.startsWith("!quit"))
+ {
+ chatUser = null;
+ autoReconnect.disable();
+ shouldQuit = true;
+ connection.sendCommand(new QuitCommand("Connection closed by user."));
+ player.sendMessage("Connection closed.");
+ return;
+ }
+ if (!connection.getClientState().getChannels().hasMoreElements())
+ {
+ player.sendMessage("Not connected yet!");
+ return;
+ }
+ if (lowMessage.startsWith("!list"))
+ {
+ final Enumeration members = ((Channel)connection.getClientState().getChannels().nextElement()).getMembers();
+ final StringBuilder sb = new StringBuilder();
+ while (members.hasMoreElements())
+ {
+ if (sb.length() > 0)
+ {
+ sb.append("§f, ");
+ }
+ final Member member = (Member)members.nextElement();
+ if (member.hasOps() || member.hasVoice())
+ {
+ sb.append("§6");
+ }
+ else
+ {
+ sb.append("§7");
+ }
+ sb.append(member.getNick());
+ }
+ player.sendMessage(sb.toString());
+ return;
+ }
+ if (lowMessage.startsWith("!help"))
+ {
+ player.sendMessage("Commands: (Note: Files send to the chat will be public viewable.)");
+ player.sendMessage("!errors - Send the last server errors to the chat.");
+ player.sendMessage("!startup - Send the last startup messages to the chat.");
+ player.sendMessage("!config - Sends your Essentials config to the chat.");
+ player.sendMessage("!list - List all players in chat.");
+ player.sendMessage("!quit - Leave chat.");
+ return;
+ }
+ if (lowMessage.startsWith("!errors"))
+ {
+ sendErrors();
+ return;
+ }
+ if (lowMessage.startsWith("!startup"))
+ {
+ sendStartup();
+ return;
+ }
+ if (lowMessage.startsWith("!config"))
+ {
+ sendConfig();
+ return;
+ }
+ final Channel channel = (Channel)connection.getClientState().getChannels().nextElement();
+ connection.sendCommand(new MessageCommand(channel.getName(), messageCleaned));
+ chatUser.sendMessage("§6" + connection.getClientState().getNick().getNick() + ": §7" + messageCleaned);
+ }
+ }
+
+ private void connectToIRC(final Player player)
+ {
+ connection = new IRCConnection();
+ // Required services
+ new AutoResponder(connection);
+ int versionNumber = 0;
+ final StringBuilder nameBuilder = new StringBuilder();
+ nameBuilder.append(player.getName());
+
+ final Matcher versionMatch = Pattern.compile("git-Bukkit-([0-9]+).([0-9]+).([0-9]+)-[0-9]+-[0-9a-z]+-b([0-9]+)jnks.*").matcher(server.getVersion());
+ if (versionMatch.matches())
+ {
+ nameBuilder.append(" CB");
+ nameBuilder.append(versionMatch.group(4));
+ }
+
+ final Plugin essentials = server.getPluginManager().getPlugin("Essentials");
+ if (essentials != null)
+ {
+ nameBuilder.append(" ESS");
+ nameBuilder.append(essentials.getDescription().getVersion());
+ }
+
+ final Plugin groupManager = server.getPluginManager().getPlugin("GroupManager");
+ if (groupManager != null)
+ {
+ nameBuilder.append(" GM");
+ if (!groupManager.isEnabled())
+ {
+ nameBuilder.append('!');
+ }
+ }
+
+ final Plugin pex = server.getPluginManager().getPlugin("PermissionsEx");
+ if (pex != null)
+ {
+ nameBuilder.append(" PEX");
+ if (!pex.isEnabled())
+ {
+ nameBuilder.append('!');
+ }
+ nameBuilder.append(pex.getDescription().getVersion());
+ }
+
+ final Plugin pb = server.getPluginManager().getPlugin("PermissionsBukkit");
+ if (pb != null)
+ {
+ nameBuilder.append(" PB");
+ if (!pb.isEnabled())
+ {
+ nameBuilder.append('!');
+ }
+ nameBuilder.append(pb.getDescription().getVersion());
+ }
+
+ final Plugin bp = server.getPluginManager().getPlugin("bPermissions");
+ if (bp != null)
+ {
+ nameBuilder.append(" BP");
+ if (!bp.isEnabled())
+ {
+ nameBuilder.append('!');
+ }
+ nameBuilder.append(bp.getDescription().getVersion());
+ }
+
+ final Plugin perm = server.getPluginManager().getPlugin("Permissions");
+ if (perm != null)
+ {
+ nameBuilder.append(" P");
+ if (!perm.isEnabled())
+ {
+ nameBuilder.append('!');
+ }
+ nameBuilder.append(perm.getDescription().getVersion());
+ }
+
+ new AutoRegister(connection, "Ess_" + player.getName(), "esshelp", nameBuilder.toString());
+
+ autoReconnect = new AutoReconnect(connection);
+ new KickAutoJoin(connection, "#essentials");
+
+ new IRCListener(connection);
+ autoReconnect.go("irc.esper.net", 6667);
+ }
+
+ private void handleIRCmessage(final String nick, final String message)
+ {
+
+ if (chatUser != null)
+ {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("§6");
+ sb.append(nick);
+ sb.append(": §7");
+ final String coloredmessage = message.replace("\u000300", "§f").replace("\u000301", "§0").replace("\u000302", "§1").replace("\u000303", "§2").replace("\u000304", "§c").replace("\u000305", "§4").replace("\u000306", "§5").replace("\u000307", "§6").replace("\u000308", "§e").replace("\u000309", "§a").replace("\u00030", "§f").replace("\u000310", "§b").replace("\u000311", "§f").replace("\u000312", "§9").replace("\u000313", "§d").replace("\u000314", "§8").replace("\u000315", "§7").replace("\u00031", "§0").replace("\u00032", "§1").replace("\u00033", "§2").replace("\u00034", "§c").replace("\u00035", "§4").replace("\u00036", "§5").replace("\u00037", "§6").replace("\u00038", "§e").replace("\u00039", "§a").replace("\u0003", "§7");
+ sb.append(coloredmessage);
+ chatUser.sendMessage(sb.toString());
+ }
+ }
+
+ private void sendErrors()
+ {
+ BufferedReader page = null;
+ try
+ {
+ File bukkitFolder = plugin.getDataFolder().getAbsoluteFile().getParentFile().getParentFile();
+ if (bukkitFolder == null || !bukkitFolder.exists())
+ {
+ chatUser.sendMessage("Bukkit folder not found.");
+ return;
+ }
+ File logFile = new File(bukkitFolder, "server.log");
+ if (!logFile.exists())
+ {
+ chatUser.sendMessage("Server log not found.");
+ return;
+ }
+ FileInputStream fis = new FileInputStream(logFile);
+ if (logFile.length() > 1000000)
+ {
+ fis.skip(logFile.length()-1000000);
+ }
+ page = new BufferedReader(new InputStreamReader(fis));
+ final StringBuilder input = new StringBuilder();
+ String line;
+ Pattern pattern = Pattern.compile("^[0-9 :-]+\\[INFO\\].*");
+ while ((line = page.readLine()) != null)
+ {
+ if (!pattern.matcher(line).matches()) {
+ input.append(line).append("\n");
+ }
+ }
+ if (input.length()>10000) {
+ input.delete(0, input.length()-10000);
+ }
+ final PastieUpload pastie = new PastieUpload();
+ final String url = pastie.send(input.toString());
+ final Channel channel = (Channel)connection.getClientState().getChannels().nextElement();
+ String message = "Errors: " + url;
+ chatUser.sendMessage("§6" + connection.getClientState().getNick().getNick() + ": §7" + message);
+ connection.sendCommand(new MessageCommand(channel.getName(), message));
+ }
+ catch (IOException ex)
+ {
+ Bukkit.getLogger().log(Level.SEVERE, null, ex);
+ chatUser.sendMessage(ex.getMessage());
+ }
+ finally
+ {
+ try
+ {
+ if (page != null)
+ {
+ page.close();
+ }
+ }
+ catch (IOException ex)
+ {
+ Logger.getLogger(EssentialsHelp.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+
+ private void sendStartup()
+ {
+ BufferedReader page = null;
+ try
+ {
+ File bukkitFolder = plugin.getDataFolder().getAbsoluteFile().getParentFile().getParentFile();
+ if (bukkitFolder == null || !bukkitFolder.exists())
+ {
+ chatUser.sendMessage("Bukkit folder not found.");
+ return;
+ }
+ File logFile = new File(bukkitFolder, "server.log");
+ if (!logFile.exists())
+ {
+ chatUser.sendMessage("Server log not found.");
+ return;
+ }
+ FileInputStream fis = new FileInputStream(logFile);
+ if (logFile.length() > 1000000)
+ {
+ fis.skip(logFile.length()-1000000);
+ }
+ page = new BufferedReader(new InputStreamReader(fis));
+ final StringBuilder input = new StringBuilder();
+ String line;
+ Pattern patternStart = Pattern.compile("^[0-9 :-]+\\[INFO\\] Starting minecraft server version.*");
+ Pattern patternEnd = Pattern.compile("^[0-9 :-]+\\[INFO\\] Done \\([0-9.,]+s\\)! For help, type \"help\".*");
+ boolean log = false;
+ while ((line = page.readLine()) != null)
+ {
+ if (patternStart.matcher(line).matches()) {
+ if (input.length() > 0) {
+ input.delete(0, input.length());
+ }
+ log = true;
+ }
+ if (log) {
+ input.append(line).append("\n");
+ }
+ if (patternEnd.matcher(line).matches()) {
+ log = false;
+ }
+ }
+ if (input.length()>10000) {
+ input.delete(0, input.length()-10000);
+ }
+ final PastieUpload pastie = new PastieUpload();
+ final String url = pastie.send(input.toString());
+ final Channel channel = (Channel)connection.getClientState().getChannels().nextElement();
+ String message = "Startup: " + url;
+ chatUser.sendMessage("§6" + connection.getClientState().getNick().getNick() + ": §7" + message);
+ connection.sendCommand(new MessageCommand(channel.getName(), message));
+ }
+ catch (IOException ex)
+ {
+ Bukkit.getLogger().log(Level.SEVERE, null, ex);
+ chatUser.sendMessage(ex.getMessage());
+ }
+ finally
+ {
+ try
+ {
+ if (page != null)
+ {
+ page.close();
+ }
+ }
+ catch (IOException ex)
+ {
+ Logger.getLogger(EssentialsHelp.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+
+ private void sendConfig()
+ {
+ BufferedReader page = null;
+ try
+ {
+ File configFolder = new File(plugin.getDataFolder().getParentFile(), "Essentials");
+ if (!configFolder.exists())
+ {
+ chatUser.sendMessage("Essentials plugin folder not found.");
+ return;
+ }
+ File configFile = new File(configFolder, "config.yml");
+ if (!configFile.exists())
+ {
+ chatUser.sendMessage("Essentials config file not found.");
+ return;
+ }
+ page = new BufferedReader(new InputStreamReader(new FileInputStream(configFile), UTF8));
+ final StringBuilder input = new StringBuilder();
+ String line;
+ while ((line = page.readLine()) != null)
+ {
+ input.append(line).append("\n");
+ }
+ final PastieUpload pastie = new PastieUpload();
+ final String url = pastie.send(input.toString());
+ final Channel channel = (Channel)connection.getClientState().getChannels().nextElement();
+ String message = "Essentials config.yml: " + url;
+ chatUser.sendMessage("§6" + connection.getClientState().getNick().getNick() + ": §7" + message);
+ connection.sendCommand(new MessageCommand(channel.getName(), message));
+
+ }
+ catch (IOException ex)
+ {
+ Bukkit.getLogger().log(Level.SEVERE, null, ex);
+ chatUser.sendMessage(ex.getMessage());
+ }
+ finally
+ {
+ try
+ {
+ if (page != null)
+ {
+ page.close();
+ }
+ }
+ catch (IOException ex)
+ {
+ Logger.getLogger(EssentialsHelp.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+
+ @Override
+ public void onPlayerChat(PlayerChatEvent event)
+ {
+ if (event.getPlayer() == chatUser)
+ {
+ sendChatMessage(event.getPlayer(), event.getMessage());
+ event.setCancelled(true);
+ return;
+ }
+ }
+
+ @Override
+ public void onPlayerQuit(PlayerQuitEvent event)
+ {
+ chatUser = null;
+ if (autoReconnect != null)
+ {
+ autoReconnect.disable();
+ }
+ shouldQuit = true;
+ if (connection != null)
+ {
+ connection.sendCommand(new QuitCommand("Connection closed by user."));
+ }
+ return;
+ }
+
+
+ class KickAutoJoin extends AutoJoin
+ {
+ private String channel;
+
+ public KickAutoJoin(IRCConnection connection, String channel)
+ {
+ super(connection, channel);
+ this.channel = channel;
+ }
+
+ @Override
+ protected void updateCommand(InCommand command_o)
+ {
+ if (command_o instanceof KickCommand)
+ {
+ final KickCommand kickCommand = (KickCommand)command_o;
+
+ if (kickCommand.kickedUs(getConnection().getClientState()))
+ {
+ if (Channel.areEqual(kickCommand.getChannel(), channel))
+ {
+ chatUser.sendMessage("You have been kicked from the channel: " + kickCommand.getComment());
+ chatUser = null;
+ autoReconnect.disable();
+ shouldQuit = true;
+ connection.sendCommand(new QuitCommand("Connection closed by user."));
+ }
+ }
+ }
+ else if (command_o instanceof GenericJoinError)
+ {
+ GenericJoinError joinErr = (GenericJoinError)command_o;
+
+ if (Channel.areEqual(joinErr.getChannel(), channel))
+ {
+ scheduleJoin();
+ }
+ }
+ else if (command_o instanceof InviteCommand)
+ {
+ InviteCommand invite = (InviteCommand)command_o;
+ if (!getConnection().getClientState().isOnChannel(invite.getChannel()))
+ {
+ performJoin();
+ }
+ }
+ }
+ }
+
+
+ class IRCListener extends GenericAutoService
+ {
+ public IRCListener(final IRCConnection connection)
+ {
+ super(connection);
+ enable();
+ }
+
+ @Override
+ protected void updateState(final State state)
+ {
+ if (state == State.UNCONNECTED && shouldQuit)
+ {
+ connection = null;
+ shouldQuit = false;
+ }
+ }
+
+ @Override
+ protected void updateCommand(final InCommand command)
+ {
+ if (command instanceof MessageCommand)
+ {
+ final MessageCommand msg = (MessageCommand)command;
+ EssentialsHelp.this.handleIRCmessage(msg.getSource().getNick(), msg.getMessage());
+ }
+ if (command instanceof TopicCommand)
+ {
+ final TopicCommand msg = (TopicCommand)command;
+ EssentialsHelp.this.handleIRCmessage(msg.getChannel(), msg.getTopic());
+ }
+ if (command instanceof NoticeCommand)
+ {
+ final NoticeCommand msg = (NoticeCommand)command;
+ EssentialsHelp.this.handleIRCmessage(msg.getFrom().getNick(), msg.getNotice());
+ }
+ }
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsUpdate.java b/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsUpdate.java
new file mode 100644
index 000000000..d4ee6c0fc
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsUpdate.java
@@ -0,0 +1,66 @@
+package com.earth2me.essentials.update;
+
+import com.earth2me.essentials.update.UpdateCheck.CheckResult;
+import org.bukkit.Bukkit;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.plugin.java.JavaPlugin;
+
+
+public class EssentialsUpdate extends JavaPlugin
+{
+ private transient EssentialsHelp essentialsHelp;
+ private transient UpdateProcess updateProcess;
+
+ @Override
+ public void onEnable()
+ {
+ if (!getDataFolder().exists() && !getDataFolder().mkdirs() ) {
+ Bukkit.getLogger().severe("Could not create data folder:"+getDataFolder().getPath());
+ }
+ essentialsHelp = new EssentialsHelp(this);
+ essentialsHelp.registerEvents();
+
+ final UpdateCheck updateCheck = new UpdateCheck(this);
+ updateProcess = new UpdateProcess(this, updateCheck);
+ updateProcess.registerEvents();
+
+ Bukkit.getLogger().info("EssentialsUpdate " + getDescription().getVersion() + " loaded.");
+
+ if (updateCheck.isEssentialsInstalled())
+ {
+ updateCheck.checkForUpdates();
+ final Version myVersion = new Version(getDescription().getVersion());
+ if (updateCheck.getResult() == CheckResult.NEW_ESS && myVersion.equals(updateCheck.getNewVersion()))
+ {
+ Bukkit.getLogger().info("Versions of EssentialsUpdate and Essentials do not match. Starting automatic update.");
+ updateProcess.doAutomaticUpdate();
+ }
+ updateCheck.scheduleUpdateTask();
+ }
+ else
+ {
+ Bukkit.getLogger().info("Essentials is ready for installation. Join the game and follow the instructions.");
+ }
+ }
+
+ @Override
+ public void onDisable()
+ {
+ essentialsHelp.onDisable();
+ }
+
+ @Override
+ public boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] args)
+ {
+ if (command.getName().equalsIgnoreCase("essentialsupdate"))
+ {
+ updateProcess.onCommand(sender);
+ }
+ if (command.getName().equalsIgnoreCase("essentialshelp"))
+ {
+ essentialsHelp.onCommand(sender);
+ }
+ return true;
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java b/EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java
new file mode 100644
index 000000000..888950f34
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java
@@ -0,0 +1,112 @@
+package com.earth2me.essentials.update;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.logging.Logger;
+
+
+public class GetFile
+{
+ private transient URLConnection connection;
+ private transient MessageDigest digest;
+
+ public GetFile(final String urlString) throws MalformedURLException, IOException
+ {
+ final URL url = new URL(urlString);
+ this.connection = url.openConnection();
+ this.connection.setConnectTimeout(1000);
+ this.connection.setReadTimeout(5000);
+ this.connection.setUseCaches(false);
+ this.connection.connect();
+ final int respCode = ((HttpURLConnection)this.connection).getResponseCode();
+ if (respCode >= 300 && respCode < 400 && this.connection.getHeaderField("Location") != null)
+ {
+ connection.getInputStream().close();
+ final URL redirect = new URL(this.connection.getHeaderField("Location"));
+ this.connection = redirect.openConnection();
+ this.connection.setConnectTimeout(1000);
+ this.connection.setReadTimeout(5000);
+ this.connection.setUseCaches(false);
+ this.connection.connect();
+ }
+ }
+
+ public void saveTo(final File file) throws IOException
+ {
+ try
+ {
+ saveTo(file, null);
+ }
+ catch (NoSuchAlgorithmException ex)
+ {
+ // Ignore because the code is never called
+ }
+ }
+
+ public void saveTo(final File file, final String key) throws IOException, NoSuchAlgorithmException
+ {
+ if (key != null)
+ {
+ digest = MessageDigest.getInstance("SHA256");
+ }
+ final byte[] buffer = new byte[1024 * 8];
+ boolean brokenFile = false;
+ final BufferedInputStream input = new BufferedInputStream(connection.getInputStream());
+ try
+ {
+ final BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file));
+ try
+ {
+ int length;
+ do
+ {
+ length = input.read(buffer);
+ if (length >= 0)
+ {
+ if (key != null)
+ {
+ digest.update(buffer, 0, length);
+ }
+ output.write(buffer, 0, length);
+ }
+ }
+ while (length >= 0);
+ if (key != null)
+ {
+ final byte[] checksum = digest.digest();
+ final String checksumString = new BigInteger(checksum).toString(36);
+ if (!checksumString.equals(key))
+ {
+ brokenFile = true;
+ }
+ }
+ }
+ finally
+ {
+ output.close();
+ }
+ if (brokenFile && !file.delete())
+ {
+ Logger.getLogger("Minecraft").severe("Could not delete file " + file.getPath());
+ }
+ }
+ finally
+ {
+ input.close();
+ }
+ if (brokenFile)
+ {
+ throw new IOException("Checksum check failed.");
+ }
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/ModuleInfo.java b/EssentialsUpdate/src/com/earth2me/essentials/update/ModuleInfo.java
new file mode 100644
index 000000000..722fca3e1
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/ModuleInfo.java
@@ -0,0 +1,35 @@
+package com.earth2me.essentials.update;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import org.bukkit.configuration.Configuration;
+
+
+public class ModuleInfo
+{
+ private final transient String url;
+ private final transient String version;
+ private final transient String hash;
+
+ public ModuleInfo(final Configuration updateConfig, final String path)
+ {
+ url = updateConfig.getString(path + ".url", null);
+ version = updateConfig.getString(path + ".version", null);
+ hash = updateConfig.getString(path + ".hash", null);
+ }
+
+ public URL getUrl() throws MalformedURLException
+ {
+ return new URL(url);
+ }
+
+ public String getVersion()
+ {
+ return version;
+ }
+
+ public String getHash()
+ {
+ return hash;
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/PastieUpload.java b/EssentialsUpdate/src/com/earth2me/essentials/update/PastieUpload.java
new file mode 100644
index 000000000..6cad44e4d
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/PastieUpload.java
@@ -0,0 +1,40 @@
+package com.earth2me.essentials.update;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+public class PastieUpload
+{
+ private final transient PostToUrl connection;
+
+ public PastieUpload() throws MalformedURLException
+ {
+ connection = new PostToUrl(new URL("http://pastie.org/pastes"));
+ }
+
+ public String send(final String data) throws IOException
+ {
+ final Map<String, Object> map = new HashMap<String, Object>();
+ map.put("paste[parser_id]", "19");
+ map.put("paste[authorization]", "burger");
+ map.put("paste[body]", data);
+ map.put("paste[restricted]", "1");
+ final String html = connection.send(map);
+ final Matcher matcher = Pattern.compile("(?s).*\\?key=([a-z0-9]+).*").matcher(html);
+ if (matcher.matches())
+ {
+ final String key = matcher.group(1);
+ return "http://pastie.org/private/" + key;
+ }
+ else
+ {
+ throw new IOException("Failed to upload to pastie.org");
+ }
+ }
+} \ No newline at end of file
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/PostToUrl.java b/EssentialsUpdate/src/com/earth2me/essentials/update/PostToUrl.java
new file mode 100644
index 000000000..c8978961b
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/PostToUrl.java
@@ -0,0 +1,66 @@
+package com.earth2me.essentials.update;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.Charset;
+import java.util.Map;
+import java.util.Random;
+
+
+public class PostToUrl
+{
+ private final transient URL url;
+ private final transient String boundary;
+ private final transient Random random = new Random();
+ private final static String CRLF = "\r\n";
+ private final static Charset UTF8 = Charset.forName("utf-8");
+
+ public PostToUrl(final URL url)
+ {
+ this.url = url;
+ final byte[] bytes = new byte[32];
+ random.nextBytes(bytes);
+ this.boundary = "----------" + new BigInteger(bytes).toString(Character.MAX_RADIX) + "_$";
+ }
+
+ public String send(final Map<String, Object> data) throws IOException
+ {
+ final URLConnection connection = url.openConnection();
+ connection.setRequestProperty("content-type", "multipart/form-data; boundary=" + boundary);
+ final StringBuilder dataBuilder = new StringBuilder();
+ for (Map.Entry<String, Object> entry : data.entrySet())
+ {
+ if (entry.getValue() instanceof String)
+ {
+ dataBuilder.append("--").append(boundary).append(CRLF);
+ dataBuilder.append("Content-Disposition: form-data; name=\"").append(entry.getKey()).append('"').append(CRLF);
+ dataBuilder.append(CRLF);
+ dataBuilder.append(entry.getValue()).append(CRLF);
+ }
+ // TODO: Add support for file upload
+ }
+ dataBuilder.append("--").append(boundary).append("--").append(CRLF);
+ dataBuilder.append(CRLF);
+ connection.setDoOutput(true);
+ final byte[] message = dataBuilder.toString().getBytes(UTF8);
+ connection.setRequestProperty("content-length", Integer.toString(message.length));
+ connection.connect();
+ final OutputStream stream = connection.getOutputStream();
+ stream.write(message);
+ stream.close();
+ final BufferedReader page = new BufferedReader(new InputStreamReader(connection.getInputStream(), UTF8));
+ final StringBuilder input = new StringBuilder();
+ String line;
+ while ((line = page.readLine()) != null)
+ {
+ input.append(line).append("\n");
+ }
+ page.close();
+ return input.toString();
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateCheck.java b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateCheck.java
new file mode 100644
index 000000000..dcda252a0
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateCheck.java
@@ -0,0 +1,203 @@
+package com.earth2me.essentials.update;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.bukkit.Bukkit;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.PluginManager;
+
+
+public class UpdateCheck
+{
+ private transient CheckResult result = CheckResult.UNKNOWN;
+ private transient Version currentVersion;
+ private transient Version newVersion = null;
+ private transient int bukkitResult = 0;
+ private transient UpdateFile updateFile;
+ private final static int CHECK_INTERVAL = 20 * 60 * 60 * 6;
+ private final transient Plugin plugin;
+ private transient boolean essentialsInstalled;
+
+ public UpdateCheck(Plugin plugin)
+ {
+ this.plugin = plugin;
+ updateFile = new UpdateFile(plugin);
+ checkForEssentials();
+ }
+
+ private void checkForEssentials()
+ {
+ PluginManager pm = plugin.getServer().getPluginManager();
+ Plugin essentials = pm.getPlugin("Essentials");
+ if (essentials == null)
+ {
+ essentialsInstalled = false;
+ if (new File(plugin.getDataFolder().getParentFile(), "Essentials.jar").exists())
+ {
+ Bukkit.getLogger().severe("Essentials.jar found, but not recognized by Bukkit. Broken download?");
+ }
+ }
+ else
+ {
+ essentialsInstalled = true;
+ currentVersion = new Version(essentials.getDescription().getVersion());
+ }
+ }
+
+ public void scheduleUpdateTask()
+ {
+ plugin.getServer().getScheduler().scheduleAsyncRepeatingTask(plugin, new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ updateFile = new UpdateFile(plugin);
+ checkForUpdates();
+ }
+ }, CHECK_INTERVAL, CHECK_INTERVAL);
+ }
+
+ public boolean isEssentialsInstalled()
+ {
+ return essentialsInstalled;
+ }
+
+ public CheckResult getResult()
+ {
+ return result;
+ }
+
+ int getNewBukkitVersion()
+ {
+ return bukkitResult;
+ }
+
+ VersionInfo getNewVersionInfo()
+ {
+ return updateFile.getVersions().get(newVersion);
+ }
+
+ public enum CheckResult
+ {
+ NEW_ESS, NEW_ESS_BUKKIT, NEW_BUKKIT, OK, UNKNOWN
+ }
+
+ public void checkForUpdates()
+ {
+ if (currentVersion == null)
+ {
+ return;
+ }
+ final Map<Version, VersionInfo> versions = updateFile.getVersions();
+ final int bukkitVersion = getBukkitVersion();
+ Version higher = null;
+ Version found = null;
+ Version lower = null;
+ int bukkitHigher = 0;
+ int bukkitLower = 0;
+ for (Entry<Version, VersionInfo> entry : versions.entrySet())
+ {
+ final int minBukkit = entry.getValue().getMinBukkit();
+ final int maxBukkit = entry.getValue().getMaxBukkit();
+ if (minBukkit == 0 || maxBukkit == 0)
+ {
+ continue;
+ }
+ if (bukkitVersion <= maxBukkit)
+ {
+ if (bukkitVersion < minBukkit)
+ {
+ if (higher == null || higher.compareTo(entry.getKey()) < 0)
+ {
+
+ higher = entry.getKey();
+ bukkitHigher = minBukkit;
+ }
+ }
+ else
+ {
+ if (found == null || found.compareTo(entry.getKey()) < 0)
+ {
+ found = entry.getKey();
+ }
+ }
+ }
+ else
+ {
+ if (lower == null || lower.compareTo(entry.getKey()) < 0)
+ {
+ lower = entry.getKey();
+ bukkitLower = minBukkit;
+ }
+ }
+ }
+ if (found != null)
+ {
+ if (found.compareTo(currentVersion) > 0)
+ {
+ result = CheckResult.NEW_ESS;
+ newVersion = found;
+ }
+ else
+ {
+ result = CheckResult.OK;
+ }
+ }
+ else if (higher != null)
+ {
+ if (higher.compareTo(currentVersion) > 0)
+ {
+ newVersion = higher;
+ result = CheckResult.NEW_ESS_BUKKIT;
+ bukkitResult = bukkitHigher;
+ }
+ else if (higher.compareTo(currentVersion) < 0)
+ {
+ result = CheckResult.UNKNOWN;
+ }
+ else
+ {
+ result = CheckResult.NEW_BUKKIT;
+ bukkitResult = bukkitHigher;
+ }
+ }
+ else if (lower != null)
+ {
+ if (lower.compareTo(currentVersion) > 0)
+ {
+ result = CheckResult.NEW_ESS_BUKKIT;
+ newVersion = lower;
+ bukkitResult = bukkitLower;
+ }
+ else if (lower.compareTo(currentVersion) < 0)
+ {
+ result = CheckResult.UNKNOWN;
+ }
+ else
+ {
+ result = CheckResult.NEW_BUKKIT;
+ bukkitResult = bukkitLower;
+ }
+ }
+
+ }
+
+ private int getBukkitVersion()
+ {
+ final Matcher versionMatch = Pattern.compile("git-Bukkit-([0-9]+).([0-9]+).([0-9]+)-[0-9]+-[0-9a-z]+-b([0-9]+)jnks.*").matcher(plugin.getServer().getVersion());
+ if (versionMatch.matches())
+ {
+ return Integer.parseInt(versionMatch.group(4));
+ }
+ throw new NumberFormatException("Bukkit Version changed!");
+ }
+
+ public Version getNewVersion()
+ {
+ return newVersion;
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateFile.java b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateFile.java
new file mode 100644
index 000000000..8f34bffc4
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateFile.java
@@ -0,0 +1,204 @@
+package com.earth2me.essentials.update;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.configuration.file.YamlConfiguration;
+
+
+public class UpdateFile
+{
+ private final static Logger LOGGER = Logger.getLogger("Minecraft");
+ private final static String UPDATE_URL = "http://goo.gl/67jev";
+ private final static BigInteger PUBLIC_KEY = new BigInteger("5ha6a2d4qdy17ttkg8evh74sl5a87djojwenu12k1lvy8ui6003e6l06rntczpoh99mhc3txj8mqlxw111oyy9yl7s7qpyluyzix3j1odxrxx4u52gxvyu6qiteapczkzvi7rxgeqsozz7b19rdx73a7quo9ybwpz1cr82r7x5k0pg2a73pjjsv2j1awr13azo7klrcxp9y5xxwf5qv1s3tw4zqftli18u0ek5qkbzfbgk1v5n2f11pkwwk6p0mibrn26wnjbv11vyiqgu95o7busmt6vf5q7grpcenl637w83mbin56s3asj1131b2mscj9xep3cbj7la9tgsxl5bj87vzy8sk2d34kzwqdqgh9nry43nqqus12l1stmiv184r8r3jcy8w43e8h1u1mzklldb5eytkuhayqik8l3ns04hwt8sgacvw534be8sx26qrn5s1", 36);
+ private final transient File file;
+ private transient YamlConfiguration updateConfig;
+ private final transient Plugin plugin;
+ private final transient TreeMap<Version, VersionInfo> versions = new TreeMap<Version, VersionInfo>();
+
+ public UpdateFile(final Plugin plugin)
+ {
+ this.plugin = plugin;
+ final long lastUpdate = Long.parseLong(plugin.getConfig().getString("lastupdate", "0"));
+ file = new File(plugin.getDataFolder(), "update.yml");
+ if (lastUpdate < System.currentTimeMillis() - 1000 * 60 * 60 * 6 || !file.exists())
+ {
+ if (file.exists() && !file.delete())
+ {
+ LOGGER.log(Level.SEVERE, "Could not delete file update.yml!");
+ return;
+ }
+ if (!downloadFile() || !checkFile())
+ {
+ LOGGER.log(Level.SEVERE, "Could not download and verify file update.yml!");
+ return;
+ }
+ }
+ try
+ {
+ readVersions();
+ }
+ catch (Exception ex)
+ {
+ LOGGER.log(Level.SEVERE, "Could not load update.yml!");
+ return;
+ }
+ }
+
+ private boolean downloadFile()
+ {
+ GetFile getFile;
+ try
+ {
+ getFile = new GetFile(UPDATE_URL);
+ getFile.saveTo(file);
+ plugin.getConfig().set("lastupdate", System.currentTimeMillis());
+ plugin.getConfig().save(new File(plugin.getDataFolder(),"config.yml"));
+ return true;
+ }
+ catch (IOException ex)
+ {
+ LOGGER.log(Level.SEVERE, "Error while downloading update.yml", ex);
+ return false;
+ }
+ }
+
+ private boolean checkFile()
+ {
+ BufferedInputStream bis = null;
+ try
+ {
+ bis = new BufferedInputStream(new FileInputStream(file));
+ if (bis.read() != '#')
+ {
+ throw new IOException("File has to start with #");
+ }
+ final StringBuilder length = new StringBuilder();
+ final StringBuilder signature = new StringBuilder();
+ boolean isSignature = false;
+ do
+ {
+ final int cur = bis.read();
+ if (cur == -1)
+ {
+ break;
+ }
+ if (cur == ':')
+ {
+ isSignature = true;
+ }
+ else if (cur == '\n')
+ {
+ break;
+ }
+ else if ((cur >= '0' && cur <= '9')
+ || (cur >= 'a' && cur <= 'z'))
+ {
+ if (isSignature)
+ {
+ signature.append((char)cur);
+ }
+ else
+ {
+ length.append((char)cur);
+ }
+ }
+ else
+ {
+ throw new IOException("Illegal character in signature!");
+ }
+ }
+ while (true);
+ if (length.length() == 0 || signature.length() == 0)
+ {
+ throw new IOException("Broken signature!");
+ }
+ final int sigLength = new BigInteger(length.toString(), 36).intValue();
+ if (sigLength < 0 || sigLength > 2048)
+ {
+ throw new IOException("Invalid signature length!");
+ }
+ final byte[] sigBytes = new BigInteger(signature.toString(), 36).toByteArray();
+ if (sigLength < sigBytes.length)
+ {
+ throw new IOException("Length is less then available bytes.");
+ }
+ byte[] realBytes;
+ if (sigLength == sigBytes.length)
+ {
+ realBytes = sigBytes;
+ }
+ else
+ {
+ realBytes = new byte[sigLength];
+ System.arraycopy(sigBytes, 0, realBytes, sigLength - sigBytes.length, sigBytes.length);
+ }
+ final X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(PUBLIC_KEY.toByteArray());
+ final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ final PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
+ final Signature rsa = Signature.getInstance("SHA256withRSA");
+ rsa.initVerify(pubKey);
+ final byte[] buffer = new byte[2048];
+ int readLength;
+ do
+ {
+ readLength = bis.read(buffer);
+ if (readLength >= 0)
+ {
+ rsa.update(buffer, 0, readLength);
+ }
+ }
+ while (readLength >= 0);
+ return rsa.verify(realBytes);
+ }
+ catch (Exception ex)
+ {
+ LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
+ }
+ finally
+ {
+ try
+ {
+ if (bis != null)
+ {
+ bis.close();
+ }
+ }
+ catch (IOException ex)
+ {
+ LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
+ }
+ }
+ return false;
+ }
+
+ private void readVersions() throws Exception
+ {
+ updateConfig = new YamlConfiguration();
+ updateConfig.load(file);
+ versions.clear();
+ for (String versionString : updateConfig.getKeys(false))
+ {
+ final Version version = new Version(versionString);
+ final VersionInfo info = new VersionInfo(updateConfig, versionString);
+ versions.put(version, info);
+ }
+ }
+
+ public Map<Version, VersionInfo> getVersions()
+ {
+ return Collections.unmodifiableMap(versions.descendingMap());
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java
new file mode 100644
index 000000000..95898bcb6
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java
@@ -0,0 +1,128 @@
+package com.earth2me.essentials.update;
+
+import java.util.List;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Event.Priority;
+import org.bukkit.event.Event.Type;
+import org.bukkit.event.player.PlayerChatEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerListener;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.PluginManager;
+
+
+public class UpdateProcess extends PlayerListener
+{
+ private transient Player currentPlayer;
+ private final transient Plugin plugin;
+ private final transient UpdateCheck updateCheck;
+
+ public UpdateProcess(final Plugin plugin, final UpdateCheck updateCheck)
+ {
+ this.plugin = plugin;
+ this.updateCheck = updateCheck;
+ }
+
+ public void registerEvents()
+ {
+ final PluginManager pluginManager = plugin.getServer().getPluginManager();
+ pluginManager.registerEvent(Type.PLAYER_QUIT, this, Priority.Low, plugin);
+ pluginManager.registerEvent(Type.PLAYER_CHAT, this, Priority.Lowest, plugin);
+ }
+
+ @Override
+ public void onPlayerChat(final PlayerChatEvent event)
+ {
+ if (event.getPlayer() == currentPlayer)
+ {
+ reactOnMessage(event.getMessage());
+ event.setCancelled(true);
+ return;
+ }
+ }
+
+ @Override
+ public void onPlayerJoin(final PlayerJoinEvent event)
+ {
+ final Player player = event.getPlayer();
+ if (player.hasPermission("essentials.update") && !updateCheck.isEssentialsInstalled())
+ {
+ player.sendMessage("Hello " + player.getDisplayName());
+ player.sendMessage("Please type /essentialsupdate into the chat to start the installation of Essentials.");
+ }
+ if (player.hasPermission("essentials.update"))
+ {
+ final UpdateCheck.CheckResult result = updateCheck.getResult();
+ switch (result)
+ {
+ case NEW_ESS:
+ player.sendMessage("The new version " + updateCheck.getNewVersion().toString() + " for Essentials is available. Please type /essentialsupdate to update.");
+ break;
+ case NEW_BUKKIT:
+ player.sendMessage("Your bukkit version is not the recommended build for Essentials, please update to version " + updateCheck.getNewBukkitVersion() + ".");
+ break;
+ case NEW_ESS_BUKKIT:
+ player.sendMessage("There is a new version " + updateCheck.getNewVersion().toString() + " of Essentials for Bukkit " + updateCheck.getNewBukkitVersion());
+ break;
+ default:
+ }
+ }
+ }
+
+ void doAutomaticUpdate()
+ {
+ final UpdatesDownloader downloader = new UpdatesDownloader();
+ final VersionInfo info = updateCheck.getNewVersionInfo();
+ final List<String> changelog = info.getChangelog();
+ Bukkit.getLogger().info("Essentials changelog " + updateCheck.getNewVersion().toString());
+ for (String line : changelog)
+ {
+ Bukkit.getLogger().info(" - "+line);
+ }
+ downloader.start(plugin.getServer().getUpdateFolderFile(), info);
+ }
+
+ void doManualUpdate()
+ {
+
+ }
+
+ void onCommand(CommandSender sender)
+ {
+ if (sender instanceof Player && sender.hasPermission("essentials.install"))
+ {
+ if (currentPlayer == null)
+ {
+ currentPlayer = (Player)sender;
+ if (updateCheck.isEssentialsInstalled())
+ {
+ doManualUpdate();
+ }
+ else
+ {
+ sender.sendMessage("Thank you for choosing Essentials.");
+ sender.sendMessage("The following installation wizard will guide you through the installation of Essentials.");
+ sender.sendMessage("Your answers will be saved for a later update.");
+ sender.sendMessage("Please answer the messages with yes or no, if not otherwise stated.");
+ sender.sendMessage("Write bye/exit/quit if you want to exit the wizard at anytime.");
+
+ }
+ }
+ if (!currentPlayer.equals(sender))
+ {
+ sender.sendMessage("The player " + currentPlayer.getDisplayName() + " is already using the wizard.");
+ }
+ }
+ else
+ {
+ sender.sendMessage("Please run the command as op from in game.");
+ }
+ }
+
+ private void reactOnMessage(String message)
+ {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java
new file mode 100644
index 000000000..28ffdfe3c
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java
@@ -0,0 +1,19 @@
+package com.earth2me.essentials.update;
+
+import java.io.File;
+
+
+public class UpdatesDownloader
+{
+
+ UpdatesDownloader()
+ {
+
+ }
+
+ void start(File updateFolderFile, VersionInfo newVersion)
+ {
+
+ }
+
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/Version.java b/EssentialsUpdate/src/com/earth2me/essentials/update/Version.java
new file mode 100644
index 000000000..8e6cbc97f
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/Version.java
@@ -0,0 +1,173 @@
+package com.earth2me.essentials.update;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+public class Version implements Comparable<Version>
+{
+ public enum Type
+ {
+ STABLE, PREVIEW, DEVELOPER
+ }
+
+ public int getMajor()
+ {
+ return major;
+ }
+
+ public int getMinor()
+ {
+ return minor;
+ }
+
+ public int getBuild()
+ {
+ return build;
+ }
+
+ public Type getType()
+ {
+ return type;
+ }
+ private final transient int major;
+ private final transient int minor;
+ private final transient int build;
+ private final transient Type type;
+
+ public Version(final String versionString)
+ {
+ final Matcher matcher = Pattern.compile("(Pre|Dev)?([0-9]+)[_\\.]([0-9]+)[_\\.]([0-9]+).*").matcher(versionString);
+ if (!matcher.matches() || matcher.groupCount() < 4)
+ {
+ type = Type.DEVELOPER;
+ major = 99;
+ minor = build = 0;
+ return;
+ }
+ if (versionString.startsWith("Pre"))
+ {
+ type = Type.PREVIEW;
+ }
+ else if (versionString.startsWith("Dev"))
+ {
+ type = Type.DEVELOPER;
+ }
+ else
+ {
+ type = Type.STABLE;
+ }
+ major = Integer.parseInt(matcher.group(2));
+ minor = Integer.parseInt(matcher.group(3));
+ build = Integer.parseInt(matcher.group(4));
+ }
+
+ @Override
+ public int compareTo(final Version other)
+ {
+ int ret = 0;
+ if (other.getType() == Type.DEVELOPER && getType() != Type.DEVELOPER)
+ {
+ ret = -1;
+ }
+ else if (getType() == Type.DEVELOPER && other.getType() != Type.DEVELOPER)
+ {
+ ret = 1;
+ }
+ else if (other.getMajor() > getMajor())
+ {
+ ret = -1;
+ }
+ else if (getMajor() > other.getMajor())
+ {
+ ret = 1;
+ }
+ else if (other.getMinor() > getMinor())
+ {
+ ret = -1;
+ }
+ else if (getMinor() > other.getMinor())
+ {
+ ret = 1;
+ }
+ else if (other.getBuild() > getBuild())
+ {
+ ret = -1;
+ }
+ else if (getBuild() > other.getBuild())
+ {
+ ret = 1;
+ }
+ else if (other.getType() == Type.STABLE && getType() == Type.PREVIEW)
+ {
+ ret = -1;
+ }
+ else if (getType() == Type.STABLE && other.getType() == Type.PREVIEW)
+ {
+ ret = 1;
+ }
+ return ret;
+ }
+
+ @Override
+ public boolean equals(final Object obj)
+ {
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ final Version other = (Version)obj;
+ if (this.major != other.major)
+ {
+ return false;
+ }
+ if (this.minor != other.minor)
+ {
+ return false;
+ }
+ if (this.build != other.build)
+ {
+ return false;
+ }
+ if (this.type != other.type)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 5;
+ hash = 71 * hash + this.major;
+ hash = 71 * hash + this.minor;
+ hash = 71 * hash + this.build;
+ hash = 71 * hash + (this.type != null ? this.type.hashCode() : 0);
+ return hash;
+ }
+
+ @Override
+ public String toString()
+ {
+ final StringBuilder builder = new StringBuilder();
+ if (type == Type.DEVELOPER)
+ {
+ builder.append("Dev");
+ }
+ if (type == Type.PREVIEW)
+ {
+ builder.append("Pre");
+ }
+ builder.append(major);
+ builder.append('.');
+ builder.append(minor);
+ builder.append('.');
+ builder.append(build);
+ return builder.toString();
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java b/EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java
new file mode 100644
index 000000000..9cd1e5edb
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java
@@ -0,0 +1,48 @@
+package com.earth2me.essentials.update;
+
+import java.util.ArrayList;
+import org.bukkit.configuration.Configuration;
+import java.util.Collections;
+import java.util.List;
+
+
+public class VersionInfo
+{
+ private final transient List<String> changelog;
+ private final transient int minBukkit;
+ private final transient int maxBukkit;
+ private final transient List<ModuleInfo> modules;
+
+ public VersionInfo(final Configuration updateConfig, final String path)
+ {
+ changelog = updateConfig.getList(path + ".changelog", Collections.<String>emptyList());
+ minBukkit = updateConfig.getInt(path + ".min-bukkit", 0);
+ maxBukkit = updateConfig.getInt(path + ".max-bukkit", 0);
+ modules = new ArrayList<ModuleInfo>();
+ final String modulesPath = path + ".modules";
+ for (String module : updateConfig.getKeys(false))
+ {
+ modules.add(new ModuleInfo(updateConfig, modulesPath + module));
+ }
+ }
+
+ public List<String> getChangelog()
+ {
+ return Collections.unmodifiableList(changelog);
+ }
+
+ public int getMinBukkit()
+ {
+ return minBukkit;
+ }
+
+ public int getMaxBukkit()
+ {
+ return maxBukkit;
+ }
+
+ public List<ModuleInfo> getModules()
+ {
+ return Collections.unmodifiableList(modules);
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java b/EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java
new file mode 100644
index 000000000..56d2445c9
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java
@@ -0,0 +1,7 @@
+package com.earth2me.essentials.update.states;
+
+
+public class Modules
+{
+
+}
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?" );
+ }
+}
+
+
diff --git a/EssentialsUpdate/src/plugin.yml b/EssentialsUpdate/src/plugin.yml
new file mode 100644
index 000000000..b8dbe8e25
--- /dev/null
+++ b/EssentialsUpdate/src/plugin.yml
@@ -0,0 +1,21 @@
+# This determines the command prefix when there are conflicts (/name:home, /name:help, etc.)
+name: EssentialsUpdate
+main: com.earth2me.essentials.update.EssentialsUpdate
+# Note to developers: This next line cannot change, or the automatic versioning system will break.
+version: TeamCity
+description: This plugin allows to install or update all Essentials plugins
+authors: [snowleo]
+commands:
+ essentialsupdate:
+ description: Install or update the Essentials plugins.
+ usage: /<command>
+ essentialshelp:
+ description: Get help from the Essentials support chat.
+ usage: /<command>
+permissions:
+ essentials.update:
+ description: Allows you to update Essentials
+ default: op
+ essentials.helpchat:
+ description: Allows you to join Essentials help chat
+ default: op \ No newline at end of file
diff --git a/EssentialsUpdate/test/com/earth2me/essentials/update/UploadTest.java b/EssentialsUpdate/test/com/earth2me/essentials/update/UploadTest.java
new file mode 100644
index 000000000..a51f03bd7
--- /dev/null
+++ b/EssentialsUpdate/test/com/earth2me/essentials/update/UploadTest.java
@@ -0,0 +1,27 @@
+package com.earth2me.essentials.update;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import junit.framework.TestCase;
+import org.junit.Test;
+
+
+public class UploadTest extends TestCase
+{
+ @Test
+ public void testPastieUpload()
+ {
+ try
+ {
+ final PastieUpload pastie = new PastieUpload();
+ assertNotNull(pastie);
+ //final String url = pastie.send("test");
+ //System.out.println(url);
+ }
+ catch (IOException ex)
+ {
+ Logger.getLogger(UploadTest.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+}
diff --git a/EssentialsUpdate/test/com/earth2me/essentials/update/VersionTest.java b/EssentialsUpdate/test/com/earth2me/essentials/update/VersionTest.java
new file mode 100644
index 000000000..901a8f9dc
--- /dev/null
+++ b/EssentialsUpdate/test/com/earth2me/essentials/update/VersionTest.java
@@ -0,0 +1,87 @@
+package com.earth2me.essentials.update;
+
+import com.earth2me.essentials.update.Version.Type;
+import java.util.TreeSet;
+import junit.framework.TestCase;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+
+public class VersionTest extends TestCase
+{
+ @Test
+ public void testStable()
+ {
+ final Version instance = new Version("1.2.3");
+ assertEquals("Testing Major", 1, instance.getMajor());
+ assertEquals("Testing Minor", 2, instance.getMinor());
+ assertEquals("Testing Build", 3, instance.getBuild());
+ assertEquals("Testing Type", Type.STABLE, instance.getType());
+ }
+
+ @Test
+ public void testDev()
+ {
+ final Version instance = new Version("Dev2.3.4");
+ assertEquals("Testing Major", 2, instance.getMajor());
+ assertEquals("Testing Minor", 3, instance.getMinor());
+ assertEquals("Testing Build", 4, instance.getBuild());
+ assertEquals("Testing Type", Type.DEVELOPER, instance.getType());
+ }
+
+ @Test
+ public void testTeamCity()
+ {
+ final Version instance = new Version("Teamcity");
+ assertEquals("Testing Type", Type.DEVELOPER, instance.getType());
+ }
+
+ @Test
+ public void testPre()
+ {
+ final Version instance = new Version("Pre5.7.400.2");
+ assertEquals("Testing Major", 5, instance.getMajor());
+ assertEquals("Testing Minor", 7, instance.getMinor());
+ assertEquals("Testing Build", 400, instance.getBuild());
+ assertEquals("Testing Type", Type.PREVIEW, instance.getType());
+ }
+
+ @Test
+ public void testCompareTo()
+ {
+ Version a = new Version("1.1.1");
+ Version b = new Version("Dev1.1.2");
+ Version c = new Version("1.1.2");
+ Version d = new Version("1.2.0");
+ Version e = new Version("2.0.0");
+ Version f = new Version("Pre1.1.1.1");
+ Version g = new Version("Dev1.2.2");
+ assertTrue("Testing dev", a.compareTo(b) < 0);
+ assertTrue("Testing dev", b.compareTo(a) > 0);
+ assertTrue("Testing build", a.compareTo(c) < 0);
+ assertTrue("Testing build", c.compareTo(a) > 0);
+ assertTrue("Testing minor", a.compareTo(d) < 0);
+ assertTrue("Testing minor", d.compareTo(a) > 0);
+ assertTrue("Testing major", a.compareTo(e) < 0);
+ assertTrue("Testing major", e.compareTo(a) > 0);
+ assertTrue("Testing pre", f.compareTo(a) < 0);
+ assertTrue("Testing pre", a.compareTo(f) > 0);
+ assertTrue("Testing dev vs dev", b.compareTo(g) < 0);
+ assertTrue("Testing dev vs dev", g.compareTo(b) > 0);
+ final TreeSet<Version> set = new TreeSet<Version>();
+ set.add(a);
+ set.add(b);
+ set.add(c);
+ set.add(d);
+ set.add(e);
+ set.add(f);
+ set.add(g);
+ assertEquals("Testing sorting", f, set.pollFirst());
+ assertEquals("Testing sorting", a, set.pollFirst());
+ assertEquals("Testing sorting", c, set.pollFirst());
+ assertEquals("Testing sorting", d, set.pollFirst());
+ assertEquals("Testing sorting", e, set.pollFirst());
+ assertEquals("Testing sorting", b, set.pollFirst());
+ assertEquals("Testing sorting", g, set.pollFirst());
+ }
+}