Try raw file name before percent encoding it (closes #536)

This commit is contained in:
Jonas Herzig
2022-03-02 18:40:46 +01:00
parent d178c0980e
commit f9c58a1165
4 changed files with 51 additions and 10 deletions

View File

@@ -55,10 +55,12 @@ import javax.net.ssl.TrustManagerFactory;
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.OutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.FileAttribute;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.KeyStore; import java.security.KeyStore;
@@ -169,8 +171,45 @@ public class Utils {
private static final PercentEscaper REPLAY_NAME_ENCODER = new PercentEscaper(".-_ ", false); private static final PercentEscaper REPLAY_NAME_ENCODER = new PercentEscaper(".-_ ", false);
public static String replayNameToFileName(String replayName) { public static Path replayNameToPath(Path folder, String replayName) {
return REPLAY_NAME_ENCODER.escape(replayName) + ".mcpr"; // If we can, prefer directly using the replay name as the file name
if (isUsable(folder, replayName + ".mcpr")) {
return folder.resolve(replayName + ".mcpr");
} else {
// otherwise, fall back to percent encoding
return folder.resolve(REPLAY_NAME_ENCODER.escape(replayName) + ".mcpr");
}
}
/**
* Checks whether a given file name is actually usable with the file system / operating system at the given folder.
*/
private static boolean isUsable(Path folder, String fileName) {
Path path = folder.resolve(fileName);
if (Files.exists(path)) {
return true; // if it already exits, it's definitely usable
}
// Otherwise, there's no sure way to know, so we just gotta try
try (OutputStream outputStream = Files.newOutputStream(path, StandardOpenOption.CREATE_NEW)) {
outputStream.flush();
} catch (IOException e) {
return false;
}
// Looking good, but now we gotta clean up that mess (and Anti-Virus / Cloud Sync are know to lock them)
int attempts = 0;
while (true) {
try {
Files.delete(path);
return true;
} catch (IOException e) {
if (attempts++ > 100) {
LOGGER.warn("Repeatedly failed to clean up temporary test file at " + path + ": ", e);
return false; // while we were able to use it, it's taken now and we can't get it back
}
}
}
} }
public static String fileNameToReplayName(String fileName) { public static String fileNameToReplayName(String fileName) {

View File

@@ -155,9 +155,9 @@ public class GuiSavingReplay {
try { try {
Path replaysFolder = core.folders.getReplayFolder(); Path replaysFolder = core.folders.getReplayFolder();
Path newPath = replaysFolder.resolve(Utils.replayNameToFileName(newName)); Path newPath = Utils.replayNameToPath(replaysFolder, newName);
for (int i = 1; Files.exists(newPath); i++) { for (int i = 1; Files.exists(newPath); i++) {
newPath = replaysFolder.resolve(Utils.replayNameToFileName(newName + " (" + i + ")")); newPath = Utils.replayNameToPath(replaysFolder, newName + " (" + i + ")");
} }
Files.move(path, newPath); Files.move(path, newPath);
} catch (IOException e) { } catch (IOException e) {

View File

@@ -127,7 +127,7 @@ public class ConnectionEventHandler {
} }
String name = sdf.format(Calendar.getInstance().getTime()); String name = sdf.format(Calendar.getInstance().getTime());
Path outputPath = core.folders.getRecordingFolder().resolve(Utils.replayNameToFileName(name)); Path outputPath = Utils.replayNameToPath(core.folders.getRecordingFolder(), name);
ReplayFile replayFile = core.files.open(outputPath); ReplayFile replayFile = core.files.open(outputPath);
replayFile.writeModInfo(ModCompat.getInstalledNetworkMods()); replayFile.writeModInfo(ModCompat.getInstalledNetworkMods());

View File

@@ -49,6 +49,8 @@ import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -132,8 +134,8 @@ public class GuiReplayViewer extends GuiScreen {
public final GuiButton renameButton = new GuiButton().onClick(new Runnable() { public final GuiButton renameButton = new GuiButton().onClick(new Runnable() {
@Override @Override
public void run() { public void run() {
final File file = list.getSelected().get(0).file; final Path path = list.getSelected().get(0).file.toPath();
String name = Utils.fileNameToReplayName(file.getName()); String name = Utils.fileNameToReplayName(path.getFileName().toString());
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),
@@ -149,16 +151,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(), Utils.replayNameToFileName(nameField.getText())).exists()); && Files.notExists(Utils.replayNameToPath(path.getParent(), nameField.getText())));
}); });
popup.onAccept(() -> { popup.onAccept(() -> {
// Sanitize their input // Sanitize their input
String newName = nameField.getText().trim(); String newName = nameField.getText().trim();
// This file is what they want // This file is what they want
File targetFile = new File(file.getParentFile(), Utils.replayNameToFileName(newName)); Path targetPath = Utils.replayNameToPath(path.getParent(), newName);
try { try {
// Finally, try to move it // Finally, try to move it
FileUtils.moveFile(file, targetFile); Files.move(path, targetPath);
} catch (IOException e) { } catch (IOException e) {
// We failed (might also be their OS) // We failed (might also be their OS)
e.printStackTrace(); e.printStackTrace();