EssentialsUpdate WIP

This commit is contained in:
snowleo 2011-10-12 03:14:07 +02:00
parent 9ec398b39b
commit 860d446d28
169 changed files with 13835 additions and 1 deletions

3
.gitignore vendored
View File

@ -32,4 +32,5 @@
/YamlAnnotations/
/EssentialsUpdate/nbproject/private/
/EssentialsRelease/
/EssentialsUpdate/
/EssentialsUpdate/dist/
/EssentialsUpdate/build/

View File

@ -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>

View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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());
}
}
}
}

View File

@ -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;
}
}

View File

@ -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.");
}
}
}

View File

@ -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;
}
}

View File

@ -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");
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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");
}
}

View File

@ -0,0 +1,19 @@
package com.earth2me.essentials.update;
import java.io.File;
public class UpdatesDownloader
{
UpdatesDownloader()
{
}
void start(File updateFolderFile, VersionInfo newVersion)
{
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,7 @@
package com.earth2me.essentials.update.states;
public class Modules
{
}

View File

@ -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
}

View File

@ -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();
}

View File

@ -0,0 +1,5 @@
package f00f.net.irc.martyr;
public class CommandObserver extends StateObserver
{
}

View File

@ -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 );
}
}

View File

@ -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 );
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}
}

View File

@ -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 );
}

View File

@ -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
}

File diff suppressed because it is too large Load Diff

View File

@ -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 );
}

View File

@ -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 --------------------------------------------
}

View File

@ -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;
}
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -0,0 +1,8 @@
package f00f.net.irc.martyr;
/**
* Should the state and state observer be one?
*/
public class StateObserver extends ForwardObservable
{
}

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -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;
}
}

View File

@ -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;
}*/
}

View File

@ -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;
}*/
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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 );
}
}

View File

@ -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);
}
}
}

View File

@ -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";
}
}

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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";
}
}

View File

@ -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";
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -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 );
}
}

View File

@ -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 );
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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
{
}

View File

@ -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 );
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

Some files were not shown because too many files have changed in this diff Show More