diff --git a/src/main/java/mod/chiselsandbits/blueprints/EntityBlueprint.java b/src/main/java/mod/chiselsandbits/blueprints/EntityBlueprint.java index 1358a586..253fb707 100644 --- a/src/main/java/mod/chiselsandbits/blueprints/EntityBlueprint.java +++ b/src/main/java/mod/chiselsandbits/blueprints/EntityBlueprint.java @@ -26,10 +26,11 @@ import mod.chiselsandbits.network.packets.PacketUndo; import mod.chiselsandbits.network.packets.WriteBlueprintPacket; import mod.chiselsandbits.share.ShareGenerator; -import mod.chiselsandbits.share.output.ClipBoardText; +import mod.chiselsandbits.share.output.ClipboardText; import mod.chiselsandbits.share.output.IShareOutput; import mod.chiselsandbits.share.output.LocalPNGFile; import mod.chiselsandbits.share.output.LocalTextFile; +import mod.chiselsandbits.share.output.PasteBin; import mod.chiselsandbits.voxelspace.IVoxelSrc; import mod.chiselsandbits.voxelspace.VoxelCompressedProviderWorld; import mod.chiselsandbits.voxelspace.VoxelOffsetRegion; @@ -413,9 +414,12 @@ private void beginCapture( case IMAGE_FILE_WITH_SCREENSHOT: out = new LocalPNGFile( newFileName( ChiselsAndBits.getConfig().getShareFileOutputFolder(), ".png" ) ); break; + case TEXT_PASTEBIN: + out = new PasteBin(); + break; default: case TEXT_CLIPBOARD: - out = new ClipBoardText(); + out = new ClipboardText(); break; case TEXT_FILE: out = new LocalTextFile( newFileName( ChiselsAndBits.getConfig().getShareFileOutputFolder(), ".txt" ) ); diff --git a/src/main/java/mod/chiselsandbits/commands/Share.java b/src/main/java/mod/chiselsandbits/commands/Share.java index 7e3e93a9..2f710656 100644 --- a/src/main/java/mod/chiselsandbits/commands/Share.java +++ b/src/main/java/mod/chiselsandbits/commands/Share.java @@ -3,7 +3,7 @@ import javax.swing.JFileChooser; import mod.chiselsandbits.share.ShareGenerator; -import mod.chiselsandbits.share.output.ClipBoardText; +import mod.chiselsandbits.share.output.ClipboardText; import mod.chiselsandbits.share.output.IShareOutput; import mod.chiselsandbits.share.output.LocalPNGFile; import mod.chiselsandbits.share.output.LocalTextFile; @@ -80,7 +80,7 @@ else if ( end == null ) else if ( start != null && end != null ) { final World clientWorld = Minecraft.getMinecraft().theWorld; - IShareOutput out = new ClipBoardText(); + IShareOutput out = new ClipboardText(); if ( args.length > 0 ) { diff --git a/src/main/java/mod/chiselsandbits/config/EnumPasteBinPrivacyMode.java b/src/main/java/mod/chiselsandbits/config/EnumPasteBinPrivacyMode.java new file mode 100644 index 00000000..87aa2115 --- /dev/null +++ b/src/main/java/mod/chiselsandbits/config/EnumPasteBinPrivacyMode.java @@ -0,0 +1,16 @@ +package mod.chiselsandbits.config; + +public enum EnumPasteBinPrivacyMode +{ + PUBLIC( "0" ), + UNLISTED( "1" ), + PRIVATE( "2" ); + + public final String value; + + private EnumPasteBinPrivacyMode( + String v ) + { + value = v; + } +} diff --git a/src/main/java/mod/chiselsandbits/config/EnumShareOutput.java b/src/main/java/mod/chiselsandbits/config/EnumShareOutput.java index b636eb15..58df0473 100644 --- a/src/main/java/mod/chiselsandbits/config/EnumShareOutput.java +++ b/src/main/java/mod/chiselsandbits/config/EnumShareOutput.java @@ -4,5 +4,6 @@ public enum EnumShareOutput { TEXT_CLIPBOARD, TEXT_FILE, + TEXT_PASTEBIN, IMAGE_FILE_WITH_SCREENSHOT } diff --git a/src/main/java/mod/chiselsandbits/config/ModConfig.java b/src/main/java/mod/chiselsandbits/config/ModConfig.java index 330419d8..106e0565 100644 --- a/src/main/java/mod/chiselsandbits/config/ModConfig.java +++ b/src/main/java/mod/chiselsandbits/config/ModConfig.java @@ -153,12 +153,24 @@ public class ModConfig extends Configuration @Configured( category = "Client Settings" ) public float radialMenuVolume; - @Configured( category = "Client Settings" ) + @Configured( category = "Blueprint Settings" ) public EnumShareOutput shareOutput; - @Configured( category = "Client Settings" ) + @Configured( category = "Blueprint Settings" ) public String shareFileOutputFolder; + @Configured( category = "Blueprint Settings" ) + public String pasteBinAPIKey; + + @Configured( category = "Blueprint Settings" ) + public String pasteBinUsername; + + @Configured( category = "Blueprint Settings" ) + public String pasteBinPassword; + + @Configured( category = "Blueprint Settings" ) + public EnumPasteBinPrivacyMode pasteBinPrivacyMode; + public String getShareFileOutputFolder() { File f = new File( shareFileOutputFolder ); @@ -449,6 +461,11 @@ private void setDefaults() ShowBitsInJEI = false; enableVivecraftCompatibility = false; enableMCMultipart = true; + + pasteBinAPIKey = ""; + pasteBinUsername = ""; + pasteBinPassword = ""; + pasteBinPrivacyMode = EnumPasteBinPrivacyMode.UNLISTED; } public ModConfig( diff --git a/src/main/java/mod/chiselsandbits/localization/LocalStrings.java b/src/main/java/mod/chiselsandbits/localization/LocalStrings.java index 14fc753d..858f9383 100644 --- a/src/main/java/mod/chiselsandbits/localization/LocalStrings.java +++ b/src/main/java/mod/chiselsandbits/localization/LocalStrings.java @@ -46,6 +46,7 @@ public enum LocalStrings implements ILocalizeable ShareNoFile( "help.share.nofile" ), ShareInvalidData( "help.share.invaliddata" ), ShareNoUrl( "help.share.badurl" ), + ShareURL( "help.share.url" ), HelpPositivePrint( "help.positiveprint" ), LongHelpPositivePrint( "help.positiveprint.long" ), diff --git a/src/main/java/mod/chiselsandbits/share/ShareWorldData.java b/src/main/java/mod/chiselsandbits/share/ShareWorldData.java index 85c29186..7347c8e8 100644 --- a/src/main/java/mod/chiselsandbits/share/ShareWorldData.java +++ b/src/main/java/mod/chiselsandbits/share/ShareWorldData.java @@ -6,9 +6,13 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Base64; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; +import org.apache.commons.lang3.StringEscapeUtils; + import mod.chiselsandbits.chiseledblock.BlockBitInfo; import mod.chiselsandbits.chiseledblock.BlockChiseled; import mod.chiselsandbits.chiseledblock.TileEntityBlockChiseled; @@ -92,9 +96,25 @@ public ShareWorldData( { final String header = "[C&B]("; final String footer = ")[C&B]"; + final String htmlHeader = "[C&B]"; + + // is it html encoded? if it is decode the page before moving on. + if ( data.indexOf( htmlHeader ) != -1 ) + { + data = StringEscapeUtils.unescapeHtml4( data ); + } - int start = data.indexOf( header ); - final int end = data.indexOf( footer ); + int start = -1; + int end = -1; + + // find a valid pattern in side... + Pattern p = Pattern.compile( "\\[C&B\\]\\([A-Za-z0-9+/=\n\r ]+\\)\\[C&B\\]" ); + Matcher m = p.matcher( data ); + if ( m.find() ) + { + start = m.start(); + end = m.end(); + } if ( start == -1 || end == -1 ) { @@ -102,7 +122,7 @@ public ShareWorldData( } start += header.length(); - data = data.substring( start, end ); + data = data.substring( start, end - footer.length() ); final byte[] compressed = Base64.getDecoder().decode( data ); readCompressed( compressed ); } diff --git a/src/main/java/mod/chiselsandbits/share/output/ClipboardImage.java b/src/main/java/mod/chiselsandbits/share/output/ClipboardImage.java deleted file mode 100644 index f1632fab..00000000 --- a/src/main/java/mod/chiselsandbits/share/output/ClipboardImage.java +++ /dev/null @@ -1,103 +0,0 @@ -package mod.chiselsandbits.share.output; - -import java.awt.Image; -import java.awt.Toolkit; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.util.Base64; - -import mod.chiselsandbits.blueprints.BlueprintData; -import mod.chiselsandbits.localization.LocalStrings; -import mod.chiselsandbits.localization.LocalizedMessage; -import mod.chiselsandbits.share.ScreenShotEncoder; - -// this won't work, java refuses to support alpha channel on clipboard. -public class ClipboardImage implements IShareOutput -{ - - byte[] data = null; - - public ClipboardImage() - { - } - - @Override - public LocalizedMessage handleOutput( - final byte[] compressedData, - final BufferedImage screenshot ) throws UnsupportedEncodingException, IOException - { - BufferedImage image = ScreenShotEncoder.encodeScreenshot( screenshot, compressedData ); - - ImageTransferable transferable = new ImageTransferable( image ); - Toolkit.getDefaultToolkit().getSystemClipboard().setContents( transferable, null ); - - // save text version to game blueprint. - final StringBuilder o = new StringBuilder(); - o.append( "[C&B](" ); - o.append( Base64.getEncoder().encodeToString( compressedData ) ); - o.append( ")[C&B]" ); - data = o.toString().getBytes( "UTF-8" ); - - return new LocalizedMessage( LocalStrings.ShareClipboard ); - } - - static class ImageTransferable implements Transferable - { - private Image image; - - public ImageTransferable( - Image image ) - { - this.image = image; - } - - public Object getTransferData( - DataFlavor flavor ) - throws UnsupportedFlavorException - { - if ( isDataFlavorSupported( flavor ) ) - { - return image; - } - else - { - throw new UnsupportedFlavorException( flavor ); - } - } - - public boolean isDataFlavorSupported( - DataFlavor flavor ) - { - return flavor == DataFlavor.imageFlavor; - } - - public DataFlavor[] getTransferDataFlavors() - { - return new DataFlavor[] { DataFlavor.imageFlavor }; - } - - } - - @Override - public BlueprintData getData() - { - BlueprintData bpd = new BlueprintData( null ); - - try - { - bpd.loadData( new ByteArrayInputStream( data ) ); - } - catch ( IOException e ) - { - - } - - return bpd; - } - -} diff --git a/src/main/java/mod/chiselsandbits/share/output/ClipBoardText.java b/src/main/java/mod/chiselsandbits/share/output/ClipboardText.java similarity index 83% rename from src/main/java/mod/chiselsandbits/share/output/ClipBoardText.java rename to src/main/java/mod/chiselsandbits/share/output/ClipboardText.java index e4ccd389..76c5d403 100644 --- a/src/main/java/mod/chiselsandbits/share/output/ClipBoardText.java +++ b/src/main/java/mod/chiselsandbits/share/output/ClipboardText.java @@ -11,7 +11,7 @@ import mod.chiselsandbits.localization.LocalizedMessage; import net.minecraft.client.gui.GuiScreen; -public class ClipBoardText implements IShareOutput +public class ClipboardText implements IShareOutput { String text; @@ -21,12 +21,7 @@ public LocalizedMessage handleOutput( final byte[] compressedData, final BufferedImage screenshot ) throws UnsupportedEncodingException, IOException { - final StringBuilder o = new StringBuilder(); - o.append( "[C&B](" ); - o.append( Base64.getEncoder().encodeToString( compressedData ) ); - o.append( ")[C&B]" ); - GuiScreen.setClipboardString( text = o.toString() ); - + GuiScreen.setClipboardString( text = getShareString( compressedData ) ); return new LocalizedMessage( LocalStrings.ShareClipboard ); } @@ -47,4 +42,14 @@ public BlueprintData getData() return bpd; } + public static String getShareString( + byte[] compressedData ) + { + final StringBuilder o = new StringBuilder(); + o.append( "[C&B](" ); + o.append( Base64.getEncoder().encodeToString( compressedData ) ); + o.append( ")[C&B]" ); + return o.toString(); + } + } diff --git a/src/main/java/mod/chiselsandbits/share/output/LocalTextFile.java b/src/main/java/mod/chiselsandbits/share/output/LocalTextFile.java index d8ed3bd0..4a08223b 100644 --- a/src/main/java/mod/chiselsandbits/share/output/LocalTextFile.java +++ b/src/main/java/mod/chiselsandbits/share/output/LocalTextFile.java @@ -6,7 +6,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.util.Base64; import mod.chiselsandbits.blueprints.BlueprintData; import mod.chiselsandbits.localization.LocalStrings; @@ -30,9 +29,7 @@ public LocalizedMessage handleOutput( final BufferedImage screenshot ) throws UnsupportedEncodingException, IOException { final FileOutputStream o = new FileOutputStream( outFile ); - o.write( new String( "[C&B](" ).getBytes( encoding ) ); - o.write( Base64.getEncoder().encodeToString( compressedData ).getBytes( encoding ) ); - o.write( new String( ")[C&B]" ).getBytes( encoding ) ); + o.write( ClipboardText.getShareString( compressedData ).getBytes( encoding ) ); o.close(); return new LocalizedMessage( LocalStrings.ShareFile, outFile ); diff --git a/src/main/java/mod/chiselsandbits/share/output/PasteBin.java b/src/main/java/mod/chiselsandbits/share/output/PasteBin.java new file mode 100644 index 00000000..e9402a9a --- /dev/null +++ b/src/main/java/mod/chiselsandbits/share/output/PasteBin.java @@ -0,0 +1,175 @@ +package mod.chiselsandbits.share.output; + +import java.awt.image.BufferedImage; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLEncoder; + +import javax.net.ssl.HttpsURLConnection; + +import mod.chiselsandbits.blueprints.BlueprintData; +import mod.chiselsandbits.core.ChiselsAndBits; +import mod.chiselsandbits.localization.LocalStrings; +import mod.chiselsandbits.localization.LocalizedMessage; +import net.minecraft.client.gui.GuiScreen; + +public class PasteBin implements IShareOutput +{ + byte[] data; + String url; + + public PasteBin() + { + + } + + @Override + public LocalizedMessage handleOutput( + final byte[] compressedData, + final BufferedImage screenshot ) throws UnsupportedEncodingException, IOException + { + String UTF8 = java.nio.charset.StandardCharsets.UTF_8.toString(); + String dataStr = ClipboardText.getShareString( compressedData ); + data = dataStr.getBytes( UTF8 ); + + String session = ""; + + if ( hasValue( ChiselsAndBits.getConfig().pasteBinUsername ) && hasValue( ChiselsAndBits.getConfig().pasteBinPassword ) ) + session = login( ChiselsAndBits.getConfig().pasteBinUsername, ChiselsAndBits.getConfig().pasteBinPassword ); + + // JSONP based loading. + url = paste( "loadcallback(\"" + dataStr + "\");", session ); + + String[] parts = url.split( "/" ); + String fileName = parts[parts.length - 1]; + + // append sharing preview url. + GuiScreen.setClipboardString( url + "\n" + "View preview at http://algorithmx2.github.io/Chisels-and-Bits-Sharing/viewer/#" + fileName ); + + return new LocalizedMessage( LocalStrings.ShareURL, url ); + } + + private boolean hasValue( + String val ) + { + return val != null && val.length() > 0; + } + + private String login( + String api_user_name, + String api_user_password ) throws MalformedURLException, IOException + { + String UTF8 = java.nio.charset.StandardCharsets.UTF_8.toString(); + HttpsURLConnection connection = (HttpsURLConnection) ( new URL( "https://pastebin.com/api/api_login.php" ) ).openConnection(); + + StringBuilder encoded = new StringBuilder(); + encoded.append( "api_dev_key=" ); + encoded.append( URLEncoder.encode( ChiselsAndBits.getConfig().pasteBinAPIKey, UTF8 ) ); + encoded.append( "&api_user_name=" ); + encoded.append( URLEncoder.encode( api_user_name, UTF8 ) ); + encoded.append( "&api_user_password=" ); + encoded.append( URLEncoder.encode( api_user_password, UTF8 ) ); + + connection.setDoOutput( true ); + connection.setDoInput( true ); + connection.setRequestMethod( "POST" ); + + OutputStream o = connection.getOutputStream(); + o.write( encoded.toString().getBytes( UTF8 ) ); + o.flush(); + o.close(); + + int code = connection.getResponseCode(); + + if ( code == 200 ) + return readInputStream( connection.getInputStream() ); + + throw new IOException( "Bad Request" ); + } + + private String paste( + String dataStr, + String session ) throws MalformedURLException, IOException + { + String UTF8 = java.nio.charset.StandardCharsets.UTF_8.toString(); + HttpsURLConnection connection = (HttpsURLConnection) ( new URL( "https://pastebin.com/api/api_post.php" ) ).openConnection(); + + StringBuilder encoded = new StringBuilder(); + encoded.append( "api_dev_key=" ); + encoded.append( URLEncoder.encode( ChiselsAndBits.getConfig().pasteBinAPIKey, UTF8 ) ); + encoded.append( "&api_paste_code=" ); + encoded.append( URLEncoder.encode( dataStr, UTF8 ) ); + encoded.append( "&api_paste_private=" ); + encoded.append( URLEncoder.encode( ChiselsAndBits.getConfig().pasteBinPrivacyMode.value, UTF8 ) ); + encoded.append( "&api_option=" ); + encoded.append( URLEncoder.encode( "paste", UTF8 ) ); + encoded.append( "&api_user_key=" ); + encoded.append( URLEncoder.encode( session, UTF8 ) ); + + connection.setDoOutput( true ); + connection.setDoInput( true ); + connection.setRequestMethod( "POST" ); + + OutputStream o = connection.getOutputStream(); + o.write( encoded.toString().getBytes( UTF8 ) ); + o.flush(); + o.close(); + + int code = connection.getResponseCode(); + + if ( code == 200 ) + return readInputStream( connection.getInputStream() ); + + throw new IOException( "Bad Request" ); + } + + private String readInputStream( + InputStream inputStream ) throws IOException + { + BufferedReader in = new BufferedReader( new InputStreamReader( inputStream ) ); + + String inputLine; + StringBuffer response = new StringBuffer(); + + while ( ( inputLine = in.readLine() ) != null ) + { + response.append( inputLine ); + } + + in.close(); + + String out = response.toString(); + + if ( out.indexOf( "Bad API request" ) != -1 ) + { + throw new IOException( out ); + } + + return out; + } + + @Override + public BlueprintData getData() + { + BlueprintData bpd = new BlueprintData( null ); + + try + { + bpd.setURLSource( new URL( url ) ); + bpd.loadData( data ); + } + catch ( IOException e ) + { + + } + + return bpd; + } + +} diff --git a/src/main/resources/assets/chiselsandbits/lang/en_us.lang b/src/main/resources/assets/chiselsandbits/lang/en_us.lang index cfbbe43b..8ca6947d 100644 --- a/src/main/resources/assets/chiselsandbits/lang/en_us.lang +++ b/src/main/resources/assets/chiselsandbits/lang/en_us.lang @@ -186,6 +186,7 @@ mod.chiselsandbits.help.share.file=Saved blueprint as {} mod.chiselsandbits.help.share.nofile=Unable to load selected file. mod.chiselsandbits.help.share.invaliddata=File was invalid and could not be loaded. mod.chiselsandbits.help.share.badurl=Unable to download file at the provided url. +mod.chiselsandbits.help.share.url=Copied {} to clipboard. mod.chiselsandbits.config.maxUndoLevel=Max Undo Level mod.chiselsandbits.config.persistCreativeClipboard=Save/Load Creative Clipboard Contents @@ -219,6 +220,13 @@ mod.chiselsandbits.config.enableTapeMeasure_Bit=Enable Mode - Bit mod.chiselsandbits.config.enableTapeMeasure_Block=Enable Mode - Block mod.chiselsandbits.config.enableTapeMeasure_Distance=Enable Mode - Distance +mod.chiselsandbits.config.shareOutput=Share Save Method +mod.chiselsandbits.config.shareFileOutputFolder=Share Files Save To +mod.chiselsandbits.config.pasteBinAPIKey=PasteBin - API Key +mod.chiselsandbits.config.pasteBinUsername=PasteBin - Username +mod.chiselsandbits.config.pasteBinPassword=PasteBin - Password +mod.chiselsandbits.config.pasteBinPrivacyMode=PasteBin - Privacy Mode + mod.chiselsandbits.config.enableToolbarIcons=Show mode icon for chisel in tool bar mod.chiselsandbits.config.perChiselMode=Store selected mode per chisel mod.chiselsandbits.config.chatModeNotification=Notify mode changes with chat