Skip to content

Commit

Permalink
feat: [#2560] new function to calculate the original torrent infohash
Browse files Browse the repository at this point in the history
We can change the final torrent infohash becuase we do not extract/parse
non standard fields in the info dictionary. We want to keep a copy of
the original uploaded torrent's infohash. Si we need to calcuylate the
infohash with all the fields in the info dictionary.

We will sotre it in the database and warn users in the UI  when the infohash
changes.
  • Loading branch information
josecelano committed Aug 25, 2023
1 parent 941694e commit 0cb63dc
Showing 1 changed file with 54 additions and 5 deletions.
59 changes: 54 additions & 5 deletions src/utils/parse_torrent.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use std::error;

use serde::{self, Deserialize, Serialize};
use serde_bencode::value::Value;
use serde_bencode::{de, Error};
use sha1::{Digest, Sha1};

use crate::models::info_hash::InfoHash;
use crate::models::torrent_file::Torrent;

/// Decode a Torrent from Bencoded Bytes
/// Decode a Torrent from Bencoded Bytes.
///
/// # Errors
///
Expand All @@ -19,7 +23,7 @@ pub fn decode_torrent(bytes: &[u8]) -> Result<Torrent, Box<dyn error::Error>> {
}
}

/// Encode a Torrent into Bencoded Bytes
/// Encode a Torrent into Bencoded Bytes.
///
/// # Errors
///
Expand All @@ -34,21 +38,66 @@ pub fn encode_torrent(torrent: &Torrent) -> Result<Vec<u8>, Error> {
}
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct MetainfoFile {
pub info: Value,
}

/// Calculates the `InfoHash` from a the torrent file binary data.
///
/// # Panics
///
/// This function will panic if the torrent file is not a valid bencoded file
/// or if the info dictionary cannot be bencoded.
#[must_use]
pub fn calculate_info_hash(bytes: &[u8]) -> InfoHash {
// Extract the info dictionary
let metainfo: MetainfoFile = serde_bencode::from_bytes(bytes).expect("Torrent file cannot be parsed from bencoded format");

// Bencode the info dictionary
let info_dict_bytes = serde_bencode::to_bytes(&metainfo.info).expect("Info dictionary cannot by bencoded");

// Calculate the SHA-1 hash of the bencoded info dictionary
let mut hasher = Sha1::new();
hasher.update(&info_dict_bytes);
let result = hasher.finalize();

InfoHash::from_bytes(&result)
}

#[cfg(test)]
mod tests {
use std::path::Path;
use std::str::FromStr;

use crate::models::info_hash::InfoHash;

#[test]
fn it_should_calculate_the_original_info_hash_using_all_fields_in_the_info_key_dictionary() {
let torrent_path = Path::new(
// cspell:disable-next-line
"tests/fixtures/torrents/6c690018c5786dbbb00161f62b0712d69296df97_with_custom_info_dict_key.torrent",
);

let original_info_hash = super::calculate_info_hash(&std::fs::read(torrent_path).unwrap());

assert_eq!(
original_info_hash,
InfoHash::from_str("6c690018c5786dbbb00161f62b0712d69296df97").unwrap()
);
}

#[test]
fn it_should_ignore_non_standard_fields_in_info_dictionary() {
fn it_should_calculate_the_new_info_hash_ignoring_non_standard_fields_in_the_info_key_dictionary() {
let torrent_path = Path::new(
// cspell:disable-next-line
"tests/fixtures/torrents/6c690018c5786dbbb00161f62b0712d69296df97_with_custom_info_dict_key.torrent",
);

let torrent = super::decode_torrent(&std::fs::read(torrent_path).unwrap()).unwrap();

// The infohash is not the original infohash of the torrent file, but the infohash of the
// info dictionary without the custom keys.
// The infohash is not the original infohash of the torrent file,
// but the infohash of the info dictionary without the custom keys.
assert_eq!(torrent.info_hash(), "8aa01a4c816332045ffec83247ccbc654547fedf".to_string());
}
}

0 comments on commit 0cb63dc

Please sign in to comment.