From 7fd90eeebd1d9c3a60e4f6bb62691d0db53833c4 Mon Sep 17 00:00:00 2001
From: zzz <zzz@i2pmail.org>
Date: Fri, 22 Mar 2024 10:09:15 -0400
Subject: [PATCH] i2psnark: Preserve private=0 in torrent files

to prevent infohash change after edit
as set by Transmission/3.00
reported by Gid
comment spelling fixes
---
 .../java/src/org/klomp/snark/MetaInfo.java    | 55 ++++++++++++++++---
 .../src/org/klomp/snark/bencode/BDecoder.java |  4 +-
 .../org/klomp/snark/web/I2PSnarkServlet.java  |  2 +-
 3 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java b/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java
index 8f19d3be26..1da9cd4c6f 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java
@@ -63,7 +63,7 @@ public class MetaInfo
   private final int piece_length;
   private final byte[] piece_hashes;
   private final long length;
-  private final boolean privateTorrent;
+  private final int privateTorrent; // 0: not present; 1: = 1; -1: = 0
   private final List<List<String>> announce_list;
   private final String comment;
   private final String created_by;
@@ -97,7 +97,7 @@ public class MetaInfo
     this.piece_length = piece_length;
     this.piece_hashes = piece_hashes;
     this.length = length;
-    this.privateTorrent = privateTorrent;
+    this.privateTorrent = privateTorrent ? 1 : 0;
     this.announce_list = announce_list;
     this.comment = comment;
     this.created_by = created_by;
@@ -117,9 +117,37 @@ public class MetaInfo
     //infoMap = null;
   }
 
+  /**
+   *  Preserves privateTorrent int value, for main()
+   *
+   *  @since 0.9.62
+   */
+  public MetaInfo(String announce, String name, String name_utf8, List<List<String>> files, List<Long> lengths,
+           int piece_length, byte[] piece_hashes, long length, int privateTorrent,
+           List<List<String>> announce_list, String created_by, List<String> url_list, String comment)
+  {
+    this.announce = announce;
+    this.name = name;
+    this.name_utf8 = name_utf8;
+    this.files = files == null ? null : Collections.unmodifiableList(files);
+    this.files_utf8 = null;
+    this.lengths = lengths == null ? null : Collections.unmodifiableList(lengths);
+    this.piece_length = piece_length;
+    this.piece_hashes = piece_hashes;
+    this.length = length;
+    this.privateTorrent = privateTorrent;
+    this.announce_list = announce_list;
+    this.comment = comment;
+    this.created_by = created_by;
+    this.creation_date = I2PAppContext.getGlobalContext().clock().now();
+    this.url_list = url_list;
+    this.attributes = null;
+    this.info_hash = calculateInfoHash();
+  }
+
   /**
    * Creates a new MetaInfo from the given InputStream.  The
-   * InputStream must start with a correctly bencoded dictonary
+   * InputStream must start with a correctly bencoded dictionary
    * describing the torrent.
    * Caller must close the stream.
    */
@@ -144,7 +172,7 @@ public class MetaInfo
 
   /**
    * Creates a new MetaInfo from a Map of BEValues and the SHA1 over
-   * the original bencoded info dictonary (this is a hack, we could
+   * the original bencoded info dictionary (this is a hack, we could
    * reconstruct the bencoded stream and recalculate the hash). Will
    * NOT throw a InvalidBEncodingException if the given map does not
    * contain a valid announce string.
@@ -257,10 +285,11 @@ public class MetaInfo
         // Transmission does numbers. So does libtorrent.
         // We handle both as of 0.9.9.
         // We switch to storing as number as of 0.9.14.
-        privateTorrent = "1".equals(o) ||
+        boolean privat = "1".equals(o) ||
                          ((o instanceof Number) && ((Number) o).intValue() == 1);
+        privateTorrent = privat ? 1 : -1;
     } else {
-        privateTorrent = false;
+        privateTorrent = 0;
     }
 
     val = info.get("piece length");
@@ -477,6 +506,14 @@ public class MetaInfo
    * @since 0.9
    */
   public boolean isPrivate() {
+    return privateTorrent > 0;
+  }
+
+  /**
+   * @return 0 (default), 1 (set to 1), -1 (set to 0)
+   * @since 0.9.62
+   */
+  public int getPrivateTrackerStatus() {
     return privateTorrent;
   }
 
@@ -738,10 +775,10 @@ public class MetaInfo
     if (name_utf8 != null)
         info.put("name.utf-8", new BEValue(DataHelper.getUTF8(name_utf8)));
     // BEP 27
-    if (privateTorrent)
+    if (privateTorrent != 0)
         // switched to number in 0.9.14
         //info.put("private", new BEValue(DataHelper.getUTF8("1")));
-        info.put("private", new BEValue(Integer.valueOf(1)));
+        info.put("private", new BEValue(Integer.valueOf(privateTorrent > 0 ? 1 : 0)));
 
     info.put("piece length", new BEValue(Integer.valueOf(piece_length)));
     info.put("pieces", new BEValue(piece_hashes));
@@ -876,7 +913,7 @@ public class MetaInfo
                   List<String> urls = url_list != null ? url_list : meta.getWebSeedURLs();
                   // changes/adds creation date
                   MetaInfo meta2 = new MetaInfo(an, meta.getName(), null, meta.getFiles(), meta.getLengths(),
-                                                meta.getPieceLength(0), meta.getPieceHashes(), meta.getTotalLength(), meta.isPrivate(),
+                                                meta.getPieceLength(0), meta.getPieceHashes(), meta.getTotalLength(), meta.getPrivateTrackerStatus(),
                                                 meta.getAnnounceList(), cb, urls, cm);
                   java.io.File from = new java.io.File(args[i]);
                   java.io.File to = new java.io.File(args[i] + ".bak");
diff --git a/apps/i2psnark/java/src/org/klomp/snark/bencode/BDecoder.java b/apps/i2psnark/java/src/org/klomp/snark/bencode/BDecoder.java
index 4d7162e3df..30345b778f 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/bencode/BDecoder.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/bencode/BDecoder.java
@@ -294,7 +294,7 @@ public class BDecoder
 
   /**
    * Returns the next bencoded value on the stream and makes sure it
-   * is a map (dictonary). If it is not a map it will throw
+   * is a map (dictionary). If it is not a map it will throw
    * InvalidBEncodingException.
    */
   public BEValue bdecodeMap() throws IOException
@@ -311,7 +311,7 @@ public class BDecoder
     c = getNextIndicator();
     while (c != 'e')
       {
-        // Dictonary keys are always strings.
+        // Dictionary keys are always strings.
         String key = bdecode().getString();
 
         // XXX ugly hack
diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
index 00ac315817..414de90a4e 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
@@ -4886,7 +4886,7 @@ public class I2PSnarkServlet extends BasicServlet {
         if (newCreatedBy.equals(""))
             newCreatedBy = null;
         MetaInfo newMeta = new MetaInfo(thePrimary, meta.getName(), null, meta.getFiles(), meta.getLengths(),
-                                        meta.getPieceLength(0), meta.getPieceHashes(), meta.getTotalLength(), meta.isPrivate(),
+                                        meta.getPieceLength(0), meta.getPieceHashes(), meta.getTotalLength(), meta.getPrivateTrackerStatus(),
                                         newAnnList, newCreatedBy, meta.getWebSeedURLs(), newComment);
         if (!DataHelper.eq(meta.getInfoHash(), newMeta.getInfoHash())) {
             // shouldn't happen
-- 
GitLab