修改Tracker服务器相关bug
Change-Id: Id3ec61034575bd794724be98f832f8c065448b92
diff --git a/src/main/java/com/pt/utils/TorrentPasskeyModifier.java b/src/main/java/com/pt/utils/TorrentPasskeyModifier.java
new file mode 100644
index 0000000..dc5a82c
--- /dev/null
+++ b/src/main/java/com/pt/utils/TorrentPasskeyModifier.java
@@ -0,0 +1,218 @@
+package com.pt.utils;
+
+import java.io.*;
+import java.util.*;
+
+public class TorrentPasskeyModifier {
+
+ private byte[] infoValueBytes; // 保存 info 的 value 原始 bencode
+
+ public byte[] analyzeTorrentFile(byte[] fileBytes, String username) throws IOException {
+ ByteArrayInputStream in = new ByteArrayInputStream(fileBytes);
+ Map<String, Object> torrentMap = decodeWithInfoPreservation(in);
+
+ // 修改 announce
+ if (torrentMap.containsKey("announce")) {
+ String announce = (String) torrentMap.get("announce");
+ torrentMap.put("announce", replacePasskeyInUrl(announce, username));
+ }
+
+ // 修改 announce-list
+ if (torrentMap.containsKey("announce-list")) {
+ Object list = torrentMap.get("announce-list");
+ if (list instanceof List) {
+ replacePasskeyInAnnounceList((List<?>) list, username);
+ }
+ }
+
+ // 编码为新 torrent 文件
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write('d');
+
+ List<String> keys = new ArrayList<>(torrentMap.keySet());
+ Collections.sort(keys);
+ for (String key : keys) {
+ encodeString(key, out);
+ if ("info".equals(key)) {
+ out.write(infoValueBytes); // 写入原始 info 的 value bencode
+ } else {
+ encode(torrentMap.get(key), out);
+ }
+ }
+
+ out.write('e');
+ return out.toByteArray();
+ }
+
+ private String replacePasskeyInUrl(String url, String username) {
+ if (url == null) return null;
+ if (url.contains("passkey=")) {
+ return url.replaceAll("(?<=passkey=)[^&]*", username);
+ } else {
+ return url.contains("?")
+ ? url + "&passkey=" + username
+ : url + "?passkey=" + username;
+ }
+ }
+
+ private void replacePasskeyInAnnounceList(List<?> list, String username) {
+ for (Object tierObj : list) {
+ if (tierObj instanceof List) {
+ List<Object> tier = (List<Object>) tierObj;
+ for (int i = 0; i < tier.size(); i++) {
+ Object url = tier.get(i);
+ if (url instanceof String) {
+ tier.set(i, replacePasskeyInUrl((String) url, username));
+ }
+ }
+ }
+ }
+ }
+
+ // --- Bencode 解码并提取原始 info 字典字节 ---
+ private Map<String, Object> decodeWithInfoPreservation(InputStream in) throws IOException {
+ if (in.read() != 'd') throw new IOException("Not a bencode dict");
+ Map<String, Object> map = new LinkedHashMap<>();
+ while (true) {
+ int c = in.read();
+ if (c == 'e') break;
+ if (c == -1) throw new EOFException();
+
+ String key = decodeString(in, (char) c);
+ if ("info".equals(key)) {
+ ByteArrayOutputStream infoOut = new ByteArrayOutputStream();
+ in.mark(1);
+ int b = in.read();
+ if (b != 'd') throw new IOException("Invalid info dict");
+ infoOut.write(b);
+ int depth = 1;
+
+ while (depth > 0) {
+ b = in.read();
+ if (b == -1) throw new IOException("Unexpected EOF in info");
+ infoOut.write(b);
+
+ if (b == 'd' || b == 'l') depth++;
+ else if (b == 'e') depth--;
+ else if (b >= '0' && b <= '9') {
+ int len = b - '0';
+ while (true) {
+ int nc = in.read();
+ infoOut.write(nc);
+ if (nc == ':') break;
+ len = len * 10 + (nc - '0');
+ }
+ for (int i = 0; i < len; i++) {
+ infoOut.write(in.read());
+ }
+ } else if (b == 'i') {
+ while (true) {
+ int nc = in.read();
+ infoOut.write(nc);
+ if (nc == 'e') break;
+ }
+ }
+ }
+
+ this.infoValueBytes = infoOut.toByteArray();
+ map.put("info", null); // 占位
+ } else {
+ map.put(key, decode(in));
+ }
+ }
+ return map;
+ }
+
+ private Object decode(InputStream in) throws IOException {
+ int c = in.read();
+ if (c == -1) throw new EOFException();
+ if (c == 'd') return decodeDict(in);
+ if (c == 'l') return decodeList(in);
+ if (c == 'i') return decodeInt(in);
+ if (c >= '0' && c <= '9') return decodeString(in, (char) c);
+ throw new IOException("Invalid bencode start: " + (char) c);
+ }
+
+ private Map<String, Object> decodeDict(InputStream in) throws IOException {
+ Map<String, Object> map = new LinkedHashMap<>();
+ while (true) {
+ int c = in.read();
+ if (c == 'e') break;
+ if (c == -1) throw new EOFException();
+ String key = decodeString(in, (char) c);
+ map.put(key, decode(in));
+ }
+ return map;
+ }
+
+ private List<Object> decodeList(InputStream in) throws IOException {
+ List<Object> list = new ArrayList<>();
+ while (true) {
+ int c = in.read();
+ if (c == 'e') break;
+ if (c == -1) throw new EOFException();
+ in.reset();
+ list.add(decode(in));
+ }
+ return list;
+ }
+
+ private String decodeString(InputStream in, char firstDigit) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ sb.append(firstDigit);
+ while (true) {
+ int c = in.read();
+ if (c == ':') break;
+ sb.append((char) c);
+ }
+ int len = Integer.parseInt(sb.toString());
+ byte[] buf = new byte[len];
+ if (in.read(buf) != len) throw new EOFException();
+ return new String(buf, "UTF-8");
+ }
+
+ private Long decodeInt(InputStream in) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ while (true) {
+ int c = in.read();
+ if (c == 'e') break;
+ sb.append((char) c);
+ }
+ return Long.parseLong(sb.toString());
+ }
+
+ // --- 编码 ---
+ private void encode(Object obj, OutputStream out) throws IOException {
+ if (obj instanceof String) encodeString((String) obj, out);
+ else if (obj instanceof Integer || obj instanceof Long) {
+ out.write('i');
+ out.write(obj.toString().getBytes());
+ out.write('e');
+ } else if (obj instanceof List) {
+ out.write('l');
+ for (Object item : (List<?>) obj) encode(item, out);
+ out.write('e');
+ } else if (obj instanceof Map) {
+ out.write('d');
+ Map<String, Object> map = (Map<String, Object>) obj;
+ List<String> keys = new ArrayList<>(map.keySet());
+ Collections.sort(keys);
+ for (String key : keys) {
+ encodeString(key, out);
+ encode(map.get(key), out);
+ }
+ out.write('e');
+ } else if (obj == null) {
+ // 跳过
+ } else {
+ throw new IOException("Unsupported type: " + obj.getClass());
+ }
+ }
+
+ private void encodeString(String str, OutputStream out) throws IOException {
+ byte[] bytes = str.getBytes("UTF-8");
+ out.write(Integer.toString(bytes.length).getBytes());
+ out.write(':');
+ out.write(bytes);
+ }
+}