summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--Essentials/nbproject/project.properties7
-rw-r--r--Essentials/src/com/earth2me/essentials/Essentials.java1
-rw-r--r--Essentials/src/com/earth2me/essentials/EssentialsPlayerListener.java12
-rw-r--r--Essentials/src/com/earth2me/essentials/OfflinePlayer.java70
-rw-r--r--Essentials/src/com/earth2me/essentials/PlayerExtension.java39
-rw-r--r--Essentials/src/com/earth2me/essentials/PlayerWrapper.java777
-rw-r--r--Essentials/src/com/earth2me/essentials/Settings.java7
-rw-r--r--Essentials/src/com/earth2me/essentials/User.java20
-rw-r--r--Essentials/src/com/earth2me/essentials/commands/Commandban.java2
-rw-r--r--Essentials/src/com/earth2me/essentials/commands/Commandcompass.java47
-rw-r--r--Essentials/src/com/earth2me/essentials/commands/Commandgetpos.java6
-rw-r--r--Essentials/src/com/earth2me/essentials/commands/Commandmail.java56
-rw-r--r--Essentials/src/com/earth2me/essentials/commands/Commandrealname.java5
-rw-r--r--Essentials/src/com/earth2me/essentials/commands/Commandweather.java43
-rw-r--r--Essentials/src/com/earth2me/essentials/commands/Commandworth.java49
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/Backup.java17
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/Chat.java32
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/Commands.java47
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/Economy.java42
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/General.java33
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/GroupOptions.java27
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/Groups.java27
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/Location.java28
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/Settings.java58
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/commands/Afk.java36
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/commands/God.java14
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/commands/Help.java23
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/commands/Home.java24
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/commands/Kit.java28
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/commands/KitObject.java18
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/commands/Lightning.java14
-rw-r--r--Essentials/src/com/earth2me/essentials/settings/commands/Spawnmob.java14
-rw-r--r--Essentials/src/com/earth2me/essentials/storage/Comment.java16
-rw-r--r--Essentials/src/com/earth2me/essentials/storage/ListType.java14
-rw-r--r--Essentials/src/com/earth2me/essentials/storage/MapType.java14
-rw-r--r--Essentials/src/com/earth2me/essentials/storage/StorageObject.java250
-rw-r--r--Essentials/src/messages.properties8
-rw-r--r--Essentials/src/messages_da.properties8
-rw-r--r--Essentials/src/messages_de.properties41
-rw-r--r--Essentials/src/messages_en.properties8
-rw-r--r--Essentials/src/messages_fr.properties8
-rw-r--r--Essentials/src/messages_nl.properties8
-rw-r--r--Essentials/test/com/earth2me/essentials/FakeServer.java65
-rw-r--r--Essentials/test/com/earth2me/essentials/StorageTest.java37
-rw-r--r--EssentialsGroupManager/src/Changelog.txt11
-rw-r--r--EssentialsGroupManager/src/config.yml13
-rw-r--r--EssentialsGroupManager/src/org/anjocaido/groupmanager/GMConfiguration.java25
-rw-r--r--EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java16
-rw-r--r--EssentialsGroupManager/src/org/anjocaido/groupmanager/data/DataUnit.java2
-rw-r--r--EssentialsGroupManager/src/org/anjocaido/groupmanager/data/Group.java6
-rw-r--r--EssentialsGroupManager/src/org/anjocaido/groupmanager/data/User.java2
-rw-r--r--EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java10
-rw-r--r--EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java29
-rw-r--r--EssentialsGroupManager/src/org/anjocaido/groupmanager/utils/Tasks.java6
-rw-r--r--EssentialsGroupManager/src/plugin.yml2
-rw-r--r--EssentialsUpdate/build.xml74
-rw-r--r--EssentialsUpdate/manifest.mf3
-rw-r--r--EssentialsUpdate/nbproject/build-impl.xml1067
-rw-r--r--EssentialsUpdate/nbproject/genfiles.properties8
-rw-r--r--EssentialsUpdate/nbproject/project.properties75
-rw-r--r--EssentialsUpdate/nbproject/project.xml18
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsHelp.java479
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsUpdate.java66
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java112
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/IrcBot.java188
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/ModuleInfo.java35
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/PastieUpload.java40
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/PostToUrl.java66
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/UpdateCheck.java203
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/UpdateFile.java204
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java128
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java19
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/Version.java173
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java48
-rw-r--r--EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java7
-rwxr-xr-xEssentialsUpdate/src/org/jibble/pircbot/Colors.java293
-rwxr-xr-xEssentialsUpdate/src/org/jibble/pircbot/InputThread.java169
-rwxr-xr-xEssentialsUpdate/src/org/jibble/pircbot/IrcException.java35
-rwxr-xr-xEssentialsUpdate/src/org/jibble/pircbot/NickAlreadyInUseException.java38
-rwxr-xr-xEssentialsUpdate/src/org/jibble/pircbot/OutputThread.java105
-rwxr-xr-xEssentialsUpdate/src/org/jibble/pircbot/PircBot.java2808
-rwxr-xr-xEssentialsUpdate/src/org/jibble/pircbot/Queue.java146
-rwxr-xr-xEssentialsUpdate/src/org/jibble/pircbot/ReplyConstants.java176
-rwxr-xr-xEssentialsUpdate/src/org/jibble/pircbot/User.java161
-rw-r--r--EssentialsUpdate/src/plugin.yml21
-rw-r--r--EssentialsUpdate/test/com/earth2me/essentials/update/UploadTest.java27
-rw-r--r--EssentialsUpdate/test/com/earth2me/essentials/update/VersionTest.java87
-rw-r--r--WebPush/apikey.php5
-rw-r--r--WebPush/index.php52
-rw-r--r--WebPush/nbproject/private/private.properties8
-rw-r--r--WebPush/nbproject/project.properties7
-rw-r--r--WebPush/nbproject/project.xml9
-rw-r--r--WebPush/simple_html_dom.php1727
-rw-r--r--WebPush/upload.php100
-rw-r--r--lib/bukkit-0.0.1-SNAPSHOT.jarbin4381379 -> 4382157 bytes
-rw-r--r--lib/craftbukkit-0.0.1-SNAPSHOT.jarbin9585907 -> 9590762 bytes
-rw-r--r--lib/lombok-0.10.1.jarbin0 -> 1668835 bytes
98 files changed, 10248 insertions, 968 deletions
diff --git a/.gitignore b/.gitignore
index 4d5963e6b..c1c78f213 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,4 +32,9 @@
/YamlAnnotations/
/EssentialsUpdate/nbproject/private/
/EssentialsRelease/
-/EssentialsUpdate/ \ No newline at end of file
+/EssentialsUpdate/dist/
+/EssentialsUpdate/build/
+/WebPush/apikey.php
+
+/WebPush/apikey.php
+/WebPush/apikey.php \ No newline at end of file
diff --git a/Essentials/nbproject/project.properties b/Essentials/nbproject/project.properties
index db78855e6..bea59e4ab 100644
--- a/Essentials/nbproject/project.properties
+++ b/Essentials/nbproject/project.properties
@@ -1,6 +1,7 @@
annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
-annotation.processing.run.all.processors=true
+annotation.processing.processors.list=lombok.core.AnnotationProcessor
+annotation.processing.run.all.processors=false
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
application.title=Essentials
application.vendor=
@@ -68,6 +69,7 @@ file.reference.iCo4.jar=../lib/iCo4.jar
file.reference.iCo5.jar=../lib/iCo5.jar
file.reference.iCo6.jar=../lib/iCo6.jar
file.reference.junit-4.5.jar=..\\lib\\junit_4\\junit-4.5.jar
+file.reference.lombok-0.10.1.jar=../lib/lombok-0.10.1.jar
file.reference.MultiCurrency.jar=../lib/MultiCurrency.jar
file.reference.Permissions3.jar=../lib/Permissions3.jar
file.reference.PermissionsBukkit-1.2.jar=../lib/PermissionsBukkit-1.2.jar
@@ -86,7 +88,8 @@ javac.classpath=\
${file.reference.BOSEconomy7.jar}:\
${file.reference.PermissionsEx.jar}:\
${file.reference.bPermissions.jar}:\
- ${file.reference.PermissionsBukkit-1.2.jar}
+ ${file.reference.PermissionsBukkit-1.2.jar}:\
+ ${file.reference.lombok-0.10.1.jar}
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
diff --git a/Essentials/src/com/earth2me/essentials/Essentials.java b/Essentials/src/com/earth2me/essentials/Essentials.java
index 6f9b9a944..fe0c3560c 100644
--- a/Essentials/src/com/earth2me/essentials/Essentials.java
+++ b/Essentials/src/com/earth2me/essentials/Essentials.java
@@ -164,7 +164,6 @@ public class Essentials extends JavaPlugin implements IEssentials
pm.registerEvent(Type.PLAYER_EGG_THROW, playerListener, Priority.High, this);
pm.registerEvent(Type.PLAYER_BUCKET_EMPTY, playerListener, Priority.High, this);
pm.registerEvent(Type.PLAYER_ANIMATION, playerListener, Priority.High, this);
- pm.registerEvent(Type.PLAYER_BED_ENTER, playerListener, Priority.Lowest, this);
final EssentialsBlockListener blockListener = new EssentialsBlockListener(this);
pm.registerEvent(Type.BLOCK_PLACE, blockListener, Priority.Lowest, this);
diff --git a/Essentials/src/com/earth2me/essentials/EssentialsPlayerListener.java b/Essentials/src/com/earth2me/essentials/EssentialsPlayerListener.java
index 3d5851d93..9147acaf0 100644
--- a/Essentials/src/com/earth2me/essentials/EssentialsPlayerListener.java
+++ b/Essentials/src/com/earth2me/essentials/EssentialsPlayerListener.java
@@ -388,16 +388,4 @@ public class EssentialsPlayerListener extends PlayerListener
user.updateActivity(true);
}
}
-
- @Override
- public void onPlayerBedEnter(PlayerBedEnterEvent event)
- {
- if (event.isCancelled()) {
- return;
- }
- if (event.getPlayer().isSleepingIgnored()) {
- event.setCancelled(true);
- event.getPlayer().sendMessage("You can't go to bed, your sleep is ignored.");
- }
- }
}
diff --git a/Essentials/src/com/earth2me/essentials/OfflinePlayer.java b/Essentials/src/com/earth2me/essentials/OfflinePlayer.java
index 3ca0c17f4..8e50aa1bd 100644
--- a/Essentials/src/com/earth2me/essentials/OfflinePlayer.java
+++ b/Essentials/src/com/earth2me/essentials/OfflinePlayer.java
@@ -3,9 +3,9 @@ package com.earth2me.essentials;
import java.net.InetSocketAddress;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import lombok.Delegate;
import org.bukkit.Achievement;
import org.bukkit.Effect;
import org.bukkit.GameMode;
@@ -36,40 +36,32 @@ import org.bukkit.util.Vector;
public class OfflinePlayer implements Player
{
- private final String name;
- final transient IEssentials ess;
+ private final transient IEssentials ess;
private Location location = new Location(null, 0, 0, 0, 0, 0);
private World world;
private UUID uniqueId = UUID.randomUUID();
- private org.bukkit.OfflinePlayer base;
+ @Delegate(types=org.bukkit.OfflinePlayer.class)
+ private final org.bukkit.OfflinePlayer base;
- public OfflinePlayer(String name, IEssentials ess)
+ public OfflinePlayer(final String name, final IEssentials ess)
{
- this.name = name;
this.ess = ess;
this.world = ess.getServer().getWorlds().get(0);
this.base = ess.getServer().getOfflinePlayer(name);
}
- public boolean isOnline()
- {
- return base.isOnline();
- }
-
- public boolean isOp()
- {
- return base.isOp();
- }
-
- public void sendMessage(String string)
+ @Override
+ public void sendMessage(final String string)
{
}
+ @Override
public String getDisplayName()
{
- return name;
+ return base.getName();
}
+ @Override
public void setDisplayName(String string)
{
}
@@ -87,11 +79,6 @@ public class OfflinePlayer implements Player
{
}
- public String getName()
- {
- return name;
- }
-
public PlayerInventory getInventory()
{
return null;
@@ -577,12 +564,7 @@ public class OfflinePlayer implements Player
{
throw new UnsupportedOperationException("Not supported yet.");
}
-
- public void setOp(boolean bln)
- {
- base.setOp(bln);
- }
-
+
@Override
public void sendMap(MapView mv)
{
@@ -590,30 +572,6 @@ public class OfflinePlayer implements Player
}
@Override
- public boolean isBanned()
- {
- return base.isBanned();
- }
-
- @Override
- public void setBanned(boolean bln)
- {
- base.setBanned(bln);
- }
-
- @Override
- public boolean isWhitelisted()
- {
- return base.isWhitelisted();
- }
-
- @Override
- public void setWhitelisted(boolean bln)
- {
- base.setWhitelisted(bln);
- }
-
- @Override
public GameMode getGameMode()
{
throw new UnsupportedOperationException("Not supported yet.");
@@ -738,10 +696,4 @@ public class OfflinePlayer implements Player
{
throw new UnsupportedOperationException("Not supported yet.");
}
-
- @Override
- public Map<String, Object> serialize()
- {
- throw new UnsupportedOperationException("Not supported yet.");
- }
}
diff --git a/Essentials/src/com/earth2me/essentials/PlayerExtension.java b/Essentials/src/com/earth2me/essentials/PlayerExtension.java
index 9555bb1a7..11c17761c 100644
--- a/Essentials/src/com/earth2me/essentials/PlayerExtension.java
+++ b/Essentials/src/com/earth2me/essentials/PlayerExtension.java
@@ -1,28 +1,53 @@
package com.earth2me.essentials;
+import lombok.Delegate;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.IInventory;
+import org.bukkit.command.CommandSender;
+import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.craftbukkit.inventory.CraftInventoryPlayer;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
+import org.bukkit.permissions.Permissible;
+import org.bukkit.permissions.ServerOperator;
-public class PlayerExtension extends PlayerWrapper
+public class PlayerExtension implements Player
{
- protected final IEssentials ess;
+ protected final transient IEssentials ess;
+ @Delegate(types =
+ {
+ Player.class, Entity.class, CommandSender.class, ServerOperator.class,
+ HumanEntity.class, ConfigurationSerializable.class, LivingEntity.class,
+ Permissible.class
+ })
+ protected Player base;
- public PlayerExtension(Player base, IEssentials ess)
+ public PlayerExtension(final Player base, final IEssentials ess)
{
- super(base);
+ this.base = base;
this.ess = ess;
}
- public void showInventory(IInventory inventory)
+ public final Player getBase()
+ {
+ return base;
+ }
+
+ public final Player setBase(final Player base)
+ {
+ return this.base = base;
+ }
+
+ public void showInventory(final IInventory inventory)
{
getHandle().a(inventory);
}
- public void showInventory(CraftInventoryPlayer inventory)
+ public void showInventory(final CraftInventoryPlayer inventory)
{
showInventory((IInventory)inventory.getInventory());
}
@@ -37,7 +62,7 @@ public class PlayerExtension extends PlayerWrapper
return ess.getPermissionsHandler().getGroup(base);
}
- public boolean inGroup(String group)
+ public boolean inGroup(final String group)
{
return ess.getPermissionsHandler().inGroup(base, group);
}
diff --git a/Essentials/src/com/earth2me/essentials/PlayerWrapper.java b/Essentials/src/com/earth2me/essentials/PlayerWrapper.java
deleted file mode 100644
index ccb61d891..000000000
--- a/Essentials/src/com/earth2me/essentials/PlayerWrapper.java
+++ /dev/null
@@ -1,777 +0,0 @@
-package com.earth2me.essentials;
-
-import java.net.InetSocketAddress;
-import java.util.*;
-import org.bukkit.*;
-import org.bukkit.block.Block;
-import org.bukkit.entity.*;
-import org.bukkit.event.entity.EntityDamageEvent;
-import org.bukkit.inventory.*;
-import org.bukkit.map.MapView;
-import org.bukkit.permissions.Permission;
-import org.bukkit.permissions.PermissionAttachment;
-import org.bukkit.permissions.PermissionAttachmentInfo;
-import org.bukkit.plugin.Plugin;
-import org.bukkit.util.Vector;
-
-public class PlayerWrapper implements Player
-{
- protected Player base;
-
- public PlayerWrapper(Player base)
- {
- this.base = base;
- }
-
- public final Player getBase()
- {
- return base;
- }
-
- public final Player setBase(Player base)
- {
- return this.base = base;
- }
-
- @Override
- public void setDisplayName(String string)
- {
- base.setDisplayName(string);
- }
-
- @Override
- public void setCompassTarget(Location lctn)
- {
- base.setCompassTarget(lctn);
- }
-
- @Override
- public InetSocketAddress getAddress()
- {
- return base.getAddress();
- }
-
- @Override
- public void kickPlayer(String string)
- {
- base.kickPlayer(string);
- }
-
- @Override
- public String getName()
- {
- return base.getName();
- }
-
- @Override
- public PlayerInventory getInventory()
- {
- return base.getInventory();
- }
-
- @Override
- public ItemStack getItemInHand()
- {
- return base.getItemInHand();
- }
-
- @Override
- public void setItemInHand(ItemStack is)
- {
- base.setItemInHand(is);
- }
-
- @Override
- public int getHealth()
- {
- return base.getHealth();
- }
-
- @Override
- public void setHealth(int i)
- {
- base.setHealth(i);
- }
-
- @Override
- public Egg throwEgg()
- {
- return base.throwEgg();
- }
-
- @Override
- public Snowball throwSnowball()
- {
- return base.throwSnowball();
- }
-
- @Override
- public Arrow shootArrow()
- {
- return base.shootArrow();
- }
-
- @Override
- public boolean isInsideVehicle()
- {
- return base.isInsideVehicle();
- }
-
- @Override
- public boolean leaveVehicle()
- {
- return base.leaveVehicle();
- }
-
- @Override
- public Vehicle getVehicle()
- {
- return base.getVehicle();
- }
-
- @Override
- public Location getLocation()
- {
- return base.getLocation();
- }
-
- @Override
- public World getWorld()
- {
- return base.getWorld();
- }
-
- @Override
- public Server getServer()
- {
- return base.getServer();
- }
-
- @Override
- public boolean isOnline()
- {
- return base.isOnline();
- }
-
- @Override
- public boolean isOp()
- {
- return base.isOp();
- }
-
- @Override
- public boolean teleport(Location lctn)
- {
- return base.teleport(lctn);
- }
-
- @Override
- public boolean teleport(Entity entity)
- {
- return base.teleport(entity);
- }
-
- @Override
- public void sendMessage(String string)
- {
- base.sendMessage(string);
- }
-
- @Override
- public void setVelocity(Vector vector)
- {
- base.setVelocity(vector);
- }
-
- @Override
- public Vector getVelocity()
- {
- return base.getVelocity();
- }
-
- @Override
- public double getEyeHeight()
- {
- return base.getEyeHeight();
- }
-
- @Override
- public double getEyeHeight(boolean bln)
- {
- return base.getEyeHeight(bln);
- }
-
- @Override
- public List<Block> getLineOfSight(HashSet<Byte> hs, int i)
- {
- return base.getLineOfSight(hs, i);
- }
-
- @Override
- public Block getTargetBlock(HashSet<Byte> hs, int i)
- {
- return base.getTargetBlock(hs, i);
- }
-
- @Override
- public List<Block> getLastTwoTargetBlocks(HashSet<Byte> hs, int i)
- {
- return base.getLastTwoTargetBlocks(hs, i);
- }
-
- @Override
- public int getFireTicks()
- {
- return base.getFireTicks();
- }
-
- @Override
- public int getMaxFireTicks()
- {
- return base.getMaxFireTicks();
- }
-
- @Override
- public void setFireTicks(int i)
- {
- base.setFireTicks(i);
- }
-
- @Override
- public void remove()
- {
- base.remove();
- }
-
- /**
- * This is not deprecated because the underlying method isn't really deprecated; rather, it's just "imperfect". By
- * We will continue to use this method even after the underlying CraftBukkit method is changed, so do not deprecate
- * it. Chances are Bukkit will also choose to un-deprecate this method at some point.
- */
- @Override
- public void updateInventory()
- {
- base.updateInventory();
- }
-
- @Override
- public void chat(String string)
- {
- base.chat(string);
- }
-
- @Override
- public boolean isSneaking()
- {
- return base.isSneaking();
- }
-
- @Override
- public void setSneaking(boolean bln)
- {
- base.setSneaking(bln);
- }
-
- @Override
- public int getEntityId()
- {
- return base.getEntityId();
- }
-
- @Override
- public boolean performCommand(String string)
- {
- return base.performCommand(string);
- }
-
- @Override
- public int getRemainingAir()
- {
- return base.getRemainingAir();
- }
-
- @Override
- public void setRemainingAir(int i)
- {
- base.setRemainingAir(i);
- }
-
- @Override
- public int getMaximumAir()
- {
- return base.getMaximumAir();
- }
-
- @Override
- public void setMaximumAir(int i)
- {
- base.setMaximumAir(i);
- }
-
- @Override
- public String getDisplayName()
- {
- if (base.getDisplayName() != null)
- return base.getDisplayName();
- else
- return base.getName();
- }
-
- @Override
- public void damage(int i)
- {
- base.damage(i);
- }
-
- @Override
- public void damage(int i, Entity entity)
- {
- base.damage(i, entity);
- }
-
- @Override
- public Location getEyeLocation()
- {
- return base.getEyeLocation();
- }
-
- @Override
- public void sendRawMessage(String string) {
- base.sendRawMessage(string);
- }
-
- @Override
- public Location getCompassTarget()
- {
- return base.getCompassTarget();
- }
-
- @Override
- public int getMaximumNoDamageTicks()
- {
- return base.getMaximumNoDamageTicks();
- }
-
- @Override
- public void setMaximumNoDamageTicks(int i)
- {
- base.setMaximumNoDamageTicks(i);
- }
-
- @Override
- public int getLastDamage()
- {
- return base.getLastDamage();
- }
-
- @Override
- public void setLastDamage(int i)
- {
- base.setLastDamage(i);
- }
-
- @Override
- public int getNoDamageTicks()
- {
- return base.getNoDamageTicks();
- }
-
- @Override
- public void setNoDamageTicks(int i)
- {
- base.setNoDamageTicks(i);
- }
-
- @Override
- public Entity getPassenger()
- {
- return base.getPassenger();
- }
-
- @Override
- public boolean setPassenger(Entity entity)
- {
- return base.setPassenger(entity);
- }
-
- @Override
- public boolean isEmpty()
- {
- return base.isEmpty();
- }
-
- @Override
- public boolean eject()
- {
- return base.eject();
- }
-
- public void saveData()
- {
- base.saveData();
- }
-
- public void loadData()
- {
- base.loadData();
- }
-
- public boolean isSleeping()
- {
- return base.isSleeping();
- }
-
- public int getSleepTicks()
- {
- return base.getSleepTicks();
- }
-
- public List<Entity> getNearbyEntities(double d, double d1, double d2)
- {
- return base.getNearbyEntities(d, d1, d2);
- }
-
- public boolean isDead()
- {
- return base.isDead();
- }
-
- public float getFallDistance()
- {
- return base.getFallDistance();
- }
-
- public void setFallDistance(float f)
- {
- base.setFallDistance(f);
- }
-
- public void setSleepingIgnored(boolean bln)
- {
- base.setSleepingIgnored(bln);
- }
-
- public boolean isSleepingIgnored()
- {
- return base.isSleepingIgnored();
- }
-
- public void awardAchievement(Achievement a)
- {
- base.awardAchievement(a);
- }
-
- public void incrementStatistic(Statistic ststc)
- {
- base.incrementStatistic(ststc);
- }
-
- public void incrementStatistic(Statistic ststc, int i)
- {
- base.incrementStatistic(ststc, i);
- }
-
- public void incrementStatistic(Statistic ststc, Material mtrl)
- {
- base.incrementStatistic(ststc, mtrl);
- }
-
- public void incrementStatistic(Statistic ststc, Material mtrl, int i)
- {
- base.incrementStatistic(ststc, mtrl, i);
- }
-
- public void playNote(Location lctn, byte b, byte b1)
- {
- base.playNote(lctn, b, b1);
- }
-
- public void sendBlockChange(Location lctn, Material mtrl, byte b)
- {
- base.sendBlockChange(lctn, mtrl, b);
- }
-
- public void sendBlockChange(Location lctn, int i, byte b)
- {
- base.sendBlockChange(lctn, i, b);
- }
-
- public void setLastDamageCause(EntityDamageEvent ede)
- {
- base.setLastDamageCause(ede);
-
- }
-
- public EntityDamageEvent getLastDamageCause()
- {
- return base.getLastDamageCause();
- }
-
- public void playEffect(Location lctn, Effect effect, int i)
- {
- base.playEffect(lctn, effect, i);
- }
-
- public boolean sendChunkChange(Location lctn, int i, int i1, int i2, byte[] bytes)
- {
- return base.sendChunkChange(lctn, i, i1, i2, bytes);
- }
-
- public UUID getUniqueId()
- {
- return base.getUniqueId();
- }
-
- public void playNote(Location lctn, Instrument i, Note note)
- {
- base.playNote(lctn, i, note);
- }
-
- public void setPlayerTime(long l, boolean bln)
- {
- base.setPlayerTime(l, bln);
- }
-
- public long getPlayerTime()
- {
- return base.getPlayerTime();
- }
-
- public long getPlayerTimeOffset()
- {
- return base.getPlayerTimeOffset();
- }
-
- public boolean isPlayerTimeRelative()
- {
- return base.isPlayerTimeRelative();
- }
-
- public void resetPlayerTime()
- {
- base.resetPlayerTime();
- }
-
- public boolean isPermissionSet(String string)
- {
- return base.isPermissionSet(string);
- }
-
- public boolean isPermissionSet(Permission prmsn)
- {
- return base.isPermissionSet(prmsn);
- }
-
- public boolean hasPermission(String string)
- {
- return base.hasPermission(string);
- }
-
- public boolean hasPermission(Permission prmsn)
- {
- return base.hasPermission(prmsn);
- }
-
- public PermissionAttachment addAttachment(Plugin plugin, String string, boolean bln)
- {
- return base.addAttachment(plugin, string, bln);
- }
-
- public PermissionAttachment addAttachment(Plugin plugin)
- {
- return base.addAttachment(plugin);
- }
-
- public PermissionAttachment addAttachment(Plugin plugin, String string, boolean bln, int i)
- {
- return base.addAttachment(plugin, string, bln, i);
- }
-
- public PermissionAttachment addAttachment(Plugin plugin, int i)
- {
- return base.addAttachment(plugin, i);
- }
-
- public void removeAttachment(PermissionAttachment pa)
- {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- public void recalculatePermissions()
- {
- base.recalculatePermissions();
- }
-
- public Set<PermissionAttachmentInfo> getEffectivePermissions()
- {
- return base.getEffectivePermissions();
- }
-
- public void setOp(boolean bln)
- {
- base.setOp(bln);
- }
-
- @Override
- public void sendMap(MapView mv)
- {
- base.sendMap(mv);
- }
-
- @Override
- public boolean isBanned()
- {
- return base.isBanned();
- }
-
- @Override
- public void setBanned(boolean bln)
- {
- base.setBanned(bln);
- }
-
- @Override
- public boolean isWhitelisted()
- {
- return base.isWhitelisted();
- }
-
- @Override
- public void setWhitelisted(boolean bln)
- {
- base.setWhitelisted(bln);
- }
-
- @Override
- public GameMode getGameMode()
- {
- return base.getGameMode();
- }
-
- @Override
- public void setGameMode(GameMode gm)
- {
- base.setGameMode(gm);
- }
-
- @Override
- public int getExperience()
- {
- return base.getExperience();
- }
-
- @Override
- public void setExperience(int i)
- {
- base.setExperience(i);
- }
-
- @Override
- public int getLevel()
- {
- return base.getLevel();
- }
-
- @Override
- public void setLevel(int i)
- {
- base.setLevel(i);
- }
-
- @Override
- public int getTotalExperience()
- {
- return base.getTotalExperience();
- }
-
- @Override
- public void setTotalExperience(int i)
- {
- base.setTotalExperience(i);
- }
-
- @Override
- public float getExhaustion()
- {
- return base.getExhaustion();
- }
-
- @Override
- public void setExhaustion(float f)
- {
- base.setExhaustion(f);
- }
-
- @Override
- public float getSaturation()
- {
- return base.getSaturation();
- }
-
- @Override
- public void setSaturation(float f)
- {
- base.setSaturation(f);
- }
-
- @Override
- public int getFoodLevel()
- {
- return base.getFoodLevel();
- }
-
- @Override
- public void setFoodLevel(int i)
- {
- base.setFoodLevel(i);
- }
-
- @Override
- public Location getBedSpawnLocation()
- {
- return base.getBedSpawnLocation();
- }
-
- @Override
- public boolean isSprinting()
- {
- return base.isSprinting();
- }
-
- @Override
- public void setSprinting(boolean bln)
- {
- base.setSprinting(bln);
- }
-
- @Override
- public void setPlayerListName(String name)
- {
- base.setPlayerListName(name);
- }
-
- @Override
- public String getPlayerListName()
- {
- return base.getPlayerListName();
- }
-
- @Override
- public int getTicksLived()
- {
- return base.getTicksLived();
- }
-
- @Override
- public void setTicksLived(int i)
- {
- base.setTicksLived(i);
- }
-
- @Override
- public Map<String, Object> serialize()
- {
- return base.serialize();
- }
-
-}
diff --git a/Essentials/src/com/earth2me/essentials/Settings.java b/Essentials/src/com/earth2me/essentials/Settings.java
index 9d11d675b..e59c3c2b2 100644
--- a/Essentials/src/com/earth2me/essentials/Settings.java
+++ b/Essentials/src/com/earth2me/essentials/Settings.java
@@ -46,7 +46,7 @@ public class Settings implements ISettings
@Override
public int getHomeLimit(final User user)
{
- final List<String> homeList = getMultipleHomes();
+ final List<String> homeList = getMultipleHomes();
if (homeList == null)
{
//TODO: Replace this code to remove backwards compat, after settings are automatically updated
@@ -56,8 +56,7 @@ public class Settings implements ISettings
int limit = getHomeLimit("default");
for (String set : homeList)
{
- logger.log(Level.INFO, "Found home set: " + set);
- if (user.hasPermission("essentials.sethome.multiple." + set) && limit < getHomeLimit(set))
+ if (user.isAuthorized("essentials.sethome.multiple." + set) && (limit < getHomeLimit(set)))
{
limit = getHomeLimit(set);
}
@@ -67,7 +66,7 @@ public class Settings implements ISettings
@Override
public int getHomeLimit(final String set)
- {
+ {
return config.getInt("sethome-multiple." + set, config.getInt("sethome-multiple.default", 3));
}
diff --git a/Essentials/src/com/earth2me/essentials/User.java b/Essentials/src/com/earth2me/essentials/User.java
index 3e335a1df..bcc871eab 100644
--- a/Essentials/src/com/earth2me/essentials/User.java
+++ b/Essentials/src/com/earth2me/essentials/User.java
@@ -282,16 +282,23 @@ public class User extends UserData implements Comparable<User>, IReplyTo, IUser
return nickname.toString();
}
-
+
public void setDisplayNick(String name)
{
setDisplayName(name);
//TODO: Maybe we need to limit nick length, or try use a string trim.
- if (name.length() <= 16) {
+ if (name.length() <= 16)
+ {
setPlayerListName(name);
}
}
+ @Override
+ public String getDisplayName()
+ {
+ return super.getDisplayName() == null ? super.getName() : super.getDisplayName();
+ }
+
public Teleport getTeleport()
{
return teleport;
@@ -355,7 +362,8 @@ public class User extends UserData implements Comparable<User>, IReplyTo, IUser
public void setAfk(final boolean set)
{
this.setSleepingIgnored(this.isAuthorized("essentials.sleepingignored") ? true : set);
- if (set && !isAfk()) {
+ if (set && !isAfk())
+ {
afkPosition = getLocation();
}
super.setAfk(set);
@@ -461,7 +469,8 @@ public class User extends UserData implements Comparable<User>, IReplyTo, IUser
if (!isAfk() && autoafk > 0 && lastActivity + autoafk * 1000 < System.currentTimeMillis() && isAuthorized("essentials.afk"))
{
setAfk(true);
- if (!isHidden()) {
+ if (!isHidden())
+ {
ess.broadcastMessage(this, Util.format("userIsAway", getDisplayName()));
}
}
@@ -475,7 +484,8 @@ public class User extends UserData implements Comparable<User>, IReplyTo, IUser
@Override
public boolean toggleGodModeEnabled()
{
- if (!isGodModeEnabled()) {
+ if (!isGodModeEnabled())
+ {
setFoodLevel(20);
}
return super.toggleGodModeEnabled();
diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandban.java b/Essentials/src/com/earth2me/essentials/commands/Commandban.java
index d6387c074..a4a5e2839 100644
--- a/Essentials/src/com/earth2me/essentials/commands/Commandban.java
+++ b/Essentials/src/com/earth2me/essentials/commands/Commandban.java
@@ -46,7 +46,7 @@ public class Commandban extends EssentialsCommand
if (args.length > 1)
{
banReason = getFinalArg(args, 1);
- player.setBanReason(commandLabel);
+ player.setBanReason(banReason);
}
else
{
diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandcompass.java b/Essentials/src/com/earth2me/essentials/commands/Commandcompass.java
index 8d582a296..eae10f0a5 100644
--- a/Essentials/src/com/earth2me/essentials/commands/Commandcompass.java
+++ b/Essentials/src/com/earth2me/essentials/commands/Commandcompass.java
@@ -15,17 +15,44 @@ public class Commandcompass extends EssentialsCommand
@Override
public void run(Server server, User user, String commandLabel, String[] args) throws Exception
{
- int r = (int)user.getLocation().getYaw();
+ int r = (int)(user.getLocation().getYaw() + 180 + 360) % 360;
String dir;
- if (r < 23) dir = "N";
- else if (r < 68) dir = "NE";
- else if (r < 113) dir = "E";
- else if (r < 158) dir = "SE";
- else if (r < 203) dir = "S";
- else if (r < 248) dir = "SW";
- else if (r < 293) dir = "W";
- else if (r < 338) dir = "NW";
- else dir = "N";
+ if (r < 23)
+ {
+ dir = "N";
+ }
+ else if (r < 68)
+ {
+ dir = "NE";
+ }
+ else if (r < 113)
+ {
+ dir = "E";
+ }
+ else if (r < 158)
+ {
+ dir = "SE";
+ }
+ else if (r < 203)
+ {
+ dir = "S";
+ }
+ else if (r < 248)
+ {
+ dir = "SW";
+ }
+ else if (r < 293)
+ {
+ dir = "W";
+ }
+ else if (r < 338)
+ {
+ dir = "NW";
+ }
+ else
+ {
+ dir = "N";
+ }
user.sendMessage(Util.format("compassBearing", dir, r));
}
}
diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandgetpos.java b/Essentials/src/com/earth2me/essentials/commands/Commandgetpos.java
index 12eeb5182..6f1fd7d6c 100644
--- a/Essentials/src/com/earth2me/essentials/commands/Commandgetpos.java
+++ b/Essentials/src/com/earth2me/essentials/commands/Commandgetpos.java
@@ -16,10 +16,10 @@ public class Commandgetpos extends EssentialsCommand
public void run(Server server, User user, String commandLabel, String[] args) throws Exception
{
Location coords = user.getLocation();
- user.sendMessage("§7X: " + coords.getBlockX() + " (-North <-> +South)");
+ user.sendMessage("§7X: " + coords.getBlockX() + " (+East <-> -West)");
user.sendMessage("§7Y: " + coords.getBlockY() + " (+Up <-> -Down)");
- user.sendMessage("§7Z: " + coords.getBlockZ() + " (+East <-> -West)");
- user.sendMessage("§7Yaw: " + coords.getYaw() + " (Rotation)");
+ user.sendMessage("§7Z: " + coords.getBlockZ() + " (+South <-> -North)");
+ user.sendMessage("§7Yaw: " + (coords.getYaw() + 180 + 360) % 360 + " (Rotation)");
user.sendMessage("§7Pitch: " + coords.getPitch() + " (Head angle)");
}
}
diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandmail.java b/Essentials/src/com/earth2me/essentials/commands/Commandmail.java
index acffd57a1..ddc26aadc 100644
--- a/Essentials/src/com/earth2me/essentials/commands/Commandmail.java
+++ b/Essentials/src/com/earth2me/essentials/commands/Commandmail.java
@@ -5,6 +5,7 @@ import org.bukkit.Server;
import com.earth2me.essentials.User;
import com.earth2me.essentials.Util;
import org.bukkit.ChatColor;
+import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -69,4 +70,59 @@ public class Commandmail extends EssentialsCommand
}
throw new NotEnoughArgumentsException();
}
+
+ @Override
+ protected void run(Server server, CommandSender sender, String commandLabel, String[] args) throws Exception
+ {
+ if (args.length >= 1 && "read".equalsIgnoreCase(args[0]))
+ {
+ throw new Exception(Util.format("onlyPlayers", commandLabel + " read"));
+ }
+ else if (args.length >= 1 && "clear".equalsIgnoreCase(args[0]))
+ {
+ throw new Exception(Util.format("onlyPlayers", commandLabel + " clear"));
+ }
+ else if (args.length >= 3 && "send".equalsIgnoreCase(args[0]))
+ {
+ Player player = server.getPlayer(args[1]);
+ User u;
+ if (player != null)
+ {
+ u = ess.getUser(player);
+ }
+ else
+ {
+ u = ess.getOfflineUser(args[1]);
+ }
+ if (u == null)
+ {
+ throw new Exception(Util.format("playerNeverOnServer", args[1]));
+ }
+ u.addMail("Server: " + getFinalArg(args, 2));
+ sender.sendMessage(Util.i18n("mailSent"));
+ return;
+ }
+ else if (args.length >= 2)
+ {
+ //allow sending from console without "send" argument, since it's the only thing the console can do
+ Player player = server.getPlayer(args[0]);
+ User u;
+ if (player != null)
+ {
+ u = ess.getUser(player);
+ }
+ else
+ {
+ u = ess.getOfflineUser(args[0]);
+ }
+ if (u == null)
+ {
+ throw new Exception(Util.format("playerNeverOnServer", args[0]));
+ }
+ u.addMail("Server: " + getFinalArg(args, 1));
+ sender.sendMessage(Util.i18n("mailSent"));
+ return;
+ }
+ throw new NotEnoughArgumentsException();
+ }
}
diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandrealname.java b/Essentials/src/com/earth2me/essentials/commands/Commandrealname.java
index 5e12c535d..5ed4ef9e6 100644
--- a/Essentials/src/com/earth2me/essentials/commands/Commandrealname.java
+++ b/Essentials/src/com/earth2me/essentials/commands/Commandrealname.java
@@ -1,6 +1,7 @@
package com.earth2me.essentials.commands;
import org.bukkit.Server;
+import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.earth2me.essentials.User;
import com.earth2me.essentials.Util;
@@ -15,7 +16,7 @@ public class Commandrealname extends EssentialsCommand
}
@Override
- public void run(Server server, User user, String commandLabel, String[] args) throws Exception
+ protected void run(Server server, CommandSender sender, String commandLabel, String[] args) throws Exception
{
if (args.length < 1)
{
@@ -36,7 +37,7 @@ public class Commandrealname extends EssentialsCommand
{
continue;
}
- user.sendMessage(u.getDisplayName() + " " + Util.i18n("is") + " " + u.getName());
+ sender.sendMessage(u.getDisplayName() + " " + Util.i18n("is") + " " + u.getName());
}
}
}
diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandweather.java b/Essentials/src/com/earth2me/essentials/commands/Commandweather.java
index 45c62d787..9603e9f72 100644
--- a/Essentials/src/com/earth2me/essentials/commands/Commandweather.java
+++ b/Essentials/src/com/earth2me/essentials/commands/Commandweather.java
@@ -4,6 +4,7 @@ import com.earth2me.essentials.User;
import com.earth2me.essentials.Util;
import org.bukkit.Server;
import org.bukkit.World;
+import org.bukkit.command.CommandSender;
public class Commandweather extends EssentialsCommand
@@ -29,16 +30,50 @@ public class Commandweather extends EssentialsCommand
world.setStorm(isStorm ? true : false);
world.setWeatherDuration(Integer.parseInt(args[1]) * 20);
user.sendMessage(isStorm
- ? Util.format("weatherStormFor", args[1])
- : Util.format("weatherSunFor", args[1]));
+ ? Util.format("weatherStormFor", world.getName(), args[1])
+ : Util.format("weatherSunFor", world.getName(), args[1]));
return;
}
else
{
world.setStorm(isStorm ? true : false);
user.sendMessage(isStorm
- ? Util.i18n("weatherStorm")
- : Util.i18n("weatherSun"));
+ ? Util.format("weatherStorm", world.getName())
+ : Util.format("weatherSun", world.getName()));
+ return;
+ }
+ }
+
+ @Override
+ protected void run(Server server, CommandSender sender, String commandLabel, String[] args) throws Exception
+ {
+ if (args.length < 2) //running from console means inserting a world arg before other args
+ {
+ throw new Exception("When running from console, usage is: /" + commandLabel + " <world> <storm/sun> [duration]");
+ }
+
+ boolean isStorm = args[1].equalsIgnoreCase("storm");
+ World world = server.getWorld(args[0]);
+ if (world == null)
+ {
+ throw new Exception("World named " + args[0] + " not found!");
+ }
+ if (args.length > 2)
+ {
+
+ world.setStorm(isStorm ? true : false);
+ world.setWeatherDuration(Integer.parseInt(args[2]) * 20);
+ sender.sendMessage(isStorm
+ ? Util.format("weatherStormFor", world.getName(), args[2])
+ : Util.format("weatherSunFor", world.getName(), args[2]));
+ return;
+ }
+ else
+ {
+ world.setStorm(isStorm ? true : false);
+ sender.sendMessage(isStorm
+ ? Util.format("weatherStorm", world.getName())
+ : Util.format("weatherSun", world.getName()));
return;
}
}
diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandworth.java b/Essentials/src/com/earth2me/essentials/commands/Commandworth.java
index b59070320..2a7e107bc 100644
--- a/Essentials/src/com/earth2me/essentials/commands/Commandworth.java
+++ b/Essentials/src/com/earth2me/essentials/commands/Commandworth.java
@@ -3,6 +3,8 @@ package com.earth2me.essentials.commands;
import org.bukkit.Server;
import com.earth2me.essentials.User;
import com.earth2me.essentials.Util;
+
+import org.bukkit.command.CommandSender;
import org.bukkit.inventory.ItemStack;
@@ -33,7 +35,7 @@ public class Commandworth extends EssentialsCommand
}
catch (NumberFormatException ex)
{
- amount = 64;
+ amount = is.getType().getMaxStackSize();
}
is.setAmount(amount);
@@ -56,4 +58,49 @@ public class Commandworth extends EssentialsCommand
amount,
Util.formatCurrency(worth, ess)));
}
+
+ @Override
+ protected void run(Server server, CommandSender sender, String commandLabel, String[] args) throws Exception
+ {
+ if (args.length < 1)
+ {
+ throw new NotEnoughArgumentsException();
+ }
+
+ ItemStack is = ess.getItemDb().get(args[0]);
+ int amount = is.getAmount();
+
+ try
+ {
+ if (args.length > 1)
+ {
+ amount = Integer.parseInt(args[1]);
+ }
+ }
+ catch (NumberFormatException ex)
+ {
+ amount = is.getType().getMaxStackSize();
+ }
+
+ is.setAmount(amount);
+ double worth = ess.getWorth().getPrice(is);
+ if (Double.isNaN(worth))
+ {
+ throw new Exception(Util.i18n("itemCannotBeSold"));
+ }
+
+ sender.sendMessage(is.getDurability() != 0
+ ? Util.format("worthMeta",
+ is.getType().toString().toLowerCase().replace("_", ""),
+ is.getDurability(),
+ Util.formatCurrency(worth * amount, ess),
+ amount,
+ Util.formatCurrency(worth, ess))
+ : Util.format("worth",
+ is.getType().toString().toLowerCase().replace("_", ""),
+ Util.formatCurrency(worth * amount, ess),
+ amount,
+ Util.formatCurrency(worth, ess)));
+
+ }
}
diff --git a/Essentials/src/com/earth2me/essentials/settings/Backup.java b/Essentials/src/com/earth2me/essentials/settings/Backup.java
new file mode 100644
index 000000000..1b59260db
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/Backup.java
@@ -0,0 +1,17 @@
+package com.earth2me.essentials.settings;
+
+import com.earth2me.essentials.storage.Comment;
+import com.earth2me.essentials.storage.StorageObject;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class Backup extends StorageObject
+{
+ @Comment("Interval in minutes")
+ private long interval = 60;
+ @Comment("Add a command that backups your data, e.g. 'rdiff-backup World1 backups/World1'")
+ private String command;
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/Chat.java b/Essentials/src/com/earth2me/essentials/settings/Chat.java
new file mode 100644
index 000000000..7c02c0e88
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/Chat.java
@@ -0,0 +1,32 @@
+package com.earth2me.essentials.settings;
+
+import com.earth2me.essentials.storage.Comment;
+import com.earth2me.essentials.storage.StorageObject;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class Chat extends StorageObject
+{
+ @Comment("The character(s) to prefix all nicknames, so that you know they are not true usernames.")
+ private String nicknamePrefix = "~";
+
+ @Comment("Disable this if you have any other plugin, that modifies the displayname of a user.")
+ private boolean changeDisplayname = true;
+
+ private String displaynameFormat = "{PREFIX}{NICKNAMEPREFIX}{NAME}{SUFFIX}";
+
+ @Comment({
+ "If EssentialsChat is installed, this will define how far a player's voice travels, in blocks. Set to 0 to make all chat global.",
+ "Note that users with the \"essentials.chat.spy\" permission will hear everything, regardless of this setting.",
+ "Users with essentials.chat.shout can override this by prefixing text with an exclamation mark (!)",
+ "Or with essentials.chat.question can override this by prefixing text with a question mark (?)",
+ "You can add command costs for shout/question by adding chat-shout and chat-question to the command costs section."
+ })
+ private int localRadius = 0;
+
+ @Comment("Set the default chat format here, it will be overwritten by group specific chat formats.")
+ private String defaultFormat = "&7[{GROUP}]&f {DISPLAYNAME}&7:&f {MESSAGE}";
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/Commands.java b/Essentials/src/com/earth2me/essentials/settings/Commands.java
new file mode 100644
index 000000000..771cef12b
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/Commands.java
@@ -0,0 +1,47 @@
+package com.earth2me.essentials.settings;
+
+import com.earth2me.essentials.settings.commands.Afk;
+import com.earth2me.essentials.settings.commands.God;
+import com.earth2me.essentials.settings.commands.Help;
+import com.earth2me.essentials.settings.commands.Home;
+import com.earth2me.essentials.settings.commands.Kit;
+import com.earth2me.essentials.settings.commands.Lightning;
+import com.earth2me.essentials.settings.commands.Spawnmob;
+import com.earth2me.essentials.storage.Comment;
+import com.earth2me.essentials.storage.ListType;
+import com.earth2me.essentials.storage.StorageObject;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class Commands extends StorageObject
+{
+ private Afk afk = new Afk();
+ private God god = new God();
+ private Help help = new Help();
+ private Home home = new Home();
+ private Kit kit = new Kit();
+ private Lightning lightning = new Lightning();
+ private Spawnmob spawnmob = new Spawnmob();
+ @ListType
+ @Comment(
+ {
+ "When a command conflicts with another plugin, by default, Essentials will try to force the OTHER plugin to take",
+ "priority. If a command is in this list, Essentials will try to give ITSELF priority. This does not always work:",
+ "usually whichever plugin was updated most recently wins out. However, the full name of the command will always work.",
+ "For example, if WorldGuard and Essentials are both enabled, and WorldGuard takes control over /god, /essentials:god",
+ "will still map to Essentials, whereas it might normally get forced upon WorldGuard. Commands prefixed with an \"e\",",
+ "such as /egod, will always grant Essentials priority.",
+ "We should try to take priority over /god. If this doesn't work, use /essentials:god or /egod.",
+ "If god is set using WorldGuard, use /ungod to remove then use whichever you see fit."
+ })
+ private List<String> overwritten = new ArrayList<String>();
+
+ @ListType
+ @Comment("Disabled commands will be completelly unavailable on the server.")
+ private List<String> disabled = new ArrayList<String>();
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/Economy.java b/Essentials/src/com/earth2me/essentials/settings/Economy.java
new file mode 100644
index 000000000..b18f05b96
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/Economy.java
@@ -0,0 +1,42 @@
+package com.earth2me.essentials.settings;
+
+import com.earth2me.essentials.storage.Comment;
+import com.earth2me.essentials.storage.MapType;
+import com.earth2me.essentials.storage.StorageObject;
+import java.util.HashMap;
+import java.util.Map;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class Economy extends StorageObject
+{
+ @Comment("Defines the balance with which new players begin. Defaults to 0.")
+ private double startingBalance = 0.0;
+ @MapType(Double.class)
+ @Comment("Defines the cost to use the given commands PER USE")
+ private Map<String, Double> commandCosts = new HashMap<String, Double>();
+ @Comment("Set this to a currency symbol you want to use.")
+ private String currencySymbol = "$";
+
+ public String getCurrencySymbol()
+ {
+ return currencySymbol == null || currencySymbol.isEmpty() ? "$" : currencySymbol.substring(0, 1);
+ }
+ private final transient static double MAXMONEY = 10000000000000.0;
+ @Comment(
+ {
+ "Set the maximum amount of money a player can have",
+ "The amount is always limited to 10 trillions because of the limitations of a java double"
+ })
+ private double maxMoney = MAXMONEY;
+
+ public double getMaxMoney()
+ {
+ return Math.abs(maxMoney) > MAXMONEY ? MAXMONEY : Math.abs(maxMoney);
+ }
+ @Comment("Enable this to log all interactions with trade/buy/sell signs and sell command")
+ private boolean logEnabled = false;
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/General.java b/Essentials/src/com/earth2me/essentials/settings/General.java
new file mode 100644
index 000000000..77143eb1a
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/General.java
@@ -0,0 +1,33 @@
+package com.earth2me.essentials.settings;
+
+import com.earth2me.essentials.storage.Comment;
+import com.earth2me.essentials.storage.StorageObject;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class General extends StorageObject
+{
+ @Comment("Backup runs a command while saving is disabled")
+ private Backup backup = new Backup();
+ @Comment("You can disable the death messages of minecraft.")
+ private boolean deathMessages = true;
+ @Comment("Turn this on, if you want to see more error messages, if something goes wrong.")
+ private boolean debug = false;
+ @Comment(
+ {
+ "Set the locale here, if you want to change the language of Essentials.",
+ "If this is not set, Essentials will use the language of your computer.",
+ "Available locales: da, de, en, fr, nl"
+ })
+ private String locale;
+ @Comment(
+ {
+ "Should we announce to the server when someone logs in for the first time?",
+ "If so, use this format, replacing {DISPLAYNAME} with the player name.",
+ "If not, set to ''"
+ })
+ private String newPlayerAnnouncement = "&dWelcome {DISPLAYNAME} to the server!";
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/GroupOptions.java b/Essentials/src/com/earth2me/essentials/settings/GroupOptions.java
new file mode 100644
index 000000000..1e0137302
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/GroupOptions.java
@@ -0,0 +1,27 @@
+package com.earth2me.essentials.settings;
+
+import com.earth2me.essentials.storage.Comment;
+import com.earth2me.essentials.storage.StorageObject;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class GroupOptions extends StorageObject
+{
+ @Comment("Message format of chat messages")
+ private String messageFormat;
+ @Comment("Prefix for name")
+ private String prefix;
+ @Comment("Suffix for name")
+ private String suffix;
+ @Comment("Amount of homes a player can have")
+ private Integer homes;
+ @Comment("Cooldown between teleports")
+ private Integer teleportCooldown;
+ @Comment("Delay before teleport")
+ private Integer teleportDelay;
+ @Comment("Cooldown between heals")
+ private Integer healCooldown;
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/Groups.java b/Essentials/src/com/earth2me/essentials/settings/Groups.java
new file mode 100644
index 000000000..06565d376
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/Groups.java
@@ -0,0 +1,27 @@
+package com.earth2me.essentials.settings;
+
+import com.earth2me.essentials.storage.Comment;
+import com.earth2me.essentials.storage.MapType;
+import com.earth2me.essentials.storage.StorageObject;
+import java.util.LinkedHashMap;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class Groups extends StorageObject
+{
+ public Groups() {
+ GroupOptions defaultOptions = new GroupOptions();
+ groups.put("default", defaultOptions);
+ }
+ @Comment(
+ {
+ "The order of the groups matters, the groups are checked from top to bottom.",
+ "All group names have to be lower case.",
+ "The groups can be connected to users using the permission essentials.groups.groupname"
+ })
+ @MapType(GroupOptions.class)
+ private LinkedHashMap<String, GroupOptions> groups = new LinkedHashMap<String, GroupOptions>();
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/Location.java b/Essentials/src/com/earth2me/essentials/settings/Location.java
new file mode 100644
index 000000000..0535fdf52
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/Location.java
@@ -0,0 +1,28 @@
+package com.earth2me.essentials.settings;
+
+import com.earth2me.essentials.storage.StorageObject;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.bukkit.Server;
+
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class Location extends StorageObject
+{
+ private String worldName = "Test";
+ private double x;
+ private double y;
+ private double z;
+ private Float yaw;
+ private Float pitch;
+
+ public org.bukkit.Location getBukkit(Server server)
+ {
+ if (yaw == null || pitch == null)
+ {
+ return new org.bukkit.Location(server.getWorld(worldName), x, y, z);
+ }
+ return new org.bukkit.Location(server.getWorld(worldName), x, y, z, yaw, pitch);
+ }
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/Settings.java b/Essentials/src/com/earth2me/essentials/settings/Settings.java
new file mode 100644
index 000000000..890bfef4e
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/Settings.java
@@ -0,0 +1,58 @@
+package com.earth2me.essentials.settings;
+
+import com.earth2me.essentials.storage.Comment;
+import com.earth2me.essentials.storage.StorageObject;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class Settings extends StorageObject
+{
+ @Comment(
+ {
+ "##########################################################",
+ "+------------------------------------------------------+ #",
+ "| General Settings | #",
+ "+------------------------------------------------------+ #",
+ "##########################################################"
+ })
+ private General general = new General();
+ @Comment(
+ {
+ "##########################################################",
+ "+------------------------------------------------------+ #",
+ "| Chat Settings | #",
+ "+------------------------------------------------------+ #",
+ "##########################################################"
+ })
+ private Chat chat = new Chat();
+ @Comment(
+ {
+ "##########################################################",
+ "+------------------------------------------------------+ #",
+ "| Economy Settings | #",
+ "+------------------------------------------------------+ #",
+ "##########################################################"
+ })
+ private Economy economy = new Economy();
+ @Comment(
+ {
+ "##########################################################",
+ "+------------------------------------------------------+ #",
+ "| Commands Settings | #",
+ "+------------------------------------------------------+ #",
+ "##########################################################"
+ })
+ private Commands commands = new Commands();
+ @Comment(
+ {
+ "##########################################################",
+ "+------------------------------------------------------+ #",
+ "| Group Settings | #",
+ "+------------------------------------------------------+ #",
+ "##########################################################"
+ })
+ private Groups groups = new Groups();
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/commands/Afk.java b/Essentials/src/com/earth2me/essentials/settings/commands/Afk.java
new file mode 100644
index 000000000..20076c273
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/commands/Afk.java
@@ -0,0 +1,36 @@
+package com.earth2me.essentials.settings.commands;
+
+import com.earth2me.essentials.storage.Comment;
+import com.earth2me.essentials.storage.StorageObject;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class Afk extends StorageObject
+{
+ @Comment(
+ {
+ "After this timeout in seconds, the user will be set as afk.",
+ "Set to -1 for no timeout."
+ })
+ private int autoAFK = 300;
+ @Comment(
+ {
+ "Auto-AFK Kick",
+ "After this timeout in seconds, the user will be kicked from the server.",
+ "Set to -1 for no timeout."
+ })
+ private int autoAFKKick = -1;
+ @Comment(
+ {
+ "Set this to true, if you want to freeze the player, if he is afk.",
+ "Other players or monsters can't push him out of afk mode then.",
+ "This will also enable temporary god mode for the afk player.",
+ "The player has to use the command /afk to leave the afk mode.",
+ "You have to add a message to your welcome message or help page,",
+ "since the player will not get a message, if he tries to move."
+ })
+ private boolean freezeAFKPlayers = false;
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/commands/God.java b/Essentials/src/com/earth2me/essentials/settings/commands/God.java
new file mode 100644
index 000000000..7740eaab1
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/commands/God.java
@@ -0,0 +1,14 @@
+package com.earth2me.essentials.settings.commands;
+
+import com.earth2me.essentials.storage.Comment;
+import com.earth2me.essentials.storage.StorageObject;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper=false)
+public class God extends StorageObject
+{
+ @Comment("Turn off god mode when people exit")
+ private boolean removeOnDisconnect = false;
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/commands/Help.java b/Essentials/src/com/earth2me/essentials/settings/commands/Help.java
new file mode 100644
index 000000000..03a9d5958
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/commands/Help.java
@@ -0,0 +1,23 @@
+package com.earth2me.essentials.settings.commands;
+
+import com.earth2me.essentials.storage.Comment;
+import com.earth2me.essentials.storage.StorageObject;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class Help extends StorageObject
+{
+ @Comment("Show other plugins commands in help")
+ private boolean showNonEssCommandsInHelp = true;
+ @Comment(
+ {
+ "Hide plugins which don't give a permission in their plugin.yml for each command.",
+ "You can override a true value here for a single plugin by adding a permission to a user/group.",
+ "The individual permission is: essentials.help.<plugin>, anyone with essentials.* or '*' will see all help this setting reguardless.",
+ "You can use negative permissions to remove access to just a single plugins help if the following is enabled."
+ })
+ private boolean hidePermissionlessCommands = true;
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/commands/Home.java b/Essentials/src/com/earth2me/essentials/settings/commands/Home.java
new file mode 100644
index 000000000..6ec2f1339
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/commands/Home.java
@@ -0,0 +1,24 @@
+package com.earth2me.essentials.settings.commands;
+
+import com.earth2me.essentials.storage.Comment;
+import com.earth2me.essentials.storage.StorageObject;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class Home extends StorageObject
+{
+ @Comment("When players die, should they respawn at their homes, instead of the spawnpoint?")
+ private boolean respawnAtHome = false;
+ @Comment(
+ {
+ "When a player interacts with a bed, should their home be set to that location?",
+ "If you enable this and remove default player access to the /sethome command, ",
+ "you can make beds the only way for players to set their home location."
+ })
+ private boolean bedSetsHome = false;
+ @Comment("If no home is set, should the player be send to spawn, when /home is used.")
+ private boolean spawnIfNoHome = false;
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/commands/Kit.java b/Essentials/src/com/earth2me/essentials/settings/commands/Kit.java
new file mode 100644
index 000000000..59b0b9a82
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/commands/Kit.java
@@ -0,0 +1,28 @@
+package com.earth2me.essentials.settings.commands;
+
+import com.earth2me.essentials.storage.MapType;
+import com.earth2me.essentials.storage.StorageObject;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class Kit extends StorageObject
+{
+
+ public Kit()
+ {
+ final KitObject kit = new KitObject();
+ kit.setDelay(10.0);
+ kit.setItems(Arrays.asList("277 1,278 1,279 1".split(",")));
+ kits.put("tools", kit);
+ }
+
+
+ @MapType(KitObject.class)
+ private Map<String,KitObject> kits = new HashMap<String, KitObject>();
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/commands/KitObject.java b/Essentials/src/com/earth2me/essentials/settings/commands/KitObject.java
new file mode 100644
index 000000000..93f6c6ade
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/commands/KitObject.java
@@ -0,0 +1,18 @@
+package com.earth2me.essentials.settings.commands;
+
+import com.earth2me.essentials.storage.ListType;
+import com.earth2me.essentials.storage.StorageObject;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class KitObject extends StorageObject
+{
+ @ListType
+ private List<String> items = new ArrayList<String>();
+ private Double delay;
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/commands/Lightning.java b/Essentials/src/com/earth2me/essentials/settings/commands/Lightning.java
new file mode 100644
index 000000000..510857247
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/commands/Lightning.java
@@ -0,0 +1,14 @@
+package com.earth2me.essentials.settings.commands;
+
+import com.earth2me.essentials.storage.Comment;
+import com.earth2me.essentials.storage.StorageObject;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class Lightning extends StorageObject
+{
+ @Comment("Shall we notify users when using /lightning")
+ private boolean warnPlayer = true;
+}
diff --git a/Essentials/src/com/earth2me/essentials/settings/commands/Spawnmob.java b/Essentials/src/com/earth2me/essentials/settings/commands/Spawnmob.java
new file mode 100644
index 000000000..771da32d1
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/settings/commands/Spawnmob.java
@@ -0,0 +1,14 @@
+package com.earth2me.essentials.settings.commands;
+
+import com.earth2me.essentials.storage.Comment;
+import com.earth2me.essentials.storage.StorageObject;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class Spawnmob extends StorageObject
+{
+ @Comment("The maximum amount of monsters, a player can spawn with a call of /spawnmob.")
+ private int limit = 10;
+}
diff --git a/Essentials/src/com/earth2me/essentials/storage/Comment.java b/Essentials/src/com/earth2me/essentials/storage/Comment.java
new file mode 100644
index 000000000..b43cec980
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/storage/Comment.java
@@ -0,0 +1,16 @@
+package com.earth2me.essentials.storage;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+@Target(ElementType.FIELD)
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Comment
+{
+ String[] value() default "";
+} \ No newline at end of file
diff --git a/Essentials/src/com/earth2me/essentials/storage/ListType.java b/Essentials/src/com/earth2me/essentials/storage/ListType.java
new file mode 100644
index 000000000..9bf6e2e64
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/storage/ListType.java
@@ -0,0 +1,14 @@
+package com.earth2me.essentials.storage;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ListType
+{
+ Class value() default String.class;
+}
diff --git a/Essentials/src/com/earth2me/essentials/storage/MapType.java b/Essentials/src/com/earth2me/essentials/storage/MapType.java
new file mode 100644
index 000000000..dc5636315
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/storage/MapType.java
@@ -0,0 +1,14 @@
+package com.earth2me.essentials.storage;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface MapType
+{
+ Class value() default String.class;
+} \ No newline at end of file
diff --git a/Essentials/src/com/earth2me/essentials/storage/StorageObject.java b/Essentials/src/com/earth2me/essentials/storage/StorageObject.java
new file mode 100644
index 000000000..a35338516
--- /dev/null
+++ b/Essentials/src/com/earth2me/essentials/storage/StorageObject.java
@@ -0,0 +1,250 @@
+package com.earth2me.essentials.storage;
+
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.composer.Composer;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.introspector.PropertyUtils;
+
+
+public class StorageObject
+{
+ protected Class<? extends StorageObject> clazz;
+
+ protected StorageObject()
+ {
+ }
+ private static Map<Class, Constructor> constructors = new HashMap<Class, Constructor>();
+
+ public static <T extends StorageObject> T load(Class<? extends T> clazz, Reader reader)
+ {
+ Constructor constructor;
+ if (constructors.containsKey(clazz))
+ {
+ constructor = constructors.get(clazz);
+ }
+ else
+ {
+ constructor = prepareConstructor(clazz);
+ constructors.put(clazz, constructor);
+ }
+
+ final Yaml yaml = new Yaml(constructor);
+ T ret = (T)yaml.load(reader);
+ if (ret == null)
+ {
+ try
+ {
+ ret = (T)clazz.newInstance();
+ }
+ catch (InstantiationException ex)
+ {
+ Logger.getLogger(StorageObject.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ catch (IllegalAccessException ex)
+ {
+ Logger.getLogger(StorageObject.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ ret.clazz = clazz;
+ return ret;
+ }
+
+ private static Constructor prepareConstructor(final Class<?> clazz)
+ {
+ final Constructor constructor = new Constructor(clazz);
+ final Set<Class> classes = new HashSet<Class>();
+
+ prepareConstructor(constructor, classes, clazz);
+ return constructor;
+ }
+
+ private static void prepareConstructor(final Constructor constructor, final Set<Class> classes, final Class clazz)
+ {
+ classes.add(clazz);
+ final TypeDescription description = new TypeDescription(clazz);
+ for (Field field : clazz.getDeclaredFields())
+ {
+ final ListType listType = field.getAnnotation(ListType.class);
+ if (listType != null)
+ {
+ description.putListPropertyType(field.getName(), listType.value());
+ if (StorageObject.class.isAssignableFrom(listType.value())
+ && !classes.contains(listType.value()))
+ {
+ prepareConstructor(constructor, classes, listType.value());
+ }
+ }
+ final MapType mapType = field.getAnnotation(MapType.class);
+ if (mapType != null)
+ {
+ description.putMapPropertyType(field.getName(), String.class, mapType.value());
+ if (StorageObject.class.isAssignableFrom(mapType.value())
+ && !classes.contains(mapType.value()))
+ {
+ prepareConstructor(constructor, classes, mapType.value());
+ }
+ }
+ if (StorageObject.class.isAssignableFrom(field.getType())
+ && !classes.contains(field.getType()))
+ {
+ prepareConstructor(constructor, classes, field.getType());
+ }
+ }
+ constructor.addTypeDescription(description);
+ }
+ private transient Yaml yaml;
+
+ public void save(final PrintWriter writer)
+ {
+ final DumperOptions ops = new DumperOptions();
+ yaml = new Yaml(ops);
+ try
+ {
+ writeToFile(this, writer, 0, clazz);
+ }
+ catch (IllegalArgumentException ex)
+ {
+ Logger.getLogger(StorageObject.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ catch (IllegalAccessException ex)
+ {
+ Logger.getLogger(StorageObject.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+
+ private void writeToFile(final Object object, final PrintWriter writer, final int depth, final Class clazz) throws IllegalArgumentException, IllegalAccessException
+ {
+ for (Field field : clazz.getDeclaredFields())
+ {
+ final int modifier = field.getModifiers();
+ if (Modifier.isPrivate(modifier) && !Modifier.isTransient(modifier) && !Modifier.isStatic(modifier))
+ {
+ field.setAccessible(true);
+ final boolean commentPresent = field.isAnnotationPresent(Comment.class);
+ final String name = field.getName();
+ if (commentPresent)
+ {
+ final Comment comments = field.getAnnotation(Comment.class);
+ for (String comment : comments.value())
+ {
+ final String trimmed = comment.trim();
+ if (trimmed.isEmpty())
+ {
+ continue;
+ }
+ writeIndention(writer, depth);
+ writer.print("# ");
+ writer.print(trimmed);
+ writer.println();
+ }
+ }
+
+ final Object data = field.get(object);
+ if (data == null && !commentPresent)
+ {
+ continue;
+ }
+ writeIndention(writer, depth);
+ if (data == null && commentPresent)
+ {
+ writer.print('#');
+ }
+ writer.print(name);
+ writer.print(": ");
+ if (data == null && commentPresent)
+ {
+ writer.println();
+ writer.println();
+ continue;
+ }
+ if (data instanceof StorageObject)
+ {
+ writer.println();
+ writeToFile(data, writer, depth + 1, data.getClass());
+ }
+ else if (data instanceof Map)
+ {
+ writer.println();
+ for (Entry<String, Object> entry : ((Map<String, Object>)data).entrySet())
+ {
+ final Object value = entry.getValue();
+ if (value != null)
+ {
+ writeIndention(writer, depth + 1);
+ writer.print(entry.getKey());
+ writer.print(": ");
+ if (value instanceof StorageObject)
+ {
+ writer.println();
+ writeToFile(value, writer, depth + 2, value.getClass());
+ }
+ else if (value instanceof String || value instanceof Boolean || value instanceof Number)
+ {
+ yaml.dumpAll(Collections.singletonList(value).iterator(), writer);
+ writer.println();
+ }
+ else
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+ }
+ }
+ else if (data instanceof Collection)
+ {
+ writer.println();
+ for (Object entry : (Collection<Object>)data)
+ {
+ if (entry != null)
+ {
+ writeIndention(writer, depth + 1);
+ writer.print("- ");
+ if (entry instanceof String || entry instanceof Boolean || entry instanceof Number)
+ {
+ yaml.dumpAll(Collections.singletonList(entry).iterator(), writer);
+ }
+ else
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+ writer.println();
+ }
+ else if (data instanceof String || data instanceof Boolean || data instanceof Number)
+ {
+ yaml.dumpAll(Collections.singletonList(data).iterator(), writer);
+ writer.println();
+ }
+ else
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+ }
+
+ private void writeIndention(final PrintWriter writer, final int depth)
+ {
+ for (int i = 0; i < depth; i++)
+ {
+ writer.print(" ");
+ }
+ }
+}
diff --git a/Essentials/src/messages.properties b/Essentials/src/messages.properties
index c756d4384..0eb9f9d37 100644
--- a/Essentials/src/messages.properties
+++ b/Essentials/src/messages.properties
@@ -351,10 +351,10 @@ warpSet = \u00a77Warp {0} set.
warpUsePermission = \u00a7cYou do not have Permission to use that warp.
warpingTo = \u00a77Warping to {0}.
warpsCount = \u00a77There are {0} warps. Showing page {1} of {2}.
-weatherStorm = \u00a77You set the weather to storm in your world
-weatherStormFor = \u00a77You set the weather to storm in your world for {0} seconds
-weatherSun = \u00a77You set the weather to sun in your world
-weatherSunFor = \u00a77You set the weather to sun in your world for {0} seconds
+weatherStorm = \u00a77You set the weather to storm in {0}
+weatherStormFor = \u00a77You set the weather to storm in {0} for {1} seconds
+weatherSun = \u00a77You set the weather to sun in {0}
+weatherSunFor = \u00a77You set the weather to sun in {0} for {1} seconds
whoisGeoLocation = \u00a79 - Location: {0}
whoisHealth = \u00a79 - Health: {0}/20
whoisIPAddress = \u00a79 - IP Address: {0}
diff --git a/Essentials/src/messages_da.properties b/Essentials/src/messages_da.properties
index 1f21b1a50..7526e4858 100644
--- a/Essentials/src/messages_da.properties
+++ b/Essentials/src/messages_da.properties
@@ -350,10 +350,10 @@ warpSet = \u00a77Warp {0} sat.
warpUsePermission = \u00a7cDu har ikke tilladelse til at benytte den warp.
warpingTo = \u00a77Warper til {0}.
warpsCount = \u00a77There are {0} warps. Showing page {1} of {2}.
-weatherStorm = \u00a77Du har sat vejret til storm i din verden
-weatherStormFor = \u00a77Du har sat vejret til storm i din verden i {0} sekunder
-weatherSun = \u00a77Du har sat vejret til sol
-weatherSunFor = \u00a77Du har sat vejret til sol i din verden i {0} sekunder
+weatherStorm = \u00a77Du har sat vejret til storm i {0}
+weatherStormFor = \u00a77Du har sat vejret til storm i {0} i {1} sekunder
+weatherSun = \u00a77Du har sat vejret til sol i {0}
+weatherSunFor = \u00a77Du har sat vejret til sol i {0} i {1} sekunder
whoisGeoLocation = \u00a79 - Placering: {0}
whoisHealth = \u00a79 - Helbred: {0}/20
whoisIPAddress = \u00a79 - IP Addresse: {0}
diff --git a/Essentials/src/messages_de.properties b/Essentials/src/messages_de.properties
index 8ae568f21..5ccedd370 100644
--- a/Essentials/src/messages_de.properties
+++ b/Essentials/src/messages_de.properties
@@ -53,9 +53,9 @@ deleteHome = \u00a77Zuhause {0} wurde gel\u00f6scht.
deleteJail = \u00a77Gef\u00e4ngnis {0} wurde gel\u00f6scht.
deleteWarp = \u00a77Warp-Punkt {0} wurde gel\u00f6scht.
deniedAccessCommand = {0} hat keinen Zugriff auf diesen Befehl.
-dependancyDownloaded = [Essentials] Dependancy {0} downloaded successfully.
-dependancyException = [Essentials] An error occured when trying to download a dependacy
-dependancyNotFound = [Essentials] A required dependancy was not found, downloading now.
+dependancyDownloaded = [Essentials] Abh\u00e4ngigkeit {0} erfolgreich runtergeladen.
+dependancyException = [Essentials] W\u00e4hrend dem Download von einer Abh\u00e4ngigkeit ist ein Fehler aufgetreten.
+dependancyNotFound = [Essentials] Eine erforderliche Abh\u00e4ngigkeit wurde nicht gefunde, Download startet jetzt..
depth = \u00a77Du bist auf Meeresh\u00f6he.
depthAboveSea = \u00a77Du bist {0} Bl\u00f6cke \u00fcber Meeresh\u00f6he.
depthBelowSea = \u00a77Du bist {0} Bl\u00f6cke unter Meeresh\u00f6he.
@@ -99,7 +99,7 @@ helpPages = Seite \u00a7c{0}\u00a7f von \u00a7c{1}\u00a7f:
holeInFloor = Loch im Boden
homeSet = \u00a77Zuhause gesetzt.
homeSetToBed = \u00a77Dein Zuhause ist nun an diesem Bett.
-homes = Homes: {0}
+homes = Heime: {0}
hour = Stunde
hours = Stunden
ignorePlayer = Du ignorierst ab jetzt Spieler {0}.
@@ -132,11 +132,11 @@ itemSoldConsole = {0} verkauft {1} f\u00fcr \u00a77{2}\u00a77 ({3} Einheiten je
itemSpawn = \u00a77Gebe {0}x {1}
itemsCsvNotLoaded = Konnte items.csv nicht laden.
jailAlreadyIncarcerated = \u00a7cPerson is already in jail: {0}
-jailMessage = \u00a7cYou do the crime, you do the time.
+jailMessage = \u00a7cDu hast ein Verbrechen begangen, also hast du Zeit.
jailNotExist = Dieses Gef\u00e4ngnis existiert nicht.
-jailReleased = \u00a77Player \u00a7e{0}\u00a77 unjailed.
-jailReleasedPlayerNotify = \u00a77You have been released!
-jailSentenceExtended = Jail time extend to: {0)
+jailReleased = \u00a77Spieler \u00a7e{0}\u00a77 wurde freigelassen.
+jailReleasedPlayerNotify = \u00a77Du wurdest freigelassen!
+jailSentenceExtended = Gef\u00e4ngnisszeit erweitert auf: {0)
jailSet = \u00a77Gef\u00e4ngnis {0} wurde erstellt.
jumpError = Das w\u00fcrde deinen Computer \u00fcberlasten.
kickDefault = Vom Server geworfen
@@ -193,7 +193,7 @@ nickSet = \u00a77Dein Nickname ist nun \u00a7c{0}
noAccessCommand = \u00a7cDu hast keinen Zugriff auf diesen Befehl.
noAccessPermission = \u00a7cDu hast keine Rechte, den Block {0} zu \u00f6ffnen.
noDestroyPermission = \u00a7cDu hast keine Rechte, den Block {0} zu zerst\u00f6ren.
-noHelpFound = \u00a7cNo matching commands.
+noHelpFound = \u00a7cKeine \u00fcbereinstimmenden Kommandos.
noHomeSet = Du hast kein Zuhause gesetzt.
noHomeSetPlayer = Spieler hat kein Zuhause gesetzt.
noKitPermission = \u00a7cDu brauchst die Berechtigung \u00a7c{0}\u00a7c um diese Ausr\u00fcstung anzufordern.
@@ -204,7 +204,7 @@ noMotd = \u00a7cEs existiert keine Willkommensnachricht.
noNewMail = \u00a77Du hast keine Nachrichten.
noPendingRequest = Du hast keine Teleportierungsanfragen.
noPlacePermission = \u00a7cDu hast keine Rechte, einen Block in der N\u00e4he des Schildes zu platzieren.
-noPowerTools=You have no power tools assigned.
+noPowerTools = Du hast keine Powertools zugewiesen.
noRules = \u00a7cEs wurden keine Regeln definiert.
noWarpsDefined = Keine Warp-Punkte erstellt.
none = keine
@@ -225,7 +225,7 @@ pTimeOthersPermission = \u00a7cDu hast keine Berechtigung die Zeit von anderen S
pTimePlayers = Diese Spieler haben ihre eigene Zeit:
pTimeReset = Zeit wurde zur\u00fcgesetzt f\u00fcr: \u00a7e{0}
pTimeSet = Zeit wurde f\u00fcr \u00a7e{1}\u00a7f zu \u00a73{0}\u00a7f gesetzt.
-pTimeSetFixed = Player time is fixed to \u00a73{0}\u00a7f for: \u00a7e{1}
+pTimeSetFixed = Spielerzeit ist festgesetzt zu \u00a73{0}\u00a7f f\u00fcr: \u00a7e{1}
parseError = Fehler beim Parsen von {0} in Zeile {1}
pendingTeleportCancelled = \u00a7cLaufende Teleportierung abgebrochen.
permissionsError = Permissions/GroupManager fehlt; Chat-Prefixe/-Suffixe sind ausgeschaltet.
@@ -244,13 +244,14 @@ possibleWorlds = \u00a77M\u00f6gliche Welten sind nummeriet von 0 bis {0}.
powerToolAir = Befehl kann nicht mit Luft verbunden werden.
powerToolAlreadySet = Befehl \u00a7c{0}\u00a7f ist bereits zu {1} hinzugef\u00fcgt.
powerToolAttach = Befehl \u00a7c{0}\u00a7f erfolgreich zu {1} hinzugef\u00fcgt.
+powerToolClearAll= Alle Powertoolkommandos wurden entfernt.
powerToolList = {1} hat die folgenden Befehle: \u00a7c{0}\u00a7f.
powerToolListEmpty = {0} hat keinen Befehl.
powerToolNoSuchCommandAssigned = Befehl \u00a7c{0}\u00a7f wurde nicht zu {1} hinzugef\u00fcgt.
powerToolRemove = Befehl \u00a7c{0}\u00a7f erfolgreich von {1} entfernt.
powerToolRemoveAll = Alle Befehle von {0} entfernt.
-powerToolsEnabled=All of your power tools have been enabled.
-powerToolsDisabled=All of your power tools have been disabled.
+powerToolsEnabled = Alle deine Powertools wurden aktiviert.
+powerToolsDisabled = Alle deine Powertools wurden deaktiviert.
protectionOwner = \u00a76[EssentialsProtect] Besitzer dieses Blocks: {0}
questionFormat = \u00a77[Frage]\u00a7f {0}
reloadAllPlugins = \u00a77Alle plugins neu geladen.
@@ -275,7 +276,7 @@ shoutFormat = \u00a77[Schrei]\u00a7f {0}
signFormatFail = \u00a74[{0}]
signFormatSuccess = \u00a71[{0}]
signFormatTemplate = [{0}]
-signProtectInvalidLocation = \u00a74You are not allowed to create sign here.
+signProtectInvalidLocation = \u00a74Du bist nicht befugt ein Schild hierhin zu setzen.
similarWarpExist = Ein Warp-Punkt mit einem \u00e4hnlichen Namen existiert bereits.
slimeMalformedSize = Ung\u00fcltige Gr\u00f6sse.
soloMob = Das Monster m\u00f6chte allein sein.
@@ -327,7 +328,7 @@ unknownItemInList = Unbekannter Gegenstand {0} in Liste {1}.
unknownItemName = Unbekannter Gegenstand: {0}
unlimitedItemPermission = \u00a7cDu hast keine Rechte f\u00fcr {0}.
unlimitedItems = Unendliche Objekte:
-unmutedPlayer = Player {0} ist nicht mehr stumm.
+unmutedPlayer = Spieler {0} ist nicht mehr stumm.
upgradingFilesError = Fehler beim Aktualisieren der Dateien
userCreatedPortal = {0} benutzt ein Portal und hat ein Ausgangsportal erstellt.
userDoesNotExist = Spieler {0} existiert nicht.
@@ -349,10 +350,10 @@ warpSet = \u00a77Warp-Punkt {0} wurde erstellt.
warpUsePermission = \u00a7cDu hast keinen Zugriff f\u00fcr diesen Warp-Punkt.
warpingTo = \u00a77Teleportiere zu Warp-Punkt {0}.
warpsCount = \u00a77Es gibt {0} Warp-Punkte. Zeige Seite {1} von {2}.
-weatherStorm = \u00a77In deiner Welt st\u00fcrmt es nun.
-weatherStormFor = \u00a77In deiner Welt st\u00fcrmt es nun f\u00fcr {0} Sekunden.
-weatherSun = \u00a77In deiner Welt scheint nun die Sonne.
-weatherSunFor = \u00a77In deiner Welt scheint nun f\u00fcr {0} Sekunden die Sonne.
+weatherStorm = \u00a77In {0} st\u00fcrmt es nun.
+weatherStormFor = \u00a77In {0} st\u00fcrmt es nun f\u00fcr {1} Sekunden.
+weatherSun = \u00a77In {0} scheint nun die Sonne.
+weatherSunFor = \u00a77In {0} scheint nun f\u00fcr {1} Sekunden die Sonne.
whoisGeoLocation = \u00a79 - Herkunft: {0}
whoisHealth = \u00a79 - Gesundheit: {0}/20
whoisIPAddress = \u00a79 - IP-Adresse: {0}
@@ -368,6 +369,4 @@ year = Jahr
years = Jahre
youAreHealed = \u00a77Du wurdest geheilt.
youHaveNewMail = \u00a7cDu hast {0} Nachrichten!\u00a7f Schreibe \u00a77/mail read\u00a7f um deine Nachrichten anzuzeigen.
-powerToolClearAll= All powertool commands have been cleared.
-
diff --git a/Essentials/src/messages_en.properties b/Essentials/src/messages_en.properties
index ea73ccc56..39fe33d83 100644
--- a/Essentials/src/messages_en.properties
+++ b/Essentials/src/messages_en.properties
@@ -350,10 +350,10 @@ warpSet = \u00a77Warp {0} set.
warpUsePermission = \u00a7cYou do not have Permission to use that warp.
warpingTo = \u00a77Warping to {0}.
warpsCount = \u00a77There are {0} warps. Showing page {1} of {2}.
-weatherStorm = \u00a77You set the weather to storm in your world
-weatherStormFor = \u00a77You set the weather to storm in your world for {0} seconds
-weatherSun = \u00a77You set the weather to sun in your world
-weatherSunFor = \u00a77You set the weather to sun in your world for {0} seconds
+weatherStorm = \u00a77You set the weather to storm in {0}
+weatherStormFor = \u00a77You set the weather to storm in {0} for {1} seconds
+weatherSun = \u00a77You set the weather to sun in {0}
+weatherSunFor = \u00a77You set the weather to sun in {0} for {1} seconds
whoisGeoLocation = \u00a79 - Location: {0}
whoisHealth = \u00a79 - Health: {0}/20
whoisIPAddress = \u00a79 - IP Address: {0}
diff --git a/Essentials/src/messages_fr.properties b/Essentials/src/messages_fr.properties
index 987a41eb0..11fa2e6a4 100644
--- a/Essentials/src/messages_fr.properties
+++ b/Essentials/src/messages_fr.properties
@@ -350,10 +350,10 @@ warpSet = \u00a77Le warp {0} a \u00e9t\u00e9 cr\u00e9\u00e9.
warpUsePermission = \u00a7cVous n''avez pas la permission d''utiliser ce warp.
warpingTo = \u00a77T\u00e9l\u00e9portation au warp {0}.
warpsCount = \u00a77There are {0} warps. Showing page {1} of {2}.
-weatherStorm = \u00a77Vous avez d\u00e9fini l''orage dans votre monde
-weatherStormFor = \u00a77Vous avez d\u00e9fini l''orage dans votre monde pour {0} secondes.
-weatherSun = \u00a77Vous avez mis le beau temps dans votre monde
-weatherSunFor = \u00a77Vous avez mis le beau temps dans votre monde pour {0} secondes.
+weatherStorm = \u00a77Vous avez d\u00e9fini l''orage dans {0}
+weatherStormFor = \u00a77Vous avez d\u00e9fini l''orage dans {0} pour {1} secondes.
+weatherSun = \u00a77Vous avez mis le beau temps dans {0}
+weatherSunFor = \u00a77Vous avez mis le beau temps dans {0} pour {1} secondes.
whoisGeoLocation = \u00a79 - Emplacement: {0}
whoisHealth = \u00a79 - Vie: {0} / 20
whoisIPAddress = \u00a79 - Adresse IP: {0}
diff --git a/Essentials/src/messages_nl.properties b/Essentials/src/messages_nl.properties
index e11791444..c3ce54543 100644
--- a/Essentials/src/messages_nl.properties
+++ b/Essentials/src/messages_nl.properties
@@ -350,10 +350,10 @@ warpSet = \u00a77Warp {0} ingesteld.
warpUsePermission = \u00a7cOnbevoegd om die warp te gebruiken.
warpingTo = \u00a77Aan het warpen naar {0}.
warpsCount = \u00a77There are {0} warps. Showing page {1} of {2}.
-weatherStorm = \u00a77Je hebt het weer naar storm gezet in de wereld
-weatherStormFor = \u00a77Je hebt het weer in de wereld naar storm gezet voor {0} seconde
-weatherSun = \u00a77Je hebt het weer naar zon gezet in de wereld
-weatherSunFor = \u00a77Je hebt het weer in de wereld naar zon gezet voor {0} seconde
+weatherStorm = \u00a77Je hebt het weer naar storm gezet in de {0}
+weatherStormFor = \u00a77Je hebt het weer in de {0} naar storm gezet voor {1} seconde
+weatherSun = \u00a77Je hebt het weer naar zon gezet in de {0}
+weatherSunFor = \u00a77Je hebt het weer in de {0} naar zon gezet voor {1} seconde
whoisGeoLocation = \u00a79 - Locatie: {0}
whoisHealth = \u00a79 - Levens: {0}/20
whoisIPAddress = \u00a79 - IP Adres: {0}
diff --git a/Essentials/test/com/earth2me/essentials/FakeServer.java b/Essentials/test/com/earth2me/essentials/FakeServer.java
index 2344c4513..7f3fb9bf2 100644
--- a/Essentials/test/com/earth2me/essentials/FakeServer.java
+++ b/Essentials/test/com/earth2me/essentials/FakeServer.java
@@ -467,9 +467,70 @@ public class FakeServer implements Server
}
@Override
- public org.bukkit.OfflinePlayer getOfflinePlayer(String string)
+ public org.bukkit.OfflinePlayer getOfflinePlayer(final String string)
{
- return null;
+ return new org.bukkit.OfflinePlayer() {
+
+ @Override
+ public boolean isOnline()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public String getName()
+ {
+ return string;
+ }
+
+ @Override
+ public boolean isBanned()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void setBanned(boolean bln)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public boolean isWhitelisted()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void setWhitelisted(boolean bln)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public Player getPlayer()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public boolean isOp()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void setOp(boolean bln)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public Map<String, Object> serialize()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+ };
}
@Override
diff --git a/Essentials/test/com/earth2me/essentials/StorageTest.java b/Essentials/test/com/earth2me/essentials/StorageTest.java
new file mode 100644
index 000000000..9ee5883e9
--- /dev/null
+++ b/Essentials/test/com/earth2me/essentials/StorageTest.java
@@ -0,0 +1,37 @@
+package com.earth2me.essentials;
+
+import junit.framework.TestCase;
+import com.earth2me.essentials.settings.Settings;
+import com.earth2me.essentials.storage.StorageObject;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.Reader;
+import org.junit.Test;
+
+
+public class StorageTest extends TestCase
+{
+ @Test
+ public void testSettings()
+ {
+ assertTrue(StorageObject.class.isAssignableFrom(Settings.class));
+ final ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]);
+ final Reader reader = new InputStreamReader(bais);
+ final Settings settings = StorageObject.load(Settings.class, reader);
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final PrintWriter writer = new PrintWriter(baos);
+ settings.save(writer);
+ writer.close();
+ byte[] written = baos.toByteArray();
+ System.out.println(new String(written));
+ final ByteArrayInputStream bais2 = new ByteArrayInputStream(written);
+ final Reader reader2 = new InputStreamReader(bais2);
+ final Settings settings2 = StorageObject.load(Settings.class, reader2);
+ System.out.println(settings.toString());
+ System.out.println(settings2.toString());
+ //assertEquals("Default and rewritten config should be equal", settings, settings2);
+ //that assertion fails, because empty list and maps return as null
+ }
+}
diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt
index fe33a9f15..6729907b5 100644
--- a/EssentialsGroupManager/src/Changelog.txt
+++ b/EssentialsGroupManager/src/Changelog.txt
@@ -39,4 +39,13 @@ v 1.3:
- Fix for Bukkit passing a null To location on a player Portaling
- Fixed manudelsub not correctly selecting the group to remove.
- Added two new permission nodes - groupmanager.notify.self & groupmanager.notify.other
- These allow players/admins to be notified when players are moved between groups. \ No newline at end of file
+ These allow players/admins to be notified when players are moved between groups.
+v 1.4:
+ - Updated for Bukkits new YamlConfiguration.
+ - Cleared remaining Cast errors cause by object cloning.
+ - Removed extra notification messages for the player issuing the group move command.
+ - Added a config setting - bukkit_perms_override: false
+ Enable to allow default Bukkit based permissions to remain enabled, unless directly negated within GroupManager.
+ - Fixed reading world mirrors from the config.
+ - Simplified config.yml while retaining backwards compatibility.
+ - Added data.save.hours setting to config. This allow control over how long backups are retained. \ No newline at end of file
diff --git a/EssentialsGroupManager/src/config.yml b/EssentialsGroupManager/src/config.yml
index 1c5e3e32d..25fb5dcd0 100644
--- a/EssentialsGroupManager/src/config.yml
+++ b/EssentialsGroupManager/src/config.yml
@@ -2,17 +2,24 @@ settings:
config:
# With this enabled anyone set as op has full permissions when managing GroupManager
opOverrides: true
+
+ # If enabled any bukkit permissiosn which default to true will be left enabled.
+ # If the player is op any permissions set to Op will follow suit.
+ bukkit_perms_override: false
+
data:
save:
# How often GroupManager will save it's data back to groups and users.yml
minutes: 10
+ # Number of hours to retain backups (plugins/GroupManager/backup)
+ hours: 24
+
logging:
# level of detail GroupManager will use when logging.
# Acceptable entries are - ALL,CONFIG,FINE,FINER,FINEST,INFO,OFF,SEVERE,WARNING
level: INFO
- permission:
- world:
- mirror:
+
+ mirrors:
# Worlds listed here have their permissions mirrored in their children.
world:
- world_nether
diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GMConfiguration.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GMConfiguration.java
index aa838d9e8..ad2945076 100644
--- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GMConfiguration.java
+++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GMConfiguration.java
@@ -10,7 +10,7 @@ import java.util.Map;
import java.util.logging.Level;
import org.anjocaido.groupmanager.utils.Tasks;
-import org.bukkit.util.config.Configuration;
+import org.bukkit.configuration.file.YamlConfiguration;
/**
*
@@ -20,7 +20,7 @@ public class GMConfiguration {
private GroupManager plugin;
private File configFile;
- private Configuration GMconfig;
+ private YamlConfiguration GMconfig;
public GMConfiguration(GroupManager plugin) {
this.plugin = plugin;
@@ -41,10 +41,10 @@ public class GMConfiguration {
}
}
- GMconfig = new Configuration(configFile);
+ GMconfig = new YamlConfiguration();
try {
- GMconfig.load();
+ GMconfig.load(configFile);
} catch (Exception ex) {
throw new IllegalArgumentException("The following file couldn't pass on Parser.\n" + configFile.getPath(), ex);
}
@@ -52,17 +52,28 @@ public class GMConfiguration {
}
public boolean isOpOverride() {
+ return GMconfig.getBoolean("settings.config.bukkit_perms_override", true);
+ }
+ public boolean isBukkitPermsOverride() {
return GMconfig.getBoolean("settings.config.opOverrides", true);
}
- @SuppressWarnings("unchecked")
- public Map<String, Object> getMirrorsMap() {
- return (Map<String, Object>) GMconfig.getProperty("settings.permission.world.mirror");
+ public Map<String, Object> getMirrorsMap() {
+ // Try to fetch the old mirror path first
+ if (GMconfig.isConfigurationSection("settings.permission.world.mirror"))
+ return (Map<String, Object>) GMconfig.getConfigurationSection("settings.permission.world.mirror").getValues(false);
+ else
+ return (Map<String, Object>) GMconfig.getConfigurationSection("settings.mirrors").getValues(false);
+
}
public Integer getSaveInterval() {
return GMconfig.getInt("settings.data.save.minutes", 10);
}
+
+ public Integer getBackupDuration() {
+ return GMconfig.getInt("settings.data.save.hours", 24);
+ }
public void adjustLoggerLevel() {
diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java
index 43a57c419..5ef4fc537 100644
--- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java
+++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java
@@ -57,7 +57,7 @@ public class GroupManager extends JavaPlugin {
private boolean validateOnlinePlayer = true;
private boolean isReady = false;
private static boolean isLoaded = false;
- private GMConfiguration config;
+ protected GMConfiguration config;
private GMLoggerHandler ch;
public static BukkitPermissions BukkitPermissions;
private static WorldListener WorldEvents;
@@ -163,6 +163,7 @@ public class GroupManager extends JavaPlugin {
int minutes = getGMConfig().getSaveInterval();
scheduler.scheduleAtFixedRate(commiter, minutes, minutes, TimeUnit.MINUTES);
GroupManager.logger.info("Scheduled Data Saving is set for every " + minutes + " minutes!");
+ GroupManager.logger.info("Backups will be retained for " + getGMConfig().getBackupDuration() + " hours!");
}
}
@@ -369,7 +370,8 @@ public class GroupManager extends JavaPlugin {
//PARECE OK
auxUser.setGroup(auxGroup);
- sender.sendMessage(ChatColor.YELLOW + "You changed player '" + auxUser.getName() + "' group to '" + auxGroup.getName() + "'.");
+ if (!sender.hasPermission("groupmanager.notify.other"))
+ sender.sendMessage(ChatColor.YELLOW + "You changed player '" + auxUser.getName() + "' group to '" + auxGroup.getName() + "'.");
targetPlayer = this.getServer().getPlayer(auxUser.getName());
if (targetPlayer != null) BukkitPermissions.updatePermissions(targetPlayer);
@@ -1590,7 +1592,8 @@ public class GroupManager extends JavaPlugin {
}
//PARECE OK
auxUser.setGroup(auxGroup);
- sender.sendMessage(ChatColor.YELLOW + "You changed " + auxUser.getName() + " group to " + auxGroup.getName() + ".");
+ if (!sender.hasPermission("groupmanager.notify.other"))
+ sender.sendMessage(ChatColor.YELLOW + "You changed " + auxUser.getName() + " group to " + auxGroup.getName() + ".");
targetPlayer = this.getServer().getPlayer(auxUser.getName());
if (targetPlayer != null) BukkitPermissions.updatePermissions(targetPlayer);
@@ -1648,7 +1651,8 @@ public class GroupManager extends JavaPlugin {
}
//PARECE OK
auxUser.setGroup(auxGroup);
- sender.sendMessage(ChatColor.YELLOW + "You changed " + auxUser.getName() + " group to " + auxGroup.getName() + ".");
+ if (!sender.hasPermission("groupmanager.notify.other"))
+ sender.sendMessage(ChatColor.YELLOW + "You changed " + auxUser.getName() + " group to " + auxGroup.getName() + ".");
targetPlayer = this.getServer().getPlayer(auxUser.getName());
if (targetPlayer != null) BukkitPermissions.updatePermissions(targetPlayer);
@@ -1770,10 +1774,10 @@ public class GroupManager extends JavaPlugin {
for(Player test: Bukkit.getServer().getOnlinePlayers()) {
if (!test.equals(player)){
if (test.hasPermission("groupmanager.notify.other"))
- test.sendMessage(ChatColor.YELLOW + name +" was " + msg);
+ test.sendMessage(ChatColor.YELLOW + name +" was" + msg);
} else
if ((player != null) && ((player.hasPermission("groupmanager.notify.self")) || (player.hasPermission("groupmanager.notify.other"))))
- player.sendMessage(ChatColor.YELLOW + "You were " + msg);
+ player.sendMessage(ChatColor.YELLOW + "You were" + msg);
}
}
diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/data/DataUnit.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/data/DataUnit.java
index f17d8b21e..7b027a94d 100644
--- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/data/DataUnit.java
+++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/data/DataUnit.java
@@ -102,7 +102,7 @@ public abstract class DataUnit {
* @return a copy of the permission list
*/
public ArrayList<String> getPermissionList() {
- return (ArrayList<String>) permissions.clone();
+ return new ArrayList<String>(permissions);
}
public void sortPermissions() {
diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/data/Group.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/data/Group.java
index c881fc95d..84d4fceed 100644
--- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/data/Group.java
+++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/data/Group.java
@@ -41,7 +41,7 @@ public class Group extends DataUnit implements Cloneable {
@Override
public Group clone() {
Group clone = new Group(getDataSource(), this.getName());
- clone.inherits = ((ArrayList<String>) this.getInherits().clone());
+ clone.inherits = new ArrayList<String>(this.getInherits());
for (String perm : this.getPermissionList()) {
clone.addPermission(perm);
}
@@ -60,7 +60,7 @@ public class Group extends DataUnit implements Cloneable {
return null;
}
Group clone = getDataSource().createGroup(this.getName());
- clone.inherits = ((ArrayList<String>) this.getInherits().clone());
+ clone.inherits = new ArrayList<String>(this.getInherits());
for (String perm : this.getPermissionList()) {
clone.addPermission(perm);
}
@@ -76,7 +76,7 @@ public class Group extends DataUnit implements Cloneable {
* @return the inherits
*/
public ArrayList<String> getInherits() {
- return (ArrayList<String>) inherits.clone();
+ return new ArrayList<String>(inherits);
}
/**
diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/data/User.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/data/User.java
index a95018fa9..8a0e6cdbb 100644
--- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/data/User.java
+++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/data/User.java
@@ -183,7 +183,7 @@ public class User extends DataUnit implements Cloneable {
}
public ArrayList<String> subGroupListStringCopy() {
- return (ArrayList<String>) subGroups.clone();
+ return new ArrayList<String>(subGroups);
}
/**
diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java
index 0bcf9a827..7cbe4f65c 100644
--- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java
+++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java
@@ -99,11 +99,11 @@ public class WorldsHolder {
* don't load any worlds which are already loaded
* or mirrored worlds that don't need data.
*/
- if (worldsData.containsKey(folder.getName().toLowerCase())
- || mirrors.containsKey(folder.getName().toLowerCase())) {
- continue;
+ if (!worldsData.containsKey(folder.getName().toLowerCase())
+ || !mirrors.containsKey(folder.getName().toLowerCase())) {
+ loadWorld(folder.getName());
}
- loadWorld(folder.getName());
+
}
}
}
@@ -168,7 +168,7 @@ public class WorldsHolder {
if (alreadyDone.contains(w)) {
continue;
}
- Tasks.removeOldFiles(plugin.getBackupFolder());
+ Tasks.removeOldFiles(plugin, plugin.getBackupFolder());
if (w == null) {
GroupManager.logger.severe("WHAT HAPPENED?");
continue;
diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java
index 19d90854b..6a5968c22 100644
--- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java
+++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java
@@ -43,6 +43,7 @@ import org.bukkit.event.server.ServerListener;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
+import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
@@ -124,15 +125,31 @@ public class BukkitPermissions {
User user = worldData.getUser(player.getName());
- // clear permissions
- for (String permission : attachment.getPermissions().keySet()) {
- attachment.unsetPermission(permission);
- }
+ // clear permissions
+ for (String permission : attachment.getPermissions().keySet())
+ attachment.unsetPermission(permission);
- // find matching permissions
+ /*
+ * find matching permissions
+ *
+ * and base bukkit perms if we are set to allow bukkit permissions to override.
+ */
Boolean value;
for (Permission permission : registeredPermissions) {
- value = worldData.getPermissionsHandler().checkUserPermission(user, permission.getName());
+
+ value = worldData.getPermissionsHandler().checkUserPermission(user, permission.getName());
+
+ // Only check bukkit override IF we don't have the permission directly.
+ if (value = false) {
+ PermissionDefault permDefault = permission.getDefault();
+
+ if ((plugin.getGMConfig().isBukkitPermsOverride())
+ && ((permDefault == PermissionDefault.TRUE)
+ || ((permDefault == PermissionDefault.NOT_OP) && !player.isOp())
+ || ((permDefault == PermissionDefault.OP) && player.isOp())))
+ value = true;
+ }
+
if (value == true)
attachment.setPermission(permission, value);
}
diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/utils/Tasks.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/utils/Tasks.java
index 8e4d58dde..f3defd94a 100644
--- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/utils/Tasks.java
+++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/utils/Tasks.java
@@ -13,6 +13,8 @@ import java.io.OutputStream;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
+
+import org.anjocaido.groupmanager.GroupManager;
import org.anjocaido.groupmanager.data.Group;
/**
@@ -43,9 +45,9 @@ public abstract class Tasks {
copy(in, dst);
}
- public static void removeOldFiles(File folder) {
+ public static void removeOldFiles(GroupManager gm, File folder) {
if (folder.isDirectory()) {
- long oldTime = System.currentTimeMillis() - 86400000L;
+ long oldTime = System.currentTimeMillis() - (((long)gm.getGMConfig().getBackupDuration()*60*60)*1000);
for (File olds : folder.listFiles()) {
if (olds.isFile()) {
if (olds.lastModified() < oldTime) {
diff --git a/EssentialsGroupManager/src/plugin.yml b/EssentialsGroupManager/src/plugin.yml
index a247c7ad0..596b4a8d8 100644
--- a/EssentialsGroupManager/src/plugin.yml
+++ b/EssentialsGroupManager/src/plugin.yml
@@ -1,5 +1,5 @@
name: GroupManager
-version: "1.3 (Phoenix)"
+version: "1.4 (Phoenix)"
main: org.anjocaido.groupmanager.GroupManager
website: http://www.anjocaido.info/
description: Provides on-the-fly system for permissions system created by Nijikokun. But all in memory, and with flat-file saving schedule.
diff --git a/EssentialsUpdate/build.xml b/EssentialsUpdate/build.xml
new file mode 100644
index 000000000..dedb62afa
--- /dev/null
+++ b/EssentialsUpdate/build.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See commented blocks below for -->
+<!-- some examples of how to customize the build. -->
+<!-- (If you delete it and reopen the project it will be recreated.) -->
+<!-- By default, only the Clean and Build commands use this build script. -->
+<!-- Commands such as Run, Debug, and Test only use this build script if -->
+<!-- the Compile on Save feature is turned off for the project. -->
+<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
+<!-- in the project's Project Properties dialog box.-->
+<project name="EssentialsUpdate" default="default" basedir=".">
+ <description>Builds, tests, and runs the project EssentialsUpdate.</description>
+ <import file="nbproject/build-impl.xml"/>
+ <!--
+
+ There exist several targets which are by default empty and which can be
+ used for execution of your tasks. These targets are usually executed
+ before and after some main targets. They are:
+
+ -pre-init: called before initialization of project properties
+ -post-init: called after initialization of project properties
+ -pre-compile: called before javac compilation
+ -post-compile: called after javac compilation
+ -pre-compile-single: called before javac compilation of single file
+ -post-compile-single: called after javac compilation of single file
+ -pre-compile-test: called before javac compilation of JUnit tests
+ -post-compile-test: called after javac compilation of JUnit tests
+ -pre-compile-test-single: called before javac compilation of single JUnit test
+ -post-compile-test-single: called after javac compilation of single JUunit test
+ -pre-jar: called before JAR building
+ -post-jar: called after JAR building
+ -post-clean: called after cleaning build products
+
+ (Targets beginning with '-' are not intended to be called on their own.)
+
+ Example of inserting an obfuscator after compilation could look like this:
+
+ <target name="-post-compile">
+ <obfuscate>
+ <fileset dir="${build.classes.dir}"/>
+ </obfuscate>
+ </target>
+
+ For list of available properties check the imported
+ nbproject/build-impl.xml file.
+
+
+ Another way to customize the build is by overriding existing main targets.
+ The targets of interest are:
+
+ -init-macrodef-javac: defines macro for javac compilation
+ -init-macrodef-junit: defines macro for junit execution
+ -init-macrodef-debug: defines macro for class debugging
+ -init-macrodef-java: defines macro for class execution
+ -do-jar-with-manifest: JAR building (if you are using a manifest)
+ -do-jar-without-manifest: JAR building (if you are not using a manifest)
+ run: execution of project
+ -javadoc-build: Javadoc generation
+ test-report: JUnit report generation
+
+ An example of overriding the target for project execution could look like this:
+
+ <target name="run" depends="EssentialsUpdate-impl.jar">
+ <exec dir="bin" executable="launcher.exe">
+ <arg file="${dist.jar}"/>
+ </exec>
+ </target>
+
+ Notice that the overridden target depends on the jar target and not only on
+ the compile target as the regular run target does. Again, for a list of available
+ properties which you can use, check the target you are overriding in the
+ nbproject/build-impl.xml file.
+
+ -->
+</project>
diff --git a/EssentialsUpdate/manifest.mf b/EssentialsUpdate/manifest.mf
new file mode 100644
index 000000000..328e8e5bc
--- /dev/null
+++ b/EssentialsUpdate/manifest.mf
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
diff --git a/EssentialsUpdate/nbproject/build-impl.xml b/EssentialsUpdate/nbproject/build-impl.xml
new file mode 100644
index 000000000..7a38ac1d4
--- /dev/null
+++ b/EssentialsUpdate/nbproject/build-impl.xml
@@ -0,0 +1,1067 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+*** GENERATED FROM project.xml - DO NOT EDIT ***
+*** EDIT ../build.xml INSTEAD ***
+
+For the purpose of easier reading the script
+is divided into following sections:
+
+ - initialization
+ - compilation
+ - jar
+ - execution
+ - debugging
+ - javadoc
+ - junit compilation
+ - junit execution
+ - junit debugging
+ - applet
+ - cleanup
+
+ -->
+<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="EssentialsUpdate-impl">
+ <fail message="Please build using Ant 1.7.1 or higher.">
+ <condition>
+ <not>
+ <antversion atleast="1.7.1"/>
+ </not>
+ </condition>
+ </fail>
+ <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
+ <!--
+ ======================
+ INITIALIZATION SECTION
+ ======================
+ -->
+ <target name="-pre-init">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="-pre-init" name="-init-private">
+ <property file="nbproject/private/config.properties"/>
+ <property file="nbproject/private/configs/${config}.properties"/>
+ <property file="nbproject/private/private.properties"/>
+ </target>
+ <target name="-pre-init-libraries">
+ <property location="../lib/nblibraries.properties" name="libraries.path"/>
+ <dirname file="${libraries.path}" property="libraries.dir.nativedirsep"/>
+ <pathconvert dirsep="/" property="libraries.dir">
+ <path path="${libraries.dir.nativedirsep}"/>
+ </pathconvert>
+ <basename file="${libraries.path}" property="libraries.basename" suffix=".properties"/>
+ <available file="${libraries.dir}/${libraries.basename}-private.properties" property="private.properties.available"/>
+ </target>
+ <target depends="-pre-init-libraries" if="private.properties.available" name="-init-private-libraries">
+ <loadproperties encoding="ISO-8859-1" srcfile="${libraries.dir}/${libraries.basename}-private.properties">
+ <filterchain>
+ <replacestring from="$${base}" to="${libraries.dir}"/>
+ <escapeunicode/>
+ </filterchain>
+ </loadproperties>
+ </target>
+ <target depends="-pre-init,-init-private,-init-private-libraries" name="-init-libraries">
+ <loadproperties encoding="ISO-8859-1" srcfile="${libraries.path}">
+ <filterchain>
+ <replacestring from="$${base}" to="${libraries.dir}"/>
+ <escapeunicode/>
+ </filterchain>
+ </loadproperties>
+ </target>
+ <target depends="-pre-init,-init-private,-init-libraries" name="-init-user">
+ <property file="${user.properties.file}"/>
+ <!-- The two properties below are usually overridden -->
+ <!-- by the active platform. Just a fallback. -->
+ <property name="default.javac.source" value="1.4"/>
+ <property name="default.javac.target" value="1.4"/>
+ </target>
+ <target depends="-pre-init,-init-private,-init-libraries,-init-user" name="-init-project">
+ <property file="nbproject/configs/${config}.properties"/>
+ <property file="nbproject/project.properties"/>
+ </target>
+ <target depends="-pre-init,-init-private,-init-libraries,-init-user,-init-project,-init-macrodef-property" name="-do-init">
+ <available file="${manifest.file}" property="manifest.available"/>
+ <condition property="splashscreen.available">
+ <and>
+ <not>
+ <equals arg1="${application.splash}" arg2="" trim="true"/>
+ </not>
+ <available file="${application.splash}"/>
+ </and>
+ </condition>
+ <condition property="main.class.available">
+ <and>
+ <isset property="main.class"/>
+ <not>
+ <equals arg1="${main.class}" arg2="" trim="true"/>
+ </not>
+ </and>
+ </condition>
+ <condition property="manifest.available+main.class">
+ <and>
+ <isset property="manifest.available"/>
+ <isset property="main.class.available"/>
+ </and>
+ </condition>
+ <condition property="do.archive">
+ <not>
+ <istrue value="${jar.archive.disabled}"/>
+ </not>
+ </condition>
+ <condition property="do.mkdist">
+ <and>
+ <isset property="do.archive"/>
+ <isset property="libs.CopyLibs.classpath"/>
+ <not>
+ <istrue value="${mkdist.disabled}"/>
+ </not>
+ </and>
+ </condition>
+ <condition property="manifest.available+main.class+mkdist.available">
+ <and>
+ <istrue value="${manifest.available+main.class}"/>
+ <isset property="do.mkdist"/>
+ </and>
+ </condition>
+ <condition property="do.archive+manifest.available">
+ <and>
+ <isset property="manifest.available"/>
+ <istrue value="${do.archive}"/>
+ </and>
+ </condition>
+ <condition property="do.archive+main.class.available">
+ <and>
+ <isset property="main.class.available"/>
+ <istrue value="${do.archive}"/>
+ </and>
+ </condition>
+ <condition property="do.archive+splashscreen.available">
+ <and>
+ <isset property="splashscreen.available"/>
+ <istrue value="${do.archive}"/>
+ </and>
+ </condition>
+ <condition property="do.archive+manifest.available+main.class">
+ <and>
+ <istrue value="${manifest.available+main.class}"/>
+ <istrue value="${do.archive}"/>
+ </and>
+ </condition>
+ <condition property="manifest.available-mkdist.available">
+ <or>
+ <istrue value="${manifest.available}"/>
+ <isset property="do.mkdist"/>
+ </or>
+ </condition>
+ <condition property="manifest.available+main.class-mkdist.available">
+ <or>
+ <istrue value="${manifest.available+main.class}"/>
+ <isset property="do.mkdist"/>
+ </or>
+ </condition>
+ <condition property="have.tests">
+ <or>
+ <available file="${test.src.dir}"/>
+ </or>
+ </condition>
+ <condition property="have.sources">
+ <or>
+ <available file="${src.dir}"/>
+ </or>
+ </condition>
+ <condition property="netbeans.home+have.tests">
+ <and>
+ <isset property="netbeans.home"/>
+ <isset property="have.tests"/>
+ </and>
+ </condition>
+ <condition property="no.javadoc.preview">
+ <and>
+ <isset property="javadoc.preview"/>
+ <isfalse value="${javadoc.preview}"/>
+ </and>
+ </condition>
+ <property name="run.jvmargs" value=""/>
+ <property name="javac.compilerargs" value=""/>
+ <property name="work.dir" value="${basedir}"/>
+ <condition property="no.deps">
+ <and>
+ <istrue value="${no.dependencies}"/>
+ </and>
+ </condition>
+ <property name="javac.debug" value="true"/>
+ <property name="javadoc.preview" value="true"/>
+ <property name="application.args" value=""/>
+ <property name="source.encoding" value="${file.encoding}"/>
+ <property name="runtime.encoding" value="${source.encoding}"/>
+ <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
+ <and>
+ <isset property="javadoc.encoding"/>
+ <not>
+ <equals arg1="${javadoc.encoding}" arg2=""/>
+ </not>
+ </and>
+ </condition>
+ <property name="javadoc.encoding.used" value="${source.encoding}"/>
+ <property name="includes" value="**"/>
+ <property name="excludes" value=""/>
+ <property name="do.depend" value="false"/>
+ <condition property="do.depend.true">
+ <istrue value="${do.depend}"/>
+ </condition>
+ <path id="endorsed.classpath.path" path="${endorsed.classpath}"/>
+ <condition else="" property="endorsed.classpath.cmd.line.arg" value="-Xbootclasspath/p:'${toString:endorsed.classpath.path}'">
+ <length length="0" string="${endorsed.classpath}" when="greater"/>
+ </condition>
+ <condition else="false" property="jdkBug6558476">
+ <and>
+ <matches pattern="1\.[56]" string="${java.specification.version}"/>
+ <not>
+ <os family="unix"/>
+ </not>
+ </and>
+ </condition>
+ <property name="javac.fork" value="${jdkBug6558476}"/>
+ <property name="jar.index" value="false"/>
+ <property name="jar.index.metainf" value="${jar.index}"/>
+ <available file="${meta.inf.dir}/persistence.xml" property="has.persistence.xml"/>
+ </target>
+ <target name="-post-init">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="-pre-init,-init-private,-init-libraries,-init-user,-init-project,-do-init" name="-init-check">
+ <fail unless="src.dir">Must set src.dir</fail>
+ <fail unless="test.src.dir">Must set test.src.dir</fail>
+ <fail unless="build.dir">Must set build.dir</fail>
+ <fail unless="dist.dir">Must set dist.dir</fail>
+ <fail unless="build.classes.dir">Must set build.classes.dir</fail>
+ <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
+ <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
+ <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
+ <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
+ <fail unless="dist.jar">Must set dist.jar</fail>
+ </target>
+ <target name="-init-macrodef-property">
+ <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute name="name"/>
+ <attribute name="value"/>
+ <sequential>
+ <property name="@{name}" value="${@{value}}"/>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-macrodef-javac-with-processors">
+ <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${src.dir}" name="srcdir"/>
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <attribute default="${javac.classpath}" name="classpath"/>
+ <attribute default="${javac.processorpath}" name="processorpath"/>
+ <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="${javac.debug}" name="debug"/>
+ <attribute default="${empty.dir}" name="sourcepath"/>
+ <attribute default="${empty.dir}" name="gensrcdir"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property location="${build.dir}/empty" name="empty.dir"/>
+ <mkdir dir="${empty.dir}"/>
+ <mkdir dir="@{apgeneratedsrcdir}"/>
+ <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
+ <src>
+ <dirset dir="@{gensrcdir}" erroronmissingdir="false">
+ <include name="*"/>
+ </dirset>
+ </src>
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <compilerarg line="${javac.compilerargs}"/>
+ <compilerarg value="-processorpath"/>
+ <compilerarg path="@{processorpath}:${empty.dir}"/>
+ <compilerarg line="${ap.processors.internal}"/>
+ <compilerarg line="${annotation.processing.processor.options}"/>
+ <compilerarg value="-s"/>
+ <compilerarg path="@{apgeneratedsrcdir}"/>
+ <compilerarg line="${ap.proc.none.internal}"/>
+ <customize/>
+ </javac>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-ap-cmdline-properties" name="-init-macrodef-javac-without-processors" unless="ap.supported.internal">
+ <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${src.dir}" name="srcdir"/>
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <attribute default="${javac.classpath}" name="classpath"/>
+ <attribute default="${javac.processorpath}" name="processorpath"/>
+ <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="${javac.debug}" name="debug"/>
+ <attribute default="${empty.dir}" name="sourcepath"/>
+ <attribute default="${empty.dir}" name="gensrcdir"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property location="${build.dir}/empty" name="empty.dir"/>
+ <mkdir dir="${empty.dir}"/>
+ <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
+ <src>
+ <dirset dir="@{gensrcdir}" erroronmissingdir="false">
+ <include name="*"/>
+ </dirset>
+ </src>
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <compilerarg line="${javac.compilerargs}"/>
+ <customize/>
+ </javac>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-macrodef-javac-with-processors,-init-macrodef-javac-without-processors" name="-init-macrodef-javac">
+ <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${src.dir}" name="srcdir"/>
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <attribute default="${javac.classpath}" name="classpath"/>
+ <sequential>
+ <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ </depend>
+ </sequential>
+ </macrodef>
+ <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <sequential>
+ <fail unless="javac.includes">Must set javac.includes</fail>
+ <pathconvert pathsep="${line.separator}" property="javac.includes.binary">
+ <path>
+ <filelist dir="@{destdir}" files="${javac.includes}"/>
+ </path>
+ <globmapper from="*.java" to="*.class"/>
+ </pathconvert>
+ <tempfile deleteonexit="true" property="javac.includesfile.binary"/>
+ <echo file="${javac.includesfile.binary}" message="${javac.includes.binary}"/>
+ <delete>
+ <files includesfile="${javac.includesfile.binary}"/>
+ </delete>
+ <delete>
+ <fileset file="${javac.includesfile.binary}"/>
+ </delete>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-macrodef-junit">
+ <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <sequential>
+ <property name="junit.forkmode" value="perTest"/>
+ <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+ <batchtest todir="${build.test.results.dir}">
+ <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
+ <filename name="@{testincludes}"/>
+ </fileset>
+ </batchtest>
+ <classpath>
+ <path path="${run.test.classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <formatter type="brief" usefile="false"/>
+ <formatter type="xml"/>
+ <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <jvmarg value="-ea"/>
+ <jvmarg line="${run.jvmargs}"/>
+ </junit>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile, -profile-init-check" name="profile-init"/>
+ <target name="-profile-pre-init">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target name="-profile-post-init">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target name="-profile-init-macrodef-profile">
+ <macrodef name="resolve">
+ <attribute name="name"/>
+ <attribute name="value"/>
+ <sequential>
+ <property name="@{name}" value="${env.@{value}}"/>
+ </sequential>
+ </macrodef>
+ <macrodef name="profile">
+ <attribute default="${main.class}" name="classname"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property environment="env"/>
+ <resolve name="profiler.current.path" value="${profiler.info.pathvar}"/>
+ <java classname="@{classname}" dir="${profiler.info.dir}" fork="true" jvm="${profiler.info.jvm}">
+ <jvmarg value="${profiler.info.jvmargs.agent}"/>
+ <jvmarg line="${profiler.info.jvmargs}"/>
+ <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
+ <arg line="${application.args}"/>
+ <classpath>
+ <path path="${run.classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="run-sys-prop."/>
+ <mapper from="run-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <customize/>
+ </java>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile" name="-profile-init-check">
+ <fail unless="profiler.info.jvm">Must set JVM to use for profiling in profiler.info.jvm</fail>
+ <fail unless="profiler.info.jvmargs.agent">Must set profiler agent JVM arguments in profiler.info.jvmargs.agent</fail>
+ </target>
+ <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
+ <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute default="${main.class}" name="name"/>
+ <attribute default="${debug.classpath}" name="classpath"/>
+ <attribute default="" name="stopclassname"/>
+ <sequential>
+ <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ </nbjpdastart>
+ </sequential>
+ </macrodef>
+ <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute default="${build.classes.dir}" name="dir"/>
+ <sequential>
+ <nbjpdareload>
+ <fileset dir="@{dir}" includes="${fix.classes}">
+ <include name="${fix.includes}*.class"/>
+ </fileset>
+ </nbjpdareload>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-debug-args">
+ <property name="version-output" value="java version &quot;${ant.java.version}"/>
+ <condition property="have-jdk-older-than-1.4">
+ <or>
+ <contains string="${version-output}" substring="java version &quot;1.0"/>
+ <contains string="${version-output}" substring="java version &quot;1.1"/>
+ <contains string="${version-output}" substring="java version &quot;1.2"/>
+ <contains string="${version-output}" substring="java version &quot;1.3"/>
+ </or>
+ </condition>
+ <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
+ <istrue value="${have-jdk-older-than-1.4}"/>
+ </condition>
+ <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
+ <os family="windows"/>
+ </condition>
+ <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
+ <isset property="debug.transport"/>
+ </condition>
+ </target>
+ <target depends="-init-debug-args" name="-init-macrodef-debug">
+ <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${main.class}" name="classname"/>
+ <attribute default="${debug.classpath}" name="classpath"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <java classname="@{classname}" dir="${work.dir}" fork="true">
+ <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <jvmarg line="${debug-args-line}"/>
+ <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
+ <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
+ <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
+ <jvmarg line="${run.jvmargs}"/>
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="run-sys-prop."/>
+ <mapper from="run-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <customize/>
+ </java>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-macrodef-java">
+ <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute default="${main.class}" name="classname"/>
+ <attribute default="${run.classpath}" name="classpath"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <java classname="@{classname}" dir="${work.dir}" fork="true">
+ <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
+ <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
+ <jvmarg line="${run.jvmargs}"/>
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="run-sys-prop."/>
+ <mapper from="run-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <customize/>
+ </java>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-macrodef-copylibs">
+ <macrodef name="copylibs" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${manifest.file}" name="manifest"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+ <pathconvert property="run.classpath.without.build.classes.dir">
+ <path path="${run.classpath}"/>
+ <map from="${build.classes.dir.resolved}" to=""/>
+ </pathconvert>
+ <pathconvert pathsep=" " property="jar.classpath">
+ <path path="${run.classpath.without.build.classes.dir}"/>
+ <chainedmapper>
+ <flattenmapper/>
+ <globmapper from="*" to="lib/*"/>
+ </chainedmapper>
+ </pathconvert>
+ <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
+ <copylibs compress="${jar.compress}" index="${jar.index}" indexMetaInf="${jar.index.metainf}" jarfile="${dist.jar}" manifest="@{manifest}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
+ <fileset dir="${build.classes.dir}"/>
+ <manifest>
+ <attribute name="Class-Path" value="${jar.classpath}"/>
+ <customize/>
+ </manifest>
+ </copylibs>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-presetdef-jar">
+ <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <jar compress="${jar.compress}" index="${jar.index}" jarfile="${dist.jar}">
+ <j2seproject1:fileset dir="${build.classes.dir}"/>
+ </jar>
+ </presetdef>
+ </target>
+ <target name="-init-ap-cmdline-properties">
+ <property name="annotation.processing.enabled" value="true"/>
+ <property name="annotation.processing.processors.list" value=""/>
+ <property name="annotation.processing.processor.options" value=""/>
+ <property name="annotation.processing.run.all.processors" value="true"/>
+ <property name="javac.processorpath" value="${javac.classpath}"/>
+ <property name="javac.test.processorpath" value="${javac.test.classpath}"/>
+ <condition property="ap.supported.internal" value="true">
+ <not>
+ <matches pattern="1\.[0-5](\..*)?" string="${javac.source}"/>
+ </not>
+ </condition>
+ </target>
+ <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-ap-cmdline-supported">
+ <condition else="" property="ap.processors.internal" value="-processor ${annotation.processing.processors.list}">
+ <isfalse value="${annotation.processing.run.all.processors}"/>
+ </condition>
+ <condition else="" property="ap.proc.none.internal" value="-proc:none">
+ <isfalse value="${annotation.processing.enabled}"/>
+ </condition>
+ </target>
+ <target depends="-init-ap-cmdline-properties,-init-ap-cmdline-supported" name="-init-ap-cmdline">
+ <property name="ap.cmd.line.internal" value=""/>
+ </target>
+ <target depends="-pre-init,-init-private,-init-libraries,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar,-init-ap-cmdline" name="init"/>
+ <!--
+ ===================
+ COMPILATION SECTION
+ ===================
+ -->
+ <target name="-deps-jar-init" unless="built-jar.properties">
+ <property location="${build.dir}/built-jar.properties" name="built-jar.properties"/>
+ <delete file="${built-jar.properties}" quiet="true"/>
+ </target>
+ <target if="already.built.jar.${basedir}" name="-warn-already-built-jar">
+ <echo level="warn" message="Cycle detected: EssentialsUpdate was already built"/>
+ </target>
+ <target depends="init,-deps-jar-init" name="deps-jar" unless="no.deps">
+ <mkdir dir="${build.dir}"/>
+ <touch file="${built-jar.properties}" verbose="false"/>
+ <property file="${built-jar.properties}" prefix="already.built.jar."/>
+ <antcall target="-warn-already-built-jar"/>
+ <propertyfile file="${built-jar.properties}">
+ <entry key="${basedir}" value=""/>
+ </propertyfile>
+ </target>
+ <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
+ <target depends="init" name="-check-automatic-build">
+ <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
+ </target>
+ <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
+ <antcall target="clean"/>
+ </target>
+ <target depends="init,deps-jar" name="-pre-pre-compile">
+ <mkdir dir="${build.classes.dir}"/>
+ </target>
+ <target name="-pre-compile">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target if="do.depend.true" name="-compile-depend">
+ <pathconvert property="build.generated.subdirs">
+ <dirset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+ <include name="*"/>
+ </dirset>
+ </pathconvert>
+ <j2seproject3:depend srcdir="${src.dir}:${build.generated.subdirs}"/>
+ </target>
+ <target depends="init,deps-jar,-pre-pre-compile,-pre-compile, -copy-persistence-xml,-compile-depend" if="have.sources" name="-do-compile">
+ <j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/>
+ <copy todir="${build.classes.dir}">
+ <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+ </copy>
+ </target>
+ <target if="has.persistence.xml" name="-copy-persistence-xml">
+ <mkdir dir="${build.classes.dir}/META-INF"/>
+ <copy todir="${build.classes.dir}/META-INF">
+ <fileset dir="${meta.inf.dir}" includes="persistence.xml"/>
+ </copy>
+ </target>
+ <target name="-post-compile">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
+ <target name="-pre-compile-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
+ <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+ <j2seproject3:force-recompile/>
+ <j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.dir}"/>
+ </target>
+ <target name="-post-compile-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
+ <!--
+ ====================
+ JAR BUILDING SECTION
+ ====================
+ -->
+ <target depends="init" name="-pre-pre-jar">
+ <dirname file="${dist.jar}" property="dist.jar.dir"/>
+ <mkdir dir="${dist.jar.dir}"/>
+ </target>
+ <target name="-pre-jar">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive" name="-do-jar-without-manifest" unless="manifest.available-mkdist.available">
+ <j2seproject1:jar/>
+ </target>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive+manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class-mkdist.available">
+ <j2seproject1:jar manifest="${manifest.file}"/>
+ </target>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive+manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
+ <j2seproject1:jar manifest="${manifest.file}">
+ <j2seproject1:manifest>
+ <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
+ </j2seproject1:manifest>
+ </j2seproject1:jar>
+ <echo level="info">To run this application from the command line without Ant, try:</echo>
+ <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+ <property location="${dist.jar}" name="dist.jar.resolved"/>
+ <pathconvert property="run.classpath.with.dist.jar">
+ <path path="${run.classpath}"/>
+ <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
+ </pathconvert>
+ <echo level="info">java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
+ </target>
+ <target depends="init" if="do.archive" name="-do-jar-with-libraries-create-manifest" unless="manifest.available">
+ <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/>
+ <touch file="${tmp.manifest.file}" verbose="false"/>
+ </target>
+ <target depends="init" if="do.archive+manifest.available" name="-do-jar-with-libraries-copy-manifest">
+ <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/>
+ <copy file="${manifest.file}" tofile="${tmp.manifest.file}"/>
+ </target>
+ <target depends="init,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest" if="do.archive+main.class.available" name="-do-jar-with-libraries-set-main">
+ <manifest file="${tmp.manifest.file}" mode="update">
+ <attribute name="Main-Class" value="${main.class}"/>
+ </manifest>
+ </target>
+ <target depends="init,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest" if="do.archive+splashscreen.available" name="-do-jar-with-libraries-set-splashscreen">
+ <basename file="${application.splash}" property="splashscreen.basename"/>
+ <mkdir dir="${build.classes.dir}/META-INF"/>
+ <copy failonerror="false" file="${application.splash}" todir="${build.classes.dir}/META-INF"/>
+ <manifest file="${tmp.manifest.file}" mode="update">
+ <attribute name="SplashScreen-Image" value="META-INF/${splashscreen.basename}"/>
+ </manifest>
+ </target>
+ <target depends="init,-init-macrodef-copylibs,compile,-pre-pre-jar,-pre-jar,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest,-do-jar-with-libraries-set-main,-do-jar-with-libraries-set-splashscreen" if="do.mkdist" name="-do-jar-with-libraries-pack">
+ <j2seproject3:copylibs manifest="${tmp.manifest.file}"/>
+ <echo level="info">To run this application from the command line without Ant, try:</echo>
+ <property location="${dist.jar}" name="dist.jar.resolved"/>
+ <echo level="info">java -jar "${dist.jar.resolved}"</echo>
+ </target>
+ <target depends="-do-jar-with-libraries-pack" if="do.archive" name="-do-jar-with-libraries-delete-manifest">
+ <delete>
+ <fileset file="${tmp.manifest.file}"/>
+ </delete>
+ </target>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest,-do-jar-with-libraries-set-main,-do-jar-with-libraries-set-splashscreen,-do-jar-with-libraries-pack,-do-jar-with-libraries-delete-manifest" name="-do-jar-with-libraries"/>
+ <target name="-post-jar">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
+ <!--
+ =================
+ EXECUTION SECTION
+ =================
+ -->
+ <target depends="init,compile" description="Run a main class." name="run">
+ <j2seproject1:java>
+ <customize>
+ <arg line="${application.args}"/>
+ </customize>
+ </j2seproject1:java>
+ </target>
+ <target name="-do-not-recompile">
+ <property name="javac.includes.binary" value=""/>
+ </target>
+ <target depends="init,compile-single" name="run-single">
+ <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+ <j2seproject1:java classname="${run.class}"/>
+ </target>
+ <target depends="init,compile-test-single" name="run-test-with-main">
+ <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+ <j2seproject1:java classname="${run.class}" classpath="${run.test.classpath}"/>
+ </target>
+ <!--
+ =================
+ DEBUGGING SECTION
+ =================
+ -->
+ <target depends="init" if="netbeans.home" name="-debug-start-debugger">
+ <j2seproject1:nbjpdastart name="${debug.class}"/>
+ </target>
+ <target depends="init" if="netbeans.home" name="-debug-start-debugger-main-test">
+ <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${debug.class}"/>
+ </target>
+ <target depends="init,compile" name="-debug-start-debuggee">
+ <j2seproject3:debug>
+ <customize>
+ <arg line="${application.args}"/>
+ </customize>
+ </j2seproject3:debug>
+ </target>
+ <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
+ <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
+ <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
+ </target>
+ <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
+ <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
+ <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+ <j2seproject3:debug classname="${debug.class}"/>
+ </target>
+ <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
+ <target depends="init,compile-test-single" if="netbeans.home" name="-debug-start-debuggee-main-test">
+ <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+ <j2seproject3:debug classname="${debug.class}" classpath="${debug.test.classpath}"/>
+ </target>
+ <target depends="init,compile-test-single,-debug-start-debugger-main-test,-debug-start-debuggee-main-test" if="netbeans.home" name="debug-test-with-main"/>
+ <target depends="init" name="-pre-debug-fix">
+ <fail unless="fix.includes">Must set fix.includes</fail>
+ <property name="javac.includes" value="${fix.includes}.java"/>
+ </target>
+ <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
+ <j2seproject1:nbjpdareload/>
+ </target>
+ <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
+ <!--
+ =================
+ PROFILING SECTION
+ =================
+ -->
+ <target depends="profile-init,compile" description="Profile a project in the IDE." if="netbeans.home" name="profile">
+ <nbprofiledirect>
+ <classpath>
+ <path path="${run.classpath}"/>
+ </classpath>
+ </nbprofiledirect>
+ <profile/>
+ </target>
+ <target depends="profile-init,compile-single" description="Profile a selected class in the IDE." if="netbeans.home" name="profile-single">
+ <fail unless="profile.class">Must select one file in the IDE or set profile.class</fail>
+ <nbprofiledirect>
+ <classpath>
+ <path path="${run.classpath}"/>
+ </classpath>
+ </nbprofiledirect>
+ <profile classname="${profile.class}"/>
+ </target>
+ <!--
+ =========================
+ APPLET PROFILING SECTION
+ =========================
+ -->
+ <target depends="profile-init,compile-single" if="netbeans.home" name="profile-applet">
+ <nbprofiledirect>
+ <classpath>
+ <path path="${run.classpath}"/>
+ </classpath>
+ </nbprofiledirect>
+ <profile classname="sun.applet.AppletViewer">
+ <customize>
+ <arg value="${applet.url}"/>
+ </customize>
+ </profile>
+ </target>
+ <!--
+ =========================
+ TESTS PROFILING SECTION
+ =========================
+ -->
+ <target depends="profile-init,compile-test-single" if="netbeans.home" name="profile-test-single">
+ <nbprofiledirect>
+ <classpath>
+ <path path="${run.test.classpath}"/>
+ </classpath>
+ </nbprofiledirect>
+ <junit dir="${profiler.info.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" jvm="${profiler.info.jvm}" showoutput="true">
+ <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
+ <jvmarg value="${profiler.info.jvmargs.agent}"/>
+ <jvmarg line="${profiler.info.jvmargs}"/>
+ <test name="${profile.class}"/>
+ <classpath>
+ <path path="${run.test.classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <formatter type="brief" usefile="false"/>
+ <formatter type="xml"/>
+ </junit>
+ </target>
+ <!--
+ ===============
+ JAVADOC SECTION
+ ===============
+ -->
+ <target depends="init" if="have.sources" name="-javadoc-build">
+ <mkdir dir="${dist.javadoc.dir}"/>
+ <javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
+ <classpath>
+ <path path="${javac.classpath}"/>
+ </classpath>
+ <fileset dir="${src.dir}" excludes="*.java,${excludes}" includes="${includes}">
+ <filename name="**/*.java"/>
+ </fileset>
+ <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+ <include name="**/*.java"/>
+ <exclude name="*.java"/>
+ </fileset>
+ </javadoc>
+ <copy todir="${dist.javadoc.dir}">
+ <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
+ <filename name="**/doc-files/**"/>
+ </fileset>
+ <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+ <include name="**/doc-files/**"/>
+ </fileset>
+ </copy>
+ </target>
+ <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
+ <nbbrowse file="${dist.javadoc.dir}/index.html"/>
+ </target>
+ <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
+ <!--
+ =========================
+ JUNIT COMPILATION SECTION
+ =========================
+ -->
+ <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
+ <mkdir dir="${build.test.classes.dir}"/>
+ </target>
+ <target name="-pre-compile-test">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target if="do.depend.true" name="-compile-test-depend">
+ <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
+ </target>
+ <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
+ <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" processorpath="${javac.test.processorpath}" srcdir="${test.src.dir}"/>
+ <copy todir="${build.test.classes.dir}">
+ <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+ </copy>
+ </target>
+ <target name="-post-compile-test">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
+ <target name="-pre-compile-test-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
+ <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+ <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
+ <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" processorpath="${javac.test.processorpath}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
+ <copy todir="${build.test.classes.dir}">
+ <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+ </copy>
+ </target>
+ <target name="-post-compile-test-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
+ <!--
+ =======================
+ JUNIT EXECUTION SECTION
+ =======================
+ -->
+ <target depends="init" if="have.tests" name="-pre-test-run">
+ <mkdir dir="${build.test.results.dir}"/>
+ </target>
+ <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
+ <j2seproject3:junit testincludes="**/*Test.java"/>
+ </target>
+ <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
+ <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+ </target>
+ <target depends="init" if="have.tests" name="test-report"/>
+ <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
+ <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
+ <target depends="init" if="have.tests" name="-pre-test-run-single">
+ <mkdir dir="${build.test.results.dir}"/>
+ </target>
+ <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
+ <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
+ <j2seproject3:junit excludes="" includes="${test.includes}"/>
+ </target>
+ <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
+ <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+ </target>
+ <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
+ <!--
+ =======================
+ JUNIT DEBUGGING SECTION
+ =======================
+ -->
+ <target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
+ <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
+ <property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
+ <delete file="${test.report.file}"/>
+ <mkdir dir="${build.test.results.dir}"/>
+ <j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
+ <customize>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <arg value="${test.class}"/>
+ <arg value="showoutput=true"/>
+ <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
+ <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
+ </customize>
+ </j2seproject3:debug>
+ </target>
+ <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
+ <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
+ </target>
+ <target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
+ <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
+ <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
+ </target>
+ <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
+ <!--
+ =========================
+ APPLET EXECUTION SECTION
+ =========================
+ -->
+ <target depends="init,compile-single" name="run-applet">
+ <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+ <j2seproject1:java classname="sun.applet.AppletViewer">
+ <customize>
+ <arg value="${applet.url}"/>
+ </customize>
+ </j2seproject1:java>
+ </target>
+ <!--
+ =========================
+ APPLET DEBUGGING SECTION
+ =========================
+ -->
+ <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
+ <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+ <j2seproject3:debug classname="sun.applet.AppletViewer">
+ <customize>
+ <arg value="${applet.url}"/>
+ </customize>
+ </j2seproject3:debug>
+ </target>
+ <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
+ <!--
+ ===============
+ CLEANUP SECTION
+ ===============
+ -->
+ <target name="-deps-clean-init" unless="built-clean.properties">
+ <property location="${build.dir}/built-clean.properties" name="built-clean.properties"/>
+ <delete file="${built-clean.properties}" quiet="true"/>
+ </target>
+ <target if="already.built.clean.${basedir}" name="-warn-already-built-clean">
+ <echo level="warn" message="Cycle detected: EssentialsUpdate was already built"/>
+ </target>
+ <target depends="init,-deps-clean-init" name="deps-clean" unless="no.deps">
+ <mkdir dir="${build.dir}"/>
+ <touch file="${built-clean.properties}" verbose="false"/>
+ <property file="${built-clean.properties}" prefix="already.built.clean."/>
+ <antcall target="-warn-already-built-clean"/>
+ <propertyfile file="${built-clean.properties}">
+ <entry key="${basedir}" value=""/>
+ </propertyfile>
+ </target>
+ <target depends="init" name="-do-clean">
+ <delete dir="${build.dir}"/>
+ <delete dir="${dist.dir}" followsymlinks="false" includeemptydirs="true"/>
+ </target>
+ <target name="-post-clean">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
+ <target name="-check-call-dep">
+ <property file="${call.built.properties}" prefix="already.built."/>
+ <condition property="should.call.dep">
+ <not>
+ <isset property="already.built.${call.subproject}"/>
+ </not>
+ </condition>
+ </target>
+ <target depends="-check-call-dep" if="should.call.dep" name="-maybe-call-dep">
+ <ant antfile="${call.script}" inheritall="false" target="${call.target}">
+ <propertyset>
+ <propertyref prefix="transfer."/>
+ <mapper from="transfer.*" to="*" type="glob"/>
+ </propertyset>
+ </ant>
+ </target>
+</project>
diff --git a/EssentialsUpdate/nbproject/genfiles.properties b/EssentialsUpdate/nbproject/genfiles.properties
new file mode 100644
index 000000000..992379672
--- /dev/null
+++ b/EssentialsUpdate/nbproject/genfiles.properties
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=fd4b98a9
+build.xml.script.CRC32=334f342d
+build.xml.stylesheet.CRC32=28e38971@1.44.1.45
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=fd4b98a9
+nbproject/build-impl.xml.script.CRC32=1cab9494
+nbproject/build-impl.xml.stylesheet.CRC32=0ae3a408@1.44.1.45
diff --git a/EssentialsUpdate/nbproject/project.properties b/EssentialsUpdate/nbproject/project.properties
new file mode 100644
index 000000000..fadaa4839
--- /dev/null
+++ b/EssentialsUpdate/nbproject/project.properties
@@ -0,0 +1,75 @@
+annotation.processing.enabled=true
+annotation.processing.enabled.in.editor=false
+annotation.processing.run.all.processors=true
+annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
+application.title=EssentialsUpdate
+application.vendor=essentialsteam
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+# Uncomment to specify the preferred debugger connection transport:
+#debug.transport=dt_socket
+debug.classpath=\
+ ${run.classpath}
+debug.test.classpath=\
+ ${run.test.classpath}
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/EssentialsUpdate.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
+excludes=
+file.reference.bukkit-0.0.1-SNAPSHOT.jar=../lib/bukkit-0.0.1-SNAPSHOT.jar
+includes=**
+jar.compress=true
+javac.classpath=\
+ ${file.reference.bukkit-0.0.1-SNAPSHOT.jar}:\
+ ${libs.junit_4.classpath}
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.processorpath=\
+ ${javac.classpath}
+javac.source=1.6
+javac.target=1.6
+javac.test.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+javac.test.processorpath=\
+ ${javac.test.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+main.class=
+manifest.file=manifest.mf
+meta.inf.dir=${src.dir}/META-INF
+mkdist.disabled=false
+platform.active=default_platform
+run.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+# Space-separated list of JVM arguments used when running the project
+# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
+# or test-sys-prop.name=value to set system properties for unit tests):
+run.jvmargs=
+run.test.classpath=\
+ ${javac.test.classpath}:\
+ ${build.test.classes.dir}
+source.encoding=UTF-8
+src.dir=src
+test.src.dir=test
diff --git a/EssentialsUpdate/nbproject/project.xml b/EssentialsUpdate/nbproject/project.xml
new file mode 100644
index 000000000..6b0efd00f
--- /dev/null
+++ b/EssentialsUpdate/nbproject/project.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+ <type>org.netbeans.modules.java.j2seproject</type>
+ <configuration>
+ <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
+ <name>EssentialsUpdate</name>
+ <source-roots>
+ <root id="src.dir"/>
+ </source-roots>
+ <test-roots>
+ <root id="test.src.dir"/>
+ </test-roots>
+ </data>
+ <libraries xmlns="http://www.netbeans.org/ns/ant-project-libraries/1">
+ <definitions>../lib/nblibraries.properties</definitions>
+ </libraries>
+ </configuration>
+</project>
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsHelp.java b/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsHelp.java
new file mode 100644
index 000000000..3caa22cc3
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsHelp.java
@@ -0,0 +1,479 @@
+package com.earth2me.essentials.update;
+
+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.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;
+import org.jibble.pircbot.User;
+
+
+public class EssentialsHelp extends PlayerListener
+{
+ private transient Player chatUser;
+ private final transient Server server;
+ private final transient Plugin plugin;
+ private final static Charset UTF8 = Charset.forName("utf-8");
+ private transient IrcBot ircBot;
+
+ public EssentialsHelp(final Plugin plugin)
+ {
+ super();
+ 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;
+ ircBot = 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 ( ircBot != null)
+ {
+ ircBot.quit();
+ ircBot = null;
+ }
+ }
+
+ private boolean sendChatMessage(final Player player, final String message)
+ {
+ final String messageCleaned = message.trim();
+ if (messageCleaned.isEmpty())
+ {
+ return false;
+ }
+ if (ircBot == null)
+ {
+ if (messageCleaned.equalsIgnoreCase("yes"))
+ {
+ player.sendMessage("Connecting...");
+ connectToIRC(player);
+ return true;
+ }
+ if (messageCleaned.equalsIgnoreCase("no") || message.equalsIgnoreCase("!quit"))
+ {
+ chatUser = null;
+ return true;
+ }
+ return false;
+ }
+ else
+ {
+ if (ircBot.isKicked()) {
+ chatUser = null;
+ ircBot.quit();
+ ircBot = null;
+ return false;
+ }
+ final String lowMessage = messageCleaned.toLowerCase();
+ if (lowMessage.startsWith("!quit"))
+ {
+ chatUser = null;
+ if (ircBot != null) {
+ ircBot.quit();
+ ircBot = null;
+ }
+ player.sendMessage("Connection closed.");
+ return true;
+ }
+ if (!ircBot.isConnected() || ircBot.getChannels().length == 0)
+ {
+ return false;
+ }
+ if (lowMessage.startsWith("!list"))
+ {
+ final User[] members = ircBot.getUsers();
+ final StringBuilder sb = new StringBuilder();
+ for (User user : members)
+ {
+ if (sb.length() > 0)
+ {
+ sb.append("§f, ");
+ }
+ if (user.isOp() || user.hasVoice())
+ {
+ sb.append("§6");
+ }
+ else
+ {
+ sb.append("§7");
+ }
+ sb.append(user.getPrefix()).append(user.getNick());
+ }
+ player.sendMessage(sb.toString());
+ return true;
+ }
+ 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 true;
+ }
+ if (lowMessage.startsWith("!errors"))
+ {
+ sendErrors();
+ return true;
+ }
+ if (lowMessage.startsWith("!startup"))
+ {
+ sendStartup();
+ return true;
+ }
+ if (lowMessage.startsWith("!config"))
+ {
+ sendConfig();
+ return true;
+ }
+ ircBot.sendMessage(messageCleaned);
+ chatUser.sendMessage("§6" + ircBot.getNick() + ": §7" + messageCleaned);
+ return true;
+ }
+ }
+
+ private String buildIrcName()
+ {
+ final StringBuilder nameBuilder = new StringBuilder();
+ nameBuilder.append(chatUser.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());
+ }
+
+ return nameBuilder.toString();
+ }
+
+ private void connectToIRC(final Player player)
+ {
+ ircBot = new IrcBot(player, "Ess_" + player.getName(), buildIrcName());
+ }
+
+ 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());
+ String message = "Errors: " + url;
+ chatUser.sendMessage("§6" + ircBot.getNick() + ": §7" + message);
+ ircBot.sendMessage(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());
+ String message = "Startup: " + url;
+ chatUser.sendMessage("§6" + ircBot.getNick() + ": §7" + message);
+ ircBot.sendMessage(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());
+ String message = "Essentials config.yml: " + url;
+ chatUser.sendMessage("§6" + ircBot.getNick() + ": §7" + message);
+ ircBot.sendMessage(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)
+ {
+ boolean success = sendChatMessage(event.getPlayer(), event.getMessage());
+ event.setCancelled(success);
+ return;
+ }
+ }
+
+ @Override
+ public void onPlayerQuit(PlayerQuitEvent event)
+ {
+ chatUser = null;
+ if (ircBot != null) {
+ ircBot.quit();
+ ircBot = null;
+ }
+ return;
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsUpdate.java b/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsUpdate.java
new file mode 100644
index 000000000..d4ee6c0fc
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsUpdate.java
@@ -0,0 +1,66 @@
+package com.earth2me.essentials.update;
+
+import com.earth2me.essentials.update.UpdateCheck.CheckResult;
+import org.bukkit.Bukkit;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.plugin.java.JavaPlugin;
+
+
+public class EssentialsUpdate extends JavaPlugin
+{
+ private transient EssentialsHelp essentialsHelp;
+ private transient UpdateProcess updateProcess;
+
+ @Override
+ public void onEnable()
+ {
+ if (!getDataFolder().exists() && !getDataFolder().mkdirs() ) {
+ Bukkit.getLogger().severe("Could not create data folder:"+getDataFolder().getPath());
+ }
+ essentialsHelp = new EssentialsHelp(this);
+ essentialsHelp.registerEvents();
+
+ final UpdateCheck updateCheck = new UpdateCheck(this);
+ updateProcess = new UpdateProcess(this, updateCheck);
+ updateProcess.registerEvents();
+
+ Bukkit.getLogger().info("EssentialsUpdate " + getDescription().getVersion() + " loaded.");
+
+ if (updateCheck.isEssentialsInstalled())
+ {
+ updateCheck.checkForUpdates();
+ final Version myVersion = new Version(getDescription().getVersion());
+ if (updateCheck.getResult() == CheckResult.NEW_ESS && myVersion.equals(updateCheck.getNewVersion()))
+ {
+ Bukkit.getLogger().info("Versions of EssentialsUpdate and Essentials do not match. Starting automatic update.");
+ updateProcess.doAutomaticUpdate();
+ }
+ updateCheck.scheduleUpdateTask();
+ }
+ else
+ {
+ Bukkit.getLogger().info("Essentials is ready for installation. Join the game and follow the instructions.");
+ }
+ }
+
+ @Override
+ public void onDisable()
+ {
+ essentialsHelp.onDisable();
+ }
+
+ @Override
+ public boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] args)
+ {
+ if (command.getName().equalsIgnoreCase("essentialsupdate"))
+ {
+ updateProcess.onCommand(sender);
+ }
+ if (command.getName().equalsIgnoreCase("essentialshelp"))
+ {
+ essentialsHelp.onCommand(sender);
+ }
+ return true;
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java b/EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java
new file mode 100644
index 000000000..888950f34
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java
@@ -0,0 +1,112 @@
+package com.earth2me.essentials.update;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.logging.Logger;
+
+
+public class GetFile
+{
+ private transient URLConnection connection;
+ private transient MessageDigest digest;
+
+ public GetFile(final String urlString) throws MalformedURLException, IOException
+ {
+ final URL url = new URL(urlString);
+ this.connection = url.openConnection();
+ this.connection.setConnectTimeout(1000);
+ this.connection.setReadTimeout(5000);
+ this.connection.setUseCaches(false);
+ this.connection.connect();
+ final int respCode = ((HttpURLConnection)this.connection).getResponseCode();
+ if (respCode >= 300 && respCode < 400 && this.connection.getHeaderField("Location") != null)
+ {
+ connection.getInputStream().close();
+ final URL redirect = new URL(this.connection.getHeaderField("Location"));
+ this.connection = redirect.openConnection();
+ this.connection.setConnectTimeout(1000);
+ this.connection.setReadTimeout(5000);
+ this.connection.setUseCaches(false);
+ this.connection.connect();
+ }
+ }
+
+ public void saveTo(final File file) throws IOException
+ {
+ try
+ {
+ saveTo(file, null);
+ }
+ catch (NoSuchAlgorithmException ex)
+ {
+ // Ignore because the code is never called
+ }
+ }
+
+ public void saveTo(final File file, final String key) throws IOException, NoSuchAlgorithmException
+ {
+ if (key != null)
+ {
+ digest = MessageDigest.getInstance("SHA256");
+ }
+ final byte[] buffer = new byte[1024 * 8];
+ boolean brokenFile = false;
+ final BufferedInputStream input = new BufferedInputStream(connection.getInputStream());
+ try
+ {
+ final BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file));
+ try
+ {
+ int length;
+ do
+ {
+ length = input.read(buffer);
+ if (length >= 0)
+ {
+ if (key != null)
+ {
+ digest.update(buffer, 0, length);
+ }
+ output.write(buffer, 0, length);
+ }
+ }
+ while (length >= 0);
+ if (key != null)
+ {
+ final byte[] checksum = digest.digest();
+ final String checksumString = new BigInteger(checksum).toString(36);
+ if (!checksumString.equals(key))
+ {
+ brokenFile = true;
+ }
+ }
+ }
+ finally
+ {
+ output.close();
+ }
+ if (brokenFile && !file.delete())
+ {
+ Logger.getLogger("Minecraft").severe("Could not delete file " + file.getPath());
+ }
+ }
+ finally
+ {
+ input.close();
+ }
+ if (brokenFile)
+ {
+ throw new IOException("Checksum check failed.");
+ }
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/IrcBot.java b/EssentialsUpdate/src/com/earth2me/essentials/update/IrcBot.java
new file mode 100644
index 000000000..a314df15d
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/IrcBot.java
@@ -0,0 +1,188 @@
+package com.earth2me.essentials.update;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.jibble.pircbot.Colors;
+import org.jibble.pircbot.IrcException;
+import org.jibble.pircbot.PircBot;
+import org.jibble.pircbot.User;
+
+
+public class IrcBot extends PircBot
+{
+ private static final String CHANNEL = "#essentials";
+ private static final int PORT = 6667;
+ private static final String SERVER = "irc.esper.net";
+ private transient boolean reconnect = true;
+ private final transient Player player;
+ private transient boolean kicked = false;
+
+ public IrcBot(final Player player, final String nickName, final String versionString)
+ {
+ super();
+ this.player = player;
+ setName(nickName);
+ setLogin("esshelp");
+ setVersion(versionString);
+ connect();
+ joinChannel(CHANNEL);
+ }
+
+ private void connect()
+ {
+ try
+ {
+ connect(SERVER, PORT);
+ return;
+ }
+ catch (IOException ex)
+ {
+ Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
+ }
+ catch (IrcException ex)
+ {
+ Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
+ }
+ }
+
+ public void quit()
+ {
+ reconnect = false;
+ disconnect();
+ }
+
+ @Override
+ protected void onConnect()
+ {
+ reconnect = true;
+ }
+
+ @Override
+ protected void onDisconnect()
+ {
+ if (reconnect)
+ {
+ int tries = 10;
+ while (!isConnected())
+ {
+ try
+ {
+ tries--;
+ reconnect();
+ }
+ catch (Exception e)
+ {
+ Bukkit.getLogger().log(Level.WARNING, e.getMessage(), e);
+ try
+ {
+ Thread.sleep(10000);
+ }
+ catch (InterruptedException ex)
+ {
+ Bukkit.getLogger().log(Level.WARNING, e.getMessage(), e);
+ }
+ }
+ if (tries <= 0)
+ {
+ player.sendMessage("Connection lost to server.");
+ kicked = true;
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onKick(String channel, String kickerNick, String kickerLogin, String kickerHostname, String recipientNick, String reason)
+ {
+ if (recipientNick.equals(getNick()))
+ {
+ player.sendMessage("You have been kicked from the channel: " + reason);
+ quit();
+ kicked = true;
+ }
+ }
+
+ public boolean isKicked()
+ {
+ return kicked;
+ }
+
+ @Override
+ protected void onMessage(String channel, String sender, String login, String hostname, String message)
+ {
+ player.sendMessage(formatChatMessage(sender, message, false));
+ }
+
+ @Override
+ protected void onAction(String sender, String login, String hostname, String target, String action)
+ {
+ player.sendMessage(formatChatMessage(sender, action, true));
+ }
+
+ @Override
+ protected void onNotice(String sourceNick, String sourceLogin, String sourceHostname, String target, String notice)
+ {
+ player.sendMessage(formatChatMessage(sourceNick, notice, false));
+ }
+
+ @Override
+ protected void onTopic(String channel, String topic, String setBy, long date, boolean changed)
+ {
+ player.sendMessage(formatChatMessage(channel, topic, false));
+ }
+
+ public String formatChatMessage(final String nick, final String message, final boolean action)
+ {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("§6");
+ if (action)
+ {
+ builder.append('*');
+ }
+ builder.append(nick);
+ if (!action)
+ {
+ builder.append(':');
+ }
+ builder.append(" §7");
+ builder.append(replaceColors(message));
+ return builder.toString();
+ }
+
+ private String replaceColors(final String message)
+ {
+ String m = Colors.removeFormatting(message);
+ m = m.replaceAll("\u000310(,(0?[0-9]|1[0-5]))?", "§b");
+ m = m.replaceAll("\u000311(,(0?[0-9]|1[0-5]))?", "§f");
+ m = m.replaceAll("\u000312(,(0?[0-9]|1[0-5]))?", "§9");
+ m = m.replaceAll("\u000313(,(0?[0-9]|1[0-5]))?", "§d");
+ m = m.replaceAll("\u000314(,(0?[0-9]|1[0-5]))?", "§8");
+ m = m.replaceAll("\u000315(,(0?[0-9]|1[0-5]))?", "§7");
+ m = m.replaceAll("\u00030?1(,(0?[0-9]|1[0-5]))?", "§0");
+ m = m.replaceAll("\u00030?2(,(0?[0-9]|1[0-5]))?", "§1");
+ m = m.replaceAll("\u00030?3(,(0?[0-9]|1[0-5]))?", "§2");
+ m = m.replaceAll("\u00030?4(,(0?[0-9]|1[0-5]))?", "§c");
+ m = m.replaceAll("\u00030?5(,(0?[0-9]|1[0-5]))?", "§4");
+ m = m.replaceAll("\u00030?6(,(0?[0-9]|1[0-5]))?", "§5");
+ m = m.replaceAll("\u00030?7(,(0?[0-9]|1[0-5]))?", "§6");
+ m = m.replaceAll("\u00030?8(,(0?[0-9]|1[0-5]))?", "§e");
+ m = m.replaceAll("\u00030?9(,(0?[0-9]|1[0-5]))?", "§a");
+ m = m.replaceAll("\u00030?0(,(0?[0-9]|1[0-5]))?", "§f");
+ m = m.replace("\u000f", "§7");
+ m = Colors.removeColors(m);
+ return m;
+ }
+
+ public void sendMessage(final String message)
+ {
+ sendMessage(CHANNEL, message);
+ }
+
+ public User[] getUsers()
+ {
+ return getUsers(CHANNEL);
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/ModuleInfo.java b/EssentialsUpdate/src/com/earth2me/essentials/update/ModuleInfo.java
new file mode 100644
index 000000000..722fca3e1
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/ModuleInfo.java
@@ -0,0 +1,35 @@
+package com.earth2me.essentials.update;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import org.bukkit.configuration.Configuration;
+
+
+public class ModuleInfo
+{
+ private final transient String url;
+ private final transient String version;
+ private final transient String hash;
+
+ public ModuleInfo(final Configuration updateConfig, final String path)
+ {
+ url = updateConfig.getString(path + ".url", null);
+ version = updateConfig.getString(path + ".version", null);
+ hash = updateConfig.getString(path + ".hash", null);
+ }
+
+ public URL getUrl() throws MalformedURLException
+ {
+ return new URL(url);
+ }
+
+ public String getVersion()
+ {
+ return version;
+ }
+
+ public String getHash()
+ {
+ return hash;
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/PastieUpload.java b/EssentialsUpdate/src/com/earth2me/essentials/update/PastieUpload.java
new file mode 100644
index 000000000..6cad44e4d
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/PastieUpload.java
@@ -0,0 +1,40 @@
+package com.earth2me.essentials.update;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+public class PastieUpload
+{
+ private final transient PostToUrl connection;
+
+ public PastieUpload() throws MalformedURLException
+ {
+ connection = new PostToUrl(new URL("http://pastie.org/pastes"));
+ }
+
+ public String send(final String data) throws IOException
+ {
+ final Map<String, Object> map = new HashMap<String, Object>();
+ map.put("paste[parser_id]", "19");
+ map.put("paste[authorization]", "burger");
+ map.put("paste[body]", data);
+ map.put("paste[restricted]", "1");
+ final String html = connection.send(map);
+ final Matcher matcher = Pattern.compile("(?s).*\\?key=([a-z0-9]+).*").matcher(html);
+ if (matcher.matches())
+ {
+ final String key = matcher.group(1);
+ return "http://pastie.org/private/" + key;
+ }
+ else
+ {
+ throw new IOException("Failed to upload to pastie.org");
+ }
+ }
+} \ No newline at end of file
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/PostToUrl.java b/EssentialsUpdate/src/com/earth2me/essentials/update/PostToUrl.java
new file mode 100644
index 000000000..c8978961b
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/PostToUrl.java
@@ -0,0 +1,66 @@
+package com.earth2me.essentials.update;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.Charset;
+import java.util.Map;
+import java.util.Random;
+
+
+public class PostToUrl
+{
+ private final transient URL url;
+ private final transient String boundary;
+ private final transient Random random = new Random();
+ private final static String CRLF = "\r\n";
+ private final static Charset UTF8 = Charset.forName("utf-8");
+
+ public PostToUrl(final URL url)
+ {
+ this.url = url;
+ final byte[] bytes = new byte[32];
+ random.nextBytes(bytes);
+ this.boundary = "----------" + new BigInteger(bytes).toString(Character.MAX_RADIX) + "_$";
+ }
+
+ public String send(final Map<String, Object> data) throws IOException
+ {
+ final URLConnection connection = url.openConnection();
+ connection.setRequestProperty("content-type", "multipart/form-data; boundary=" + boundary);
+ final StringBuilder dataBuilder = new StringBuilder();
+ for (Map.Entry<String, Object> entry : data.entrySet())
+ {
+ if (entry.getValue() instanceof String)
+ {
+ dataBuilder.append("--").append(boundary).append(CRLF);
+ dataBuilder.append("Content-Disposition: form-data; name=\"").append(entry.getKey()).append('"').append(CRLF);
+ dataBuilder.append(CRLF);
+ dataBuilder.append(entry.getValue()).append(CRLF);
+ }
+ // TODO: Add support for file upload
+ }
+ dataBuilder.append("--").append(boundary).append("--").append(CRLF);
+ dataBuilder.append(CRLF);
+ connection.setDoOutput(true);
+ final byte[] message = dataBuilder.toString().getBytes(UTF8);
+ connection.setRequestProperty("content-length", Integer.toString(message.length));
+ connection.connect();
+ final OutputStream stream = connection.getOutputStream();
+ stream.write(message);
+ stream.close();
+ final BufferedReader page = new BufferedReader(new InputStreamReader(connection.getInputStream(), UTF8));
+ final StringBuilder input = new StringBuilder();
+ String line;
+ while ((line = page.readLine()) != null)
+ {
+ input.append(line).append("\n");
+ }
+ page.close();
+ return input.toString();
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateCheck.java b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateCheck.java
new file mode 100644
index 000000000..dcda252a0
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateCheck.java
@@ -0,0 +1,203 @@
+package com.earth2me.essentials.update;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.bukkit.Bukkit;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.PluginManager;
+
+
+public class UpdateCheck
+{
+ private transient CheckResult result = CheckResult.UNKNOWN;
+ private transient Version currentVersion;
+ private transient Version newVersion = null;
+ private transient int bukkitResult = 0;
+ private transient UpdateFile updateFile;
+ private final static int CHECK_INTERVAL = 20 * 60 * 60 * 6;
+ private final transient Plugin plugin;
+ private transient boolean essentialsInstalled;
+
+ public UpdateCheck(Plugin plugin)
+ {
+ this.plugin = plugin;
+ updateFile = new UpdateFile(plugin);
+ checkForEssentials();
+ }
+
+ private void checkForEssentials()
+ {
+ PluginManager pm = plugin.getServer().getPluginManager();
+ Plugin essentials = pm.getPlugin("Essentials");
+ if (essentials == null)
+ {
+ essentialsInstalled = false;
+ if (new File(plugin.getDataFolder().getParentFile(), "Essentials.jar").exists())
+ {
+ Bukkit.getLogger().severe("Essentials.jar found, but not recognized by Bukkit. Broken download?");
+ }
+ }
+ else
+ {
+ essentialsInstalled = true;
+ currentVersion = new Version(essentials.getDescription().getVersion());
+ }
+ }
+
+ public void scheduleUpdateTask()
+ {
+ plugin.getServer().getScheduler().scheduleAsyncRepeatingTask(plugin, new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ updateFile = new UpdateFile(plugin);
+ checkForUpdates();
+ }
+ }, CHECK_INTERVAL, CHECK_INTERVAL);
+ }
+
+ public boolean isEssentialsInstalled()
+ {
+ return essentialsInstalled;
+ }
+
+ public CheckResult getResult()
+ {
+ return result;
+ }
+
+ int getNewBukkitVersion()
+ {
+ return bukkitResult;
+ }
+
+ VersionInfo getNewVersionInfo()
+ {
+ return updateFile.getVersions().get(newVersion);
+ }
+
+ public enum CheckResult
+ {
+ NEW_ESS, NEW_ESS_BUKKIT, NEW_BUKKIT, OK, UNKNOWN
+ }
+
+ public void checkForUpdates()
+ {
+ if (currentVersion == null)
+ {
+ return;
+ }
+ final Map<Version, VersionInfo> versions = updateFile.getVersions();
+ final int bukkitVersion = getBukkitVersion();
+ Version higher = null;
+ Version found = null;
+ Version lower = null;
+ int bukkitHigher = 0;
+ int bukkitLower = 0;
+ for (Entry<Version, VersionInfo> entry : versions.entrySet())
+ {
+ final int minBukkit = entry.getValue().getMinBukkit();
+ final int maxBukkit = entry.getValue().getMaxBukkit();
+ if (minBukkit == 0 || maxBukkit == 0)
+ {
+ continue;
+ }
+ if (bukkitVersion <= maxBukkit)
+ {
+ if (bukkitVersion < minBukkit)
+ {
+ if (higher == null || higher.compareTo(entry.getKey()) < 0)
+ {
+
+ higher = entry.getKey();
+ bukkitHigher = minBukkit;
+ }
+ }
+ else
+ {
+ if (found == null || found.compareTo(entry.getKey()) < 0)
+ {
+ found = entry.getKey();
+ }
+ }
+ }
+ else
+ {
+ if (lower == null || lower.compareTo(entry.getKey()) < 0)
+ {
+ lower = entry.getKey();
+ bukkitLower = minBukkit;
+ }
+ }
+ }
+ if (found != null)
+ {
+ if (found.compareTo(currentVersion) > 0)
+ {
+ result = CheckResult.NEW_ESS;
+ newVersion = found;
+ }
+ else
+ {
+ result = CheckResult.OK;
+ }
+ }
+ else if (higher != null)
+ {
+ if (higher.compareTo(currentVersion) > 0)
+ {
+ newVersion = higher;
+ result = CheckResult.NEW_ESS_BUKKIT;
+ bukkitResult = bukkitHigher;
+ }
+ else if (higher.compareTo(currentVersion) < 0)
+ {
+ result = CheckResult.UNKNOWN;
+ }
+ else
+ {
+ result = CheckResult.NEW_BUKKIT;
+ bukkitResult = bukkitHigher;
+ }
+ }
+ else if (lower != null)
+ {
+ if (lower.compareTo(currentVersion) > 0)
+ {
+ result = CheckResult.NEW_ESS_BUKKIT;
+ newVersion = lower;
+ bukkitResult = bukkitLower;
+ }
+ else if (lower.compareTo(currentVersion) < 0)
+ {
+ result = CheckResult.UNKNOWN;
+ }
+ else
+ {
+ result = CheckResult.NEW_BUKKIT;
+ bukkitResult = bukkitLower;
+ }
+ }
+
+ }
+
+ private int getBukkitVersion()
+ {
+ final Matcher versionMatch = Pattern.compile("git-Bukkit-([0-9]+).([0-9]+).([0-9]+)-[0-9]+-[0-9a-z]+-b([0-9]+)jnks.*").matcher(plugin.getServer().getVersion());
+ if (versionMatch.matches())
+ {
+ return Integer.parseInt(versionMatch.group(4));
+ }
+ throw new NumberFormatException("Bukkit Version changed!");
+ }
+
+ public Version getNewVersion()
+ {
+ return newVersion;
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateFile.java b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateFile.java
new file mode 100644
index 000000000..8f34bffc4
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateFile.java
@@ -0,0 +1,204 @@
+package com.earth2me.essentials.update;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.configuration.file.YamlConfiguration;
+
+
+public class UpdateFile
+{
+ private final static Logger LOGGER = Logger.getLogger("Minecraft");
+ private final static String UPDATE_URL = "http://goo.gl/67jev";
+ private final static BigInteger PUBLIC_KEY = new BigInteger("5ha6a2d4qdy17ttkg8evh74sl5a87djojwenu12k1lvy8ui6003e6l06rntczpoh99mhc3txj8mqlxw111oyy9yl7s7qpyluyzix3j1odxrxx4u52gxvyu6qiteapczkzvi7rxgeqsozz7b19rdx73a7quo9ybwpz1cr82r7x5k0pg2a73pjjsv2j1awr13azo7klrcxp9y5xxwf5qv1s3tw4zqftli18u0ek5qkbzfbgk1v5n2f11pkwwk6p0mibrn26wnjbv11vyiqgu95o7busmt6vf5q7grpcenl637w83mbin56s3asj1131b2mscj9xep3cbj7la9tgsxl5bj87vzy8sk2d34kzwqdqgh9nry43nqqus12l1stmiv184r8r3jcy8w43e8h1u1mzklldb5eytkuhayqik8l3ns04hwt8sgacvw534be8sx26qrn5s1", 36);
+ private final transient File file;
+ private transient YamlConfiguration updateConfig;
+ private final transient Plugin plugin;
+ private final transient TreeMap<Version, VersionInfo> versions = new TreeMap<Version, VersionInfo>();
+
+ public UpdateFile(final Plugin plugin)
+ {
+ this.plugin = plugin;
+ final long lastUpdate = Long.parseLong(plugin.getConfig().getString("lastupdate", "0"));
+ file = new File(plugin.getDataFolder(), "update.yml");
+ if (lastUpdate < System.currentTimeMillis() - 1000 * 60 * 60 * 6 || !file.exists())
+ {
+ if (file.exists() && !file.delete())
+ {
+ LOGGER.log(Level.SEVERE, "Could not delete file update.yml!");
+ return;
+ }
+ if (!downloadFile() || !checkFile())
+ {
+ LOGGER.log(Level.SEVERE, "Could not download and verify file update.yml!");
+ return;
+ }
+ }
+ try
+ {
+ readVersions();
+ }
+ catch (Exception ex)
+ {
+ LOGGER.log(Level.SEVERE, "Could not load update.yml!");
+ return;
+ }
+ }
+
+ private boolean downloadFile()
+ {
+ GetFile getFile;
+ try
+ {
+ getFile = new GetFile(UPDATE_URL);
+ getFile.saveTo(file);
+ plugin.getConfig().set("lastupdate", System.currentTimeMillis());
+ plugin.getConfig().save(new File(plugin.getDataFolder(),"config.yml"));
+ return true;
+ }
+ catch (IOException ex)
+ {
+ LOGGER.log(Level.SEVERE, "Error while downloading update.yml", ex);
+ return false;
+ }
+ }
+
+ private boolean checkFile()
+ {
+ BufferedInputStream bis = null;
+ try
+ {
+ bis = new BufferedInputStream(new FileInputStream(file));
+ if (bis.read() != '#')
+ {
+ throw new IOException("File has to start with #");
+ }
+ final StringBuilder length = new StringBuilder();
+ final StringBuilder signature = new StringBuilder();
+ boolean isSignature = false;
+ do
+ {
+ final int cur = bis.read();
+ if (cur == -1)
+ {
+ break;
+ }
+ if (cur == ':')
+ {
+ isSignature = true;
+ }
+ else if (cur == '\n')
+ {
+ break;
+ }
+ else if ((cur >= '0' && cur <= '9')
+ || (cur >= 'a' && cur <= 'z'))
+ {
+ if (isSignature)
+ {
+ signature.append((char)cur);
+ }
+ else
+ {
+ length.append((char)cur);
+ }
+ }
+ else
+ {
+ throw new IOException("Illegal character in signature!");
+ }
+ }
+ while (true);
+ if (length.length() == 0 || signature.length() == 0)
+ {
+ throw new IOException("Broken signature!");
+ }
+ final int sigLength = new BigInteger(length.toString(), 36).intValue();
+ if (sigLength < 0 || sigLength > 2048)
+ {
+ throw new IOException("Invalid signature length!");
+ }
+ final byte[] sigBytes = new BigInteger(signature.toString(), 36).toByteArray();
+ if (sigLength < sigBytes.length)
+ {
+ throw new IOException("Length is less then available bytes.");
+ }
+ byte[] realBytes;
+ if (sigLength == sigBytes.length)
+ {
+ realBytes = sigBytes;
+ }
+ else
+ {
+ realBytes = new byte[sigLength];
+ System.arraycopy(sigBytes, 0, realBytes, sigLength - sigBytes.length, sigBytes.length);
+ }
+ final X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(PUBLIC_KEY.toByteArray());
+ final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ final PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
+ final Signature rsa = Signature.getInstance("SHA256withRSA");
+ rsa.initVerify(pubKey);
+ final byte[] buffer = new byte[2048];
+ int readLength;
+ do
+ {
+ readLength = bis.read(buffer);
+ if (readLength >= 0)
+ {
+ rsa.update(buffer, 0, readLength);
+ }
+ }
+ while (readLength >= 0);
+ return rsa.verify(realBytes);
+ }
+ catch (Exception ex)
+ {
+ LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
+ }
+ finally
+ {
+ try
+ {
+ if (bis != null)
+ {
+ bis.close();
+ }
+ }
+ catch (IOException ex)
+ {
+ LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
+ }
+ }
+ return false;
+ }
+
+ private void readVersions() throws Exception
+ {
+ updateConfig = new YamlConfiguration();
+ updateConfig.load(file);
+ versions.clear();
+ for (String versionString : updateConfig.getKeys(false))
+ {
+ final Version version = new Version(versionString);
+ final VersionInfo info = new VersionInfo(updateConfig, versionString);
+ versions.put(version, info);
+ }
+ }
+
+ public Map<Version, VersionInfo> getVersions()
+ {
+ return Collections.unmodifiableMap(versions.descendingMap());
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java
new file mode 100644
index 000000000..95898bcb6
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java
@@ -0,0 +1,128 @@
+package com.earth2me.essentials.update;
+
+import java.util.List;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Event.Priority;
+import org.bukkit.event.Event.Type;
+import org.bukkit.event.player.PlayerChatEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerListener;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.PluginManager;
+
+
+public class UpdateProcess extends PlayerListener
+{
+ private transient Player currentPlayer;
+ private final transient Plugin plugin;
+ private final transient UpdateCheck updateCheck;
+
+ public UpdateProcess(final Plugin plugin, final UpdateCheck updateCheck)
+ {
+ this.plugin = plugin;
+ this.updateCheck = updateCheck;
+ }
+
+ public void registerEvents()
+ {
+ final PluginManager pluginManager = plugin.getServer().getPluginManager();
+ pluginManager.registerEvent(Type.PLAYER_QUIT, this, Priority.Low, plugin);
+ pluginManager.registerEvent(Type.PLAYER_CHAT, this, Priority.Lowest, plugin);
+ }
+
+ @Override
+ public void onPlayerChat(final PlayerChatEvent event)
+ {
+ if (event.getPlayer() == currentPlayer)
+ {
+ reactOnMessage(event.getMessage());
+ event.setCancelled(true);
+ return;
+ }
+ }
+
+ @Override
+ public void onPlayerJoin(final PlayerJoinEvent event)
+ {
+ final Player player = event.getPlayer();
+ if (player.hasPermission("essentials.update") && !updateCheck.isEssentialsInstalled())
+ {
+ player.sendMessage("Hello " + player.getDisplayName());
+ player.sendMessage("Please type /essentialsupdate into the chat to start the installation of Essentials.");
+ }
+ if (player.hasPermission("essentials.update"))
+ {
+ final UpdateCheck.CheckResult result = updateCheck.getResult();
+ switch (result)
+ {
+ case NEW_ESS:
+ player.sendMessage("The new version " + updateCheck.getNewVersion().toString() + " for Essentials is available. Please type /essentialsupdate to update.");
+ break;
+ case NEW_BUKKIT:
+ player.sendMessage("Your bukkit version is not the recommended build for Essentials, please update to version " + updateCheck.getNewBukkitVersion() + ".");
+ break;
+ case NEW_ESS_BUKKIT:
+ player.sendMessage("There is a new version " + updateCheck.getNewVersion().toString() + " of Essentials for Bukkit " + updateCheck.getNewBukkitVersion());
+ break;
+ default:
+ }
+ }
+ }
+
+ void doAutomaticUpdate()
+ {
+ final UpdatesDownloader downloader = new UpdatesDownloader();
+ final VersionInfo info = updateCheck.getNewVersionInfo();
+ final List<String> changelog = info.getChangelog();
+ Bukkit.getLogger().info("Essentials changelog " + updateCheck.getNewVersion().toString());
+ for (String line : changelog)
+ {
+ Bukkit.getLogger().info(" - "+line);
+ }
+ downloader.start(plugin.getServer().getUpdateFolderFile(), info);
+ }
+
+ void doManualUpdate()
+ {
+
+ }
+
+ void onCommand(CommandSender sender)
+ {
+ if (sender instanceof Player && sender.hasPermission("essentials.install"))
+ {
+ if (currentPlayer == null)
+ {
+ currentPlayer = (Player)sender;
+ if (updateCheck.isEssentialsInstalled())
+ {
+ doManualUpdate();
+ }
+ else
+ {
+ sender.sendMessage("Thank you for choosing Essentials.");
+ sender.sendMessage("The following installation wizard will guide you through the installation of Essentials.");
+ sender.sendMessage("Your answers will be saved for a later update.");
+ sender.sendMessage("Please answer the messages with yes or no, if not otherwise stated.");
+ sender.sendMessage("Write bye/exit/quit if you want to exit the wizard at anytime.");
+
+ }
+ }
+ if (!currentPlayer.equals(sender))
+ {
+ sender.sendMessage("The player " + currentPlayer.getDisplayName() + " is already using the wizard.");
+ }
+ }
+ else
+ {
+ sender.sendMessage("Please run the command as op from in game.");
+ }
+ }
+
+ private void reactOnMessage(String message)
+ {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java
new file mode 100644
index 000000000..28ffdfe3c
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java
@@ -0,0 +1,19 @@
+package com.earth2me.essentials.update;
+
+import java.io.File;
+
+
+public class UpdatesDownloader
+{
+
+ UpdatesDownloader()
+ {
+
+ }
+
+ void start(File updateFolderFile, VersionInfo newVersion)
+ {
+
+ }
+
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/Version.java b/EssentialsUpdate/src/com/earth2me/essentials/update/Version.java
new file mode 100644
index 000000000..8e6cbc97f
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/Version.java
@@ -0,0 +1,173 @@
+package com.earth2me.essentials.update;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+public class Version implements Comparable<Version>
+{
+ public enum Type
+ {
+ STABLE, PREVIEW, DEVELOPER
+ }
+
+ public int getMajor()
+ {
+ return major;
+ }
+
+ public int getMinor()
+ {
+ return minor;
+ }
+
+ public int getBuild()
+ {
+ return build;
+ }
+
+ public Type getType()
+ {
+ return type;
+ }
+ private final transient int major;
+ private final transient int minor;
+ private final transient int build;
+ private final transient Type type;
+
+ public Version(final String versionString)
+ {
+ final Matcher matcher = Pattern.compile("(Pre|Dev)?([0-9]+)[_\\.]([0-9]+)[_\\.]([0-9]+).*").matcher(versionString);
+ if (!matcher.matches() || matcher.groupCount() < 4)
+ {
+ type = Type.DEVELOPER;
+ major = 99;
+ minor = build = 0;
+ return;
+ }
+ if (versionString.startsWith("Pre"))
+ {
+ type = Type.PREVIEW;
+ }
+ else if (versionString.startsWith("Dev"))
+ {
+ type = Type.DEVELOPER;
+ }
+ else
+ {
+ type = Type.STABLE;
+ }
+ major = Integer.parseInt(matcher.group(2));
+ minor = Integer.parseInt(matcher.group(3));
+ build = Integer.parseInt(matcher.group(4));
+ }
+
+ @Override
+ public int compareTo(final Version other)
+ {
+ int ret = 0;
+ if (other.getType() == Type.DEVELOPER && getType() != Type.DEVELOPER)
+ {
+ ret = -1;
+ }
+ else if (getType() == Type.DEVELOPER && other.getType() != Type.DEVELOPER)
+ {
+ ret = 1;
+ }
+ else if (other.getMajor() > getMajor())
+ {
+ ret = -1;
+ }
+ else if (getMajor() > other.getMajor())
+ {
+ ret = 1;
+ }
+ else if (other.getMinor() > getMinor())
+ {
+ ret = -1;
+ }
+ else if (getMinor() > other.getMinor())
+ {
+ ret = 1;
+ }
+ else if (other.getBuild() > getBuild())
+ {
+ ret = -1;
+ }
+ else if (getBuild() > other.getBuild())
+ {
+ ret = 1;
+ }
+ else if (other.getType() == Type.STABLE && getType() == Type.PREVIEW)
+ {
+ ret = -1;
+ }
+ else if (getType() == Type.STABLE && other.getType() == Type.PREVIEW)
+ {
+ ret = 1;
+ }
+ return ret;
+ }
+
+ @Override
+ public boolean equals(final Object obj)
+ {
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ final Version other = (Version)obj;
+ if (this.major != other.major)
+ {
+ return false;
+ }
+ if (this.minor != other.minor)
+ {
+ return false;
+ }
+ if (this.build != other.build)
+ {
+ return false;
+ }
+ if (this.type != other.type)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 5;
+ hash = 71 * hash + this.major;
+ hash = 71 * hash + this.minor;
+ hash = 71 * hash + this.build;
+ hash = 71 * hash + (this.type != null ? this.type.hashCode() : 0);
+ return hash;
+ }
+
+ @Override
+ public String toString()
+ {
+ final StringBuilder builder = new StringBuilder();
+ if (type == Type.DEVELOPER)
+ {
+ builder.append("Dev");
+ }
+ if (type == Type.PREVIEW)
+ {
+ builder.append("Pre");
+ }
+ builder.append(major);
+ builder.append('.');
+ builder.append(minor);
+ builder.append('.');
+ builder.append(build);
+ return builder.toString();
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java b/EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java
new file mode 100644
index 000000000..9cd1e5edb
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java
@@ -0,0 +1,48 @@
+package com.earth2me.essentials.update;
+
+import java.util.ArrayList;
+import org.bukkit.configuration.Configuration;
+import java.util.Collections;
+import java.util.List;
+
+
+public class VersionInfo
+{
+ private final transient List<String> changelog;
+ private final transient int minBukkit;
+ private final transient int maxBukkit;
+ private final transient List<ModuleInfo> modules;
+
+ public VersionInfo(final Configuration updateConfig, final String path)
+ {
+ changelog = updateConfig.getList(path + ".changelog", Collections.<String>emptyList());
+ minBukkit = updateConfig.getInt(path + ".min-bukkit", 0);
+ maxBukkit = updateConfig.getInt(path + ".max-bukkit", 0);
+ modules = new ArrayList<ModuleInfo>();
+ final String modulesPath = path + ".modules";
+ for (String module : updateConfig.getKeys(false))
+ {
+ modules.add(new ModuleInfo(updateConfig, modulesPath + module));
+ }
+ }
+
+ public List<String> getChangelog()
+ {
+ return Collections.unmodifiableList(changelog);
+ }
+
+ public int getMinBukkit()
+ {
+ return minBukkit;
+ }
+
+ public int getMaxBukkit()
+ {
+ return maxBukkit;
+ }
+
+ public List<ModuleInfo> getModules()
+ {
+ return Collections.unmodifiableList(modules);
+ }
+}
diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java b/EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java
new file mode 100644
index 000000000..56d2445c9
--- /dev/null
+++ b/EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java
@@ -0,0 +1,7 @@
+package com.earth2me.essentials.update.states;
+
+
+public class Modules
+{
+
+}
diff --git a/EssentialsUpdate/src/org/jibble/pircbot/Colors.java b/EssentialsUpdate/src/org/jibble/pircbot/Colors.java
new file mode 100755
index 000000000..c763cba22
--- /dev/null
+++ b/EssentialsUpdate/src/org/jibble/pircbot/Colors.java
@@ -0,0 +1,293 @@
+/*
+Copyright Paul James Mutton, 2001-2009, http://www.jibble.org/
+
+This file is part of PircBot.
+
+This software is dual-licensed, allowing you to choose between the GNU
+General Public License (GPL) and the www.jibble.org Commercial License.
+Since the GPL may be too restrictive for use in a proprietary application,
+a commercial license is also provided. Full license information can be
+found at http://www.jibble.org/licenses/
+
+*/
+
+
+package org.jibble.pircbot;
+
+/**
+ * The Colors class provides several static fields and methods that you may
+ * find useful when writing an IRC Bot.
+ * <p>
+ * This class contains constants that are useful for formatting lines
+ * sent to IRC servers. These constants allow you to apply various
+ * formatting to the lines, such as colours, boldness, underlining
+ * and reverse text.
+ * <p>
+ * The class contains static methods to remove colours and formatting
+ * from lines of IRC text.
+ * <p>
+ * Here are some examples of how to use the contants from within a
+ * class that extends PircBot and imports org.jibble.pircbot.*;
+ *
+ * <pre> sendMessage("#cs", Colors.BOLD + "A bold hello!");
+ * <b>A bold hello!</b>
+ * sendMessage("#cs", Colors.RED + "Red" + Colors.NORMAL + " text");
+ * <font color="red">Red</font> text
+ * sendMessage("#cs", Colors.BOLD + Colors.RED + "Bold and red");
+ * <b><font color="red">Bold and red</font></b></pre>
+ *
+ * Please note that some IRC channels may be configured to reject any
+ * messages that use colours. Also note that older IRC clients may be
+ * unable to correctly display lines that contain colours and other
+ * control characters.
+ * <p>
+ * Note that this class name has been spelt in the American style in
+ * order to remain consistent with the rest of the Java API.
+ *
+ *
+ * @since 0.9.12
+ * @author Paul James Mutton,
+ * <a href="http://www.jibble.org/">http://www.jibble.org/</a>
+ * @version 1.5.0 (Build time: Mon Dec 14 20:07:17 2009)
+ */
+public class Colors {
+
+
+ /**
+ * Removes all previously applied color and formatting attributes.
+ */
+ public static final String NORMAL = "\u000f";
+
+
+ /**
+ * Bold text.
+ */
+ public static final String BOLD = "\u0002";
+
+
+ /**
+ * Underlined text.
+ */
+ public static final String UNDERLINE = "\u001f";
+
+
+ /**
+ * Reversed text (may be rendered as italic text in some clients).
+ */
+ public static final String REVERSE = "\u0016";
+
+
+ /**
+ * White coloured text.
+ */
+ public static final String WHITE = "\u000300";
+
+
+ /**
+ * Black coloured text.
+ */
+ public static final String BLACK = "\u000301";
+
+
+ /**
+ * Dark blue coloured text.
+ */
+ public static final String DARK_BLUE = "\u000302";
+
+
+ /**
+ * Dark green coloured text.
+ */
+ public static final String DARK_GREEN = "\u000303";
+
+
+ /**
+ * Red coloured text.
+ */
+ public static final String RED = "\u000304";
+
+
+ /**
+ * Brown coloured text.
+ */
+ public static final String BROWN = "\u000305";
+
+
+ /**
+ * Purple coloured text.
+ */
+ public static final String PURPLE = "\u000306";
+
+
+ /**
+ * Olive coloured text.
+ */
+ public static final String OLIVE = "\u000307";
+
+
+ /**
+ * Yellow coloured text.
+ */
+ public static final String YELLOW = "\u000308";
+
+
+ /**
+ * Green coloured text.
+ */
+ public static final String GREEN = "\u000309";
+
+
+ /**
+ * Teal coloured text.
+ */
+ public static final String TEAL = "\u000310";
+
+
+ /**
+ * Cyan coloured text.
+ */
+ public static final String CYAN = "\u000311";
+
+
+ /**
+ * Blue coloured text.
+ */
+ public static final String BLUE = "\u000312";
+
+
+ /**
+ * Magenta coloured text.
+ */
+ public static final String MAGENTA = "\u000313";
+
+
+ /**
+ * Dark gray coloured text.
+ */
+ public static final String DARK_GRAY = "\u000314";
+
+
+ /**
+ * Light gray coloured text.
+ */
+ public static final String LIGHT_GRAY = "\u000315";
+
+
+ /**
+ * This class should not be constructed.
+ */
+ private Colors() {
+
+ }
+
+
+ /**
+ * Removes all colours from a line of IRC text.
+ *
+ * @since PircBot 1.2.0
+ *
+ * @param line the input text.
+ *
+ * @return the same text, but with all colours removed.
+ */
+ public static String removeColors(String line) {
+ int length = line.length();
+ StringBuffer buffer = new StringBuffer();
+ int i = 0;
+ while (i < length) {
+ char ch = line.charAt(i);
+ if (ch == '\u0003') {
+ i++;
+ // Skip "x" or "xy" (foreground color).
+ if (i < length) {
+ ch = line.charAt(i);
+ if (Character.isDigit(ch)) {
+ i++;
+ if (i < length) {
+ ch = line.charAt(i);
+ if (Character.isDigit(ch)) {
+ i++;
+ }
+ }
+ // Now skip ",x" or ",xy" (background color).
+ if (i < length) {
+ ch = line.charAt(i);
+ if (ch == ',') {
+ i++;
+ if (i < length) {
+ ch = line.charAt(i);
+ if (Character.isDigit(ch)) {
+ i++;
+ if (i < length) {
+ ch = line.charAt(i);
+ if (Character.isDigit(ch)) {
+ i++;
+ }
+ }
+ }
+ else {
+ // Keep the comma.
+ i--;
+ }
+ }
+ else {
+ // Keep the comma.
+ i--;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (ch == '\u000f') {
+ i++;
+ }
+ else {
+ buffer.append(ch);
+ i++;
+ }
+ }
+ return buffer.toString();
+ }
+
+
+ /**
+ * Remove formatting from a line of IRC text.
+ *
+ * @since PircBot 1.2.0
+ *
+ * @param line the input text.
+ *
+ * @return the same text, but without any bold, underlining, reverse, etc.
+ */
+ public static String removeFormatting(String line) {
+ int length = line.length();
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < length; i++) {
+ char ch = line.charAt(i);
+ if (ch == '\u000f' || ch == '\u0002' || ch == '\u001f' || ch == '\u0016') {
+ // Don't add this character.
+ }
+ else {
+ buffer.append(ch);
+ }
+ }
+ return buffer.toString();
+ }
+
+
+ /**
+ * Removes all formatting and colours from a line of IRC text.
+ *
+ * @since PircBot 1.2.0
+ *
+ * @param line the input text.
+ *
+ * @return the same text, but without formatting and colour characters.
+ *
+ */
+ public static String removeFormattingAndColors(String line) {
+ return removeFormatting(removeColors(line));
+ }
+
+}
diff --git a/EssentialsUpdate/src/org/jibble/pircbot/InputThread.java b/EssentialsUpdate/src/org/jibble/pircbot/InputThread.java
new file mode 100755
index 000000000..1c30ad815
--- /dev/null
+++ b/EssentialsUpdate/src/org/jibble/pircbot/InputThread.java
@@ -0,0 +1,169 @@
+/*
+Copyright Paul James Mutton, 2001-2009, http://www.jibble.org/
+
+This file is part of PircBot.
+
+This software is dual-licensed, allowing you to choose between the GNU
+General Public License (GPL) and the www.jibble.org Commercial License.
+Since the GPL may be too restrictive for use in a proprietary application,
+a commercial license is also provided. Full license information can be
+found at http://www.jibble.org/licenses/
+
+*/
+
+
+package org.jibble.pircbot;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * A Thread which reads lines from the IRC server. It then
+ * passes these lines to the PircBot without changing them.
+ * This running Thread also detects disconnection from the server
+ * and is thus used by the OutputThread to send lines to the server.
+ *
+ * @author Paul James Mutton,
+ * <a href="http://www.jibble.org/">http://www.jibble.org/</a>
+ * @version 1.5.0 (Build time: Mon Dec 14 20:07:17 2009)
+ */
+public class InputThread extends Thread {
+
+ /**
+ * The InputThread reads lines from the IRC server and allows the
+ * PircBot to handle them.
+ *
+ * @param bot An instance of the underlying PircBot.
+ * @param breader The BufferedReader that reads lines from the server.
+ * @param bwriter The BufferedWriter that sends lines to the server.
+ */
+ InputThread(PircBot bot, Socket socket, BufferedReader breader, BufferedWriter bwriter) {
+ _bot = bot;
+ _socket = socket;
+ _breader = breader;
+ _bwriter = bwriter;
+ this.setName(this.getClass() + "-Thread");
+ }
+
+
+ /**
+ * Sends a raw line to the IRC server as soon as possible, bypassing the
+ * outgoing message queue.
+ *
+ * @param line The raw line to send to the IRC server.
+ */
+ void sendRawLine(String line) {
+ OutputThread.sendRawLine(_bot, _bwriter, line);
+ }
+
+
+ /**
+ * Returns true if this InputThread is connected to an IRC server.
+ * The result of this method should only act as a rough guide,
+ * as the result may not be valid by the time you act upon it.
+ *
+ * @return True if still connected.
+ */
+ boolean isConnected() {
+ return _isConnected;
+ }
+
+
+ /**
+ * Called to start this Thread reading lines from the IRC server.
+ * When a line is read, this method calls the handleLine method
+ * in the PircBot, which may subsequently call an 'onXxx' method
+ * in the PircBot subclass. If any subclass of Throwable (i.e.
+ * any Exception or Error) is thrown by your method, then this
+ * method will print the stack trace to the standard output. It
+ * is probable that the PircBot may still be functioning normally
+ * after such a problem, but the existance of any uncaught exceptions
+ * in your code is something you should really fix.
+ */
+ public void run() {
+ try {
+ boolean running = true;
+ while (running) {
+ try {
+ String line = null;
+ while ((line = _breader.readLine()) != null) {
+ try {
+ _bot.handleLine(line);
+ }
+ catch (Throwable t) {
+ // Stick the whole stack trace into a String so we can output it nicely.
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ t.printStackTrace(pw);
+ pw.flush();
+ StringTokenizer tokenizer = new StringTokenizer(sw.toString(), "\r\n");
+ synchronized (_bot) {
+ _bot.log("### Your implementation of PircBot is faulty and you have");
+ _bot.log("### allowed an uncaught Exception or Error to propagate in your");
+ _bot.log("### code. It may be possible for PircBot to continue operating");
+ _bot.log("### normally. Here is the stack trace that was produced: -");
+ _bot.log("### ");
+ while (tokenizer.hasMoreTokens()) {
+ _bot.log("### " + tokenizer.nextToken());
+ }
+ }
+ }
+ }
+ if (line == null) {
+ // The server must have disconnected us.
+ running = false;
+ }
+ }
+ catch (InterruptedIOException iioe) {
+ // This will happen if we haven't received anything from the server for a while.
+ // So we shall send it a ping to check that we are still connected.
+ this.sendRawLine("PING " + (System.currentTimeMillis() / 1000));
+ // Now we go back to listening for stuff from the server...
+ }
+ }
+ }
+ catch (Exception e) {
+ // Do nothing.
+ }
+
+ // If we reach this point, then we must have disconnected.
+ try {
+ _socket.close();
+ }
+ catch (Exception e) {
+ // Just assume the socket was already closed.
+ }
+
+ if (!_disposed) {
+ _bot.log("*** Disconnected.");
+ _isConnected = false;
+ _bot.onDisconnect();
+ }
+
+ }
+
+
+ /**
+ * Closes the socket without onDisconnect being called subsequently.
+ */
+ public void dispose () {
+ try {
+ _disposed = true;
+ _socket.close();
+ }
+ catch (Exception e) {
+ // Do nothing.
+ }
+ }
+
+ private PircBot _bot = null;
+ private Socket _socket = null;
+ private BufferedReader _breader = null;
+ private BufferedWriter _bwriter = null;
+ private boolean _isConnected = true;
+ private boolean _disposed = false;
+
+ public static final int MAX_LINE_LENGTH = 512;
+
+}
diff --git a/EssentialsUpdate/src/org/jibble/pircbot/IrcException.java b/EssentialsUpdate/src/org/jibble/pircbot/IrcException.java
new file mode 100755
index 000000000..d07d8a956
--- /dev/null
+++ b/EssentialsUpdate/src/org/jibble/pircbot/IrcException.java
@@ -0,0 +1,35 @@
+/*
+Copyright Paul James Mutton, 2001-2009, http://www.jibble.org/
+
+This file is part of PircBot.
+
+This software is dual-licensed, allowing you to choose between the GNU
+General Public License (GPL) and the www.jibble.org Commercial License.
+Since the GPL may be too restrictive for use in a proprietary application,
+a commercial license is also provided. Full license information can be
+found at http://www.jibble.org/licenses/
+
+*/
+
+package org.jibble.pircbot;
+
+/**
+ * An IrcException class.
+ *
+ * @since 0.9
+ * @author Paul James Mutton,
+ * <a href="http://www.jibble.org/">http://www.jibble.org/</a>
+ * @version 1.5.0 (Build time: Mon Dec 14 20:07:17 2009)
+ */
+public class IrcException extends Exception {
+
+ /**
+ * Constructs a new IrcException.
+ *
+ * @param e The error message to report.
+ */
+ public IrcException(String e) {
+ super(e);
+ }
+
+}
diff --git a/EssentialsUpdate/src/org/jibble/pircbot/NickAlreadyInUseException.java b/EssentialsUpdate/src/org/jibble/pircbot/NickAlreadyInUseException.java
new file mode 100755
index 000000000..33887be34
--- /dev/null
+++ b/EssentialsUpdate/src/org/jibble/pircbot/NickAlreadyInUseException.java
@@ -0,0 +1,38 @@
+/*
+Copyright Paul James Mutton, 2001-2009, http://www.jibble.org/
+
+This file is part of PircBot.
+
+This software is dual-licensed, allowing you to choose between the GNU
+General Public License (GPL) and the www.jibble.org Commercial License.
+Since the GPL may be too restrictive for use in a proprietary application,
+a commercial license is also provided. Full license information can be
+found at http://www.jibble.org/licenses/
+
+*/
+
+
+package org.jibble.pircbot;
+
+/**
+ * A NickAlreadyInUseException class. This exception is
+ * thrown when the PircBot attempts to join an IRC server
+ * with a user name that is already in use.
+ *
+ * @since 0.9
+ * @author Paul James Mutton,
+ * <a href="http://www.jibble.org/">http://www.jibble.org/</a>
+ * @version 1.5.0 (Build time: Mon Dec 14 20:07:17 2009)
+ */
+public class NickAlreadyInUseException extends IrcException {
+
+ /**
+ * Constructs a new IrcException.
+ *
+ * @param e The error message to report.
+ */
+ public NickAlreadyInUseException(String e) {
+ super(e);
+ }
+
+}
diff --git a/EssentialsUpdate/src/org/jibble/pircbot/OutputThread.java b/EssentialsUpdate/src/org/jibble/pircbot/OutputThread.java
new file mode 100755
index 000000000..86ac404d2
--- /dev/null
+++ b/EssentialsUpdate/src/org/jibble/pircbot/OutputThread.java
@@ -0,0 +1,105 @@
+/*
+Copyright Paul James Mutton, 2001-2009, http://www.jibble.org/
+
+This file is part of PircBot.
+
+This software is dual-licensed, allowing you to choose between the GNU
+General Public License (GPL) and the www.jibble.org Commercial License.
+Since the GPL may be too restrictive for use in a proprietary application,
+a commercial license is also provided. Full license information can be
+found at http://www.jibble.org/licenses/
+
+*/
+
+
+package org.jibble.pircbot;
+
+import java.io.*;
+import java.net.*;
+
+/**
+ * A Thread which is responsible for sending messages to the IRC server.
+ * Messages are obtained from the outgoing message queue and sent
+ * immediately if possible. If there is a flood of messages, then to
+ * avoid getting kicked from a channel, we put a small delay between
+ * each one.
+ *
+ * @author Paul James Mutton,
+ * <a href="http://www.jibble.org/">http://www.jibble.org/</a>
+ * @version 1.5.0 (Build time: Mon Dec 14 20:07:17 2009)
+ */
+public class OutputThread extends Thread {
+
+
+ /**
+ * Constructs an OutputThread for the underlying PircBot. All messages
+ * sent to the IRC server are sent by this OutputThread to avoid hammering
+ * the server. Messages are sent immediately if possible. If there are
+ * multiple messages queued, then there is a delay imposed.
+ *
+ * @param bot The underlying PircBot instance.
+ * @param outQueue The Queue from which we will obtain our messages.
+ */
+ OutputThread(PircBot bot, Queue outQueue) {
+ _bot = bot;
+ _outQueue = outQueue;
+ this.setName(this.getClass() + "-Thread");
+ }
+
+
+ /**
+ * A static method to write a line to a BufferedOutputStream and then pass
+ * the line to the log method of the supplied PircBot instance.
+ *
+ * @param bot The underlying PircBot instance.
+ * @param out The BufferedOutputStream to write to.
+ * @param line The line to be written. "\r\n" is appended to the end.
+ * @param encoding The charset to use when encoing this string into a
+ * byte array.
+ */
+ static void sendRawLine(PircBot bot, BufferedWriter bwriter, String line) {
+ if (line.length() > bot.getMaxLineLength() - 2) {
+ line = line.substring(0, bot.getMaxLineLength() - 2);
+ }
+ synchronized(bwriter) {
+ try {
+ bwriter.write(line + "\r\n");
+ bwriter.flush();
+ bot.log(">>>" + line);
+ }
+ catch (Exception e) {
+ // Silent response - just lose the line.
+ }
+ }
+ }
+
+
+ /**
+ * This method starts the Thread consuming from the outgoing message
+ * Queue and sending lines to the server.
+ */
+ public void run() {
+ try {
+ boolean running = true;
+ while (running) {
+ // Small delay to prevent spamming of the channel
+ Thread.sleep(_bot.getMessageDelay());
+
+ String line = (String) _outQueue.next();
+ if (line != null) {
+ _bot.sendRawLine(line);
+ }
+ else {
+ running = false;
+ }
+ }
+ }
+ catch (InterruptedException e) {
+ // Just let the method return naturally...
+ }
+ }
+
+ private PircBot _bot = null;
+ private Queue _outQueue = null;
+
+}
diff --git a/EssentialsUpdate/src/org/jibble/pircbot/PircBot.java b/EssentialsUpdate/src/org/jibble/pircbot/PircBot.java
new file mode 100755
index 000000000..a5f049cde
--- /dev/null
+++ b/EssentialsUpdate/src/org/jibble/pircbot/PircBot.java
@@ -0,0 +1,2808 @@
+/*
+Copyright Paul James Mutton, 2001-2009, http://www.jibble.org/
+
+This file is part of PircBot.
+
+This software is dual-licensed, allowing you to choose between the GNU
+General Public License (GPL) and the www.jibble.org Commercial License.
+Since the GPL may be too restrictive for use in a proprietary application,
+a commercial license is also provided. Full license information can be
+found at http://www.jibble.org/licenses/
+
+*/
+
+
+package org.jibble.pircbot;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * PircBot is a Java framework for writing IRC bots quickly and easily.
+ * <p>
+ * It provides an event-driven architecture to handle common IRC
+ * events, flood protection, DCC support, ident support, and more.
+ * The comprehensive logfile format is suitable for use with pisg to generate
+ * channel statistics.
+ * <p>
+ * Methods of the PircBot class can be called to send events to the IRC server
+ * that it connects to. For example, calling the sendMessage method will
+ * send a message to a channel or user on the IRC server. Multiple servers
+ * can be supported using multiple instances of PircBot.
+ * <p>
+ * To perform an action when the PircBot receives a normal message from the IRC
+ * server, you would override the onMessage method defined in the PircBot
+ * class. All on<i>XYZ</i> methods in the PircBot class are automatically called
+ * when the event <i>XYZ</i> happens, so you would override these if you wish
+ * to do something when it does happen.
+ * <p>
+ * Some event methods, such as onPing, should only really perform a specific
+ * function (i.e. respond to a PING from the server). For your convenience, such
+ * methods are already correctly implemented in the PircBot and should not
+ * normally need to be overridden. Please read the full documentation for each
+ * method to see which ones are already implemented by the PircBot class.
+ * <p>
+ * Please visit the PircBot homepage at
+ * <a href="http://www.jibble.org/pircbot.php">http://www.jibble.org/pircbot.php</a>
+ * for full revision history, a beginners guide to creating your first PircBot
+ * and a list of some existing Java IRC bots and clients that use the PircBot
+ * framework.
+ *
+ * @author Paul James Mutton,
+ * <a href="http://www.jibble.org/">http://www.jibble.org/</a>
+ * @version 1.5.0 (Build time: Mon Dec 14 20:07:17 2009)
+ */
+public abstract class PircBot implements ReplyConstants {
+
+
+ /**
+ * The definitive version number of this release of PircBot.
+ * (Note: Change this before automatically building releases)
+ */
+ public static final String VERSION = "1.5.0";
+
+
+ private static final int OP_ADD = 1;
+ private static final int OP_REMOVE = 2;
+ private static final int VOICE_ADD = 3;
+ private static final int VOICE_REMOVE = 4;
+
+
+ /**
+ * Constructs a PircBot with the default settings. Your own constructors
+ * in classes which extend the PircBot abstract class should be responsible
+ * for changing the default settings if required.
+ */
+ public PircBot() {}
+
+
+ /**
+ * Attempt to connect to the specified IRC server.
+ * The onConnect method is called upon success.
+ *
+ * @param hostname The hostname of the server to connect to.
+ *
+ * @throws IOException if it was not possible to connect to the server.
+ * @throws IrcException if the server would not let us join it.
+ * @throws NickAlreadyInUseException if our nick is already in use on the server.
+ */
+ public final synchronized void connect(String hostname) throws IOException, IrcException, NickAlreadyInUseException {
+ this.connect(hostname, 6667, null);
+ }
+
+
+ /**
+ * Attempt to connect to the specified IRC server and port number.
+ * The onConnect method is called upon success.
+ *
+ * @param hostname The hostname of the server to connect to.
+ * @param port The port number to connect to on the server.
+ *
+ * @throws IOException if it was not possible to connect to the server.
+ * @throws IrcException if the server would not let us join it.
+ * @throws NickAlreadyInUseException if our nick is already in use on the server.
+ */
+ public final synchronized void connect(String hostname, int port) throws IOException, IrcException, NickAlreadyInUseException {
+ this.connect(hostname, port, null);
+ }
+
+
+ /**
+ * Attempt to connect to the specified IRC server using the supplied
+ * password.
+ * The onConnect method is called upon success.
+ *
+ * @param hostname The hostname of the server to connect to.
+ * @param port The port number to connect to on the server.
+ * @param password The password to use to join the server.
+ *
+ * @throws IOException if it was not possible to connect to the server.
+ * @throws IrcException if the server would not let us join it.
+ * @throws NickAlreadyInUseException if our nick is already in use on the server.
+ */
+ public final synchronized void connect(String hostname, int port, String password) throws IOException, IrcException, NickAlreadyInUseException {
+
+ _server = hostname;
+ _port = port;
+ _password = password;
+
+ if (isConnected()) {
+ throw new IOException("The PircBot is already connected to an IRC server. Disconnect first.");
+ }
+
+ // Don't clear the outqueue - there might be something important in it!
+
+ // Clear everything we may have know about channels.
+ this.removeAllChannels();
+
+ // Connect to the server.
+ Socket socket = new Socket(hostname, port);
+ this.log("*** Connected to server.");
+
+ _inetAddress = socket.getLocalAddress();
+
+ InputStreamReader inputStreamReader = null;
+ OutputStreamWriter outputStreamWriter = null;
+ if (getEncoding() != null) {
+ // Assume the specified encoding is valid for this JVM.
+ inputStreamReader = new InputStreamReader(socket.getInputStream(), getEncoding());
+ outputStreamWriter = new OutputStreamWriter(socket.getOutputStream(), getEncoding());
+ }
+ else {
+ // Otherwise, just use the JVM's default encoding.
+ inputStreamReader = new InputStreamReader(socket.getInputStream());
+ outputStreamWriter = new OutputStreamWriter(socket.getOutputStream());
+ }
+
+ BufferedReader breader = new BufferedReader(inputStreamReader);
+ BufferedWriter bwriter = new BufferedWriter(outputStreamWriter);
+
+ // Attempt to join the server.
+ if (password != null && !password.equals("")) {
+ OutputThread.sendRawLine(this, bwriter, "PASS " + password);
+ }
+ String nick = this.getName();
+ OutputThread.sendRawLine(this, bwriter, "NICK " + nick);
+ OutputThread.sendRawLine(this, bwriter, "USER " + this.getLogin() + " 8 * :" + this.getVersion());
+
+ _inputThread = new InputThread(this, socket, breader, bwriter);
+
+ // Read stuff back from the server to see if we connected.
+ String line = null;
+ int tries = 1;
+ while ((line = breader.readLine()) != null) {
+
+ this.handleLine(line);
+
+ int firstSpace = line.indexOf(" ");
+ int secondSpace = line.indexOf(" ", firstSpace + 1);
+ if (secondSpace >= 0) {
+ String code = line.substring(firstSpace + 1, secondSpace);
+
+ if (code.equals("004")) {
+ // We're connected to the server.
+ break;
+ }
+ else if (code.equals("433")) {
+ if (_autoNickChange) {
+ tries++;
+ nick = getName() + tries;
+ OutputThread.sendRawLine(this, bwriter, "NICK " + nick);
+ }
+ else {
+ socket.close();
+ _inputThread = null;
+ throw new NickAlreadyInUseException(line);
+ }
+ }
+ else if (code.equals("439")) {
+ // No action required.
+ }
+ else if (code.startsWith("5") || code.startsWith("4")) {
+ socket.close();
+ _inputThread = null;
+ throw new IrcException("Could not log into the IRC server: " + line);
+ }
+ }
+ this.setNick(nick);
+
+ }
+
+ this.log("*** Logged onto server.");
+
+ // This makes the socket timeout on read operations after 5 minutes.
+ // Maybe in some future version I will let the user change this at runtime.
+ socket.setSoTimeout(5 * 60 * 1000);
+
+ // Now start the InputThread to read all other lines from the server.
+ _inputThread.start();
+
+ // Now start the outputThread that will be used to send all messages.
+ if (_outputThread == null) {
+ _outputThread = new OutputThread(this, _outQueue);
+ _outputThread.start();
+ }
+
+ this.onConnect();
+
+ }
+
+
+ /**
+ * Reconnects to the IRC server that we were previously connected to.
+ * If necessary, the appropriate port number and password will be used.
+ * This method will throw an IrcException if we have never connected
+ * to an IRC server previously.
+ *
+ * @since PircBot 0.9.9
+ *
+ * @throws IOException if it was not possible to connect to the server.
+ * @throws IrcException if the server would not let us join it.
+ * @throws NickAlreadyInUseException if our nick is already in use on the server.
+ */
+ public final synchronized void reconnect() throws IOException, IrcException, NickAlreadyInUseException{
+ if (getServer() == null) {
+ throw new IrcException("Cannot reconnect to an IRC server because we were never connected to one previously!");
+ }
+ connect(getServer(), getPort(), getPassword());
+ }
+
+
+ /**
+ * This method disconnects from the server cleanly by calling the
+ * quitServer() method. Providing the PircBot was connected to an
+ * IRC server, the onDisconnect() will be called as soon as the
+ * disconnection is made by the server.
+ *
+ * @see #quitServer() quitServer
+ * @see #quitServer(String) quitServer
+ */
+ public final synchronized void disconnect() {
+ this.quitServer();
+ }
+
+
+ /**
+ * When you connect to a server and your nick is already in use and
+ * this is set to true, a new nick will be automatically chosen.
+ * This is done by adding numbers to the end of the nick until an
+ * available nick is found.
+ *
+ * @param autoNickChange Set to true if you want automatic nick changes
+ * during connection.
+ */
+ public void setAutoNickChange(boolean autoNickChange) {
+ _autoNickChange = autoNickChange;
+ }
+
+ /**
+ * Joins a channel.
+ *
+ * @param channel The name of the channel to join (eg "#cs").
+ */
+ public final void joinChannel(String channel) {
+ this.sendRawLine("JOIN " + channel);
+ }
+
+
+ /**
+ * Joins a channel with a key.
+ *
+ * @param channel The name of the channel to join (eg "#cs").
+ * @param key The key that will be used to join the channel.
+ */
+ public final void joinChannel(String channel, String key) {
+ this.joinChannel(channel + " " + key);
+ }
+
+
+ /**
+ * Parts a channel.
+ *
+ * @param channel The name of the channel to leave.
+ */
+ public final void partChannel(String channel) {
+ this.sendRawLine("PART " + channel);
+ }
+
+
+ /**
+ * Parts a channel, giving a reason.
+ *
+ * @param channel The name of the channel to leave.
+ * @param reason The reason for parting the channel.
+ */
+ public final void partChannel(String channel, String reason) {
+ this.sendRawLine("PART " + channel + " :" + reason);
+ }
+
+
+ /**
+ * Quits from the IRC server.
+ * Providing we are actually connected to an IRC server, the
+ * onDisconnect() method will be called as soon as the IRC server
+ * disconnects us.
+ */
+ public final void quitServer() {
+ this.quitServer("");
+ }
+
+
+ /**
+ * Quits from the IRC server with a reason.
+ * Providing we are actually connected to an IRC server, the
+ * onDisconnect() method will be called as soon as the IRC server
+ * disconnects us.
+ *
+ * @param reason The reason for quitting the server.
+ */
+ public final void quitServer(String reason) {
+ this.sendRawLine("QUIT :" + reason);
+ }
+
+
+ /**
+ * Sends a raw line to the IRC server as soon as possible, bypassing the
+ * outgoing message queue.
+ *
+ * @param line The raw line to send to the IRC server.
+ */
+ public final synchronized void sendRawLine(String line) {
+ if (isConnected()) {
+ _inputThread.sendRawLine(line);
+ }
+ }
+
+ /**
+ * Sends a raw line through the outgoing message queue.
+ *
+ * @param line The raw line to send to the IRC server.
+ */
+ public final synchronized void sendRawLineViaQueue(String line) {
+ if (line == null) {
+ throw new NullPointerException("Cannot send null messages to server");
+ }
+ if (isConnected()) {
+ _outQueue.add(line);
+ }
+ }
+
+
+ /**
+ * Sends a message to a channel or a private message to a user. These
+ * messages are added to the outgoing message queue and sent at the
+ * earliest possible opportunity.
+ * <p>
+ * Some examples: -
+ * <pre> // Send the message "Hello!" to the channel #cs.
+ * sendMessage("#cs", "Hello!");
+ *
+ * // Send a private message to Paul that says "Hi".
+ * sendMessage("Paul", "Hi");</pre>
+ *
+ * You may optionally apply colours, boldness, underlining, etc to
+ * the message by using the <code>Colors</code> class.
+ *
+ * @param target The name of the channel or user nick to send to.
+ * @param message The message to send.
+ *
+ * @see Colors
+ */
+ public final void sendMessage(String target, String message) {
+ _outQueue.add("PRIVMSG " + target + " :" + message);
+ }
+
+
+ /**
+ * Sends an action to the channel or to a user.
+ *
+ * @param target The name of the channel or user nick to send to.
+ * @param action The action to send.
+ *
+ * @see Colors
+ */
+ public final void sendAction(String target, String action) {
+ sendCTCPCommand(target, "ACTION " + action);
+ }
+
+
+ /**
+ * Sends a notice to the channel or to a user.
+ *
+ * @param target The name of the channel or user nick to send to.
+ * @param notice The notice to send.
+ */
+ public final void sendNotice(String target, String notice) {
+ _outQueue.add("NOTICE " + target + " :" + notice);
+ }
+
+
+ /**
+ * Sends a CTCP command to a channel or user. (Client to client protocol).
+ * Examples of such commands are "PING <number>", "FINGER", "VERSION", etc.
+ * For example, if you wish to request the version of a user called "Dave",
+ * then you would call <code>sendCTCPCommand("Dave", "VERSION");</code>.
+ * The type of response to such commands is largely dependant on the target
+ * client software.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param target The name of the channel or user to send the CTCP message to.
+ * @param command The CTCP command to send.
+ */
+ public final void sendCTCPCommand(String target, String command) {
+ _outQueue.add("PRIVMSG " + target + " :\u0001" + command + "\u0001");
+ }
+
+
+ /**
+ * Attempt to change the current nick (nickname) of the bot when it
+ * is connected to an IRC server.
+ * After confirmation of a successful nick change, the getNick method
+ * will return the new nick.
+ *
+ * @param newNick The new nick to use.
+ */
+ public final void changeNick(String newNick) {
+ this.sendRawLine("NICK " + newNick);
+ }
+
+
+ /**
+ * Identify the bot with NickServ, supplying the appropriate password.
+ * Some IRC Networks (such as freenode) require users to <i>register</i> and
+ * <i>identify</i> with NickServ before they are able to send private messages
+ * to other users, thus reducing the amount of spam. If you are using
+ * an IRC network where this kind of policy is enforced, you will need
+ * to make your bot <i>identify</i> itself to NickServ before you can send
+ * private messages. Assuming you have already registered your bot's
+ * nick with NickServ, this method can be used to <i>identify</i> with
+ * the supplied password. It usually makes sense to identify with NickServ
+ * immediately after connecting to a server.
+ * <p>
+ * This method issues a raw NICKSERV command to the server, and is therefore
+ * safer than the alternative approach of sending a private message to
+ * NickServ. The latter approach is considered dangerous, as it may cause
+ * you to inadvertently transmit your password to an untrusted party if you
+ * connect to a network which does not run a NickServ service and where the
+ * untrusted party has assumed the nick "NickServ". However, if your IRC
+ * network is only compatible with the private message approach, you may
+ * typically identify like so:
+ * <pre>sendMessage("NickServ", "identify PASSWORD");</pre>
+ *
+ * @param password The password which will be used to identify with NickServ.
+ */
+ public final void identify(String password) {
+ this.sendRawLine("NICKSERV IDENTIFY " + password);
+ }
+
+
+ /**
+ * Set the mode of a channel.
+ * This method attempts to set the mode of a channel. This
+ * may require the bot to have operator status on the channel.
+ * For example, if the bot has operator status, we can grant
+ * operator status to "Dave" on the #cs channel
+ * by calling setMode("#cs", "+o Dave");
+ * An alternative way of doing this would be to use the op method.
+ *
+ * @param channel The channel on which to perform the mode change.
+ * @param mode The new mode to apply to the channel. This may include
+ * zero or more arguments if necessary.
+ *
+ * @see #op(String,String) op
+ */
+ public final void setMode(String channel, String mode) {
+ this.sendRawLine("MODE " + channel + " " + mode);
+ }
+
+
+ /**
+ * Sends an invitation to join a channel. Some channels can be marked
+ * as "invite-only", so it may be useful to allow a bot to invite people
+ * into it.
+ *
+ * @param nick The nick of the user to invite
+ * @param channel The channel you are inviting the user to join.
+ *
+ */
+ public final void sendInvite(String nick, String channel) {
+ this.sendRawLine("INVITE " + nick + " :" + channel);
+ }
+
+
+ /**
+ * Bans a user from a channel. An example of a valid hostmask is
+ * "*!*compu@*.18hp.net". This may be used in conjunction with the
+ * kick method to permanently remove a user from a channel.
+ * Successful use of this method may require the bot to have operator
+ * status itself.
+ *
+ * @param channel The channel to ban the user from.
+ * @param hostmask A hostmask representing the user we're banning.
+ */
+ public final void ban(String channel, String hostmask) {
+ this.sendRawLine("MODE " + channel + " +b " + hostmask);
+ }
+
+
+ /**
+ * Unbans a user from a channel. An example of a valid hostmask is
+ * "*!*compu@*.18hp.net".
+ * Successful use of this method may require the bot to have operator
+ * status itself.
+ *
+ * @param channel The channel to unban the user from.
+ * @param hostmask A hostmask representing the user we're unbanning.
+ */
+ public final void unBan(String channel, String hostmask) {
+ this.sendRawLine("MODE " + channel + " -b " + hostmask);
+ }
+
+
+ /**
+ * Grants operator privilidges to a user on a channel.
+ * Successful use of this method may require the bot to have operator
+ * status itself.
+ *
+ * @param channel The channel we're opping the user on.
+ * @param nick The nick of the user we are opping.
+ */
+ public final void op(String channel, String nick) {
+ this.setMode(channel, "+o " + nick);
+ }
+
+
+ /**
+ * Removes operator privilidges from a user on a channel.
+ * Successful use of this method may require the bot to have operator
+ * status itself.
+ *
+ * @param channel The channel we're deopping the user on.
+ * @param nick The nick of the user we are deopping.
+ */
+ public final void deOp(String channel, String nick) {
+ this.setMode(channel, "-o " + nick);
+ }
+
+
+ /**
+ * Grants voice privilidges to a user on a channel.
+ * Successful use of this method may require the bot to have operator
+ * status itself.
+ *
+ * @param channel The channel we're voicing the user on.
+ * @param nick The nick of the user we are voicing.
+ */
+ public final void voice(String channel, String nick) {
+ this.setMode(channel, "+v " + nick);
+ }
+
+
+ /**
+ * Removes voice privilidges from a user on a channel.
+ * Successful use of this method may require the bot to have operator
+ * status itself.
+ *
+ * @param channel The channel we're devoicing the user on.
+ * @param nick The nick of the user we are devoicing.
+ */
+ public final void deVoice(String channel, String nick) {
+ this.setMode(channel, "-v " + nick);
+ }
+
+
+ /**
+ * Set the topic for a channel.
+ * This method attempts to set the topic of a channel. This
+ * may require the bot to have operator status if the topic
+ * is protected.
+ *
+ * @param channel The channel on which to perform the mode change.
+ * @param topic The new topic for the channel.
+ *
+ */
+ public final void setTopic(String channel, String topic) {
+ this.sendRawLine("TOPIC " + channel + " :" + topic);
+ }
+
+
+ /**
+ * Kicks a user from a channel.
+ * This method attempts to kick a user from a channel and
+ * may require the bot to have operator status in the channel.
+ *
+ * @param channel The channel to kick the user from.
+ * @param nick The nick of the user to kick.
+ */
+ public final void kick(String channel, String nick) {
+ this.kick(channel, nick, "");
+ }
+
+
+ /**
+ * Kicks a user from a channel, giving a reason.
+ * This method attempts to kick a user from a channel and
+ * may require the bot to have operator status in the channel.
+ *
+ * @param channel The channel to kick the user from.
+ * @param nick The nick of the user to kick.
+ * @param reason A description of the reason for kicking a user.
+ */
+ public final void kick(String channel, String nick, String reason) {
+ this.sendRawLine("KICK " + channel + " " + nick + " :" + reason);
+ }
+
+
+ /**
+ * Issues a request for a list of all channels on the IRC server.
+ * When the PircBot receives information for each channel, it will
+ * call the onChannelInfo method, which you will need to override
+ * if you want it to do anything useful.
+ *
+ * @see #onChannelInfo(String,int,String) onChannelInfo
+ */
+ public final void listChannels() {
+ this.listChannels(null);
+ }
+
+
+ /**
+ * Issues a request for a list of all channels on the IRC server.
+ * When the PircBot receives information for each channel, it will
+ * call the onChannelInfo method, which you will need to override
+ * if you want it to do anything useful.
+ * <p>
+ * Some IRC servers support certain parameters for LIST requests.
+ * One example is a parameter of ">10" to list only those channels
+ * that have more than 10 users in them. Whether these parameters
+ * are supported or not will depend on the IRC server software.
+ *
+ * @param parameters The parameters to supply when requesting the
+ * list.
+ *
+ * @see #onChannelInfo(String,int,String) onChannelInfo
+ */
+ public final void listChannels(String parameters) {
+ if (parameters == null) {
+ this.sendRawLine("LIST");
+ }
+ else {
+ this.sendRawLine("LIST " + parameters);
+ }
+ }
+
+ /**
+ * Adds a line to the log. This log is currently output to the standard
+ * output and is in the correct format for use by tools such as pisg, the
+ * Perl IRC Statistics Generator. You may override this method if you wish
+ * to do something else with log entries.
+ * Each line in the log begins with a number which
+ * represents the logging time (as the number of milliseconds since the
+ * epoch). This timestamp and the following log entry are separated by
+ * a single space character, " ". Outgoing messages are distinguishable
+ * by a log entry that has ">>>" immediately following the space character
+ * after the timestamp. DCC events use "+++" and warnings about unhandled
+ * Exceptions and Errors use "###".
+ * <p>
+ * This implementation of the method will only cause log entries to be
+ * output if the PircBot has had its verbose mode turned on by calling
+ * setVerbose(true);
+ *
+ * @param line The line to add to the log.
+ */
+ public void log(String line) {
+ if (_verbose) {
+ System.out.println(System.currentTimeMillis() + " " + line);
+ }
+ }
+
+
+ /**
+ * This method handles events when any line of text arrives from the server,
+ * then calling the appropriate method in the PircBot. This method is
+ * protected and only called by the InputThread for this instance.
+ * <p>
+ * This method may not be overridden!
+ *
+ * @param line The raw line of text from the server.
+ */
+ protected void handleLine(String line) {
+ this.log(line);
+
+ // Check for server pings.
+ if (line.startsWith("PING ")) {
+ // Respond to the ping and return immediately.
+ this.onServerPing(line.substring(5));
+ return;
+ }
+
+ String sourceNick = "";
+ String sourceLogin = "";
+ String sourceHostname = "";
+
+ StringTokenizer tokenizer = new StringTokenizer(line);
+ String senderInfo = tokenizer.nextToken();
+ String command = tokenizer.nextToken();
+ String target = null;
+
+ int exclamation = senderInfo.indexOf("!");
+ int at = senderInfo.indexOf("@");
+ if (senderInfo.startsWith(":")) {
+ if (exclamation > 0 && at > 0 && exclamation < at) {
+ sourceNick = senderInfo.substring(1, exclamation);
+ sourceLogin = senderInfo.substring(exclamation + 1, at);
+ sourceHostname = senderInfo.substring(at + 1);
+ }
+ else {
+
+ if (tokenizer.hasMoreTokens()) {
+ String token = command;
+
+ int code = -1;
+ try {
+ code = Integer.parseInt(token);
+ }
+ catch (NumberFormatException e) {
+ // Keep the existing value.
+ }
+
+ if (code != -1) {
+ String errorStr = token;
+ String response = line.substring(line.indexOf(errorStr, senderInfo.length()) + 4, line.length());
+ this.processServerResponse(code, response);
+ // Return from the method.
+ return;
+ }
+ else {
+ // This is not a server response.
+ // It must be a nick without login and hostname.
+ // (or maybe a NOTICE or suchlike from the server)
+ sourceNick = senderInfo;
+ target = token;
+ }
+ }
+ else {
+ // We don't know what this line means.
+ this.onUnknown(line);
+ // Return from the method;
+ return;
+ }
+
+ }
+ }
+
+ command = command.toUpperCase();
+ if (sourceNick.startsWith(":")) {
+ sourceNick = sourceNick.substring(1);
+ }
+ if (target == null) {
+ target = tokenizer.nextToken();
+ }
+ if (target.startsWith(":")) {
+ target = target.substring(1);
+ }
+
+ // Check for CTCP requests.
+ if (command.equals("PRIVMSG") && line.indexOf(":\u0001") > 0 && line.endsWith("\u0001")) {
+ String request = line.substring(line.indexOf(":\u0001") + 2, line.length() - 1);
+ if (request.equals("VERSION")) {
+ // VERSION request
+ this.onVersion(sourceNick, sourceLogin, sourceHostname, target);
+ }
+ else if (request.startsWith("ACTION ")) {
+ // ACTION request
+ this.onAction(sourceNick, sourceLogin, sourceHostname, target, request.substring(7));
+ }
+ else if (request.startsWith("PING ")) {
+ // PING request
+ this.onPing(sourceNick, sourceLogin, sourceHostname, target, request.substring(5));
+ }
+ else if (request.equals("TIME")) {
+ // TIME request
+ this.onTime(sourceNick, sourceLogin, sourceHostname, target);
+ }
+ else if (request.equals("FINGER")) {
+ // FINGER request
+ this.onFinger(sourceNick, sourceLogin, sourceHostname, target);
+ }
+ else {
+ // An unknown CTCP message - ignore it.
+ this.onUnknown(line);
+ }
+ }
+ else if (command.equals("PRIVMSG") && _channelPrefixes.indexOf(target.charAt(0)) >= 0) {
+ // This is a normal message to a channel.
+ this.onMessage(target, sourceNick, sourceLogin, sourceHostname, line.substring(line.indexOf(" :") + 2));
+ }
+ else if (command.equals("PRIVMSG")) {
+ // This is a private message to us.
+ this.onPrivateMessage(sourceNick, sourceLogin, sourceHostname, line.substring(line.indexOf(" :") + 2));
+ }
+ else if (command.equals("JOIN")) {
+ // Someone is joining a channel.
+ String channel = target;
+ this.addUser(channel, new User("", sourceNick));
+ this.onJoin(channel, sourceNick, sourceLogin, sourceHostname);
+ }
+ else if (command.equals("PART")) {
+ // Someone is parting from a channel.
+ this.removeUser(target, sourceNick);
+ if (sourceNick.equals(this.getNick())) {
+ this.removeChannel(target);
+ }
+ this.onPart(target, sourceNick, sourceLogin, sourceHostname);
+ }
+ else if (command.equals("NICK")) {
+ // Somebody is changing their nick.
+ String newNick = target;
+ this.renameUser(sourceNick, newNick);
+ if (sourceNick.equals(this.getNick())) {
+ // Update our nick if it was us that changed nick.
+ this.setNick(newNick);
+ }
+ this.onNickChange(sourceNick, sourceLogin, sourceHostname, newNick);
+ }
+ else if (command.equals("NOTICE")) {
+ // Someone is sending a notice.
+ this.onNotice(sourceNick, sourceLogin, sourceHostname, target, line.substring(line.indexOf(" :") + 2));
+ }
+ else if (command.equals("QUIT")) {
+ // Someone has quit from the IRC server.
+ if (sourceNick.equals(this.getNick())) {
+ this.removeAllChannels();
+ }
+ else {
+ this.removeUser(sourceNick);
+ }
+ this.onQuit(sourceNick, sourceLogin, sourceHostname, line.substring(line.indexOf(" :") + 2));
+ }
+ else if (command.equals("KICK")) {
+ // Somebody has been kicked from a channel.
+ String recipient = tokenizer.nextToken();
+ if (recipient.equals(this.getNick())) {
+ this.removeChannel(target);
+ }
+ this.removeUser(target, recipient);
+ this.onKick(target, sourceNick, sourceLogin, sourceHostname, recipient, line.substring(line.indexOf(" :") + 2));
+ }
+ else if (command.equals("MODE")) {
+ // Somebody is changing the mode on a channel or user.
+ String mode = line.substring(line.indexOf(target, 2) + target.length() + 1);
+ if (mode.startsWith(":")) {
+ mode = mode.substring(1);
+ }
+ this.processMode(target, sourceNick, sourceLogin, sourceHostname, mode);
+ }
+ else if (command.equals("TOPIC")) {
+ // Someone is changing the topic.
+ this.onTopic(target, line.substring(line.indexOf(" :") + 2), sourceNick, System.currentTimeMillis(), true);
+ }
+ else if (command.equals("INVITE")) {
+ // Somebody is inviting somebody else into a channel.
+ this.onInvite(target, sourceNick, sourceLogin, sourceHostname, line.substring(line.indexOf(" :") + 2));
+ }
+ else {
+ // If we reach this point, then we've found something that the PircBot
+ // Doesn't currently deal with.
+ this.onUnknown(line);
+ }
+
+ }
+
+
+ /**
+ * This method is called once the PircBot has successfully connected to
+ * the IRC server.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.6
+ */
+ protected void onConnect() {}
+
+
+ /**
+ * This method carries out the actions to be performed when the PircBot
+ * gets disconnected. This may happen if the PircBot quits from the
+ * server, or if the connection is unexpectedly lost.
+ * <p>
+ * Disconnection from the IRC server is detected immediately if either
+ * we or the server close the connection normally. If the connection to
+ * the server is lost, but neither we nor the server have explicitly closed
+ * the connection, then it may take a few minutes to detect (this is
+ * commonly referred to as a "ping timeout").
+ * <p>
+ * If you wish to get your IRC bot to automatically rejoin a server after
+ * the connection has been lost, then this is probably the ideal method to
+ * override to implement such functionality.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ */
+ protected void onDisconnect() {}
+
+
+ /**
+ * This method is called by the PircBot when a numeric response
+ * is received from the IRC server. We use this method to
+ * allow PircBot to process various responses from the server
+ * before then passing them on to the onServerResponse method.
+ * <p>
+ * Note that this method is private and should not appear in any
+ * of the javadoc generated documenation.
+ *
+ * @param code The three-digit numerical code for the response.
+ * @param response The full response from the IRC server.
+ */
+ private final void processServerResponse(int code, String response) {
+
+ if (code == RPL_LIST) {
+ // This is a bit of information about a channel.
+ int firstSpace = response.indexOf(' ');
+ int secondSpace = response.indexOf(' ', firstSpace + 1);
+ int thirdSpace = response.indexOf(' ', secondSpace + 1);
+ int colon = response.indexOf(':');
+ String channel = response.substring(firstSpace + 1, secondSpace);
+ int userCount = 0;
+ try {
+ userCount = Integer.parseInt(response.substring(secondSpace + 1, thirdSpace));
+ }
+ catch (NumberFormatException e) {
+ // Stick with the value of zero.
+ }
+ String topic = response.substring(colon + 1);
+ this.onChannelInfo(channel, userCount, topic);
+ }
+ else if (code == RPL_TOPIC) {
+ // This is topic information about a channel we've just joined.
+ int firstSpace = response.indexOf(' ');
+ int secondSpace = response.indexOf(' ', firstSpace + 1);
+ int colon = response.indexOf(':');
+ String channel = response.substring(firstSpace + 1, secondSpace);
+ String topic = response.substring(colon + 1);
+
+ _topics.put(channel, topic);
+
+ // For backwards compatibility only - this onTopic method is deprecated.
+ this.onTopic(channel, topic);
+ }
+ else if (code == RPL_TOPICINFO) {
+ StringTokenizer tokenizer = new StringTokenizer(response);
+ tokenizer.nextToken();
+ String channel = tokenizer.nextToken();
+ String setBy = tokenizer.nextToken();
+ long date = 0;
+ try {
+ date = Long.parseLong(tokenizer.nextToken()) * 1000;
+ }
+ catch (NumberFormatException e) {
+ // Stick with the default value of zero.
+ }
+
+ String topic = (String) _topics.get(channel);
+ _topics.remove(channel);
+
+ this.onTopic(channel, topic, setBy, date, false);
+ }
+ else if (code == RPL_NAMREPLY) {
+ // This is a list of nicks in a channel that we've just joined.
+ int channelEndIndex = response.indexOf(" :");
+ String channel = response.substring(response.lastIndexOf(' ', channelEndIndex - 1) + 1, channelEndIndex);
+
+ StringTokenizer tokenizer = new StringTokenizer(response.substring(response.indexOf(" :") + 2));
+ while (tokenizer.hasMoreTokens()) {
+ String nick = tokenizer.nextToken();
+ String prefix = "";
+ if (nick.startsWith("@")) {
+ // User is an operator in this channel.
+ prefix = "@";
+ }
+ else if (nick.startsWith("+")) {
+ // User is voiced in this channel.
+ prefix = "+";
+ }
+ else if (nick.startsWith(".")) {
+ // Some wibbly status I've never seen before...
+ prefix = ".";
+ }
+ nick = nick.substring(prefix.length());
+ this.addUser(channel, new User(prefix, nick));
+ }
+ }
+ else if (code == RPL_ENDOFNAMES) {
+ // This is the end of a NAMES list, so we know that we've got
+ // the full list of users in the channel that we just joined.
+ String channel = response.substring(response.indexOf(' ') + 1, response.indexOf(" :"));
+ User[] users = this.getUsers(channel);
+ this.onUserList(channel, users);
+ }
+
+ this.onServerResponse(code, response);
+ }
+
+
+ /**
+ * This method is called when we receive a numeric response from the
+ * IRC server.
+ * <p>
+ * Numerics in the range from 001 to 099 are used for client-server
+ * connections only and should never travel between servers. Replies
+ * generated in response to commands are found in the range from 200
+ * to 399. Error replies are found in the range from 400 to 599.
+ * <p>
+ * For example, we can use this method to discover the topic of a
+ * channel when we join it. If we join the channel #test which
+ * has a topic of &quot;I am King of Test&quot; then the response
+ * will be &quot;<code>PircBot #test :I Am King of Test</code>&quot;
+ * with a code of 332 to signify that this is a topic.
+ * (This is just an example - note that overriding the
+ * <code>onTopic</code> method is an easier way of finding the
+ * topic for a channel). Check the IRC RFC for the full list of other
+ * command response codes.
+ * <p>
+ * PircBot implements the interface ReplyConstants, which contains
+ * contstants that you may find useful here.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param code The three-digit numerical code for the response.
+ * @param response The full response from the IRC server.
+ *
+ * @see ReplyConstants
+ */
+ protected void onServerResponse(int code, String response) {}
+
+
+ /**
+ * This method is called when we receive a user list from the server
+ * after joining a channel.
+ * <p>
+ * Shortly after joining a channel, the IRC server sends a list of all
+ * users in that channel. The PircBot collects this information and
+ * calls this method as soon as it has the full list.
+ * <p>
+ * To obtain the nick of each user in the channel, call the getNick()
+ * method on each User object in the array.
+ * <p>
+ * At a later time, you may call the getUsers method to obtain an
+ * up to date list of the users in the channel.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 1.0.0
+ *
+ * @param channel The name of the channel.
+ * @param users An array of User objects belonging to this channel.
+ *
+ * @see User
+ */
+ protected void onUserList(String channel, User[] users) {}
+
+
+ /**
+ * This method is called whenever a message is sent to a channel.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param channel The channel to which the message was sent.
+ * @param sender The nick of the person who sent the message.
+ * @param login The login of the person who sent the message.
+ * @param hostname The hostname of the person who sent the message.
+ * @param message The actual message sent to the channel.
+ */
+ protected void onMessage(String channel, String sender, String login, String hostname, String message) {}
+
+
+ /**
+ * This method is called whenever a private message is sent to the PircBot.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param sender The nick of the person who sent the private message.
+ * @param login The login of the person who sent the private message.
+ * @param hostname The hostname of the person who sent the private message.
+ * @param message The actual message.
+ */
+ protected void onPrivateMessage(String sender, String login, String hostname, String message) {}
+
+
+ /**
+ * This method is called whenever an ACTION is sent from a user. E.g.
+ * such events generated by typing "/me goes shopping" in most IRC clients.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param sender The nick of the user that sent the action.
+ * @param login The login of the user that sent the action.
+ * @param hostname The hostname of the user that sent the action.
+ * @param target The target of the action, be it a channel or our nick.
+ * @param action The action carried out by the user.
+ */
+ protected void onAction(String sender, String login, String hostname, String target, String action) {}
+
+
+ /**
+ * This method is called whenever we receive a notice.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param sourceNick The nick of the user that sent the notice.
+ * @param sourceLogin The login of the user that sent the notice.
+ * @param sourceHostname The hostname of the user that sent the notice.
+ * @param target The target of the notice, be it our nick or a channel name.
+ * @param notice The notice message.
+ */
+ protected void onNotice(String sourceNick, String sourceLogin, String sourceHostname, String target, String notice) {}
+
+
+ /**
+ * This method is called whenever someone (possibly us) joins a channel
+ * which we are on.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param channel The channel which somebody joined.
+ * @param sender The nick of the user who joined the channel.
+ * @param login The login of the user who joined the channel.
+ * @param hostname The hostname of the user who joined the channel.
+ */
+ protected void onJoin(String channel, String sender, String login, String hostname) {}
+
+
+ /**
+ * This method is called whenever someone (possibly us) parts a channel
+ * which we are on.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param channel The channel which somebody parted from.
+ * @param sender The nick of the user who parted from the channel.
+ * @param login The login of the user who parted from the channel.
+ * @param hostname The hostname of the user who parted from the channel.
+ */
+ protected void onPart(String channel, String sender, String login, String hostname) {}
+
+
+ /**
+ * This method is called whenever someone (possibly us) changes nick on any
+ * of the channels that we are on.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param oldNick The old nick.
+ * @param login The login of the user.
+ * @param hostname The hostname of the user.
+ * @param newNick The new nick.
+ */
+ protected void onNickChange(String oldNick, String login, String hostname, String newNick) {}
+
+
+ /**
+ * This method is called whenever someone (possibly us) is kicked from
+ * any of the channels that we are in.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param channel The channel from which the recipient was kicked.
+ * @param kickerNick The nick of the user who performed the kick.
+ * @param kickerLogin The login of the user who performed the kick.
+ * @param kickerHostname The hostname of the user who performed the kick.
+ * @param recipientNick The unfortunate recipient of the kick.
+ * @param reason The reason given by the user who performed the kick.
+ */
+ protected void onKick(String channel, String kickerNick, String kickerLogin, String kickerHostname, String recipientNick, String reason) {}
+
+
+ /**
+ * This method is called whenever someone (possibly us) quits from the
+ * server. We will only observe this if the user was in one of the
+ * channels to which we are connected.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param sourceNick The nick of the user that quit from the server.
+ * @param sourceLogin The login of the user that quit from the server.
+ * @param sourceHostname The hostname of the user that quit from the server.
+ * @param reason The reason given for quitting the server.
+ */
+ protected void onQuit(String sourceNick, String sourceLogin, String sourceHostname, String reason) {}
+
+
+ /**
+ * This method is called whenever a user sets the topic, or when
+ * PircBot joins a new channel and discovers its topic.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param channel The channel that the topic belongs to.
+ * @param topic The topic for the channel.
+ *
+ * @deprecated As of 1.2.0, replaced by {@link #onTopic(String,String,String,long,boolean)}
+ */
+ protected void onTopic(String channel, String topic) {}
+
+
+ /**
+ * This method is called whenever a user sets the topic, or when
+ * PircBot joins a new channel and discovers its topic.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param channel The channel that the topic belongs to.
+ * @param topic The topic for the channel.
+ * @param setBy The nick of the user that set the topic.
+ * @param date When the topic was set (milliseconds since the epoch).
+ * @param changed True if the topic has just been changed, false if
+ * the topic was already there.
+ *
+ */
+ protected void onTopic(String channel, String topic, String setBy, long date, boolean changed) {}
+
+
+ /**
+ * After calling the listChannels() method in PircBot, the server
+ * will start to send us information about each channel on the
+ * server. You may override this method in order to receive the
+ * information about each channel as soon as it is received.
+ * <p>
+ * Note that certain channels, such as those marked as hidden,
+ * may not appear in channel listings.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param channel The name of the channel.
+ * @param userCount The number of users visible in this channel.
+ * @param topic The topic for this channel.
+ *
+ * @see #listChannels() listChannels
+ */
+ protected void onChannelInfo(String channel, int userCount, String topic) {}
+
+
+ /**
+ * Called when the mode of a channel is set. We process this in
+ * order to call the appropriate onOp, onDeop, etc method before
+ * finally calling the override-able onMode method.
+ * <p>
+ * Note that this method is private and is not intended to appear
+ * in the javadoc generated documentation.
+ *
+ * @param target The channel or nick that the mode operation applies to.
+ * @param sourceNick The nick of the user that set the mode.
+ * @param sourceLogin The login of the user that set the mode.
+ * @param sourceHostname The hostname of the user that set the mode.
+ * @param mode The mode that has been set.
+ */
+ private final void processMode(String target, String sourceNick, String sourceLogin, String sourceHostname, String mode) {
+
+ if (_channelPrefixes.indexOf(target.charAt(0)) >= 0) {
+ // The mode of a channel is being changed.
+ String channel = target;
+ StringTokenizer tok = new StringTokenizer(mode);
+ String[] params = new String[tok.countTokens()];
+
+ int t = 0;
+ while (tok.hasMoreTokens()) {
+ params[t] = tok.nextToken();
+ t++;
+ }
+
+ char pn = ' ';
+ int p = 1;
+
+ // All of this is very large and ugly, but it's the only way of providing
+ // what the users want :-/
+ for (int i = 0; i < params[0].length(); i++) {
+ char atPos = params[0].charAt(i);
+
+ if (atPos == '+' || atPos == '-') {
+ pn = atPos;
+ }
+ else if (atPos == 'o') {
+ if (pn == '+') {
+ this.updateUser(channel, OP_ADD, params[p]);
+ onOp(channel, sourceNick, sourceLogin, sourceHostname, params[p]);
+ }
+ else {
+ this.updateUser(channel, OP_REMOVE, params[p]);
+ onDeop(channel, sourceNick, sourceLogin, sourceHostname, params[p]);
+ }
+ p++;
+ }
+ else if (atPos == 'v') {
+ if (pn == '+') {
+ this.updateUser(channel, VOICE_ADD, params[p]);
+ onVoice(channel, sourceNick, sourceLogin, sourceHostname, params[p]);
+ }
+ else {
+ this.updateUser(channel, VOICE_REMOVE, params[p]);
+ onDeVoice(channel, sourceNick, sourceLogin, sourceHostname, params[p]);
+ }
+ p++;
+ }
+ else if (atPos == 'k') {
+ if (pn == '+') {
+ onSetChannelKey(channel, sourceNick, sourceLogin, sourceHostname, params[p]);
+ }
+ else {
+ onRemoveChannelKey(channel, sourceNick, sourceLogin, sourceHostname, params[p]);
+ }
+ p++;
+ }
+ else if (atPos == 'l') {
+ if (pn == '+') {
+ onSetChannelLimit(channel, sourceNick, sourceLogin, sourceHostname, Integer.parseInt(params[p]));
+ p++;
+ }
+ else {
+ onRemoveChannelLimit(channel, sourceNick, sourceLogin, sourceHostname);
+ }
+ }
+ else if (atPos == 'b') {
+ if (pn == '+') {
+ onSetChannelBan(channel, sourceNick, sourceLogin, sourceHostname,params[p]);
+ }
+ else {
+ onRemoveChannelBan(channel, sourceNick, sourceLogin, sourceHostname, params[p]);
+ }
+ p++;
+ }
+ else if (atPos == 't') {
+ if (pn == '+') {
+ onSetTopicProtection(channel, sourceNick, sourceLogin, sourceHostname);
+ }
+ else {
+ onRemoveTopicProtection(channel, sourceNick, sourceLogin, sourceHostname);
+ }
+ }
+ else if (atPos == 'n') {
+ if (pn == '+') {
+ onSetNoExternalMessages(channel, sourceNick, sourceLogin, sourceHostname);
+ }
+ else {
+ onRemoveNoExternalMessages(channel, sourceNick, sourceLogin, sourceHostname);
+ }
+ }
+ else if (atPos == 'i') {
+ if (pn == '+') {
+ onSetInviteOnly(channel, sourceNick, sourceLogin, sourceHostname);
+ }
+ else {
+ onRemoveInviteOnly(channel, sourceNick, sourceLogin, sourceHostname);
+ }
+ }
+ else if (atPos == 'm') {
+ if (pn == '+') {
+ onSetModerated(channel, sourceNick, sourceLogin, sourceHostname);
+ }
+ else {
+ onRemoveModerated(channel, sourceNick, sourceLogin, sourceHostname);
+ }
+ }
+ else if (atPos == 'p') {
+ if (pn == '+') {
+ onSetPrivate(channel, sourceNick, sourceLogin, sourceHostname);
+ }
+ else {
+ onRemovePrivate(channel, sourceNick, sourceLogin, sourceHostname);
+ }
+ }
+ else if (atPos == 's') {
+ if (pn == '+') {
+ onSetSecret(channel, sourceNick, sourceLogin, sourceHostname);
+ }
+ else {
+ onRemoveSecret(channel, sourceNick, sourceLogin, sourceHostname);
+ }
+ }
+ }
+
+ this.onMode(channel, sourceNick, sourceLogin, sourceHostname, mode);
+ }
+ else {
+ // The mode of a user is being changed.
+ String nick = target;
+ this.onUserMode(nick, sourceNick, sourceLogin, sourceHostname, mode);
+ }
+ }
+
+
+ /**
+ * Called when the mode of a channel is set.
+ * <p>
+ * You may find it more convenient to decode the meaning of the mode
+ * string by overriding the onOp, onDeOp, onVoice, onDeVoice,
+ * onChannelKey, onDeChannelKey, onChannelLimit, onDeChannelLimit,
+ * onChannelBan or onDeChannelBan methods as appropriate.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param channel The channel that the mode operation applies to.
+ * @param sourceNick The nick of the user that set the mode.
+ * @param sourceLogin The login of the user that set the mode.
+ * @param sourceHostname The hostname of the user that set the mode.
+ * @param mode The mode that has been set.
+ *
+ */
+ protected void onMode(String channel, String sourceNick, String sourceLogin, String sourceHostname, String mode) {}
+
+
+ /**
+ * Called when the mode of a user is set.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 1.2.0
+ *
+ * @param targetNick The nick that the mode operation applies to.
+ * @param sourceNick The nick of the user that set the mode.
+ * @param sourceLogin The login of the user that set the mode.
+ * @param sourceHostname The hostname of the user that set the mode.
+ * @param mode The mode that has been set.
+ *
+ */
+ protected void onUserMode(String targetNick, String sourceNick, String sourceLogin, String sourceHostname, String mode) {}
+
+
+
+ /**
+ * Called when a user (possibly us) gets granted operator status for a channel.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ * @param recipient The nick of the user that got 'opped'.
+ */
+ protected void onOp(String channel, String sourceNick, String sourceLogin, String sourceHostname, String recipient) {}
+
+
+ /**
+ * Called when a user (possibly us) gets operator status taken away.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ * @param recipient The nick of the user that got 'deopped'.
+ */
+ protected void onDeop(String channel, String sourceNick, String sourceLogin, String sourceHostname, String recipient) {}
+
+
+ /**
+ * Called when a user (possibly us) gets voice status granted in a channel.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ * @param recipient The nick of the user that got 'voiced'.
+ */
+ protected void onVoice(String channel, String sourceNick, String sourceLogin, String sourceHostname, String recipient) {}
+
+
+ /**
+ * Called when a user (possibly us) gets voice status removed.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ * @param recipient The nick of the user that got 'devoiced'.
+ */
+ protected void onDeVoice(String channel, String sourceNick, String sourceLogin, String sourceHostname, String recipient) {}
+
+
+ /**
+ * Called when a channel key is set. When the channel key has been set,
+ * other users may only join that channel if they know the key. Channel keys
+ * are sometimes referred to as passwords.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ * @param key The new key for the channel.
+ */
+ protected void onSetChannelKey(String channel, String sourceNick, String sourceLogin, String sourceHostname, String key) {}
+
+
+ /**
+ * Called when a channel key is removed.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ * @param key The key that was in use before the channel key was removed.
+ */
+ protected void onRemoveChannelKey(String channel, String sourceNick, String sourceLogin, String sourceHostname, String key) {}
+
+
+ /**
+ * Called when a user limit is set for a channel. The number of users in
+ * the channel cannot exceed this limit.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ * @param limit The maximum number of users that may be in this channel at the same time.
+ */
+ protected void onSetChannelLimit(String channel, String sourceNick, String sourceLogin, String sourceHostname, int limit) {}
+
+
+ /**
+ * Called when the user limit is removed for a channel.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ */
+ protected void onRemoveChannelLimit(String channel, String sourceNick, String sourceLogin, String sourceHostname) {}
+
+
+ /**
+ * Called when a user (possibly us) gets banned from a channel. Being
+ * banned from a channel prevents any user with a matching hostmask from
+ * joining the channel. For this reason, most bans are usually directly
+ * followed by the user being kicked :-)
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ * @param hostmask The hostmask of the user that has been banned.
+ */
+ protected void onSetChannelBan(String channel, String sourceNick, String sourceLogin, String sourceHostname, String hostmask) {}
+
+
+ /**
+ * Called when a hostmask ban is removed from a channel.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ * @param hostmask
+ */
+ protected void onRemoveChannelBan(String channel, String sourceNick, String sourceLogin, String sourceHostname, String hostmask) {}
+
+
+ /**
+ * Called when topic protection is enabled for a channel. Topic protection
+ * means that only operators in a channel may change the topic.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ */
+ protected void onSetTopicProtection(String channel, String sourceNick, String sourceLogin, String sourceHostname) {}
+
+
+ /**
+ * Called when topic protection is removed for a channel.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ */
+ protected void onRemoveTopicProtection(String channel, String sourceNick, String sourceLogin, String sourceHostname) {}
+
+
+ /**
+ * Called when a channel is set to only allow messages from users that
+ * are in the channel.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ */
+ protected void onSetNoExternalMessages(String channel, String sourceNick, String sourceLogin, String sourceHostname) {}
+
+
+ /**
+ * Called when a channel is set to allow messages from any user, even
+ * if they are not actually in the channel.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ */
+ protected void onRemoveNoExternalMessages(String channel, String sourceNick, String sourceLogin, String sourceHostname) {}
+
+
+ /**
+ * Called when a channel is set to 'invite only' mode. A user may only
+ * join the channel if they are invited by someone who is already in the
+ * channel.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ */
+ protected void onSetInviteOnly(String channel, String sourceNick, String sourceLogin, String sourceHostname) {}
+
+
+ /**
+ * Called when a channel has 'invite only' removed.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ */
+ protected void onRemoveInviteOnly(String channel, String sourceNick, String sourceLogin, String sourceHostname) {}
+
+
+ /**
+ * Called when a channel is set to 'moderated' mode. If a channel is
+ * moderated, then only users who have been 'voiced' or 'opped' may speak
+ * or change their nicks.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ */
+ protected void onSetModerated(String channel, String sourceNick, String sourceLogin, String sourceHostname) {}
+
+
+ /**
+ * Called when a channel has moderated mode removed.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ */
+ protected void onRemoveModerated(String channel, String sourceNick, String sourceLogin, String sourceHostname) {}
+
+
+ /**
+ * Called when a channel is marked as being in private mode.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ */
+ protected void onSetPrivate(String channel, String sourceNick, String sourceLogin, String sourceHostname) {}
+
+
+ /**
+ * Called when a channel is marked as not being in private mode.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ */
+ protected void onRemovePrivate(String channel, String sourceNick, String sourceLogin, String sourceHostname) {}
+
+
+ /**
+ * Called when a channel is set to be in 'secret' mode. Such channels
+ * typically do not appear on a server's channel listing.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ */
+ protected void onSetSecret(String channel, String sourceNick, String sourceLogin, String sourceHostname) {}
+
+
+ /**
+ * Called when a channel has 'secret' mode removed.
+ * <p>
+ * This is a type of mode change and is also passed to the onMode
+ * method in the PircBot class.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param channel The channel in which the mode change took place.
+ * @param sourceNick The nick of the user that performed the mode change.
+ * @param sourceLogin The login of the user that performed the mode change.
+ * @param sourceHostname The hostname of the user that performed the mode change.
+ */
+ protected void onRemoveSecret(String channel, String sourceNick, String sourceLogin, String sourceHostname) {}
+
+
+ /**
+ * Called when we are invited to a channel by a user.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @since PircBot 0.9.5
+ *
+ * @param targetNick The nick of the user being invited - should be us!
+ * @param sourceNick The nick of the user that sent the invitation.
+ * @param sourceLogin The login of the user that sent the invitation.
+ * @param sourceHostname The hostname of the user that sent the invitation.
+ * @param channel The channel that we're being invited to.
+ */
+ protected void onInvite(String targetNick, String sourceNick, String sourceLogin, String sourceHostname, String channel) {}
+
+
+ /**
+ * This method used to be called when a DCC SEND request was sent to the PircBot.
+ * Please use the onIncomingFileTransfer method to receive files, as it
+ * has better functionality and supports resuming.
+ *
+ * @deprecated As of PircBot 1.2.0, use {@link #onIncomingFileTransfer(DccFileTransfer)}
+ */
+ protected void onDccSendRequest(String sourceNick, String sourceLogin, String sourceHostname, String filename, long address, int port, int size) {}
+
+
+ /**
+ * This method used to be called when a DCC CHAT request was sent to the PircBot.
+ * Please use the onIncomingChatRequest method to accept chats, as it
+ * has better functionality.
+ *
+ * @deprecated As of PircBot 1.2.0, use {@link #onIncomingChatRequest(DccChat)}
+ */
+ protected void onDccChatRequest(String sourceNick, String sourceLogin, String sourceHostname, long address, int port) {}
+
+
+ /**
+ * This method is called whenever we receive a VERSION request.
+ * This abstract implementation responds with the PircBot's _version string,
+ * so if you override this method, be sure to either mimic its functionality
+ * or to call super.onVersion(...);
+ *
+ * @param sourceNick The nick of the user that sent the VERSION request.
+ * @param sourceLogin The login of the user that sent the VERSION request.
+ * @param sourceHostname The hostname of the user that sent the VERSION request.
+ * @param target The target of the VERSION request, be it our nick or a channel name.
+ */
+ protected void onVersion(String sourceNick, String sourceLogin, String sourceHostname, String target) {
+ this.sendRawLine("NOTICE " + sourceNick + " :\u0001VERSION " + _version + "\u0001");
+ }
+
+
+ /**
+ * This method is called whenever we receive a PING request from another
+ * user.
+ * <p>
+ * This abstract implementation responds correctly, so if you override this
+ * method, be sure to either mimic its functionality or to call
+ * super.onPing(...);
+ *
+ * @param sourceNick The nick of the user that sent the PING request.
+ * @param sourceLogin The login of the user that sent the PING request.
+ * @param sourceHostname The hostname of the user that sent the PING request.
+ * @param target The target of the PING request, be it our nick or a channel name.
+ * @param pingValue The value that was supplied as an argument to the PING command.
+ */
+ protected void onPing(String sourceNick, String sourceLogin, String sourceHostname, String target, String pingValue) {
+ this.sendRawLine("NOTICE " + sourceNick + " :\u0001PING " + pingValue + "\u0001");
+ }
+
+
+ /**
+ * The actions to perform when a PING request comes from the server.
+ * <p>
+ * This sends back a correct response, so if you override this method,
+ * be sure to either mimic its functionality or to call
+ * super.onServerPing(response);
+ *
+ * @param response The response that should be given back in your PONG.
+ */
+ protected void onServerPing(String response) {
+ this.sendRawLine("PONG " + response);
+ }
+
+
+ /**
+ * This method is called whenever we receive a TIME request.
+ * <p>
+ * This abstract implementation responds correctly, so if you override this
+ * method, be sure to either mimic its functionality or to call
+ * super.onTime(...);
+ *
+ * @param sourceNick The nick of the user that sent the TIME request.
+ * @param sourceLogin The login of the user that sent the TIME request.
+ * @param sourceHostname The hostname of the user that sent the TIME request.
+ * @param target The target of the TIME request, be it our nick or a channel name.
+ */
+ protected void onTime(String sourceNick, String sourceLogin, String sourceHostname, String target) {
+ this.sendRawLine("NOTICE " + sourceNick + " :\u0001TIME " + new Date().toString() + "\u0001");
+ }
+
+
+ /**
+ * This method is called whenever we receive a FINGER request.
+ * <p>
+ * This abstract implementation responds correctly, so if you override this
+ * method, be sure to either mimic its functionality or to call
+ * super.onFinger(...);
+ *
+ * @param sourceNick The nick of the user that sent the FINGER request.
+ * @param sourceLogin The login of the user that sent the FINGER request.
+ * @param sourceHostname The hostname of the user that sent the FINGER request.
+ * @param target The target of the FINGER request, be it our nick or a channel name.
+ */
+ protected void onFinger(String sourceNick, String sourceLogin, String sourceHostname, String target) {
+ this.sendRawLine("NOTICE " + sourceNick + " :\u0001FINGER " + _finger + "\u0001");
+ }
+
+
+ /**
+ * This method is called whenever we receive a line from the server that
+ * the PircBot has not been programmed to recognise.
+ * <p>
+ * The implementation of this method in the PircBot abstract class
+ * performs no actions and may be overridden as required.
+ *
+ * @param line The raw line that was received from the server.
+ */
+ protected void onUnknown(String line) {
+ // And then there were none :)
+ }
+
+
+ /**
+ * Sets the verbose mode. If verbose mode is set to true, then log entries
+ * will be printed to the standard output. The default value is false and
+ * will result in no output. For general development, we strongly recommend
+ * setting the verbose mode to true.
+ *
+ * @param verbose true if verbose mode is to be used. Default is false.
+ */
+ public final void setVerbose(boolean verbose) {
+ _verbose = verbose;
+ }
+
+
+ /**
+ * Sets the name of the bot, which will be used as its nick when it
+ * tries to join an IRC server. This should be set before joining
+ * any servers, otherwise the default nick will be used. You would
+ * typically call this method from the constructor of the class that
+ * extends PircBot.
+ * <p>
+ * The changeNick method should be used if you wish to change your nick
+ * when you are connected to a server.
+ *
+ * @param name The new name of the Bot.
+ */
+ protected final void setName(String name) {
+ _name = name;
+ }
+
+
+ /**
+ * Sets the internal nick of the bot. This is only to be called by the
+ * PircBot class in response to notification of nick changes that apply
+ * to us.
+ *
+ * @param nick The new nick.
+ */
+ private final void setNick(String nick) {
+ _nick = nick;
+ }
+
+
+ /**
+ * Sets the internal login of the Bot. This should be set before joining
+ * any servers.
+ *
+ * @param login The new login of the Bot.
+ */
+ protected final void setLogin(String login) {
+ _login = login;
+ }
+
+
+ /**
+ * Sets the internal version of the Bot. This should be set before joining
+ * any servers.
+ *
+ * @param version The new version of the Bot.
+ */
+ protected final void setVersion(String version) {
+ _version = version;
+ }
+
+
+ /**
+ * Sets the interal finger message. This should be set before joining
+ * any servers.
+ *
+ * @param finger The new finger message for the Bot.
+ */
+ protected final void setFinger(String finger) {
+ _finger = finger;
+ }
+
+
+ /**
+ * Gets the name of the PircBot. This is the name that will be used as
+ * as a nick when we try to join servers.
+ *
+ * @return The name of the PircBot.
+ */
+ public final String getName() {
+ return _name;
+ }
+
+
+ /**
+ * Returns the current nick of the bot. Note that if you have just changed
+ * your nick, this method will still return the old nick until confirmation
+ * of the nick change is received from the server.
+ * <p>
+ * The nick returned by this method is maintained only by the PircBot
+ * class and is guaranteed to be correct in the context of the IRC server.
+ *
+ * @since PircBot 1.0.0
+ *
+ * @return The current nick of the bot.
+ */
+ public String getNick() {
+ return _nick;
+ }
+
+
+ /**
+ * Gets the internal login of the PircBot.
+ *
+ * @return The login of the PircBot.
+ */
+ public final String getLogin() {
+ return _login;
+ }
+
+
+ /**
+ * Gets the internal version of the PircBot.
+ *
+ * @return The version of the PircBot.
+ */
+ public final String getVersion() {
+ return _version;
+ }
+
+
+ /**
+ * Gets the internal finger message of the PircBot.
+ *
+ * @return The finger message of the PircBot.
+ */
+ public final String getFinger() {
+ return _finger;
+ }
+
+
+ /**
+ * Returns whether or not the PircBot is currently connected to a server.
+ * The result of this method should only act as a rough guide,
+ * as the result may not be valid by the time you act upon it.
+ *
+ * @return True if and only if the PircBot is currently connected to a server.
+ */
+ public final synchronized boolean isConnected() {
+ return _inputThread != null && _inputThread.isConnected();
+ }
+
+
+ /**
+ * Sets the number of milliseconds to delay between consecutive
+ * messages when there are multiple messages waiting in the
+ * outgoing message queue. This has a default value of 1000ms.
+ * It is a good idea to stick to this default value, as it will
+ * prevent your bot from spamming servers and facing the subsequent
+ * wrath! However, if you do need to change this delay value (<b>not
+ * recommended</b>), then this is the method to use.
+ *
+ * @param delay The number of milliseconds between each outgoing message.
+ *
+ */
+ public final void setMessageDelay(long delay) {
+ if (delay < 0) {
+ throw new IllegalArgumentException("Cannot have a negative time.");
+ }
+ _messageDelay = delay;
+ }
+
+
+ /**
+ * Returns the number of milliseconds that will be used to separate
+ * consecutive messages to the server from the outgoing message queue.
+ *
+ * @return Number of milliseconds.
+ */
+ public final long getMessageDelay() {
+ return _messageDelay;
+ }
+
+
+ /**
+ * Gets the maximum length of any line that is sent via the IRC protocol.
+ * The IRC RFC specifies that line lengths, including the trailing \r\n
+ * must not exceed 512 bytes. Hence, there is currently no option to
+ * change this value in PircBot. All lines greater than this length
+ * will be truncated before being sent to the IRC server.
+ *
+ * @return The maximum line length (currently fixed at 512)
+ */
+ public final int getMaxLineLength() {
+ return InputThread.MAX_LINE_LENGTH;
+ }
+
+
+ /**
+ * Gets the number of lines currently waiting in the outgoing message Queue.
+ * If this returns 0, then the Queue is empty and any new message is likely
+ * to be sent to the IRC server immediately.
+ *
+ * @since PircBot 0.9.9
+ *
+ * @return The number of lines in the outgoing message Queue.
+ */
+ public final int getOutgoingQueueSize() {
+ return _outQueue.size();
+ }
+
+
+ /**
+ * Returns the name of the last IRC server the PircBot tried to connect to.
+ * This does not imply that the connection attempt to the server was
+ * successful (we suggest you look at the onConnect method).
+ * A value of null is returned if the PircBot has never tried to connect
+ * to a server.
+ *
+ * @return The name of the last machine we tried to connect to. Returns
+ * null if no connection attempts have ever been made.
+ */
+ public final String getServer() {
+ return _server;
+ }
+
+
+ /**
+ * Returns the port number of the last IRC server that the PircBot tried
+ * to connect to.
+ * This does not imply that the connection attempt to the server was
+ * successful (we suggest you look at the onConnect method).
+ * A value of -1 is returned if the PircBot has never tried to connect
+ * to a server.
+ *
+ * @since PircBot 0.9.9
+ *
+ * @return The port number of the last IRC server we connected to.
+ * Returns -1 if no connection attempts have ever been made.
+ */
+ public final int getPort() {
+ return _port;
+ }
+
+
+ /**
+ * Returns the last password that we used when connecting to an IRC server.
+ * This does not imply that the connection attempt to the server was
+ * successful (we suggest you look at the onConnect method).
+ * A value of null is returned if the PircBot has never tried to connect
+ * to a server using a password.
+ *
+ * @since PircBot 0.9.9
+ *
+ * @return The last password that we used when connecting to an IRC server.
+ * Returns null if we have not previously connected using a password.
+ */
+ public final String getPassword() {
+ return _password;
+ }
+
+
+ /**
+ * A convenient method that accepts an IP address represented as a
+ * long and returns an integer array of size 4 representing the same
+ * IP address.
+ *
+ * @since PircBot 0.9.4
+ *
+ * @param address the long value representing the IP address.
+ *
+ * @return An int[] of size 4.
+ */
+ public int[] longToIp(long address) {
+ int[] ip = new int[4];
+ for (int i = 3; i >= 0; i--) {
+ ip[i] = (int) (address % 256);
+ address = address / 256;
+ }
+ return ip;
+ }
+
+
+ /**
+ * A convenient method that accepts an IP address represented by a byte[]
+ * of size 4 and returns this as a long representation of the same IP
+ * address.
+ *
+ * @since PircBot 0.9.4
+ *
+ * @param address the byte[] of size 4 representing the IP address.
+ *
+ * @return a long representation of the IP address.
+ */
+ public long ipToLong(byte[] address) {
+ if (address.length != 4) {
+ throw new IllegalArgumentException("byte array must be of length 4");
+ }
+ long ipNum = 0;
+ long multiplier = 1;
+ for (int i = 3; i >= 0; i--) {
+ int byteVal = (address[i] + 256) % 256;
+ ipNum += byteVal*multiplier;
+ multiplier *= 256;
+ }
+ return ipNum;
+ }
+
+
+ /**
+ * Sets the encoding charset to be used when sending or receiving lines
+ * from the IRC server. If set to null, then the platform's default
+ * charset is used. You should only use this method if you are
+ * trying to send text to an IRC server in a different charset, e.g.
+ * "GB2312" for Chinese encoding. If a PircBot is currently connected
+ * to a server, then it must reconnect before this change takes effect.
+ *
+ * @since PircBot 1.0.4
+ *
+ * @param charset The new encoding charset to be used by PircBot.
+ *
+ * @throws UnsupportedEncodingException If the named charset is not
+ * supported.
+ */
+ public void setEncoding(String charset) throws UnsupportedEncodingException {
+ // Just try to see if the charset is supported first...
+ "".getBytes(charset);
+
+ _charset = charset;
+ }
+
+
+ /**
+ * Returns the encoding used to send and receive lines from
+ * the IRC server, or null if not set. Use the setEncoding
+ * method to change the encoding charset.
+ *
+ * @since PircBot 1.0.4
+ *
+ * @return The encoding used to send outgoing messages, or
+ * null if not set.
+ */
+ public String getEncoding() {
+ return _charset;
+ }
+
+ /**
+ * Returns the InetAddress used by the PircBot.
+ * This can be used to find the I.P. address from which the PircBot is
+ * connected to a server.
+ *
+ * @since PircBot 1.4.4
+ *
+ * @return The current local InetAddress, or null if never connected.
+ */
+ public InetAddress getInetAddress() {
+ return _inetAddress;
+ }
+
+
+ /**
+ * Sets the InetAddress to be used when sending DCC chat or file transfers.
+ * This can be very useful when you are running a bot on a machine which
+ * is behind a firewall and you need to tell receiving clients to connect
+ * to a NAT/router, which then forwards the connection.
+ *
+ * @since PircBot 1.4.4
+ *
+ * @param dccInetAddress The new InetAddress, or null to use the default.
+ */
+ public void setDccInetAddress(InetAddress dccInetAddress) {
+ _dccInetAddress = dccInetAddress;
+ }
+
+
+ /**
+ * Returns the InetAddress used when sending DCC chat or file transfers.
+ * If this is null, the default InetAddress will be used.
+ *
+ * @since PircBot 1.4.4
+ *
+ * @return The current DCC InetAddress, or null if left as default.
+ */
+ public InetAddress getDccInetAddress() {
+ return _dccInetAddress;
+ }
+
+
+ /**
+ * Returns the set of port numbers to be used when sending a DCC chat
+ * or file transfer. This is useful when you are behind a firewall and
+ * need to set up port forwarding. The array of port numbers is traversed
+ * in sequence until a free port is found to listen on. A DCC tranfer will
+ * fail if all ports are already in use.
+ * If set to null, <i>any</i> free port number will be used.
+ *
+ * @since PircBot 1.4.4
+ *
+ * @return An array of port numbers that PircBot can use to send DCC
+ * transfers, or null if any port is allowed.
+ */
+ public int[] getDccPorts() {
+ if (_dccPorts == null || _dccPorts.length == 0) {
+ return null;
+ }
+ // Clone the array to prevent external modification.
+ return (int[]) _dccPorts.clone();
+ }
+
+
+ /**
+ * Sets the choice of port numbers that can be used when sending a DCC chat
+ * or file transfer. This is useful when you are behind a firewall and
+ * need to set up port forwarding. The array of port numbers is traversed
+ * in sequence until a free port is found to listen on. A DCC tranfer will
+ * fail if all ports are already in use.
+ * If set to null, <i>any</i> free port number will be used.
+ *
+ * @since PircBot 1.4.4
+ *
+ * @param ports The set of port numbers that PircBot may use for DCC
+ * transfers, or null to let it use any free port (default).
+ *
+ */
+ public void setDccPorts(int[] ports) {
+ if (ports == null || ports.length == 0) {
+ _dccPorts = null;
+ }
+ else {
+ // Clone the array to prevent external modification.
+ _dccPorts = (int[]) ports.clone();
+ }
+ }
+
+
+ /**
+ * Returns true if and only if the object being compared is the exact
+ * same instance as this PircBot. This may be useful if you are writing
+ * a multiple server IRC bot that uses more than one instance of PircBot.
+ *
+ * @since PircBot 0.9.9
+ *
+ * @return true if and only if Object o is a PircBot and equal to this.
+ */
+ public boolean equals(Object o) {
+ // This probably has the same effect as Object.equals, but that may change...
+ if (o instanceof PircBot) {
+ PircBot other = (PircBot) o;
+ return other == this;
+ }
+ return false;
+ }
+
+
+ /**
+ * Returns the hashCode of this PircBot. This method can be called by hashed
+ * collection classes and is useful for managing multiple instances of
+ * PircBots in such collections.
+ *
+ * @since PircBot 0.9.9
+ *
+ * @return the hash code for this instance of PircBot.
+ */
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+
+ /**
+ * Returns a String representation of this object.
+ * You may find this useful for debugging purposes, particularly
+ * if you are using more than one PircBot instance to achieve
+ * multiple server connectivity. The format of
+ * this String may change between different versions of PircBot
+ * but is currently something of the form
+ * <code>
+ * Version{PircBot x.y.z Java IRC Bot - www.jibble.org}
+ * Connected{true}
+ * Server{irc.dal.net}
+ * Port{6667}
+ * Password{}
+ * </code>
+ *
+ * @since PircBot 0.9.10
+ *
+ * @return a String representation of this object.
+ */
+ public String toString() {
+ return "Version{" + _version + "}" +
+ " Connected{" + isConnected() + "}" +
+ " Server{" + _server + "}" +
+ " Port{" + _port + "}" +
+ " Password{" + _password + "}";
+ }
+
+
+ /**
+ * Returns an array of all users in the specified channel.
+ * <p>
+ * There are some important things to note about this method:-
+ * <ul>
+ * <li>This method may not return a full list of users if you call it
+ * before the complete nick list has arrived from the IRC server.
+ * </li>
+ * <li>If you wish to find out which users are in a channel as soon
+ * as you join it, then you should override the onUserList method
+ * instead of calling this method, as the onUserList method is only
+ * called as soon as the full user list has been received.
+ * </li>
+ * <li>This method will return immediately, as it does not require any
+ * interaction with the IRC server.
+ * </li>
+ * <li>The bot must be in a channel to be able to know which users are
+ * in it.
+ * </li>
+ * </ul>
+ *
+ * @since PircBot 1.0.0
+ *
+ * @param channel The name of the channel to list.
+ *
+ * @return An array of User objects. This array is empty if we are not
+ * in the channel.
+ *
+ * @see #onUserList(String,User[]) onUserList
+ */
+ public final User[] getUsers(String channel) {
+ channel = channel.toLowerCase();
+ User[] userArray = new User[0];
+ synchronized (_channels) {
+ Hashtable users = (Hashtable) _channels.get(channel);
+ if (users != null) {
+ userArray = new User[users.size()];
+ Enumeration enumeration = users.elements();
+ for (int i = 0; i < userArray.length; i++) {
+ User user = (User) enumeration.nextElement();
+ userArray[i] = user;
+ }
+ }
+ }
+ return userArray;
+ }
+
+
+ /**
+ * Returns an array of all channels that we are in. Note that if you
+ * call this method immediately after joining a new channel, the new
+ * channel may not appear in this array as it is not possible to tell
+ * if the join was successful until a response is received from the
+ * IRC server.
+ *
+ * @since PircBot 1.0.0
+ *
+ * @return A String array containing the names of all channels that we
+ * are in.
+ */
+ public final String[] getChannels() {
+ String[] channels = new String[0];
+ synchronized (_channels) {
+ channels = new String[_channels.size()];
+ Enumeration enumeration = _channels.keys();
+ for (int i = 0; i < channels.length; i++) {
+ channels[i] = (String) enumeration.nextElement();
+ }
+ }
+ return channels;
+ }
+
+
+ /**
+ * Disposes of all thread resources used by this PircBot. This may be
+ * useful when writing bots or clients that use multiple servers (and
+ * therefore multiple PircBot instances) or when integrating a PircBot
+ * with an existing program.
+ * <p>
+ * Each PircBot runs its own threads for dispatching messages from its
+ * outgoing message queue and receiving messages from the server.
+ * Calling dispose() ensures that these threads are
+ * stopped, thus freeing up system resources and allowing the PircBot
+ * object to be garbage collected if there are no other references to
+ * it.
+ * <p>
+ * Once a PircBot object has been disposed, it should not be used again.
+ * Attempting to use a PircBot that has been disposed may result in
+ * unpredictable behaviour.
+ *
+ * @since 1.2.2
+ */
+ public synchronized void dispose() {
+ //System.out.println("disposing...");
+ _outputThread.interrupt();
+ _inputThread.dispose();
+ }
+
+
+ /**
+ * Add a user to the specified channel in our memory.
+ * Overwrite the existing entry if it exists.
+ */
+ private final void addUser(String channel, User user) {
+ channel = channel.toLowerCase();
+ synchronized (_channels) {
+ Hashtable users = (Hashtable) _channels.get(channel);
+ if (users == null) {
+ users = new Hashtable();
+ _channels.put(channel, users);
+ }
+ users.put(user, user);
+ }
+ }
+
+
+ /**
+ * Remove a user from the specified channel in our memory.
+ */
+ private final User removeUser(String channel, String nick) {
+ channel = channel.toLowerCase();
+ User user = new User("", nick);
+ synchronized (_channels) {
+ Hashtable users = (Hashtable) _channels.get(channel);
+ if (users != null) {
+ return (User) users.remove(user);
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Remove a user from all channels in our memory.
+ */
+ private final void removeUser(String nick) {
+ synchronized (_channels) {
+ Enumeration enumeration = _channels.keys();
+ while (enumeration.hasMoreElements()) {
+ String channel = (String) enumeration.nextElement();
+ this.removeUser(channel, nick);
+ }
+ }
+ }
+
+
+ /**
+ * Rename a user if they appear in any of the channels we know about.
+ */
+ private final void renameUser(String oldNick, String newNick) {
+ synchronized (_channels) {
+ Enumeration enumeration = _channels.keys();
+ while (enumeration.hasMoreElements()) {
+ String channel = (String) enumeration.nextElement();
+ User user = this.removeUser(channel, oldNick);
+ if (user != null) {
+ user = new User(user.getPrefix(), newNick);
+ this.addUser(channel, user);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Removes an entire channel from our memory of users.
+ */
+ private final void removeChannel(String channel) {
+ channel = channel.toLowerCase();
+ synchronized (_channels) {
+ _channels.remove(channel);
+ }
+ }
+
+
+ /**
+ * Removes all channels from our memory of users.
+ */
+ private final void removeAllChannels() {
+ synchronized(_channels) {
+ _channels = new Hashtable();
+ }
+ }
+
+
+ private final void updateUser(String channel, int userMode, String nick) {
+ channel = channel.toLowerCase();
+ synchronized (_channels) {
+ Hashtable users = (Hashtable) _channels.get(channel);
+ User newUser = null;
+ if (users != null) {
+ Enumeration enumeration = users.elements();
+ while(enumeration.hasMoreElements()) {
+ User userObj = (User) enumeration.nextElement();
+ if (userObj.getNick().equalsIgnoreCase(nick)) {
+ if (userMode == OP_ADD) {
+ if (userObj.hasVoice()) {
+ newUser = new User("@+", nick);
+ }
+ else {
+ newUser = new User("@", nick);
+ }
+ }
+ else if (userMode == OP_REMOVE) {
+ if(userObj.hasVoice()) {
+ newUser = new User("+", nick);
+ }
+ else {
+ newUser = new User("", nick);
+ }
+ }
+ else if (userMode == VOICE_ADD) {
+ if(userObj.isOp()) {
+ newUser = new User("@+", nick);
+ }
+ else {
+ newUser = new User("+", nick);
+ }
+ }
+ else if (userMode == VOICE_REMOVE) {
+ if(userObj.isOp()) {
+ newUser = new User("@", nick);
+ }
+ else {
+ newUser = new User("", nick);
+ }
+ }
+ }
+ }
+ }
+ if (newUser != null) {
+ users.put(newUser, newUser);
+ }
+ else {
+ // just in case ...
+ newUser = new User("", nick);
+ users.put(newUser, newUser);
+ }
+ }
+ }
+
+
+ // Connection stuff.
+ private InputThread _inputThread = null;
+ private OutputThread _outputThread = null;
+ private String _charset = null;
+ private InetAddress _inetAddress = null;
+
+ // Details about the last server that we connected to.
+ private String _server = null;
+ private int _port = -1;
+ private String _password = null;
+
+ // Outgoing message stuff.
+ private Queue _outQueue = new Queue();
+ private long _messageDelay = 1000;
+
+ // A Hashtable of channels that points to a selfreferential Hashtable of
+ // User objects (used to remember which users are in which channels).
+ private Hashtable _channels = new Hashtable();
+
+ // A Hashtable to temporarily store channel topics when we join them
+ // until we find out who set that topic.
+ private Hashtable _topics = new Hashtable();
+
+ private int[] _dccPorts = null;
+ private InetAddress _dccInetAddress = null;
+
+ // Default settings for the PircBot.
+ private boolean _autoNickChange = false;
+ private boolean _verbose = false;
+ private String _name = "PircBot";
+ private String _nick = _name;
+ private String _login = "PircBot";
+ private String _version = "PircBot " + VERSION + " Java IRC Bot - www.jibble.org";
+ private String _finger = "You ought to be arrested for fingering a bot!";
+
+ private String _channelPrefixes = "#&+!";
+}
diff --git a/EssentialsUpdate/src/org/jibble/pircbot/Queue.java b/EssentialsUpdate/src/org/jibble/pircbot/Queue.java
new file mode 100755
index 000000000..fd8d47781
--- /dev/null
+++ b/EssentialsUpdate/src/org/jibble/pircbot/Queue.java
@@ -0,0 +1,146 @@
+/*
+Copyright Paul James Mutton, 2001-2009, http://www.jibble.org/
+
+This file is part of PircBot.
+
+This software is dual-licensed, allowing you to choose between the GNU
+General Public License (GPL) and the www.jibble.org Commercial License.
+Since the GPL may be too restrictive for use in a proprietary application,
+a commercial license is also provided. Full license information can be
+found at http://www.jibble.org/licenses/
+
+*/
+
+
+package org.jibble.pircbot;
+
+import java.util.Vector;
+
+/**
+ * Queue is a definition of a data structure that may
+ * act as a queue - that is, data can be added to one end of the
+ * queue and data can be requested from the head end of the queue.
+ * This class is thread safe for multiple producers and a single
+ * consumer. The next() method will block until there is data in
+ * the queue.
+ *
+ * This has now been modified so that it is compatible with
+ * the earlier JDK1.1 in order to be suitable for running on
+ * mobile appliances. This means replacing the LinkedList with
+ * a Vector, which is hardly ideal, but this Queue is typically
+ * only polled every second before dispatching messages.
+ *
+ * @author Paul James Mutton,
+ * <a href="http://www.jibble.org/">http://www.jibble.org/</a>
+ * @version 1.5.0 (Build time: Mon Dec 14 20:07:17 2009)
+ */
+public class Queue {
+
+
+ /**
+ * Constructs a Queue object of unlimited size.
+ */
+ public Queue() {
+
+ }
+
+
+ /**
+ * Adds an Object to the end of the Queue.
+ *
+ * @param o The Object to be added to the Queue.
+ */
+ public void add(Object o) {
+ synchronized(_queue) {
+ _queue.addElement(o);
+ _queue.notify();
+ }
+ }
+
+
+ /**
+ * Adds an Object to the front of the Queue.
+ *
+ * @param o The Object to be added to the Queue.
+ */
+ public void addFront(Object o) {
+ synchronized(_queue) {
+ _queue.insertElementAt(o, 0);
+ _queue.notify();
+ }
+ }
+
+
+ /**
+ * Returns the Object at the front of the Queue. This
+ * Object is then removed from the Queue. If the Queue
+ * is empty, then this method shall block until there
+ * is an Object in the Queue to return.
+ *
+ * @return The next item from the front of the queue.
+ */
+ public Object next() {
+
+ Object o = null;
+
+ // Block if the Queue is empty.
+ synchronized(_queue) {
+ if (_queue.size() == 0) {
+ try {
+ _queue.wait();
+ }
+ catch (InterruptedException e) {
+ return null;
+ }
+ }
+
+ // Return the Object.
+ try {
+ o = _queue.firstElement();
+ _queue.removeElementAt(0);
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ throw new InternalError("Race hazard in Queue object.");
+ }
+ }
+
+ return o;
+ }
+
+
+ /**
+ * Returns true if the Queue is not empty. If another
+ * Thread empties the Queue before <b>next()</b> is
+ * called, then the call to <b>next()</b> shall block
+ * until the Queue has been populated again.
+ *
+ * @return True only if the Queue not empty.
+ */
+ public boolean hasNext() {
+ return (this.size() != 0);
+ }
+
+
+ /**
+ * Clears the contents of the Queue.
+ */
+ public void clear() {
+ synchronized(_queue) {
+ _queue.removeAllElements();
+ }
+ }
+
+
+ /**
+ * Returns the size of the Queue.
+ *
+ * @return The current size of the queue.
+ */
+ public int size() {
+ return _queue.size();
+ }
+
+
+ private Vector _queue = new Vector();
+
+}
diff --git a/EssentialsUpdate/src/org/jibble/pircbot/ReplyConstants.java b/EssentialsUpdate/src/org/jibble/pircbot/ReplyConstants.java
new file mode 100755
index 000000000..2d8e696d5
--- /dev/null
+++ b/EssentialsUpdate/src/org/jibble/pircbot/ReplyConstants.java
@@ -0,0 +1,176 @@
+/*
+Copyright Paul James Mutton, 2001-2009, http://www.jibble.org/
+
+This file is part of PircBot.
+
+This software is dual-licensed, allowing you to choose between the GNU
+General Public License (GPL) and the www.jibble.org Commercial License.
+Since the GPL may be too restrictive for use in a proprietary application,
+a commercial license is also provided. Full license information can be
+found at http://www.jibble.org/licenses/
+
+*/
+
+
+package org.jibble.pircbot;
+
+/**
+ * This interface contains the values of all numeric replies specified
+ * in section 6 of RFC 1459. Refer to RFC 1459 for further information.
+ * <p>
+ * If you override the onServerResponse method in the PircBot class,
+ * you may find these constants useful when comparing the numeric
+ * value of a given code.
+ *
+ * @since 1.0.0
+ * @author Paul James Mutton,
+ * <a href="http://www.jibble.org/">http://www.jibble.org/</a>
+ * @version 1.5.0 (Build time: Mon Dec 14 20:07:17 2009)
+ */
+public interface ReplyConstants {
+
+
+ // Error Replies.
+ public static final int ERR_NOSUCHNICK = 401;
+ public static final int ERR_NOSUCHSERVER = 402;
+ public static final int ERR_NOSUCHCHANNEL = 403;
+ public static final int ERR_CANNOTSENDTOCHAN = 404;
+ public static final int ERR_TOOMANYCHANNELS = 405;
+ public static final int ERR_WASNOSUCHNICK = 406;
+ public static final int ERR_TOOMANYTARGETS = 407;
+ public static final int ERR_NOORIGIN = 409;
+ public static final int ERR_NORECIPIENT = 411;
+ public static final int ERR_NOTEXTTOSEND = 412;
+ public static final int ERR_NOTOPLEVEL = 413;
+ public static final int ERR_WILDTOPLEVEL = 414;
+ public static final int ERR_UNKNOWNCOMMAND = 421;
+ public static final int ERR_NOMOTD = 422;
+ public static final int ERR_NOADMININFO = 423;
+ public static final int ERR_FILEERROR = 424;
+ public static final int ERR_NONICKNAMEGIVEN = 431;
+ public static final int ERR_ERRONEUSNICKNAME = 432;
+ public static final int ERR_NICKNAMEINUSE = 433;
+ public static final int ERR_NICKCOLLISION = 436;
+ public static final int ERR_USERNOTINCHANNEL = 441;
+ public static final int ERR_NOTONCHANNEL = 442;
+ public static final int ERR_USERONCHANNEL = 443;
+ public static final int ERR_NOLOGIN = 444;
+ public static final int ERR_SUMMONDISABLED = 445;
+ public static final int ERR_USERSDISABLED = 446;
+ public static final int ERR_NOTREGISTERED = 451;
+ public static final int ERR_NEEDMOREPARAMS = 461;
+ public static final int ERR_ALREADYREGISTRED = 462;
+ public static final int ERR_NOPERMFORHOST = 463;
+ public static final int ERR_PASSWDMISMATCH = 464;
+ public static final int ERR_YOUREBANNEDCREEP = 465;
+ public static final int ERR_KEYSET = 467;
+ public static final int ERR_CHANNELISFULL = 471;
+ public static final int ERR_UNKNOWNMODE = 472;
+ public static final int ERR_INVITEONLYCHAN = 473;
+ public static final int ERR_BANNEDFROMCHAN = 474;
+ public static final int ERR_BADCHANNELKEY = 475;
+ public static final int ERR_NOPRIVILEGES = 481;
+ public static final int ERR_CHANOPRIVSNEEDED = 482;
+ public static final int ERR_CANTKILLSERVER = 483;
+ public static final int ERR_NOOPERHOST = 491;
+ public static final int ERR_UMODEUNKNOWNFLAG = 501;
+ public static final int ERR_USERSDONTMATCH = 502;
+
+
+ // Command Responses.
+ public static final int RPL_TRACELINK = 200;
+ public static final int RPL_TRACECONNECTING = 201;
+ public static final int RPL_TRACEHANDSHAKE = 202;
+ public static final int RPL_TRACEUNKNOWN = 203;
+ public static final int RPL_TRACEOPERATOR = 204;
+ public static final int RPL_TRACEUSER = 205;
+ public static final int RPL_TRACESERVER = 206;
+ public static final int RPL_TRACENEWTYPE = 208;
+ public static final int RPL_STATSLINKINFO = 211;
+ public static final int RPL_STATSCOMMANDS = 212;
+ public static final int RPL_STATSCLINE = 213;
+ public static final int RPL_STATSNLINE = 214;
+ public static final int RPL_STATSILINE = 215;
+ public static final int RPL_STATSKLINE = 216;
+ public static final int RPL_STATSYLINE = 218;
+ public static final int RPL_ENDOFSTATS = 219;
+ public static final int RPL_UMODEIS = 221;
+ public static final int RPL_STATSLLINE = 241;
+ public static final int RPL_STATSUPTIME = 242;
+ public static final int RPL_STATSOLINE = 243;
+ public static final int RPL_STATSHLINE = 244;
+ public static final int RPL_LUSERCLIENT = 251;
+ public static final int RPL_LUSEROP = 252;
+ public static final int RPL_LUSERUNKNOWN = 253;
+ public static final int RPL_LUSERCHANNELS = 254;
+ public static final int RPL_LUSERME = 255;
+ public static final int RPL_ADMINME = 256;
+ public static final int RPL_ADMINLOC1 = 257;
+ public static final int RPL_ADMINLOC2 = 258;
+ public static final int RPL_ADMINEMAIL = 259;
+ public static final int RPL_TRACELOG = 261;
+ public static final int RPL_NONE = 300;
+ public static final int RPL_AWAY = 301;
+ public static final int RPL_USERHOST = 302;
+ public static final int RPL_ISON = 303;
+ public static final int RPL_UNAWAY = 305;
+ public static final int RPL_NOWAWAY = 306;
+ public static final int RPL_WHOISUSER = 311;
+ public static final int RPL_WHOISSERVER = 312;
+ public static final int RPL_WHOISOPERATOR = 313;
+ public static final int RPL_WHOWASUSER = 314;
+ public static final int RPL_ENDOFWHO = 315;
+ public static final int RPL_WHOISIDLE = 317;
+ public static final int RPL_ENDOFWHOIS = 318;
+ public static final int RPL_WHOISCHANNELS = 319;
+ public static final int RPL_LISTSTART = 321;
+ public static final int RPL_LIST = 322;
+ public static final int RPL_LISTEND = 323;
+ public static final int RPL_CHANNELMODEIS = 324;
+ public static final int RPL_NOTOPIC = 331;
+ public static final int RPL_TOPIC = 332;
+ public static final int RPL_TOPICINFO = 333;
+ public static final int RPL_INVITING = 341;
+ public static final int RPL_SUMMONING = 342;
+ public static final int RPL_VERSION = 351;
+ public static final int RPL_WHOREPLY = 352;
+ public static final int RPL_NAMREPLY = 353;
+ public static final int RPL_LINKS = 364;
+ public static final int RPL_ENDOFLINKS = 365;
+ public static final int RPL_ENDOFNAMES = 366;
+ public static final int RPL_BANLIST = 367;
+ public static final int RPL_ENDOFBANLIST = 368;
+ public static final int RPL_ENDOFWHOWAS = 369;
+ public static final int RPL_INFO = 371;
+ public static final int RPL_MOTD = 372;
+ public static final int RPL_ENDOFINFO = 374;
+ public static final int RPL_MOTDSTART = 375;
+ public static final int RPL_ENDOFMOTD = 376;
+ public static final int RPL_YOUREOPER = 381;
+ public static final int RPL_REHASHING = 382;
+ public static final int RPL_TIME = 391;
+ public static final int RPL_USERSSTART = 392;
+ public static final int RPL_USERS = 393;
+ public static final int RPL_ENDOFUSERS = 394;
+ public static final int RPL_NOUSERS = 395;
+
+
+ // Reserved Numerics.
+ public static final int RPL_TRACECLASS = 209;
+ public static final int RPL_STATSQLINE = 217;
+ public static final int RPL_SERVICEINFO = 231;
+ public static final int RPL_ENDOFSERVICES = 232;
+ public static final int RPL_SERVICE = 233;
+ public static final int RPL_SERVLIST = 234;
+ public static final int RPL_SERVLISTEND = 235;
+ public static final int RPL_WHOISCHANOP = 316;
+ public static final int RPL_KILLDONE = 361;
+ public static final int RPL_CLOSING = 362;
+ public static final int RPL_CLOSEEND = 363;
+ public static final int RPL_INFOSTART = 373;
+ public static final int RPL_MYPORTIS = 384;
+ public static final int ERR_YOUWILLBEBANNED = 466;
+ public static final int ERR_BADCHANMASK = 476;
+ public static final int ERR_NOSERVICEHOST = 492;
+
+}
diff --git a/EssentialsUpdate/src/org/jibble/pircbot/User.java b/EssentialsUpdate/src/org/jibble/pircbot/User.java
new file mode 100755
index 000000000..cb2bfa051
--- /dev/null
+++ b/EssentialsUpdate/src/org/jibble/pircbot/User.java
@@ -0,0 +1,161 @@
+/*
+Copyright Paul James Mutton, 2001-2009, http://www.jibble.org/
+
+This file is part of PircBot.
+
+This software is dual-licensed, allowing you to choose between the GNU
+General Public License (GPL) and the www.jibble.org Commercial License.
+Since the GPL may be too restrictive for use in a proprietary application,
+a commercial license is also provided. Full license information can be
+found at http://www.jibble.org/licenses/
+
+*/
+
+package org.jibble.pircbot;
+
+/**
+ * This class is used to represent a user on an IRC server.
+ * Instances of this class are returned by the getUsers method
+ * in the PircBot class.
+ * <p>
+ * Note that this class no longer implements the Comparable interface
+ * for Java 1.1 compatibility reasons.
+ *
+ * @since 1.0.0
+ * @author Paul James Mutton,
+ * <a href="http://www.jibble.org/">http://www.jibble.org/</a>
+ * @version 1.5.0 (Build time: Mon Dec 14 20:07:17 2009)
+ */
+public class User {
+
+
+ /**
+ * Constructs a User object with a known prefix and nick.
+ *
+ * @param prefix The status of the user, for example, "@".
+ * @param nick The nick of the user.
+ */
+ User(String prefix, String nick) {
+ _prefix = prefix;
+ _nick = nick;
+ _lowerNick = nick.toLowerCase();
+ }
+
+
+ /**
+ * Returns the prefix of the user. If the User object has been obtained
+ * from a list of users in a channel, then this will reflect the user's
+ * status in that channel.
+ *
+ * @return The prefix of the user. If there is no prefix, then an empty
+ * String is returned.
+ */
+ public String getPrefix() {
+ return _prefix;
+ }
+
+
+ /**
+ * Returns whether or not the user represented by this object is an
+ * operator. If the User object has been obtained from a list of users
+ * in a channel, then this will reflect the user's operator status in
+ * that channel.
+ *
+ * @return true if the user is an operator in the channel.
+ */
+ public boolean isOp() {
+ return _prefix.indexOf('@') >= 0;
+ }
+
+
+ /**
+ * Returns whether or not the user represented by this object has
+ * voice. If the User object has been obtained from a list of users
+ * in a channel, then this will reflect the user's voice status in
+ * that channel.
+ *
+ * @return true if the user has voice in the channel.
+ */
+ public boolean hasVoice() {
+ return _prefix.indexOf('+') >= 0;
+ }
+
+
+ /**
+ * Returns the nick of the user.
+ *
+ * @return The user's nick.
+ */
+ public String getNick() {
+ return _nick;
+ }
+
+
+ /**
+ * Returns the nick of the user complete with their prefix if they
+ * have one, e.g. "@Dave".
+ *
+ * @return The user's prefix and nick.
+ */
+ public String toString() {
+ return this.getPrefix() + this.getNick();
+ }
+
+
+ /**
+ * Returns true if the nick represented by this User object is the same
+ * as the argument. A case insensitive comparison is made.
+ *
+ * @return true if the nicks are identical (case insensitive).
+ */
+ public boolean equals(String nick) {
+ return nick.toLowerCase().equals(_lowerNick);
+ }
+
+
+ /**
+ * Returns true if the nick represented by this User object is the same
+ * as the nick of the User object given as an argument.
+ * A case insensitive comparison is made.
+ *
+ * @return true if o is a User object with a matching lowercase nick.
+ */
+ public boolean equals(Object o) {
+ if (o instanceof User) {
+ User other = (User) o;
+ return other._lowerNick.equals(_lowerNick);
+ }
+ return false;
+ }
+
+
+ /**
+ * Returns the hash code of this User object.
+ *
+ * @return the hash code of the User object.
+ */
+ public int hashCode() {
+ return _lowerNick.hashCode();
+ }
+
+
+ /**
+ * Returns the result of calling the compareTo method on lowercased
+ * nicks. This is useful for sorting lists of User objects.
+ *
+ * @return the result of calling compareTo on lowercased nicks.
+ */
+ public int compareTo(Object o) {
+ if (o instanceof User) {
+ User other = (User) o;
+ return other._lowerNick.compareTo(_lowerNick);
+ }
+ return -1;
+ }
+
+
+ private String _prefix;
+ private String _nick;
+ private String _lowerNick;
+
+}
diff --git a/EssentialsUpdate/src/plugin.yml b/EssentialsUpdate/src/plugin.yml
new file mode 100644
index 000000000..b8dbe8e25
--- /dev/null
+++ b/EssentialsUpdate/src/plugin.yml
@@ -0,0 +1,21 @@
+# This determines the command prefix when there are conflicts (/name:home, /name:help, etc.)
+name: EssentialsUpdate
+main: com.earth2me.essentials.update.EssentialsUpdate
+# Note to developers: This next line cannot change, or the automatic versioning system will break.
+version: TeamCity
+description: This plugin allows to install or update all Essentials plugins
+authors: [snowleo]
+commands:
+ essentialsupdate:
+ description: Install or update the Essentials plugins.
+ usage: /<command>
+ essentialshelp:
+ description: Get help from the Essentials support chat.
+ usage: /<command>
+permissions:
+ essentials.update:
+ description: Allows you to update Essentials
+ default: op
+ essentials.helpchat:
+ description: Allows you to join Essentials help chat
+ default: op \ No newline at end of file
diff --git a/EssentialsUpdate/test/com/earth2me/essentials/update/UploadTest.java b/EssentialsUpdate/test/com/earth2me/essentials/update/UploadTest.java
new file mode 100644
index 000000000..a51f03bd7
--- /dev/null
+++ b/EssentialsUpdate/test/com/earth2me/essentials/update/UploadTest.java
@@ -0,0 +1,27 @@
+package com.earth2me.essentials.update;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import junit.framework.TestCase;
+import org.junit.Test;
+
+
+public class UploadTest extends TestCase
+{
+ @Test
+ public void testPastieUpload()
+ {
+ try
+ {
+ final PastieUpload pastie = new PastieUpload();
+ assertNotNull(pastie);
+ //final String url = pastie.send("test");
+ //System.out.println(url);
+ }
+ catch (IOException ex)
+ {
+ Logger.getLogger(UploadTest.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+}
diff --git a/EssentialsUpdate/test/com/earth2me/essentials/update/VersionTest.java b/EssentialsUpdate/test/com/earth2me/essentials/update/VersionTest.java
new file mode 100644
index 000000000..901a8f9dc
--- /dev/null
+++ b/EssentialsUpdate/test/com/earth2me/essentials/update/VersionTest.java
@@ -0,0 +1,87 @@
+package com.earth2me.essentials.update;
+
+import com.earth2me.essentials.update.Version.Type;
+import java.util.TreeSet;
+import junit.framework.TestCase;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+
+public class VersionTest extends TestCase
+{
+ @Test
+ public void testStable()
+ {
+ final Version instance = new Version("1.2.3");
+ assertEquals("Testing Major", 1, instance.getMajor());
+ assertEquals("Testing Minor", 2, instance.getMinor());
+ assertEquals("Testing Build", 3, instance.getBuild());
+ assertEquals("Testing Type", Type.STABLE, instance.getType());
+ }
+
+ @Test
+ public void testDev()
+ {
+ final Version instance = new Version("Dev2.3.4");
+ assertEquals("Testing Major", 2, instance.getMajor());
+ assertEquals("Testing Minor", 3, instance.getMinor());
+ assertEquals("Testing Build", 4, instance.getBuild());
+ assertEquals("Testing Type", Type.DEVELOPER, instance.getType());
+ }
+
+ @Test
+ public void testTeamCity()
+ {
+ final Version instance = new Version("Teamcity");
+ assertEquals("Testing Type", Type.DEVELOPER, instance.getType());
+ }
+
+ @Test
+ public void testPre()
+ {
+ final Version instance = new Version("Pre5.7.400.2");
+ assertEquals("Testing Major", 5, instance.getMajor());
+ assertEquals("Testing Minor", 7, instance.getMinor());
+ assertEquals("Testing Build", 400, instance.getBuild());
+ assertEquals("Testing Type", Type.PREVIEW, instance.getType());
+ }
+
+ @Test
+ public void testCompareTo()
+ {
+ Version a = new Version("1.1.1");
+ Version b = new Version("Dev1.1.2");
+ Version c = new Version("1.1.2");
+ Version d = new Version("1.2.0");
+ Version e = new Version("2.0.0");
+ Version f = new Version("Pre1.1.1.1");
+ Version g = new Version("Dev1.2.2");
+ assertTrue("Testing dev", a.compareTo(b) < 0);
+ assertTrue("Testing dev", b.compareTo(a) > 0);
+ assertTrue("Testing build", a.compareTo(c) < 0);
+ assertTrue("Testing build", c.compareTo(a) > 0);
+ assertTrue("Testing minor", a.compareTo(d) < 0);
+ assertTrue("Testing minor", d.compareTo(a) > 0);
+ assertTrue("Testing major", a.compareTo(e) < 0);
+ assertTrue("Testing major", e.compareTo(a) > 0);
+ assertTrue("Testing pre", f.compareTo(a) < 0);
+ assertTrue("Testing pre", a.compareTo(f) > 0);
+ assertTrue("Testing dev vs dev", b.compareTo(g) < 0);
+ assertTrue("Testing dev vs dev", g.compareTo(b) > 0);
+ final TreeSet<Version> set = new TreeSet<Version>();
+ set.add(a);
+ set.add(b);
+ set.add(c);
+ set.add(d);
+ set.add(e);
+ set.add(f);
+ set.add(g);
+ assertEquals("Testing sorting", f, set.pollFirst());
+ assertEquals("Testing sorting", a, set.pollFirst());
+ assertEquals("Testing sorting", c, set.pollFirst());
+ assertEquals("Testing sorting", d, set.pollFirst());
+ assertEquals("Testing sorting", e, set.pollFirst());
+ assertEquals("Testing sorting", b, set.pollFirst());
+ assertEquals("Testing sorting", g, set.pollFirst());
+ }
+}
diff --git a/WebPush/apikey.php b/WebPush/apikey.php
new file mode 100644
index 000000000..59f2bf3ac
--- /dev/null
+++ b/WebPush/apikey.php
@@ -0,0 +1,5 @@
+<?php
+
+$params['api-key'] = "c73c331c7e44c156c852f7d08de3f22bb7a6e948";
+
+?>
diff --git a/WebPush/index.php b/WebPush/index.php
new file mode 100644
index 000000000..ab018bf27
--- /dev/null
+++ b/WebPush/index.php
@@ -0,0 +1,52 @@
+<?php
+
+//We want to be able to continue if the client aborts.
+ignore_user_abort();
+set_time_limit(0);
+error_reporting(E_ALL);
+ini_set('display_errors', 'Off');
+ini_set('error_log', 'errors.log');
+
+//Abort the browser so it doesn't hang while we do the uploading.
+ob_end_clean();
+header("Connection: close");
+ob_start();
+header("Content-Length: 0");
+ob_end_flush();
+flush();
+
+//Lets get to work!
+include('upload.php');
+
+$build = $_GET['buildid'];
+$branch = $_GET['branch'];
+$version = $_GET['version'];
+
+if ($build == "" || $branch == "" || $version == "")
+{
+ die();
+}
+
+//Don't upload dev builds atm.
+if ($branch == "bt2")
+{
+ die();
+}
+
+sleep(60);
+
+$changes = getChanges($build, $branch);
+
+uploadit($build, $branch, 'Essentials.jar', $version, $changes);
+sleep(1);
+uploadit($build, $branch, 'EssentialsChat.jar', $version, $changes);
+sleep(1);
+uploadit($build, $branch, 'EssentialsSpawn.jar', $version, $changes);
+sleep(1);
+uploadit($build, $branch, 'EssentialsProtect.jar', $version, $changes);
+sleep(1);
+uploadit($build, $branch, 'EssentialsXMPP.jar', $version, $changes);
+sleep(1);
+uploadit($build, $branch, 'EssentialsGeoIP.jar', $version, $changes);
+?>
+
diff --git a/WebPush/nbproject/private/private.properties b/WebPush/nbproject/private/private.properties
new file mode 100644
index 000000000..8c2a80c6f
--- /dev/null
+++ b/WebPush/nbproject/private/private.properties
@@ -0,0 +1,8 @@
+copy.src.files=false
+copy.src.target=
+index.file=index.php
+remote.connection=localhost-d13e79
+remote.directory=/upload
+remote.upload=ON_SAVE
+run.as=REMOTE
+url=http://ess.khhq.net/upload/
diff --git a/WebPush/nbproject/project.properties b/WebPush/nbproject/project.properties
new file mode 100644
index 000000000..6ffde2f50
--- /dev/null
+++ b/WebPush/nbproject/project.properties
@@ -0,0 +1,7 @@
+include.path=${php.global.include.path}
+php.version=PHP_5
+source.encoding=UTF-8
+src.dir=.
+tags.asp=false
+tags.short=true
+web.root=.
diff --git a/WebPush/nbproject/project.xml b/WebPush/nbproject/project.xml
new file mode 100644
index 000000000..da4214310
--- /dev/null
+++ b/WebPush/nbproject/project.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+ <type>org.netbeans.modules.php.project</type>
+ <configuration>
+ <data xmlns="http://www.netbeans.org/ns/php-project/1">
+ <name>Push</name>
+ </data>
+ </configuration>
+</project>
diff --git a/WebPush/simple_html_dom.php b/WebPush/simple_html_dom.php
new file mode 100644
index 000000000..3f96f8d95
--- /dev/null
+++ b/WebPush/simple_html_dom.php
@@ -0,0 +1,1727 @@
+<?php
+
+/**
+ * Website: http://sourceforge.net/projects/simplehtmldom/
+ * Acknowledge: Jose Solorzano (https://sourceforge.net/projects/php-html/)
+ * Contributions by:
+ * Yousuke Kumakura (Attribute filters)
+ * Vadim Voituk (Negative indexes supports of "find" method)
+ * Antcs (Constructor with automatically load contents either text or file/url)
+ *
+ * all affected sections have comments starting with "PaperG"
+ *
+ * Paperg - Added case insensitive testing of the value of the selector.
+ * Paperg - Added tag_start for the starting index of tags - NOTE: This works but not accurately.
+ * This tag_start gets counted AFTER \r\n have been crushed out, and after the remove_noice calls so it will not reflect the REAL position of the tag in the source,
+ * it will almost always be smaller by some amount.
+ * We use this to determine how far into the file the tag in question is. This "percentage will never be accurate as the $dom->size is the "real" number of bytes the dom was created from.
+ * but for most purposes, it's a really good estimation.
+ * Paperg - Added the forceTagsClosed to the dom constructor. Forcing tags closed is great for malformed html, but it CAN lead to parsing errors.
+ * Allow the user to tell us how much they trust the html.
+ * Paperg add the text and plaintext to the selectors for the find syntax. plaintext implies text in the innertext of a node. text implies that the tag is a text node.
+ * This allows for us to find tags based on the text they contain.
+ * Create find_ancestor_tag to see if a tag is - at any level - inside of another specific tag.
+ * Paperg: added parse_charset so that we know about the character set of the source document.
+ * NOTE: If the user's system has a routine called get_last_retrieve_url_contents_content_type availalbe, we will assume it's returning the content-type header from the
+ * last transfer or curl_exec, and we will parse that and use it in preference to any other method of charset detection.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author S.C. Chen <me578022@gmail.com>
+ * @author John Schlick
+ * @author Rus Carroll
+ * @version 1.11 ($Rev: 184 $)
+ * @package PlaceLocalInclude
+ * @subpackage simple_html_dom
+ */
+/**
+ * All of the Defines for the classes below.
+ * @author S.C. Chen <me578022@gmail.com>
+ */
+define('HDOM_TYPE_ELEMENT', 1);
+define('HDOM_TYPE_COMMENT', 2);
+define('HDOM_TYPE_TEXT', 3);
+define('HDOM_TYPE_ENDTAG', 4);
+define('HDOM_TYPE_ROOT', 5);
+define('HDOM_TYPE_UNKNOWN', 6);
+define('HDOM_QUOTE_DOUBLE', 0);
+define('HDOM_QUOTE_SINGLE', 1);
+define('HDOM_QUOTE_NO', 3);
+define('HDOM_INFO_BEGIN', 0);
+define('HDOM_INFO_END', 1);
+define('HDOM_INFO_QUOTE', 2);
+define('HDOM_INFO_SPACE', 3);
+define('HDOM_INFO_TEXT', 4);
+define('HDOM_INFO_INNER', 5);
+define('HDOM_INFO_OUTER', 6);
+define('HDOM_INFO_ENDSPACE', 7);
+define('DEFAULT_TARGET_CHARSET', 'UTF-8');
+define('DEFAULT_BR_TEXT', "\r\n");
+
+// helper functions
+// -----------------------------------------------------------------------------
+// get html dom from file
+// $maxlen is defined in the code as PHP_STREAM_COPY_ALL which is defined as -1.
+function file_get_html($url, $use_include_path = false, $context=null, $offset = -1, $maxLen=-1, $lowercase = true, $forceTagsClosed=true, $target_charset = DEFAULT_TARGET_CHARSET, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT)
+{
+ // We DO force the tags to be terminated.
+ $dom = new simple_html_dom(null, $lowercase, $forceTagsClosed, $target_charset, $defaultBRText);
+ // For sourceforge users: uncomment the next line and comment the retreive_url_contents line 2 lines down if it is not already done.
+ $contents = file_get_contents($url, $use_include_path, $context, $offset);
+ // Paperg - use our own mechanism for getting the contents as we want to control the timeout.
+// $contents = retrieve_url_contents($url);
+ if (empty($contents))
+ {
+ return false;
+ }
+ // The second parameter can force the selectors to all be lowercase.
+ $dom->load($contents, $lowercase, $stripRN);
+ return $dom;
+}
+
+// get html dom from string
+function str_get_html($str, $lowercase=true, $forceTagsClosed=true, $target_charset = DEFAULT_TARGET_CHARSET, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT)
+{
+ $dom = new simple_html_dom(null, $lowercase, $forceTagsClosed, $target_charset, $defaultBRText);
+ if (empty($str))
+ {
+ $dom->clear();
+ return false;
+ }
+ $dom->load($str, $lowercase, $stripRN);
+ return $dom;
+}
+
+// dump html dom tree
+function dump_html_tree($node, $show_attr=true, $deep=0)
+{
+ $node->dump($node);
+}
+
+/**
+ * simple html dom node
+ * PaperG - added ability for "find" routine to lowercase the value of the selector.
+ * PaperG - added $tag_start to track the start position of the tag in the total byte index
+ *
+ * @package PlaceLocalInclude
+ */
+class simple_html_dom_node
+{
+ public $nodetype = HDOM_TYPE_TEXT;
+ public $tag = 'text';
+ public $attr = array();
+ public $children = array();
+ public $nodes = array();
+ public $parent = null;
+ public $_ = array();
+ public $tag_start = 0;
+ private $dom = null;
+
+ function __construct($dom)
+ {
+ $this->dom = $dom;
+ $dom->nodes[] = $this;
+ }
+
+ function __destruct()
+ {
+ $this->clear();
+ }
+
+ function __toString()
+ {
+ return $this->outertext();
+ }
+
+ // clean up memory due to php5 circular references memory leak...
+ function clear()
+ {
+ $this->dom = null;
+ $this->nodes = null;
+ $this->parent = null;
+ $this->children = null;
+ }
+
+ // dump node's tree
+ function dump($show_attr=true, $deep=0)
+ {
+ $lead = str_repeat(' ', $deep);
+
+ echo $lead . $this->tag;
+ if ($show_attr && count($this->attr) > 0)
+ {
+ echo '(';
+ foreach ($this->attr as $k => $v)
+ echo "[$k]=>\"" . $this->$k . '", ';
+ echo ')';
+ }
+ echo "\n";
+
+ foreach ($this->nodes as $c)
+ $c->dump($show_attr, $deep + 1);
+ }
+
+ // Debugging function to dump a single dom node with a bunch of information about it.
+ function dump_node()
+ {
+ echo $this->tag;
+ if (count($this->attr) > 0)
+ {
+ echo '(';
+ foreach ($this->attr as $k => $v)
+ {
+ echo "[$k]=>\"" . $this->$k . '", ';
+ }
+ echo ')';
+ }
+ if (count($this->attr) > 0)
+ {
+ echo ' $_ (';
+ foreach ($this->_ as $k => $v)
+ {
+ if (is_array($v))
+ {
+ echo "[$k]=>(";
+ foreach ($v as $k2 => $v2)
+ {
+ echo "[$k2]=>\"" . $v2 . '", ';
+ }
+ echo ")";
+ }
+ else
+ {
+ echo "[$k]=>\"" . $v . '", ';
+ }
+ }
+ echo ")";
+ }
+
+ if (isset($this->text))
+ {
+ echo " text: (" . $this->text . ")";
+ }
+
+ echo " children: " . count($this->children);
+ echo " nodes: " . count($this->nodes);
+ echo " tag_start: " . $this->tag_start;
+ echo "\n";
+ }
+
+ // returns the parent of node
+ function parent()
+ {
+ return $this->parent;
+ }
+
+ // returns children of node
+ function children($idx=-1)
+ {
+ if ($idx === -1)
+ return $this->children;
+ if (isset($this->children[$idx]))
+ return $this->children[$idx];
+ return null;
+ }
+
+ // returns the first child of node
+ function first_child()
+ {
+ if (count($this->children) > 0)
+ return $this->children[0];
+ return null;
+ }
+
+ // returns the last child of node
+ function last_child()
+ {
+ if (($count = count($this->children)) > 0)
+ return $this->children[$count - 1];
+ return null;
+ }
+
+ // returns the next sibling of node
+ function next_sibling()
+ {
+ if ($this->parent === null)
+ return null;
+ $idx = 0;
+ $count = count($this->parent->children);
+ while ($idx < $count && $this !== $this->parent->children[$idx])
+ ++$idx;
+ if (++$idx >= $count)
+ return null;
+ return $this->parent->children[$idx];
+ }
+
+ // returns the previous sibling of node
+ function prev_sibling()
+ {
+ if ($this->parent === null)
+ return null;
+ $idx = 0;
+ $count = count($this->parent->children);
+ while ($idx < $count && $this !== $this->parent->children[$idx])
+ ++$idx;
+ if (--$idx < 0)
+ return null;
+ return $this->parent->children[$idx];
+ }
+
+ // function to locate a specific ancestor tag in the path to the root.
+ function find_ancestor_tag($tag)
+ {
+ global $debugObject;
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLogEntry(1);
+ }
+
+ // Start by including ourselves in the comparison.
+ $returnDom = $this;
+
+ while (!is_null($returnDom))
+ {
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLog(2, "Current tag is: " . $returnDom->tag);
+ }
+
+ if ($returnDom->tag == $tag)
+ {
+ break;
+ }
+ $returnDom = $returnDom->parent;
+ }
+ return $returnDom;
+ }
+
+ // get dom node's inner html
+ function innertext()
+ {
+ if (isset($this->_[HDOM_INFO_INNER]))
+ return $this->_[HDOM_INFO_INNER];
+ if (isset($this->_[HDOM_INFO_TEXT]))
+ return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
+
+ $ret = '';
+ foreach ($this->nodes as $n)
+ $ret .= $n->outertext();
+ return $ret;
+ }
+
+ // get dom node's outer text (with tag)
+ function outertext()
+ {
+ global $debugObject;
+ if (is_object($debugObject))
+ {
+ $text = '';
+ if ($this->tag == 'text')
+ {
+ if (!empty($this->text))
+ {
+ $text = " with text: " . $this->text;
+ }
+ }
+ $debugObject->debugLog(1, 'Innertext of tag: ' . $this->tag . $text);
+ }
+
+ if ($this->tag === 'root')
+ return $this->innertext();
+
+ // trigger callback
+ if ($this->dom && $this->dom->callback !== null)
+ {
+ call_user_func_array($this->dom->callback, array($this));
+ }
+
+ if (isset($this->_[HDOM_INFO_OUTER]))
+ return $this->_[HDOM_INFO_OUTER];
+ if (isset($this->_[HDOM_INFO_TEXT]))
+ return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
+
+ // render begin tag
+ if ($this->dom && $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]])
+ {
+ $ret = $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]->makeup();
+ }
+ else
+ {
+ $ret = "";
+ }
+
+ // render inner text
+ if (isset($this->_[HDOM_INFO_INNER]))
+ {
+ // If it's a br tag... don't return the HDOM_INNER_INFO that we may or may not have added.
+ if ($this->tag != "br")
+ {
+ $ret .= $this->_[HDOM_INFO_INNER];
+ }
+ }
+ else
+ {
+ if ($this->nodes)
+ {
+ foreach ($this->nodes as $n)
+ {
+ $ret .= $this->convert_text($n->outertext());
+ }
+ }
+ }
+
+ // render end tag
+ if (isset($this->_[HDOM_INFO_END]) && $this->_[HDOM_INFO_END] != 0)
+ $ret .= '</' . $this->tag . '>';
+ return $ret;
+ }
+
+ // get dom node's plain text
+ function text()
+ {
+ if (isset($this->_[HDOM_INFO_INNER]))
+ return $this->_[HDOM_INFO_INNER];
+ switch ($this->nodetype)
+ {
+ case HDOM_TYPE_TEXT: return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
+ case HDOM_TYPE_COMMENT: return '';
+ case HDOM_TYPE_UNKNOWN: return '';
+ }
+ if (strcasecmp($this->tag, 'script') === 0)
+ return '';
+ if (strcasecmp($this->tag, 'style') === 0)
+ return '';
+
+ $ret = '';
+ // In rare cases, (always node type 1 or HDOM_TYPE_ELEMENT - observed for some span tags, and some p tags) $this->nodes is set to NULL.
+ // NOTE: This indicates that there is a problem where it's set to NULL without a clear happening.
+ // WHY is this happening?
+ if (!is_null($this->nodes))
+ {
+ foreach ($this->nodes as $n)
+ {
+ $ret .= $this->convert_text($n->text());
+ }
+ }
+ return $ret;
+ }
+
+ function xmltext()
+ {
+ $ret = $this->innertext();
+ $ret = str_ireplace('<![CDATA[', '', $ret);
+ $ret = str_replace(']]>', '', $ret);
+ return $ret;
+ }
+
+ // build node's text with tag
+ function makeup()
+ {
+ // text, comment, unknown
+ if (isset($this->_[HDOM_INFO_TEXT]))
+ return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
+
+ $ret = '<' . $this->tag;
+ $i = -1;
+
+ foreach ($this->attr as $key => $val)
+ {
+ ++$i;
+
+ // skip removed attribute
+ if ($val === null || $val === false)
+ continue;
+
+ $ret .= $this->_[HDOM_INFO_SPACE][$i][0];
+ //no value attr: nowrap, checked selected...
+ if ($val === true)
+ $ret .= $key;
+ else
+ {
+ switch ($this->_[HDOM_INFO_QUOTE][$i])
+ {
+ case HDOM_QUOTE_DOUBLE: $quote = '"';
+ break;
+ case HDOM_QUOTE_SINGLE: $quote = '\'';
+ break;
+ default: $quote = '';
+ }
+ $ret .= $key . $this->_[HDOM_INFO_SPACE][$i][1] . '=' . $this->_[HDOM_INFO_SPACE][$i][2] . $quote . $val . $quote;
+ }
+ }
+ $ret = $this->dom->restore_noise($ret);
+ return $ret . $this->_[HDOM_INFO_ENDSPACE] . '>';
+ }
+
+ // find elements by css selector
+ //PaperG - added ability for find to lowercase the value of the selector.
+ function find($selector, $idx=null, $lowercase=false)
+ {
+ $selectors = $this->parse_selector($selector);
+ if (($count = count($selectors)) === 0)
+ return array();
+ $found_keys = array();
+
+ // find each selector
+ for ($c = 0; $c < $count; ++$c)
+ {
+ // The change on the below line was documented on the sourceforge code tracker id 2788009
+ // used to be: if (($levle=count($selectors[0]))===0) return array();
+ if (($levle = count($selectors[$c])) === 0)
+ return array();
+ if (!isset($this->_[HDOM_INFO_BEGIN]))
+ return array();
+
+ $head = array($this->_[HDOM_INFO_BEGIN] => 1);
+
+ // handle descendant selectors, no recursive!
+ for ($l = 0; $l < $levle; ++$l)
+ {
+ $ret = array();
+ foreach ($head as $k => $v)
+ {
+ $n = ($k === -1) ? $this->dom->root : $this->dom->nodes[$k];
+ //PaperG - Pass this optional parameter on to the seek function.
+ $n->seek($selectors[$c][$l], $ret, $lowercase);
+ }
+ $head = $ret;
+ }
+
+ foreach ($head as $k => $v)
+ {
+ if (!isset($found_keys[$k]))
+ $found_keys[$k] = 1;
+ }
+ }
+
+ // sort keys
+ ksort($found_keys);
+
+ $found = array();
+ foreach ($found_keys as $k => $v)
+ $found[] = $this->dom->nodes[$k];
+
+ // return nth-element or array
+ if (is_null($idx))
+ return $found;
+ else if ($idx < 0)
+ $idx = count($found) + $idx;
+ return (isset($found[$idx])) ? $found[$idx] : null;
+ }
+
+ // seek for given conditions
+ // PaperG - added parameter to allow for case insensitive testing of the value of a selector.
+ protected function seek($selector, &$ret, $lowercase=false)
+ {
+ global $debugObject;
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLogEntry(1);
+ }
+
+ list($tag, $key, $val, $exp, $no_key) = $selector;
+
+ // xpath index
+ if ($tag && $key && is_numeric($key))
+ {
+ $count = 0;
+ foreach ($this->children as $c)
+ {
+ if ($tag === '*' || $tag === $c->tag)
+ {
+ if (++$count == $key)
+ {
+ $ret[$c->_[HDOM_INFO_BEGIN]] = 1;
+ return;
+ }
+ }
+ }
+ return;
+ }
+
+ $end = (!empty($this->_[HDOM_INFO_END])) ? $this->_[HDOM_INFO_END] : 0;
+ if ($end == 0)
+ {
+ $parent = $this->parent;
+ while (!isset($parent->_[HDOM_INFO_END]) && $parent !== null)
+ {
+ $end -= 1;
+ $parent = $parent->parent;
+ }
+ $end += $parent->_[HDOM_INFO_END];
+ }
+
+ for ($i = $this->_[HDOM_INFO_BEGIN] + 1; $i < $end; ++$i)
+ {
+ $node = $this->dom->nodes[$i];
+
+ $pass = true;
+
+ if ($tag === '*' && !$key)
+ {
+ if (in_array($node, $this->children, true))
+ $ret[$i] = 1;
+ continue;
+ }
+
+ // compare tag
+ if ($tag && $tag != $node->tag && $tag !== '*')
+ {
+ $pass = false;
+ }
+ // compare key
+ if ($pass && $key)
+ {
+ if ($no_key)
+ {
+ if (isset($node->attr[$key]))
+ $pass = false;
+ } else
+ {
+ if (($key != "plaintext") && !isset($node->attr[$key]))
+ $pass = false;
+ }
+ }
+ // compare value
+ if ($pass && $key && $val && $val !== '*')
+ {
+ // If they have told us that this is a "plaintext" search then we want the plaintext of the node - right?
+ if ($key == "plaintext")
+ {
+ // $node->plaintext actually returns $node->text();
+ $nodeKeyValue = $node->text();
+ }
+ else
+ {
+ // this is a normal search, we want the value of that attribute of the tag.
+ $nodeKeyValue = $node->attr[$key];
+ }
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLog(2, "testing node: " . $node->tag . " for attribute: " . $key . $exp . $val . " where nodes value is: " . $nodeKeyValue);
+ }
+
+ //PaperG - If lowercase is set, do a case insensitive test of the value of the selector.
+ if ($lowercase)
+ {
+ $check = $this->match($exp, strtolower($val), strtolower($nodeKeyValue));
+ }
+ else
+ {
+ $check = $this->match($exp, $val, $nodeKeyValue);
+ }
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLog(2, "after match: " . ($check ? "true" : "false"));
+ }
+
+ // handle multiple class
+ if (!$check && strcasecmp($key, 'class') === 0)
+ {
+ foreach (explode(' ', $node->attr[$key]) as $k)
+ {
+ // Without this, there were cases where leading, trailing, or double spaces lead to our comparing blanks - bad form.
+ if (!empty($k))
+ {
+ if ($lowercase)
+ {
+ $check = $this->match($exp, strtolower($val), strtolower($k));
+ }
+ else
+ {
+ $check = $this->match($exp, $val, $k);
+ }
+ if ($check)
+ break;
+ }
+ }
+ }
+ if (!$check)
+ $pass = false;
+ }
+ if ($pass)
+ $ret[$i] = 1;
+ unset($node);
+ }
+ // It's passed by reference so this is actually what this function returns.
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLog(1, "EXIT - ret: ", $ret);
+ }
+ }
+
+ protected function match($exp, $pattern, $value)
+ {
+ global $debugObject;
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLogEntry(1);
+ }
+
+ switch ($exp)
+ {
+ case '=':
+ return ($value === $pattern);
+ case '!=':
+ return ($value !== $pattern);
+ case '^=':
+ return preg_match("/^" . preg_quote($pattern, '/') . "/", $value);
+ case '$=':
+ return preg_match("/" . preg_quote($pattern, '/') . "$/", $value);
+ case '*=':
+ if ($pattern[0] == '/')
+ {
+ return preg_match($pattern, $value);
+ }
+ return preg_match("/" . $pattern . "/i", $value);
+ }
+ return false;
+ }
+
+ protected function parse_selector($selector_string)
+ {
+ global $debugObject;
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLogEntry(1);
+ }
+
+ // pattern of CSS selectors, modified from mootools
+ // Paperg: Add the colon to the attrbute, so that it properly finds <tag attr:ibute="something" > like google does.
+ // Note: if you try to look at this attribute, yo MUST use getAttribute since $dom->x:y will fail the php syntax check.
+// Notice the \[ starting the attbute? and the @? following? This implies that an attribute can begin with an @ sign that is not captured.
+// This implies that an html attribute specifier may start with an @ sign that is NOT captured by the expression.
+// farther study is required to determine of this should be documented or removed.
+// $pattern = "/([\w-:\*]*)(?:\#([\w-]+)|\.([\w-]+))?(?:\[@?(!?[\w-]+)(?:([!*^$]?=)[\"']?(.*?)[\"']?)?\])?([\/, ]+)/is";
+ $pattern = "/([\w-:\*]*)(?:\#([\w-]+)|\.([\w-]+))?(?:\[@?(!?[\w-:]+)(?:([!*^$]?=)[\"']?(.*?)[\"']?)?\])?([\/, ]+)/is";
+ preg_match_all($pattern, trim($selector_string) . ' ', $matches, PREG_SET_ORDER);
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLog(2, "Matches Array: ", $matches);
+ }
+
+ $selectors = array();
+ $result = array();
+ //print_r($matches);
+
+ foreach ($matches as $m)
+ {
+ $m[0] = trim($m[0]);
+ if ($m[0] === '' || $m[0] === '/' || $m[0] === '//')
+ continue;
+ // for browser generated xpath
+ if ($m[1] === 'tbody')
+ continue;
+
+ list($tag, $key, $val, $exp, $no_key) = array($m[1], null, null, '=', false);
+ if (!empty($m[2]))
+ {
+ $key = 'id';
+ $val = $m[2];
+ }
+ if (!empty($m[3]))
+ {
+ $key = 'class';
+ $val = $m[3];
+ }
+ if (!empty($m[4]))
+ {
+ $key = $m[4];
+ }
+ if (!empty($m[5]))
+ {
+ $exp = $m[5];
+ }
+ if (!empty($m[6]))
+ {
+ $val = $m[6];
+ }
+
+ // convert to lowercase
+ if ($this->dom->lowercase)
+ {
+ $tag = strtolower($tag);
+ $key = strtolower($key);
+ }
+ //elements that do NOT have the specified attribute
+ if (isset($key[0]) && $key[0] === '!')
+ {
+ $key = substr($key, 1);
+ $no_key = true;
+ }
+
+ $result[] = array($tag, $key, $val, $exp, $no_key);
+ if (trim($m[7]) === ',')
+ {
+ $selectors[] = $result;
+ $result = array();
+ }
+ }
+ if (count($result) > 0)
+ $selectors[] = $result;
+ return $selectors;
+ }
+
+ function __get($name)
+ {
+ if (isset($this->attr[$name]))
+ {
+ return $this->convert_text($this->attr[$name]);
+ }
+ switch ($name)
+ {
+ case 'outertext': return $this->outertext();
+ case 'innertext': return $this->innertext();
+ case 'plaintext': return $this->text();
+ case 'xmltext': return $this->xmltext();
+ default: return array_key_exists($name, $this->attr);
+ }
+ }
+
+ function __set($name, $value)
+ {
+ switch ($name)
+ {
+ case 'outertext': return $this->_[HDOM_INFO_OUTER] = $value;
+ case 'innertext':
+ if (isset($this->_[HDOM_INFO_TEXT]))
+ return $this->_[HDOM_INFO_TEXT] = $value;
+ return $this->_[HDOM_INFO_INNER] = $value;
+ }
+ if (!isset($this->attr[$name]))
+ {
+ $this->_[HDOM_INFO_SPACE][] = array(' ', '', '');
+ $this->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE;
+ }
+ $this->attr[$name] = $value;
+ }
+
+ function __isset($name)
+ {
+ switch ($name)
+ {
+ case 'outertext': return true;
+ case 'innertext': return true;
+ case 'plaintext': return true;
+ }
+ //no value attr: nowrap, checked selected...
+ return (array_key_exists($name, $this->attr)) ? true : isset($this->attr[$name]);
+ }
+
+ function __unset($name)
+ {
+ if (isset($this->attr[$name]))
+ unset($this->attr[$name]);
+ }
+
+ // PaperG - Function to convert the text from one character set to another if the two sets are not the same.
+ function convert_text($text)
+ {
+ global $debugObject;
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLogEntry(1);
+ }
+
+ $converted_text = $text;
+
+ $sourceCharset = "";
+ $targetCharset = "";
+ if ($this->dom)
+ {
+ $sourceCharset = strtoupper($this->dom->_charset);
+ $targetCharset = strtoupper($this->dom->_target_charset);
+ }
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLog(3, "source charset: " . $sourceCharset . " target charaset: " . $targetCharset);
+ }
+
+ if (!empty($sourceCharset) && !empty($targetCharset) && (strcasecmp($sourceCharset, $targetCharset) != 0))
+ {
+ // Check if the reported encoding could have been incorrect and the text is actually already UTF-8
+ if ((strcasecmp($targetCharset, 'UTF-8') == 0) && ($this->is_utf8($text)))
+ {
+ $converted_text = $text;
+ }
+ else
+ {
+ $converted_text = iconv($sourceCharset, $targetCharset, $text);
+ }
+ }
+
+ return $converted_text;
+ }
+
+ function is_utf8($string)
+ {
+ return (utf8_encode(utf8_decode($string)) == $string);
+ }
+
+ // camel naming conventions
+ function getAllAttributes()
+ {
+ return $this->attr;
+ }
+
+ function getAttribute($name)
+ {
+ return $this->__get($name);
+ }
+
+ function setAttribute($name, $value)
+ {
+ $this->__set($name, $value);
+ }
+
+ function hasAttribute($name)
+ {
+ return $this->__isset($name);
+ }
+
+ function removeAttribute($name)
+ {
+ $this->__set($name, null);
+ }
+
+ function getElementById($id)
+ {
+ return $this->find("#$id", 0);
+ }
+
+ function getElementsById($id, $idx=null)
+ {
+ return $this->find("#$id", $idx);
+ }
+
+ function getElementByTagName($name)
+ {
+ return $this->find($name, 0);
+ }
+
+ function getElementsByTagName($name, $idx=null)
+ {
+ return $this->find($name, $idx);
+ }
+
+ function parentNode()
+ {
+ return $this->parent();
+ }
+
+ function childNodes($idx=-1)
+ {
+ return $this->children($idx);
+ }
+
+ function firstChild()
+ {
+ return $this->first_child();
+ }
+
+ function lastChild()
+ {
+ return $this->last_child();
+ }
+
+ function nextSibling()
+ {
+ return $this->next_sibling();
+ }
+
+ function previousSibling()
+ {
+ return $this->prev_sibling();
+ }
+}
+
+/**
+ * simple html dom parser
+ * Paperg - in the find routine: allow us to specify that we want case insensitive testing of the value of the selector.
+ * Paperg - change $size from protected to public so we can easily access it
+ * Paperg - added ForceTagsClosed in the constructor which tells us whether we trust the html or not. Default is to NOT trust it.
+ *
+ * @package PlaceLocalInclude
+ */
+class simple_html_dom
+{
+ public $root = null;
+ public $nodes = array();
+ public $callback = null;
+ public $lowercase = false;
+ public $size;
+ protected $pos;
+ protected $doc;
+ protected $char;
+ protected $cursor;
+ protected $parent;
+ protected $noise = array();
+ protected $token_blank = " \t\r\n";
+ protected $token_equal = ' =/>';
+ protected $token_slash = " />\r\n\t";
+ protected $token_attr = ' >';
+ protected $_charset = '';
+ protected $_target_charset = '';
+ protected $default_br_text = "";
+ // use isset instead of in_array, performance boost about 30%...
+ protected $self_closing_tags = array('img' => 1, 'br' => 1, 'input' => 1, 'meta' => 1, 'link' => 1, 'hr' => 1, 'base' => 1, 'embed' => 1, 'spacer' => 1);
+ protected $block_tags = array('root' => 1, 'body' => 1, 'form' => 1, 'div' => 1, 'span' => 1, 'table' => 1);
+ // Known sourceforge issue #2977341
+ // B tags that are not closed cause us to return everything to the end of the document.
+ protected $optional_closing_tags = array(
+ 'tr' => array('tr' => 1, 'td' => 1, 'th' => 1),
+ 'th' => array('th' => 1),
+ 'td' => array('td' => 1),
+ 'li' => array('li' => 1),
+ 'dt' => array('dt' => 1, 'dd' => 1),
+ 'dd' => array('dd' => 1, 'dt' => 1),
+ 'dl' => array('dd' => 1, 'dt' => 1),
+ 'p' => array('p' => 1),
+ 'nobr' => array('nobr' => 1),
+ 'b' => array('b' => 1),
+ );
+
+ function __construct($str=null, $lowercase=true, $forceTagsClosed=true, $target_charset=DEFAULT_TARGET_CHARSET, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT)
+ {
+ if ($str)
+ {
+ if (preg_match("/^http:\/\//i", $str) || is_file($str))
+ $this->load_file($str);
+ else
+ $this->load($str, $lowercase, $stripRN, $defaultBRText);
+ }
+ // Forcing tags to be closed implies that we don't trust the html, but it can lead to parsing errors if we SHOULD trust the html.
+ if (!$forceTagsClosed)
+ {
+ $this->optional_closing_array = array();
+ }
+ $this->_target_charset = $target_charset;
+ }
+
+ function __destruct()
+ {
+ $this->clear();
+ }
+
+ // load html from string
+ function load($str, $lowercase=true, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT)
+ {
+ global $debugObject;
+
+ // prepare
+ $this->prepare($str, $lowercase, $stripRN, $defaultBRText);
+ // strip out comments
+ $this->remove_noise("'<!--(.*?)-->'is");
+ // strip out cdata
+ $this->remove_noise("'<!\[CDATA\[(.*?)\]\]>'is", true);
+ // Per sourceforge http://sourceforge.net/tracker/?func=detail&aid=2949097&group_id=218559&atid=1044037
+ // Script tags removal now preceeds style tag removal.
+ // strip out <script> tags
+ $this->remove_noise("'<\s*script[^>]*[^/]>(.*?)<\s*/\s*script\s*>'is");
+ $this->remove_noise("'<\s*script\s*>(.*?)<\s*/\s*script\s*>'is");
+ // strip out <style> tags
+ $this->remove_noise("'<\s*style[^>]*[^/]>(.*?)<\s*/\s*style\s*>'is");
+ $this->remove_noise("'<\s*style\s*>(.*?)<\s*/\s*style\s*>'is");
+ // strip out preformatted tags
+ $this->remove_noise("'<\s*(?:code)[^>]*>(.*?)<\s*/\s*(?:code)\s*>'is");
+ // strip out server side scripts
+ $this->remove_noise("'(<\?)(.*?)(\?>)'s", true);
+ // strip smarty scripts
+ $this->remove_noise("'(\{\w)(.*?)(\})'s", true);
+
+ // parsing
+ while ($this->parse());
+ // end
+ $this->root->_[HDOM_INFO_END] = $this->cursor;
+ $this->parse_charset();
+ }
+
+ // load html from file
+ function load_file()
+ {
+ $args = func_get_args();
+ $this->load(call_user_func_array('file_get_contents', $args), true);
+ // Per the simple_html_dom repositiry this is a planned upgrade to the codebase.
+ // Throw an error if we can't properly load the dom.
+ if (($error = error_get_last()) !== null)
+ {
+ $this->clear();
+ return false;
+ }
+ }
+
+ // set callback function
+ function set_callback($function_name)
+ {
+ $this->callback = $function_name;
+ }
+
+ // remove callback function
+ function remove_callback()
+ {
+ $this->callback = null;
+ }
+
+ // save dom as string
+ function save($filepath='')
+ {
+ $ret = $this->root->innertext();
+ if ($filepath !== '')
+ file_put_contents($filepath, $ret, LOCK_EX);
+ return $ret;
+ }
+
+ // find dom node by css selector
+ // Paperg - allow us to specify that we want case insensitive testing of the value of the selector.
+ function find($selector, $idx=null, $lowercase=false)
+ {
+ return $this->root->find($selector, $idx, $lowercase);
+ }
+
+ // clean up memory due to php5 circular references memory leak...
+ function clear()
+ {
+ foreach ($this->nodes as $n)
+ {
+ $n->clear();
+ $n = null;
+ }
+ // This add next line is documented in the sourceforge repository. 2977248 as a fix for ongoing memory leaks that occur even with the use of clear.
+ if (isset($this->children))
+ foreach ($this->children as $n)
+ {
+ $n->clear();
+ $n = null;
+ }
+ if (isset($this->parent))
+ {
+ $this->parent->clear();
+ unset($this->parent);
+ }
+ if (isset($this->root))
+ {
+ $this->root->clear();
+ unset($this->root);
+ }
+ unset($this->doc);
+ unset($this->noise);
+ }
+
+ function dump($show_attr=true)
+ {
+ $this->root->dump($show_attr);
+ }
+
+ // prepare HTML data and init everything
+ protected function prepare($str, $lowercase=true, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT)
+ {
+ $this->clear();
+
+ // set the length of content before we do anything to it.
+ $this->size = strlen($str);
+
+ //before we save the string as the doc... strip out the \r \n's if we are told to.
+ if ($stripRN)
+ {
+ $str = str_replace("\r", " ", $str);
+ $str = str_replace("\n", " ", $str);
+ }
+
+ $this->doc = $str;
+ $this->pos = 0;
+ $this->cursor = 1;
+ $this->noise = array();
+ $this->nodes = array();
+ $this->lowercase = $lowercase;
+ $this->default_br_text = $defaultBRText;
+ $this->root = new simple_html_dom_node($this);
+ $this->root->tag = 'root';
+ $this->root->_[HDOM_INFO_BEGIN] = -1;
+ $this->root->nodetype = HDOM_TYPE_ROOT;
+ $this->parent = $this->root;
+ if ($this->size > 0)
+ $this->char = $this->doc[0];
+ }
+
+ // parse html content
+ protected function parse()
+ {
+ if (($s = $this->copy_until_char('<')) === '')
+ return $this->read_tag();
+
+ // text
+ $node = new simple_html_dom_node($this);
+ ++$this->cursor;
+ $node->_[HDOM_INFO_TEXT] = $s;
+ $this->link_nodes($node, false);
+ return true;
+ }
+
+ // PAPERG - dkchou - added this to try to identify the character set of the page we have just parsed so we know better how to spit it out later.
+ // NOTE: IF you provide a routine called get_last_retrieve_url_contents_content_type which returns the CURLINFO_CONTENT_TYPE fromt he last curl_exec
+ // (or the content_type header fromt eh last transfer), we will parse THAT, and if a charset is specified, we will use it over any other mechanism.
+ protected function parse_charset()
+ {
+ global $debugObject;
+
+ $charset = null;
+
+ if (function_exists('get_last_retrieve_url_contents_content_type'))
+ {
+ $contentTypeHeader = get_last_retrieve_url_contents_content_type();
+ $success = preg_match('/charset=(.+)/', $contentTypeHeader, $matches);
+ if ($success)
+ {
+ $charset = $matches[1];
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLog(2, 'header content-type found charset of: ' . $charset);
+ }
+ }
+ }
+
+ if (empty($charset))
+ {
+ $el = $this->root->find('meta[http-equiv=Content-Type]', 0);
+ if (!empty($el))
+ {
+ $fullvalue = $el->content;
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLog(2, 'meta content-type tag found' . $fullValue);
+ }
+
+ if (!empty($fullvalue))
+ {
+ $success = preg_match('/charset=(.+)/', $fullvalue, $matches);
+ if ($success)
+ {
+ $charset = $matches[1];
+ }
+ else
+ {
+ // If there is a meta tag, and they don't specify the character set, research says that it's typically ISO-8859-1
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLog(2, 'meta content-type tag couldn\'t be parsed. using iso-8859 default.');
+ }
+ $charset = 'ISO-8859-1';
+ }
+ }
+ }
+ }
+
+ // If we couldn't find a charset above, then lets try to detect one based on the text we got...
+ if (empty($charset))
+ {
+ // Have php try to detect the encoding from the text given to us.
+ $charset = mb_detect_encoding($this->root->plaintext . "ascii", $encoding_list = array("UTF-8", "CP1252"));
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLog(2, 'mb_detect found: ' . $charset);
+ }
+
+ // and if this doesn't work... then we need to just wrongheadedly assume it's UTF-8 so that we can move on - cause this will usually give us most of what we need...
+ if ($charset === false)
+ {
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLog(2, 'since mb_detect failed - using default of utf-8');
+ }
+ $charset = 'UTF-8';
+ }
+ }
+
+ // Since CP1252 is a superset, if we get one of it's subsets, we want it instead.
+ if ((strtolower($charset) == strtolower('ISO-8859-1')) || (strtolower($charset) == strtolower('Latin1')) || (strtolower($charset) == strtolower('Latin-1')))
+ {
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLog(2, 'replacing ' . $charset . ' with CP1252 as its a superset');
+ }
+ $charset = 'CP1252';
+ }
+
+ if (is_object($debugObject))
+ {
+ $debugObject->debugLog(1, 'EXIT - ' . $charset);
+ }
+
+ return $this->_charset = $charset;
+ }
+
+ // read tag info
+ protected function read_tag()
+ {
+ if ($this->char !== '<')
+ {
+ $this->root->_[HDOM_INFO_END] = $this->cursor;
+ return false;
+ }
+ $begin_tag_pos = $this->pos;
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ // end tag
+ if ($this->char === '/')
+ {
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ // This represetns the change in the simple_html_dom trunk from revision 180 to 181.
+ // $this->skip($this->token_blank_t);
+ $this->skip($this->token_blank);
+ $tag = $this->copy_until_char('>');
+
+ // skip attributes in end tag
+ if (($pos = strpos($tag, ' ')) !== false)
+ $tag = substr($tag, 0, $pos);
+
+ $parent_lower = strtolower($this->parent->tag);
+ $tag_lower = strtolower($tag);
+
+ if ($parent_lower !== $tag_lower)
+ {
+ if (isset($this->optional_closing_tags[$parent_lower]) && isset($this->block_tags[$tag_lower]))
+ {
+ $this->parent->_[HDOM_INFO_END] = 0;
+ $org_parent = $this->parent;
+
+ while (($this->parent->parent) && strtolower($this->parent->tag) !== $tag_lower)
+ $this->parent = $this->parent->parent;
+
+ if (strtolower($this->parent->tag) !== $tag_lower)
+ {
+ $this->parent = $org_parent; // restore origonal parent
+ if ($this->parent->parent)
+ $this->parent = $this->parent->parent;
+ $this->parent->_[HDOM_INFO_END] = $this->cursor;
+ return $this->as_text_node($tag);
+ }
+ }
+ else if (($this->parent->parent) && isset($this->block_tags[$tag_lower]))
+ {
+ $this->parent->_[HDOM_INFO_END] = 0;
+ $org_parent = $this->parent;
+
+ while (($this->parent->parent) && strtolower($this->parent->tag) !== $tag_lower)
+ $this->parent = $this->parent->parent;
+
+ if (strtolower($this->parent->tag) !== $tag_lower)
+ {
+ $this->parent = $org_parent; // restore origonal parent
+ $this->parent->_[HDOM_INFO_END] = $this->cursor;
+ return $this->as_text_node($tag);
+ }
+ }
+ else if (($this->parent->parent) && strtolower($this->parent->parent->tag) === $tag_lower)
+ {
+ $this->parent->_[HDOM_INFO_END] = 0;
+ $this->parent = $this->parent->parent;
+ }
+ else
+ return $this->as_text_node($tag);
+ }
+
+ $this->parent->_[HDOM_INFO_END] = $this->cursor;
+ if ($this->parent->parent)
+ $this->parent = $this->parent->parent;
+
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ return true;
+ }
+
+ $node = new simple_html_dom_node($this);
+ $node->_[HDOM_INFO_BEGIN] = $this->cursor;
+ ++$this->cursor;
+ $tag = $this->copy_until($this->token_slash);
+ $node->tag_start = $begin_tag_pos;
+
+ // doctype, cdata & comments...
+ if (isset($tag[0]) && $tag[0] === '!')
+ {
+ $node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until_char('>');
+
+ if (isset($tag[2]) && $tag[1] === '-' && $tag[2] === '-')
+ {
+ $node->nodetype = HDOM_TYPE_COMMENT;
+ $node->tag = 'comment';
+ }
+ else
+ {
+ $node->nodetype = HDOM_TYPE_UNKNOWN;
+ $node->tag = 'unknown';
+ }
+ if ($this->char === '>')
+ $node->_[HDOM_INFO_TEXT].='>';
+ $this->link_nodes($node, true);
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ return true;
+ }
+
+ // text
+ if ($pos = strpos($tag, '<') !== false)
+ {
+ $tag = '<' . substr($tag, 0, -1);
+ $node->_[HDOM_INFO_TEXT] = $tag;
+ $this->link_nodes($node, false);
+ $this->char = $this->doc[--$this->pos]; // prev
+ return true;
+ }
+
+ if (!preg_match("/^[\w-:]+$/", $tag))
+ {
+ $node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until('<>');
+ if ($this->char === '<')
+ {
+ $this->link_nodes($node, false);
+ return true;
+ }
+
+ if ($this->char === '>')
+ $node->_[HDOM_INFO_TEXT].='>';
+ $this->link_nodes($node, false);
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ return true;
+ }
+
+ // begin tag
+ $node->nodetype = HDOM_TYPE_ELEMENT;
+ $tag_lower = strtolower($tag);
+ $node->tag = ($this->lowercase) ? $tag_lower : $tag;
+
+ // handle optional closing tags
+ if (isset($this->optional_closing_tags[$tag_lower]))
+ {
+ while (isset($this->optional_closing_tags[$tag_lower][strtolower($this->parent->tag)]))
+ {
+ $this->parent->_[HDOM_INFO_END] = 0;
+ $this->parent = $this->parent->parent;
+ }
+ $node->parent = $this->parent;
+ }
+
+ $guard = 0; // prevent infinity loop
+ $space = array($this->copy_skip($this->token_blank), '', '');
+
+ // attributes
+ do
+ {
+ if ($this->char !== null && $space[0] === '')
+ break;
+ $name = $this->copy_until($this->token_equal);
+ if ($guard === $this->pos)
+ {
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ continue;
+ }
+ $guard = $this->pos;
+
+ // handle endless '<'
+ if ($this->pos >= $this->size - 1 && $this->char !== '>')
+ {
+ $node->nodetype = HDOM_TYPE_TEXT;
+ $node->_[HDOM_INFO_END] = 0;
+ $node->_[HDOM_INFO_TEXT] = '<' . $tag . $space[0] . $name;
+ $node->tag = 'text';
+ $this->link_nodes($node, false);
+ return true;
+ }
+
+ // handle mismatch '<'
+ if ($this->doc[$this->pos - 1] == '<')
+ {
+ $node->nodetype = HDOM_TYPE_TEXT;
+ $node->tag = 'text';
+ $node->attr = array();
+ $node->_[HDOM_INFO_END] = 0;
+ $node->_[HDOM_INFO_TEXT] = substr($this->doc, $begin_tag_pos, $this->pos - $begin_tag_pos - 1);
+ $this->pos -= 2;
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ $this->link_nodes($node, false);
+ return true;
+ }
+
+ if ($name !== '/' && $name !== '')
+ {
+ $space[1] = $this->copy_skip($this->token_blank);
+ $name = $this->restore_noise($name);
+ if ($this->lowercase)
+ $name = strtolower($name);
+ if ($this->char === '=')
+ {
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ $this->parse_attr($node, $name, $space);
+ }
+ else
+ {
+ //no value attr: nowrap, checked selected...
+ $node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_NO;
+ $node->attr[$name] = true;
+ if ($this->char != '>')
+ $this->char = $this->doc[--$this->pos]; // prev
+ }
+ $node->_[HDOM_INFO_SPACE][] = $space;
+ $space = array($this->copy_skip($this->token_blank), '', '');
+ }
+ else
+ break;
+ } while ($this->char !== '>' && $this->char !== '/');
+
+ $this->link_nodes($node, true);
+ $node->_[HDOM_INFO_ENDSPACE] = $space[0];
+
+ // check self closing
+ if ($this->copy_until_char_escape('>') === '/')
+ {
+ $node->_[HDOM_INFO_ENDSPACE] .= '/';
+ $node->_[HDOM_INFO_END] = 0;
+ }
+ else
+ {
+ // reset parent
+ if (!isset($this->self_closing_tags[strtolower($node->tag)]))
+ $this->parent = $node;
+ }
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ // If it's a BR tag, we need to set it's text to the default text.
+ // This way when we see it in plaintext, we can generate formatting that the user wants.
+ if ($node->tag == "br")
+ {
+ $node->_[HDOM_INFO_INNER] = $this->default_br_text;
+ }
+
+ return true;
+ }
+
+ // parse attributes
+ protected function parse_attr($node, $name, &$space)
+ {
+ // Per sourceforge: http://sourceforge.net/tracker/?func=detail&aid=3061408&group_id=218559&atid=1044037
+ // If the attribute is already defined inside a tag, only pay atetntion to the first one as opposed to the last one.
+ if (isset($node->attr[$name]))
+ {
+ return;
+ }
+
+ $space[2] = $this->copy_skip($this->token_blank);
+ switch ($this->char)
+ {
+ case '"':
+ $node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE;
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ $node->attr[$name] = $this->restore_noise($this->copy_until_char_escape('"'));
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ break;
+ case '\'':
+ $node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_SINGLE;
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ $node->attr[$name] = $this->restore_noise($this->copy_until_char_escape('\''));
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ break;
+ default:
+ $node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_NO;
+ $node->attr[$name] = $this->restore_noise($this->copy_until($this->token_attr));
+ }
+ // PaperG: Attributes should not have \r or \n in them, that counts as html whitespace.
+ $node->attr[$name] = str_replace("\r", "", $node->attr[$name]);
+ $node->attr[$name] = str_replace("\n", "", $node->attr[$name]);
+ // PaperG: If this is a "class" selector, lets get rid of the preceeding and trailing space since some people leave it in the multi class case.
+ if ($name == "class")
+ {
+ $node->attr[$name] = trim($node->attr[$name]);
+ }
+ }
+
+ // link node's parent
+ protected function link_nodes(&$node, $is_child)
+ {
+ $node->parent = $this->parent;
+ $this->parent->nodes[] = $node;
+ if ($is_child)
+ $this->parent->children[] = $node;
+ }
+
+ // as a text node
+ protected function as_text_node($tag)
+ {
+ $node = new simple_html_dom_node($this);
+ ++$this->cursor;
+ $node->_[HDOM_INFO_TEXT] = '</' . $tag . '>';
+ $this->link_nodes($node, false);
+ $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ return true;
+ }
+
+ protected function skip($chars)
+ {
+ $this->pos += strspn($this->doc, $chars, $this->pos);
+ $this->char = ($this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ }
+
+ protected function copy_skip($chars)
+ {
+ $pos = $this->pos;
+ $len = strspn($this->doc, $chars, $pos);
+ $this->pos += $len;
+ $this->char = ($this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ if ($len === 0)
+ return '';
+ return substr($this->doc, $pos, $len);
+ }
+
+ protected function copy_until($chars)
+ {
+ $pos = $this->pos;
+ $len = strcspn($this->doc, $chars, $pos);
+ $this->pos += $len;
+ $this->char = ($this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
+ return substr($this->doc, $pos, $len);
+ }
+
+ protected function copy_until_char($char)
+ {
+ if ($this->char === null)
+ return '';
+
+ if (($pos = strpos($this->doc, $char, $this->pos)) === false)
+ {
+ $ret = substr($this->doc, $this->pos, $this->size - $this->pos);
+ $this->char = null;
+ $this->pos = $this->size;
+ return $ret;
+ }
+
+ if ($pos === $this->pos)
+ return '';
+ $pos_old = $this->pos;
+ $this->char = $this->doc[$pos];
+ $this->pos = $pos;
+ return substr($this->doc, $pos_old, $pos - $pos_old);
+ }
+
+ protected function copy_until_char_escape($char)
+ {
+ if ($this->char === null)
+ return '';
+
+ $start = $this->pos;
+ while (1)
+ {
+ if (($pos = strpos($this->doc, $char, $start)) === false)
+ {
+ $ret = substr($this->doc, $this->pos, $this->size - $this->pos);
+ $this->char = null;
+ $this->pos = $this->size;
+ return $ret;
+ }
+
+ if ($pos === $this->pos)
+ return '';
+
+ if ($this->doc[$pos - 1] === '\\')
+ {
+ $start = $pos + 1;
+ continue;
+ }
+
+ $pos_old = $this->pos;
+ $this->char = $this->doc[$pos];
+ $this->pos = $pos;
+ return substr($this->doc, $pos_old, $pos - $pos_old);
+ }
+ }
+
+ // remove noise from html content
+ protected function remove_noise($pattern, $remove_tag=false)
+ {
+ $count = preg_match_all($pattern, $this->doc, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
+
+ for ($i = $count - 1; $i > -1; --$i)
+ {
+ $key = '___noise___' . sprintf('% 3d', count($this->noise) + 100);
+ $idx = ($remove_tag) ? 0 : 1;
+ $this->noise[$key] = $matches[$i][$idx][0];
+ $this->doc = substr_replace($this->doc, $key, $matches[$i][$idx][1], strlen($matches[$i][$idx][0]));
+ }
+
+ // reset the length of content
+ $this->size = strlen($this->doc);
+ if ($this->size > 0)
+ $this->char = $this->doc[0];
+ }
+
+ // restore noise to html content
+ function restore_noise($text)
+ {
+ while (($pos = strpos($text, '___noise___')) !== false)
+ {
+ $key = '___noise___' . $text[$pos + 11] . $text[$pos + 12] . $text[$pos + 13];
+ if (isset($this->noise[$key]))
+ $text = substr($text, 0, $pos) . $this->noise[$key] . substr($text, $pos + 14);
+ }
+ return $text;
+ }
+
+ function __toString()
+ {
+ return $this->root->innertext();
+ }
+
+ function __get($name)
+ {
+ switch ($name)
+ {
+ case 'outertext':
+ return $this->root->innertext();
+ case 'innertext':
+ return $this->root->innertext();
+ case 'plaintext':
+ return $this->root->text();
+ case 'charset':
+ return $this->_charset;
+ case 'target_charset':
+ return $this->_target_charset;
+ }
+ }
+
+ // camel naming conventions
+ function childNodes($idx=-1)
+ {
+ return $this->root->childNodes($idx);
+ }
+
+ function firstChild()
+ {
+ return $this->root->first_child();
+ }
+
+ function lastChild()
+ {
+ return $this->root->last_child();
+ }
+
+ function getElementById($id)
+ {
+ return $this->find("#$id", 0);
+ }
+
+ function getElementsById($id, $idx=null)
+ {
+ return $this->find("#$id", $idx);
+ }
+
+ function getElementByTagName($name)
+ {
+ return $this->find($name, 0);
+ }
+
+ function getElementsByTagName($name, $idx=-1)
+ {
+ return $this->find($name, $idx);
+ }
+
+ function loadFile()
+ {
+ $args = func_get_args();
+ $this->load_file($args);
+ }
+}
+?> \ No newline at end of file
diff --git a/WebPush/upload.php b/WebPush/upload.php
new file mode 100644
index 000000000..cf5c2112e
--- /dev/null
+++ b/WebPush/upload.php
@@ -0,0 +1,100 @@
+<?php
+include_once('simple_html_dom.php');
+
+function uploadit($build, $branch, $file, $version, $changes)
+{
+ file_put_contents('status.log', "\nUploading file $file to devbukkit! ", FILE_APPEND);
+ $slug = "essentials";
+ $plugin = "Essentials";
+ $url = "http://ci.earth2me.net/guestAuth/repository/download/$branch/$build:id/$file";
+ $filename = explode('.', $file);
+ $request_url = "http://dev.bukkit.org/server-mods/$slug/upload-file.json";
+
+ include ('apikey.php');
+
+ $params['name'] = $filename[0] . '-' . $version;
+ $params['game_versions'] = 176;
+ $params['change_log'] = $changes;
+ $params['change_markup_type'] = "html";
+ $params['fileurl'] = $url;
+
+ if (stripos($version, 'Dev') !== false)
+ {
+ $params['file_type'] = "a";
+ }
+ elseif (stripos($version, 'Pre') !== false)
+ {
+ $params['file_type'] = "b";
+ }
+ else
+ {
+ $params['file_type'] = "r";
+ }
+
+ $content = file_get_contents($url);
+ file_put_contents($file, $content);
+
+ $params['file'] = '@' . $file;
+
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $request_url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_POST, true);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
+ $result = curl_exec($ch);
+
+ if ($result === false)
+ {
+ $result = curl_error($ch);
+ }
+ elseif ($result == "")
+ {
+ $result = "Success uploading $file - $version";
+ }
+ curl_close($ch);
+
+ file_put_contents('status.log', $result, FILE_APPEND);
+ return true;
+}
+
+function getChanges($job, $project)
+{
+ $commitblacklist = array(
+ 'Merge branch',
+ 'Merge pull',
+ 'Revert',
+ 'Cleanup',
+ );
+
+ $url = "http://ci.earth2me.net/viewLog.html?buildId=$job&tab=buildChangesDiv&buildTypeId=$project&guest=1";
+
+ $html = new simple_html_dom();
+ $html->load_file($url);
+
+ $output = "Change Log:<ul>";
+ foreach ($html->find('.changelist') as $list)
+ {
+ foreach ($list->find('.comment') as $comment)
+ {
+ $text = $comment->innertext;
+ foreach ($commitblacklist as $matchtext)
+ {
+ if (stripos($text, $matchtext) !== FALSE)
+ {
+ $text = "";
+ }
+ }
+ if ($text != "")
+ {
+ $output .= "<li>$text</li>\n";
+ }
+ }
+ }
+ $output .= "</ul>";
+
+ file_put_contents('status.log', "Collected changes! ", FILE_APPEND);
+
+ return $output;
+}
+?>
+
diff --git a/lib/bukkit-0.0.1-SNAPSHOT.jar b/lib/bukkit-0.0.1-SNAPSHOT.jar
index e901b2a2b..1551ebdd5 100644
--- a/lib/bukkit-0.0.1-SNAPSHOT.jar
+++ b/lib/bukkit-0.0.1-SNAPSHOT.jar
Binary files differ
diff --git a/lib/craftbukkit-0.0.1-SNAPSHOT.jar b/lib/craftbukkit-0.0.1-SNAPSHOT.jar
index 548ba80d3..d6d49561d 100644
--- a/lib/craftbukkit-0.0.1-SNAPSHOT.jar
+++ b/lib/craftbukkit-0.0.1-SNAPSHOT.jar
Binary files differ
diff --git a/lib/lombok-0.10.1.jar b/lib/lombok-0.10.1.jar
new file mode 100644
index 000000000..993ebc3c2
--- /dev/null
+++ b/lib/lombok-0.10.1.jar
Binary files differ