1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2024-11-24 08:42:19 +02:00

Update MonoTorrent from nuget

Co-authored-by: ta264 <ta264@users.noreply.github.com>
This commit is contained in:
Qstick 2021-12-30 11:41:59 -06:00 committed by GitHub
parent d284c29b6f
commit 9889ab7b48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 5 additions and 4206 deletions

View File

@ -1,320 +0,0 @@
using System;
using System.IO;
using System.Collections.Generic;
namespace MonoTorrent.BEncoding
{
/// <summary>
/// Class representing a BEncoded Dictionary
/// </summary>
public class BEncodedDictionary : BEncodedValue, IDictionary<BEncodedString, BEncodedValue>
{
#region Member Variables
private SortedDictionary<BEncodedString, BEncodedValue> dictionary;
#endregion
#region Constructors
/// <summary>
/// Create a new BEncodedDictionary
/// </summary>
public BEncodedDictionary()
{
this.dictionary = new SortedDictionary<BEncodedString, BEncodedValue>();
}
#endregion
#region Encode/Decode Methods
/// <summary>
/// Encodes the dictionary to a byte[]
/// </summary>
/// <param name="buffer">The buffer to encode the data to</param>
/// <param name="offset">The offset to start writing the data to</param>
/// <returns></returns>
public override int Encode(byte[] buffer, int offset)
{
int written = 0;
//Dictionaries start with 'd'
buffer[offset] = (byte)'d';
written++;
foreach (KeyValuePair<BEncodedString, BEncodedValue> keypair in this)
{
written += keypair.Key.Encode(buffer, offset + written);
written += keypair.Value.Encode(buffer, offset + written);
}
// Dictionaries end with 'e'
buffer[offset + written] = (byte)'e';
written++;
return written;
}
/// <summary>
///
/// </summary>
/// <param name="reader"></param>
internal override void DecodeInternal(RawReader reader)
{
DecodeInternal(reader, reader.StrictDecoding);
}
private void DecodeInternal(RawReader reader, bool strictDecoding)
{
BEncodedString key = null;
BEncodedValue value = null;
BEncodedString oldkey = null;
if (reader.ReadByte() != 'd')
throw new BEncodingException("Invalid data found. Aborting"); // Remove the leading 'd'
while ((reader.PeekByte() != -1) && (reader.PeekByte() != 'e'))
{
key = (BEncodedString)BEncodedValue.Decode(reader); // keys have to be BEncoded strings
if (oldkey != null && oldkey.CompareTo(key) > 0)
if (strictDecoding)
throw new BEncodingException(String.Format(
"Illegal BEncodedDictionary. The attributes are not ordered correctly. Old key: {0}, New key: {1}",
oldkey, key));
oldkey = key;
value = BEncodedValue.Decode(reader); // the value is a BEncoded value
dictionary.Add(key, value);
}
if (reader.ReadByte() != 'e') // remove the trailing 'e'
throw new BEncodingException("Invalid data found. Aborting");
}
public static BEncodedDictionary DecodeTorrent(byte[] bytes)
{
return DecodeTorrent(new MemoryStream(bytes));
}
public static BEncodedDictionary DecodeTorrent(Stream s)
{
return DecodeTorrent(new RawReader(s));
}
/// <summary>
/// Special decoding method for torrent files - allows dictionary attributes to be out of order for the
/// overall torrent file, but imposes strict rules on the info dictionary.
/// </summary>
/// <returns></returns>
public static BEncodedDictionary DecodeTorrent(RawReader reader)
{
BEncodedString key = null;
BEncodedValue value = null;
BEncodedDictionary torrent = new BEncodedDictionary();
if (reader.ReadByte() != 'd')
throw new BEncodingException("Invalid data found. Aborting"); // Remove the leading 'd'
while ((reader.PeekByte() != -1) && (reader.PeekByte() != 'e'))
{
key = (BEncodedString)BEncodedValue.Decode(reader); // keys have to be BEncoded strings
if (reader.PeekByte() == 'd')
{
value = new BEncodedDictionary();
if (key.Text.ToLower().Equals("info"))
((BEncodedDictionary)value).DecodeInternal(reader, true);
else
((BEncodedDictionary)value).DecodeInternal(reader, false);
}
else
value = BEncodedValue.Decode(reader); // the value is a BEncoded value
torrent.dictionary.Add(key, value);
}
if (reader.ReadByte() != 'e') // remove the trailing 'e'
throw new BEncodingException("Invalid data found. Aborting");
return torrent;
}
#endregion
#region Helper Methods
/// <summary>
/// Returns the size of the dictionary in bytes using UTF8 encoding
/// </summary>
/// <returns></returns>
public override int LengthInBytes()
{
int length = 0;
length += 1; // Dictionaries start with 'd'
foreach (KeyValuePair<BEncodedString, BEncodedValue> keypair in this.dictionary)
{
length += keypair.Key.LengthInBytes();
length += keypair.Value.LengthInBytes();
}
length += 1; // Dictionaries end with 'e'
return length;
}
#endregion
#region Overridden Methods
public override bool Equals(object obj)
{
BEncodedValue val;
BEncodedDictionary other = obj as BEncodedDictionary;
if (other == null)
return false;
if (this.dictionary.Count != other.dictionary.Count)
return false;
foreach (KeyValuePair<BEncodedString, BEncodedValue> keypair in this.dictionary)
{
if (!other.TryGetValue(keypair.Key, out val))
return false;
if (!keypair.Value.Equals(val))
return false;
}
return true;
}
public override int GetHashCode()
{
int result = 0;
foreach (KeyValuePair<BEncodedString, BEncodedValue> keypair in dictionary)
{
result ^= keypair.Key.GetHashCode();
result ^= keypair.Value.GetHashCode();
}
return result;
}
public override string ToString()
{
return System.Text.Encoding.UTF8.GetString(Encode());
}
#endregion
#region IDictionary and IList methods
public void Add(BEncodedString key, BEncodedValue value)
{
this.dictionary.Add(key, value);
}
public void Add(KeyValuePair<BEncodedString, BEncodedValue> item)
{
this.dictionary.Add(item.Key, item.Value);
}
public void Clear()
{
this.dictionary.Clear();
}
public bool Contains(KeyValuePair<BEncodedString, BEncodedValue> item)
{
if (!this.dictionary.ContainsKey(item.Key))
return false;
return this.dictionary[item.Key].Equals(item.Value);
}
public bool ContainsKey(BEncodedString key)
{
return this.dictionary.ContainsKey(key);
}
public void CopyTo(KeyValuePair<BEncodedString, BEncodedValue>[] array, int arrayIndex)
{
this.dictionary.CopyTo(array, arrayIndex);
}
public int Count
{
get { return this.dictionary.Count; }
}
//public int IndexOf(KeyValuePair<BEncodedString, IBEncodedValue> item)
//{
// return this.dictionary.IndexOf(item);
//}
//public void Insert(int index, KeyValuePair<BEncodedString, IBEncodedValue> item)
//{
// this.dictionary.Insert(index, item);
//}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(BEncodedString key)
{
return this.dictionary.Remove(key);
}
public bool Remove(KeyValuePair<BEncodedString, BEncodedValue> item)
{
return this.dictionary.Remove(item.Key);
}
//public void RemoveAt(int index)
//{
// this.dictionary.RemoveAt(index);
//}
public bool TryGetValue(BEncodedString key, out BEncodedValue value)
{
return this.dictionary.TryGetValue(key, out value);
}
public BEncodedValue this[BEncodedString key]
{
get { return this.dictionary[key]; }
set { this.dictionary[key] = value; }
}
//public KeyValuePair<BEncodedString, IBEncodedValue> this[int index]
//{
// get { return this.dictionary[index]; }
// set { this.dictionary[index] = value; }
//}
public ICollection<BEncodedString> Keys
{
get { return this.dictionary.Keys; }
}
public ICollection<BEncodedValue> Values
{
get { return this.dictionary.Values; }
}
public IEnumerator<KeyValuePair<BEncodedString, BEncodedValue>> GetEnumerator()
{
return this.dictionary.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.dictionary.GetEnumerator();
}
#endregion
}
}

View File

@ -1,217 +0,0 @@
using System;
using System.Collections.Generic;
namespace MonoTorrent.BEncoding
{
/// <summary>
/// Class representing a BEncoded list
/// </summary>
public class BEncodedList : BEncodedValue, IList<BEncodedValue>
{
#region Member Variables
private List<BEncodedValue> list;
#endregion
#region Constructors
/// <summary>
/// Create a new BEncoded List with default capacity
/// </summary>
public BEncodedList()
: this(new List<BEncodedValue>())
{
}
/// <summary>
/// Create a new BEncoded List with the supplied capacity
/// </summary>
/// <param name="capacity">The initial capacity</param>
public BEncodedList(int capacity)
: this(new List<BEncodedValue>(capacity))
{
}
public BEncodedList(IEnumerable<BEncodedValue> list)
{
if (list == null)
throw new ArgumentNullException("list");
this.list = new List<BEncodedValue>(list);
}
private BEncodedList(List<BEncodedValue> value)
{
this.list = value;
}
#endregion
#region Encode/Decode Methods
/// <summary>
/// Encodes the list to a byte[]
/// </summary>
/// <param name="buffer">The buffer to encode the list to</param>
/// <param name="offset">The offset to start writing the data at</param>
/// <returns></returns>
public override int Encode(byte[] buffer, int offset)
{
int written = 0;
buffer[offset] = (byte)'l';
written++;
for (int i = 0; i < this.list.Count; i++)
written += this.list[i].Encode(buffer, offset + written);
buffer[offset + written] = (byte)'e';
written++;
return written;
}
/// <summary>
/// Decodes a BEncodedList from the given StreamReader
/// </summary>
/// <param name="reader"></param>
internal override void DecodeInternal(RawReader reader)
{
if (reader.ReadByte() != 'l') // Remove the leading 'l'
throw new BEncodingException("Invalid data found. Aborting");
while ((reader.PeekByte() != -1) && (reader.PeekByte() != 'e'))
list.Add(BEncodedValue.Decode(reader));
if (reader.ReadByte() != 'e') // Remove the trailing 'e'
throw new BEncodingException("Invalid data found. Aborting");
}
#endregion
#region Helper Methods
/// <summary>
/// Returns the size of the list in bytes
/// </summary>
/// <returns></returns>
public override int LengthInBytes()
{
int length = 0;
length += 1; // Lists start with 'l'
for (int i=0; i < this.list.Count; i++)
length += this.list[i].LengthInBytes();
length += 1; // Lists end with 'e'
return length;
}
#endregion
#region Overridden Methods
public override bool Equals(object obj)
{
BEncodedList other = obj as BEncodedList;
if (other == null)
return false;
for (int i = 0; i < this.list.Count; i++)
if (!this.list[i].Equals(other.list[i]))
return false;
return true;
}
public override int GetHashCode()
{
int result = 0;
for (int i = 0; i < list.Count; i++)
result ^= list[i].GetHashCode();
return result;
}
public override string ToString()
{
return System.Text.Encoding.UTF8.GetString(Encode());
}
#endregion
#region IList methods
public void Add(BEncodedValue item)
{
this.list.Add(item);
}
public void AddRange (IEnumerable<BEncodedValue> collection)
{
list.AddRange (collection);
}
public void Clear()
{
this.list.Clear();
}
public bool Contains(BEncodedValue item)
{
return this.list.Contains(item);
}
public void CopyTo(BEncodedValue[] array, int arrayIndex)
{
this.list.CopyTo(array, arrayIndex);
}
public int Count
{
get { return this.list.Count; }
}
public int IndexOf(BEncodedValue item)
{
return this.list.IndexOf(item);
}
public void Insert(int index, BEncodedValue item)
{
this.list.Insert(index, item);
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(BEncodedValue item)
{
return this.list.Remove(item);
}
public void RemoveAt(int index)
{
this.list.RemoveAt(index);
}
public BEncodedValue this[int index]
{
get { return this.list[index]; }
set { this.list[index] = value; }
}
public IEnumerator<BEncodedValue> GetEnumerator()
{
return this.list.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
}
}

View File

@ -1,206 +0,0 @@
using System;
namespace MonoTorrent.BEncoding
{
/// <summary>
/// Class representing a BEncoded number
/// </summary>
public class BEncodedNumber : BEncodedValue, IComparable<BEncodedNumber>
{
#region Member Variables
/// <summary>
/// The value of the BEncodedNumber
/// </summary>
public long Number
{
get { return number; }
set { number = value; }
}
internal long number;
#endregion
#region Constructors
public BEncodedNumber()
: this(0)
{
}
/// <summary>
/// Create a new BEncoded number with the given value
/// </summary>
/// <param name="initialValue">The inital value of the BEncodedNumber</param>
public BEncodedNumber(long value)
{
this.number = value;
}
public static implicit operator BEncodedNumber(long value)
{
return new BEncodedNumber(value);
}
#endregion
#region Encode/Decode Methods
/// <summary>
/// Encodes this number to the supplied byte[] starting at the supplied offset
/// </summary>
/// <param name="buffer">The buffer to write the data to</param>
/// <param name="offset">The offset to start writing the data at</param>
/// <returns></returns>
public override int Encode(byte[] buffer, int offset)
{
long number = this.number;
int written = offset;
buffer[written++] = (byte)'i';
if (number < 0)
{
buffer[written++] = (byte)'-';
number = -number;
}
// Reverse the number '12345' to get '54321'
long reversed = 0;
for (long i = number; i != 0; i /= 10)
reversed = reversed * 10 + i % 10;
// Write each digit of the reversed number to the array. We write '1'
// first, then '2', etc
for (long i = reversed; i != 0; i /= 10)
buffer[written++] = (byte)(i % 10 + '0');
if (number == 0)
buffer[written++] = (byte)'0';
// If the original number ends in one or more zeros, they are lost
// when we reverse the number. We add them back in here.
for (long i = number; i % 10 == 0 && number != 0; i /= 10)
buffer[written++] = (byte)'0';
buffer[written++] = (byte)'e';
return written - offset;
}
/// <summary>
/// Decodes a BEncoded number from the supplied RawReader
/// </summary>
/// <param name="reader">RawReader containing a BEncoded Number</param>
internal override void DecodeInternal(RawReader reader)
{
int sign = 1;
if (reader == null)
throw new ArgumentNullException("reader");
if (reader.ReadByte() != 'i') // remove the leading 'i'
throw new BEncodingException("Invalid data found. Aborting.");
if (reader.PeekByte() == '-')
{
sign = -1;
reader.ReadByte ();
}
int letter;
while (((letter = reader.PeekByte()) != -1) && letter != 'e')
{
if(letter < '0' || letter > '9')
throw new BEncodingException("Invalid number found.");
number = number * 10 + (letter - '0');
reader.ReadByte ();
}
if (reader.ReadByte() != 'e') //remove the trailing 'e'
throw new BEncodingException("Invalid data found. Aborting.");
number *= sign;
}
#endregion
#region Helper Methods
/// <summary>
/// Returns the length of the encoded string in bytes
/// </summary>
/// <returns></returns>
public override int LengthInBytes()
{
long number = this.number;
int count = 2; // account for the 'i' and 'e'
if (number == 0)
return count + 1;
if (number < 0)
{
number = -number;
count++;
}
for (long i = number; i != 0; i /= 10)
count++;
return count;
}
public int CompareTo(object other)
{
if (other is BEncodedNumber || other is long || other is int)
return CompareTo((BEncodedNumber)other);
return -1;
}
public int CompareTo(BEncodedNumber other)
{
if (other == null)
throw new ArgumentNullException("other");
return this.number.CompareTo(other.number);
}
public int CompareTo(long other)
{
return this.number.CompareTo(other);
}
#endregion
#region Overridden Methods
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
BEncodedNumber obj2 = obj as BEncodedNumber;
if (obj2 == null)
return false;
return (this.number == obj2.number);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return this.number.GetHashCode();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override string ToString()
{
return (this.number.ToString());
}
#endregion
}
}

View File

@ -1,217 +0,0 @@
using System;
using System.Text;
using MonoTorrent.Messages;
namespace MonoTorrent.BEncoding
{
/// <summary>
/// Class representing a BEncoded string
/// </summary>
public class BEncodedString : BEncodedValue, IComparable<BEncodedString>
{
#region Member Variables
/// <summary>
/// The value of the BEncodedString
/// </summary>
public string Text
{
get { return Encoding.UTF8.GetString(textBytes); }
set { textBytes = Encoding.UTF8.GetBytes(value); }
}
/// <summary>
/// The underlying byte[] associated with this BEncodedString
/// </summary>
public byte[] TextBytes
{
get { return this.textBytes; }
}
private byte[] textBytes;
#endregion
#region Constructors
/// <summary>
/// Create a new BEncodedString using UTF8 encoding
/// </summary>
public BEncodedString()
: this(new byte[0])
{
}
/// <summary>
/// Create a new BEncodedString using UTF8 encoding
/// </summary>
/// <param name="value"></param>
public BEncodedString(char[] value)
: this(System.Text.Encoding.UTF8.GetBytes(value))
{
}
/// <summary>
/// Create a new BEncodedString using UTF8 encoding
/// </summary>
/// <param name="value">Initial value for the string</param>
public BEncodedString(string value)
: this(System.Text.Encoding.UTF8.GetBytes(value))
{
}
/// <summary>
/// Create a new BEncodedString using UTF8 encoding
/// </summary>
/// <param name="value"></param>
public BEncodedString(byte[] value)
{
this.textBytes = value;
}
public static implicit operator BEncodedString(string value)
{
return new BEncodedString(value);
}
public static implicit operator BEncodedString(char[] value)
{
return new BEncodedString(value);
}
public static implicit operator BEncodedString(byte[] value)
{
return new BEncodedString(value);
}
#endregion
#region Encode/Decode Methods
/// <summary>
/// Encodes the BEncodedString to a byte[] using the supplied Encoding
/// </summary>
/// <param name="buffer">The buffer to encode the string to</param>
/// <param name="offset">The offset at which to save the data to</param>
/// <param name="e">The encoding to use</param>
/// <returns>The number of bytes encoded</returns>
public override int Encode(byte[] buffer, int offset)
{
int written = offset;
written += Message.WriteAscii(buffer, written, textBytes.Length.ToString ());
written += Message.WriteAscii(buffer, written, ":");
written += Message.Write(buffer, written, textBytes);
return written - offset;
}
/// <summary>
/// Decodes a BEncodedString from the supplied StreamReader
/// </summary>
/// <param name="reader">The StreamReader containing the BEncodedString</param>
internal override void DecodeInternal(RawReader reader)
{
if (reader == null)
throw new ArgumentNullException("reader");
int letterCount;
string length = string.Empty;
while ((reader.PeekByte() != -1) && (reader.PeekByte() != ':')) // read in how many characters
length += (char)reader.ReadByte(); // the string is
if (reader.ReadByte() != ':') // remove the ':'
throw new BEncodingException("Invalid data found. Aborting");
if (!int.TryParse(length, out letterCount))
throw new BEncodingException(string.Format("Invalid BEncodedString. Length was '{0}' instead of a number", length));
this.textBytes = new byte[letterCount];
if (reader.Read(textBytes, 0, letterCount) != letterCount)
throw new BEncodingException("Couldn't decode string");
}
#endregion
#region Helper Methods
public string Hex
{
get { return BitConverter.ToString(TextBytes); }
}
public override int LengthInBytes()
{
// The length is equal to the length-prefix + ':' + length of data
int prefix = 1; // Account for ':'
// Count the number of characters needed for the length prefix
for (int i = textBytes.Length; i != 0; i = i/10)
prefix += 1;
if (textBytes.Length == 0)
prefix++;
return prefix + textBytes.Length;
}
public int CompareTo(object other)
{
return CompareTo(other as BEncodedString);
}
public int CompareTo(BEncodedString other)
{
if (other == null)
return 1;
int difference=0;
int length = this.textBytes.Length > other.textBytes.Length ? other.textBytes.Length : this.textBytes.Length;
for (int i = 0; i < length; i++)
if ((difference = this.textBytes[i].CompareTo(other.textBytes[i])) != 0)
return difference;
if (this.textBytes.Length == other.textBytes.Length)
return 0;
return this.textBytes.Length > other.textBytes.Length ? 1 : -1;
}
#endregion
#region Overridden Methods
public override bool Equals(object obj)
{
if (obj == null)
return false;
BEncodedString other;
if (obj is string)
other = new BEncodedString((string)obj);
else if (obj is BEncodedString)
other = (BEncodedString)obj;
else
return false;
return Toolbox.ByteMatch(this.textBytes, other.textBytes);
}
public override int GetHashCode()
{
int hash = 0;
for (int i = 0; i < this.textBytes.Length; i++)
hash += this.textBytes[i];
return hash;
}
public override string ToString()
{
return System.Text.Encoding.UTF8.GetString(textBytes);
}
#endregion
}
}

View File

@ -1,29 +0,0 @@
using System;
using System.Runtime.Serialization;
namespace MonoTorrent.BEncoding
{
[Serializable]
public class BEncodingException : Exception
{
public BEncodingException()
: base()
{
}
public BEncodingException(string message)
: base(message)
{
}
public BEncodingException(string message, Exception innerException)
: base(message, innerException)
{
}
protected BEncodingException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@ -1,202 +0,0 @@
using System;
using System.IO;
namespace MonoTorrent.BEncoding
{
/// <summary>
/// Base interface for all BEncoded values.
/// </summary>
public abstract class BEncodedValue
{
internal abstract void DecodeInternal(RawReader reader);
/// <summary>
/// Encodes the BEncodedValue into a byte array
/// </summary>
/// <returns>Byte array containing the BEncoded Data</returns>
public byte[] Encode()
{
byte[] buffer = new byte[LengthInBytes()];
if (Encode(buffer, 0) != buffer.Length)
throw new BEncodingException("Error encoding the data");
return buffer;
}
/// <summary>
/// Encodes the BEncodedValue into the supplied buffer
/// </summary>
/// <param name="buffer">The buffer to encode the information to</param>
/// <param name="offset">The offset in the buffer to start writing the data</param>
/// <returns></returns>
public abstract int Encode(byte[] buffer, int offset);
public static T Clone <T> (T value)
where T : BEncodedValue
{
Check.Value (value);
return (T) BEncodedValue.Decode (value.Encode ());
}
/// <summary>
/// Interface for all BEncoded values
/// </summary>
/// <param name="data">The byte array containing the BEncoded data</param>
/// <returns></returns>
public static BEncodedValue Decode(byte[] data)
{
if (data == null)
throw new ArgumentNullException("data");
using (RawReader stream = new RawReader(new MemoryStream(data)))
return (Decode(stream));
}
internal static BEncodedValue Decode(byte[] buffer, bool strictDecoding)
{
return Decode(buffer, 0, buffer.Length, strictDecoding);
}
/// <summary>
/// Decode BEncoded data in the given byte array
/// </summary>
/// <param name="buffer">The byte array containing the BEncoded data</param>
/// <param name="offset">The offset at which the data starts at</param>
/// <param name="length">The number of bytes to be decoded</param>
/// <returns>BEncodedValue containing the data that was in the byte[]</returns>
public static BEncodedValue Decode(byte[] buffer, int offset, int length)
{
return Decode(buffer, offset, length, true);
}
public static BEncodedValue Decode(byte[] buffer, int offset, int length, bool strictDecoding)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || length < 0)
throw new IndexOutOfRangeException("Neither offset or length can be less than zero");
if (offset > buffer.Length - length)
throw new ArgumentOutOfRangeException("length");
using (RawReader reader = new RawReader(new MemoryStream(buffer, offset, length), strictDecoding))
return (BEncodedValue.Decode(reader));
}
/// <summary>
/// Decode BEncoded data in the given stream
/// </summary>
/// <param name="stream">The stream containing the BEncoded data</param>
/// <returns>BEncodedValue containing the data that was in the stream</returns>
public static BEncodedValue Decode(Stream stream)
{
if (stream == null)
throw new ArgumentNullException("stream");
return Decode(new RawReader(stream));
}
/// <summary>
/// Decode BEncoded data in the given RawReader
/// </summary>
/// <param name="reader">The RawReader containing the BEncoded data</param>
/// <returns>BEncodedValue containing the data that was in the stream</returns>
public static BEncodedValue Decode(RawReader reader)
{
if (reader == null)
throw new ArgumentNullException("reader");
BEncodedValue data;
switch (reader.PeekByte())
{
case ('i'): // Integer
data = new BEncodedNumber();
break;
case ('d'): // Dictionary
data = new BEncodedDictionary();
break;
case ('l'): // List
data = new BEncodedList();
break;
case ('1'): // String
case ('2'):
case ('3'):
case ('4'):
case ('5'):
case ('6'):
case ('7'):
case ('8'):
case ('9'):
case ('0'):
data = new BEncodedString();
break;
default:
throw new BEncodingException("Could not find what value to decode");
}
data.DecodeInternal(reader);
return data;
}
/// <summary>
/// Interface for all BEncoded values
/// </summary>
/// <param name="data">The byte array containing the BEncoded data</param>
/// <returns></returns>
public static T Decode<T>(byte[] data) where T : BEncodedValue
{
return (T)BEncodedValue.Decode(data);
}
/// <summary>
/// Decode BEncoded data in the given byte array
/// </summary>
/// <param name="buffer">The byte array containing the BEncoded data</param>
/// <param name="offset">The offset at which the data starts at</param>
/// <param name="length">The number of bytes to be decoded</param>
/// <returns>BEncodedValue containing the data that was in the byte[]</returns>
public static T Decode<T>(byte[] buffer, int offset, int length) where T : BEncodedValue
{
return BEncodedValue.Decode<T>(buffer, offset, length, true);
}
public static T Decode<T>(byte[] buffer, int offset, int length, bool strictDecoding) where T : BEncodedValue
{
return (T)BEncodedValue.Decode(buffer, offset, length, strictDecoding);
}
/// <summary>
/// Decode BEncoded data in the given stream
/// </summary>
/// <param name="stream">The stream containing the BEncoded data</param>
/// <returns>BEncodedValue containing the data that was in the stream</returns>
public static T Decode<T>(Stream stream) where T : BEncodedValue
{
return (T)BEncodedValue.Decode(stream);
}
public static T Decode<T>(RawReader reader) where T : BEncodedValue
{
return (T)BEncodedValue.Decode(reader);
}
/// <summary>
/// Returns the size of the byte[] needed to encode this BEncodedValue
/// </summary>
/// <returns></returns>
public abstract int LengthInBytes();
}
}

View File

@ -1,127 +0,0 @@
using System;
using System.IO;
namespace MonoTorrent.BEncoding
{
public class RawReader : Stream
{
bool hasPeek;
Stream input;
byte[] peeked;
bool strictDecoding;
public bool StrictDecoding
{
get { return strictDecoding; }
}
public RawReader(Stream input)
: this(input, true)
{
}
public RawReader(Stream input, bool strictDecoding)
{
this.input = input;
this.peeked = new byte[1];
this.strictDecoding = strictDecoding;
}
public override bool CanRead
{
get { return input.CanRead; }
}
public override bool CanSeek
{
get { return input.CanSeek; }
}
public override bool CanWrite
{
get { return false; }
}
public override void Flush()
{
throw new NotSupportedException();
}
public override long Length
{
get { return input.Length; }
}
public int PeekByte()
{
if (!hasPeek)
hasPeek = Read(peeked, 0, 1) == 1;
return hasPeek ? peeked[0] : -1;
}
public override int ReadByte()
{
if (hasPeek)
{
hasPeek = false;
return peeked[0];
}
return base.ReadByte();
}
public override long Position
{
get
{
if (hasPeek)
return input.Position - 1;
return input.Position;
}
set
{
if (value != Position)
{
hasPeek = false;
input.Position = value;
}
}
}
public override int Read(byte[] buffer, int offset, int count)
{
int read = 0;
if (hasPeek && count > 0)
{
hasPeek = false;
buffer[offset] = peeked[0];
offset++;
count--;
read++;
}
read += input.Read(buffer, offset, count);
return read;
}
public override long Seek(long offset, SeekOrigin origin)
{
long val;
if (hasPeek && origin == SeekOrigin.Current)
val = input.Seek(offset - 1, origin);
else
val = input.Seek(offset, origin);
hasPeek = false;
return val;
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}
}

View File

@ -1,420 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace MonoTorrent
{
/// <summary>
/// This class is for represting the Peer's bitfield
/// </summary>
public class BitField : ICloneable, IEnumerable<bool>
{
#region Member Variables
private int[] array;
private int length;
private int trueCount;
internal bool AllFalse
{
get { return this.trueCount == 0; }
}
internal bool AllTrue
{
get { return this.trueCount == this.length; }
}
public int Length
{
get { return this.length; }
}
public double PercentComplete
{
get { return (double)this.trueCount / this.length * 100.0; }
}
#endregion
#region Constructors
public BitField(byte[] array, int length)
: this(length)
{
this.FromArray(array, 0, array.Length);
}
public BitField(int length)
{
if (length < 0)
throw new ArgumentOutOfRangeException("length");
this.length = length;
this.array = new int[(length + 31) / 32];
}
public BitField(bool[] array)
{
this.length = array.Length;
this.array = new int[(array.Length + 31) / 32];
for (int i = 0; i < array.Length; i++)
this.Set(i, array[i]);
}
#endregion
#region Methods BitArray
public bool this[int index]
{
get { return this.Get(index); }
internal set { this.Set(index, value); }
}
object ICloneable.Clone()
{
return this.Clone();
}
public BitField Clone()
{
BitField b = new BitField(this.length);
Buffer.BlockCopy(this.array, 0, b.array, 0, this.array.Length * 4);
b.trueCount = this.trueCount;
return b;
}
public BitField From(BitField value)
{
this.Check(value);
Buffer.BlockCopy(value.array, 0, this.array, 0, this.array.Length * 4);
this.trueCount = value.trueCount;
return this;
}
public BitField Not()
{
for (int i = 0; i < this.array.Length; i++)
this.array[i] = ~this.array[i];
this.trueCount = this.length - this.trueCount;
return this;
}
public BitField And(BitField value)
{
this.Check(value);
for (int i = 0; i < this.array.Length; i++)
this.array[i] &= value.array[i];
this.Validate();
return this;
}
internal BitField NAnd(BitField value)
{
this.Check(value);
for (int i = 0; i < this.array.Length; i++)
this.array[i] &= ~value.array[i];
this.Validate();
return this;
}
public BitField Or(BitField value)
{
this.Check(value);
for (int i = 0; i < this.array.Length; i++)
this.array[i] |= value.array[i];
this.Validate();
return this;
}
public BitField Xor(BitField value)
{
this.Check(value);
for (int i = 0; i < this.array.Length; i++)
this.array[i] ^= value.array[i];
this.Validate();
return this;
}
public override bool Equals(object obj)
{
BitField bf = obj as BitField;
if (bf == null || this.array.Length != bf.array.Length || this.TrueCount != bf.TrueCount)
return false;
for (int i = 0; i < this.array.Length; i++)
if (this.array[i] != bf.array[i])
return false;
return true;
}
public int FirstTrue()
{
return this.FirstTrue(0, this.length);
}
public int FirstTrue(int startIndex, int endIndex)
{
int start;
int end;
// If the number of pieces is an exact multiple of 32, we need to decrement by 1 so we don't overrun the array
// For the case when endIndex == 0, we need to ensure we don't go negative
int loopEnd = Math.Min((endIndex / 32), this.array.Length - 1);
for (int i = (startIndex / 32); i <= loopEnd; i++)
{
if (this.array[i] == 0) // This one has no true values
continue;
start = i * 32;
end = start + 32;
start = (start < startIndex) ? startIndex : start;
end = (end > this.length) ? this.length : end;
end = (end > endIndex) ? endIndex : end;
if (end == this.Length && end > 0)
end--;
for (int j = start; j <= end; j++)
if (this.Get(j)) // This piece is true
return j;
}
return -1; // Nothing is true
}
public int FirstFalse()
{
return this.FirstFalse(0, this.Length);
}
public int FirstFalse(int startIndex, int endIndex)
{
int start;
int end;
// If the number of pieces is an exact multiple of 32, we need to decrement by 1 so we don't overrun the array
// For the case when endIndex == 0, we need to ensure we don't go negative
int loopEnd = Math.Min((endIndex / 32), this.array.Length - 1);
for (int i = (startIndex / 32); i <= loopEnd; i++)
{
if (this.array[i] == ~0) // This one has no false values
continue;
start = i * 32;
end = start + 32;
start = (start < startIndex) ? startIndex : start;
end = (end > this.length) ? this.length : end;
end = (end > endIndex) ? endIndex : end;
if (end == this.Length && end > 0)
end--;
for (int j = start; j <= end; j++)
if (!this.Get(j)) // This piece is true
return j;
}
return -1; // Nothing is true
}
internal void FromArray(byte[] buffer, int offset, int length)
{
int end = this.Length / 32;
for (int i = 0; i < end; i++)
this.array[i] = (buffer[offset++] << 24) |
(buffer[offset++] << 16) |
(buffer[offset++] << 8) |
(buffer[offset++] << 0);
int shift = 24;
for (int i = end * 32; i < this.Length; i += 8)
{
this.array[this.array.Length - 1] |= buffer[offset++] << shift;
shift -= 8;
}
this.Validate();
}
bool Get(int index)
{
if (index < 0 || index >= this.length)
throw new ArgumentOutOfRangeException("index");
return (this.array[index >> 5] & (1 << (31 - (index & 31)))) != 0;
}
public IEnumerator<bool> GetEnumerator()
{
for (int i = 0; i < this.length; i++)
yield return this.Get(i);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public override int GetHashCode()
{
int count = 0;
for (int i = 0; i < this.array.Length; i++)
count += this.array[i];
return count;
}
public int LengthInBytes
{
get { return (this.length + 7) / 8; } //8 bits in a byte.
}
public BitField Set(int index, bool value)
{
if (index < 0 || index >= this.length)
throw new ArgumentOutOfRangeException("index");
if (value)
{
if ((this.array[index >> 5] & (1 << (31 - (index & 31)))) == 0)// If it's not already true
this.trueCount++; // Increase true count
this.array[index >> 5] |= (1 << (31 - index & 31));
}
else
{
if ((this.array[index >> 5] & (1 << (31 - (index & 31)))) != 0)// If it's not already false
this.trueCount--; // Decrease true count
this.array[index >> 5] &= ~(1 << (31 - (index & 31)));
}
return this;
}
internal BitField SetTrue(params int[] indices)
{
foreach (int index in indices)
this.Set(index, true);
return this;
}
internal BitField SetFalse(params int[] indices)
{
foreach (int index in indices)
this.Set(index, false);
return this;
}
internal BitField SetAll(bool value)
{
if (value)
{
for (int i = 0; i < this.array.Length; i++)
this.array[i] = ~0;
this.Validate();
}
else
{
for (int i = 0; i < this.array.Length; i++)
this.array[i] = 0;
this.trueCount = 0;
}
return this;
}
internal byte[] ToByteArray()
{
byte[] data = new byte[this.LengthInBytes];
this.ToByteArray(data, 0);
return data;
}
internal void ToByteArray(byte[] buffer, int offset)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
this.ZeroUnusedBits();
int end = this.Length / 32;
for (int i = 0; i < end; i++)
{
buffer[offset++] = (byte)(this.array[i] >> 24);
buffer[offset++] = (byte)(this.array[i] >> 16);
buffer[offset++] = (byte)(this.array[i] >> 8);
buffer[offset++] = (byte)(this.array[i] >> 0);
}
int shift = 24;
for (int i = end * 32; i < this.Length; i += 8)
{
buffer[offset++] = (byte)(this.array[this.array.Length - 1] >> shift);
shift -= 8;
}
}
public override string ToString()
{
StringBuilder sb = new StringBuilder(this.array.Length * 16);
for (int i = 0; i < this.Length; i++)
{
sb.Append(this.Get(i) ? 'T' : 'F');
sb.Append(' ');
}
return sb.ToString(0, sb.Length - 1);
}
public int TrueCount
{
get { return this.trueCount; }
}
void Validate()
{
this.ZeroUnusedBits();
// Update the population count
uint count = 0;
for (int i = 0; i < this.array.Length; i++)
{
uint v = (uint)this.array[i];
v = v - ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
count += (((v + (v >> 4) & 0xF0F0F0F) * 0x1010101)) >> 24;
}
this.trueCount = (int)count ;
}
void ZeroUnusedBits()
{
if (this.array.Length == 0)
return;
// Zero the unused bits
int shift = 32 - this.length % 32;
if (shift != 0)
this.array[this.array.Length - 1] &= (-1 << shift);
}
void Check(BitField value)
{
MonoTorrent.Check.Value(value);
if (this.length != value.length)
throw new ArgumentException("BitFields are of different lengths", "value");
}
#endregion
}
}

View File

@ -1,235 +0,0 @@
using System;
namespace MonoTorrent
{
public static class Check
{
static void DoCheck(object toCheck, string name)
{
if (toCheck == null)
throw new ArgumentNullException(name);
}
static void IsNullOrEmpty(string toCheck, string name)
{
DoCheck(toCheck, name);
if (toCheck.Length == 0)
throw new ArgumentException("Cannot be empty", name);
}
public static void Address(object address)
{
DoCheck(address, "address");
}
public static void AddressRange(object addressRange)
{
DoCheck(addressRange, "addressRange");
}
public static void AddressRanges(object addressRanges)
{
DoCheck(addressRanges, "addressRanges");
}
public static void Announces(object announces)
{
DoCheck(announces, "announces");
}
public static void BaseDirectory(object baseDirectory)
{
DoCheck(baseDirectory, "baseDirectory");
}
internal static void BaseType(Type baseType)
{
DoCheck(baseType, "baseType");
}
internal static void Buffer(object buffer)
{
DoCheck(buffer, "buffer");
}
internal static void Cache(object cache)
{
DoCheck(cache, "cache");
}
public static void Data(object data)
{
DoCheck(data, "data");
}
public static void Destination (object destination)
{
DoCheck (destination, "destination");
}
public static void Endpoint(object endpoint)
{
DoCheck(endpoint, "endpoint");
}
public static void File(object file)
{
DoCheck(file, "file");
}
public static void Files(object files)
{
DoCheck(files, "files");
}
public static void FileSource(object fileSource)
{
DoCheck(fileSource, "fileSource");
}
public static void InfoHash(object infoHash)
{
DoCheck(infoHash, "infoHash");
}
public static void Key (object key)
{
DoCheck (key, "key");
}
public static void Limiter(object limiter)
{
DoCheck(limiter, "limiter");
}
public static void Listener(object listener)
{
DoCheck(listener, "listener");
}
public static void Location(object location)
{
DoCheck(location, "location");
}
public static void MagnetLink(object magnetLink)
{
DoCheck(magnetLink, "magnetLink");
}
public static void Manager(object manager)
{
DoCheck(manager, "manager");
}
public static void Mappings (object mappings)
{
DoCheck (mappings, "mappings");
}
public static void Metadata(object metadata)
{
DoCheck(metadata, "metadata");
}
public static void Name (object name)
{
DoCheck (name, "name");
}
public static void Path(object path)
{
DoCheck(path, "path");
}
public static void Paths (object paths)
{
DoCheck (paths, "paths");
}
public static void PathNotEmpty(string path)
{
IsNullOrEmpty(path, "path");
}
public static void Peer (object peer)
{
DoCheck (peer, "peer");
}
public static void Peers (object peers)
{
DoCheck (peers, "peers");
}
public static void Picker(object picker)
{
DoCheck(picker, "picker");
}
public static void Result(object result)
{
DoCheck(result, "result");
}
public static void SavePath(object savePath)
{
DoCheck(savePath, "savePath");
}
public static void Settings(object settings)
{
DoCheck(settings, "settings");
}
internal static void SpecificType(Type specificType)
{
DoCheck(specificType, "specificType");
}
public static void Stream(object stream)
{
DoCheck(stream, "stream");
}
public static void Torrent(object torrent)
{
DoCheck(torrent, "torrent");
}
public static void TorrentInformation(object torrentInformation)
{
DoCheck(torrentInformation, "torrentInformation");
}
public static void TorrentSave(object torrentSave)
{
DoCheck(torrentSave, "torrentSave");
}
public static void Tracker(object tracker)
{
DoCheck(tracker, "tracker");
}
public static void Url(object url)
{
DoCheck(url, "url");
}
public static void Uri(Uri uri)
{
DoCheck(uri, "uri");
}
public static void Value(object value)
{
DoCheck(value, "value");
}
public static void Writer(object writer)
{
DoCheck(writer, "writer");
}
}
}

View File

@ -1,13 +0,0 @@
namespace MonoTorrent
{
public enum Priority
{
DoNotDownload = 0,
Lowest = 1,
Low = 2,
Normal = 4,
High = 8,
Highest = 16,
Immediate = 32
}
}

View File

@ -1,30 +0,0 @@
using System;
namespace MonoTorrent.Exceptions
{
public class MessageException : TorrentException
{
public MessageException()
: base()
{
}
public MessageException(string message)
: base(message)
{
}
public MessageException(string message, Exception innerException)
: base(message, innerException)
{
}
public MessageException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
: base(info, context)
{
}
}
}

View File

@ -1,41 +0,0 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
namespace MonoTorrent
{
public static class HashAlgoFactory
{
static Dictionary<Type, Type> algos = new Dictionary<Type, Type>();
static HashAlgoFactory()
{
Register<MD5, MD5CryptoServiceProvider>();
Register<SHA1, SHA1CryptoServiceProvider>();
}
public static void Register<T, U>()
where T : HashAlgorithm
where U : HashAlgorithm
{
Register(typeof(T), typeof(U));
}
public static void Register(Type baseType, Type specificType)
{
Check.BaseType(baseType);
Check.SpecificType(specificType);
lock (algos)
algos[baseType] = specificType;
}
public static T Create<T>()
where T : HashAlgorithm
{
if (algos.ContainsKey(typeof(T)))
return (T)Activator.CreateInstance(algos[typeof(T)]);
return null;
}
}
}

View File

@ -1,93 +0,0 @@
using System;
namespace MonoTorrent
{
public class Hashes
{
#region Constants
/// <summary>
/// Hash code length (in bytes)
/// </summary>
internal static readonly int HashCodeLength = 20;
#endregion
#region Private Fields
private int count;
private byte[] hashData;
#endregion Private Fields
#region Properties
/// <summary>
/// Number of Hashes (equivalent to number of Pieces)
/// </summary>
public int Count
{
get { return this.count; }
}
#endregion Properties
#region Constructors
internal Hashes(byte[] hashData, int count)
{
this.hashData = hashData;
this.count = count;
}
#endregion Constructors
#region Methods
/// <summary>
/// Determine whether a calculated hash is equal to our stored hash
/// </summary>
/// <param name="hash">Hash code to check</param>
/// <param name="hashIndex">Index of hash/piece to verify against</param>
/// <returns>true iff hash is equal to our stored hash, false otherwise</returns>
public bool IsValid(byte[] hash, int hashIndex)
{
if (hash == null)
throw new ArgumentNullException("hash");
if (hash.Length != HashCodeLength)
throw new ArgumentException(string.Format("Hash must be {0} bytes in length", HashCodeLength), "hash");
if (hashIndex < 0 || hashIndex > this.count)
throw new ArgumentOutOfRangeException("hashIndex", string.Format("hashIndex must be between 0 and {0}", this.count));
int start = hashIndex * HashCodeLength;
for (int i = 0; i < HashCodeLength; i++)
if (hash[i] != this.hashData[i + start])
return false;
return true;
}
/// <summary>
/// Returns the hash for a specific piece
/// </summary>
/// <param name="hashIndex">Piece/hash index to return</param>
/// <returns>byte[] (length HashCodeLength) containing hashdata</returns>
public byte[] ReadHash(int hashIndex)
{
if (hashIndex < 0 || hashIndex >= this.count)
throw new ArgumentOutOfRangeException("hashIndex");
// Read out our specified piece's hash data
byte[] hash = new byte[HashCodeLength];
Buffer.BlockCopy(this.hashData, hashIndex * HashCodeLength, hash, 0, HashCodeLength);
return hash;
}
#endregion Methods
}
}

View File

@ -1,169 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace MonoTorrent
{
public class InfoHash : IEquatable <InfoHash>
{
static Dictionary<char, byte> base32DecodeTable;
static InfoHash()
{
base32DecodeTable = new Dictionary<char, byte>();
string table = "abcdefghijklmnopqrstuvwxyz234567";
for (int i = 0; i < table.Length; i++)
base32DecodeTable[table[i]] = (byte)i;
}
byte[] hash;
internal byte[] Hash
{
get { return hash; }
}
public InfoHash(byte[] infoHash)
{
Check.InfoHash(infoHash);
if (infoHash.Length != 20)
throw new ArgumentException("Infohash must be exactly 20 bytes long");
hash = (byte[])infoHash.Clone();
}
public override bool Equals(object obj)
{
return Equals(obj as InfoHash);
}
public bool Equals(byte[] other)
{
return other == null || other.Length != 20 ? false : Toolbox.ByteMatch(Hash, other);
}
public bool Equals(InfoHash other)
{
return this == other;
}
public override int GetHashCode()
{
// Equality is based generally on checking 20 positions, checking 4 should be enough
// for the hashcode as infohashes are randomly distributed.
return Hash[0] | (Hash[1] << 8) | (Hash[2] << 16) | (Hash[3] << 24);
}
public byte[] ToArray()
{
return (byte[])hash.Clone();
}
public string ToHex()
{
StringBuilder sb = new StringBuilder(40);
for (int i = 0; i < hash.Length; i++)
{
string hex = hash[i].ToString("X");
if (hex.Length != 2)
sb.Append("0");
sb.Append(hex);
}
return sb.ToString();
}
public override string ToString()
{
return BitConverter.ToString(hash);
}
public string UrlEncode()
{
return UriHelper.UrlEncode(Hash);
}
public static bool operator ==(InfoHash left, InfoHash right)
{
if ((object)left == null)
return (object)right == null;
if ((object)right == null)
return false;
return Toolbox.ByteMatch(left.Hash, right.Hash);
}
public static bool operator !=(InfoHash left, InfoHash right)
{
return !(left == right);
}
public static InfoHash FromBase32(string infoHash)
{
Check.InfoHash (infoHash);
if (infoHash.Length != 32)
throw new ArgumentException("Infohash must be a base32 encoded 32 character string");
infoHash = infoHash.ToLower();
int infohashOffset =0 ;
byte[] hash = new byte[20];
var temp = new byte[8];
for (int i = 0; i < hash.Length; ) {
for (int j=0; j < 8; j++)
if (!base32DecodeTable.TryGetValue(infoHash[infohashOffset++], out temp[j]))
throw new ArgumentException ("infoHash", "Value is not a valid base32 encoded string");
//8 * 5bits = 40 bits = 5 bytes
hash[i++] = (byte)((temp[0] << 3) | (temp [1]>> 2));
hash[i++] = (byte)((temp[1] << 6) | (temp[2] << 1) | (temp[3] >> 4));
hash[i++] = (byte)((temp[3] << 4) | (temp [4]>> 1));
hash[i++] = (byte)((temp[4] << 7) | (temp[5] << 2) | (temp [6]>> 3));
hash[i++] = (byte)((temp[6] << 5) | temp[7]);
}
return new InfoHash(hash);
}
public static InfoHash FromHex(string infoHash)
{
Check.InfoHash (infoHash);
if (infoHash.Length != 40)
throw new ArgumentException("Infohash must be 40 characters long");
byte[] hash = new byte[20];
for (int i = 0; i < hash.Length; i++)
hash[i] = byte.Parse(infoHash.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
return new InfoHash(hash);
}
public static InfoHash FromMagnetLink(string magnetLink)
{
Check.MagnetLink(magnetLink);
if (!magnetLink.StartsWith("magnet:?"))
throw new ArgumentException("Invalid magnet link format");
magnetLink = magnetLink.Substring("magnet:?".Length);
int hashStart = magnetLink.IndexOf("xt=urn:btih:");
if (hashStart == -1)
throw new ArgumentException("Magnet link does not contain an infohash");
hashStart += "xt=urn:btih:".Length;
int hashEnd = magnetLink.IndexOf('&', hashStart);
if (hashEnd == -1)
hashEnd = magnetLink.Length;
switch (hashEnd - hashStart)
{
case 32:
return FromBase32(magnetLink.Substring(hashStart, 32));
case 40:
return FromHex(magnetLink.Substring(hashStart, 40));
default:
throw new ArgumentException("Infohash must be base32 or hex encoded.");
}
}
public static InfoHash UrlDecode(string infoHash)
{
Check.InfoHash(infoHash);
return new InfoHash(UriHelper.UrlDecode(infoHash));
}
}
}

View File

@ -1,95 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace MonoTorrent
{
public class MagnetLink
{
public RawTrackerTier AnnounceUrls {
get; private set;
}
public InfoHash InfoHash {
get; private set;
}
public string Name {
get; private set;
}
public List<string> Webseeds {
get; private set;
}
public MagnetLink (string url)
{
Check.Url (url);
AnnounceUrls = new RawTrackerTier ();
Webseeds = new List<string> ();
ParseMagnetLink (url);
}
void ParseMagnetLink (string url)
{
string[] splitStr = url.Split ('?');
if (splitStr.Length == 0 || splitStr[0] != "magnet:")
throw new FormatException ("The magnet link must start with 'magnet:?'.");
if (splitStr.Length == 1)
return;//no parametter
string[] parameters = splitStr[1].Split ('&', ';');
for (int i = 0; i < parameters.Length ; i++)
{
string[] keyval = parameters[i].Split ('=');
if (keyval.Length != 2)
throw new FormatException ("A field-value pair of the magnet link contain more than one equal'.");
switch (keyval[0].Substring(0, 2))
{
case "xt"://exact topic
if (InfoHash != null)
throw new FormatException ("More than one infohash in magnet link is not allowed.");
string val = keyval[1].Substring(9);
switch (keyval[1].Substring(0, 9))
{
case "urn:sha1:"://base32 hash
case "urn:btih:":
if (val.Length == 32)
InfoHash = InfoHash.FromBase32 (val);
else if (val.Length == 40)
InfoHash = InfoHash.FromHex (val);
else
throw new FormatException("Infohash must be base32 or hex encoded.");
break;
}
break;
case "tr" ://address tracker
var bytes = UriHelper.UrlDecode(keyval[1]);
AnnounceUrls.Add(Encoding.UTF8.GetString(bytes));
break;
case "as"://Acceptable Source
Webseeds.Add (keyval[1]);
break;
case "dn"://display name
var name = UriHelper.UrlDecode(keyval[1]);
Name = Encoding.UTF8.GetString(name);
break;
case "xl"://exact length
case "xs":// eXact Source - P2P link.
case "kt"://keyword topic
case "mt"://manifest topic
//not supported for moment
break;
default:
//not supported
break;
}
}
}
}
}

View File

@ -1,12 +0,0 @@
namespace MonoTorrent.Messages
{
interface IMessage
{
int ByteLength { get;}
byte[] Encode();
int Encode(byte[] buffer, int offset);
void Decode(byte[] buffer, int offset, int length);
}
}

View File

@ -1,164 +0,0 @@
using System;
using System.Net;
using MonoTorrent.Exceptions;
namespace MonoTorrent.Messages
{
public abstract class Message : IMessage
{
public abstract int ByteLength { get; }
protected int CheckWritten(int written)
{
if (written != this.ByteLength)
throw new MessageException("Message encoded incorrectly. Incorrect number of bytes written");
return written;
}
public abstract void Decode(byte[] buffer, int offset, int length);
public byte[] Encode()
{
byte[] buffer = new byte[this.ByteLength];
this.Encode(buffer, 0);
return buffer;
}
public abstract int Encode(byte[] buffer, int offset);
static public byte ReadByte(byte[] buffer, int offset)
{
return buffer[offset];
}
static public byte ReadByte(byte[] buffer, ref int offset)
{
byte b = buffer[offset];
offset++;
return b;
}
static public byte[] ReadBytes(byte[] buffer, int offset, int count)
{
return ReadBytes(buffer, ref offset, count);
}
static public byte[] ReadBytes(byte[] buffer, ref int offset, int count)
{
byte[] result = new byte[count];
Buffer.BlockCopy(buffer, offset, result, 0, count);
offset += count;
return result;
}
static public short ReadShort(byte[] buffer, int offset)
{
return ReadShort(buffer, ref offset);
}
static public short ReadShort(byte[] buffer, ref int offset)
{
short ret = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(buffer, offset));
offset += 2;
return ret;
}
static public string ReadString(byte[] buffer, int offset, int count)
{
return ReadString(buffer, ref offset, count);
}
static public string ReadString(byte[] buffer, ref int offset, int count)
{
string s = System.Text.Encoding.ASCII.GetString(buffer, offset, count);
offset += count;
return s;
}
static public int ReadInt(byte[] buffer, int offset)
{
return ReadInt(buffer, ref offset);
}
static public int ReadInt(byte[] buffer, ref int offset)
{
int ret = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(buffer, offset));
offset += 4;
return ret;
}
static public long ReadLong(byte[] buffer, int offset)
{
return ReadLong(buffer, ref offset);
}
static public long ReadLong(byte[] buffer, ref int offset)
{
long ret = IPAddress.NetworkToHostOrder(BitConverter.ToInt64(buffer, offset));
offset += 8;
return ret;
}
static public int Write(byte[] buffer, int offset, byte value)
{
buffer[offset] = value;
return 1;
}
static public int Write(byte[] dest, int destOffset, byte[] src, int srcOffset, int count)
{
Buffer.BlockCopy(src, srcOffset, dest, destOffset, count);
return count;
}
static public int Write(byte[] buffer, int offset, ushort value)
{
return Write(buffer, offset, (short)value);
}
static public int Write(byte[] buffer, int offset, short value)
{
offset += Write(buffer, offset, (byte)(value >> 8));
offset += Write(buffer, offset, (byte)value);
return 2;
}
static public int Write(byte[] buffer, int offset, int value)
{
offset += Write(buffer, offset, (byte)(value >> 24));
offset += Write(buffer, offset, (byte)(value >> 16));
offset += Write(buffer, offset, (byte)(value >> 8));
offset += Write(buffer, offset, (byte)(value));
return 4;
}
static public int Write(byte[] buffer, int offset, uint value)
{
return Write(buffer, offset, (int)value);
}
static public int Write(byte[] buffer, int offset, long value)
{
offset += Write(buffer, offset, (int)(value >> 32));
offset += Write(buffer, offset, (int)value);
return 8;
}
static public int Write(byte[] buffer, int offset, ulong value)
{
return Write(buffer, offset, (long)value);
}
static public int Write(byte[] buffer, int offset, byte[] value)
{
return Write(buffer, offset, value, 0, value.Length);
}
static public int WriteAscii(byte[] buffer, int offset, string text)
{
for (int i = 0; i < text.Length; i++)
Write(buffer, offset + i, (byte)text[i]);
return text.Length;
}
}
}

View File

@ -1,8 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net462</TargetFramework>
<Platforms>x86</Platforms>
<Version>9.0.21022</Version>
</PropertyGroup>
</Project>

View File

@ -1,96 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using MonoTorrent.BEncoding;
namespace MonoTorrent
{
public class RawTrackerTier : IList<string>
{
public string this[int index] {
get { return ((BEncodedString) Tier [index]).Text; }
set { Tier [index] = new BEncodedString (value );}
}
internal BEncodedList Tier {
get; set;
}
public RawTrackerTier ()
: this (new BEncodedList ())
{
}
public RawTrackerTier (BEncodedList tier)
{
Tier = tier;
}
public RawTrackerTier (IEnumerable<string> announces)
: this ()
{
foreach (var v in announces)
Add (v);
}
public int IndexOf (string item)
{
return Tier.IndexOf ((BEncodedString) item);
}
public void Insert (int index, string item)
{
Tier.Insert (index, (BEncodedString) item);
}
public void RemoveAt (int index)
{
Tier.RemoveAt (index);
}
public void Add (string item)
{
Tier.Add ((BEncodedString) item);
}
public void Clear ()
{
Tier.Clear ();
}
public bool Contains (string item)
{
return Tier.Contains ((BEncodedString) item);
}
public void CopyTo (string[] array, int arrayIndex)
{
foreach (var s in this)
array [arrayIndex ++] = s;
}
public bool Remove (string item)
{
return Tier.Remove ((BEncodedString) item);
}
public int Count {
get { return Tier.Count; }
}
public bool IsReadOnly {
get { return Tier.IsReadOnly; }
}
public IEnumerator<string> GetEnumerator ()
{
foreach (BEncodedString v in Tier)
yield return v.Text;
}
IEnumerator IEnumerable.GetEnumerator ()
{
return GetEnumerator ();
}
}
}

View File

@ -1,104 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using MonoTorrent.BEncoding;
namespace MonoTorrent
{
public class RawTrackerTiers : IList<RawTrackerTier>
{
BEncodedList Tiers {
get; set;
}
public RawTrackerTiers ()
: this (new BEncodedList ())
{
}
public RawTrackerTiers (BEncodedList tiers)
{
Tiers = tiers;
}
public int IndexOf (RawTrackerTier item)
{
if (item != null) {
for (int i = 0; i < Tiers.Count; i++)
if (item.Tier == Tiers [i])
return i;
}
return -1;
}
public void Insert (int index, RawTrackerTier item)
{
Tiers.Insert (index, item.Tier);
}
public void RemoveAt (int index)
{
Tiers.RemoveAt (index);
}
public RawTrackerTier this[int index] {
get { return new RawTrackerTier ((BEncodedList) Tiers [index]); }
set { Tiers [index] = value.Tier; }
}
public void Add (RawTrackerTier item)
{
Tiers.Add (item.Tier);
}
public void AddRange (IEnumerable<RawTrackerTier> tiers)
{
foreach (var v in tiers)
Add (v);
}
public void Clear ()
{
Tiers.Clear ();
}
public bool Contains (RawTrackerTier item)
{
return IndexOf (item) != -1;
}
public void CopyTo (RawTrackerTier[] array, int arrayIndex)
{
foreach (var v in this)
array [arrayIndex ++] = v;
}
public bool Remove (RawTrackerTier item)
{
int index = IndexOf (item);
if (index != -1)
RemoveAt (index);
return index != -1;
}
public int Count {
get { return Tiers.Count; }
}
public bool IsReadOnly {
get { return Tiers.IsReadOnly; }
}
public IEnumerator<RawTrackerTier> GetEnumerator ()
{
foreach (var v in Tiers)
yield return new RawTrackerTier ((BEncodedList) v);
}
IEnumerator IEnumerable.GetEnumerator ()
{
return GetEnumerator ();
}
}
}

View File

@ -1,123 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace MonoTorrent
{
public delegate long Operation<T>(T target);
public static class Toolbox
{
private static Random r = new Random();
public static int Count<T>(IEnumerable<T> enumerable, Predicate<T> predicate)
{
int count = 0;
foreach (T t in enumerable)
if (predicate(t))
count++;
return count;
}
public static long Accumulate<T>(IEnumerable<T> enumerable, Operation<T> action)
{
long count = 0;
foreach (T t in enumerable)
count += action(t);
return count;
}
public static void RaiseAsyncEvent<T>(EventHandler<T> e, object o, T args)
where T : EventArgs
{
if (e == null)
return;
ThreadPool.QueueUserWorkItem(delegate {
if (e != null)
e(o, args);
});
}
/// <summary>
/// Randomizes the contents of the array
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array"></param>
public static void Randomize<T>(List<T> array)
{
List<T> clone = new List<T>(array);
array.Clear();
while (clone.Count > 0)
{
int index = r.Next(0, clone.Count);
array.Add(clone[index]);
clone.RemoveAt(index);
}
}
/// <summary>
/// Switches the positions of two elements in an array
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array"></param>
/// <param name="first"></param>
/// <param name="second"></param>
public static void Switch<T>(IList<T> array, int first, int second)
{
T obj = array[first];
array[first] = array[second];
array[second] = obj;
}
/// <summary>
/// Checks to see if the contents of two byte arrays are equal
/// </summary>
/// <param name="array1">The first array</param>
/// <param name="array2">The second array</param>
/// <returns>True if the arrays are equal, false if they aren't</returns>
public static bool ByteMatch(byte[] array1, byte[] array2)
{
if (array1 == null)
throw new ArgumentNullException("array1");
if (array2 == null)
throw new ArgumentNullException("array2");
if (array1.Length != array2.Length)
return false;
return ByteMatch(array1, 0, array2, 0, array1.Length);
}
/// <summary>
/// Checks to see if the contents of two byte arrays are equal
/// </summary>
/// <param name="array1">The first array</param>
/// <param name="array2">The second array</param>
/// <param name="offset1">The starting index for the first array</param>
/// <param name="offset2">The starting index for the second array</param>
/// <param name="count">The number of bytes to check</param>
/// <returns></returns>
public static bool ByteMatch(byte[] array1, int offset1, byte[] array2, int offset2, int count)
{
if (array1 == null)
throw new ArgumentNullException("array1");
if (array2 == null)
throw new ArgumentNullException("array2");
// If either of the arrays is too small, they're not equal
if ((array1.Length - offset1) < count || (array2.Length - offset2) < count)
return false;
// Check if any elements are unequal
for (int i = 0; i < count; i++)
if (array1[offset1 + i] != array2[offset2 + i])
return false;
return true;
}
}
}

View File

@ -1,885 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using MonoTorrent.BEncoding;
namespace MonoTorrent
{
/// <summary>
/// The "Torrent" class for both Tracker and Client should inherit from this
/// as it contains the fields that are common to both.
/// </summary>
public class Torrent : IEquatable<Torrent>
{
#region Private Fields
private BEncodedDictionary originalDictionary;
private BEncodedValue azureusProperties;
private IList<RawTrackerTier> announceUrls;
private string comment;
private string createdBy;
private DateTime creationDate;
private byte[] ed2k;
private string encoding;
internal InfoHash infoHash;
private bool isPrivate;
protected string name;
private BEncodedList nodes;
protected int pieceLength;
protected Hashes pieces;
private string publisher;
private string publisherUrl;
private byte[] sha1;
protected long size;
private string source;
protected TorrentFile[] torrentFiles;
protected string torrentPath;
private List<string> getRightHttpSeeds;
private byte[] metadata;
#endregion Private Fields
#region Properties
internal byte[] Metadata
{
get { return this.metadata; }
}
/// <summary>
/// The announce URLs contained within the .torrent file
/// </summary>
public IList<RawTrackerTier> AnnounceUrls
{
get { return this.announceUrls; }
}
/// <summary>
/// This dictionary is specific for azureus client
/// It can contain
/// dht_backup_enable (number)
/// Content (dictionnary)
/// Publisher
/// Description
/// Title
/// Creation Date
/// Content Hash
/// Revision Date
/// Thumbnail (string) = Base64 encoded image
/// Progressive
/// Speed Bps (number)
/// but not useful for MT
/// </summary>
public BEncodedValue AzureusProperties
{
get { return this.azureusProperties; }
}
/// <summary>
/// The comment contained within the .torrent file
/// </summary>
public string Comment
{
get { return this.comment; }
}
/// <summary>
/// The optional string showing who/what created the .torrent
/// </summary>
public string CreatedBy
{
get { return this.createdBy; }
}
/// <summary>
/// The creation date of the .torrent file
/// </summary>
public DateTime CreationDate
{
get { return this.creationDate; }
}
/// <summary>
/// The optional ED2K hash contained within the .torrent file
/// </summary>
public byte[] ED2K
{
get { return this.ed2k; }
}
/// <summary>
/// The encoding used by the client that created the .torrent file
/// </summary>
public string Encoding
{
get { return this.encoding; }
}
/// <summary>
/// The list of files contained within the .torrent which are available for download
/// </summary>
public TorrentFile[] Files
{
get { return this.torrentFiles; }
}
/// <summary>
/// This is the infohash that is generated by putting the "Info" section of a .torrent
/// through a ManagedSHA1 hasher.
/// </summary>
public InfoHash InfoHash
{
get { return this.infoHash; }
}
/// <summary>
/// Shows whether DHT is allowed or not. If it is a private torrent, no peer
/// sharing should be allowed.
/// </summary>
public bool IsPrivate
{
get { return this.isPrivate; }
}
/// <summary>
/// In the case of a single file torrent, this is the name of the file.
/// In the case of a multi file torrent, it is the name of the root folder.
/// </summary>
public string Name
{
get { return this.name; }
private set { this.name = value; }
}
/// <summary>
/// FIXME: No idea what this is.
/// </summary>
public BEncodedList Nodes
{
get { return this.nodes; }
}
/// <summary>
/// The length of each piece in bytes.
/// </summary>
public int PieceLength
{
get { return this.pieceLength; }
}
/// <summary>
/// This is the array of hashes contained within the torrent.
/// </summary>
public Hashes Pieces
{
get { return this.pieces; }
}
/// <summary>
/// The name of the Publisher
/// </summary>
public string Publisher
{
get { return this.publisher; }
}
/// <summary>
/// The Url of the publisher of either the content or the .torrent file
/// </summary>
public string PublisherUrl
{
get { return this.publisherUrl; }
}
/// <summary>
/// The optional SHA1 hash contained within the .torrent file
/// </summary>
public byte[] SHA1
{
get { return this.sha1; }
}
/// <summary>
/// The total size of all the files that have to be downloaded.
/// </summary>
public long Size
{
get { return this.size; }
private set { this.size = value; }
}
/// <summary>
/// The source of the .torrent file
/// </summary>
public string Source
{
get { return this.source; }
}
/// <summary>
/// This is the path at which the .torrent file is located
/// </summary>
public string TorrentPath
{
get { return this.torrentPath; }
internal set { this.torrentPath = value; }
}
/// <summary>
/// This is the http-based seeding (getright protocole)
/// </summary>
public List<string> GetRightHttpSeeds
{
get { return this.getRightHttpSeeds; }
}
#endregion Properties
#region Constructors
protected Torrent()
{
this.announceUrls = new RawTrackerTiers ();
this.comment = string.Empty;
this.createdBy = string.Empty;
this.creationDate = new DateTime(1970, 1, 1, 0, 0, 0);
this.encoding = string.Empty;
this.name = string.Empty;
this.publisher = string.Empty;
this.publisherUrl = string.Empty;
this.source = string.Empty;
this.getRightHttpSeeds = new List<string>();
}
#endregion
#region Public Methods
public override bool Equals(object obj)
{
return this.Equals(obj as Torrent);
}
public bool Equals(Torrent other)
{
if (other == null)
return false;
return this.infoHash == other.infoHash;
}
public override int GetHashCode()
{
return this.infoHash.GetHashCode();
}
internal byte [] ToBytes ()
{
return this.originalDictionary.Encode ();
}
internal BEncodedDictionary ToDictionary ()
{
// Give the user a copy of the original dictionary.
return BEncodedValue.Clone (this.originalDictionary);
}
public override string ToString()
{
return this.name;
}
#endregion Public Methods
#region Private Methods
/// <summary>
/// This method is called internally to read out the hashes from the info section of the
/// .torrent file.
/// </summary>
/// <param name="data">The byte[]containing the hashes from the .torrent file</param>
private void LoadHashPieces(byte[] data)
{
if (data.Length % 20 != 0)
throw new TorrentException("Invalid infohash detected");
this.pieces = new Hashes(data, data.Length / 20);
}
/// <summary>
/// This method is called internally to load in all the files found within the "Files" section
/// of the .torrents infohash
/// </summary>
/// <param name="list">The list containing the files available to download</param>
private void LoadTorrentFiles(BEncodedList list)
{
List<TorrentFile> files = new List<TorrentFile>();
int endIndex;
long length;
string path;
byte[] md5sum;
byte[] ed2k;
byte[] sha1;
int startIndex;
StringBuilder sb = new StringBuilder(32);
foreach (BEncodedDictionary dict in list)
{
length = 0;
path = null;
md5sum = null;
ed2k = null;
sha1 = null;
foreach (KeyValuePair<BEncodedString, BEncodedValue> keypair in dict)
{
switch (keypair.Key.Text)
{
case ("sha1"):
sha1 = ((BEncodedString)keypair.Value).TextBytes;
break;
case ("ed2k"):
ed2k = ((BEncodedString)keypair.Value).TextBytes;
break;
case ("length"):
length = long.Parse(keypair.Value.ToString());
break;
case ("path.utf-8"):
foreach (BEncodedString str in ((BEncodedList)keypair.Value))
{
sb.Append(str.Text);
sb.Append(Path.DirectorySeparatorChar);
}
path = sb.ToString(0, sb.Length - 1);
sb.Remove(0, sb.Length);
break;
case ("path"):
if (string.IsNullOrEmpty(path))
{
foreach (BEncodedString str in ((BEncodedList)keypair.Value))
{
sb.Append(str.Text);
sb.Append(Path.DirectorySeparatorChar);
}
path = sb.ToString(0, sb.Length - 1);
sb.Remove(0, sb.Length);
}
break;
case ("md5sum"):
md5sum = ((BEncodedString)keypair.Value).TextBytes;
break;
default:
break; //FIXME: Log unknown values
}
}
// A zero length file always belongs to the same piece as the previous file
if (length == 0)
{
if (files.Count > 0)
{
startIndex = files[files.Count - 1].EndPieceIndex;
endIndex = files[files.Count - 1].EndPieceIndex;
}
else
{
startIndex = 0;
endIndex = 0;
}
}
else
{
startIndex = (int)(this.size / this.pieceLength);
endIndex = (int)((this.size + length) / this.pieceLength);
if ((this.size + length) % this.pieceLength == 0)
endIndex--;
}
this.size += length;
files.Add(new TorrentFile(path, length, path, startIndex, endIndex, md5sum, ed2k, sha1));
}
this.torrentFiles = files.ToArray();
}
/// <summary>
/// This method is called internally to load the information found within the "Info" section
/// of the .torrent file
/// </summary>
/// <param name="dictionary">The dictionary representing the Info section of the .torrent file</param>
private void ProcessInfo(BEncodedDictionary dictionary)
{
this.metadata = dictionary.Encode();
this.pieceLength = int.Parse(dictionary["piece length"].ToString());
this.LoadHashPieces(((BEncodedString)dictionary["pieces"]).TextBytes);
foreach (KeyValuePair<BEncodedString, BEncodedValue> keypair in dictionary)
{
switch (keypair.Key.Text)
{
case ("source"):
this.source = keypair.Value.ToString();
break;
case ("sha1"):
this.sha1 = ((BEncodedString)keypair.Value).TextBytes;
break;
case ("ed2k"):
this.ed2k = ((BEncodedString)keypair.Value).TextBytes;
break;
case ("publisher-url.utf-8"):
if (keypair.Value.ToString().Length > 0)
this.publisherUrl = keypair.Value.ToString();
break;
case ("publisher-url"):
if ((String.IsNullOrEmpty(this.publisherUrl)) && (keypair.Value.ToString().Length > 0))
this.publisherUrl = keypair.Value.ToString();
break;
case ("publisher.utf-8"):
if (keypair.Value.ToString().Length > 0)
this.publisher = keypair.Value.ToString();
break;
case ("publisher"):
if ((String.IsNullOrEmpty(this.publisher)) && (keypair.Value.ToString().Length > 0))
this.publisher = keypair.Value.ToString();
break;
case ("files"):
this.LoadTorrentFiles(((BEncodedList)keypair.Value));
break;
case ("name.utf-8"):
if (keypair.Value.ToString().Length > 0)
this.name = keypair.Value.ToString();
break;
case ("name"):
if ((String.IsNullOrEmpty(this.name)) && (keypair.Value.ToString().Length > 0))
this.name = keypair.Value.ToString();
break;
case ("piece length"): // Already handled
break;
case ("length"):
break; // This is a singlefile torrent
case ("private"):
this.isPrivate = (keypair.Value.ToString() == "1") ? true : false;
break;
default:
break;
}
}
if (this.torrentFiles == null) // Not a multi-file torrent
{
long length = long.Parse(dictionary["length"].ToString());
this.size = length;
string path = this.name;
byte[] md5 = (dictionary.ContainsKey("md5")) ? ((BEncodedString)dictionary["md5"]).TextBytes : null;
byte[] ed2k = (dictionary.ContainsKey("ed2k")) ? ((BEncodedString)dictionary["ed2k"]).TextBytes : null;
byte[] sha1 = (dictionary.ContainsKey("sha1")) ? ((BEncodedString)dictionary["sha1"]).TextBytes : null;
this.torrentFiles = new TorrentFile[1];
int endPiece = Math.Min(this.Pieces.Count - 1, (int)((this.size + (this.pieceLength - 1)) / this.pieceLength));
this.torrentFiles[0] = new TorrentFile(path, length, path, 0, endPiece, md5, ed2k, sha1);
}
}
#endregion Private Methods
#region Loading methods
/// <summary>
/// This method loads a .torrent file from the specified path.
/// </summary>
/// <param name="path">The path to load the .torrent file from</param>
public static Torrent Load(string path)
{
Check.Path(path);
using (Stream s = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
return Torrent.Load(s, path);
}
/// <summary>
/// Loads a torrent from a byte[] containing the bencoded data
/// </summary>
/// <param name="data">The byte[] containing the data</param>
/// <returns></returns>
public static Torrent Load(byte[] data)
{
Check.Data(data);
using (MemoryStream s = new MemoryStream(data))
return Load(s, "");
}
/// <summary>
/// Loads a .torrent from the supplied stream
/// </summary>
/// <param name="stream">The stream containing the data to load</param>
/// <returns></returns>
public static Torrent Load(Stream stream)
{
Check.Stream(stream);
if (stream == null)
throw new ArgumentNullException("stream");
return Torrent.Load(stream, "");
}
/// <summary>
/// Loads a .torrent file from the specified URL
/// </summary>
/// <param name="url">The URL to download the .torrent from</param>
/// <param name="location">The path to download the .torrent to before it gets loaded</param>
/// <returns></returns>
public static Torrent Load(Uri url, string location)
{
Check.Url(url);
Check.Location(location);
try
{
using (WebClient client = new WebClient())
client.DownloadFile(url, location);
}
catch (Exception ex)
{
throw new TorrentException("Could not download .torrent file from the specified url", ex);
}
return Torrent.Load(location);
}
/// <summary>
/// Loads a .torrent from the specificed path. A return value indicates
/// whether the operation was successful.
/// </summary>
/// <param name="path">The path to load the .torrent file from</param>
/// <param name="torrent">If the loading was succesful it is assigned the Torrent</param>
/// <returns>True if successful</returns>
public static bool TryLoad(string path, out Torrent torrent)
{
Check.Path(path);
try
{
torrent = Torrent.Load(path);
}
catch
{
torrent = null;
}
return torrent != null;
}
/// <summary>
/// Loads a .torrent from the specified byte[]. A return value indicates
/// whether the operation was successful.
/// </summary>
/// <param name="data">The byte[] to load the .torrent from</param>
/// <param name="torrent">If loading was successful, it contains the Torrent</param>
/// <returns>True if successful</returns>
public static bool TryLoad(byte[] data, out Torrent torrent)
{
Check.Data(data);
try
{
torrent = Torrent.Load(data);
}
catch
{
torrent = null;
}
return torrent != null;
}
/// <summary>
/// Loads a .torrent from the supplied stream. A return value indicates
/// whether the operation was successful.
/// </summary>
/// <param name="stream">The stream containing the data to load</param>
/// <param name="torrent">If the loading was succesful it is assigned the Torrent</param>
/// <returns>True if successful</returns>
public static bool TryLoad(Stream stream, out Torrent torrent)
{
Check.Stream(stream);
try
{
torrent = Torrent.Load(stream);
}
catch
{
torrent = null;
}
return torrent != null;
}
/// <summary>
/// Loads a .torrent file from the specified URL. A return value indicates
/// whether the operation was successful.
/// </summary>
/// <param name="url">The URL to download the .torrent from</param>
/// <param name="location">The path to download the .torrent to before it gets loaded</param>
/// <param name="torrent">If the loading was succesful it is assigned the Torrent</param>
/// <returns>True if successful</returns>
public static bool TryLoad(Uri url, string location, out Torrent torrent)
{
Check.Url(url);
Check.Location(location);
try
{
torrent = Torrent.Load(url, location);
}
catch
{
torrent = null;
}
return torrent != null;
}
/// <summary>
/// Called from either Load(stream) or Load(string).
/// </summary>
/// <param name="stream"></param>
/// <param name="path"></param>
/// <returns></returns>
private static Torrent Load(Stream stream, string path)
{
Check.Stream(stream);
Check.Path(path);
try
{
Torrent t = Torrent.LoadCore ((BEncodedDictionary) BEncodedDictionary.Decode(stream));
t.torrentPath = path;
return t;
}
catch (BEncodingException ex)
{
throw new TorrentException("Invalid torrent file specified", ex);
}
}
public static Torrent Load(BEncodedDictionary torrentInformation)
{
return LoadCore ((BEncodedDictionary)BEncodedValue.Decode (torrentInformation.Encode ()));
}
internal static Torrent LoadCore(BEncodedDictionary torrentInformation)
{
Check.TorrentInformation(torrentInformation);
Torrent t = new Torrent();
t.LoadInternal(torrentInformation);
return t;
}
protected void LoadInternal(BEncodedDictionary torrentInformation)
{
Check.TorrentInformation(torrentInformation);
this.originalDictionary = torrentInformation;
this.torrentPath = "";
try
{
foreach (KeyValuePair<BEncodedString, BEncodedValue> keypair in torrentInformation)
{
switch (keypair.Key.Text)
{
case ("announce"):
// Ignore this if we have an announce-list
if (torrentInformation.ContainsKey("announce-list"))
break;
this.announceUrls.Add(new RawTrackerTier ());
this.announceUrls[0].Add(keypair.Value.ToString());
break;
case ("creation date"):
try
{
try
{
this.creationDate = this.creationDate.AddSeconds(long.Parse(keypair.Value.ToString()));
}
catch (Exception e)
{
if (e is ArgumentOutOfRangeException)
this.creationDate = this.creationDate.AddMilliseconds(long.Parse(keypair.Value.ToString()));
else
throw;
}
}
catch (Exception e)
{
if (e is ArgumentOutOfRangeException)
throw new BEncodingException("Argument out of range exception when adding seconds to creation date.", e);
else if (e is FormatException)
throw new BEncodingException(String.Format("Could not parse {0} into a number", keypair.Value), e);
else
throw;
}
break;
case ("nodes"):
if (keypair.Value.ToString().Length != 0)
this.nodes = (BEncodedList)keypair.Value;
break;
case ("comment.utf-8"):
if (keypair.Value.ToString().Length != 0)
this.comment = keypair.Value.ToString(); // Always take the UTF-8 version
break; // even if there's an existing value
case ("comment"):
if (String.IsNullOrEmpty(this.comment))
this.comment = keypair.Value.ToString();
break;
case ("publisher-url.utf-8"): // Always take the UTF-8 version
this.publisherUrl = keypair.Value.ToString(); // even if there's an existing value
break;
case ("publisher-url"):
if (String.IsNullOrEmpty(this.publisherUrl))
this.publisherUrl = keypair.Value.ToString();
break;
case ("azureus_properties"):
this.azureusProperties = keypair.Value;
break;
case ("created by"):
this.createdBy = keypair.Value.ToString();
break;
case ("encoding"):
this.encoding = keypair.Value.ToString();
break;
case ("info"):
using (SHA1 s = HashAlgoFactory.Create<SHA1>())
this.infoHash = new InfoHash (s.ComputeHash(keypair.Value.Encode()));
this.ProcessInfo(((BEncodedDictionary)keypair.Value));
break;
case ("name"): // Handled elsewhere
break;
case ("announce-list"):
if (keypair.Value is BEncodedString)
break;
BEncodedList announces = (BEncodedList)keypair.Value;
for (int j = 0; j < announces.Count; j++)
{
if (announces[j] is BEncodedList)
{
BEncodedList bencodedTier = (BEncodedList)announces[j];
List<string> tier = new List<string>(bencodedTier.Count);
for (int k = 0; k < bencodedTier.Count; k++)
tier.Add(bencodedTier[k].ToString());
Toolbox.Randomize<string>(tier);
RawTrackerTier collection = new RawTrackerTier ();
for (int k = 0; k < tier.Count; k++)
collection.Add(tier[k]);
if (collection.Count != 0)
this.announceUrls.Add(collection);
}
else
{
throw new BEncodingException(String.Format("Non-BEncodedList found in announce-list (found {0})",
announces[j].GetType()));
}
}
break;
case ("httpseeds"):
// This form of web-seeding is not supported.
break;
case ("url-list"):
if (keypair.Value is BEncodedString)
{
this.getRightHttpSeeds.Add(((BEncodedString)keypair.Value).Text);
}
else if (keypair.Value is BEncodedList)
{
foreach (BEncodedString str in (BEncodedList)keypair.Value)
this.GetRightHttpSeeds.Add(str.Text);
}
break;
default:
break;
}
}
}
catch (Exception e)
{
if (e is BEncodingException)
throw;
else
throw new BEncodingException("", e);
}
}
#endregion Loading methods
}
}

View File

@ -1,28 +0,0 @@
using System;
namespace MonoTorrent
{
[Serializable]
public class TorrentException : Exception
{
public TorrentException()
: base()
{
}
public TorrentException(string message)
: base(message)
{
}
public TorrentException(string message, Exception innerException)
: base(message, innerException)
{
}
public TorrentException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
: base(info, context)
{
}
}
}

View File

@ -1,205 +0,0 @@
using System;
using System.Text;
namespace MonoTorrent
{
/// <summary>
/// This is the base class for the files available to download from within a .torrent.
/// This should be inherited by both Client and Tracker "TorrentFile" classes
/// </summary>
public class TorrentFile : IEquatable<TorrentFile>
{
#region Private Fields
private BitField bitfield;
private BitField selector;
private byte[] ed2k;
private int endPiece;
private string fullPath;
private long length;
private byte[] md5;
private string path;
private Priority priority;
private byte[] sha1;
private int startPiece;
#endregion Private Fields
#region Member Variables
/// <summary>
/// The number of pieces which have been successfully downloaded which are from this file
/// </summary>
public BitField BitField
{
get { return this.bitfield; }
}
public long BytesDownloaded
{
get { return (long)(this.BitField.PercentComplete * this.Length / 100.0); }
}
/// <summary>
/// The ED2K hash of the file
/// </summary>
public byte[] ED2K
{
get { return this.ed2k; }
}
/// <summary>
/// The index of the last piece of this file
/// </summary>
public int EndPieceIndex
{
get { return this.endPiece; }
}
public string FullPath
{
get { return this.fullPath; }
internal set { this.fullPath = value; }
}
/// <summary>
/// The length of the file in bytes
/// </summary>
public long Length
{
get { return this.length; }
}
/// <summary>
/// The MD5 hash of the file
/// </summary>
public byte[] MD5
{
get { return this.md5; }
internal set { this.md5 = value; }
}
/// <summary>
/// In the case of a single torrent file, this is the name of the file.
/// In the case of a multi-file torrent this is the relative path of the file
/// (including the filename) from the base directory
/// </summary>
public string Path
{
get { return this.path; }
}
/// <summary>
/// The priority of this torrent file
/// </summary>
public Priority Priority
{
get { return this.priority; }
set { this.priority = value; }
}
/// <summary>
/// The SHA1 hash of the file
/// </summary>
public byte[] SHA1
{
get { return this.sha1; }
}
/// <summary>
/// The index of the first piece of this file
/// </summary>
public int StartPieceIndex
{
get { return this.startPiece; }
}
#endregion
#region Constructors
public TorrentFile(string path, long length)
: this(path, length, path)
{
}
public TorrentFile (string path, long length, string fullPath)
: this (path, length, fullPath, 0, 0)
{
}
public TorrentFile (string path, long length, int startIndex, int endIndex)
: this (path, length, path, startIndex, endIndex)
{
}
public TorrentFile(string path, long length, string fullPath, int startIndex, int endIndex)
: this(path, length, fullPath, startIndex, endIndex, null, null, null)
{
}
public TorrentFile(string path, long length, string fullPath, int startIndex, int endIndex, byte[] md5, byte[] ed2k, byte[] sha1)
{
this.bitfield = new BitField(endIndex - startIndex + 1);
this.ed2k = ed2k;
this.endPiece = endIndex;
this.fullPath = fullPath;
this.length = length;
this.md5 = md5;
this.path = path;
this.priority = Priority.Normal;
this.sha1 = sha1;
this.startPiece = startIndex;
}
#endregion
#region Methods
public override bool Equals(object obj)
{
return this.Equals(obj as TorrentFile);
}
public bool Equals(TorrentFile other)
{
return other == null ? false : this.path == other.path && this.length == other.length; ;
}
public override int GetHashCode()
{
return this.path.GetHashCode();
}
internal BitField GetSelector(int totalPieces)
{
if (this.selector != null)
return this.selector;
this.selector = new BitField(totalPieces);
for (int i = this.StartPieceIndex; i <= this.EndPieceIndex; i++)
this.selector[i] = true;
return this.selector;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder(32);
sb.Append("File: ");
sb.Append(this.path);
sb.Append(" StartIndex: ");
sb.Append(this.StartPieceIndex);
sb.Append(" EndIndex: ");
sb.Append(this.EndPieceIndex);
return sb.ToString();
}
#endregion Methods
}
}

View File

@ -1,153 +0,0 @@
//
// System.Web.HttpUtility/HttpEncoder
//
// Authors:
// Patrik Torstensson (Patrik.Torstensson@labs2.com)
// Wictor Wilén (decode/encode functions) (wictor@ibizkit.se)
// Tim Coleman (tim@timcoleman.com)
using System;
using System.Text;
using System.IO;
using System.Collections.Generic;
namespace MonoTorrent
{
static class UriHelper
{
static readonly char [] hexChars = "0123456789abcdef".ToCharArray ();
public static string UrlEncode (byte[] bytes)
{
if (bytes == null)
throw new ArgumentNullException ("bytes");
var result = new MemoryStream (bytes.Length);
for (int i = 0; i < bytes.Length; i++)
UrlEncodeChar ((char)bytes [i], result, false);
return Encoding.ASCII.GetString (result.ToArray());
}
public static byte [] UrlDecode (string s)
{
if (null == s)
return null;
var e = Encoding.UTF8;
if (s.IndexOf ('%') == -1 && s.IndexOf ('+') == -1)
return e.GetBytes (s);
long len = s.Length;
var bytes = new List <byte> ();
int xchar;
char ch;
for (int i = 0; i < len; i++) {
ch = s [i];
if (ch == '%' && i + 2 < len && s [i + 1] != '%') {
if (s [i + 1] == 'u' && i + 5 < len) {
// unicode hex sequence
xchar = GetChar (s, i + 2, 4);
if (xchar != -1) {
WriteCharBytes (bytes, (char)xchar, e);
i += 5;
} else
WriteCharBytes (bytes, '%', e);
} else if ((xchar = GetChar (s, i + 1, 2)) != -1) {
WriteCharBytes (bytes, (char)xchar, e);
i += 2;
} else {
WriteCharBytes (bytes, '%', e);
}
continue;
}
if (ch == '+')
WriteCharBytes (bytes, ' ', e);
else
WriteCharBytes (bytes, ch, e);
}
return bytes.ToArray ();
}
static void UrlEncodeChar (char c, Stream result, bool isUnicode) {
if (c > ' ' && NotEncoded (c)) {
result.WriteByte ((byte)c);
return;
}
if (c==' ') {
result.WriteByte ((byte)'+');
return;
}
if ( (c < '0') ||
(c < 'A' && c > '9') ||
(c > 'Z' && c < 'a') ||
(c > 'z')) {
if (isUnicode && c > 127) {
result.WriteByte ((byte)'%');
result.WriteByte ((byte)'u');
result.WriteByte ((byte)'0');
result.WriteByte ((byte)'0');
}
else
result.WriteByte ((byte)'%');
int idx = ((int) c) >> 4;
result.WriteByte ((byte)hexChars [idx]);
idx = ((int) c) & 0x0F;
result.WriteByte ((byte)hexChars [idx]);
}
else {
result.WriteByte ((byte)c);
}
}
static int GetChar (string str, int offset, int length)
{
int val = 0;
int end = length + offset;
for (int i = offset; i < end; i++) {
char c = str [i];
if (c > 127)
return -1;
int current = GetInt ((byte) c);
if (current == -1)
return -1;
val = (val << 4) + current;
}
return val;
}
static int GetInt (byte b)
{
char c = (char) b;
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
static bool NotEncoded (char c)
{
return c == '!' || c == '(' || c == ')' || c == '*' || c == '-' || c == '.' || c == '_' || c == '\'';
}
static void WriteCharBytes (List<byte> buf, char ch, Encoding e)
{
if (ch > 255) {
foreach (byte b in e.GetBytes (new char[] { ch }))
buf.Add (b);
} else
buf.Add ((byte)ch);
}
}
}

View File

@ -206,7 +206,7 @@ private string DownloadFromMagnetUrl(RemoteEpisode remoteEpisode, string magnetU
try
{
hash = new MagnetLink(magnetUrl).InfoHash.ToHex();
hash = MagnetLink.Parse(magnetUrl).InfoHash.ToHex();
}
catch (FormatException ex)
{

View File

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using MonoTorrent;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Parser.Model;
@ -51,8 +52,7 @@ protected virtual string GetInfoHash(XElement item)
{
try
{
var magnetLink = new MonoTorrent.MagnetLink(magnetUrl);
return magnetLink.InfoHash.ToHex();
return MagnetLink.Parse(magnetUrl).InfoHash.ToHex();
}
catch
{

View File

@ -15,10 +15,10 @@
<PackageReference Include="RestSharp" Version="106.15.0" />
<PackageReference Include="TinyTwitter" Version="1.1.2" />
<PackageReference Include="xmlrpcnet" Version="3.0.0.266" />
<PackageReference Include="MonoTorrent" Version="1.0.29" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Marr.Data\Marr.Data.csproj" />
<ProjectReference Include="..\MonoTorrent\MonoTorrent.csproj" />
<ProjectReference Include="..\NzbDrone.Common\Sonarr.Common.csproj" />
</ItemGroup>
<ItemGroup>

View File

@ -83,8 +83,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sonarr.Windows.Test", "NzbD
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sonarr.Mono.Test", "NzbDrone.Mono.Test\Sonarr.Mono.Test.csproj", "{40D72824-7D02-4A77-9106-8FE0EEA2B997}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoTorrent", "MonoTorrent\MonoTorrent.csproj", "{411A9E0E-FDC6-4E25-828A-0C2CD1CD96F8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogentriesCore", "LogentriesCore\LogentriesCore.csproj", "{90D6E9FC-7B88-4E1B-B018-8FA742274558}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogentriesNLog", "LogentriesNLog\LogentriesNLog.csproj", "{9DC31DE3-79FF-47A8-96B4-6BA18F6BB1CB}"
@ -251,12 +249,6 @@ Global
{40D72824-7D02-4A77-9106-8FE0EEA2B997}.Mono|x86.Build.0 = Release|x86
{40D72824-7D02-4A77-9106-8FE0EEA2B997}.Release|x86.ActiveCfg = Release|x86
{40D72824-7D02-4A77-9106-8FE0EEA2B997}.Release|x86.Build.0 = Release|x86
{411A9E0E-FDC6-4E25-828A-0C2CD1CD96F8}.Debug|x86.ActiveCfg = Debug|x86
{411A9E0E-FDC6-4E25-828A-0C2CD1CD96F8}.Debug|x86.Build.0 = Debug|x86
{411A9E0E-FDC6-4E25-828A-0C2CD1CD96F8}.Mono|x86.ActiveCfg = Release|x86
{411A9E0E-FDC6-4E25-828A-0C2CD1CD96F8}.Mono|x86.Build.0 = Release|x86
{411A9E0E-FDC6-4E25-828A-0C2CD1CD96F8}.Release|x86.ActiveCfg = Release|x86
{411A9E0E-FDC6-4E25-828A-0C2CD1CD96F8}.Release|x86.Build.0 = Release|x86
{90D6E9FC-7B88-4E1B-B018-8FA742274558}.Debug|x86.ActiveCfg = Debug|x86
{90D6E9FC-7B88-4E1B-B018-8FA742274558}.Debug|x86.Build.0 = Debug|x86
{90D6E9FC-7B88-4E1B-B018-8FA742274558}.Mono|x86.ActiveCfg = Release|x86
@ -320,15 +312,14 @@ Global
{4EACDBBC-BCD7-4765-A57B-3E08331E4749} = {57A04B72-8088-4F75-A582-1158CF8291F7}
{80B51429-7A0E-46D6-BEE3-C80DCB1C4EAA} = {4EACDBBC-BCD7-4765-A57B-3E08331E4749}
{40D72824-7D02-4A77-9106-8FE0EEA2B997} = {4EACDBBC-BCD7-4765-A57B-3E08331E4749}
{411A9E0E-FDC6-4E25-828A-0C2CD1CD96F8} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
{90D6E9FC-7B88-4E1B-B018-8FA742274558} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
{9DC31DE3-79FF-47A8-96B4-6BA18F6BB1CB} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5} = {57A04B72-8088-4F75-A582-1158CF8291F7}
{F3F63718-63C6-432F-BDDC-C960AD95EC82} = {0F0D4998-8F5D-4467-A909-BB192C4B3B4B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.0\lib\NET35;packages\Unity.2.1.505.2\lib\NET35
SolutionGuid = {2955716E-0882-41EC-935D-C95694C5C30F}
EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.0\lib\NET35;packages\Unity.2.1.505.2\lib\NET35
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = NzbDrone.Console\NzbDrone.Console.csproj