| package com.example.g8backend.util; |
| |
| import com.example.g8backend.dto.AnnounceResponseDTO; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.nio.charset.StandardCharsets; |
| import java.util.*; |
| |
| //package com.bjtu.pt_station.common; |
| |
| import com.example.g8backend.entity.Peer; |
| import com.dampcake.bencode.Bencode; |
| import com.dampcake.bencode.Type; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.web.multipart.MultipartFile; |
| |
| import java.net.InetAddress; |
| import java.net.UnknownHostException; |
| import java.nio.ByteBuffer; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.net.InetSocketAddress; |
| import java.nio.charset.StandardCharsets; |
| import java.security.MessageDigest; |
| import java.util.*; |
| |
| import java.util.List; |
| import java.util.Map; |
| |
| public class BencodeUtil { |
| private static final Logger logger = LoggerFactory.getLogger(BencodeUtil.class); |
| private static final Bencode bencodeUTF8 = new Bencode(StandardCharsets.UTF_8); |
| private static final Bencode bencodeInfoHash = new Bencode(StandardCharsets.ISO_8859_1); |
| |
| public static String encodeUTF8(Map<String, Object> map) { |
| byte[] encodedBytes = bencodeUTF8.encode(map); |
| return new String(encodedBytes, StandardCharsets.UTF_8); |
| } |
| |
| public static String encodeISO(Map<String, Object> map) { |
| byte[] encodedBytes = bencodeInfoHash.encode(map); |
| return new String(encodedBytes, StandardCharsets.ISO_8859_1); |
| } |
| |
| public static Map<String, Object> decodeHash(byte[] torrentBytes) throws IOException { |
| return bencodeInfoHash.decode(torrentBytes, Type.DICTIONARY); |
| } |
| |
| public static Map<String, Object> decodeUTF8(byte[] torrentBytes) throws IOException { |
| return bencodeUTF8.decode(torrentBytes, Type.DICTIONARY); |
| } |
| |
| public static Map<String, Object> decodeISO(byte[] torrentBytes) throws IOException { |
| return bencodeInfoHash.decode(torrentBytes, Type.DICTIONARY); |
| } |
| |
| public static String calInfoHash(MultipartFile torrentFile) throws IOException { |
| byte[] torrentBytes = torrentFile.getBytes(); |
| try { |
| Map<String, Object> dict = decodeUTF8(torrentBytes); |
| Map<String, Object> dictInfoHash = decodeISO(torrentBytes); |
| |
| Object infoObj = dictInfoHash.get("info"); |
| if (infoObj == null) { |
| logger.error("Torrent file does not contain 'info' field."); |
| return null; |
| } |
| |
| byte[] infoData = bencodeInfoHash.encode((Map<?, ?>) infoObj); |
| return calculateInfoHash(infoData); |
| } catch (IOException e) { |
| logger.error("Failed to calculate info hash: {}", e.getMessage()); |
| throw e; |
| } |
| } |
| |
| private static String calculateInfoHash(byte[] bencodedInfo) { |
| try { |
| MessageDigest digest = MessageDigest.getInstance("SHA-1"); |
| byte[] hashBytes = digest.digest(bencodedInfo); |
| |
| StringBuilder hexString = new StringBuilder(); |
| for (byte b : hashBytes) { |
| String hex = Integer.toHexString(0xff & b); |
| if (hex.length() == 1) { |
| hexString.append('0'); |
| } |
| hexString.append(hex); |
| } |
| return hexString.toString(); |
| } catch (NoSuchAlgorithmException e) { |
| throw new RuntimeException("SHA-1 algorithm not available", e); |
| } |
| } |
| |
| public static String convertToString(byte[] bytes) { |
| return new String(bytes, bencodeInfoHash.getCharset()); |
| } |
| |
| public static Bencode bittorrent() { |
| return bencodeInfoHash; |
| } |
| |
| public static Bencode utf8() { |
| return bencodeUTF8; |
| } |
| |
| public static String compactPeers(List<Peer> peers) throws UnknownHostException { |
| ByteBuffer buffer = ByteBuffer.allocate(6 * peers.size()); |
| for (Peer peer : peers) { |
| for (byte addr : InetAddress.getByName(peer.getIpAddress()).getAddress()) { |
| buffer.put(addr); |
| } |
| int in = peer.getPort(); |
| buffer.put((byte) ((in >>> 8) & 0xFF)); |
| buffer.put((byte) (in & 0xFF)); |
| } |
| return convertToString(buffer.array()); |
| } |
| } |
| |
| //public class BencodeUtil { |
| // public static byte[] encodeAnnounceResponse(AnnounceResponseDTO dto) { |
| // ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| // try { |
| // out.write('d'); // dictionary start |
| // |
| // writeString(out, "interval"); |
| // writeInt(out, dto.getInterval()); |
| // |
| // writeString(out, "peers"); |
| // out.write('l'); // list start |
| // for (Map<String, Object> peer : dto.getPeers()) { |
| // out.write('d'); |
| // writeString(out, "ip"); |
| // writeString(out, (String) peer.get("ip")); |
| // writeString(out, "port"); |
| // writeInt(out, ((Number) peer.get("port")).intValue()); |
| // out.write('e'); |
| // } |
| // out.write('e'); // list end |
| // |
| // out.write('e'); // dictionary end |
| // } catch (Exception e) { |
| // throw new RuntimeException("Bencoding failed", e); |
| // } |
| // return out.toByteArray(); |
| // } |
| // |
| // private static void writeString(ByteArrayOutputStream out, String str) throws Exception { |
| // byte[] bytes = str.getBytes(StandardCharsets.UTF_8); |
| // out.write(String.valueOf(bytes.length).getBytes(StandardCharsets.UTF_8)); |
| // out.write(':'); |
| // out.write(bytes); |
| // } |
| // |
| // private static void writeInt(ByteArrayOutputStream out, int i) throws Exception { |
| // out.write('i'); |
| // out.write(String.valueOf(i).getBytes(StandardCharsets.UTF_8)); |
| // out.write('e'); |
| // } |
| //} |