Skip to content

Commit

Permalink
Add a torrent from-link subcommand (#511)
Browse files Browse the repository at this point in the history
It is now possible to create a torrent file from a magnet link. The
search is performed with minimal concurrency, and so may take a while to
complete.

type: added
  • Loading branch information
atomgardner authored and casey committed Aug 20, 2023
1 parent 50d5a93 commit a88f0ee
Show file tree
Hide file tree
Showing 25 changed files with 1,879 additions and 24 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ rand = "0.7.3"
open = "1.4.0"
pretty_assertions = "0.6.0"
pretty_env_logger = "0.4.0"
rayon = "<=1.6.0"
regex = "1.0.0"
serde-hex = "0.1.0"
serde_bytes = "0.11.0"
Expand Down
4 changes: 4 additions & 0 deletions bin/gen/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ examples:
text: "Intermodal can be used to create `.torrent` files:"
code: "imdl torrent create --input foo"

- command: imdl torrent from-link
text: "Intermodal can be used to create a `.torrent` file from a magnet link:"
code: "imdl torrent from-link magnet:?foo"

- command: imdl torrent show
text: "Print information about existing `.torrent` files:"
code: "imdl torrent show --input foo.torrent"
Expand Down
10 changes: 7 additions & 3 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ pub(crate) use std::{
hash::Hash,
io::{self, BufRead, BufReader, Cursor, Read, Write},
iter::{self, Sum},
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs, UdpSocket},
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, TcpStream, ToSocketAddrs, UdpSocket},
num::{ParseFloatError, ParseIntError, TryFromIntError},
ops::{AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
path::{self, Path, PathBuf},
process::ExitStatus,
str::{self, FromStr},
string::FromUtf8Error,
sync::Once,
sync::{mpsc::channel, Once},
time::{Duration, SystemTime, SystemTimeError},
usize,
};
Expand Down Expand Up @@ -54,7 +54,9 @@ pub(crate) use url::{Host, Url};
pub(crate) use log::trace;

// modules
pub(crate) use crate::{consts, error, host_port_parse_error, magnet_link_parse_error, tracker};
pub(crate) use crate::{
consts, error, host_port_parse_error, magnet_link_parse_error, peer, tracker,
};

// functions
pub(crate) use crate::xor_args::xor_args;
Expand Down Expand Up @@ -88,9 +90,11 @@ mod test {
// test stdlib types
pub(crate) use std::{
cell::RefCell,
net::TcpListener,
ops::{Deref, DerefMut},
process::Command,
rc::Rc,
thread,
};

// test dependencies
Expand Down
32 changes: 32 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub(crate) enum Error {
Filesystem { source: io::Error, path: PathBuf },
#[snafu(display("Error searching for files: {}", source))]
FileSearch { source: ignore::Error },
#[snafu(display("Failed to fetch infodict from accessible peers"))]
FromLinkNoInfo,
#[snafu(display("Invalid glob: {}", source))]
GlobParse { source: globset::Error },
#[snafu(display("Failed to serialize torrent info dictionary: {}", source))]
Expand Down Expand Up @@ -68,6 +70,8 @@ pub(crate) enum Error {
input: InputTarget,
source: MetainfoError,
},
#[snafu(display("Network error: {}", source))]
Network { source: io::Error },
#[snafu(display("Failed to invoke opener: {}", source))]
OpenerInvoke { source: io::Error },
#[snafu(display("Opener failed: {}", exit_status))]
Expand Down Expand Up @@ -114,6 +118,34 @@ pub(crate) enum Error {
bytes: Bytes,
source: TryFromIntError,
},
#[snafu(display("Received peer handshake with the wrong infohash"))]
PeerHandshakeInfohash,
#[snafu(display("Received peer handshake with the wrong protocol header"))]
PeerHandshakeHeader,
#[snafu(display("Bencoding error: `{}`", source))]
PeerMessageBencode { source: bendy::serde::Error },
#[snafu(display("Peer extended message payload is malformed"))]
PeerMessageExtendedPayload,
#[snafu(display("Failed to decode bencoded message: `{}`", source))]
PeerMessageFromBencode { source: bendy::serde::Error },
#[snafu(display("Peer message payload is too large"))]
PeerMessagePayload { source: TryFromIntError },
#[snafu(display("Extended handshake has not been received from peer"))]
PeerNoExtendedHandshake,
#[snafu(display("Received UtMetadata info dict that's failed to deserialize"))]
PeerUtMetadataInfoDeserialize { source: bendy::serde::Error },
#[snafu(display("Received UtMetadata info dict that's too long"))]
PeerUtMetadataInfoLength,
#[snafu(display("Received UtMetadata data message that's too long"))]
PeerUtMetadataPieceLength,
#[snafu(display("Peer doesn't know metadata size"))]
PeerUtMetadataMetadataSizeNotKnown,
#[snafu(display("Peer doesn't support UtMetadata extension"))]
PeerUtMetadataNotSupported,
#[snafu(display("Hash of received info dict does not match"))]
PeerUtMetadataWrongInfohash,
#[snafu(display("Received the wrong UtMetadata piece"))]
PeerUtMetadataWrongPiece,
#[snafu(display("Piece length `{}` is not an even power of two", bytes))]
PieceLengthUneven { bytes: Bytes },
#[snafu(display("Piece length must be at least 16 KiB"))]
Expand Down
8 changes: 8 additions & 0 deletions src/infohash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ impl From<Sha1Digest> for Infohash {
}
}

impl From<[u8; 20]> for Infohash {
fn from(bytes: [u8; 20]) -> Self {
Infohash {
inner: Sha1Digest::from_bytes(bytes),
}
}
}

impl From<Infohash> for [u8; 20] {
fn from(infohash: Infohash) -> Self {
infohash.inner.bytes()
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ mod mode;
mod options;
mod output_stream;
mod output_target;
mod peer;
mod piece_length_picker;
mod piece_list;
mod platform;
Expand Down
12 changes: 6 additions & 6 deletions src/magnet_link.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::common::*;

#[derive(Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct MagnetLink {
infohash: Infohash,
name: Option<String>,
peers: Vec<HostPort>,
trackers: Vec<Url>,
indices: BTreeSet<u64>,
pub(crate) infohash: Infohash,
pub(crate) name: Option<String>,
pub(crate) peers: Vec<HostPort>,
pub(crate) trackers: Vec<Url>,
pub(crate) indices: BTreeSet<u64>,
}

impl MagnetLink {
Expand Down
6 changes: 6 additions & 0 deletions src/peer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub(crate) use client::Client;

pub(crate) mod client;
pub(crate) mod connection;
pub(crate) mod handshake;
pub(crate) mod message;
Loading

0 comments on commit a88f0ee

Please sign in to comment.