Skip to content

Commit

Permalink
refactor: [#276] extract fn parse_torrent::decode_and_validate_torren…
Browse files Browse the repository at this point in the history
…t_file
  • Loading branch information
josecelano committed Sep 20, 2023
1 parent a46d300 commit 329485f
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 14 deletions.
11 changes: 11 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use hyper::StatusCode;

use crate::databases::database;
use crate::models::torrent::MetadataError;
use crate::utils::parse_torrent::MetainfoFileDataError;

pub type ServiceResult<V> = Result<V, ServiceError>;

Expand Down Expand Up @@ -215,6 +216,16 @@ impl From<MetadataError> for ServiceError {
}
}

impl From<MetainfoFileDataError> for ServiceError {
fn from(e: MetainfoFileDataError) -> Self {
eprintln!("{e}");
match e {
MetainfoFileDataError::InvalidBencodeData => ServiceError::InvalidTorrentFile,
MetainfoFileDataError::InvalidTorrentPiecesLength => ServiceError::InvalidTorrentTitleLength,
}
}
}

#[must_use]
pub fn http_status_code_for_service_error(error: &ServiceError) -> StatusCode {
#[allow(clippy::match_same_arms)]
Expand Down
18 changes: 4 additions & 14 deletions src/services/torrent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::models::torrent_file::{DbTorrent, Torrent, TorrentFile};
use crate::models::torrent_tag::{TagId, TorrentTag};
use crate::models::user::UserId;
use crate::tracker::statistics_importer::StatisticsImporter;
use crate::utils::parse_torrent;
use crate::utils::parse_torrent::decode_and_validate_torrent_file;
use crate::{tracker, AsCSV};

pub struct Index {
Expand Down Expand Up @@ -134,20 +134,10 @@ impl Index {

let metadata = self.validate_and_build_metadata(&add_torrent_req).await?;

// Validate and build torrent file

let original_info_hash = parse_torrent::calculate_info_hash(&add_torrent_req.torrent_buffer);

let mut torrent =
parse_torrent::decode_torrent(&add_torrent_req.torrent_buffer).map_err(|_| ServiceError::InvalidTorrentFile)?;

// Make sure that the pieces key has a length that is a multiple of 20
if let Some(pieces) = torrent.info.pieces.as_ref() {
if pieces.as_ref().len() % 20 != 0 {
return Err(ServiceError::InvalidTorrentPiecesLength);
}
}
let (mut torrent, original_info_hash) = decode_and_validate_torrent_file(&add_torrent_req.torrent_buffer)?;

// Customize the announce URLs with the linked tracker URL
// and remove others if the torrent is private.
torrent.set_announce_urls(&self.configuration).await;

let canonical_info_hash = torrent.canonical_info_hash();
Expand Down
38 changes: 38 additions & 0 deletions src/utils/parse_torrent.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::error;

use derive_more::{Display, Error};
use serde::{self, Deserialize, Serialize};
use serde_bencode::value::Value;
use serde_bencode::{de, Error};
Expand All @@ -8,6 +9,43 @@ use sha1::{Digest, Sha1};
use crate::models::info_hash::InfoHash;
use crate::models::torrent_file::Torrent;

#[derive(Debug, Display, PartialEq, Eq, Error)]
pub enum MetainfoFileDataError {
#[display(fmt = "Torrent data could not be decoded from the bencoded format.")]
InvalidBencodeData,

#[display(fmt = "Torrent has an invalid pieces key length. It should be a multiple of 20.")]
InvalidTorrentPiecesLength,
}

/// It decodes and validate an array of bytes containing a torrent file.
///
/// It returns a tuple containing the decoded torrent and the original info hash.
/// The original info-hash migth not match the new one in the `Torrent` because
/// the info dictionary might have been modified. For example, ignoring some
/// non-standard fields.
///
/// # Errors
///
/// This function will return an error if
///
/// - The torrent file is not a valid bencoded file.
/// - The pieces key has a length that is not a multiple of 20.
pub fn decode_and_validate_torrent_file(bytes: &[u8]) -> Result<(Torrent, InfoHash), MetainfoFileDataError> {
let original_info_hash = calculate_info_hash(bytes);

let torrent = decode_torrent(bytes).map_err(|_| MetainfoFileDataError::InvalidBencodeData)?;

// Make sure that the pieces key has a length that is a multiple of 20
if let Some(pieces) = torrent.info.pieces.as_ref() {
if pieces.as_ref().len() % 20 != 0 {
return Err(MetainfoFileDataError::InvalidTorrentPiecesLength);
}
}

Ok((torrent, original_info_hash))
}

/// Decode a Torrent from Bencoded Bytes.
///
/// # Errors
Expand Down

0 comments on commit 329485f

Please sign in to comment.