1
0
mirror of https://github.com/essentials/Essentials.git synced 2025-10-02 09:07:08 +02:00

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

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

View File

@@ -0,0 +1,40 @@
package f00f.net.irc.martyr.errors;
import f00f.net.irc.martyr.InCommand;
/**
* Code: 464 ERR_PASSWDMISMATCH
* :Password incorrect
* Returned to indicate a failed attempt at registering a connection for which a
* password was required and was either not given or incorrect.
*/
public class PasswdMismatchError extends GenericError
{
private String errorMessage;
public PasswdMismatchError()
{
}
public PasswdMismatchError(String errorMessage)
{
this.errorMessage = errorMessage;
}
public String getIrcIdentifier()
{
return "464";
}
public InCommand parse( String prefix, String identifier, String params )
{
return new PasswdMismatchError(getParameter(params, 1));
}
public String getErrorMessage()
{
return errorMessage;
}
}

View File

@@ -0,0 +1,40 @@
package f00f.net.irc.martyr.errors;
import f00f.net.irc.martyr.InCommand;
/**
* Code: 445 ERR_SUMMONDISABLED
* :SUMMON has been disabled
* Returned as a response to the SUMMON command. Must be returned by any server
* which does not implement it.
*/
public class SummonDisabledError extends GenericError
{
private String errorMessage;
public SummonDisabledError()
{
}
public SummonDisabledError(String errorMessage)
{
this.errorMessage = errorMessage;
}
public String getIrcIdentifier()
{
return "445";
}
public InCommand parse( String prefix, String identifier, String params )
{
return new SummonDisabledError(getParameter(params, 1));
}
public String getErrorMessage()
{
return errorMessage;
}
}

View File

@@ -0,0 +1,46 @@
package f00f.net.irc.martyr.errors;
import f00f.net.irc.martyr.InCommand;
/**
* Code: 405 ERR_TOOMANYCHANNELS
* &lt;channel name&gt; :You have joined too many channels
* Sent to a user when they have joined the maximum number of allowed channels and they try to join another channel.
*/
public class TooManyChannelsError extends GenericError
{
private String channel;
private String errorMessage;
public TooManyChannelsError()
{
}
public TooManyChannelsError(String channel, String errorMessage)
{
this.channel = channel;
this.errorMessage = errorMessage;
}
public String getIrcIdentifier()
{
return "405";
}
public InCommand parse( String prefix, String identifier, String params )
{
return new TooManyChannelsError(getParameter(params, 1), getParameter(params, 2));
}
public String getChannel()
{
return channel;
}
public String getErrorMessage()
{
return errorMessage;
}
}

View File

@@ -0,0 +1,47 @@
package f00f.net.irc.martyr.errors;
import f00f.net.irc.martyr.InCommand;
/**
* Code: 407 ERR_TOOMANYTARGETS
* &lt;target&gt; :Duplicate recipients. No message delivered
* Returned to a client which is attempting to send a PRIVMSG/NOTICE using the user@host destination
* format and for a user@host which has several occurrences.
*/
public class TooManyTargetsError extends GenericError
{
private String dest;
private String errorMessage;
public TooManyTargetsError()
{
}
public TooManyTargetsError(String dest, String errorMessage)
{
this.dest = dest;
this.errorMessage = errorMessage;
}
public String getIrcIdentifier()
{
return "407";
}
public InCommand parse( String prefix, String identifier, String params )
{
return new TooManyTargetsError(getParameter(params, 1), getParameter(params, 2));
}
public String getDest()
{
return dest;
}
public String getErrorMessage()
{
return errorMessage;
}
}

View File

@@ -0,0 +1,40 @@
package f00f.net.irc.martyr.errors;
import f00f.net.irc.martyr.InCommand;
/**
* Code: 501 ERR_UMODEUNKNOWNFLAG
* :Unknown MODE flag
* Returned by the server to indicate that a MODE message was sent with a nickname
* parameter and that the a mode flag sent was not recognized.
*/
public class UModeUnknownFlagError extends GenericError
{
private String errorMessage;
public UModeUnknownFlagError()
{
}
public UModeUnknownFlagError(String errorMessage)
{
this.errorMessage = errorMessage;
}
public String getIrcIdentifier()
{
return "501";
}
public InCommand parse( String prefix, String identifier, String params )
{
return new UModeUnknownFlagError(getParameter(params, 1));
}
public String getErrorMessage()
{
return errorMessage;
}
}

View File

@@ -0,0 +1,46 @@
package f00f.net.irc.martyr.errors;
import f00f.net.irc.martyr.InCommand;
/**
* Code: 421 ERR_UNKNOWNCOMMAND
* &lt;command&gt; :Unknown command
* Returned to a registered client to indicate that the command sent is unknown by the server.
*/
public class UnknownCommandError extends GenericError
{
private String command;
private String errorMessage;
public UnknownCommandError()
{
}
public UnknownCommandError(String command, String errorMessage)
{
this.command = command;
this.errorMessage = errorMessage;
}
public String getIrcIdentifier()
{
return "421";
}
public InCommand parse( String prefix, String identifier, String params )
{
return new UnknownCommandError(getParameter(params, 1), getParameter(params, 2));
}
public String getCommand()
{
return command;
}
public String getErrorMessage()
{
return errorMessage;
}
}

View File

@@ -0,0 +1,69 @@
package f00f.net.irc.martyr.errors;
import f00f.net.irc.martyr.InCommand;
import f00f.net.irc.martyr.State;
import f00f.net.irc.martyr.commands.UnknownCommand;
/**
* Some unknown command, for which there is no factory. This is a
* special case command, created by IRCConnection if it can't find a
* proper command object.
*/
public class UnknownError extends UnknownCommand
{
private String errorStr;
private int errorCode;
public UnknownError( String ident )
{
errorStr = ident;
errorCode = Integer.parseInt( ident );
}
public int getErrorCode()
{
return errorCode;
}
public String getError()
{
return errorStr;
}
public static boolean isError( String ident )
{
char c = ident.charAt(0);
return ( c == '4' || c == '5' );
}
public State getState()
{
return State.UNKNOWN;
}
/**
* Never parsed.
*/
public InCommand parse( String prefix, String identifier, String params )
{
throw new UnsupportedOperationException("UnknownError does no parsing.");
}
/**
* Unknown, so we don't know what the identifier is ahead of time.
*/
public String getIrcIdentifier()
{
return errorStr;
}
public String toString()
{
return "UnknownError[" + errorStr + "]";
}
}

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