Use percent-encoding for replay file names (fixes #71)
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package com.replaymod.core.utils;
|
package com.replaymod.core.utils;
|
||||||
|
|
||||||
|
import com.google.common.net.PercentEscaper;
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
@@ -16,11 +17,14 @@ import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
|||||||
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||||
import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
|
import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
|
||||||
import de.johni0702.minecraft.gui.utils.Colors;
|
import de.johni0702.minecraft.gui.utils.Colors;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import net.minecraft.client.gui.GuiScreen;
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
import net.minecraft.client.network.NetworkPlayerInfo;
|
import net.minecraft.client.network.NetworkPlayerInfo;
|
||||||
import net.minecraft.client.resources.DefaultPlayerSkin;
|
import net.minecraft.client.resources.DefaultPlayerSkin;
|
||||||
import net.minecraft.crash.CrashReport;
|
import net.minecraft.crash.CrashReport;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import org.apache.commons.io.Charsets;
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.lwjgl.input.Keyboard;
|
import org.lwjgl.input.Keyboard;
|
||||||
import org.lwjgl.util.Dimension;
|
import org.lwjgl.util.Dimension;
|
||||||
@@ -36,6 +40,8 @@ import java.awt.image.BufferedImage;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
@@ -137,6 +143,22 @@ public class Utils {
|
|||||||
return mail.matches("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$");
|
return mail.matches("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final PercentEscaper REPLAY_NAME_ENCODER = new PercentEscaper(".-_ ", false);
|
||||||
|
|
||||||
|
public static String replayNameToFileName(String replayName) {
|
||||||
|
return REPLAY_NAME_ENCODER.escape(replayName) + ".mcpr";
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows(UnsupportedEncodingException.class)
|
||||||
|
public static String fileNameToReplayName(String fileName) {
|
||||||
|
String baseName = FilenameUtils.getBaseName(fileName);
|
||||||
|
try {
|
||||||
|
return URLDecoder.decode(baseName, Charsets.UTF_8.name());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return baseName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static ResourceLocation getResourceLocationForPlayerUUID(UUID uuid) {
|
public static ResourceLocation getResourceLocationForPlayerUUID(UUID uuid) {
|
||||||
NetworkPlayerInfo info = getMinecraft().getNetHandler().getPlayerInfo(uuid);
|
NetworkPlayerInfo info = getMinecraft().getNetHandler().getPlayerInfo(uuid);
|
||||||
ResourceLocation skinLocation;
|
ResourceLocation skinLocation;
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
|
|||||||
import de.johni0702.minecraft.gui.utils.Colors;
|
import de.johni0702.minecraft.gui.utils.Colors;
|
||||||
import net.minecraft.crash.CrashReport;
|
import net.minecraft.crash.CrashReport;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
|
||||||
import org.lwjgl.util.ReadableDimension;
|
import org.lwjgl.util.ReadableDimension;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -125,7 +124,7 @@ public class GuiReplayEditor extends GuiScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void save(File inputFile, PacketStream.FilterInfo...filters) {
|
public void save(File inputFile, PacketStream.FilterInfo...filters) {
|
||||||
save(FilenameUtils.getBaseName(inputFile.getName()), (outputFile) -> {
|
save(Utils.fileNameToReplayName(inputFile.getName()), (outputFile) -> {
|
||||||
Studio studio = new ReplayStudio();
|
Studio studio = new ReplayStudio();
|
||||||
File tmpDir = null;
|
File tmpDir = null;
|
||||||
try {
|
try {
|
||||||
@@ -220,7 +219,7 @@ public class GuiReplayEditor extends GuiScreen {
|
|||||||
Utils.error(LOGGER, GuiReplayEditor.this, CrashReport.makeCrashReport(e, "Getting replay folder"), null);
|
Utils.error(LOGGER, GuiReplayEditor.this, CrashReport.makeCrashReport(e, "Getting replay folder"), null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
File targetFile = new File(replayFolder, name + ".mcpr");
|
File targetFile = new File(replayFolder, Utils.replayNameToFileName(name));
|
||||||
if (targetFile.exists()) {
|
if (targetFile.exists()) {
|
||||||
LOGGER.trace("Selected file already exists, asking for confirmation");
|
LOGGER.trace("Selected file already exists, asking for confirmation");
|
||||||
Futures.addCallback(GuiYesNoPopup.open(GuiReplayEditor.this,
|
Futures.addCallback(GuiYesNoPopup.open(GuiReplayEditor.this,
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
|||||||
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||||
import net.minecraft.client.resources.I18n;
|
import net.minecraft.client.resources.I18n;
|
||||||
import net.minecraft.crash.CrashReport;
|
import net.minecraft.crash.CrashReport;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
|
||||||
import org.apache.commons.io.IOCase;
|
import org.apache.commons.io.IOCase;
|
||||||
import org.apache.commons.io.filefilter.SuffixFileFilter;
|
import org.apache.commons.io.filefilter.SuffixFileFilter;
|
||||||
import org.lwjgl.util.Dimension;
|
import org.lwjgl.util.Dimension;
|
||||||
@@ -52,7 +51,7 @@ public class GuiTrimPanel extends GuiPanel {
|
|||||||
|
|
||||||
public final GuiDropdownMenu<File> inputReplays = new GuiDropdownMenu<File>(this)
|
public final GuiDropdownMenu<File> inputReplays = new GuiDropdownMenu<File>(this)
|
||||||
.setMinSize(new Dimension(200, 20)).onSelection(i -> updateSelectedReplay())
|
.setMinSize(new Dimension(200, 20)).onSelection(i -> updateSelectedReplay())
|
||||||
.setToString(f -> f == NO_REPLAY ? "" : FilenameUtils.getBaseName(f.getName()));
|
.setToString(f -> f == NO_REPLAY ? "" : Utils.fileNameToReplayName(f.getName()));
|
||||||
|
|
||||||
public final GuiNumberField startHour = newGuiNumberField();
|
public final GuiNumberField startHour = newGuiNumberField();
|
||||||
public final GuiNumberField startMin = newGuiNumberField();
|
public final GuiNumberField startMin = newGuiNumberField();
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
|||||||
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
|
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
|
||||||
import de.johni0702.minecraft.gui.utils.Colors;
|
import de.johni0702.minecraft.gui.utils.Colors;
|
||||||
import de.johni0702.minecraft.gui.utils.Consumer;
|
import de.johni0702.minecraft.gui.utils.Consumer;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
|
||||||
import org.apache.logging.log4j.core.helpers.Strings;
|
import org.apache.logging.log4j.core.helpers.Strings;
|
||||||
import org.lwjgl.util.Dimension;
|
import org.lwjgl.util.Dimension;
|
||||||
import org.lwjgl.util.ReadableDimension;
|
import org.lwjgl.util.ReadableDimension;
|
||||||
@@ -418,7 +417,7 @@ public class GuiReplayCenter extends GuiScreen {
|
|||||||
this.downloaded = downloaded;
|
this.downloaded = downloaded;
|
||||||
ReplayMetaData metaData = fileInfo.getMetadata();
|
ReplayMetaData metaData = fileInfo.getMetadata();
|
||||||
|
|
||||||
name.setText(ChatFormatting.UNDERLINE + FilenameUtils.getBaseName(fileInfo.getName()));
|
name.setText(ChatFormatting.UNDERLINE + Utils.fileNameToReplayName(fileInfo.getName()));
|
||||||
author.setI18nText("replaymod.gui.center.author",
|
author.setI18nText("replaymod.gui.center.author",
|
||||||
"" + ChatFormatting.GRAY + ChatFormatting.ITALIC, fileInfo.getOwner());
|
"" + ChatFormatting.GRAY + ChatFormatting.ITALIC, fileInfo.getOwner());
|
||||||
if (Strings.isEmpty(metaData.getServerName())) {
|
if (Strings.isEmpty(metaData.getServerName())) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.replaymod.online.gui;
|
|||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
import com.replaymod.core.utils.Utils;
|
||||||
import de.johni0702.minecraft.gui.container.GuiPanel;
|
import de.johni0702.minecraft.gui.container.GuiPanel;
|
||||||
import de.johni0702.minecraft.gui.container.GuiScreen;
|
import de.johni0702.minecraft.gui.container.GuiScreen;
|
||||||
import de.johni0702.minecraft.gui.element.GuiButton;
|
import de.johni0702.minecraft.gui.element.GuiButton;
|
||||||
@@ -27,7 +28,7 @@ public class GuiSaveModifiedReplay extends GuiScreen {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
String resultName = name.getText().trim().replace("[^a-zA-Z0-9\\.\\- ]", "_");
|
String resultName = name.getText().trim().replace("[^a-zA-Z0-9\\.\\- ]", "_");
|
||||||
final File resultFile = new File(file.getParentFile(), resultName + ".mcpr");
|
final File resultFile = new File(file.getParentFile(), Utils.replayNameToFileName(resultName));
|
||||||
if (resultFile.exists()) {
|
if (resultFile.exists()) {
|
||||||
Futures.addCallback(GuiYesNoPopup.open(GuiSaveModifiedReplay.this,
|
Futures.addCallback(GuiYesNoPopup.open(GuiSaveModifiedReplay.this,
|
||||||
new GuiLabel().setI18nText("replaymod.gui.replaymodified.warning1", resultName).setColor(Colors.BLACK),
|
new GuiLabel().setI18nText("replaymod.gui.replaymodified.warning1", resultName).setColor(Colors.BLACK),
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ import net.minecraft.client.settings.KeyBinding;
|
|||||||
import net.minecraft.crash.CrashReport;
|
import net.minecraft.crash.CrashReport;
|
||||||
import net.minecraft.util.ReportedException;
|
import net.minecraft.util.ReportedException;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -135,7 +134,7 @@ public class GuiUploadReplay extends GuiScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply to gui
|
// Apply to gui
|
||||||
name.setText(FilenameUtils.getBaseName(file.getName()));
|
name.setText(Utils.fileNameToReplayName(file.getName()));
|
||||||
int secs = metaData.getDuration() / 1000;
|
int secs = metaData.getDuration() / 1000;
|
||||||
durationLabel.setI18nText("replaymod.gui.upload.duration", secs / 60, secs % 60);
|
durationLabel.setI18nText("replaymod.gui.upload.duration", secs / 60, secs % 60);
|
||||||
hideServerIP.setEnabled(!metaData.isSingleplayer());
|
hideServerIP.setEnabled(!metaData.isSingleplayer());
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.replaymod.recording.handler;
|
|||||||
|
|
||||||
import com.replaymod.core.ReplayMod;
|
import com.replaymod.core.ReplayMod;
|
||||||
import com.replaymod.core.utils.ModCompat;
|
import com.replaymod.core.utils.ModCompat;
|
||||||
|
import com.replaymod.core.utils.Utils;
|
||||||
import com.replaymod.recording.Setting;
|
import com.replaymod.recording.Setting;
|
||||||
import com.replaymod.recording.gui.GuiRecordingOverlay;
|
import com.replaymod.recording.gui.GuiRecordingOverlay;
|
||||||
import com.replaymod.recording.packet.PacketListener;
|
import com.replaymod.recording.packet.PacketListener;
|
||||||
@@ -74,7 +75,7 @@ public class ConnectionEventHandler {
|
|||||||
File folder = core.getReplayFolder();
|
File folder = core.getReplayFolder();
|
||||||
|
|
||||||
String name = sdf.format(Calendar.getInstance().getTime());
|
String name = sdf.format(Calendar.getInstance().getTime());
|
||||||
File currentFile = new File(folder, name + ".mcpr");
|
File currentFile = new File(folder, Utils.replayNameToFileName(name));
|
||||||
ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), currentFile);
|
ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), currentFile);
|
||||||
|
|
||||||
replayFile.writeModInfo(ModCompat.getInstalledNetworkMods());
|
replayFile.writeModInfo(ModCompat.getInstalledNetworkMods());
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import net.minecraft.client.gui.GuiErrorScreen;
|
|||||||
import net.minecraft.client.resources.I18n;
|
import net.minecraft.client.resources.I18n;
|
||||||
import net.minecraft.util.Util;
|
import net.minecraft.util.Util;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
|
||||||
import org.apache.commons.io.IOCase;
|
import org.apache.commons.io.IOCase;
|
||||||
import org.apache.commons.io.filefilter.SuffixFileFilter;
|
import org.apache.commons.io.filefilter.SuffixFileFilter;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
@@ -156,7 +155,7 @@ public class GuiReplayViewer extends GuiScreen {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final File file = list.getSelected().file;
|
final File file = list.getSelected().file;
|
||||||
String name = FilenameUtils.getBaseName(file.getName());
|
String name = Utils.fileNameToReplayName(file.getName());
|
||||||
final GuiTextField nameField = new GuiTextField().setSize(200, 20).setFocused(true).setText(name);
|
final GuiTextField nameField = new GuiTextField().setSize(200, 20).setFocused(true).setText(name);
|
||||||
final GuiYesNoPopup popup = GuiYesNoPopup.open(GuiReplayViewer.this,
|
final GuiYesNoPopup popup = GuiYesNoPopup.open(GuiReplayViewer.this,
|
||||||
new GuiLabel().setI18nText("replaymod.gui.viewer.rename.name").setColor(Colors.BLACK),
|
new GuiLabel().setI18nText("replaymod.gui.viewer.rename.name").setColor(Colors.BLACK),
|
||||||
@@ -172,16 +171,16 @@ public class GuiReplayViewer extends GuiScreen {
|
|||||||
}
|
}
|
||||||
}).onTextChanged(obj -> {
|
}).onTextChanged(obj -> {
|
||||||
popup.getYesButton().setEnabled(!nameField.getText().isEmpty()
|
popup.getYesButton().setEnabled(!nameField.getText().isEmpty()
|
||||||
&& !new File(file.getParentFile(), nameField.getText() + ".mcpr").exists());
|
&& !new File(file.getParentFile(), Utils.replayNameToFileName(nameField.getText())).exists());
|
||||||
});
|
});
|
||||||
Futures.addCallback(popup.getFuture(), new FutureCallback<Boolean>() {
|
Futures.addCallback(popup.getFuture(), new FutureCallback<Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Boolean delete) {
|
public void onSuccess(Boolean delete) {
|
||||||
if (delete) {
|
if (delete) {
|
||||||
// Sanitize their input
|
// Sanitize their input
|
||||||
String name = nameField.getText().trim().replace("[^a-zA-Z0-9\\.\\- ]", "_");
|
String name = nameField.getText().trim();
|
||||||
// This file is what they want
|
// This file is what they want
|
||||||
File targetFile = new File(file.getParentFile(), name + ".mcpr");
|
File targetFile = new File(file.getParentFile(), Utils.replayNameToFileName(name));
|
||||||
try {
|
try {
|
||||||
// Finally, try to move it
|
// Finally, try to move it
|
||||||
FileUtils.moveFile(file, targetFile);
|
FileUtils.moveFile(file, targetFile);
|
||||||
@@ -303,7 +302,7 @@ public class GuiReplayViewer extends GuiScreen {
|
|||||||
public GuiReplayEntry(File file, ReplayMetaData metaData, BufferedImage thumbImage) {
|
public GuiReplayEntry(File file, ReplayMetaData metaData, BufferedImage thumbImage) {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
|
|
||||||
name.setText(ChatFormatting.UNDERLINE + FilenameUtils.getBaseName(file.getName()));
|
name.setText(ChatFormatting.UNDERLINE + Utils.fileNameToReplayName(file.getName()));
|
||||||
if (Strings.isEmpty(metaData.getServerName())) {
|
if (Strings.isEmpty(metaData.getServerName())) {
|
||||||
server.setI18nText("replaymod.gui.iphidden").setColor(Colors.DARK_RED);
|
server.setI18nText("replaymod.gui.iphidden").setColor(Colors.DARK_RED);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user