From 190acf7ed2bc9e46aff1e2f98edb03cc6f936642 Mon Sep 17 00:00:00 2001 From: zzz <zzz@i2pmail.org> Date: Fri, 3 Feb 2023 07:32:52 -0500 Subject: [PATCH] i2psnark: Fix for torrents with # in the name By using custom version of storeProps()/loadprops() for torrent config files ref: http://zzz.i2p/topics/3576 --- .../src/org/klomp/snark/I2PSnarkUtil.java | 97 +++++++++++++++++++ .../src/org/klomp/snark/SnarkManager.java | 4 +- 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java index f51ecf64b4..3444ea82ba 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java +++ b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java @@ -1,8 +1,15 @@ package org.klomp.snark; +import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStreamReader; import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; @@ -13,6 +20,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.regex.Pattern; import net.i2p.I2PAppContext; import net.i2p.I2PException; @@ -28,6 +36,7 @@ import net.i2p.client.streaming.I2PSocketManagerFactory; import net.i2p.client.streaming.I2PSocketOptions; import net.i2p.data.Base32; import net.i2p.data.DataFormatException; +import net.i2p.data.DataHelper; import net.i2p.data.Destination; import net.i2p.data.Hash; import net.i2p.util.ConcurrentHashSet; @@ -36,7 +45,9 @@ import net.i2p.util.FileUtil; import net.i2p.util.Log; import net.i2p.util.SecureDirectory; import net.i2p.util.SecureFile; +import net.i2p.util.SecureFileOutputStream; import net.i2p.util.SimpleTimer; +import net.i2p.util.SystemVersion; import net.i2p.util.Translate; import org.klomp.snark.dht.DHT; @@ -835,4 +846,90 @@ public class I2PSnarkUtil implements DisconnectListener { public String getString(int n, String s, String p) { return Translate.getString(n, s, p, _context, BUNDLE_NAME); } + + private static final boolean SHOULD_SYNC = !(SystemVersion.isAndroid() || SystemVersion.isARM()); + private static final Pattern ILLEGAL_KEY = Pattern.compile("[#=\\r\\n;]"); + private static final Pattern ILLEGAL_VALUE = Pattern.compile("[\\r\\n]"); + + /** + * Same as DataHelper.loadProps() but allows '#' in values, + * so we can have filenames with '#' in them in torrent config files. + * '#' must be in column 1 for a comment. + * + * @since 0.9.58 + */ + static void loadProps(Properties props, File f) throws IOException { + BufferedReader in = null; + try { + in = new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8"), 1024); + String line = null; + while ( (line = in.readLine()) != null) { + if (line.trim().length() <= 0) continue; + if (line.charAt(0) == '#') continue; + if (line.charAt(0) == ';') continue; + int split = line.indexOf('='); + if (split <= 0) continue; + String key = line.substring(0, split); + String val = line.substring(split+1).trim(); + props.setProperty(key, val); + } + } finally { + if (in != null) try { in.close(); } catch (IOException ioe) {} + } + } + + /** + * Same as DataHelper.loadProps() but allows '#' in values, + * so we can have filenames with '#' in them in torrent config files. + * '#' must be in column 1 for a comment. + * + * @since 0.9.58 + */ + static void storeProps(Properties props, File file) throws IOException { + FileOutputStream fos = null; + PrintWriter out = null; + IOException ioe = null; + File tmpFile = new File(file.getPath() + ".tmp"); + try { + fos = new SecureFileOutputStream(tmpFile); + out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(fos, "UTF-8"))); + out.println("# NOTE: This I2P config file must use UTF-8 encoding"); + out.println("# Last saved: " + DataHelper.formatTime(System.currentTimeMillis())); + for (Map.Entry<Object, Object> entry : props.entrySet()) { + String name = (String) entry.getKey(); + String val = (String) entry.getValue(); + if (ILLEGAL_KEY.matcher(name).find()) { + if (ioe == null) + ioe = new IOException("Invalid character (one of \"#;=\\r\\n\") in key: \"" + + name + "\" = \"" + val + '\"'); + continue; + } + if (ILLEGAL_VALUE.matcher(val).find()) { + if (ioe == null) + ioe = new IOException("Invalid character (one of \"\\r\\n\") in value: \"" + + name + "\" = \"" + val + '\"'); + continue; + } + out.println(name + "=" + val); + } + if (SHOULD_SYNC) { + out.flush(); + fos.getFD().sync(); + } + out.close(); + if (out.checkError()) { + out = null; + tmpFile.delete(); + throw new IOException("Failed to write properties to " + tmpFile); + } + out = null; + if (!FileUtil.rename(tmpFile, file)) + throw new IOException("Failed rename from " + tmpFile + " to " + file); + } finally { + if (out != null) out.close(); + if (fos != null) try { fos.close(); } catch (IOException e) {} + } + if (ioe != null) + throw ioe; + } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index d80366ca2e..1304a809f3 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -727,7 +727,7 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList File conf = configFile(_configDir, ih); synchronized(_configLock) { // one lock for all try { - DataHelper.loadProps(rv, conf); + I2PSnarkUtil.loadProps(rv, conf); } catch (IOException ioe) {} } return rv; @@ -2309,7 +2309,7 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList if (!subdir.exists()) subdir.mkdirs(); try { - DataHelper.storeProps(config, conf); + I2PSnarkUtil.storeProps(config, conf); if (_log.shouldInfo()) _log.info("Saved config to " + conf /* , new Exception() */ ); } catch (IOException ioe) { -- GitLab