1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! [`lava_torrent`] is a library for parsing/encoding/creating bencode and *.torrent* files. It is
//! dual-licensed under [Apache 2.0] and [MIT].
//!
//! # *Quick Start*
//! Read a torrent ([v1]) and print it and its info hash.
//!
//! ```no_run
//! use lava_torrent::torrent::v1::Torrent;
//!
//! let torrent = Torrent::read_from_file("sample.torrent").unwrap();
//! println!("{}", torrent);
//! println!("Info hash: {}", torrent.info_hash());
//! ```
//!
//! Create a torrent ([v1]) from files in a directory and save the *.torrent* file.
//!
//! ```no_run
//! use lava_torrent::torrent::v1::TorrentBuilder;
//!
//! let torrent = TorrentBuilder::new("dir/", 1048576).build().unwrap();
//! torrent.write_into_file("sample.torrent").unwrap();
//! ```
//!
//! # *Overview*
//! - **It is not recommended to use [`lava_torrent`] in any critical system at this point.**
//! - Currently, only [v1] torrents are supported. [Merkle tree torrents] can be supported
//! if there's enough demand. [v2] torrents might be supported once it's stabilized.
//! - Methods for parsing and encoding are generally bound to structs (i.e. they are
//! "associated methods"). Methods that are general enough are placed at the module-level (e.g.
//! [`lava_torrent::bencode::write::encode_bytes()`]).
//!
//! ## Functionality
//! - bencode parsing/encoding (i.e. "bencoding/bdecoding") => [`BencodeElem`]
//! - torrent parsing/encoding (based on [`BencodeElem`]) => [`Torrent`]
//! - torrent creation => [`TorrentBuilder`]
//! - tracker response parsing => [`tracker`] [experimental]
//!
//! # *Correctness*
//! [`lava_torrent`] is written without using any existing parser or parser generator.
//! The [BitTorrent specification] is also rather vague on certain points. Thus, bugs
//! should not be a surprise. If you do find one, please open a GitHub issue.
//!
//! That said, a lot of unit tests and several integration tests are written to minimize the
//! possibility of incorrect behaviors.
//!
//! ## Known Issues
//! 1. [BEP 3] specifies that a bencode integer has no
//! size limit. This is a reasonable choice as it allows the protocol to be used
//! in the future when file sizes grow significantly. However, using a 64-bit signed
//! integer to represent a bencode integer should be more-than sufficient even in 2018.
//! Therefore, while technically we should use something like
//! [`bigint`] to represent bencode integers,
//! `i64` is used in the current implementation. If a bencode integer larger than
//! [`i64::max_value()`]
//! is found, an `Error` will be returned.
//!
//! 2. Several private methods will panic if something that "just won't happen"
//! happens. For the purpose of full disclosure this behavior is mentioned here,
//! but in reality panic should never be triggered. If you want to locate these
//! private methods try searching for "panic", "unwrap", and "expect" in `*.rs` files.
//!
//! # *Implemented BEPs*
//! NOTE: Only the parsing/encoding aspects are implemented.
//! - [BEP 3]
//! - [BEP 9] \(partial, only implemented magnet url v1)
//! - [BEP 12]
//! - [BEP 27]
//!
//! # *Other Stuff*
//! - Feature Request: To request a feature please open a GitHub issue (please
//! try to request only 1 feature per issue).
//! - Contribution: PR is always welcome.
//! - What's with "lava": Originally I intended to start a project for downloading/crawling
//! stuff. When downloading files, a stream of bits will be moving around--like lava.
//! - Other "lava" crates: The landscape for downloading/crawling stuff is fairly mature
//! at this point, which means reinventing the wheels now is rather pointless... So this
//! might be the only crate published under the "lava" name.
//! - Similar crates: [bip-rs]
//!
//! [`lava_torrent`]: index.html
//! [Apache 2.0]: https://www.apache.org/licenses/LICENSE-2.0
//! [MIT]: https://opensource.org/licenses/MIT
//! [profiling]: https://github.com/ttlajus/lava_torrent/wiki/Performance
//! [v1]: http://bittorrent.org/beps/bep_0003.html
//! [Merkle tree torrents]: http://bittorrent.org/beps/bep_0030.html
//! [v2]: http://bittorrent.org/beps/bep_0052.html
//! [`lava_torrent::bencode::write::encode_bytes()`]: bencode/write/fn.encode_bytes.html
//! [`BencodeElem`]: bencode/enum.BencodeElem.html
//! [`Torrent`]: torrent/v1/struct.Torrent.html
//! [`TorrentBuilder`]: torrent/v1/struct.TorrentBuilder.html
//! [`tracker`]: tracker/index.html
//! [BitTorrent specification]: http://bittorrent.org/beps/bep_0003.html
//! [BEP 3]: http://bittorrent.org/beps/bep_0003.html
//! [`bigint`]: https://github.com/rust-num/num-bigint
//! [`i64::max_value()`]: https://doc.rust-lang.org/stable/std/primitive.i64.html#method.max_value
//! [BEP 9]: http://bittorrent.org/beps/bep_0009.html
//! [BEP 12]: http://bittorrent.org/beps/bep_0012.html
//! [BEP 27]: http://bittorrent.org/beps/bep_0027.html
//! [bip-rs]: https://github.com/GGist/bip-rs

extern crate conv;
extern crate itertools;
extern crate percent_encoding;
extern crate sha1;
extern crate unicode_normalization;
#[macro_use]
extern crate error_chain;

pub(crate) mod util;
#[macro_use]
pub mod bencode;
pub mod torrent;
pub mod tracker;

/// Custom error.
///
pub mod error {
    error_chain! {
        foreign_links {
            Io(::std::io::Error)
            #[doc = "IO error occurred. \
            The bencode and the torrent may or may not be malformed \
            (as we can't verify that)."];
        }

        errors {
            #[doc = "The bencode is found to be bad before we can parse \
             the torrent, so the torrent may or may not be malformed. \
             This is generally unexpected behavior and thus should be handled."]
            MalformedBencode(reason: ::std::borrow::Cow<'static, str>) {
                description("malformed bencode")
                display("malformed bencode: {}", reason)
            }

            #[doc = "Bencode is fine, but parsed data is gibberish, so we \
             can't extract a torrent from it."]
            MalformedTorrent(reason: ::std::borrow::Cow<'static, str>) {
                description("malformed torrent")
                display("malformed torrent: {}", reason)
            }

            #[doc = "Bencode is fine, but parsed data is gibberish, so we \
             can't extract a response from it."]
            MalformedResponse(reason: ::std::borrow::Cow<'static, str>) {
                description("malformed response")
                display("malformed response: {}", reason)
            }

            #[doc = "`TorrentBuilder` encounters problems when \
             building `Torrent`. For instance, a field is set to \
             an empty string by the caller."]
            TorrentBuilderFailure(reason: ::std::borrow::Cow<'static, str>) {
                description("failed to build torrent")
                display("failed to build torrent: {}", reason)
            }

            #[doc = "An invalid argument is passed to a function."]
            InvalidArgument(reason: ::std::borrow::Cow<'static, str>) {
                description("invalid argument:")
                display("invalid argument: {}", reason)
            }

            #[doc = "Conversion between numeric types (e.g. `i64 -> u64`) has failed."]
            FailedNumericConv(msg: ::std::borrow::Cow<'static, str>) {
                description("numeric conversion failed:")
                display("numeric conversion failed: {}", msg)
            }
        }
    }
}