Merge pull request #506 from hawken93/linting

Removing tabs and trailing whitespace
pull/702/head
Joshua M. Boniface 6 years ago committed by GitHub
commit 15c89d281e

@ -15,14 +15,14 @@ namespace Emby.Server.Implementations.Archiving
/// </summary> /// </summary>
public class ZipClient : IZipClient public class ZipClient : IZipClient
{ {
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
public ZipClient(IFileSystem fileSystem) public ZipClient(IFileSystem fileSystem)
{ {
_fileSystem = fileSystem; _fileSystem = fileSystem;
} }
/// <summary> /// <summary>
/// Extracts all. /// Extracts all.
/// </summary> /// </summary>
/// <param name="sourceFile">The source file.</param> /// <param name="sourceFile">The source file.</param>
@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Archiving
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles) public void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles)
{ {
using (var fileStream = _fileSystem.OpenRead(sourceFile)) using (var fileStream = _fileSystem.OpenRead(sourceFile))
{ {
ExtractAll(fileStream, targetPath, overwriteExistingFiles); ExtractAll(fileStream, targetPath, overwriteExistingFiles);
} }
@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.Archiving
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles) public void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles)
{ {
using (var fileStream = _fileSystem.OpenRead(sourceFile)) using (var fileStream = _fileSystem.OpenRead(sourceFile))
{ {
ExtractAllFrom7z(fileStream, targetPath, overwriteExistingFiles); ExtractAllFrom7z(fileStream, targetPath, overwriteExistingFiles);
} }
@ -156,7 +156,7 @@ namespace Emby.Server.Implementations.Archiving
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAllFromTar(string sourceFile, string targetPath, bool overwriteExistingFiles) public void ExtractAllFromTar(string sourceFile, string targetPath, bool overwriteExistingFiles)
{ {
using (var fileStream = _fileSystem.OpenRead(sourceFile)) using (var fileStream = _fileSystem.OpenRead(sourceFile))
{ {
ExtractAllFromTar(fileStream, targetPath, overwriteExistingFiles); ExtractAllFromTar(fileStream, targetPath, overwriteExistingFiles);
} }

@ -10,8 +10,8 @@ namespace Emby.Server.Implementations.Devices
public class DeviceId public class DeviceId
{ {
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly object _syncLock = new object(); private readonly object _syncLock = new object();
@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.Devices
{ {
lock (_syncLock) lock (_syncLock)
{ {
var value = File.ReadAllText(CachePath, Encoding.UTF8); var value = File.ReadAllText(CachePath, Encoding.UTF8);
Guid guid; Guid guid;
if (Guid.TryParse(value, out guid)) if (Guid.TryParse(value, out guid))
@ -57,7 +57,7 @@ namespace Emby.Server.Implementations.Devices
{ {
var path = CachePath; var path = CachePath;
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
lock (_syncLock) lock (_syncLock)
{ {
@ -92,13 +92,13 @@ namespace Emby.Server.Implementations.Devices
public DeviceId(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem) public DeviceId(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem)
{ {
if (fileSystem == null) { if (fileSystem == null) {
throw new ArgumentNullException ("fileSystem"); throw new ArgumentNullException ("fileSystem");
} }
_appPaths = appPaths; _appPaths = appPaths;
_logger = logger; _logger = logger;
_fileSystem = fileSystem; _fileSystem = fileSystem;
} }
public string Value public string Value

@ -1842,11 +1842,11 @@ namespace System.Net
/// <summary> /// <summary>
/// ///
/// Class Leading bits Default netmask /// Class Leading bits Default netmask
/// A (CIDR /8) 00 255.0.0.0 /// A (CIDR /8) 00 255.0.0.0
/// A (CIDR /8) 01 255.0.0.0 /// A (CIDR /8) 01 255.0.0.0
/// B (CIDR /16) 10 255.255.0.0 /// B (CIDR /16) 10 255.255.0.0
/// C (CIDR /24) 11 255.255.255.0 /// C (CIDR /24) 11 255.255.255.0
/// ///
/// </summary> /// </summary>
/// <param name="ip"></param> /// <param name="ip"></param>

@ -43,7 +43,6 @@ namespace UniversalDetector.Core
/// </summary> /// </summary>
public abstract class CharDistributionAnalyser public abstract class CharDistributionAnalyser
{ {
protected const float SURE_YES = 0.99f; protected const float SURE_YES = 0.99f;
protected const float SURE_NO = 0.01f; protected const float SURE_NO = 0.01f;
protected const int MINIMUM_DATA_THRESHOLD = 4; protected const int MINIMUM_DATA_THRESHOLD = 4;
@ -401,7 +400,7 @@ namespace UniversalDetector.Core
852,1221,1400,1486, 882,2299,4036, 351, 28,1122, 700,6479,6480,6481,6482,6483, //last 512 852,1221,1400,1486, 882,2299,4036, 351, 28,1122, 700,6479,6480,6481,6482,6483, //last 512
/*************************************************************************************** /***************************************************************************************
*Everything below is of no interest for detection purpose * *Everything below is of no interest for detection purpose *
*************************************************************************************** ***************************************************************************************
5508,6484,3900,3414,3974,4441,4024,3537,4037,5628,5099,3633,6485,3148,6486,3636, 5508,6484,3900,3414,3974,4441,4024,3537,4037,5628,5099,3633,6485,3148,6486,3636,
@ -980,7 +979,7 @@ namespace UniversalDetector.Core
2294, 208,3499,4057,2019, 330,4294,3865,2892,2492,3733,4295,8115,8116,8117,8118, // 8102 2294, 208,3499,4057,2019, 330,4294,3865,2892,2492,3733,4295,8115,8116,8117,8118, // 8102
/*************************************************************************************** /***************************************************************************************
*Everything below is of no interest for detection purpose * *Everything below is of no interest for detection purpose *
*************************************************************************************** ***************************************************************************************
2515,1613,4582,8119,3312,3866,2516,8120,4058,8121,1637,4059,2466,4583,3867,8122, // 8118 2515,1613,4582,8119,3312,3866,2516,8120,4058,8121,1637,4059,2466,4583,3867,8122, // 8118
@ -1022,7 +1021,7 @@ namespace UniversalDetector.Core
8678,8679,8680,8681,8682,8683,8684,8685,8686,8687,8688,8689,8690,8691,8692,8693, // 8694 8678,8679,8680,8681,8682,8683,8684,8685,8686,8687,8688,8689,8690,8691,8692,8693, // 8694
8694,8695,8696,8697,8698,8699,8700,8701,8702,8703,8704,8705,8706,8707,8708,8709, // 8710 8694,8695,8696,8697,8698,8699,8700,8701,8702,8703,8704,8705,8706,8707,8708,8709, // 8710
8710,8711,8712,8713,8714,8715,8716,8717,8718,8719,8720,8721,8722,8723,8724,8725, // 8726 8710,8711,8712,8713,8714,8715,8716,8717,8718,8719,8720,8721,8722,8723,8724,8725, // 8726
8726,8727,8728,8729,8730,8731,8732,8733,8734,8735,8736,8737,8738,8739,8740,8741, // 8742 //13973 8726,8727,8728,8729,8730,8731,8732,8733,8734,8735,8736,8737,8738,8739,8740,8741, // 8742 //13973
****************************************************************************************/ ****************************************************************************************/
}; };
@ -1216,7 +1215,7 @@ namespace UniversalDetector.Core
670,1190,2635,2636,2637,2638, 168,2639, 652, 873, 542,1054,1541,2640,2641,2642, //512, 256 670,1190,2635,2636,2637,2638, 168,2639, 652, 873, 542,1054,1541,2640,2641,2642, //512, 256
/*************************************************************************************** /***************************************************************************************
* Everything below is of no interest for detection purpose * Everything below is of no interest for detection purpose *
*************************************************************************************** ***************************************************************************************
2643,2644,2645,2646,2647,2648,2649,2650,2651,2652,2653,2654,2655,2656,2657,2658, 2643,2644,2645,2646,2647,2648,2649,2650,2651,2652,2653,2654,2655,2656,2657,2658,
@ -2002,7 +2001,7 @@ namespace UniversalDetector.Core
2299, 208,3546,4161,2020, 330,4438,3944,2906,2499,3799,4439,4811,5796,5797,5798, // 5376 //last 512 2299, 208,3546,4161,2020, 330,4438,3944,2906,2499,3799,4439,4811,5796,5797,5798, // 5376 //last 512
/*************************************************************************************** /***************************************************************************************
*Everything below is of no interest for detection purpose * *Everything below is of no interest for detection purpose *
*************************************************************************************** ***************************************************************************************
2522,1613,4812,5799,3345,3945,2523,5800,4162,5801,1637,4163,2471,4813,3946,5802, // 5392 2522,1613,4812,5799,3345,3945,2523,5800,4162,5801,1637,4163,2471,4813,3946,5802, // 5392
@ -2870,7 +2869,7 @@ namespace UniversalDetector.Core
2922,3625, 544, 461,6189, 566, 209,2437,3398,2098,1065,2068,3331,3626,3257,2137, // 4368 //last 512 2922,3625, 544, 461,6189, 566, 209,2437,3398,2098,1065,2068,3331,3626,3257,2137, // 4368 //last 512
/*************************************************************************************** /***************************************************************************************
*Everything below is of no interest for detection purpose * *Everything below is of no interest for detection purpose *
*************************************************************************************** ***************************************************************************************
2138,2122,3730,2888,1995,1820,1044,6190,6191,6192,6193,6194,6195,6196,6197,6198, // 4384 2138,2122,3730,2888,1995,1820,1044,6190,6191,6192,6193,6194,6195,6196,6197,6198, // 4384
@ -3168,6 +3167,4 @@ namespace UniversalDetector.Core
return -1; return -1;
} }
} }
} }

@ -12,23 +12,22 @@ namespace Emby.XmlTv.Test
[TestClass] [TestClass]
public class XmlTvReaderLanguageTests public class XmlTvReaderLanguageTests
{ {
/* /* <title lang="es">Homes Under the Hammer - Spanish</title>
<title lang="es">Homes Under the Hammer - Spanish</title> * <title lang="es">Homes Under the Hammer - Spanish 2</title>
<title lang="es">Homes Under the Hammer - Spanish 2</title> * <title lang="en">Homes Under the Hammer - English</title>
<title lang="en">Homes Under the Hammer - English</title> * <title lang="en">Homes Under the Hammer - English 2</title>
<title lang="en">Homes Under the Hammer - English 2</title> * <title lang="">Homes Under the Hammer - Empty Language</title>
<title lang="">Homes Under the Hammer - Empty Language</title> * <title lang="">Homes Under the Hammer - Empty Language 2</title>
<title lang="">Homes Under the Hammer - Empty Language 2</title> * <title>Homes Under the Hammer - No Language</title>
<title>Homes Under the Hammer - No Language</title> * <title>Homes Under the Hammer - No Language 2</title>
<title>Homes Under the Hammer - No Language 2</title> */
*/
/* Expected Behaviour:
/* Expected Behaviour: * - Language = Null Homes Under the Hammer - No Language
- Language = Null Homes Under the Hammer - No Language * - Language = "" Homes Under the Hammer - No Language
- Language = "" Homes Under the Hammer - No Language * - Language = es Homes Under the Hammer - Spanish
- Language = es Homes Under the Hammer - Spanish * - Language = en Homes Under the Hammer - English
- Language = en Homes Under the Hammer - English */
*/
[TestMethod] [TestMethod]
[DeploymentItem("Xml Files\\MultilanguageData.xml")] [DeploymentItem("Xml Files\\MultilanguageData.xml")]

@ -832,23 +832,22 @@ namespace Emby.XmlTv.Classes
public void ProcessNode(XmlReader reader, Action<string> setter, string languageRequired = null, Action<string> allOccurrencesSetter = null) public void ProcessNode(XmlReader reader, Action<string> setter, string languageRequired = null, Action<string> allOccurrencesSetter = null)
{ {
/* /* <title lang="es">Homes Under the Hammer - Spanish</title>
<title lang="es">Homes Under the Hammer - Spanish</title> * <title lang="es">Homes Under the Hammer - Spanish 2</title>
<title lang="es">Homes Under the Hammer - Spanish 2</title> * <title lang="en">Homes Under the Hammer - English</title>
<title lang="en">Homes Under the Hammer - English</title> * <title lang="en">Homes Under the Hammer - English 2</title>
<title lang="en">Homes Under the Hammer - English 2</title> * <title lang="">Homes Under the Hammer - Empty Language</title>
<title lang="">Homes Under the Hammer - Empty Language</title> * <title lang="">Homes Under the Hammer - Empty Language 2</title>
<title lang="">Homes Under the Hammer - Empty Language 2</title> * <title>Homes Under the Hammer - No Language</title>
<title>Homes Under the Hammer - No Language</title> * <title>Homes Under the Hammer - No Language 2</title>
<title>Homes Under the Hammer - No Language 2</title> */
*/
/* Expected Behaviour:
/* Expected Behaviour: * - Language = Null Homes Under the Hammer - No Language
- Language = Null Homes Under the Hammer - No Language * - Language = "" Homes Under the Hammer - No Language
- Language = "" Homes Under the Hammer - No Language * - Language = es Homes Under the Hammer - Spanish
- Language = es Homes Under the Hammer - Spanish * - Language = en Homes Under the Hammer - English
- Language = en Homes Under the Hammer - English */
*/
var results = new List<Tuple<string, string>>(); var results = new List<Tuple<string, string>>();
@ -921,23 +920,22 @@ namespace Emby.XmlTv.Classes
public void ProcessMultipleNodes(XmlReader reader, Action<string> setter, string languageRequired = null) public void ProcessMultipleNodes(XmlReader reader, Action<string> setter, string languageRequired = null)
{ {
/* /* <category lang="en">Property - English</category>
<category lang="en">Property - English</category> * <category lang="en">Property - English 2</category>
<category lang="en">Property - English 2</category> * <category lang="es">Property - Spanish</category>
<category lang="es">Property - Spanish</category> * <category lang="es">Property - Spanish 2</category>
<category lang="es">Property - Spanish 2</category> * <category lang="">Property - Empty Language</category>
<category lang="">Property - Empty Language</category> * <category lang="">Property - Empty Language 2</category>
<category lang="">Property - Empty Language 2</category> * <category>Property - No Language</category>
<category>Property - No Language</category> * <category>Property - No Language 2</category>
<category>Property - No Language 2</category> */
*/
/* Expected Behaviour:
/* Expected Behaviour: * - Language = Null Property - No Language / Property - No Language 2
- Language = Null Property - No Language / Property - No Language 2 * - Language = "" Property - Empty Language / Property - Empty Language 2
- Language = "" Property - Empty Language / Property - Empty Language 2 * - Language = es Property - Spanish / Property - Spanish 2
- Language = es Property - Spanish / Property - Spanish 2 * - Language = en Property - English / Property - English 2
- Language = en Property - English / Property - English 2 */
*/
var currentElementName = reader.Name; var currentElementName = reader.Name;
var values = new[] { new { Language = reader.GetAttribute("lang"), Value = reader.ReadElementContentAsString() } }.ToList(); var values = new[] { new { Language = reader.GetAttribute("lang"), Value = reader.ReadElementContentAsString() } }.ToList();

@ -97,12 +97,12 @@ namespace Jellyfin.SocketSharp
} }
#if NET_4_0 #if NET_4_0
if (validateRequestNewMode && !checked_form) { if (validateRequestNewMode && !checked_form) {
// Setting this before calling the validator prevents // Setting this before calling the validator prevents
// possible endless recursion // possible endless recursion
checked_form = true; checked_form = true;
ValidateNameValueCollection ("Form", query_string_nvc, RequestValidationSource.Form); ValidateNameValueCollection ("Form", query_string_nvc, RequestValidationSource.Form);
} else } else
#endif #endif
if (validate_form && !checked_form) if (validate_form && !checked_form)
{ {

@ -133,7 +133,7 @@ namespace MediaBrowser.Api.Images
{ {
try try
{ {
return _fileSystem.GetFiles(path, BaseItem.SupportedImageExtensions, false, true) return _fileSystem.GetFiles(path, BaseItem.SupportedImageExtensions, false, true)
.Select(i => new ImageByNameInfo .Select(i => new ImageByNameInfo
{ {
Name = _fileSystem.GetFileNameWithoutExtension(i), Name = _fileSystem.GetFileNameWithoutExtension(i),
@ -185,7 +185,7 @@ namespace MediaBrowser.Api.Images
var paths = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(_appPaths.GeneralPath, request.Name, filename + i)).ToList(); var paths = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(_appPaths.GeneralPath, request.Name, filename + i)).ToList();
var path = paths.FirstOrDefault(_fileSystem.FileExists) ?? paths.FirstOrDefault(); var path = paths.FirstOrDefault(_fileSystem.FileExists) ?? paths.FirstOrDefault();
return _resultFactory.GetStaticFileResult(Request, path); return _resultFactory.GetStaticFileResult(Request, path);
} }
@ -199,11 +199,11 @@ namespace MediaBrowser.Api.Images
{ {
var themeFolder = Path.Combine(_appPaths.RatingsPath, request.Theme); var themeFolder = Path.Combine(_appPaths.RatingsPath, request.Theme);
if (_fileSystem.DirectoryExists(themeFolder)) if (_fileSystem.DirectoryExists(themeFolder))
{ {
var path = BaseItem.SupportedImageExtensions var path = BaseItem.SupportedImageExtensions
.Select(i => Path.Combine(themeFolder, request.Name + i)) .Select(i => Path.Combine(themeFolder, request.Name + i))
.FirstOrDefault(_fileSystem.FileExists); .FirstOrDefault(_fileSystem.FileExists);
if (!string.IsNullOrEmpty(path)) if (!string.IsNullOrEmpty(path))
{ {
@ -213,14 +213,14 @@ namespace MediaBrowser.Api.Images
var allFolder = Path.Combine(_appPaths.RatingsPath, "all"); var allFolder = Path.Combine(_appPaths.RatingsPath, "all");
if (_fileSystem.DirectoryExists(allFolder)) if (_fileSystem.DirectoryExists(allFolder))
{ {
// Avoid implicitly captured closure // Avoid implicitly captured closure
var currentRequest = request; var currentRequest = request;
var path = BaseItem.SupportedImageExtensions var path = BaseItem.SupportedImageExtensions
.Select(i => Path.Combine(allFolder, currentRequest.Name + i)) .Select(i => Path.Combine(allFolder, currentRequest.Name + i))
.FirstOrDefault(_fileSystem.FileExists); .FirstOrDefault(_fileSystem.FileExists);
if (!string.IsNullOrEmpty(path)) if (!string.IsNullOrEmpty(path))
{ {
@ -240,10 +240,10 @@ namespace MediaBrowser.Api.Images
{ {
var themeFolder = Path.Combine(_appPaths.MediaInfoImagesPath, request.Theme); var themeFolder = Path.Combine(_appPaths.MediaInfoImagesPath, request.Theme);
if (_fileSystem.DirectoryExists(themeFolder)) if (_fileSystem.DirectoryExists(themeFolder))
{ {
var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(themeFolder, request.Name + i)) var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(themeFolder, request.Name + i))
.FirstOrDefault(_fileSystem.FileExists); .FirstOrDefault(_fileSystem.FileExists);
if (!string.IsNullOrEmpty(path)) if (!string.IsNullOrEmpty(path))
{ {
@ -253,13 +253,13 @@ namespace MediaBrowser.Api.Images
var allFolder = Path.Combine(_appPaths.MediaInfoImagesPath, "all"); var allFolder = Path.Combine(_appPaths.MediaInfoImagesPath, "all");
if (_fileSystem.DirectoryExists(allFolder)) if (_fileSystem.DirectoryExists(allFolder))
{ {
// Avoid implicitly captured closure // Avoid implicitly captured closure
var currentRequest = request; var currentRequest = request;
var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(allFolder, currentRequest.Name + i)) var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(allFolder, currentRequest.Name + i))
.FirstOrDefault(_fileSystem.FileExists); .FirstOrDefault(_fileSystem.FileExists);
if (!string.IsNullOrEmpty(path)) if (!string.IsNullOrEmpty(path))
{ {

@ -1706,7 +1706,8 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(outputVideoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)) if (string.Equals(outputVideoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
{ {
// per docs: // per docs:
// -threads number of threads to use for encoding, can't be 0 [auto] with VP8 (recommended value : number of real cores - 1) // -threads number of threads to use for encoding, can't be 0 [auto] with VP8
// (recommended value : number of real cores - 1)
return Math.Max(Environment.ProcessorCount - 1, 1); return Math.Max(Environment.ProcessorCount - 1, 1);
} }

@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Net
object GetResult(IRequest requestContext, byte[] content, string contentType, IDictionary<string, string> responseHeaders = null); object GetResult(IRequest requestContext, byte[] content, string contentType, IDictionary<string, string> responseHeaders = null);
object GetResult(IRequest requestContext, Stream content, string contentType, IDictionary<string, string> responseHeaders = null); object GetResult(IRequest requestContext, Stream content, string contentType, IDictionary<string, string> responseHeaders = null);
object GetResult(IRequest requestContext, string content, string contentType, IDictionary<string, string> responseHeaders = null); object GetResult(IRequest requestContext, string content, string contentType, IDictionary<string, string> responseHeaders = null);
object GetRedirectResult(string url); object GetRedirectResult(string url);

@ -5,14 +5,14 @@ namespace MediaBrowser.Model.Dlna
[Flags] [Flags]
public enum DlnaFlags : ulong public enum DlnaFlags : ulong
{ {
/*! <i>Background</i> transfer mode. /*! <i>Background</i> transfer mode.
For use with upload and download transfers to and from the server. For use with upload and download transfers to and from the server.
The primary difference between \ref DH_TransferMode_Interactive and The primary difference between \ref DH_TransferMode_Interactive and
\ref DH_TransferMode_Bulk is that the latter assumes that the user \ref DH_TransferMode_Bulk is that the latter assumes that the user
is not relying on the transfer for immediately rendering the content is not relying on the transfer for immediately rendering the content
and there are no issues with causing a buffer overflow if the and there are no issues with causing a buffer overflow if the
receiver uses TCP flow control to reduce total throughput. receiver uses TCP flow control to reduce total throughput.
*/ */
BackgroundTransferMode = 1 << 22, BackgroundTransferMode = 1 << 22,
ByteBasedSeek = 1 << 29, ByteBasedSeek = 1 << 29,
@ -35,11 +35,11 @@ namespace MediaBrowser.Model.Dlna
SenderPaced = 1L << 31, SenderPaced = 1L << 31,
SnIncrease = 1 << 26, SnIncrease = 1 << 26,
/*! <i>Streaming</i> transfer mode. /*! <i>Streaming</i> transfer mode.
The server transmits at a throughput sufficient for real-time playback of The server transmits at a throughput sufficient for real-time playback of
audio or video. URIs with audio or video often support the audio or video. URIs with audio or video often support the
\ref DH_TransferMode_Interactive and \ref DH_TransferMode_Bulk transfer modes. \ref DH_TransferMode_Interactive and \ref DH_TransferMode_Bulk transfer modes.
The most well-known exception to this general claim is for live streams. The most well-known exception to this general claim is for live streams.
*/ */
StreamingTransferMode = 1 << 24, StreamingTransferMode = 1 << 24,

@ -7,14 +7,13 @@ namespace MediaBrowser.Model.Net
/// Implemented by components that can create a platform specific UDP socket implementation, and wrap it in the cross platform <see cref="ISocket"/> interface. /// Implemented by components that can create a platform specific UDP socket implementation, and wrap it in the cross platform <see cref="ISocket"/> interface.
/// </summary> /// </summary>
public interface ISocketFactory public interface ISocketFactory
{ {
/// <summary>
/// <summary> /// Createa a new unicast socket using the specified local port number.
/// Createa a new unicast socket using the specified local port number. /// </summary>
/// </summary> /// <param name="localPort">The local port to bind to.</param>
/// <param name="localPort">The local port to bind to.</param> /// <returns>A <see cref="ISocket"/> implementation.</returns>
/// <returns>A <see cref="ISocket"/> implementation.</returns> ISocket CreateUdpSocket(int localPort);
ISocket CreateUdpSocket(int localPort);
ISocket CreateUdpBroadcastSocket(int localPort); ISocket CreateUdpBroadcastSocket(int localPort);

@ -5,16 +5,16 @@ namespace MediaBrowser.Model.Net
/// Used by the sockets wrapper to hold raw data received from a UDP socket. /// Used by the sockets wrapper to hold raw data received from a UDP socket.
/// </summary> /// </summary>
public sealed class SocketReceiveResult public sealed class SocketReceiveResult
{ {
/// <summary> /// <summary>
/// The buffer to place received data into. /// The buffer to place received data into.
/// </summary> /// </summary>
public byte[] Buffer { get; set; } public byte[] Buffer { get; set; }
/// <summary> /// <summary>
/// The number of bytes received. /// The number of bytes received.
/// </summary> /// </summary>
public int ReceivedBytes { get; set; } public int ReceivedBytes { get; set; }
/// <summary> /// <summary>
/// The <see cref="IpEndPointInfo"/> the data was received from. /// The <see cref="IpEndPointInfo"/> the data was received from.

@ -9,516 +9,286 @@ namespace MediaBrowser.Model.Services
{ {
// Must be sorted // Must be sorted
static readonly long[] entities = new long[] { static readonly long[] entities = new long[] {
(long)'A' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, (long)'A' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
(long)'A' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, (long)'A' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'A' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, (long)'A' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'A' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, (long)'A' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'A' << 56 | (long)'l' << 48 | (long)'p' << 40 | (long)'h' << 32 | (long)'a' << 24, (long)'A' << 56 | (long)'l' << 48 | (long)'p' << 40 | (long)'h' << 32 | (long)'a' << 24,
(long)'A' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'g' << 24, (long)'A' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'g' << 24,
(long)'A' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, (long)'A' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
(long)'A' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, (long)'A' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'B' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, (long)'B' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32,
(long)'C' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'d' << 32 | (long)'i' << 24 | (long)'l' << 16, (long)'C' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'d' << 32 | (long)'i' << 24 | (long)'l' << 16,
(long)'C' << 56 | (long)'h' << 48 | (long)'i' << 40, (long)'C' << 56 | (long)'h' << 48 | (long)'i' << 40,
(long)'D' << 56 | (long)'a' << 48 | (long)'g' << 40 | (long)'g' << 32 | (long)'e' << 24 | (long)'r' << 16, (long)'D' << 56 | (long)'a' << 48 | (long)'g' << 40 | (long)'g' << 32 | (long)'e' << 24 | (long)'r' << 16,
(long)'D' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'t' << 32 | (long)'a' << 24, (long)'D' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'t' << 32 | (long)'a' << 24,
(long)'E' << 56 | (long)'T' << 48 | (long)'H' << 40, (long)'E' << 56 | (long)'T' << 48 | (long)'H' << 40,
(long)'E' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, (long)'E' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'E' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, (long)'E' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'E' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, (long)'E' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'E' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, (long)'E' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8,
(long)'E' << 56 | (long)'t' << 48 | (long)'a' << 40, (long)'E' << 56 | (long)'t' << 48 | (long)'a' << 40,
(long)'E' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, (long)'E' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'G' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'m' << 32 | (long)'a' << 24, (long)'G' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'m' << 32 | (long)'a' << 24,
(long)'I' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, (long)'I' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'I' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, (long)'I' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'I' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, (long)'I' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'I' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'a' << 32, (long)'I' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'a' << 32,
(long)'I' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, (long)'I' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'K' << 56 | (long)'a' << 48 | (long)'p' << 40 | (long)'p' << 32 | (long)'a' << 24, (long)'K' << 56 | (long)'a' << 48 | (long)'p' << 40 | (long)'p' << 32 | (long)'a' << 24,
(long)'L' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'b' << 32 | (long)'d' << 24 | (long)'a' << 16, (long)'L' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'b' << 32 | (long)'d' << 24 | (long)'a' << 16,
(long)'M' << 56 | (long)'u' << 48, (long)'M' << 56 | (long)'u' << 48,
(long)'N' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, (long)'N' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
(long)'N' << 56 | (long)'u' << 48, (long)'N' << 56 | (long)'u' << 48,
(long)'O' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, (long)'O' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
(long)'O' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, (long)'O' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'O' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, (long)'O' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'O' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, (long)'O' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'O' << 56 | (long)'m' << 48 | (long)'e' << 40 | (long)'g' << 32 | (long)'a' << 24, (long)'O' << 56 | (long)'m' << 48 | (long)'e' << 40 | (long)'g' << 32 | (long)'a' << 24,
(long)'O' << 56 | (long)'m' << 48 | (long)'i' << 40 | (long)'c' << 32 | (long)'r' << 24 | (long)'o' << 16 | (long)'n' << 8, (long)'O' << 56 | (long)'m' << 48 | (long)'i' << 40 | (long)'c' << 32 | (long)'r' << 24 | (long)'o' << 16 | (long)'n' << 8,
(long)'O' << 56 | (long)'s' << 48 | (long)'l' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'h' << 16, (long)'O' << 56 | (long)'s' << 48 | (long)'l' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'h' << 16,
(long)'O' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, (long)'O' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
(long)'O' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, (long)'O' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'P' << 56 | (long)'h' << 48 | (long)'i' << 40, (long)'P' << 56 | (long)'h' << 48 | (long)'i' << 40,
(long)'P' << 56 | (long)'i' << 48, (long)'P' << 56 | (long)'i' << 48,
(long)'P' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24, (long)'P' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24,
(long)'P' << 56 | (long)'s' << 48 | (long)'i' << 40, (long)'P' << 56 | (long)'s' << 48 | (long)'i' << 40,
(long)'R' << 56 | (long)'h' << 48 | (long)'o' << 40, (long)'R' << 56 | (long)'h' << 48 | (long)'o' << 40,
(long)'S' << 56 | (long)'c' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'o' << 24 | (long)'n' << 16, (long)'S' << 56 | (long)'c' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'o' << 24 | (long)'n' << 16,
(long)'S' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24, (long)'S' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24,
(long)'T' << 56 | (long)'H' << 48 | (long)'O' << 40 | (long)'R' << 32 | (long)'N' << 24, (long)'T' << 56 | (long)'H' << 48 | (long)'O' << 40 | (long)'R' << 32 | (long)'N' << 24,
(long)'T' << 56 | (long)'a' << 48 | (long)'u' << 40, (long)'T' << 56 | (long)'a' << 48 | (long)'u' << 40,
(long)'T' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24, (long)'T' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24,
(long)'U' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, (long)'U' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'U' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, (long)'U' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'U' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, (long)'U' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'U' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, (long)'U' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8,
(long)'U' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, (long)'U' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'X' << 56 | (long)'i' << 48, (long)'X' << 56 | (long)'i' << 48,
(long)'Y' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, (long)'Y' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'Y' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, (long)'Y' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'Z' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, (long)'Z' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32,
(long)'a' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, (long)'a' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'a' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, (long)'a' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'a' << 56 | (long)'c' << 48 | (long)'u' << 40 | (long)'t' << 32 | (long)'e' << 24, (long)'a' << 56 | (long)'c' << 48 | (long)'u' << 40 | (long)'t' << 32 | (long)'e' << 24,
(long)'a' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, (long)'a' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
(long)'a' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, (long)'a' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'a' << 56 | (long)'l' << 48 | (long)'e' << 40 | (long)'f' << 32 | (long)'s' << 24 | (long)'y' << 16 | (long)'m' << 8, (long)'a' << 56 | (long)'l' << 48 | (long)'e' << 40 | (long)'f' << 32 | (long)'s' << 24 | (long)'y' << 16 | (long)'m' << 8,
(long)'a' << 56 | (long)'l' << 48 | (long)'p' << 40 | (long)'h' << 32 | (long)'a' << 24, (long)'a' << 56 | (long)'l' << 48 | (long)'p' << 40 | (long)'h' << 32 | (long)'a' << 24,
(long)'a' << 56 | (long)'m' << 48 | (long)'p' << 40, (long)'a' << 56 | (long)'m' << 48 | (long)'p' << 40,
(long)'a' << 56 | (long)'n' << 48 | (long)'d' << 40, (long)'a' << 56 | (long)'n' << 48 | (long)'d' << 40,
(long)'a' << 56 | (long)'n' << 48 | (long)'g' << 40, (long)'a' << 56 | (long)'n' << 48 | (long)'g' << 40,
(long)'a' << 56 | (long)'p' << 48 | (long)'o' << 40 | (long)'s' << 32, (long)'a' << 56 | (long)'p' << 48 | (long)'o' << 40 | (long)'s' << 32,
(long)'a' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'g' << 24, (long)'a' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'g' << 24,
(long)'a' << 56 | (long)'s' << 48 | (long)'y' << 40 | (long)'m' << 32 | (long)'p' << 24, (long)'a' << 56 | (long)'s' << 48 | (long)'y' << 40 | (long)'m' << 32 | (long)'p' << 24,
(long)'a' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, (long)'a' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
(long)'a' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, (long)'a' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'b' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, (long)'b' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'b' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, (long)'b' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32,
(long)'b' << 56 | (long)'r' << 48 | (long)'v' << 40 | (long)'b' << 32 | (long)'a' << 24 | (long)'r' << 16, (long)'b' << 56 | (long)'r' << 48 | (long)'v' << 40 | (long)'b' << 32 | (long)'a' << 24 | (long)'r' << 16,
(long)'b' << 56 | (long)'u' << 48 | (long)'l' << 40 | (long)'l' << 32, (long)'b' << 56 | (long)'u' << 48 | (long)'l' << 40 | (long)'l' << 32,
(long)'c' << 56 | (long)'a' << 48 | (long)'p' << 40, (long)'c' << 56 | (long)'a' << 48 | (long)'p' << 40,
(long)'c' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'d' << 32 | (long)'i' << 24 | (long)'l' << 16, (long)'c' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'d' << 32 | (long)'i' << 24 | (long)'l' << 16,
(long)'c' << 56 | (long)'e' << 48 | (long)'d' << 40 | (long)'i' << 32 | (long)'l' << 24, (long)'c' << 56 | (long)'e' << 48 | (long)'d' << 40 | (long)'i' << 32 | (long)'l' << 24,
(long)'c' << 56 | (long)'e' << 48 | (long)'n' << 40 | (long)'t' << 32, (long)'c' << 56 | (long)'e' << 48 | (long)'n' << 40 | (long)'t' << 32,
(long)'c' << 56 | (long)'h' << 48 | (long)'i' << 40, (long)'c' << 56 | (long)'h' << 48 | (long)'i' << 40,
(long)'c' << 56 | (long)'i' << 48 | (long)'r' << 40 | (long)'c' << 32, (long)'c' << 56 | (long)'i' << 48 | (long)'r' << 40 | (long)'c' << 32,
(long)'c' << 56 | (long)'l' << 48 | (long)'u' << 40 | (long)'b' << 32 | (long)'s' << 24, (long)'c' << 56 | (long)'l' << 48 | (long)'u' << 40 | (long)'b' << 32 | (long)'s' << 24,
(long)'c' << 56 | (long)'o' << 48 | (long)'n' << 40 | (long)'g' << 32, (long)'c' << 56 | (long)'o' << 48 | (long)'n' << 40 | (long)'g' << 32,
(long)'c' << 56 | (long)'o' << 48 | (long)'p' << 40 | (long)'y' << 32, (long)'c' << 56 | (long)'o' << 48 | (long)'p' << 40 | (long)'y' << 32,
(long)'c' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'r' << 24, (long)'c' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'r' << 24,
(long)'c' << 56 | (long)'u' << 48 | (long)'p' << 40, (long)'c' << 56 | (long)'u' << 48 | (long)'p' << 40,
(long)'c' << 56 | (long)'u' << 48 | (long)'r' << 40 | (long)'r' << 32 | (long)'e' << 24 | (long)'n' << 16, (long)'c' << 56 | (long)'u' << 48 | (long)'r' << 40 | (long)'r' << 32 | (long)'e' << 24 | (long)'n' << 16,
(long)'d' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, (long)'d' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'d' << 56 | (long)'a' << 48 | (long)'g' << 40 | (long)'g' << 32 | (long)'e' << 24 | (long)'r' << 16, (long)'d' << 56 | (long)'a' << 48 | (long)'g' << 40 | (long)'g' << 32 | (long)'e' << 24 | (long)'r' << 16,
(long)'d' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, (long)'d' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'d' << 56 | (long)'e' << 48 | (long)'g' << 40, (long)'d' << 56 | (long)'e' << 48 | (long)'g' << 40,
(long)'d' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'t' << 32 | (long)'a' << 24, (long)'d' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'t' << 32 | (long)'a' << 24,
(long)'d' << 56 | (long)'i' << 48 | (long)'a' << 40 | (long)'m' << 32 | (long)'s' << 24, (long)'d' << 56 | (long)'i' << 48 | (long)'a' << 40 | (long)'m' << 32 | (long)'s' << 24,
(long)'d' << 56 | (long)'i' << 48 | (long)'v' << 40 | (long)'i' << 32 | (long)'d' << 24 | (long)'e' << 16, (long)'d' << 56 | (long)'i' << 48 | (long)'v' << 40 | (long)'i' << 32 | (long)'d' << 24 | (long)'e' << 16,
(long)'e' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, (long)'e' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'e' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, (long)'e' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'e' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, (long)'e' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'e' << 56 | (long)'m' << 48 | (long)'p' << 40 | (long)'t' << 32 | (long)'y' << 24, (long)'e' << 56 | (long)'m' << 48 | (long)'p' << 40 | (long)'t' << 32 | (long)'y' << 24,
(long)'e' << 56 | (long)'m' << 48 | (long)'s' << 40 | (long)'p' << 32, (long)'e' << 56 | (long)'m' << 48 | (long)'s' << 40 | (long)'p' << 32,
(long)'e' << 56 | (long)'n' << 48 | (long)'s' << 40 | (long)'p' << 32, (long)'e' << 56 | (long)'n' << 48 | (long)'s' << 40 | (long)'p' << 32,
(long)'e' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, (long)'e' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8,
(long)'e' << 56 | (long)'q' << 48 | (long)'u' << 40 | (long)'i' << 32 | (long)'v' << 24, (long)'e' << 56 | (long)'q' << 48 | (long)'u' << 40 | (long)'i' << 32 | (long)'v' << 24,
(long)'e' << 56 | (long)'t' << 48 | (long)'a' << 40, (long)'e' << 56 | (long)'t' << 48 | (long)'a' << 40,
(long)'e' << 56 | (long)'t' << 48 | (long)'h' << 40, (long)'e' << 56 | (long)'t' << 48 | (long)'h' << 40,
(long)'e' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, (long)'e' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'e' << 56 | (long)'u' << 48 | (long)'r' << 40 | (long)'o' << 32, (long)'e' << 56 | (long)'u' << 48 | (long)'r' << 40 | (long)'o' << 32,
(long)'e' << 56 | (long)'x' << 48 | (long)'i' << 40 | (long)'s' << 32 | (long)'t' << 24, (long)'e' << 56 | (long)'x' << 48 | (long)'i' << 40 | (long)'s' << 32 | (long)'t' << 24,
(long)'f' << 56 | (long)'n' << 48 | (long)'o' << 40 | (long)'f' << 32, (long)'f' << 56 | (long)'n' << 48 | (long)'o' << 40 | (long)'f' << 32,
(long)'f' << 56 | (long)'o' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'l' << 24 | (long)'l' << 16, (long)'f' << 56 | (long)'o' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'l' << 24 | (long)'l' << 16,
(long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'1' << 24 | (long)'2' << 16, (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'1' << 24 | (long)'2' << 16,
(long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'1' << 24 | (long)'4' << 16, (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'1' << 24 | (long)'4' << 16,
(long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'3' << 24 | (long)'4' << 16, (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'3' << 24 | (long)'4' << 16,
(long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'l' << 24, (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'l' << 24,
(long)'g' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'m' << 32 | (long)'a' << 24, (long)'g' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'m' << 32 | (long)'a' << 24,
(long)'g' << 56 | (long)'e' << 48, (long)'g' << 56 | (long)'e' << 48,
(long)'g' << 56 | (long)'t' << 48, (long)'g' << 56 | (long)'t' << 48,
(long)'h' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, (long)'h' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'h' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, (long)'h' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'h' << 56 | (long)'e' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'t' << 24 | (long)'s' << 16, (long)'h' << 56 | (long)'e' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'t' << 24 | (long)'s' << 16,
(long)'h' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'l' << 32 | (long)'i' << 24 | (long)'p' << 16, (long)'h' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'l' << 32 | (long)'i' << 24 | (long)'p' << 16,
(long)'i' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, (long)'i' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'i' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, (long)'i' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'i' << 56 | (long)'e' << 48 | (long)'x' << 40 | (long)'c' << 32 | (long)'l' << 24, (long)'i' << 56 | (long)'e' << 48 | (long)'x' << 40 | (long)'c' << 32 | (long)'l' << 24,
(long)'i' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, (long)'i' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'i' << 56 | (long)'m' << 48 | (long)'a' << 40 | (long)'g' << 32 | (long)'e' << 24, (long)'i' << 56 | (long)'m' << 48 | (long)'a' << 40 | (long)'g' << 32 | (long)'e' << 24,
(long)'i' << 56 | (long)'n' << 48 | (long)'f' << 40 | (long)'i' << 32 | (long)'n' << 24, (long)'i' << 56 | (long)'n' << 48 | (long)'f' << 40 | (long)'i' << 32 | (long)'n' << 24,
(long)'i' << 56 | (long)'n' << 48 | (long)'t' << 40, (long)'i' << 56 | (long)'n' << 48 | (long)'t' << 40,
(long)'i' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'a' << 32, (long)'i' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'a' << 32,
(long)'i' << 56 | (long)'q' << 48 | (long)'u' << 40 | (long)'e' << 32 | (long)'s' << 24 | (long)'t' << 16, (long)'i' << 56 | (long)'q' << 48 | (long)'u' << 40 | (long)'e' << 32 | (long)'s' << 24 | (long)'t' << 16,
(long)'i' << 56 | (long)'s' << 48 | (long)'i' << 40 | (long)'n' << 32, (long)'i' << 56 | (long)'s' << 48 | (long)'i' << 40 | (long)'n' << 32,
(long)'i' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, (long)'i' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'k' << 56 | (long)'a' << 48 | (long)'p' << 40 | (long)'p' << 32 | (long)'a' << 24, (long)'k' << 56 | (long)'a' << 48 | (long)'p' << 40 | (long)'p' << 32 | (long)'a' << 24,
(long)'l' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, (long)'l' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'l' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'b' << 32 | (long)'d' << 24 | (long)'a' << 16, (long)'l' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'b' << 32 | (long)'d' << 24 | (long)'a' << 16,
(long)'l' << 56 | (long)'a' << 48 | (long)'n' << 40 | (long)'g' << 32, (long)'l' << 56 | (long)'a' << 48 | (long)'n' << 40 | (long)'g' << 32,
(long)'l' << 56 | (long)'a' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, (long)'l' << 56 | (long)'a' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'l' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, (long)'l' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'l' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'i' << 32 | (long)'l' << 24, (long)'l' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'i' << 32 | (long)'l' << 24,
(long)'l' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, (long)'l' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'l' << 56 | (long)'e' << 48, (long)'l' << 56 | (long)'e' << 48,
(long)'l' << 56 | (long)'f' << 48 | (long)'l' << 40 | (long)'o' << 32 | (long)'o' << 24 | (long)'r' << 16, (long)'l' << 56 | (long)'f' << 48 | (long)'l' << 40 | (long)'o' << 32 | (long)'o' << 24 | (long)'r' << 16,
(long)'l' << 56 | (long)'o' << 48 | (long)'w' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'t' << 16, (long)'l' << 56 | (long)'o' << 48 | (long)'w' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'t' << 16,
(long)'l' << 56 | (long)'o' << 48 | (long)'z' << 40, (long)'l' << 56 | (long)'o' << 48 | (long)'z' << 40,
(long)'l' << 56 | (long)'r' << 48 | (long)'m' << 40, (long)'l' << 56 | (long)'r' << 48 | (long)'m' << 40,
(long)'l' << 56 | (long)'s' << 48 | (long)'a' << 40 | (long)'q' << 32 | (long)'u' << 24 | (long)'o' << 16, (long)'l' << 56 | (long)'s' << 48 | (long)'a' << 40 | (long)'q' << 32 | (long)'u' << 24 | (long)'o' << 16,
(long)'l' << 56 | (long)'s' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, (long)'l' << 56 | (long)'s' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'l' << 56 | (long)'t' << 48, (long)'l' << 56 | (long)'t' << 48,
(long)'m' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'r' << 32, (long)'m' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'r' << 32,
(long)'m' << 56 | (long)'d' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'h' << 24, (long)'m' << 56 | (long)'d' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'h' << 24,
(long)'m' << 56 | (long)'i' << 48 | (long)'c' << 40 | (long)'r' << 32 | (long)'o' << 24, (long)'m' << 56 | (long)'i' << 48 | (long)'c' << 40 | (long)'r' << 32 | (long)'o' << 24,
(long)'m' << 56 | (long)'i' << 48 | (long)'d' << 40 | (long)'d' << 32 | (long)'o' << 24 | (long)'t' << 16, (long)'m' << 56 | (long)'i' << 48 | (long)'d' << 40 | (long)'d' << 32 | (long)'o' << 24 | (long)'t' << 16,
(long)'m' << 56 | (long)'i' << 48 | (long)'n' << 40 | (long)'u' << 32 | (long)'s' << 24, (long)'m' << 56 | (long)'i' << 48 | (long)'n' << 40 | (long)'u' << 32 | (long)'s' << 24,
(long)'m' << 56 | (long)'u' << 48, (long)'m' << 56 | (long)'u' << 48,
(long)'n' << 56 | (long)'a' << 48 | (long)'b' << 40 | (long)'l' << 32 | (long)'a' << 24, (long)'n' << 56 | (long)'a' << 48 | (long)'b' << 40 | (long)'l' << 32 | (long)'a' << 24,
(long)'n' << 56 | (long)'b' << 48 | (long)'s' << 40 | (long)'p' << 32, (long)'n' << 56 | (long)'b' << 48 | (long)'s' << 40 | (long)'p' << 32,
(long)'n' << 56 | (long)'d' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'h' << 24, (long)'n' << 56 | (long)'d' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'h' << 24,
(long)'n' << 56 | (long)'e' << 48, (long)'n' << 56 | (long)'e' << 48,
(long)'n' << 56 | (long)'i' << 48, (long)'n' << 56 | (long)'i' << 48,
(long)'n' << 56 | (long)'o' << 48 | (long)'t' << 40, (long)'n' << 56 | (long)'o' << 48 | (long)'t' << 40,
(long)'n' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'i' << 32 | (long)'n' << 24, (long)'n' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'i' << 32 | (long)'n' << 24,
(long)'n' << 56 | (long)'s' << 48 | (long)'u' << 40 | (long)'b' << 32, (long)'n' << 56 | (long)'s' << 48 | (long)'u' << 40 | (long)'b' << 32,
(long)'n' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, (long)'n' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
(long)'n' << 56 | (long)'u' << 48, (long)'n' << 56 | (long)'u' << 48,
(long)'o' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, (long)'o' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'o' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, (long)'o' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'o' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, (long)'o' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
(long)'o' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, (long)'o' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'o' << 56 | (long)'l' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'e' << 24, (long)'o' << 56 | (long)'l' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'e' << 24,
(long)'o' << 56 | (long)'m' << 48 | (long)'e' << 40 | (long)'g' << 32 | (long)'a' << 24, (long)'o' << 56 | (long)'m' << 48 | (long)'e' << 40 | (long)'g' << 32 | (long)'a' << 24,
(long)'o' << 56 | (long)'m' << 48 | (long)'i' << 40 | (long)'c' << 32 | (long)'r' << 24 | (long)'o' << 16 | (long)'n' << 8, (long)'o' << 56 | (long)'m' << 48 | (long)'i' << 40 | (long)'c' << 32 | (long)'r' << 24 | (long)'o' << 16 | (long)'n' << 8,
(long)'o' << 56 | (long)'p' << 48 | (long)'l' << 40 | (long)'u' << 32 | (long)'s' << 24, (long)'o' << 56 | (long)'p' << 48 | (long)'l' << 40 | (long)'u' << 32 | (long)'s' << 24,
(long)'o' << 56 | (long)'r' << 48, (long)'o' << 56 | (long)'r' << 48,
(long)'o' << 56 | (long)'r' << 48 | (long)'d' << 40 | (long)'f' << 32, (long)'o' << 56 | (long)'r' << 48 | (long)'d' << 40 | (long)'f' << 32,
(long)'o' << 56 | (long)'r' << 48 | (long)'d' << 40 | (long)'m' << 32, (long)'o' << 56 | (long)'r' << 48 | (long)'d' << 40 | (long)'m' << 32,
(long)'o' << 56 | (long)'s' << 48 | (long)'l' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'h' << 16, (long)'o' << 56 | (long)'s' << 48 | (long)'l' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'h' << 16,
(long)'o' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, (long)'o' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
(long)'o' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24 | (long)'s' << 16, (long)'o' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24 | (long)'s' << 16,
(long)'o' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, (long)'o' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'p' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'a' << 32, (long)'p' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'a' << 32,
(long)'p' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'t' << 32, (long)'p' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'t' << 32,
(long)'p' << 56 | (long)'e' << 48 | (long)'r' << 40 | (long)'m' << 32 | (long)'i' << 24 | (long)'l' << 16, (long)'p' << 56 | (long)'e' << 48 | (long)'r' << 40 | (long)'m' << 32 | (long)'i' << 24 | (long)'l' << 16,
(long)'p' << 56 | (long)'e' << 48 | (long)'r' << 40 | (long)'p' << 32, (long)'p' << 56 | (long)'e' << 48 | (long)'r' << 40 | (long)'p' << 32,
(long)'p' << 56 | (long)'h' << 48 | (long)'i' << 40, (long)'p' << 56 | (long)'h' << 48 | (long)'i' << 40,
(long)'p' << 56 | (long)'i' << 48, (long)'p' << 56 | (long)'i' << 48,
(long)'p' << 56 | (long)'i' << 48 | (long)'v' << 40, (long)'p' << 56 | (long)'i' << 48 | (long)'v' << 40,
(long)'p' << 56 | (long)'l' << 48 | (long)'u' << 40 | (long)'s' << 32 | (long)'m' << 24 | (long)'n' << 16, (long)'p' << 56 | (long)'l' << 48 | (long)'u' << 40 | (long)'s' << 32 | (long)'m' << 24 | (long)'n' << 16,
(long)'p' << 56 | (long)'o' << 48 | (long)'u' << 40 | (long)'n' << 32 | (long)'d' << 24, (long)'p' << 56 | (long)'o' << 48 | (long)'u' << 40 | (long)'n' << 32 | (long)'d' << 24,
(long)'p' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24, (long)'p' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24,
(long)'p' << 56 | (long)'r' << 48 | (long)'o' << 40 | (long)'d' << 32, (long)'p' << 56 | (long)'r' << 48 | (long)'o' << 40 | (long)'d' << 32,
(long)'p' << 56 | (long)'r' << 48 | (long)'o' << 40 | (long)'p' << 32, (long)'p' << 56 | (long)'r' << 48 | (long)'o' << 40 | (long)'p' << 32,
(long)'p' << 56 | (long)'s' << 48 | (long)'i' << 40, (long)'p' << 56 | (long)'s' << 48 | (long)'i' << 40,
(long)'q' << 56 | (long)'u' << 48 | (long)'o' << 40 | (long)'t' << 32, (long)'q' << 56 | (long)'u' << 48 | (long)'o' << 40 | (long)'t' << 32,
(long)'r' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, (long)'r' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'r' << 56 | (long)'a' << 48 | (long)'d' << 40 | (long)'i' << 32 | (long)'c' << 24, (long)'r' << 56 | (long)'a' << 48 | (long)'d' << 40 | (long)'i' << 32 | (long)'c' << 24,
(long)'r' << 56 | (long)'a' << 48 | (long)'n' << 40 | (long)'g' << 32, (long)'r' << 56 | (long)'a' << 48 | (long)'n' << 40 | (long)'g' << 32,
(long)'r' << 56 | (long)'a' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, (long)'r' << 56 | (long)'a' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'r' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, (long)'r' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'r' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'i' << 32 | (long)'l' << 24, (long)'r' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'i' << 32 | (long)'l' << 24,
(long)'r' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, (long)'r' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'r' << 56 | (long)'e' << 48 | (long)'a' << 40 | (long)'l' << 32, (long)'r' << 56 | (long)'e' << 48 | (long)'a' << 40 | (long)'l' << 32,
(long)'r' << 56 | (long)'e' << 48 | (long)'g' << 40, (long)'r' << 56 | (long)'e' << 48 | (long)'g' << 40,
(long)'r' << 56 | (long)'f' << 48 | (long)'l' << 40 | (long)'o' << 32 | (long)'o' << 24 | (long)'r' << 16, (long)'r' << 56 | (long)'f' << 48 | (long)'l' << 40 | (long)'o' << 32 | (long)'o' << 24 | (long)'r' << 16,
(long)'r' << 56 | (long)'h' << 48 | (long)'o' << 40, (long)'r' << 56 | (long)'h' << 48 | (long)'o' << 40,
(long)'r' << 56 | (long)'l' << 48 | (long)'m' << 40, (long)'r' << 56 | (long)'l' << 48 | (long)'m' << 40,
(long)'r' << 56 | (long)'s' << 48 | (long)'a' << 40 | (long)'q' << 32 | (long)'u' << 24 | (long)'o' << 16, (long)'r' << 56 | (long)'s' << 48 | (long)'a' << 40 | (long)'q' << 32 | (long)'u' << 24 | (long)'o' << 16,
(long)'r' << 56 | (long)'s' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, (long)'r' << 56 | (long)'s' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'s' << 56 | (long)'b' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, (long)'s' << 56 | (long)'b' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'s' << 56 | (long)'c' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'o' << 24 | (long)'n' << 16, (long)'s' << 56 | (long)'c' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'o' << 24 | (long)'n' << 16,
(long)'s' << 56 | (long)'d' << 48 | (long)'o' << 40 | (long)'t' << 32, (long)'s' << 56 | (long)'d' << 48 | (long)'o' << 40 | (long)'t' << 32,
(long)'s' << 56 | (long)'e' << 48 | (long)'c' << 40 | (long)'t' << 32, (long)'s' << 56 | (long)'e' << 48 | (long)'c' << 40 | (long)'t' << 32,
(long)'s' << 56 | (long)'h' << 48 | (long)'y' << 40, (long)'s' << 56 | (long)'h' << 48 | (long)'y' << 40,
(long)'s' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24, (long)'s' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24,
(long)'s' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24 | (long)'f' << 16, (long)'s' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24 | (long)'f' << 16,
(long)'s' << 56 | (long)'i' << 48 | (long)'m' << 40, (long)'s' << 56 | (long)'i' << 48 | (long)'m' << 40,
(long)'s' << 56 | (long)'p' << 48 | (long)'a' << 40 | (long)'d' << 32 | (long)'e' << 24 | (long)'s' << 16, (long)'s' << 56 | (long)'p' << 48 | (long)'a' << 40 | (long)'d' << 32 | (long)'e' << 24 | (long)'s' << 16,
(long)'s' << 56 | (long)'u' << 48 | (long)'b' << 40, (long)'s' << 56 | (long)'u' << 48 | (long)'b' << 40,
(long)'s' << 56 | (long)'u' << 48 | (long)'b' << 40 | (long)'e' << 32, (long)'s' << 56 | (long)'u' << 48 | (long)'b' << 40 | (long)'e' << 32,
(long)'s' << 56 | (long)'u' << 48 | (long)'m' << 40, (long)'s' << 56 | (long)'u' << 48 | (long)'m' << 40,
(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40, (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40,
(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'1' << 32, (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'1' << 32,
(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'2' << 32, (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'2' << 32,
(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'3' << 32, (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'3' << 32,
(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'e' << 32, (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'e' << 32,
(long)'s' << 56 | (long)'z' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, (long)'s' << 56 | (long)'z' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
(long)'t' << 56 | (long)'a' << 48 | (long)'u' << 40, (long)'t' << 56 | (long)'a' << 48 | (long)'u' << 40,
(long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'r' << 32 | (long)'e' << 24 | (long)'4' << 16, (long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'r' << 32 | (long)'e' << 24 | (long)'4' << 16,
(long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24, (long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24,
(long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24 | (long)'s' << 16 | (long)'y' << 8 | (long)'m' << 0, (long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24 | (long)'s' << 16 | (long)'y' << 8 | (long)'m' << 0,
(long)'t' << 56 | (long)'h' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'s' << 24 | (long)'p' << 16, (long)'t' << 56 | (long)'h' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'s' << 24 | (long)'p' << 16,
(long)'t' << 56 | (long)'h' << 48 | (long)'o' << 40 | (long)'r' << 32 | (long)'n' << 24, (long)'t' << 56 | (long)'h' << 48 | (long)'o' << 40 | (long)'r' << 32 | (long)'n' << 24,
(long)'t' << 56 | (long)'i' << 48 | (long)'l' << 40 | (long)'d' << 32 | (long)'e' << 24, (long)'t' << 56 | (long)'i' << 48 | (long)'l' << 40 | (long)'d' << 32 | (long)'e' << 24,
(long)'t' << 56 | (long)'i' << 48 | (long)'m' << 40 | (long)'e' << 32 | (long)'s' << 24, (long)'t' << 56 | (long)'i' << 48 | (long)'m' << 40 | (long)'e' << 32 | (long)'s' << 24,
(long)'t' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'d' << 32 | (long)'e' << 24, (long)'t' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'d' << 32 | (long)'e' << 24,
(long)'u' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, (long)'u' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'u' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, (long)'u' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'u' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, (long)'u' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'u' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, (long)'u' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'u' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, (long)'u' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'u' << 56 | (long)'m' << 48 | (long)'l' << 40, (long)'u' << 56 | (long)'m' << 48 | (long)'l' << 40,
(long)'u' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'h' << 24, (long)'u' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'h' << 24,
(long)'u' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, (long)'u' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8,
(long)'u' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, (long)'u' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'w' << 56 | (long)'e' << 48 | (long)'i' << 40 | (long)'e' << 32 | (long)'r' << 24 | (long)'p' << 16, (long)'w' << 56 | (long)'e' << 48 | (long)'i' << 40 | (long)'e' << 32 | (long)'r' << 24 | (long)'p' << 16,
(long)'x' << 56 | (long)'i' << 48, (long)'x' << 56 | (long)'i' << 48,
(long)'y' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, (long)'y' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'y' << 56 | (long)'e' << 48 | (long)'n' << 40, (long)'y' << 56 | (long)'e' << 48 | (long)'n' << 40,
(long)'y' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, (long)'y' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'z' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, (long)'z' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32,
(long)'z' << 56 | (long)'w' << 48 | (long)'j' << 40, (long)'z' << 56 | (long)'w' << 48 | (long)'j' << 40,
(long)'z' << 56 | (long)'w' << 48 | (long)'n' << 40 | (long)'j' << 32 (long)'z' << 56 | (long)'w' << 48 | (long)'n' << 40 | (long)'j' << 32
}; };
static readonly char[] entities_values = new char[] { static readonly char[] entities_values = new char[] {
'\u00C6', '\u00C6', '\u00C1', '\u00C2', '\u00C0', '\u0391', '\u00C5', '\u00C3', '\u00C4', '\u0392', '\u00C7', '\u03A7',
'\u00C1', '\u2021', '\u0394', '\u00D0', '\u00C9', '\u00CA', '\u00C8', '\u0395', '\u0397', '\u00CB', '\u0393', '\u00CD',
'\u00C2', '\u00CE', '\u00CC', '\u0399', '\u00CF', '\u039A', '\u039B', '\u039C', '\u00D1', '\u039D', '\u0152', '\u00D3',
'\u00C0', '\u00D4', '\u00D2', '\u03A9', '\u039F', '\u00D8', '\u00D5', '\u00D6', '\u03A6', '\u03A0', '\u2033', '\u03A8',
'\u0391', '\u03A1', '\u0160', '\u03A3', '\u00DE', '\u03A4', '\u0398', '\u00DA', '\u00DB', '\u00D9', '\u03A5', '\u00DC',
'\u00C5', '\u039E', '\u00DD', '\u0178', '\u0396', '\u00E1', '\u00E2', '\u00B4', '\u00E6', '\u00E0', '\u2135', '\u03B1',
'\u00C3', '\u0026', '\u2227', '\u2220', '\u0027', '\u00E5', '\u2248', '\u00E3', '\u00E4', '\u201E', '\u03B2', '\u00A6',
'\u00C4', '\u2022', '\u2229', '\u00E7', '\u00B8', '\u00A2', '\u03C7', '\u02C6', '\u2663', '\u2245', '\u00A9', '\u21B5',
'\u0392', '\u222A', '\u00A4', '\u21D3', '\u2020', '\u2193', '\u00B0', '\u03B4', '\u2666', '\u00F7', '\u00E9', '\u00EA',
'\u00C7', '\u00E8', '\u2205', '\u2003', '\u2002', '\u03B5', '\u2261', '\u03B7', '\u00F0', '\u00EB', '\u20AC', '\u2203',
'\u03A7', '\u0192', '\u2200', '\u00BD', '\u00BC', '\u00BE', '\u2044', '\u03B3', '\u2265', '\u003E', '\u21D4', '\u2194',
'\u2021', '\u2665', '\u2026', '\u00ED', '\u00EE', '\u00A1', '\u00EC', '\u2111', '\u221E', '\u222B', '\u03B9', '\u00BF',
'\u0394', '\u2208', '\u00EF', '\u03BA', '\u21D0', '\u03BB', '\u2329', '\u00AB', '\u2190', '\u2308', '\u201C', '\u2264',
'\u00D0', '\u230A', '\u2217', '\u25CA', '\u200E', '\u2039', '\u2018', '\u003C', '\u00AF', '\u2014', '\u00B5', '\u00B7',
'\u00C9', '\u2212', '\u03BC', '\u2207', '\u00A0', '\u2013', '\u2260', '\u220B', '\u00AC', '\u2209', '\u2284', '\u00F1',
'\u00CA', '\u03BD', '\u00F3', '\u00F4', '\u0153', '\u00F2', '\u203E', '\u03C9', '\u03BF', '\u2295', '\u2228', '\u00AA',
'\u00C8', '\u00BA', '\u00F8', '\u00F5', '\u2297', '\u00F6', '\u00B6', '\u2202', '\u2030', '\u22A5', '\u03C6', '\u03C0',
'\u0395', '\u03D6', '\u00B1', '\u00A3', '\u2032', '\u220F', '\u221D', '\u03C8', '\u0022', '\u21D2', '\u221A', '\u232A',
'\u0397', '\u00BB', '\u2192', '\u2309', '\u201D', '\u211C', '\u00AE', '\u230B', '\u03C1', '\u200F', '\u203A', '\u2019',
'\u00CB', '\u201A', '\u0161', '\u22C5', '\u00A7', '\u00AD', '\u03C3', '\u03C2', '\u223C', '\u2660', '\u2282', '\u2286',
'\u0393', '\u2211', '\u2283', '\u00B9', '\u00B2', '\u00B3', '\u2287', '\u00DF', '\u03C4', '\u2234', '\u03B8', '\u03D1',
'\u00CD', '\u2009', '\u00FE', '\u02DC', '\u00D7', '\u2122', '\u21D1', '\u00FA', '\u2191', '\u00FB', '\u00F9', '\u00A8',
'\u00CE', '\u03D2', '\u03C5', '\u00FC', '\u2118', '\u03BE', '\u00FD', '\u00A5', '\u00FF', '\u03B6', '\u200D', '\u200C'
'\u00CC', };
'\u0399',
'\u00CF',
'\u039A',
'\u039B',
'\u039C',
'\u00D1',
'\u039D',
'\u0152',
'\u00D3',
'\u00D4',
'\u00D2',
'\u03A9',
'\u039F',
'\u00D8',
'\u00D5',
'\u00D6',
'\u03A6',
'\u03A0',
'\u2033',
'\u03A8',
'\u03A1',
'\u0160',
'\u03A3',
'\u00DE',
'\u03A4',
'\u0398',
'\u00DA',
'\u00DB',
'\u00D9',
'\u03A5',
'\u00DC',
'\u039E',
'\u00DD',
'\u0178',
'\u0396',
'\u00E1',
'\u00E2',
'\u00B4',
'\u00E6',
'\u00E0',
'\u2135',
'\u03B1',
'\u0026',
'\u2227',
'\u2220',
'\u0027',
'\u00E5',
'\u2248',
'\u00E3',
'\u00E4',
'\u201E',
'\u03B2',
'\u00A6',
'\u2022',
'\u2229',
'\u00E7',
'\u00B8',
'\u00A2',
'\u03C7',
'\u02C6',
'\u2663',
'\u2245',
'\u00A9',
'\u21B5',
'\u222A',
'\u00A4',
'\u21D3',
'\u2020',
'\u2193',
'\u00B0',
'\u03B4',
'\u2666',
'\u00F7',
'\u00E9',
'\u00EA',
'\u00E8',
'\u2205',
'\u2003',
'\u2002',
'\u03B5',
'\u2261',
'\u03B7',
'\u00F0',
'\u00EB',
'\u20AC',
'\u2203',
'\u0192',
'\u2200',
'\u00BD',
'\u00BC',
'\u00BE',
'\u2044',
'\u03B3',
'\u2265',
'\u003E',
'\u21D4',
'\u2194',
'\u2665',
'\u2026',
'\u00ED',
'\u00EE',
'\u00A1',
'\u00EC',
'\u2111',
'\u221E',
'\u222B',
'\u03B9',
'\u00BF',
'\u2208',
'\u00EF',
'\u03BA',
'\u21D0',
'\u03BB',
'\u2329',
'\u00AB',
'\u2190',
'\u2308',
'\u201C',
'\u2264',
'\u230A',
'\u2217',
'\u25CA',
'\u200E',
'\u2039',
'\u2018',
'\u003C',
'\u00AF',
'\u2014',
'\u00B5',
'\u00B7',
'\u2212',
'\u03BC',
'\u2207',
'\u00A0',
'\u2013',
'\u2260',
'\u220B',
'\u00AC',
'\u2209',
'\u2284',
'\u00F1',
'\u03BD',
'\u00F3',
'\u00F4',
'\u0153',
'\u00F2',
'\u203E',
'\u03C9',
'\u03BF',
'\u2295',
'\u2228',
'\u00AA',
'\u00BA',
'\u00F8',
'\u00F5',
'\u2297',
'\u00F6',
'\u00B6',
'\u2202',
'\u2030',
'\u22A5',
'\u03C6',
'\u03C0',
'\u03D6',
'\u00B1',
'\u00A3',
'\u2032',
'\u220F',
'\u221D',
'\u03C8',
'\u0022',
'\u21D2',
'\u221A',
'\u232A',
'\u00BB',
'\u2192',
'\u2309',
'\u201D',
'\u211C',
'\u00AE',
'\u230B',
'\u03C1',
'\u200F',
'\u203A',
'\u2019',
'\u201A',
'\u0161',
'\u22C5',
'\u00A7',
'\u00AD',
'\u03C3',
'\u03C2',
'\u223C',
'\u2660',
'\u2282',
'\u2286',
'\u2211',
'\u2283',
'\u00B9',
'\u00B2',
'\u00B3',
'\u2287',
'\u00DF',
'\u03C4',
'\u2234',
'\u03B8',
'\u03D1',
'\u2009',
'\u00FE',
'\u02DC',
'\u00D7',
'\u2122',
'\u21D1',
'\u00FA',
'\u2191',
'\u00FB',
'\u00F9',
'\u00A8',
'\u03D2',
'\u03C5',
'\u00FC',
'\u2118',
'\u03BE',
'\u00FD',
'\u00A5',
'\u00FF',
'\u03B6',
'\u200D',
'\u200C'
};
#region Methods #region Methods

@ -6,12 +6,12 @@ namespace MediaBrowser.Model.Services
public class RouteAttribute : Attribute public class RouteAttribute : Attribute
{ {
/// <summary> /// <summary>
/// <para>Initializes an instance of the <see cref="RouteAttribute"/> class.</para> /// <para>Initializes an instance of the <see cref="RouteAttribute"/> class.</para>
/// </summary> /// </summary>
/// <param name="path"> /// <param name="path">
/// <para>The path template to map to the request. See /// <para>The path template to map to the request. See
/// <see cref="Path">RouteAttribute.Path</see> /// <see cref="Path">RouteAttribute.Path</see>
/// for details on the correct format.</para> /// for details on the correct format.</para>
/// </param> /// </param>
public RouteAttribute(string path) public RouteAttribute(string path)
: this(path, null) : this(path, null)
@ -19,15 +19,15 @@ namespace MediaBrowser.Model.Services
} }
/// <summary> /// <summary>
/// <para>Initializes an instance of the <see cref="RouteAttribute"/> class.</para> /// <para>Initializes an instance of the <see cref="RouteAttribute"/> class.</para>
/// </summary> /// </summary>
/// <param name="path"> /// <param name="path">
/// <para>The path template to map to the request. See /// <para>The path template to map to the request. See
/// <see cref="Path">RouteAttribute.Path</see> /// <see cref="Path">RouteAttribute.Path</see>
/// for details on the correct format.</para> /// for details on the correct format.</para>
/// </param> /// </param>
/// <param name="verbs">A comma-delimited list of HTTP verbs supported by the /// <param name="verbs">A comma-delimited list of HTTP verbs supported by the
/// service. If unspecified, all verbs are assumed to be supported.</param> /// service. If unspecified, all verbs are assumed to be supported.</param>
public RouteAttribute(string path, string verbs) public RouteAttribute(string path, string verbs)
{ {
Path = path; Path = path;
@ -35,51 +35,51 @@ namespace MediaBrowser.Model.Services
} }
/// <summary> /// <summary>
/// Gets or sets the path template to be mapped to the request. /// Gets or sets the path template to be mapped to the request.
/// </summary> /// </summary>
/// <value> /// <value>
/// A <see cref="String"/> value providing the path mapped to /// A <see cref="String"/> value providing the path mapped to
/// the request. Never <see langword="null"/>. /// the request. Never <see langword="null"/>.
/// </value> /// </value>
/// <remarks> /// <remarks>
/// <para>Some examples of valid paths are:</para> /// <para>Some examples of valid paths are:</para>
/// ///
/// <list> /// <list>
/// <item>"/Inventory"</item> /// <item>"/Inventory"</item>
/// <item>"/Inventory/{Category}/{ItemId}"</item> /// <item>"/Inventory/{Category}/{ItemId}"</item>
/// <item>"/Inventory/{ItemPath*}"</item> /// <item>"/Inventory/{ItemPath*}"</item>
/// </list> /// </list>
/// ///
/// <para>Variables are specified within "{}" /// <para>Variables are specified within "{}"
/// brackets. Each variable in the path is mapped to the same-named property /// brackets. Each variable in the path is mapped to the same-named property
/// on the request DTO. At runtime, ServiceStack will parse the /// on the request DTO. At runtime, ServiceStack will parse the
/// request URL, extract the variable values, instantiate the request DTO, /// request URL, extract the variable values, instantiate the request DTO,
/// and assign the variable values into the corresponding request properties, /// and assign the variable values into the corresponding request properties,
/// prior to passing the request DTO to the service object for processing.</para> /// prior to passing the request DTO to the service object for processing.</para>
/// ///
/// <para>It is not necessary to specify all request properties as /// <para>It is not necessary to specify all request properties as
/// variables in the path. For unspecified properties, callers may provide /// variables in the path. For unspecified properties, callers may provide
/// values in the query string. For example: the URL /// values in the query string. For example: the URL
/// "http://services/Inventory?Category=Books&amp;ItemId=12345" causes the same /// "http://services/Inventory?Category=Books&amp;ItemId=12345" causes the same
/// request DTO to be processed as "http://services/Inventory/Books/12345", /// request DTO to be processed as "http://services/Inventory/Books/12345",
/// provided that the paths "/Inventory" (which supports the first URL) and /// provided that the paths "/Inventory" (which supports the first URL) and
/// "/Inventory/{Category}/{ItemId}" (which supports the second URL) /// "/Inventory/{Category}/{ItemId}" (which supports the second URL)
/// are both mapped to the request DTO.</para> /// are both mapped to the request DTO.</para>
/// ///
/// <para>Please note that while it is possible to specify property values /// <para>Please note that while it is possible to specify property values
/// in the query string, it is generally considered to be less RESTful and /// in the query string, it is generally considered to be less RESTful and
/// less desirable than to specify them as variables in the path. Using the /// less desirable than to specify them as variables in the path. Using the
/// query string to specify property values may also interfere with HTTP /// query string to specify property values may also interfere with HTTP
/// caching.</para> /// caching.</para>
/// ///
/// <para>The final variable in the path may contain a "*" suffix /// <para>The final variable in the path may contain a "*" suffix
/// to grab all remaining segments in the path portion of the request URL and assign /// to grab all remaining segments in the path portion of the request URL and assign
/// them to a single property on the request DTO. /// them to a single property on the request DTO.
/// For example, if the path "/Inventory/{ItemPath*}" is mapped to the request DTO, /// For example, if the path "/Inventory/{ItemPath*}" is mapped to the request DTO,
/// then the request URL "http://services/Inventory/Books/12345" will result /// then the request URL "http://services/Inventory/Books/12345" will result
/// in a request DTO whose ItemPath property contains "Books/12345". /// in a request DTO whose ItemPath property contains "Books/12345".
/// You may only specify one such variable in the path, and it must be positioned at /// You may only specify one such variable in the path, and it must be positioned at
/// the end of the path.</para> /// the end of the path.</para>
/// </remarks> /// </remarks>
public string Path { get; set; } public string Path { get; set; }
@ -98,12 +98,12 @@ namespace MediaBrowser.Model.Services
public string Notes { get; set; } public string Notes { get; set; }
/// <summary> /// <summary>
/// Gets or sets a comma-delimited list of HTTP verbs supported by the service, such as /// Gets or sets a comma-delimited list of HTTP verbs supported by the service, such as
/// "GET,PUT,POST,DELETE". /// "GET,PUT,POST,DELETE".
/// </summary> /// </summary>
/// <value> /// <value>
/// A <see cref="String"/> providing a comma-delimited list of HTTP verbs supported /// A <see cref="String"/> providing a comma-delimited list of HTTP verbs supported
/// by the service, <see langword="null"/> or empty if all verbs are supported. /// by the service, <see langword="null"/> or empty if all verbs are supported.
/// </value> /// </value>
public string Verbs { get; set; } public string Verbs { get; set; }

@ -158,7 +158,7 @@ namespace MediaBrowser.Providers.BoxSets
var dataFilePath = GetDataFilePath(_config.ApplicationPaths, tmdbId, preferredMetadataLanguage); var dataFilePath = GetDataFilePath(_config.ApplicationPaths, tmdbId, preferredMetadataLanguage);
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath)); _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath));
_json.SerializeToFile(mainResult, dataFilePath); _json.SerializeToFile(mainResult, dataFilePath);
} }

@ -270,7 +270,7 @@ namespace MediaBrowser.Providers.Movies
var path = GetFanartJsonPath(id); var path = GetFanartJsonPath(id);
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
try try
{ {

@ -158,7 +158,7 @@ namespace MediaBrowser.Providers.Music
var path = GetAlbumInfoPath(_config.ApplicationPaths, musicBrainzReleaseGroupId); var path = GetAlbumInfoPath(_config.ApplicationPaths, musicBrainzReleaseGroupId);
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
using (var httpResponse = await _httpClient.SendAsync(new HttpRequestOptions using (var httpResponse = await _httpClient.SendAsync(new HttpRequestOptions
{ {

@ -312,7 +312,7 @@ namespace MediaBrowser.Providers.TV
var path = GetFanartJsonPath(tvdbId); var path = GetFanartJsonPath(tvdbId);
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path)); _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
try try
{ {

@ -106,7 +106,7 @@ namespace MediaBrowser.Providers.TV
var dataFilePath = GetDataFilePath(id, seasonNumber, episodeNumber, preferredMetadataLanguage); var dataFilePath = GetDataFilePath(id, seasonNumber, episodeNumber, preferredMetadataLanguage);
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath)); _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath));
_jsonSerializer.SerializeToFile(mainResult, dataFilePath); _jsonSerializer.SerializeToFile(mainResult, dataFilePath);
} }

@ -194,7 +194,7 @@ namespace MediaBrowser.Providers.TV
var dataFilePath = GetDataFilePath(id, seasonNumber, preferredMetadataLanguage); var dataFilePath = GetDataFilePath(id, seasonNumber, preferredMetadataLanguage);
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath)); _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath));
_jsonSerializer.SerializeToFile(mainResult, dataFilePath); _jsonSerializer.SerializeToFile(mainResult, dataFilePath);
} }

@ -58,27 +58,27 @@ namespace MediaBrowser.Providers.TV
// Process images // Process images
var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.ProviderIds); var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.ProviderIds);
var nodes = TvdbEpisodeProvider.Current.GetEpisodeXmlNodes(seriesDataPath, episode.GetLookupInfo()); var nodes = TvdbEpisodeProvider.Current.GetEpisodeXmlNodes(seriesDataPath, episode.GetLookupInfo());
var result = nodes.Select(i => GetImageInfo(i, cancellationToken)) var result = nodes.Select(i => GetImageInfo(i, cancellationToken))
.Where(i => i != null) .Where(i => i != null)
.ToList(); .ToList();
return Task.FromResult<IEnumerable<RemoteImageInfo>>(result); return Task.FromResult<IEnumerable<RemoteImageInfo>>(result);
} }
return Task.FromResult<IEnumerable<RemoteImageInfo>>(new RemoteImageInfo[] { }); return Task.FromResult<IEnumerable<RemoteImageInfo>>(new RemoteImageInfo[] { });
} }
private RemoteImageInfo GetImageInfo(XmlReader reader, CancellationToken cancellationToken) private RemoteImageInfo GetImageInfo(XmlReader reader, CancellationToken cancellationToken)
{ {
var height = 225; var height = 225;
var width = 400; var width = 400;
var url = string.Empty; var url = string.Empty;
// Use XmlReader for best performance // Use XmlReader for best performance
using (reader) using (reader)
{ {
reader.MoveToContent(); reader.MoveToContent();
reader.Read(); reader.Read();
@ -146,7 +146,7 @@ namespace MediaBrowser.Providers.TV
reader.Read(); reader.Read();
} }
} }
} }
if (string.IsNullOrEmpty(url)) if (string.IsNullOrEmpty(url))
{ {

@ -143,7 +143,7 @@ namespace MediaBrowser.Providers.TV
/// <param name="seriesDataPath">The series data path.</param> /// <param name="seriesDataPath">The series data path.</param>
/// <param name="searchInfo">The search information.</param> /// <param name="searchInfo">The search information.</param>
/// <returns>List{FileInfo}.</returns> /// <returns>List{FileInfo}.</returns>
internal List<XmlReader> GetEpisodeXmlNodes(string seriesDataPath, EpisodeInfo searchInfo) internal List<XmlReader> GetEpisodeXmlNodes(string seriesDataPath, EpisodeInfo searchInfo)
{ {
var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage); var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage);

@ -38,7 +38,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
return false; return false;
} }
return updateType >= MinimumUpdateType || (updateType >= ItemUpdateType.MetadataImport && FileSystem.FileExists(GetSavePath(item))); return updateType >= MinimumUpdateType || (updateType >= ItemUpdateType.MetadataImport && FileSystem.FileExists(GetSavePath(item)));
} }
protected override void WriteCustomElements(BaseItem item, XmlWriter writer) protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
@ -62,7 +62,15 @@ namespace MediaBrowser.XbmcMetadata.Savers
return list; return list;
} }
public SeasonNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger, xmlReaderSettingsFactory) public SeasonNfoSaver(
IFileSystem fileSystem,
IServerConfigurationManager configurationManager,
ILibraryManager libraryManager,
IUserManager userManager,
IUserDataManager userDataManager,
ILogger logger,
IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger, xmlReaderSettingsFactory)
{ {
} }
} }

@ -34,23 +34,22 @@ using System.Threading.Tasks;
namespace Mono.Nat namespace Mono.Nat
{ {
public abstract class AbstractNatDevice : INatDevice public abstract class AbstractNatDevice : INatDevice
{ {
private DateTime lastSeen; private DateTime lastSeen;
protected AbstractNatDevice () protected AbstractNatDevice()
{ {
}
} public abstract IPAddress LocalAddress { get; }
public abstract IPAddress LocalAddress { get; } public DateTime LastSeen
{
get { return lastSeen; }
set { lastSeen = value; }
}
public DateTime LastSeen public abstract Task CreatePortMap(Mapping mapping);
{ }
get { return lastSeen; }
set { lastSeen = value; }
}
public abstract Task CreatePortMap(Mapping mapping);
}
} }

@ -28,9 +28,9 @@ using System;
namespace Mono.Nat namespace Mono.Nat
{ {
public enum Protocol public enum Protocol
{ {
Tcp, Tcp,
Udp Udp
} }
} }

@ -28,18 +28,18 @@ using System;
namespace Mono.Nat namespace Mono.Nat
{ {
public class DeviceEventArgs : EventArgs public class DeviceEventArgs : EventArgs
{ {
private INatDevice device; private INatDevice device;
public DeviceEventArgs(INatDevice device) public DeviceEventArgs(INatDevice device)
{ {
this.device = device; this.device = device;
} }
public INatDevice Device public INatDevice Device
{ {
get { return this.device; } get { return this.device; }
} }
} }
} }

@ -34,12 +34,12 @@ using System.Threading.Tasks;
namespace Mono.Nat namespace Mono.Nat
{ {
public interface INatDevice public interface INatDevice
{ {
Task CreatePortMap (Mapping mapping); Task CreatePortMap (Mapping mapping);
IPAddress LocalAddress { get; } IPAddress LocalAddress { get; }
DateTime LastSeen { get; set; } DateTime LastSeen { get; set; }
} }
} }

@ -30,36 +30,34 @@ using System;
namespace Mono.Nat namespace Mono.Nat
{ {
public class Mapping public class Mapping
{ {
private string description; private string description;
private DateTime expiration; private DateTime expiration;
private int lifetime; private int lifetime;
private int privatePort; private int privatePort;
private Protocol protocol; private Protocol protocol;
private int publicPort; private int publicPort;
public Mapping(Protocol protocol, int privatePort, int publicPort)
: this (protocol, privatePort, publicPort, 0)
{
}
public Mapping(Protocol protocol, int privatePort, int publicPort, int lifetime)
public Mapping (Protocol protocol, int privatePort, int publicPort) {
: this (protocol, privatePort, publicPort, 0) this.protocol = protocol;
{ this.privatePort = privatePort;
} this.publicPort = publicPort;
this.lifetime = lifetime;
public Mapping (Protocol protocol, int privatePort, int publicPort, int lifetime)
{ if (lifetime == int.MaxValue)
this.protocol = protocol; this.expiration = DateTime.MaxValue;
this.privatePort = privatePort; else if (lifetime == 0)
this.publicPort = publicPort; this.expiration = DateTime.Now;
this.lifetime = lifetime; else
this.expiration = DateTime.Now.AddSeconds (lifetime);
if (lifetime == int.MaxValue) }
this.expiration = DateTime.MaxValue;
else if (lifetime == 0)
this.expiration = DateTime.Now;
else
this.expiration = DateTime.Now.AddSeconds (lifetime);
}
public string Description public string Description
{ {
@ -67,57 +65,57 @@ namespace Mono.Nat
set { description = value; } set { description = value; }
} }
public Protocol Protocol public Protocol Protocol
{ {
get { return protocol; } get { return protocol; }
internal set { protocol = value; } internal set { protocol = value; }
} }
public int PrivatePort public int PrivatePort
{ {
get { return privatePort; } get { return privatePort; }
internal set { privatePort = value; } internal set { privatePort = value; }
} }
public int PublicPort public int PublicPort
{ {
get { return publicPort; } get { return publicPort; }
internal set { publicPort = value; } internal set { publicPort = value; }
} }
public int Lifetime public int Lifetime
{ {
get { return lifetime; } get { return lifetime; }
internal set { lifetime = value; } internal set { lifetime = value; }
} }
public DateTime Expiration public DateTime Expiration
{ {
get { return expiration; } get { return expiration; }
internal set { expiration = value; } internal set { expiration = value; }
} }
public bool IsExpired () public bool IsExpired()
{ {
return expiration < DateTime.Now; return expiration < DateTime.Now;
} }
public override bool Equals (object obj) public override bool Equals(object obj)
{ {
Mapping other = obj as Mapping; Mapping other = obj as Mapping;
return other == null ? false : this.protocol == other.protocol && return other == null ? false : this.protocol == other.protocol &&
this.privatePort == other.privatePort && this.publicPort == other.publicPort; this.privatePort == other.privatePort && this.publicPort == other.publicPort;
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return this.protocol.GetHashCode() ^ this.privatePort.GetHashCode() ^ this.publicPort.GetHashCode(); return this.protocol.GetHashCode() ^ this.privatePort.GetHashCode() ^ this.publicPort.GetHashCode();
} }
public override string ToString( ) public override string ToString()
{ {
return String.Format( "Protocol: {0}, Public Port: {1}, Private Port: {2}, Description: {3}, Expiration: {4}, Lifetime: {5}", return String.Format( "Protocol: {0}, Public Port: {1}, Private Port: {2}, Description: {3}, Expiration: {4}, Lifetime: {5}",
this.protocol, this.publicPort, this.privatePort, this.description, this.expiration, this.lifetime ); this.protocol, this.publicPort, this.privatePort, this.description, this.expiration, this.lifetime );
} }
} }
} }

@ -29,28 +29,28 @@ using System;
namespace Mono.Nat.Pmp namespace Mono.Nat.Pmp
{ {
internal static class PmpConstants internal static class PmpConstants
{ {
public const byte Version = (byte)0; public const byte Version = (byte)0;
public const byte OperationCode = (byte)0; public const byte OperationCode = (byte)0;
public const byte OperationCodeUdp = (byte)1; public const byte OperationCodeUdp = (byte)1;
public const byte OperationCodeTcp = (byte)2; public const byte OperationCodeTcp = (byte)2;
public const byte ServerNoop = (byte)128; public const byte ServerNoop = (byte)128;
public const int ClientPort = 5350; public const int ClientPort = 5350;
public const int ServerPort = 5351; public const int ServerPort = 5351;
public const int RetryDelay = 250; public const int RetryDelay = 250;
public const int RetryAttempts = 9; public const int RetryAttempts = 9;
public const int RecommendedLeaseTime = 60 * 60; public const int RecommendedLeaseTime = 60 * 60;
public const int DefaultLeaseTime = RecommendedLeaseTime; public const int DefaultLeaseTime = RecommendedLeaseTime;
public const short ResultCodeSuccess = 0; public const short ResultCodeSuccess = 0;
public const short ResultCodeUnsupportedVersion = 1; public const short ResultCodeUnsupportedVersion = 1;
public const short ResultCodeNotAuthorized = 2; public const short ResultCodeNotAuthorized = 2;
public const short ResultCodeNetworkFailure = 3; public const short ResultCodeNetworkFailure = 3;
public const short ResultCodeOutOfResources = 4; public const short ResultCodeOutOfResources = 4;
public const short ResultCodeUnsupportedOperationCode = 5; public const short ResultCodeUnsupportedOperationCode = 5;
} }
} }

@ -108,8 +108,8 @@ namespace Mono.Nat
foreach (var unicast in properties.UnicastAddresses) foreach (var unicast in properties.UnicastAddresses)
{ {
if (/*unicast.DuplicateAddressDetectionState == DuplicateAddressDetectionState.Preferred if (/*unicast.DuplicateAddressDetectionState == DuplicateAddressDetectionState.Preferred
&& unicast.AddressPreferredLifetime != UInt32.MaxValue && unicast.AddressPreferredLifetime != UInt32.MaxValue
&& */unicast.Address.AddressFamily == AddressFamily.InterNetwork) && */unicast.Address.AddressFamily == AddressFamily.InterNetwork)
{ {
var bytes = unicast.Address.GetAddressBytes(); var bytes = unicast.Address.GetAddressBytes();
bytes[3] = 1; bytes[3] = 1;

@ -58,8 +58,8 @@ namespace Mono.Nat
} }
public void Search() public void Search()
{ {
} }
public async Task Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint) public async Task Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint)
{ {
@ -71,15 +71,15 @@ namespace Mono.Nat
try try
{ {
/* For UPnP Port Mapping we need ot find either WANPPPConnection or WANIPConnection. /* For UPnP Port Mapping we need ot find either WANPPPConnection or WANIPConnection.
Any other device type is no good to us for this purpose. See the IGP overview paper * Any other device type is no good to us for this purpose. See the IGP overview paper
page 5 for an overview of device types and their hierarchy. * page 5 for an overview of device types and their hierarchy.
http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf */ * http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf */
/* TODO: Currently we are assuming version 1 of the protocol. We should figure out which /* TODO: Currently we are assuming version 1 of the protocol. We should figure out which
version it is and apply the correct URN. */ * version it is and apply the correct URN. */
/* Some routers don't correctly implement the version ID on the URN, so we only search for the type /* Some routers don't correctly implement the version ID on the URN, so we only search for the type
prefix. */ * prefix. */
// We have an internet gateway device now // We have an internet gateway device now
UpnpNatDevice d = new UpnpNatDevice(localAddress, deviceInfo, endpoint, string.Empty, _logger, _httpClient); UpnpNatDevice d = new UpnpNatDevice(localAddress, deviceInfo, endpoint, string.Empty, _logger, _httpClient);

@ -47,7 +47,7 @@ namespace OpenSubtitlesHandler
/// <returns>The hash as Hexadecimal string</returns> /// <returns>The hash as Hexadecimal string</returns>
public static string ComputeHash(Stream stream) public static string ComputeHash(Stream stream)
{ {
byte[] hash = MovieHasher.ComputeMovieHash(stream); byte[] hash = MovieHasher.ComputeMovieHash(stream);
return MovieHasher.ToHexadecimal(hash); return MovieHasher.ToHexadecimal(hash);
} }
/// <summary> /// <summary>

@ -6,57 +6,56 @@ using MediaBrowser.Model.Net;
namespace Rssdp namespace Rssdp
{ {
/// <summary> /// <summary>
/// Event arguments for the <see cref="Rssdp.Infrastructure.SsdpDeviceLocatorBase.DeviceAvailable"/> event. /// Event arguments for the <see cref="Rssdp.Infrastructure.SsdpDeviceLocatorBase.DeviceAvailable"/> event.
/// </summary> /// </summary>
public sealed class DeviceAvailableEventArgs : EventArgs public sealed class DeviceAvailableEventArgs : EventArgs
{ {
public IpAddressInfo LocalIpAddress { get; set; } public IpAddressInfo LocalIpAddress { get; set; }
#region Fields #region Fields
private readonly DiscoveredSsdpDevice _DiscoveredDevice; private readonly DiscoveredSsdpDevice _DiscoveredDevice;
private readonly bool _IsNewlyDiscovered; private readonly bool _IsNewlyDiscovered;
#endregion #endregion
#region Constructors #region Constructors
/// <summary> /// <summary>
/// Full constructor. /// Full constructor.
/// </summary> /// </summary>
/// <param name="discoveredDevice">A <see cref="DiscoveredSsdpDevice"/> instance representing the available device.</param> /// <param name="discoveredDevice">A <see cref="DiscoveredSsdpDevice"/> instance representing the available device.</param>
/// <param name="isNewlyDiscovered">A boolean value indicating whether or not this device came from the cache. See <see cref="IsNewlyDiscovered"/> for more detail.</param> /// <param name="isNewlyDiscovered">A boolean value indicating whether or not this device came from the cache. See <see cref="IsNewlyDiscovered"/> for more detail.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="discoveredDevice"/> parameter is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="discoveredDevice"/> parameter is null.</exception>
public DeviceAvailableEventArgs(DiscoveredSsdpDevice discoveredDevice, bool isNewlyDiscovered) public DeviceAvailableEventArgs(DiscoveredSsdpDevice discoveredDevice, bool isNewlyDiscovered)
{ {
if (discoveredDevice == null) throw new ArgumentNullException("discoveredDevice"); if (discoveredDevice == null) throw new ArgumentNullException("discoveredDevice");
_DiscoveredDevice = discoveredDevice; _DiscoveredDevice = discoveredDevice;
_IsNewlyDiscovered = isNewlyDiscovered; _IsNewlyDiscovered = isNewlyDiscovered;
} }
#endregion #endregion
#region Public Properties #region Public Properties
/// <summary> /// <summary>
/// Returns true if the device was discovered due to an alive notification, or a search and was not already in the cache. Returns false if the item came from the cache but matched the current search request. /// Returns true if the device was discovered due to an alive notification, or a search and was not already in the cache. Returns false if the item came from the cache but matched the current search request.
/// </summary> /// </summary>
public bool IsNewlyDiscovered public bool IsNewlyDiscovered
{ {
get { return _IsNewlyDiscovered; } get { return _IsNewlyDiscovered; }
} }
/// <summary> /// <summary>
/// A reference to a <see cref="Rssdp.DiscoveredSsdpDevice"/> instance containing the discovered details and allowing access to the full device description. /// A reference to a <see cref="Rssdp.DiscoveredSsdpDevice"/> instance containing the discovered details and allowing access to the full device description.
/// </summary> /// </summary>
public DiscoveredSsdpDevice DiscoveredDevice public DiscoveredSsdpDevice DiscoveredDevice
{ {
get { return _DiscoveredDevice; } get { return _DiscoveredDevice; }
} }
#endregion #endregion
}
}
} }

@ -4,45 +4,45 @@ using System.Text;
namespace Rssdp namespace Rssdp
{ {
/// <summary> /// <summary>
/// Event arguments for the <see cref="SsdpDevice.DeviceAdded"/> and <see cref="SsdpDevice.DeviceRemoved"/> events. /// Event arguments for the <see cref="SsdpDevice.DeviceAdded"/> and <see cref="SsdpDevice.DeviceRemoved"/> events.
/// </summary> /// </summary>
public sealed class DeviceEventArgs : EventArgs public sealed class DeviceEventArgs : EventArgs
{ {
#region Fields #region Fields
private readonly SsdpDevice _Device; private readonly SsdpDevice _Device;
#endregion #endregion
#region Constructors #region Constructors
/// <summary> /// <summary>
/// Constructs a new instance for the specified <see cref="SsdpDevice"/>. /// Constructs a new instance for the specified <see cref="SsdpDevice"/>.
/// </summary> /// </summary>
/// <param name="device">The <see cref="SsdpDevice"/> associated with the event this argument class is being used for.</param> /// <param name="device">The <see cref="SsdpDevice"/> associated with the event this argument class is being used for.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception>
public DeviceEventArgs(SsdpDevice device) public DeviceEventArgs(SsdpDevice device)
{ {
if (device == null) throw new ArgumentNullException("device"); if (device == null) throw new ArgumentNullException("device");
_Device = device; _Device = device;
} }
#endregion #endregion
#region Public Properties #region Public Properties
/// <summary> /// <summary>
/// Returns the <see cref="SsdpDevice"/> instance the event being raised for. /// Returns the <see cref="SsdpDevice"/> instance the event being raised for.
/// </summary> /// </summary>
public SsdpDevice Device public SsdpDevice Device
{ {
get { return _Device; } get { return _Device; }
} }
#endregion #endregion
} }
} }

@ -5,55 +5,55 @@ using System.Threading.Tasks;
namespace Rssdp namespace Rssdp
{ {
/// <summary> /// <summary>
/// Event arguments for the <see cref="Rssdp.Infrastructure.SsdpDeviceLocatorBase.DeviceUnavailable"/> event. /// Event arguments for the <see cref="Rssdp.Infrastructure.SsdpDeviceLocatorBase.DeviceUnavailable"/> event.
/// </summary> /// </summary>
public sealed class DeviceUnavailableEventArgs : EventArgs public sealed class DeviceUnavailableEventArgs : EventArgs
{ {
#region Fields #region Fields
private readonly DiscoveredSsdpDevice _DiscoveredDevice; private readonly DiscoveredSsdpDevice _DiscoveredDevice;
private readonly bool _Expired; private readonly bool _Expired;
#endregion #endregion
#region Constructors #region Constructors
/// <summary> /// <summary>
/// Full constructor. /// Full constructor.
/// </summary> /// </summary>
/// <param name="discoveredDevice">A <see cref="DiscoveredSsdpDevice"/> instance representing the device that has become unavailable.</param> /// <param name="discoveredDevice">A <see cref="DiscoveredSsdpDevice"/> instance representing the device that has become unavailable.</param>
/// <param name="expired">A boolean value indicating whether this device is unavailable because it expired, or because it explicitly sent a byebye notification.. See <see cref="Expired"/> for more detail.</param> /// <param name="expired">A boolean value indicating whether this device is unavailable because it expired, or because it explicitly sent a byebye notification.. See <see cref="Expired"/> for more detail.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="discoveredDevice"/> parameter is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="discoveredDevice"/> parameter is null.</exception>
public DeviceUnavailableEventArgs(DiscoveredSsdpDevice discoveredDevice, bool expired) public DeviceUnavailableEventArgs(DiscoveredSsdpDevice discoveredDevice, bool expired)
{ {
if (discoveredDevice == null) throw new ArgumentNullException("discoveredDevice"); if (discoveredDevice == null) throw new ArgumentNullException("discoveredDevice");
_DiscoveredDevice = discoveredDevice; _DiscoveredDevice = discoveredDevice;
_Expired = expired; _Expired = expired;
} }
#endregion #endregion
#region Public Properties #region Public Properties
/// <summary> /// <summary>
/// Returns true if the device is considered unavailable because it's cached information expired before a new alive notification or search result was received. Returns false if the device is unavailable because it sent an explicit notification of it's unavailability. /// Returns true if the device is considered unavailable because it's cached information expired before a new alive notification or search result was received. Returns false if the device is unavailable because it sent an explicit notification of it's unavailability.
/// </summary> /// </summary>
public bool Expired public bool Expired
{ {
get { return _Expired; } get { return _Expired; }
} }
/// <summary> /// <summary>
/// A reference to a <see cref="Rssdp.DiscoveredSsdpDevice"/> instance containing the discovery details of the removed device. /// A reference to a <see cref="Rssdp.DiscoveredSsdpDevice"/> instance containing the discovery details of the removed device.
/// </summary> /// </summary>
public DiscoveredSsdpDevice DiscoveredDevice public DiscoveredSsdpDevice DiscoveredDevice
{ {
get { return _DiscoveredDevice; } get { return _DiscoveredDevice; }
} }
#endregion #endregion
} }
} }

@ -7,88 +7,88 @@ using System.Net.Http.Headers;
namespace Rssdp namespace Rssdp
{ {
/// <summary> /// <summary>
/// Represents a discovered device, containing basic information about the device and the location of it's full device description document. Also provides convenience methods for retrieving the device description document. /// Represents a discovered device, containing basic information about the device and the location of it's full device description document. Also provides convenience methods for retrieving the device description document.
/// </summary> /// </summary>
/// <seealso cref="SsdpDevice"/> /// <seealso cref="SsdpDevice"/>
/// <seealso cref="Rssdp.Infrastructure.ISsdpDeviceLocator"/> /// <seealso cref="Rssdp.Infrastructure.ISsdpDeviceLocator"/>
public sealed class DiscoveredSsdpDevice public sealed class DiscoveredSsdpDevice
{ {
#region Fields #region Fields
private DateTimeOffset _AsAt; private DateTimeOffset _AsAt;
#endregion #endregion
#region Public Properties #region Public Properties
/// <summary> /// <summary>
/// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice. /// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice.
/// </summary> /// </summary>
public string NotificationType { get; set; } public string NotificationType { get; set; }
/// <summary> /// <summary>
/// Sets or returns the universal service name (USN) of the device. /// Sets or returns the universal service name (USN) of the device.
/// </summary> /// </summary>
public string Usn { get; set; } public string Usn { get; set; }
/// <summary> /// <summary>
/// Sets or returns a URL pointing to the device description document for this device. /// Sets or returns a URL pointing to the device description document for this device.
/// </summary> /// </summary>
public Uri DescriptionLocation { get; set; } public Uri DescriptionLocation { get; set; }
/// <summary> /// <summary>
/// Sets or returns the length of time this information is valid for (from the <see cref="AsAt"/> time). /// Sets or returns the length of time this information is valid for (from the <see cref="AsAt"/> time).
/// </summary> /// </summary>
public TimeSpan CacheLifetime { get; set; } public TimeSpan CacheLifetime { get; set; }
/// <summary> /// <summary>
/// Sets or returns the date and time this information was received. /// Sets or returns the date and time this information was received.
/// </summary> /// </summary>
public DateTimeOffset AsAt public DateTimeOffset AsAt
{ {
get { return _AsAt; } get { return _AsAt; }
set set
{ {
if (_AsAt != value) if (_AsAt != value)
{ {
_AsAt = value; _AsAt = value;
} }
} }
} }
/// <summary> /// <summary>
/// Returns the headers from the SSDP device response message /// Returns the headers from the SSDP device response message
/// </summary> /// </summary>
public HttpHeaders ResponseHeaders { get; set; } public HttpHeaders ResponseHeaders { get; set; }
#endregion #endregion
#region Public Methods #region Public Methods
/// <summary> /// <summary>
/// Returns true if this device information has expired, based on the current date/time, and the <see cref="CacheLifetime"/> &amp; <see cref="AsAt"/> properties. /// Returns true if this device information has expired, based on the current date/time, and the <see cref="CacheLifetime"/> &amp; <see cref="AsAt"/> properties.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public bool IsExpired() public bool IsExpired()
{ {
return this.CacheLifetime == TimeSpan.Zero || this.AsAt.Add(this.CacheLifetime) <= DateTimeOffset.Now; return this.CacheLifetime == TimeSpan.Zero || this.AsAt.Add(this.CacheLifetime) <= DateTimeOffset.Now;
} }
#endregion #endregion
#region Overrides #region Overrides
/// <summary> /// <summary>
/// Returns the device's <see cref="Usn"/> value. /// Returns the device's <see cref="Usn"/> value.
/// </summary> /// </summary>
/// <returns>A string containing the device's universal service name.</returns> /// <returns>A string containing the device's universal service name.</returns>
public override string ToString() public override string ToString()
{ {
return this.Usn; return this.Usn;
} }
#endregion #endregion
} }
} }

@ -5,44 +5,44 @@ using System.Threading.Tasks;
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
/// <summary> /// <summary>
/// Correclty implements the <see cref="IDisposable"/> interface and pattern for an object containing only managed resources, and adds a few common niceities not on the interface such as an <see cref="IsDisposed"/> property. /// Correclty implements the <see cref="IDisposable"/> interface and pattern for an object containing only managed resources, and adds a few common niceities not on the interface such as an <see cref="IsDisposed"/> property.
/// </summary> /// </summary>
public abstract class DisposableManagedObjectBase : IDisposable public abstract class DisposableManagedObjectBase : IDisposable
{ {
#region Public Methods #region Public Methods
/// <summary> /// <summary>
/// Override this method and dispose any objects you own the lifetime of if disposing is true; /// Override this method and dispose any objects you own the lifetime of if disposing is true;
/// </summary> /// </summary>
/// <param name="disposing">True if managed objects should be disposed, if false, only unmanaged resources should be released.</param> /// <param name="disposing">True if managed objects should be disposed, if false, only unmanaged resources should be released.</param>
protected abstract void Dispose(bool disposing); protected abstract void Dispose(bool disposing);
/// <summary> /// <summary>
/// Throws and <see cref="System.ObjectDisposedException"/> if the <see cref="IsDisposed"/> property is true. /// Throws and <see cref="System.ObjectDisposedException"/> if the <see cref="IsDisposed"/> property is true.
/// </summary> /// </summary>
/// <seealso cref="IsDisposed"/> /// <seealso cref="IsDisposed"/>
/// <exception cref="System.ObjectDisposedException">Thrown if the <see cref="IsDisposed"/> property is true.</exception> /// <exception cref="System.ObjectDisposedException">Thrown if the <see cref="IsDisposed"/> property is true.</exception>
/// <seealso cref="Dispose()"/> /// <seealso cref="Dispose()"/>
protected virtual void ThrowIfDisposed() protected virtual void ThrowIfDisposed()
{ {
if (this.IsDisposed) throw new ObjectDisposedException(this.GetType().FullName); if (this.IsDisposed) throw new ObjectDisposedException(this.GetType().FullName);
} }
#endregion #endregion
#region Public Properties #region Public Properties
/// <summary> /// <summary>
/// Sets or returns a boolean indicating whether or not this instance has been disposed. /// Sets or returns a boolean indicating whether or not this instance has been disposed.
/// </summary> /// </summary>
/// <seealso cref="Dispose()"/> /// <seealso cref="Dispose()"/>
public bool IsDisposed public bool IsDisposed
{ {
get; get;
private set; private set;
} }
#endregion #endregion
@ -74,8 +74,8 @@ namespace Rssdp.Infrastructure
/// </remarks> /// </remarks>
/// <seealso cref="IsDisposed"/> /// <seealso cref="IsDisposed"/>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification="We do exactly as asked, but CA doesn't seem to like us also setting the IsDisposed property. Too bad, it's a good idea and shouldn't cause an exception or anything likely to interfer with the dispose process.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification="We do exactly as asked, but CA doesn't seem to like us also setting the IsDisposed property. Too bad, it's a good idea and shouldn't cause an exception or anything likely to interfer with the dispose process.")]
public void Dispose() public void Dispose()
{ {
IsDisposed = true; IsDisposed = true;
Dispose(true); Dispose(true);

@ -8,70 +8,70 @@ using System.Threading.Tasks;
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
/// <summary> /// <summary>
/// Parses a string into a <see cref="System.Net.Http.HttpRequestMessage"/> or throws an exception. /// Parses a string into a <see cref="System.Net.Http.HttpRequestMessage"/> or throws an exception.
/// </summary> /// </summary>
public sealed class HttpRequestParser : HttpParserBase<HttpRequestMessage> public sealed class HttpRequestParser : HttpParserBase<HttpRequestMessage>
{ {
#region Fields & Constants #region Fields & Constants
private readonly string[] ContentHeaderNames = new string[] private readonly string[] ContentHeaderNames = new string[]
{ {
"Allow", "Content-Disposition", "Content-Encoding", "Content-Language", "Content-Length", "Content-Location", "Content-MD5", "Content-Range", "Content-Type", "Expires", "Last-Modified" "Allow", "Content-Disposition", "Content-Encoding", "Content-Language", "Content-Length", "Content-Location", "Content-MD5", "Content-Range", "Content-Type", "Expires", "Last-Modified"
}; };
#endregion #endregion
#region Public Methods #region Public Methods
/// <summary> /// <summary>
/// Parses the specified data into a <see cref="System.Net.Http.HttpRequestMessage"/> instance. /// Parses the specified data into a <see cref="System.Net.Http.HttpRequestMessage"/> instance.
/// </summary> /// </summary>
/// <param name="data">A string containing the data to parse.</param> /// <param name="data">A string containing the data to parse.</param>
/// <returns>A <see cref="System.Net.Http.HttpRequestMessage"/> instance containing the parsed data.</returns> /// <returns>A <see cref="System.Net.Http.HttpRequestMessage"/> instance containing the parsed data.</returns>
public override System.Net.Http.HttpRequestMessage Parse(string data) public override System.Net.Http.HttpRequestMessage Parse(string data)
{ {
System.Net.Http.HttpRequestMessage retVal = null; System.Net.Http.HttpRequestMessage retVal = null;
try try
{ {
retVal = new System.Net.Http.HttpRequestMessage(); retVal = new System.Net.Http.HttpRequestMessage();
Parse(retVal, retVal.Headers, data); Parse(retVal, retVal.Headers, data);
return retVal; return retVal;
} }
finally finally
{ {
if (retVal != null) if (retVal != null)
retVal.Dispose(); retVal.Dispose();
} }
} }
#endregion #endregion
#region Overrides #region Overrides
/// <summary> /// <summary>
/// Used to parse the first line of an HTTP request or response and assign the values to the appropriate properties on the <paramref name="message"/>. /// Used to parse the first line of an HTTP request or response and assign the values to the appropriate properties on the <paramref name="message"/>.
/// </summary> /// </summary>
/// <param name="data">The first line of the HTTP message to be parsed.</param> /// <param name="data">The first line of the HTTP message to be parsed.</param>
/// <param name="message">Either a <see cref="System.Net.Http.HttpResponseMessage"/> or <see cref="System.Net.Http.HttpRequestMessage"/> to assign the parsed values to.</param> /// <param name="message">Either a <see cref="System.Net.Http.HttpResponseMessage"/> or <see cref="System.Net.Http.HttpRequestMessage"/> to assign the parsed values to.</param>
protected override void ParseStatusLine(string data, HttpRequestMessage message) protected override void ParseStatusLine(string data, HttpRequestMessage message)
{ {
if (data == null) throw new ArgumentNullException("data"); if (data == null) throw new ArgumentNullException("data");
if (message == null) throw new ArgumentNullException("message"); if (message == null) throw new ArgumentNullException("message");
var parts = data.Split(' '); var parts = data.Split(' ');
if (parts.Length < 2) throw new ArgumentException("Status line is invalid. Insufficient status parts.", "data"); if (parts.Length < 2) throw new ArgumentException("Status line is invalid. Insufficient status parts.", "data");
message.Method = new HttpMethod(parts[0].Trim()); message.Method = new HttpMethod(parts[0].Trim());
Uri requestUri; Uri requestUri;
if (Uri.TryCreate(parts[1].Trim(), UriKind.RelativeOrAbsolute, out requestUri)) if (Uri.TryCreate(parts[1].Trim(), UriKind.RelativeOrAbsolute, out requestUri))
message.RequestUri = requestUri; message.RequestUri = requestUri;
else else
System.Diagnostics.Debug.WriteLine(parts[1]); System.Diagnostics.Debug.WriteLine(parts[1]);
if (parts.Length >= 3) if (parts.Length >= 3)
{ {
@ -79,16 +79,16 @@ namespace Rssdp.Infrastructure
} }
} }
/// <summary> /// <summary>
/// Returns a boolean indicating whether the specified HTTP header name represents a content header (true), or a message header (false). /// Returns a boolean indicating whether the specified HTTP header name represents a content header (true), or a message header (false).
/// </summary> /// </summary>
/// <param name="headerName">A string containing the name of the header to return the type of.</param> /// <param name="headerName">A string containing the name of the header to return the type of.</param>
protected override bool IsContentHeader(string headerName) protected override bool IsContentHeader(string headerName)
{ {
return ContentHeaderNames.Contains(headerName, StringComparer.OrdinalIgnoreCase); return ContentHeaderNames.Contains(headerName, StringComparer.OrdinalIgnoreCase);
} }
#endregion #endregion
} }
} }

@ -8,82 +8,82 @@ using System.Threading.Tasks;
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
/// <summary> /// <summary>
/// Parses a string into a <see cref="System.Net.Http.HttpResponseMessage"/> or throws an exception. /// Parses a string into a <see cref="System.Net.Http.HttpResponseMessage"/> or throws an exception.
/// </summary> /// </summary>
public sealed class HttpResponseParser : HttpParserBase<System.Net.Http.HttpResponseMessage> public sealed class HttpResponseParser : HttpParserBase<System.Net.Http.HttpResponseMessage>
{ {
#region Fields & Constants #region Fields & Constants
private readonly string[] ContentHeaderNames = new string[] private readonly string[] ContentHeaderNames = new string[]
{ {
"Allow", "Content-Disposition", "Content-Encoding", "Content-Language", "Content-Length", "Content-Location", "Content-MD5", "Content-Range", "Content-Type", "Expires", "Last-Modified" "Allow", "Content-Disposition", "Content-Encoding", "Content-Language", "Content-Length", "Content-Location", "Content-MD5", "Content-Range", "Content-Type", "Expires", "Last-Modified"
}; };
#endregion #endregion
#region Public Methods #region Public Methods
/// <summary> /// <summary>
/// Parses the specified data into a <see cref="System.Net.Http.HttpResponseMessage"/> instance. /// Parses the specified data into a <see cref="System.Net.Http.HttpResponseMessage"/> instance.
/// </summary> /// </summary>
/// <param name="data">A string containing the data to parse.</param> /// <param name="data">A string containing the data to parse.</param>
/// <returns>A <see cref="System.Net.Http.HttpResponseMessage"/> instance containing the parsed data.</returns> /// <returns>A <see cref="System.Net.Http.HttpResponseMessage"/> instance containing the parsed data.</returns>
public override HttpResponseMessage Parse(string data) public override HttpResponseMessage Parse(string data)
{ {
System.Net.Http.HttpResponseMessage retVal = null; System.Net.Http.HttpResponseMessage retVal = null;
try try
{ {
retVal = new System.Net.Http.HttpResponseMessage(); retVal = new System.Net.Http.HttpResponseMessage();
Parse(retVal, retVal.Headers, data); Parse(retVal, retVal.Headers, data);
return retVal; return retVal;
} }
catch catch
{ {
if (retVal != null) if (retVal != null)
retVal.Dispose(); retVal.Dispose();
throw; throw;
} }
} }
#endregion #endregion
#region Overrides Methods #region Overrides Methods
/// <summary> /// <summary>
/// Returns a boolean indicating whether the specified HTTP header name represents a content header (true), or a message header (false). /// Returns a boolean indicating whether the specified HTTP header name represents a content header (true), or a message header (false).
/// </summary> /// </summary>
/// <param name="headerName">A string containing the name of the header to return the type of.</param> /// <param name="headerName">A string containing the name of the header to return the type of.</param>
/// <returns>A boolean, true if th specified header relates to HTTP content, otherwise false.</returns> /// <returns>A boolean, true if th specified header relates to HTTP content, otherwise false.</returns>
protected override bool IsContentHeader(string headerName) protected override bool IsContentHeader(string headerName)
{ {
return ContentHeaderNames.Contains(headerName, StringComparer.OrdinalIgnoreCase); return ContentHeaderNames.Contains(headerName, StringComparer.OrdinalIgnoreCase);
} }
/// <summary> /// <summary>
/// Used to parse the first line of an HTTP request or response and assign the values to the appropriate properties on the <paramref name="message"/>. /// Used to parse the first line of an HTTP request or response and assign the values to the appropriate properties on the <paramref name="message"/>.
/// </summary> /// </summary>
/// <param name="data">The first line of the HTTP message to be parsed.</param> /// <param name="data">The first line of the HTTP message to be parsed.</param>
/// <param name="message">Either a <see cref="System.Net.Http.HttpResponseMessage"/> or <see cref="System.Net.Http.HttpRequestMessage"/> to assign the parsed values to.</param> /// <param name="message">Either a <see cref="System.Net.Http.HttpResponseMessage"/> or <see cref="System.Net.Http.HttpRequestMessage"/> to assign the parsed values to.</param>
protected override void ParseStatusLine(string data, HttpResponseMessage message) protected override void ParseStatusLine(string data, HttpResponseMessage message)
{ {
if (data == null) throw new ArgumentNullException("data"); if (data == null) throw new ArgumentNullException("data");
if (message == null) throw new ArgumentNullException("message"); if (message == null) throw new ArgumentNullException("message");
var parts = data.Split(' '); var parts = data.Split(' ');
if (parts.Length < 2) throw new ArgumentException("data status line is invalid. Insufficient status parts.", "data"); if (parts.Length < 2) throw new ArgumentException("data status line is invalid. Insufficient status parts.", "data");
message.Version = ParseHttpVersion(parts[0].Trim()); message.Version = ParseHttpVersion(parts[0].Trim());
int statusCode = -1; int statusCode = -1;
if (!Int32.TryParse(parts[1].Trim(), out statusCode)) if (!Int32.TryParse(parts[1].Trim(), out statusCode))
throw new ArgumentException("data status line is invalid. Status code is not a valid integer.", "data"); throw new ArgumentException("data status line is invalid. Status code is not a valid integer.", "data");
message.StatusCode = (HttpStatusCode)statusCode; message.StatusCode = (HttpStatusCode)statusCode;
if (parts.Length >= 3) if (parts.Length >= 3)
{ {
@ -91,7 +91,6 @@ namespace Rssdp.Infrastructure
} }
} }
#endregion #endregion
}
}
} }

@ -5,24 +5,24 @@ using System.Text;
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
internal static class IEnumerableExtensions internal static class IEnumerableExtensions
{ {
public static IEnumerable<T> SelectManyRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector) public static IEnumerable<T> SelectManyRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
{ {
if (source == null) throw new ArgumentNullException("source"); if (source == null) throw new ArgumentNullException("source");
if (selector == null) throw new ArgumentNullException("selector"); if (selector == null) throw new ArgumentNullException("selector");
return !source.Any() ? source : return !source.Any() ? source :
source.Concat( source.Concat(
source source
.SelectMany(i => selector(i).EmptyIfNull()) .SelectMany(i => selector(i).EmptyIfNull())
.SelectManyRecursive(selector) .SelectManyRecursive(selector)
); );
} }
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> source) public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> source)
{ {
return source ?? Enumerable.Empty<T>(); return source ?? Enumerable.Empty<T>();
} }
} }
} }

@ -2,140 +2,140 @@
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
/// <summary> /// <summary>
/// Interface for components that discover the existence of SSDP devices. /// Interface for components that discover the existence of SSDP devices.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>Discovering devices includes explicit search requests as well as listening for broadcast status notifications.</para> /// <para>Discovering devices includes explicit search requests as well as listening for broadcast status notifications.</para>
/// </remarks> /// </remarks>
/// <seealso cref="DiscoveredSsdpDevice"/> /// <seealso cref="DiscoveredSsdpDevice"/>
/// <seealso cref="SsdpDevice"/> /// <seealso cref="SsdpDevice"/>
/// <seealso cref="ISsdpDevicePublisher"/> /// <seealso cref="ISsdpDevicePublisher"/>
public interface ISsdpDeviceLocator public interface ISsdpDeviceLocator
{ {
#region Events #region Events
/// <summary> /// <summary>
/// Event raised when a device becomes available or is found by a search request. /// Event raised when a device becomes available or is found by a search request.
/// </summary> /// </summary>
/// <seealso cref="NotificationFilter"/> /// <seealso cref="NotificationFilter"/>
/// <seealso cref="DeviceUnavailable"/> /// <seealso cref="DeviceUnavailable"/>
/// <seealso cref="StartListeningForNotifications"/> /// <seealso cref="StartListeningForNotifications"/>
/// <seealso cref="StopListeningForNotifications"/> /// <seealso cref="StopListeningForNotifications"/>
event EventHandler<DeviceAvailableEventArgs> DeviceAvailable; event EventHandler<DeviceAvailableEventArgs> DeviceAvailable;
/// <summary> /// <summary>
/// Event raised when a device explicitly notifies of shutdown or a device expires from the cache. /// Event raised when a device explicitly notifies of shutdown or a device expires from the cache.
/// </summary> /// </summary>
/// <seeseealso cref="NotificationFilter"/> /// <seeseealso cref="NotificationFilter"/>
/// <seealso cref="DeviceAvailable"/> /// <seealso cref="DeviceAvailable"/>
/// <seealso cref="StartListeningForNotifications"/> /// <seealso cref="StartListeningForNotifications"/>
/// <seealso cref="StopListeningForNotifications"/> /// <seealso cref="StopListeningForNotifications"/>
event EventHandler<DeviceUnavailableEventArgs> DeviceUnavailable; event EventHandler<DeviceUnavailableEventArgs> DeviceUnavailable;
#endregion #endregion
#region Properties #region Properties
/// <summary> /// <summary>
/// Sets or returns a string containing the filter for notifications. Notifications not matching the filter will not raise the <see cref="DeviceAvailable"/> or <see cref="DeviceUnavailable"/> events. /// Sets or returns a string containing the filter for notifications. Notifications not matching the filter will not raise the <see cref="DeviceAvailable"/> or <see cref="DeviceUnavailable"/> events.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>Device alive/byebye notifications whose NT header does not match this filter value will still be captured and cached internally, but will not raise events about device availability. Usually used with either a device type of uuid NT header value.</para> /// <para>Device alive/byebye notifications whose NT header does not match this filter value will still be captured and cached internally, but will not raise events about device availability. Usually used with either a device type of uuid NT header value.</para>
/// <para>Example filters follow;</para> /// <para>Example filters follow;</para>
/// <example>upnp:rootdevice</example> /// <example>upnp:rootdevice</example>
/// <example>urn:schemas-upnp-org:device:WANDevice:1</example> /// <example>urn:schemas-upnp-org:device:WANDevice:1</example>
/// <example>"uuid:9F15356CC-95FA-572E-0E99-85B456BD3012"</example> /// <example>"uuid:9F15356CC-95FA-572E-0E99-85B456BD3012"</example>
/// </remarks> /// </remarks>
/// <seealso cref="DeviceAvailable"/> /// <seealso cref="DeviceAvailable"/>
/// <seealso cref="DeviceUnavailable"/> /// <seealso cref="DeviceUnavailable"/>
/// <seealso cref="StartListeningForNotifications"/> /// <seealso cref="StartListeningForNotifications"/>
/// <seealso cref="StopListeningForNotifications"/> /// <seealso cref="StopListeningForNotifications"/>
string NotificationFilter string NotificationFilter
{ {
get; get;
set; set;
} }
#endregion #endregion
#region Methods #region Methods
#region SearchAsync Overloads #region SearchAsync Overloads
/// <summary> /// <summary>
/// Aynchronously performs a search for all devices using the default search timeout, and returns an awaitable task that can be used to retrieve the results. /// Aynchronously performs a search for all devices using the default search timeout, and returns an awaitable task that can be used to retrieve the results.
/// </summary> /// </summary>
/// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns> /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(); System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync();
/// <summary> /// <summary>
/// Performs a search for the specified search target (criteria) and default search timeout. /// Performs a search for the specified search target (criteria) and default search timeout.
/// </summary> /// </summary>
/// <param name="searchTarget">The criteria for the search. Value can be; /// <param name="searchTarget">The criteria for the search. Value can be;
/// <list type="table"> /// <list type="table">
/// <item><term>Root devices</term><description>upnp:rootdevice</description></item> /// <item><term>Root devices</term><description>upnp:rootdevice</description></item>
/// <item><term>Specific device by UUID</term><description>uuid:&lt;device uuid&gt;</description></item> /// <item><term>Specific device by UUID</term><description>uuid:&lt;device uuid&gt;</description></item>
/// <item><term>Device type</term><description>Fully qualified device type starting with urn: i.e urn:schemas-upnp-org:Basic:1</description></item> /// <item><term>Device type</term><description>Fully qualified device type starting with urn: i.e urn:schemas-upnp-org:Basic:1</description></item>
/// </list> /// </list>
/// </param> /// </param>
/// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns> /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(string searchTarget); System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(string searchTarget);
/// <summary> /// <summary>
/// Performs a search for the specified search target (criteria) and search timeout. /// Performs a search for the specified search target (criteria) and search timeout.
/// </summary> /// </summary>
/// <param name="searchTarget">The criteria for the search. Value can be; /// <param name="searchTarget">The criteria for the search. Value can be;
/// <list type="table"> /// <list type="table">
/// <item><term>Root devices</term><description>upnp:rootdevice</description></item> /// <item><term>Root devices</term><description>upnp:rootdevice</description></item>
/// <item><term>Specific device by UUID</term><description>uuid:&lt;device uuid&gt;</description></item> /// <item><term>Specific device by UUID</term><description>uuid:&lt;device uuid&gt;</description></item>
/// <item><term>Device type</term><description>A device namespace and type in format of urn:&lt;device namespace&gt;:device:&lt;device type&gt;:&lt;device version&gt; i.e urn:schemas-upnp-org:device:Basic:1</description></item> /// <item><term>Device type</term><description>A device namespace and type in format of urn:&lt;device namespace&gt;:device:&lt;device type&gt;:&lt;device version&gt; i.e urn:schemas-upnp-org:device:Basic:1</description></item>
/// <item><term>Service type</term><description>A service namespace and type in format of urn:&lt;service namespace&gt;:service:&lt;servicetype&gt;:&lt;service version&gt; i.e urn:my-namespace:service:MyCustomService:1</description></item> /// <item><term>Service type</term><description>A service namespace and type in format of urn:&lt;service namespace&gt;:service:&lt;servicetype&gt;:&lt;service version&gt; i.e urn:my-namespace:service:MyCustomService:1</description></item>
/// </list> /// </list>
/// </param> /// </param>
/// <param name="searchWaitTime">The amount of time to wait for network responses to the search request. Longer values will likely return more devices, but increase search time. A value between 1 and 5 is recommended by the UPnP 1.1 specification. Specify TimeSpan.Zero to return only devices already in the cache.</param> /// <param name="searchWaitTime">The amount of time to wait for network responses to the search request. Longer values will likely return more devices, but increase search time. A value between 1 and 5 is recommended by the UPnP 1.1 specification. Specify TimeSpan.Zero to return only devices already in the cache.</param>
/// <remarks> /// <remarks>
/// <para>By design RSSDP does not support 'publishing services' as it is intended for use with non-standard UPnP devices that don't publish UPnP style services. However, it is still possible to use RSSDP to search for devices implemetning these services if you know the service type.</para> /// <para>By design RSSDP does not support 'publishing services' as it is intended for use with non-standard UPnP devices that don't publish UPnP style services. However, it is still possible to use RSSDP to search for devices implemetning these services if you know the service type.</para>
/// </remarks> /// </remarks>
/// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns> /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(string searchTarget, TimeSpan searchWaitTime); System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(string searchTarget, TimeSpan searchWaitTime);
/// <summary> /// <summary>
/// Performs a search for all devices using the specified search timeout. /// Performs a search for all devices using the specified search timeout.
/// </summary> /// </summary>
/// <param name="searchWaitTime">The amount of time to wait for network responses to the search request. Longer values will likely return more devices, but increase search time. A value between 1 and 5 is recommended by the UPnP 1.1 specification. Specify TimeSpan.Zero to return only devices already in the cache.</param> /// <param name="searchWaitTime">The amount of time to wait for network responses to the search request. Longer values will likely return more devices, but increase search time. A value between 1 and 5 is recommended by the UPnP 1.1 specification. Specify TimeSpan.Zero to return only devices already in the cache.</param>
/// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns> /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(TimeSpan searchWaitTime); System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(TimeSpan searchWaitTime);
#endregion #endregion
/// <summary> /// <summary>
/// Starts listening for broadcast notifications of service availability. /// Starts listening for broadcast notifications of service availability.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>When called the system will listen for 'alive' and 'byebye' notifications. This can speed up searching, as well as provide dynamic notification of new devices appearing on the network, and previously discovered devices disappearing.</para> /// <para>When called the system will listen for 'alive' and 'byebye' notifications. This can speed up searching, as well as provide dynamic notification of new devices appearing on the network, and previously discovered devices disappearing.</para>
/// </remarks> /// </remarks>
/// <seealso cref="StopListeningForNotifications"/> /// <seealso cref="StopListeningForNotifications"/>
/// <seealso cref="DeviceAvailable"/> /// <seealso cref="DeviceAvailable"/>
/// <seealso cref="DeviceUnavailable"/> /// <seealso cref="DeviceUnavailable"/>
/// <seealso cref="NotificationFilter"/> /// <seealso cref="NotificationFilter"/>
void StartListeningForNotifications(); void StartListeningForNotifications();
/// <summary> /// <summary>
/// Stops listening for broadcast notifications of service availability. /// Stops listening for broadcast notifications of service availability.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>Does nothing if this instance is not already listening for notifications.</para> /// <para>Does nothing if this instance is not already listening for notifications.</para>
/// </remarks> /// </remarks>
/// <exception cref="System.ObjectDisposedException">Throw if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true.</exception> /// <exception cref="System.ObjectDisposedException">Throw if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true.</exception>
/// <seealso cref="StartListeningForNotifications"/> /// <seealso cref="StartListeningForNotifications"/>
/// <seealso cref="DeviceAvailable"/> /// <seealso cref="DeviceAvailable"/>
/// <seealso cref="DeviceUnavailable"/> /// <seealso cref="DeviceUnavailable"/>
/// <seealso cref="NotificationFilter"/> /// <seealso cref="NotificationFilter"/>
void StopListeningForNotifications(); void StopListeningForNotifications();
#endregion #endregion
} }
} }

@ -3,35 +3,34 @@ using System.Threading.Tasks;
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
/// <summary> /// <summary>
/// Interface for components that publish the existence of SSDP devices. /// Interface for components that publish the existence of SSDP devices.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>Publishing a device includes sending notifications (alive and byebye) as well as responding to search requests when appropriate.</para> /// <para>Publishing a device includes sending notifications (alive and byebye) as well as responding to search requests when appropriate.</para>
/// </remarks> /// </remarks>
/// <seealso cref="SsdpRootDevice"/> /// <seealso cref="SsdpRootDevice"/>
/// <seealso cref="ISsdpDeviceLocator"/> /// <seealso cref="ISsdpDeviceLocator"/>
public interface ISsdpDevicePublisher public interface ISsdpDevicePublisher
{ {
/// <summary> /// <summary>
/// Adds a device (and it's children) to the list of devices being published by this server, making them discoverable to SSDP clients. /// Adds a device (and it's children) to the list of devices being published by this server, making them discoverable to SSDP clients.
/// </summary> /// </summary>
/// <param name="device">The <see cref="SsdpRootDevice"/> instance to add.</param> /// <param name="device">The <see cref="SsdpRootDevice"/> instance to add.</param>
/// <returns>An awaitable <see cref="System.Threading.Tasks.Task"/>.</returns> /// <returns>An awaitable <see cref="System.Threading.Tasks.Task"/>.</returns>
void AddDevice(SsdpRootDevice device); void AddDevice(SsdpRootDevice device);
/// <summary> /// <summary>
/// Removes a device (and it's children) from the list of devices being published by this server, making them undiscoverable. /// Removes a device (and it's children) from the list of devices being published by this server, making them undiscoverable.
/// </summary> /// </summary>
/// <param name="device">The <see cref="SsdpRootDevice"/> instance to add.</param> /// <param name="device">The <see cref="SsdpRootDevice"/> instance to add.</param>
/// <returns>An awaitable <see cref="System.Threading.Tasks.Task"/>.</returns> /// <returns>An awaitable <see cref="System.Threading.Tasks.Task"/>.</returns>
Task RemoveDevice(SsdpRootDevice device); Task RemoveDevice(SsdpRootDevice device);
/// <summary> /// <summary>
/// Returns a read only list of devices being published by this instance. /// Returns a read only list of devices being published by this instance.
/// </summary> /// </summary>
/// <seealso cref="SsdpDevice"/> /// <seealso cref="SsdpDevice"/>
System.Collections.Generic.IEnumerable<SsdpRootDevice> Devices { get; } System.Collections.Generic.IEnumerable<SsdpRootDevice> Devices { get; }
}
}
} }

@ -8,54 +8,52 @@ using MediaBrowser.Model.Net;
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
/// <summary> /// <summary>
/// Provides arguments for the <see cref="ISsdpCommunicationsServer.RequestReceived"/> event. /// Provides arguments for the <see cref="ISsdpCommunicationsServer.RequestReceived"/> event.
/// </summary> /// </summary>
public sealed class RequestReceivedEventArgs : EventArgs public sealed class RequestReceivedEventArgs : EventArgs
{ {
#region Fields
#region Fields
private readonly HttpRequestMessage _Message;
private readonly HttpRequestMessage _Message; private readonly IpEndPointInfo _ReceivedFrom;
private readonly IpEndPointInfo _ReceivedFrom;
#endregion
#endregion
public IpAddressInfo LocalIpAddress { get; private set; }
public IpAddressInfo LocalIpAddress { get; private set; }
#region Constructors
#region Constructors
/// <summary>
/// <summary> /// Full constructor.
/// Full constructor. /// </summary>
/// </summary> public RequestReceivedEventArgs(HttpRequestMessage message, IpEndPointInfo receivedFrom, IpAddressInfo localIpAddress)
public RequestReceivedEventArgs(HttpRequestMessage message, IpEndPointInfo receivedFrom, IpAddressInfo localIpAddress) {
{ _Message = message;
_Message = message; _ReceivedFrom = receivedFrom;
_ReceivedFrom = receivedFrom; LocalIpAddress = localIpAddress;
LocalIpAddress = localIpAddress; }
}
#endregion
#endregion
#region Public Properties
#region Public Properties
/// <summary>
/// <summary> /// The <see cref="HttpRequestMessage"/> that was received.
/// The <see cref="HttpRequestMessage"/> that was received. /// </summary>
/// </summary> public HttpRequestMessage Message
public HttpRequestMessage Message {
{ get { return _Message; }
get { return _Message; } }
}
/// <summary>
/// <summary> /// The <see cref="UdpEndPoint"/> the request came from.
/// The <see cref="UdpEndPoint"/> the request came from. /// </summary>
/// </summary> public IpEndPointInfo ReceivedFrom
public IpEndPointInfo ReceivedFrom {
{ get { return _ReceivedFrom; }
get { return _ReceivedFrom; } }
}
#endregion
#endregion }
}
} }

@ -8,53 +8,52 @@ using MediaBrowser.Model.Net;
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
/// <summary> /// <summary>
/// Provides arguments for the <see cref="ISsdpCommunicationsServer.ResponseReceived"/> event. /// Provides arguments for the <see cref="ISsdpCommunicationsServer.ResponseReceived"/> event.
/// </summary> /// </summary>
public sealed class ResponseReceivedEventArgs : EventArgs public sealed class ResponseReceivedEventArgs : EventArgs
{ {
public IpAddressInfo LocalIpAddress { get; set; } public IpAddressInfo LocalIpAddress { get; set; }
#region Fields #region Fields
private readonly HttpResponseMessage _Message; private readonly HttpResponseMessage _Message;
private readonly IpEndPointInfo _ReceivedFrom; private readonly IpEndPointInfo _ReceivedFrom;
#endregion #endregion
#region Constructors #region Constructors
/// <summary> /// <summary>
/// Full constructor. /// Full constructor.
/// </summary> /// </summary>
public ResponseReceivedEventArgs(HttpResponseMessage message, IpEndPointInfo receivedFrom) public ResponseReceivedEventArgs(HttpResponseMessage message, IpEndPointInfo receivedFrom)
{ {
_Message = message; _Message = message;
_ReceivedFrom = receivedFrom; _ReceivedFrom = receivedFrom;
} }
#endregion #endregion
#region Public Properties #region Public Properties
/// <summary> /// <summary>
/// The <see cref="HttpResponseMessage"/> that was received. /// The <see cref="HttpResponseMessage"/> that was received.
/// </summary> /// </summary>
public HttpResponseMessage Message public HttpResponseMessage Message
{ {
get { return _Message; } get { return _Message; }
} }
/// <summary> /// <summary>
/// The <see cref="UdpEndPoint"/> the response came from. /// The <see cref="UdpEndPoint"/> the response came from.
/// </summary> /// </summary>
public IpEndPointInfo ReceivedFrom public IpEndPointInfo ReceivedFrom
{ {
get { return _ReceivedFrom; } get { return _ReceivedFrom; }
} }
#endregion #endregion
}
}
} }

@ -20,22 +20,19 @@ namespace Rssdp.Infrastructure
#region Fields #region Fields
/* /* We could technically use one socket listening on port 1900 for everything.
* This should get both multicast (notifications) and unicast (search response) messages, however
We could technically use one socket listening on port 1900 for everything. * this often doesn't work under Windows because the MS SSDP service is running. If that service
This should get both multicast (notifications) and unicast (search response) messages, however * is running then it will steal the unicast messages and we will never see search responses.
this often doesn't work under Windows because the MS SSDP service is running. If that service * Since stopping the service would be a bad idea (might not be allowed security wise and might
is running then it will steal the unicast messages and we will never see search responses. * break other apps running on the system) the only other work around is to use two sockets.
Since stopping the service would be a bad idea (might not be allowed security wise and might *
break other apps running on the system) the only other work around is to use two sockets. * We use one socket to listen for/receive notifications and search requests (_BroadcastListenSocket).
* We use a second socket, bound to a different local port, to send search requests and listen for
We use one socket to listen for/receive notifications and search requests (_BroadcastListenSocket). * responses (_SendSocket). The responses are sent to the local port this socket is bound to,
We use a second socket, bound to a different local port, to send search requests and listen for * which isn't port 1900 so the MS service doesn't steal them. While the caller can specify a local
responses (_SendSocket). The responses are sent to the local port this socket is bound to, * port to use, we will default to 0 which allows the underlying system to auto-assign a free port.
which isn't port 1900 so the MS service doesn't steal them. While the caller can specify a local */
port to use, we will default to 0 which allows the underlying system to auto-assign a free port.
*/
private object _BroadcastListenSocketSynchroniser = new object(); private object _BroadcastListenSocketSynchroniser = new object();
private ISocket _BroadcastListenSocket; private ISocket _BroadcastListenSocket;

@ -5,63 +5,63 @@ using System.Threading.Tasks;
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
/// <summary> /// <summary>
/// Provides constants for common values related to the SSDP protocols. /// Provides constants for common values related to the SSDP protocols.
/// </summary> /// </summary>
public static class SsdpConstants public static class SsdpConstants
{ {
/// <summary> /// <summary>
/// Multicast IP Address used for SSDP multicast messages. Values is 239.255.255.250. /// Multicast IP Address used for SSDP multicast messages. Values is 239.255.255.250.
/// </summary> /// </summary>
public const string MulticastLocalAdminAddress = "239.255.255.250"; public const string MulticastLocalAdminAddress = "239.255.255.250";
/// <summary> /// <summary>
/// The UDP port used for SSDP multicast messages. Values is 1900. /// The UDP port used for SSDP multicast messages. Values is 1900.
/// </summary> /// </summary>
public const int MulticastPort = 1900; public const int MulticastPort = 1900;
/// <summary> /// <summary>
/// The default multicase TTL for SSDP multicast messages. Value is 4. /// The default multicase TTL for SSDP multicast messages. Value is 4.
/// </summary> /// </summary>
public const int SsdpDefaultMulticastTimeToLive = 4; public const int SsdpDefaultMulticastTimeToLive = 4;
internal const string MSearchMethod = "M-SEARCH"; internal const string MSearchMethod = "M-SEARCH";
internal const string SsdpDiscoverMessage = "ssdp:discover"; internal const string SsdpDiscoverMessage = "ssdp:discover";
internal const string SsdpDiscoverAllSTHeader = "ssdp:all"; internal const string SsdpDiscoverAllSTHeader = "ssdp:all";
internal const string SsdpDeviceDescriptionXmlNamespace = "urn:schemas-upnp-org:device-1-0"; internal const string SsdpDeviceDescriptionXmlNamespace = "urn:schemas-upnp-org:device-1-0";
/// <summary> /// <summary>
/// Default buffer size for receiving SSDP broadcasts. Value is 8192 (bytes). /// Default buffer size for receiving SSDP broadcasts. Value is 8192 (bytes).
/// </summary> /// </summary>
public const int DefaultUdpSocketBufferSize = 8192; public const int DefaultUdpSocketBufferSize = 8192;
/// <summary> /// <summary>
/// The maximum possible buffer size for a UDP message. Value is 65507 (bytes). /// The maximum possible buffer size for a UDP message. Value is 65507 (bytes).
/// </summary> /// </summary>
public const int MaxUdpSocketBufferSize = 65507; // Max possible UDP packet size on IPv4 without using 'jumbograms'. public const int MaxUdpSocketBufferSize = 65507; // Max possible UDP packet size on IPv4 without using 'jumbograms'.
/// <summary> /// <summary>
/// Namespace/prefix for UPnP device types. Values is schemas-upnp-org. /// Namespace/prefix for UPnP device types. Values is schemas-upnp-org.
/// </summary> /// </summary>
public const string UpnpDeviceTypeNamespace = "schemas-upnp-org"; public const string UpnpDeviceTypeNamespace = "schemas-upnp-org";
/// <summary> /// <summary>
/// UPnP Root Device type. Value is upnp:rootdevice. /// UPnP Root Device type. Value is upnp:rootdevice.
/// </summary> /// </summary>
public const string UpnpDeviceTypeRootDevice = "upnp:rootdevice"; public const string UpnpDeviceTypeRootDevice = "upnp:rootdevice";
/// <summary> /// <summary>
/// The value is used by Windows Explorer for device searches instead of the UPNPDeviceTypeRootDevice constant. /// The value is used by Windows Explorer for device searches instead of the UPNPDeviceTypeRootDevice constant.
/// Not sure why (different spec, bug, alternate protocol etc). Used to enable Windows Explorer support. /// Not sure why (different spec, bug, alternate protocol etc). Used to enable Windows Explorer support.
/// </summary> /// </summary>
public const string PnpDeviceTypeRootDevice = "pnp:rootdevice"; public const string PnpDeviceTypeRootDevice = "pnp:rootdevice";
/// <summary> /// <summary>
/// UPnP Basic Device type. Value is Basic. /// UPnP Basic Device type. Value is Basic.
/// </summary> /// </summary>
public const string UpnpDeviceTypeBasicDevice = "Basic"; public const string UpnpDeviceTypeBasicDevice = "Basic";
internal const string SsdpKeepAliveNotification = "ssdp:alive"; internal const string SsdpKeepAliveNotification = "ssdp:alive";
internal const string SsdpByeByeNotification = "ssdp:byebye"; internal const string SsdpByeByeNotification = "ssdp:byebye";
internal const int UdpResendCount = 3; internal const int UdpResendCount = 3;
} }
} }

@ -538,9 +538,9 @@ namespace Rssdp.Infrastructure
//According to SSDP/UPnP spec, ignore message if missing these headers. //According to SSDP/UPnP spec, ignore message if missing these headers.
// Edit: But some devices do it anyway // Edit: But some devices do it anyway
//if (!e.Message.Headers.Contains("MX")) //if (!e.Message.Headers.Contains("MX"))
// WriteTrace("Ignoring search request - missing MX header."); // WriteTrace("Ignoring search request - missing MX header.");
//else if (!e.Message.Headers.Contains("MAN")) //else if (!e.Message.Headers.Contains("MAN"))
// WriteTrace("Ignoring search request - missing MAN header."); // WriteTrace("Ignoring search request - missing MAN header.");
//else //else
ProcessSearchRequest(GetFirstHeaderValue(e.Message.Headers, "MX"), GetFirstHeaderValue(e.Message.Headers, "ST"), e.ReceivedFrom, e.LocalIpAddress, CancellationToken.None); ProcessSearchRequest(GetFirstHeaderValue(e.Message.Headers, "MX"), GetFirstHeaderValue(e.Message.Headers, "ST"), e.ReceivedFrom, e.LocalIpAddress, CancellationToken.None);
} }

@ -4,54 +4,52 @@ using System.Text;
namespace Rssdp namespace Rssdp
{ {
/// <summary> /// <summary>
/// Represents a device that is a descendant of a <see cref="SsdpRootDevice"/> instance. /// Represents a device that is a descendant of a <see cref="SsdpRootDevice"/> instance.
/// </summary> /// </summary>
public class SsdpEmbeddedDevice : SsdpDevice public class SsdpEmbeddedDevice : SsdpDevice
{ {
#region Fields #region Fields
private SsdpRootDevice _RootDevice;
private SsdpRootDevice _RootDevice;
#endregion
#endregion
#region Constructors
#region Constructors
/// <summary>
/// <summary> /// Default constructor.
/// Default constructor. /// </summary>
/// </summary> public SsdpEmbeddedDevice()
public SsdpEmbeddedDevice() {
{ }
}
#endregion
#endregion
#region Public Properties
#region Public Properties
/// <summary>
/// <summary> /// Returns the <see cref="SsdpRootDevice"/> that is this device's first ancestor. If this device is itself an <see cref="SsdpRootDevice"/>, then returns a reference to itself.
/// Returns the <see cref="SsdpRootDevice"/> that is this device's first ancestor. If this device is itself an <see cref="SsdpRootDevice"/>, then returns a reference to itself. /// </summary>
/// </summary> public SsdpRootDevice RootDevice
public SsdpRootDevice RootDevice {
{ get
get {
{ return _RootDevice;
return _RootDevice; }
} internal set
internal set {
{ _RootDevice = value;
_RootDevice = value; lock (this.Devices)
lock (this.Devices) {
{ foreach (var embeddedDevice in this.Devices)
foreach (var embeddedDevice in this.Devices) {
{ ((SsdpEmbeddedDevice)embeddedDevice).RootDevice = _RootDevice;
((SsdpEmbeddedDevice)embeddedDevice).RootDevice = _RootDevice; }
} }
} }
} }
}
#endregion
#endregion }
}
} }

@ -6,74 +6,72 @@ using Rssdp.Infrastructure;
namespace Rssdp namespace Rssdp
{ {
/// <summary> /// <summary>
/// Represents a 'root' device, a device that has no parent. Used for publishing devices and for the root device in a tree of discovered devices. /// Represents a 'root' device, a device that has no parent. Used for publishing devices and for the root device in a tree of discovered devices.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>Child (embedded) devices are represented by the <see cref="SsdpDevice"/> in the <see cref="SsdpDevice.Devices"/> property.</para> /// <para>Child (embedded) devices are represented by the <see cref="SsdpDevice"/> in the <see cref="SsdpDevice.Devices"/> property.</para>
/// <para>Root devices contain some information that applies to the whole device tree and is therefore not present on child devices, such as <see cref="CacheLifetime"/> and <see cref="Location"/>.</para> /// <para>Root devices contain some information that applies to the whole device tree and is therefore not present on child devices, such as <see cref="CacheLifetime"/> and <see cref="Location"/>.</para>
/// </remarks> /// </remarks>
public class SsdpRootDevice : SsdpDevice public class SsdpRootDevice : SsdpDevice
{ {
#region Fields
#region Fields private Uri _UrlBase;
private Uri _UrlBase; #endregion
#endregion #region Constructors
#region Constructors /// <summary>
/// Default constructor.
/// </summary>
public SsdpRootDevice() : base()
{
}
/// <summary> #endregion
/// Default constructor.
/// </summary>
public SsdpRootDevice() : base()
{
}
#endregion #region Public Properties
#region Public Properties /// <summary>
/// Specifies how long clients can cache this device's details for. Optional but defaults to <see cref="TimeSpan.Zero"/> which means no-caching. Recommended value is half an hour.
/// </summary>
/// <remarks>
/// <para>Specifiy <see cref="TimeSpan.Zero"/> to indicate no caching allowed.</para>
/// <para>Also used to specify how often to rebroadcast alive notifications.</para>
/// <para>The UPnP/SSDP specifications indicate this should not be less than 1800 seconds (half an hour), but this is not enforced by this library.</para>
/// </remarks>
public TimeSpan CacheLifetime
{
get; set;
}
/// <summary> /// <summary>
/// Specifies how long clients can cache this device's details for. Optional but defaults to <see cref="TimeSpan.Zero"/> which means no-caching. Recommended value is half an hour. /// Gets or sets the URL used to retrieve the description document for this device/tree. Required.
/// </summary> /// </summary>
/// <remarks> public Uri Location { get; set; }
/// <para>Specifiy <see cref="TimeSpan.Zero"/> to indicate no caching allowed.</para>
/// <para>Also used to specify how often to rebroadcast alive notifications.</para>
/// <para>The UPnP/SSDP specifications indicate this should not be less than 1800 seconds (half an hour), but this is not enforced by this library.</para>
/// </remarks>
public TimeSpan CacheLifetime
{
get; set;
}
/// <summary>
/// Gets or sets the URL used to retrieve the description document for this device/tree. Required.
/// </summary>
public Uri Location { get; set; }
/// <summary>
/// The base URL to use for all relative url's provided in other propertise (and those of child devices). Optional.
/// </summary>
/// <remarks>
/// <para>Defines the base URL. Used to construct fully-qualified URLs. All relative URLs that appear elsewhere in the description are combined with this base URL. If URLBase is empty or not given, the base URL is the URL from which the device description was retrieved (which is the preferred implementation; use of URLBase is no longer recommended). Specified by UPnP vendor. Single URL.</para>
/// </remarks>
public Uri UrlBase
{
get
{
return _UrlBase ?? this.Location;
}
/// <summary> set
/// The base URL to use for all relative url's provided in other propertise (and those of child devices). Optional. {
/// </summary> _UrlBase = value;
/// <remarks> }
/// <para>Defines the base URL. Used to construct fully-qualified URLs. All relative URLs that appear elsewhere in the description are combined with this base URL. If URLBase is empty or not given, the base URL is the URL from which the device description was retrieved (which is the preferred implementation; use of URLBase is no longer recommended). Specified by UPnP vendor. Single URL.</para> }
/// </remarks>
public Uri UrlBase
{
get
{
return _UrlBase ?? this.Location;
}
set #endregion
{ }
_UrlBase = value;
}
}
#endregion
}
} }

@ -14,7 +14,7 @@ namespace SocketHttpListener.Net
// System.Net.ResponseStream // System.Net.ResponseStream
// //
// Author: // Author:
// Gonzalo Paniagua Javier (gonzalo@novell.com) // Gonzalo Paniagua Javier (gonzalo@novell.com)
// //
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com) // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
// //

@ -12,7 +12,7 @@ namespace SocketHttpListener.Net
// System.Net.ResponseStream // System.Net.ResponseStream
// //
// Author: // Author:
// Gonzalo Paniagua Javier (gonzalo@novell.com) // Gonzalo Paniagua Javier (gonzalo@novell.com)
// //
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com) // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
// //

@ -207,13 +207,13 @@ namespace SocketHttpListener.Net
} }
/* Apache forces closing the connection for these status codes: /* Apache forces closing the connection for these status codes:
* HttpStatusCode.BadRequest 400 * HttpStatusCode.BadRequest 400
* HttpStatusCode.RequestTimeout 408 * HttpStatusCode.RequestTimeout 408
* HttpStatusCode.LengthRequired 411 * HttpStatusCode.LengthRequired 411
* HttpStatusCode.RequestEntityTooLarge 413 * HttpStatusCode.RequestEntityTooLarge 413
* HttpStatusCode.RequestUriTooLong 414 * HttpStatusCode.RequestUriTooLong 414
* HttpStatusCode.InternalServerError 500 * HttpStatusCode.InternalServerError 500
* HttpStatusCode.ServiceUnavailable 503 * HttpStatusCode.ServiceUnavailable 503
*/ */
bool conn_close = (_statusCode == (int)HttpStatusCode.BadRequest || _statusCode == (int)HttpStatusCode.RequestTimeout bool conn_close = (_statusCode == (int)HttpStatusCode.BadRequest || _statusCode == (int)HttpStatusCode.RequestTimeout
|| _statusCode == (int)HttpStatusCode.LengthRequired || _statusCode == (int)HttpStatusCode.RequestEntityTooLarge || _statusCode == (int)HttpStatusCode.LengthRequired || _statusCode == (int)HttpStatusCode.RequestEntityTooLarge

@ -13,7 +13,7 @@ namespace SocketHttpListener.Net
// System.Net.ResponseStream // System.Net.ResponseStream
// //
// Author: // Author:
// Gonzalo Paniagua Javier (gonzalo@novell.com) // Gonzalo Paniagua Javier (gonzalo@novell.com)
// //
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com) // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
// //

@ -13,7 +13,7 @@ namespace SocketHttpListener.Net
// System.Net.ResponseStream // System.Net.ResponseStream
// //
// Author: // Author:
// Gonzalo Paniagua Javier (gonzalo@novell.com) // Gonzalo Paniagua Javier (gonzalo@novell.com)
// //
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com) // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
// //

@ -19,7 +19,7 @@ namespace SocketHttpListener.Net
// System.Net.ResponseStream // System.Net.ResponseStream
// //
// Author: // Author:
// Gonzalo Paniagua Javier (gonzalo@novell.com) // Gonzalo Paniagua Javier (gonzalo@novell.com)
// //
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com) // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
// //

@ -23,69 +23,69 @@ namespace SocketHttpListener.Net
} }
static readonly bool[] allowed_chars = { static readonly bool[] allowed_chars = {
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, true, false, true, true, true, true, false, false, false, true, false, false, false, false, false, true, false, true, true, true, true, false, false, false, true,
true, false, true, true, false, true, true, true, true, true, true, true, true, true, true, false, true, false, true, true, false, true, true, true, true, true, true, true, true, true, true, false,
false, false, false, false, false, false, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
false, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
false, true, false false, true, false
}; };
static readonly Dictionary<string, HeaderInfo> headers; static readonly Dictionary<string, HeaderInfo> headers;
static WebHeaderCollection() static WebHeaderCollection()
{ {
headers = new Dictionary<string, HeaderInfo>(StringComparer.OrdinalIgnoreCase) { headers = new Dictionary<string, HeaderInfo>(StringComparer.OrdinalIgnoreCase) {
{ "Allow", HeaderInfo.MultiValue }, { "Allow", HeaderInfo.MultiValue },
{ "Accept", HeaderInfo.Request | HeaderInfo.MultiValue }, { "Accept", HeaderInfo.Request | HeaderInfo.MultiValue },
{ "Accept-Charset", HeaderInfo.MultiValue }, { "Accept-Charset", HeaderInfo.MultiValue },
{ "Accept-Encoding", HeaderInfo.MultiValue }, { "Accept-Encoding", HeaderInfo.MultiValue },
{ "Accept-Language", HeaderInfo.MultiValue }, { "Accept-Language", HeaderInfo.MultiValue },
{ "Accept-Ranges", HeaderInfo.MultiValue }, { "Accept-Ranges", HeaderInfo.MultiValue },
{ "Age", HeaderInfo.Response }, { "Age", HeaderInfo.Response },
{ "Authorization", HeaderInfo.MultiValue }, { "Authorization", HeaderInfo.MultiValue },
{ "Cache-Control", HeaderInfo.MultiValue }, { "Cache-Control", HeaderInfo.MultiValue },
{ "Cookie", HeaderInfo.MultiValue }, { "Cookie", HeaderInfo.MultiValue },
{ "Connection", HeaderInfo.Request | HeaderInfo.MultiValue }, { "Connection", HeaderInfo.Request | HeaderInfo.MultiValue },
{ "Content-Encoding", HeaderInfo.MultiValue }, { "Content-Encoding", HeaderInfo.MultiValue },
{ "Content-Length", HeaderInfo.Request | HeaderInfo.Response }, { "Content-Length", HeaderInfo.Request | HeaderInfo.Response },
{ "Content-Type", HeaderInfo.Request }, { "Content-Type", HeaderInfo.Request },
{ "Content-Language", HeaderInfo.MultiValue }, { "Content-Language", HeaderInfo.MultiValue },
{ "Date", HeaderInfo.Request }, { "Date", HeaderInfo.Request },
{ "Expect", HeaderInfo.Request | HeaderInfo.MultiValue}, { "Expect", HeaderInfo.Request | HeaderInfo.MultiValue},
{ "Host", HeaderInfo.Request }, { "Host", HeaderInfo.Request },
{ "If-Match", HeaderInfo.MultiValue }, { "If-Match", HeaderInfo.MultiValue },
{ "If-Modified-Since", HeaderInfo.Request }, { "If-Modified-Since", HeaderInfo.Request },
{ "If-None-Match", HeaderInfo.MultiValue }, { "If-None-Match", HeaderInfo.MultiValue },
{ "Keep-Alive", HeaderInfo.Response }, { "Keep-Alive", HeaderInfo.Response },
{ "Pragma", HeaderInfo.MultiValue }, { "Pragma", HeaderInfo.MultiValue },
{ "Proxy-Authenticate", HeaderInfo.MultiValue }, { "Proxy-Authenticate", HeaderInfo.MultiValue },
{ "Proxy-Authorization", HeaderInfo.MultiValue }, { "Proxy-Authorization", HeaderInfo.MultiValue },
{ "Proxy-Connection", HeaderInfo.Request | HeaderInfo.MultiValue }, { "Proxy-Connection", HeaderInfo.Request | HeaderInfo.MultiValue },
{ "Range", HeaderInfo.Request | HeaderInfo.MultiValue }, { "Range", HeaderInfo.Request | HeaderInfo.MultiValue },
{ "Referer", HeaderInfo.Request }, { "Referer", HeaderInfo.Request },
{ "Set-Cookie", HeaderInfo.MultiValue }, { "Set-Cookie", HeaderInfo.MultiValue },
{ "Set-Cookie2", HeaderInfo.MultiValue }, { "Set-Cookie2", HeaderInfo.MultiValue },
{ "Server", HeaderInfo.Response }, { "Server", HeaderInfo.Response },
{ "TE", HeaderInfo.MultiValue }, { "TE", HeaderInfo.MultiValue },
{ "Trailer", HeaderInfo.MultiValue }, { "Trailer", HeaderInfo.MultiValue },
{ "Transfer-Encoding", HeaderInfo.Request | HeaderInfo.Response | HeaderInfo.MultiValue }, { "Transfer-Encoding", HeaderInfo.Request | HeaderInfo.Response | HeaderInfo.MultiValue },
{ "Translate", HeaderInfo.Request | HeaderInfo.Response }, { "Translate", HeaderInfo.Request | HeaderInfo.Response },
{ "Upgrade", HeaderInfo.MultiValue }, { "Upgrade", HeaderInfo.MultiValue },
{ "User-Agent", HeaderInfo.Request }, { "User-Agent", HeaderInfo.Request },
{ "Vary", HeaderInfo.MultiValue }, { "Vary", HeaderInfo.MultiValue },
{ "Via", HeaderInfo.MultiValue }, { "Via", HeaderInfo.MultiValue },
{ "Warning", HeaderInfo.MultiValue }, { "Warning", HeaderInfo.MultiValue },
{ "WWW-Authenticate", HeaderInfo.Response | HeaderInfo. MultiValue }, { "WWW-Authenticate", HeaderInfo.Response | HeaderInfo. MultiValue },
{ "SecWebSocketAccept", HeaderInfo.Response }, { "SecWebSocketAccept", HeaderInfo.Response },
{ "SecWebSocketExtensions", HeaderInfo.Request | HeaderInfo.Response | HeaderInfo. MultiValue }, { "SecWebSocketExtensions", HeaderInfo.Request | HeaderInfo.Response | HeaderInfo. MultiValue },
{ "SecWebSocketKey", HeaderInfo.Request }, { "SecWebSocketKey", HeaderInfo.Request },
{ "Sec-WebSocket-Protocol", HeaderInfo.Request | HeaderInfo.Response | HeaderInfo. MultiValue }, { "Sec-WebSocket-Protocol", HeaderInfo.Request | HeaderInfo.Response | HeaderInfo. MultiValue },
{ "SecWebSocketVersion", HeaderInfo.Response | HeaderInfo. MultiValue } { "SecWebSocketVersion", HeaderInfo.Response | HeaderInfo. MultiValue }
}; };
} }
// Methods // Methods

Loading…
Cancel
Save