blob: dc5a82c241070b84f06f96a7e8e6b5461e53db88 [file] [log] [blame]
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);
}
}