using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using Tesses.VirtualFilesystem; using System.Security.Cryptography; using System.Collections; using System.Linq; namespace Tesses.CMS { public class Torrent { public string UrlList {get;set;}=""; private BencodedDictionary torrent=null; public BencodedDictionary ToTorrentFile() { if(torrent != null) return torrent; BencodedDictionary dict=new BencodedDictionary(); if(this.AnnounceList.Count> 0) { dict["announce"] = (BencodedString)AnnounceList[0]; var al = new BencodedList(); foreach(var item in AnnounceList) { BencodedList ls2=new BencodedList(); ls2.Add((BencodedString)item); al.Add(ls2); } dict["announce-list"] = al; } if(!string.IsNullOrWhiteSpace(UrlList)) { dict["url-list"] = (BencodedString)UrlList; } dict["created by"] = (BencodedString)CreatedBy; dict["creation date"] = new BencodedNumber(CreationDate.ToUnixTimeSeconds()); var info = new BencodedDictionary(); byte[] pieces = new byte[Info.Pieces.Count * 20]; for(int i = 0;i< Info.Pieces.Count;i++) { Array.Copy(Info.Pieces[i],0,pieces,i*20,20); } if(Info.Files.Count > 0) { BencodedList ls=new BencodedList(); foreach(var item in Info.Files) { BencodedDictionary file=new BencodedDictionary(); file["length"] = new BencodedNumber(item.Length); BencodedList paths = new BencodedList(); foreach(var path in item.Path) { paths.Add((BencodedString)path); } file["path"] = paths; ls.Add(file); } info["files"] = ls; } else { info["length"] = new BencodedNumber(Info.Length); } info["name"] = (BencodedString)Info.Name; info["piece length"] = new BencodedNumber(Info.PieceLength); info["pieces"] = new BencodedString(pieces); dict["info"] = info; return dict; } public static Torrent FromTorrentFile(BencodedData file) { var torrent = file as BencodedDictionary; if(torrent == null) return new Torrent(); Torrent torrentObj= new Torrent(); torrentObj.torrent = torrent; if(torrent.TryGetValue("url-list",out var urlList)) { torrentObj.UrlList = urlList.ToString(); } if(torrent.TryGetValue("created by", out var created_by)) { torrentObj.CreatedBy = created_by.ToString(); } if(torrent.TryGetValue("creation date", out var creation_date)) { var creationdate = creation_date as BencodedNumber; if(creationdate != null) { torrentObj.CreationDate = DateTimeOffset.FromUnixTimeSeconds(creationdate.Value); } } if(torrent.TryGetValue("announce-list", out var announce_list)) { var announcelist = announce_list as BencodedList; if(announcelist != null) foreach(var item in announcelist) { var ls0 = item as BencodedList; if(ls0 != null) foreach(var item0 in ls0) torrentObj.AnnounceList.Add(item0.ToString()); } } if(torrent.TryGetValue("announce", out var announce)) { if(torrentObj.AnnounceList.Count == 0) torrentObj.AnnounceList.Add(announce.ToString()); } if(torrent.TryGetValue("info", out var info)) { var info_dict = info as BencodedDictionary; if(info_dict != null) { torrentObj.InfoHash = torrentObj.GetInfoHash(info_dict); if(info_dict.TryGetValue("name",out var name)) { torrentObj.Info.Name = name.ToString(); } if(info_dict.TryGetValue("piece length",out var piece_length)) { var piecelength = piece_length as BencodedNumber; if(piecelength != null) { torrentObj.Info.PieceLength = (int)piecelength.Value; } } if(info_dict.TryGetValue("files",out var files_list)) { var files = files_list as BencodedList; if(files != null) { long offset = 0; foreach(var _file in files) { var _file_dict = _file as BencodedDictionary; if(_file_dict != null) { TorrentInfoFile torrfile=new TorrentInfoFile(); torrfile.Offset = offset; if(_file_dict.TryGetValue("length", out var length2)) { var l = length2 as BencodedNumber; if(l != null) offset += torrfile.Length = l.Value; } if(_file_dict.TryGetValue("path", out var path)) { var pathlist = path as BencodedList; if(pathlist != null) torrfile.Path = pathlist.Select(e=>e.ToString()).ToArray(); } torrentObj.Info.Files.Add(torrfile); } } torrentObj.Info.Length = offset; } } if(info_dict.TryGetValue("length", out var _length)) { var length = _length as BencodedNumber; if(length != null) torrentObj.Info.Length = length.Value; } if(info_dict.TryGetValue("pieces",out var _pieces)) { var pieces = _pieces as BencodedString; if(pieces !=null) { byte[] array=(byte[])pieces; int noPieces=pieces.Length / 20; for(int i = 0;i AnnounceList {get;set;}=new List(); public string CreatedBy {get;set;}="TessesCMS"; public byte[] InfoHash {get;set;}=null; public async Task GetInfoHashAsync() { if(InfoHash != null) return InfoHash; using(MemoryStream memoryStream=new MemoryStream()) { await ToTorrentFile()["info"].WriteToStreamAsync(memoryStream); using(var sha1=SHA1Managed.Create()) return sha1.ComputeHash(memoryStream); } } public byte[] GetInfoHash() { if(InfoHash != null) return InfoHash; return GetInfoHash(ToTorrentFile()["info"]); } internal byte[] GetInfoHash(BencodedData data) { using(MemoryStream memoryStream=new MemoryStream()) { data.WriteToStream(memoryStream); using(var sha1=SHA1Managed.Create()) return sha1.ComputeHash(memoryStream); } } public TorrentInfo Info {get;set;}=new TorrentInfo(); } public class TorrentInfo { public List Files {get;set;}=new List(); public string Name {get;set;} public long Length {get;set;} public int PieceLength {get;set;} public List Pieces {get;set;}=new List(); } public class TorrentInfoFile { public long Offset {get;set;} public long Length {get;set;} public string[] Path {get;set;} public override string ToString() { return System.IO.Path.Combine(Path); } public UnixPath ToUnixPath() { return new UnixPath(Path); } } public abstract class BencodedData { internal static async Task ReadByteAsync(Stream strm) { byte[] buffer=new byte[1]; if(await strm.ReadAsync(buffer,0,1) == 0) return -1; return buffer[0]; } public static BencodedData ReadFromStream(Stream strm) { int b=strm.ReadByte(); switch(b) { case 'e': return null; case 'i': return BencodedNumber.ReadIntFromStream(strm); case 'l': return BencodedList.ReadListFromStream(strm); case 'd': return BencodedDictionary.ReadDictionaryFromStream(strm); default: { StringBuilder sb= new StringBuilder(); sb.Append((char)b); b = strm.ReadByte(); while(b != ':' && b !=-1) { sb.Append((char)b); b = strm.ReadByte(); } if(int.TryParse(sb.ToString(), out var number)) { byte[] str = new byte[number]; if(strm.Read(str,0,str.Length) != str.Length) { return (BencodedString)""; } return new BencodedString(str); } return (BencodedString)""; } } } public static async Task ReadFromStreamAsync(Stream strm) { int b=await ReadByteAsync(strm); switch(b) { case 'e': return null; case 'i': return await BencodedNumber.ReadIntFromStreamAsync(strm); case 'l': return await BencodedList.ReadListFromStreamAsync(strm); case 'd': return await BencodedDictionary.ReadDictionaryFromStreamAsync(strm); default: { StringBuilder sb= new StringBuilder(); sb.Append((char)b); b = await ReadByteAsync(strm); while(b != ':' && b !=-1) { sb.Append((char)b); b = await ReadByteAsync(strm); } if(int.TryParse(sb.ToString(), out var number)) { byte[] str = new byte[number]; if(await strm.ReadAsync(str,0,str.Length) != str.Length) { return (BencodedString)""; } return new BencodedString(str); } return (BencodedString)""; } } } public abstract Task WriteToStreamAsync(Stream strm); public abstract void WriteToStream(Stream strm); } public class BencodedDictionary : BencodedData, IDictionary { List> _dict=new List>(); public BencodedData this[string key] { get{ foreach(var item in _dict) { if(item.Key == key) return item.Value; } return null; } set { var val= new KeyValuePair(key,value); for(int i = 0;i<_dict.Count;i++) { if(_dict[i].Key == key) { _dict[i] = val; return; } } _dict.Add(val); } } public ICollection Keys => _dict.Select(e=>e.Key).ToList(); public ICollection Values => _dict.Select(e=>e.Value).ToList(); public int Count => _dict.Count; public bool IsReadOnly => false; internal static async Task ReadDictionaryFromStreamAsync(Stream strm) { BencodedDictionary dict=new BencodedDictionary(); BencodedData data; while((data=await ReadFromStreamAsync(strm)) != null) { var res=await ReadFromStreamAsync(strm); if(res == null) break; dict.Add(data.ToString(), res); } return dict; } internal static BencodedData ReadDictionaryFromStream(Stream strm) { BencodedDictionary dict=new BencodedDictionary(); BencodedData data; while((data=ReadFromStream(strm)) != null) { var res=ReadFromStream(strm); if(res == null) break; dict.Add(data.ToString(), res); } return dict; } public void Add(string key, BencodedData value) { _dict.Add(new KeyValuePair(key,value)); } public void Add(KeyValuePair item) { _dict.Add(item); } public void Clear() { _dict.Clear(); } public bool Contains(KeyValuePair item) { return _dict.Contains(item); } public bool ContainsKey(string key) { foreach(var item in _dict) { if(item.Key == key) return true; } return false; } public void CopyTo(KeyValuePair[] array, int arrayIndex) { _dict.CopyTo(array,arrayIndex); } public IEnumerator> GetEnumerator() { return _dict.GetEnumerator(); } public bool Remove(string key) { for(int i = 0;i<_dict.Count;i++) { if(_dict[i].Key == key) { _dict.RemoveAt(i); return true; } } return false; } public bool Remove(KeyValuePair item) { return _dict.Remove(item); } public bool TryGetValue(string key, out BencodedData value) { value = null; foreach(var item in _dict) { if(item.Key == key) {value = item.Value; return true;} } return false; } public override void WriteToStream(Stream strm) { strm.WriteByte((byte)'d'); foreach(var item in this) { BencodedString str = item.Key; str.WriteToStream(strm); item.Value.WriteToStream(strm); } strm.WriteByte((byte)'e'); } public override async Task WriteToStreamAsync(Stream strm) { await strm.WriteAsync(new byte[]{(byte)'d'},0,1); foreach(var item in this) { BencodedString str = item.Key; await str.WriteToStreamAsync(strm); await item.Value.WriteToStreamAsync(strm); } await strm.WriteAsync(new byte[]{(byte)'e'},0,1); } IEnumerator IEnumerable.GetEnumerator() { return _dict.GetEnumerator(); } } public sealed class BencodedList : BencodedData, IList { List bencodedDatas=new List(); public BencodedList() { } public BencodedData this[int index] { get => bencodedDatas[index]; set => bencodedDatas[index]=value; } public int Count => bencodedDatas.Count; public bool IsReadOnly => false; internal static BencodedData ReadListFromStream(Stream strm) { BencodedList list=new BencodedList(); BencodedData data; while((data = ReadFromStream(strm)) != null) { list.Add(data); } return list; } internal static async Task ReadListFromStreamAsync(Stream strm) { BencodedList list=new BencodedList(); BencodedData data; while((data = await ReadFromStreamAsync(strm)) != null) { list.Add(data); } return list; } public void Add(BencodedData item) { bencodedDatas.Add(item); } public void AddRange(IEnumerable items) { bencodedDatas.AddRange(items); } public void Clear() { bencodedDatas.Clear(); } public bool Contains(BencodedData item) { return bencodedDatas.Contains(item); } public void CopyTo(BencodedData[] array, int arrayIndex) { bencodedDatas.CopyTo(array,arrayIndex); } public IEnumerator GetEnumerator() { return bencodedDatas.GetEnumerator(); } public int IndexOf(BencodedData item) { return bencodedDatas.IndexOf(item); } public void Insert(int index, BencodedData item) { bencodedDatas.Insert(index,item); } public bool Remove(BencodedData item) { return bencodedDatas.Remove(item); } public void RemoveAt(int index) { bencodedDatas.RemoveAt(index); } public override void WriteToStream(Stream strm) { strm.WriteByte((byte)'l'); foreach(var item in this) { item.WriteToStream(strm); } strm.WriteByte((byte)'e'); } public override async Task WriteToStreamAsync(Stream strm) { await strm.WriteAsync(new byte[]{(byte)'l'},0,1); foreach(var item in this) { await item.WriteToStreamAsync(strm); } await strm.WriteAsync(new byte[]{(byte)'e'},0,1); } IEnumerator IEnumerable.GetEnumerator() { return bencodedDatas.GetEnumerator(); } } public sealed class BencodedNumber : BencodedData { public override string ToString() { return Value.ToString(); } public BencodedNumber(long value) { Value = value; } public static implicit operator BencodedNumber(long number) { return new BencodedNumber(number); } public static explicit operator long(BencodedNumber number) { return number.Value; } public long Value {get;private set;} public override async Task WriteToStreamAsync(Stream strm) { var bytes=Encoding.UTF8.GetBytes($"i{Value}e"); await strm.WriteAsync(bytes,0,bytes.Length); } internal static async Task ReadIntFromStreamAsync(Stream strm) { StringBuilder b=new StringBuilder(); int read = await ReadByteAsync(strm); while(read != 'e' && read != -1) { b.Append((char)read); read = await ReadByteAsync(strm); } if(long.TryParse(b.ToString(),out var number)) { return new BencodedNumber(number); } return new BencodedNumber(0); } public override void WriteToStream(Stream strm) { var bytes=Encoding.UTF8.GetBytes($"i{Value}e"); strm.Write(bytes,0,bytes.Length); } internal static BencodedData ReadIntFromStream(Stream strm) { StringBuilder b=new StringBuilder(); int read = strm.ReadByte(); while(read != 'e' && read != -1) { b.Append((char)read); read = strm.ReadByte(); } if(long.TryParse(b.ToString(),out var number)) { return new BencodedNumber(number); } return new BencodedNumber(0); } } public sealed class BencodedString : BencodedData, IEnumerable { public static implicit operator BencodedString(byte[] data) { return new BencodedString(data); } public static implicit operator BencodedString(string text) { return new BencodedString(Encoding.UTF8.GetBytes(text)); } public static explicit operator string(BencodedString str) { return str.ToString(); } public static explicit operator byte[](BencodedString str) { byte[] myL=new byte[str.data.Length]; Array.Copy(str.data,myL,myL.Length); return myL; } public BencodedString(byte[] array) { data = array; } public byte this[int index] { get=>data[index]; } byte[] data; public int Count => data.Length; public int Length => data.Length; public override async Task WriteToStreamAsync(Stream strm) { byte[] utf8 = Encoding.UTF8.GetBytes($"{data.Length}:"); await strm.WriteAsync(utf8,0,utf8.Length); await strm.WriteAsync(data,0,data.Length); } private IEnumerable _E() { foreach(var c in data) yield return c; } public IEnumerator GetEnumerator() { return _E().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return data.GetEnumerator(); } public override string ToString() { return Encoding.UTF8.GetString(data); } public override void WriteToStream(Stream strm) { byte[] utf8 = Encoding.UTF8.GetBytes($"{data.Length}:"); strm.Write(utf8,0,utf8.Length); strm.Write(data,0,data.Length); } } }