Alllow downloading images by URL

Check file name to make sure people don't overwrite C:\somefile or /etc/passwd
Right clicking air cancels placement
This commit is contained in:
Guntram Blohm 2019-04-06 11:00:52 +02:00
parent 20517bfa16
commit 265c9d05e1
6 changed files with 193 additions and 11 deletions

View File

@ -0,0 +1,39 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package de.craftlancer.imagemaps;
import java.util.List;
import org.bukkit.event.Listener;
import org.bukkit.scheduler.BukkitRunnable;
/**
*
* @author gbl
*/
public class ImageDownloadCompleteNotifier extends BukkitRunnable implements Listener {
private ImageMaps plugin;
public ImageDownloadCompleteNotifier(ImageMaps plugin) {
this.plugin=plugin;
}
@Override
public void run() {
List<ImageDownloadTask> tasks = plugin.getDownloadTasks();
for (ImageDownloadTask task: tasks) {
if (task.isDone()) {
tasks.remove(task);
try {
task.getSender().sendMessage("Download "+task.getURL()+": "+task.getResult());
} catch (Exception e) {
// ignore it, the sender might have logged out
}
return; // one message per tick, and no ConcurrentModificationExceptions
}
}
}
}

View File

@ -0,0 +1,109 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package de.craftlancer.imagemaps;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.concurrent.CompletableFuture;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
/**
*
* @author gbl
*/
public class ImageDownloadTask implements Runnable {
private JavaPlugin plugin;
private String filename;
private String downloadUrl;
private CommandSender sender;
private CompletableFuture future;
ImageDownloadTask(ImageMaps plugin, String url, String filename, CommandSender sender) {
this.plugin=plugin;
this.sender=sender;
this.downloadUrl=url;
this.filename=filename;
future=CompletableFuture.runAsync(this);
}
public CommandSender getSender() {
return sender;
}
public boolean isDone() {
return future.isDone();
}
public String getResult() {
try {
return (future.isDone() ? (String) future.get() : null);
} catch (Exception ex) {
return "Exception when getting result";
}
}
public String getURL() {
return this.downloadUrl;
}
@Override
public void run() {
ReadableByteChannel in = null;
FileChannel out = null;
InputStream is = null;
try {
URL url=new URL(downloadUrl);
URLConnection connection=url.openConnection();
if (!(connection instanceof HttpURLConnection)) {
future.complete("Not a http(s) URL");
return;
}
int responseCode = ((HttpURLConnection) connection).getResponseCode();
if (responseCode != 200) {
future.complete("HTTP Status "+responseCode);
return;
}
String mimeType = ((HttpURLConnection) connection).getHeaderField("Content-type");
if (!(mimeType.startsWith("image/"))) {
future.complete("That is a "+mimeType+", not an image");
return;
}
in = Channels.newChannel(is=connection.getInputStream());
out = new FileOutputStream(new File(plugin.getDataFolder()+"/images", filename)).getChannel();
out.transferFrom(in, 0, Long.MAX_VALUE);
future.complete("Download to "+filename+" finished");
} catch (MalformedURLException ex) {
future.complete("URL invalid");
} catch (IOException ex) {
future.complete("IO Exception");
} finally {
close(out);
close(in);
close(is);
}
}
public void close(Closeable c) {
if (c != null) {
try {
c.close();
} catch (IOException ex) {
}
}
}
}

View File

@ -51,7 +51,17 @@ public class ImageMapCommand implements TabExecutor
if (args.length < 1)
return false;
String filename=args[0];
for (int i=0; i<filename.length(); i++) {
if (filename.charAt(i) == '/'
|| filename.charAt(i) == '\\'
|| filename.charAt(i) == ':') {
sender.sendMessage("Sorry, this filename isn't allowed");
return true;
}
}
if(args.length >= 2 && args[1].equalsIgnoreCase("reload"))
{
plugin.reloadImage(args[0]);
@ -65,12 +75,21 @@ public class ImageMapCommand implements TabExecutor
sender.sendMessage("Error getting this image, please consult server logs");
return true;
}
int tileWidth=image.getWidth()+127/128;
int tileHeight=image.getHeight()+127/128;
int tileWidth=(image.getWidth()+ImageMaps.MAP_WIDTH-1)/ImageMaps.MAP_WIDTH;
int tileHeight=(image.getHeight()+ImageMaps.MAP_HEIGHT-1)/ImageMaps.MAP_HEIGHT;
sender.sendMessage("This image is "+tileWidth+" tiles ("+image.getWidth()+" pixels) wide and "+tileHeight+" tiles ("+image.getHeight()+" pixels) high");
return true;
}
if (args.length >= 2 && args[1].equals("download")) {
if (sender.hasPermission("imagemaps.download")) {
plugin.appendDownloadTask(new ImageDownloadTask(plugin, args[2], args[0], sender));
} else {
sender.sendMessage("You don't have download permission");
}
return true;
}
if (!(sender instanceof Player)) {
sender.sendMessage("You need to be a player to do that");
@ -125,8 +144,9 @@ public class ImageMapCommand implements TabExecutor
}
plugin.startPlacing((Player) sender, args[0], fastsend, scalex);
sender.sendMessage("Started placing of " + args[0] + " which needs "+image.getWidth()*scalex+" width and "+image.getHeight()*scaley+" height. Rightclick on a block, that shall be the upper left corner.");
int tileWidth=(int)((image.getWidth()*scalex+0.001)+ImageMaps.MAP_WIDTH-1)/ImageMaps.MAP_WIDTH;
int tileHeight=(int)((image.getHeight()*scaley+0.001)+ImageMaps.MAP_HEIGHT-1)/ImageMaps.MAP_HEIGHT;
sender.sendMessage("Started placing of " + args[0] + " which needs "+image.getWidth()*scalex/ImageMaps.MAP_WIDTH+" width and "+image.getHeight()*scaley/ImageMaps.MAP_HEIGHT+" height. Rightclick on a block, that shall be the upper left corner.");
return true;
}

View File

@ -1,6 +1,5 @@
package de.craftlancer.imagemaps;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;

View File

@ -44,6 +44,7 @@ public class ImageMaps extends JavaPlugin implements Listener {
private Map<String, BufferedImage> images = new HashMap<>();
private List<Integer> sendList = new ArrayList<>();
private FastSendTask sendTask;
private List<ImageDownloadTask> downloadTasks;
@Override
public void onEnable() {
@ -59,6 +60,8 @@ public class ImageMaps extends JavaPlugin implements Listener {
sendTask = new FastSendTask(this, mapsPerSend);
getServer().getPluginManager().registerEvents(sendTask, this);
sendTask.runTaskTimer(this, sendPerTicks, sendPerTicks);
downloadTasks=new ArrayList<>();
new ImageDownloadCompleteNotifier(this).runTaskTimer(this, 20, 20);
}
@Override
@ -135,14 +138,17 @@ public class ImageMaps extends JavaPlugin implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = false)
public void onInteract(PlayerInteractEvent e) {
if (!e.hasBlock())
return;
if (e.getAction() != Action.RIGHT_CLICK_BLOCK)
return;
if (!placing.containsKey(e.getPlayer().getName()))
return;
if (!e.hasBlock()) {
e.getPlayer().sendMessage("Placing cancelled");
placing.remove(e.getPlayer().getName());
return;
}
if (!placeImage(e.getClickedBlock(), e.getBlockFace(), placing.get(e.getPlayer().getName())))
e.getPlayer().sendMessage(ChatColor.RED + "Can't place the image here!\nMake sure the area is large enough, unobstructed and without pre-existing hanging entities.");
@ -309,4 +315,12 @@ public class ImageMaps extends JavaPlugin implements Listener {
}
}
}
public void appendDownloadTask(ImageDownloadTask task) {
this.downloadTasks.add(task);
}
public List<ImageDownloadTask> getDownloadTasks() {
return this.downloadTasks;
}
}

View File

@ -11,4 +11,5 @@ commands:
permissions:
imagemaps.use:
default: op
imagemaps.download:
default: op