Modernize pastebin code.

This commit is contained in:
sk89q 2014-12-30 17:41:56 -08:00
parent 53fdff1d9b
commit 18ec3db1bb
7 changed files with 704 additions and 149 deletions

View File

@ -144,6 +144,12 @@
<scope>compile</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.khelekore</groupId>

View File

@ -19,16 +19,14 @@
package com.sk89q.worldguard.bukkit.commands;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.sk89q.minecraft.util.commands.*;
import com.sk89q.worldguard.bukkit.ConfigurationManager;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.bukkit.util.LoggerToChatHandler;
import com.sk89q.worldguard.bukkit.util.ReportWriter;
import com.sk89q.worldguard.util.PastebinPoster;
import com.sk89q.worldguard.util.PastebinPoster.PasteCallback;
import com.sk89q.worldguard.util.paste.Pastebin;
import com.sk89q.worldguard.util.task.Task;
import com.sk89q.worldguard.util.task.TaskStateComparator;
import org.bukkit.Bukkit;
@ -39,12 +37,16 @@
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class WorldGuardCommands {
private static final Logger log = Logger.getLogger(WorldGuardCommands.class.getCanonicalName());
private final WorldGuardPlugin plugin;
public WorldGuardCommands(WorldGuardPlugin plugin) {
@ -117,16 +119,17 @@ public void report(CommandContext args, final CommandSender sender) throws Comma
plugin.checkPermission(sender, "worldguard.report.pastebin");
sender.sendMessage(ChatColor.YELLOW + "Now uploading to Pastebin...");
PastebinPoster.paste(report.toString(), new PasteCallback() {
public void handleSuccess(String url) {
// Hope we don't have a thread safety issue here
sender.sendMessage(ChatColor.YELLOW + "WorldGuard report (1 hour): " + url);
Futures.addCallback(new Pastebin().paste(report.toString()), new FutureCallback<URL>() {
@Override
public void onSuccess(URL url) {
sender.sendMessage(ChatColor.YELLOW + "WorldGuard report: " + url);
}
public void handleError(String err) {
// Hope we don't have a thread safety issue here
sender.sendMessage(ChatColor.YELLOW + "WorldGuard report pastebin error: " + err);
@Override
public void onFailure(Throwable throwable) {
log.log(Level.WARNING, "Failed to submit pastebin", throwable);
sender.sendMessage(ChatColor.RED + "The WorldGuard report could not be saved to a pastebin service. Please see console for the error.");
}
});
}

View File

@ -1,134 +0,0 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
public final class PastebinPoster {
private static final int CONNECT_TIMEOUT = 5000;
private static final int READ_TIMEOUT = 5000;
private PastebinPoster() {
}
public static void paste(String code, PasteCallback callback) {
PasteProcessor processor = new PasteProcessor(code, callback);
Thread thread = new Thread(processor);
thread.start();
}
public static interface PasteCallback {
public void handleSuccess(String url);
public void handleError(String err);
}
private static class PasteProcessor implements Runnable {
private String code;
private PasteCallback callback;
public PasteProcessor(String code, PasteCallback callback) {
this.code = code;
this.callback = callback;
}
public void run() {
HttpURLConnection conn = null;
OutputStream out = null;
InputStream in = null;
try {
URL url = new URL("http://pastebin.com/api/api_post.php");
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(READ_TIMEOUT);
conn.setRequestMethod("POST");
conn.addRequestProperty("Content-type",
"application/x-www-form-urlencoded");
conn.setInstanceFollowRedirects(false);
conn.setDoOutput(true);
out = conn.getOutputStream();
out.write(("api_option=paste"
+ "&api_dev_key=" + URLEncoder.encode("4867eae74c6990dbdef07c543cf8f805", "utf-8")
+ "&api_paste_code=" + URLEncoder.encode(code, "utf-8")
+ "&api_paste_private=" + URLEncoder.encode("0", "utf-8")
+ "&api_paste_name=" + URLEncoder.encode("", "utf-8")
+ "&api_paste_expire_date=" + URLEncoder.encode("1D", "utf-8")
+ "&api_paste_format=" + URLEncoder.encode("text", "utf-8")
+ "&api_user_key=" + URLEncoder.encode("", "utf-8")).getBytes());
out.flush();
out.close();
if (conn.getResponseCode() == 200) {//Get Response
in = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
StringBuilder response = new StringBuilder();
while ((line = reader.readLine()) != null) {
response.append(line);
response.append("\r\n");
}
reader.close();
String result = response.toString().trim();
if (result.matches("^https?://.*")) {
callback.handleSuccess(result.trim());
} else {
String err =result.trim();
if (err.length() > 100) {
err = err.substring(0, 100);
}
callback.handleError(err);
}
} else {
callback.handleError("didn't get a 200 response code!");
}
} catch (IOException e) {
callback.handleError(e.getMessage());
} finally {
if (conn != null) {
conn.disconnect();
}
if (in != null) {
try {
in.close();
} catch (IOException ignored) {
}
}
if (out != null) {
try {
out.close();
} catch (IOException ignored) {
}
}
}
}
}
}

View File

@ -0,0 +1,496 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.util.net;
import com.sk89q.worldguard.util.io.Closer;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HttpRequest implements Closeable {
private static final int CONNECT_TIMEOUT = 1000 * 5;
private static final int READ_TIMEOUT = 1000 * 5;
private static final int READ_BUFFER_SIZE = 1024 * 8;
private final Map<String, String> headers = new HashMap<String, String>();
private final String method;
private final URL url;
private String contentType;
private byte[] body;
private HttpURLConnection conn;
private InputStream inputStream;
private long contentLength = -1;
private long readBytes = 0;
/**
* Create a new HTTP request.
*
* @param method the method
* @param url the URL
*/
private HttpRequest(String method, URL url) {
this.method = method;
this.url = url;
}
/**
* Submit data.
*
* @return this object
*/
public HttpRequest body(String data) {
body = data.getBytes();
return this;
}
/**
* Submit form data.
*
* @param form the form
* @return this object
*/
public HttpRequest bodyForm(Form form) {
contentType = "application/x-www-form-urlencoded";
body = form.toString().getBytes();
return this;
}
/**
* Add a header.
*
* @param key the header key
* @param value the header value
* @return this object
*/
public HttpRequest header(String key, String value) {
if (key.equalsIgnoreCase("Content-Type")) {
contentType = value;
} else {
headers.put(key, value);
}
return this;
}
/**
* Execute the request.
* <p/>
* After execution, {@link #close()} should be called.
*
* @return this object
* @throws java.io.IOException on I/O error
*/
public HttpRequest execute() throws IOException {
boolean successful = false;
try {
if (conn != null) {
throw new IllegalArgumentException("Connection already executed");
}
conn = (HttpURLConnection) reformat(url).openConnection();
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Java)");
if (body != null) {
conn.setRequestProperty("Content-Type", contentType);
conn.setRequestProperty("Content-Length", Integer.toString(body.length));
conn.setDoInput(true);
}
for (Map.Entry<String, String> entry : headers.entrySet()) {
conn.setRequestProperty(entry.getKey(), entry.getValue());
}
conn.setRequestMethod(method);
conn.setUseCaches(false);
conn.setDoOutput(true);
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(READ_TIMEOUT);
conn.connect();
if (body != null) {
DataOutputStream out = new DataOutputStream(conn.getOutputStream());
out.write(body);
out.flush();
out.close();
}
inputStream = conn.getResponseCode() == HttpURLConnection.HTTP_OK ?
conn.getInputStream() : conn.getErrorStream();
successful = true;
} finally {
if (!successful) {
close();
}
}
return this;
}
/**
* Require that the response code is one of the given response codes.
*
* @param codes a list of codes
* @return this object
* @throws java.io.IOException if there is an I/O error or the response code is not expected
*/
public HttpRequest expectResponseCode(int... codes) throws IOException {
int responseCode = getResponseCode();
for (int code : codes) {
if (code == responseCode) {
return this;
}
}
close();
throw new IOException("Did not get expected response code, got " + responseCode + " for " + url);
}
/**
* Get the response code.
*
* @return the response code
* @throws java.io.IOException on I/O error
*/
public int getResponseCode() throws IOException {
if (conn == null) {
throw new IllegalArgumentException("No connection has been made");
}
return conn.getResponseCode();
}
/**
* Get the input stream.
*
* @return the input stream
*/
public InputStream getInputStream() {
return inputStream;
}
/**
* Buffer the returned response.
*
* @return the buffered response
* @throws java.io.IOException on I/O error
* @throws InterruptedException on interruption
*/
public BufferedResponse returnContent() throws IOException, InterruptedException {
if (inputStream == null) {
throw new IllegalArgumentException("No input stream available");
}
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int b = 0;
while ((b = inputStream.read()) != -1) {
bos.write(b);
}
return new BufferedResponse(bos.toByteArray());
} finally {
close();
}
}
/**
* Save the result to a file.
*
* @param file the file
* @return this object
* @throws java.io.IOException on I/O error
* @throws InterruptedException on interruption
*/
public HttpRequest saveContent(File file) throws IOException, InterruptedException {
Closer closer = Closer.create();
try {
FileOutputStream fos = closer.register(new FileOutputStream(file));
BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos));
saveContent(bos);
} finally {
closer.close();
}
return this;
}
/**
* Save the result to an output stream.
*
* @param out the output stream
* @return this object
* @throws java.io.IOException on I/O error
* @throws InterruptedException on interruption
*/
public HttpRequest saveContent(OutputStream out) throws IOException, InterruptedException {
BufferedInputStream bis;
try {
String field = conn.getHeaderField("Content-Length");
if (field != null) {
long len = Long.parseLong(field);
if (len >= 0) { // Let's just not deal with really big numbers
contentLength = len;
}
}
} catch (NumberFormatException ignored) {
}
try {
bis = new BufferedInputStream(inputStream);
byte[] data = new byte[READ_BUFFER_SIZE];
int len = 0;
while ((len = bis.read(data, 0, READ_BUFFER_SIZE)) >= 0) {
out.write(data, 0, len);
readBytes += len;
}
} finally {
close();
}
return this;
}
@Override
public void close() throws IOException {
if (conn != null) conn.disconnect();
}
/**
* Perform a GET request.
*
* @param url the URL
* @return a new request object
*/
public static HttpRequest get(URL url) {
return request("GET", url);
}
/**
* Perform a POST request.
*
* @param url the URL
* @return a new request object
*/
public static HttpRequest post(URL url) {
return request("POST", url);
}
/**
* Perform a request.
*
* @param method the method
* @param url the URL
* @return a new request object
*/
public static HttpRequest request(String method, URL url) {
return new HttpRequest(method, url);
}
/**
* Create a new {@link java.net.URL} and throw a {@link RuntimeException} if the URL
* is not valid.
*
* @param url the url
* @return a URL object
* @throws RuntimeException if the URL is invalid
*/
public static URL url(String url) {
try {
return new URL(url);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
/**
* URL may contain spaces and other nasties that will cause a failure.
*
* @param existing the existing URL to transform
* @return the new URL, or old one if there was a failure
*/
private static URL reformat(URL existing) {
try {
URL url = new URL(existing.toString());
URI uri = new URI(
url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(),
url.getPath(), url.getQuery(), url.getRef());
url = uri.toURL();
return url;
} catch (MalformedURLException e) {
return existing;
} catch (URISyntaxException e) {
return existing;
}
}
/**
* Used with {@link #bodyForm(Form)}.
*/
public final static class Form {
public final List<String> elements = new ArrayList<String>();
private Form() {
}
/**
* Add a key/value to the form.
*
* @param key the key
* @param value the value
* @return this object
*/
public Form add(String key, String value) {
try {
elements.add(URLEncoder.encode(key, "UTF-8") +
"=" + URLEncoder.encode(value, "UTF-8"));
return this;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
boolean first = true;
for (String element : elements) {
if (first) {
first = false;
} else {
builder.append("&");
}
builder.append(element);
}
return builder.toString();
}
/**
* Create a new form.
*
* @return a new form
*/
public static Form create() {
return new Form();
}
}
/**
* Used to buffer the response in memory.
*/
public class BufferedResponse {
private final byte[] data;
private BufferedResponse(byte[] data) {
this.data = data;
}
/**
* Return the result as bytes.
*
* @return the data
*/
public byte[] asBytes() {
return data;
}
/**
* Return the result as a string.
*
* @param encoding the encoding
* @return the string
* @throws java.io.IOException on I/O error
*/
public String asString(String encoding) throws IOException {
return new String(data, encoding);
}
/**
* Return the result as an instance of the given class that has been
* deserialized from a XML payload.
*
* @return the object
* @throws java.io.IOException on I/O error
*/
@SuppressWarnings("unchecked")
public <T> T asXml(Class<T> cls) throws IOException {
try {
JAXBContext context = JAXBContext.newInstance(cls);
Unmarshaller um = context.createUnmarshaller();
return (T) um.unmarshal(new ByteArrayInputStream(data));
} catch (JAXBException e) {
throw new IOException(e);
}
}
/**
* Save the result to a file.
*
* @param file the file
* @return this object
* @throws java.io.IOException on I/O error
* @throws InterruptedException on interruption
*/
public BufferedResponse saveContent(File file) throws IOException, InterruptedException {
Closer closer = Closer.create();
file.getParentFile().mkdirs();
try {
FileOutputStream fos = closer.register(new FileOutputStream(file));
BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos));
saveContent(bos);
} finally {
closer.close();
}
return this;
}
/**
* Save the result to an output stream.
*
* @param out the output stream
* @return this object
* @throws java.io.IOException on I/O error
* @throws InterruptedException on interruption
*/
public BufferedResponse saveContent(OutputStream out) throws IOException, InterruptedException {
out.write(data);
return this;
}
}
}

View File

@ -0,0 +1,110 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.util.paste;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.sk89q.worldguard.util.net.HttpRequest;
import com.sk89q.worldguard.util.net.HttpRequest.Form;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Pastebin implements Paster {
private static final Pattern URL_PATTERN = Pattern.compile("https?://pastebin.com/([^/]+)$");
private boolean mungingLinks = true;
public boolean isMungingLinks() {
return mungingLinks;
}
public void setMungingLinks(boolean mungingLinks) {
this.mungingLinks = mungingLinks;
}
@Override
public ListenableFuture<URL> paste(String content) {
if (mungingLinks) {
content = content.replaceAll("http://", "http_//");
}
return Pasters.getExecutor().submit(new PasteTask(content));
}
private final class PasteTask implements Callable<URL> {
private final String content;
private PasteTask(String content) {
this.content = content;
}
@Override
public URL call() throws IOException, InterruptedException {
Form form = Form.create();
form.add("api_option", "paste");
form.add("api_dev_key", "4867eae74c6990dbdef07c543cf8f805");
form.add("api_paste_code", content);
form.add("api_paste_private", "0");
form.add("api_paste_name", "");
form.add("api_paste_expire_date", "1W");
form.add("api_paste_format", "text");
form.add("api_user_key", "");
URL url = HttpRequest.url("http://pastebin.com/api/api_post.php");
String result = HttpRequest.post(url)
.bodyForm(form)
.execute()
.expectResponseCode(200)
.returnContent()
.asString("UTF-8").trim();
Matcher m = URL_PATTERN.matcher(result);
if (m.matches()) {
return new URL("http://pastebin.com/raw.php?i=" + m.group(1));
} else if (result.matches("^https?://.+")) {
return new URL(result);
} else {
throw new IOException("Failed to save paste; instead, got: " + result);
}
}
}
public static void main(String[] args) {
Futures.addCallback(new Pastebin().paste("hi there"), new FutureCallback<URL>() {
@Override
public void onSuccess(URL url) {
System.out.println("got: " + url);
}
@Override
public void onFailure(Throwable throwable) {
throwable.printStackTrace();
}
});
}
}

View File

@ -0,0 +1,30 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.util.paste;
import com.google.common.util.concurrent.ListenableFuture;
import java.net.URL;
public interface Paster {
ListenableFuture<URL> paste(String content);
}

View File

@ -0,0 +1,44 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.util.paste;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
final class Pasters {
private static final ListeningExecutorService executor =
MoreExecutors.listeningDecorator(
new ThreadPoolExecutor(0, Integer.MAX_VALUE,
2L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>()));
private Pasters() {
}
static ListeningExecutorService getExecutor() {
return executor;
}
}