extern crate hex;
extern crate urlencoding;
extern crate bencode;
use std::collections::HashMap;
use std::net::IpAddr;
use chrono::{DateTime, Utc, TimeZone};
use sha2::{Sha256, Digest};
use sha1::{Sha1};
use bencode::{Bencode, FromBencode};
use bencode::util::ByteString;
use std::str;
use std::net::SocketAddr;
enum Version {
V1,
V2,
Hybrid
}
struct Torrent {
name: String,
sha1: [u8; 20],
sha256: [u8; 32],
version: Version,
files: HashMap<String, u64>,
source: Option<SocketAddr>,
date: Option<DateTime<Utc>>,
hostname: Option<String>,
software: Option<String>
}
impl Default for Torrent {
fn default() -> Torrent {
Torrent {
name: String::from("travnik_default_name_unset"),
sha1: [0; 20],
sha256: [0; 32],
version: Version::V1,
files: HashMap::new(),
source: None,
date: None,
hostname: None,
software: None
}
}
}
impl Torrent {
fn magnet(&self) -> String {
let mut string = String::from("magnet:?dn=");
string.push_str(&urlencoding::encode(&self.name));
if matches!(self.version, Version::V1) || matches!(self.version, Version::Hybrid) {
string.push_str("&xt=urn:btih:");
string.push_str(&hex::encode(&self.sha1));
}
if matches!(self.version, Version::V2) || matches!(self.version, Version::Hybrid) {
string.push_str("&xt=urn:btmh:1220");
string.push_str(&hex::encode(&self.sha256));
}
string
}
}
fn sockaddr_from_source(string) -> Result<SockAddra> // TODO nadaljuj
#[derive(Debug)]
enum TorrentDecodeError {
NotADict,
NoName,
NoLength,
CreatedByNotUtf8,
SoftwareNotUtf8
}
impl FromBencode for Torrent {
type Err = TorrentDecodeError;
fn from_bencode(bencode: &Bencode) -> Result<Torrent, TorrentDecodeError> {
let metainfo = match bencode {
&Bencode::Dict(ref metainfo) => metainfo,
_ => return Err(TorrentDecodeError::NotADict)
};
let torrent = Torrent {
date: match metainfo.get(&ByteString::from_str("creation date")) {
Some(&Bencode::Number(c)) => Some(Utc.timestamp(c, 0)),
_ => None
},
hostname: match metainfo.get(&ByteString::from_str("created by")) {
Some(&Bencode::ByteString(ref s)) => match str::from_utf8(&s) {
Ok(v) => Some(match v.split(" ").nth(3) {
Some(c) => String::from(c),
None => String::from("b")
}),
Err(_) => return Err(TorrentDecodeError::CreatedByNotUtf8)
},
_ => None
},
software: match metainfo.get(&ByteString::from_str("source")) {
Some(&Bencode::Dict(ref s)) => match s.get(&ByteString::from_str("v")) {
Some(&Bencode::ByteString(ref s)) => match String::from_utf8(s.to_vec()) {
Ok(v) => Some(v),
Err(_) => return Err(TorrentDecodeError::SoftwareNotUtf8)
},
_ => None
},
_ => None
},
source: match metainfo.get(&ByteString::from_str("source")) {
Some(&Bencode::Dict(ref s)) => match s.get(&ByteString::from_str("ip")) {
Some(&Bencode::ByteString(ref s)) => match str::from_utf8(&s) {
Ok(v) => Some(sockaddr_from_source(v)?), // TODO nadaljuj
Err(_) => return Err(Torrent)
},
_ => None
},
_ => None
},
..Default::default()
};
Ok(torrent)
}
}
fn main() {
// let hash2 = Sha256::new().chain_update(b"test").finalize();
// let hash1 = Sha1::new().chain_update(b"test").finalize();
// println!("Hello, world! {:#?} {:#?}", hash2, hash1);
let bencode: bencode::Bencode = bencode::from_vec(String::from("d1:wlee").as_bytes().to_vec()).unwrap();
println!("{:#?}", bencode);
}