tesses-cms/Tesses.CMS/Bencode.cs

757 lines
24 KiB
C#

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 LiteDB;
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<BencodedData,string>(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<noPieces;i++)
{
byte[] piece = new byte[20];
Array.Copy(array, i*20, piece,0,20);
torrentObj.Info.Pieces.Add(piece);
}
}
}
}
}
return torrentObj;
}
public DateTimeOffset CreationDate {get;set;}=DateTimeOffset.Now;
public List<string> AnnounceList {get;set;}=new List<string>();
public string CreatedBy {get;set;}="TessesCMS";
public byte[] InfoHash {get;set;}=null;
public async Task<byte[]> 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<TorrentInfoFile> Files {get;set;}=new List<TorrentInfoFile>();
public string Name {get;set;}
public long Length {get;set;}
public int PieceLength {get;set;}
public List<byte[]> Pieces {get;set;}=new List<byte[]>();
}
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<int> 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<BencodedData> 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<string,BencodedData>
{
List<KeyValuePair<string,BencodedData>> _dict=new List<KeyValuePair<string, BencodedData>>();
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<string, BencodedData>(key,value);
for(int i = 0;i<_dict.Count;i++)
{
if(_dict[i].Key == key)
{
_dict[i] = val;
return;
}
}
_dict.Add(val);
}
}
public ICollection<string> Keys => _dict.Select(e=>e.Key).ToList();
public ICollection<BencodedData> Values => _dict.Select(e=>e.Value).ToList();
public int Count => _dict.Count;
public bool IsReadOnly => false;
internal static async Task<BencodedData> 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<string, BencodedData>(key,value));
}
public void Add(KeyValuePair<string, BencodedData> item)
{
_dict.Add(item);
}
public void Clear()
{
_dict.Clear();
}
public bool Contains(KeyValuePair<string, BencodedData> 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<string, BencodedData>[] array, int arrayIndex)
{
_dict.CopyTo(array,arrayIndex);
}
public IEnumerator<KeyValuePair<string, BencodedData>> 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<string, BencodedData> 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<BencodedData>
{
List<BencodedData> bencodedDatas=new List<BencodedData>();
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<BencodedData> 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<BencodedData> 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<BencodedData> 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<BencodedData> 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<byte>
{
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<byte> _E()
{
foreach(var c in data) yield return c;
}
public IEnumerator<byte> 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);
}
}
}