2019-04-28 13:41:24 +02:00
package world.bentobox.bentobox.managers ;
import java.io.BufferedOutputStream ;
import java.io.File ;
import java.io.FileInputStream ;
import java.io.FileOutputStream ;
2019-05-15 20:16:41 +02:00
import java.io.FileReader ;
import java.io.FileWriter ;
2019-04-28 13:41:24 +02:00
import java.io.IOException ;
2022-09-29 17:44:07 +02:00
import java.nio.charset.StandardCharsets ;
2019-04-28 13:41:24 +02:00
import java.nio.file.Files ;
import java.nio.file.Path ;
import java.nio.file.Paths ;
import java.util.zip.ZipEntry ;
import java.util.zip.ZipInputStream ;
import java.util.zip.ZipOutputStream ;
2019-06-23 07:44:33 +02:00
import org.bukkit.Material ;
import org.bukkit.util.Vector ;
2019-05-15 20:16:41 +02:00
import com.google.gson.Gson ;
import com.google.gson.GsonBuilder ;
2019-04-28 13:41:24 +02:00
import world.bentobox.bentobox.BentoBox ;
import world.bentobox.bentobox.api.user.User ;
2019-05-15 20:16:41 +02:00
import world.bentobox.bentobox.blueprints.Blueprint ;
import world.bentobox.bentobox.blueprints.BlueprintClipboard ;
2019-06-23 07:44:33 +02:00
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock ;
2019-05-15 20:16:41 +02:00
import world.bentobox.bentobox.database.json.BentoboxTypeAdapterFactory ;
2019-04-28 13:41:24 +02:00
/ * *
* @author tastybento
2019-05-15 20:16:41 +02:00
* @since 1 . 5 . 0
2019-04-28 13:41:24 +02:00
* /
2019-05-15 20:16:41 +02:00
public class BlueprintClipboardManager {
2019-04-28 13:41:24 +02:00
2019-05-15 20:16:41 +02:00
private static final String LOAD_ERROR = " Could not load blueprint file - does not exist : " ;
2019-04-28 13:41:24 +02:00
2021-08-30 03:17:21 +02:00
private final File blueprintFolder ;
2019-04-28 13:41:24 +02:00
2019-05-15 20:16:41 +02:00
private BlueprintClipboard clipboard ;
2019-04-28 13:41:24 +02:00
2019-05-15 20:16:41 +02:00
private Gson gson ;
2019-04-28 13:41:24 +02:00
2021-08-30 03:17:21 +02:00
private final BentoBox plugin ;
2019-04-28 13:41:24 +02:00
2019-05-15 20:16:41 +02:00
public BlueprintClipboardManager ( BentoBox plugin , File blueprintFolder ) {
this ( plugin , blueprintFolder , null ) ;
2019-04-28 13:41:24 +02:00
}
2019-05-15 20:16:41 +02:00
public BlueprintClipboardManager ( BentoBox plugin , File blueprintFolder , BlueprintClipboard clipboard ) {
2019-04-28 13:41:24 +02:00
super ( ) ;
this . plugin = plugin ;
2019-05-15 20:16:41 +02:00
if ( ! blueprintFolder . exists ( ) ) {
blueprintFolder . mkdirs ( ) ;
2019-04-28 13:41:24 +02:00
}
2019-05-15 20:16:41 +02:00
this . blueprintFolder = blueprintFolder ;
2019-04-28 13:41:24 +02:00
this . clipboard = clipboard ;
2019-05-15 20:16:41 +02:00
getGson ( ) ;
2019-04-28 13:41:24 +02:00
}
/ * *
* @return the clipboard
* /
2019-05-15 20:16:41 +02:00
public BlueprintClipboard getClipboard ( ) {
2019-04-28 13:41:24 +02:00
return clipboard ;
}
2019-05-15 20:16:41 +02:00
private void getGson ( ) {
GsonBuilder builder = new GsonBuilder ( ) . excludeFieldsWithoutExposeAnnotation ( ) . enableComplexMapKeySerialization ( ) ;
// Disable <>'s escaping etc.
builder . disableHtmlEscaping ( ) ;
// Register adapter factory
builder . registerTypeAdapterFactory ( new BentoboxTypeAdapterFactory ( plugin ) ) ;
gson = builder . create ( ) ;
}
2019-04-28 13:41:24 +02:00
/ * *
* Load a file to clipboard
2019-05-15 20:16:41 +02:00
* @param fileName - filename in blueprints folder
2021-08-30 03:17:38 +02:00
* @throws IOException - if there ' s a load error with unzipping or name
2019-04-28 13:41:24 +02:00
* /
2019-05-15 20:16:41 +02:00
public void load ( String fileName ) throws IOException {
clipboard = new BlueprintClipboard ( loadBlueprint ( fileName ) ) ;
}
/ * *
* Loads a blueprint
2022-09-29 17:44:07 +02:00
* @param fileName - the sanitized filename without the suffix
2019-05-15 20:16:41 +02:00
* @return the blueprint
2019-07-11 22:55:17 +02:00
* @throws IOException exception if there ' s an issue loading or unzipping
2019-05-15 20:16:41 +02:00
* /
public Blueprint loadBlueprint ( String fileName ) throws IOException {
2022-09-29 17:44:07 +02:00
File zipFile = new File ( blueprintFolder , fileName + BlueprintsManager . BLUEPRINT_SUFFIX ) ;
2019-04-28 13:41:24 +02:00
if ( ! zipFile . exists ( ) ) {
plugin . logError ( LOAD_ERROR + zipFile . getName ( ) ) ;
throw new IOException ( LOAD_ERROR + zipFile . getName ( ) ) ;
}
2022-01-02 02:45:05 +01:00
unzip ( zipFile . getCanonicalPath ( ) ) ;
2022-09-29 17:44:07 +02:00
File file = new File ( blueprintFolder , fileName ) ;
2019-04-28 13:41:24 +02:00
if ( ! file . exists ( ) ) {
plugin . logError ( LOAD_ERROR + file . getName ( ) ) ;
2019-05-15 20:16:41 +02:00
throw new IOException ( LOAD_ERROR + file . getName ( ) + " temp file " ) ;
2019-04-28 13:41:24 +02:00
}
2019-05-18 06:02:49 +02:00
Blueprint bp ;
2022-09-29 17:44:07 +02:00
try ( FileReader fr = new FileReader ( file , StandardCharsets . UTF_8 ) ) {
2019-05-18 06:02:49 +02:00
bp = gson . fromJson ( fr , Blueprint . class ) ;
2019-06-23 07:44:33 +02:00
} catch ( Exception e ) {
plugin . logError ( " Blueprint has JSON error: " + zipFile . getName ( ) ) ;
throw new IOException ( " Blueprint has JSON error: " + zipFile . getName ( ) ) ;
2019-05-18 06:02:49 +02:00
}
2019-04-28 13:41:24 +02:00
Files . delete ( file . toPath ( ) ) ;
2019-06-23 07:44:33 +02:00
// Bedrock check and set
if ( bp . getBedrock ( ) = = null ) {
bp . setBedrock ( new Vector ( bp . getxSize ( ) / 2 , bp . getySize ( ) / 2 , bp . getzSize ( ) / 2 ) ) ;
bp . getBlocks ( ) . put ( bp . getBedrock ( ) , new BlueprintBlock ( Material . BEDROCK . createBlockData ( ) . getAsString ( ) ) ) ;
2022-09-29 17:44:07 +02:00
plugin . logWarning ( " Blueprint " + fileName + BlueprintsManager . BLUEPRINT_SUFFIX + " had no bedrock block in it so one was added automatically in the center. You should check it. " ) ;
2019-06-23 07:44:33 +02:00
}
2019-05-15 20:16:41 +02:00
return bp ;
2019-04-28 13:41:24 +02:00
}
/ * *
2019-05-15 20:16:41 +02:00
* Load a blueprint to the clipboard for a user
2019-04-28 13:41:24 +02:00
* @param user - user trying to load
* @param fileName - filename
* @return - < tt > true < / tt > if load is successful , < tt > false < / tt > if not
* /
public boolean load ( User user , String fileName ) {
try {
load ( fileName ) ;
} catch ( IOException e1 ) {
2019-05-15 20:16:41 +02:00
user . sendMessage ( " commands.admin.blueprint.could-not-load " ) ;
2022-09-29 17:44:07 +02:00
plugin . logError ( " Could not load blueprint file: " + fileName + BlueprintsManager . BLUEPRINT_SUFFIX + " " + e1 . getMessage ( ) ) ;
2019-04-28 13:41:24 +02:00
return false ;
}
user . sendMessage ( " general.success " ) ;
return true ;
}
/ * *
2019-05-15 20:16:41 +02:00
* Save the clipboard to a file
* @param user - user who is copying
* @param newName - new name of this blueprint
* @return - true if successful , false if error
2019-04-28 13:41:24 +02:00
* /
2022-09-29 17:44:07 +02:00
public boolean save ( User user , String newName , String displayName )
{
if ( this . clipboard . isFull ( ) )
{
this . clipboard . getBlueprint ( ) . setName ( newName ) ;
this . clipboard . getBlueprint ( ) . setDisplayName ( displayName ) ;
if ( this . saveBlueprint ( this . clipboard . getBlueprint ( ) ) )
{
2021-09-19 02:59:32 +02:00
user . sendMessage ( " general.success " ) ;
return true ;
}
2019-04-28 13:41:24 +02:00
}
2022-09-29 17:44:07 +02:00
2019-05-15 20:16:41 +02:00
user . sendMessage ( " commands.admin.blueprint.could-not-save " , " [message] " , " Could not save temp blueprint file. " ) ;
return false ;
2019-04-28 13:41:24 +02:00
}
/ * *
2019-05-15 20:16:41 +02:00
* Save a blueprint
* @param blueprint - blueprint
* @return true if successful , false if not
2019-04-28 13:41:24 +02:00
* /
2019-05-15 20:16:41 +02:00
public boolean saveBlueprint ( Blueprint blueprint ) {
if ( blueprint . getName ( ) . isEmpty ( ) ) {
plugin . logError ( " Blueprint name was empty - could not save it " ) ;
return false ;
}
2022-09-29 17:44:07 +02:00
File file = new File ( blueprintFolder , blueprint . getName ( ) ) ;
2019-05-15 20:16:41 +02:00
String toStore = gson . toJson ( blueprint , Blueprint . class ) ;
2022-09-29 17:44:07 +02:00
try ( FileWriter fileWriter = new FileWriter ( file , StandardCharsets . UTF_8 ) ) {
2019-05-15 20:16:41 +02:00
fileWriter . write ( toStore ) ;
2019-04-28 13:41:24 +02:00
} catch ( IOException e ) {
2019-05-15 20:16:41 +02:00
plugin . logError ( " Could not save temporary blueprint file: " + file . getName ( ) ) ;
2019-04-28 13:41:24 +02:00
return false ;
}
try {
zip ( file ) ;
} catch ( IOException e ) {
2019-05-15 20:16:41 +02:00
plugin . logError ( " Could not zip temporary blueprint file: " + file . getName ( ) ) ;
2019-04-28 13:41:24 +02:00
return false ;
}
return true ;
}
2019-05-15 20:16:41 +02:00
2019-04-28 13:41:24 +02:00
private void unzip ( final String zipFilePath ) throws IOException {
Path path = Paths . get ( zipFilePath ) ;
if ( ! ( path . toFile ( ) . exists ( ) ) ) {
throw new IOException ( " No file exists! " ) ;
}
try ( ZipInputStream zipInputStream = new ZipInputStream ( new FileInputStream ( zipFilePath ) ) ) {
ZipEntry entry = zipInputStream . getNextEntry ( ) ;
while ( entry ! = null ) {
Path filePath = Paths . get ( path . getParent ( ) . toString ( ) , entry . getName ( ) ) ;
if ( ! entry . isDirectory ( ) ) {
unzipFiles ( zipInputStream , filePath ) ;
} else {
2022-01-02 02:45:05 +01:00
if ( ! filePath . startsWith ( blueprintFolder . getCanonicalPath ( ) ) ) {
2021-09-13 06:53:26 +02:00
throw new IOException ( " Entry is outside of the target directory " ) ;
}
2019-04-28 13:41:24 +02:00
Files . createDirectories ( filePath ) ;
}
zipInputStream . closeEntry ( ) ;
entry = zipInputStream . getNextEntry ( ) ;
}
}
}
2019-05-15 20:16:41 +02:00
private void unzipFiles ( final ZipInputStream zipInputStream , final Path unzipFilePath ) throws IOException {
2024-03-10 19:46:44 +01:00
// Prevent directory traversal attacks by normalizing the path
if ( ! unzipFilePath . startsWith ( blueprintFolder . getCanonicalFile ( ) . toPath ( ) . normalize ( ) ) ) {
throw new IOException (
" Blueprint file is trying to write outside of the target directory! Blocked attempt to write to "
+ unzipFilePath . toString ( ) ) ;
2021-09-13 06:53:26 +02:00
}
2022-01-02 02:45:05 +01:00
try ( BufferedOutputStream bos = new BufferedOutputStream ( new FileOutputStream ( unzipFilePath . toFile ( ) . getCanonicalPath ( ) ) ) ) {
2019-05-15 20:16:41 +02:00
byte [ ] bytesIn = new byte [ 1024 ] ;
int read ;
while ( ( read = zipInputStream . read ( bytesIn ) ) ! = - 1 ) {
bos . write ( bytesIn , 0 , read ) ;
}
}
}
2019-04-28 13:41:24 +02:00
private void zip ( File targetFile ) throws IOException {
2022-01-02 02:45:05 +01:00
try ( ZipOutputStream zipOutputStream = new ZipOutputStream ( new FileOutputStream ( targetFile . getCanonicalPath ( ) + BlueprintsManager . BLUEPRINT_SUFFIX ) ) ) {
2019-04-28 13:41:24 +02:00
zipOutputStream . putNextEntry ( new ZipEntry ( targetFile . getName ( ) ) ) ;
try ( FileInputStream inputStream = new FileInputStream ( targetFile ) ) {
final byte [ ] buffer = new byte [ 1024 ] ;
int length ;
while ( ( length = inputStream . read ( buffer ) ) > = 0 ) {
zipOutputStream . write ( buffer , 0 , length ) ;
}
2019-05-18 06:02:49 +02:00
}
try {
Files . delete ( targetFile . toPath ( ) ) ;
} catch ( Exception e ) {
plugin . logError ( e . getMessage ( ) ) ;
2019-04-28 13:41:24 +02:00
}
}
}
}