UPnP10DeviceValidator.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. namespace Rssdp.Infrastructure
  7. {
  8. /// <summary>
  9. /// Validates a <see cref="SsdpDevice"/> object's properties meet the UPnP 1.0 specification.
  10. /// </summary>
  11. /// <remarks>
  12. /// <para>This is a best effort validation for known rules, it doesn't guarantee 100% compatibility with the specification. Reading the specification yourself is the best way to ensure compatibility.</para>
  13. /// </remarks>
  14. public class Upnp10DeviceValidator : IUpnpDeviceValidator
  15. {
  16. #region Public Methods
  17. /// <summary>
  18. /// Returns an enumerable set of strings, each one being a description of an invalid property on the specified root device.
  19. /// </summary>
  20. /// <remarks>
  21. /// <para>If no errors are found, an empty (but non-null) enumerable is returned.</para>
  22. /// </remarks>
  23. /// <param name="device">The <see cref="SsdpRootDevice"/> to validate.</param>
  24. /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception>
  25. /// <returns>A non-null enumerable set of strings, empty if there are no validation errors, otherwise each string represents a discrete problem.</returns>
  26. public IEnumerable<string> GetValidationErrors(SsdpRootDevice device)
  27. {
  28. if (device == null) throw new ArgumentNullException("device");
  29. var retVal = GetValidationErrors((SsdpDevice)device) as IList<string>;
  30. if (device.Location == null)
  31. retVal.Add("Location cannot be null.");
  32. else if (!device.Location.IsAbsoluteUri)
  33. retVal.Add("Location must be an absolute URL.");
  34. return retVal;
  35. }
  36. /// <summary>
  37. /// Returns an enumerable set of strings, each one being a description of an invalid property on the specified device.
  38. /// </summary>
  39. /// <remarks>
  40. /// <para>If no errors are found, an empty (but non-null) enumerable is returned.</para>
  41. /// </remarks>
  42. /// <param name="device">The <see cref="SsdpDevice"/> to validate.</param>
  43. /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception>
  44. /// <returns>A non-null enumerable set of strings, empty if there are no validation errors, otherwise each string represents a discrete problem.</returns>
  45. public IEnumerable<string> GetValidationErrors(SsdpDevice device)
  46. {
  47. if (device == null) throw new ArgumentNullException("device");
  48. var retVal = new List<string>();
  49. if (String.IsNullOrEmpty(device.Uuid))
  50. retVal.Add("Uuid is not set.");
  51. if (!String.IsNullOrEmpty(device.Upc))
  52. ValidateUpc(device, retVal);
  53. if (String.IsNullOrEmpty(device.Udn))
  54. retVal.Add("UDN is not set.");
  55. else
  56. ValidateUdn(device, retVal);
  57. if (String.IsNullOrEmpty(device.DeviceType))
  58. retVal.Add("DeviceType is not set.");
  59. if (String.IsNullOrEmpty(device.DeviceTypeNamespace))
  60. retVal.Add("DeviceTypeNamespace is not set.");
  61. else
  62. {
  63. if (IsOverLength(device.DeviceTypeNamespace, 64))
  64. retVal.Add("DeviceTypeNamespace cannot be longer than 64 characters.");
  65. //if (device.DeviceTypeNamespace.Contains("."))
  66. // retVal.Add("Period (.) characters in the DeviceTypeNamespace property must be replaced with hyphens (-).");
  67. }
  68. if (device.DeviceVersion <= 0)
  69. retVal.Add("DeviceVersion must be 1 or greater.");
  70. if (IsOverLength(device.ModelName, 32))
  71. retVal.Add("ModelName cannot be longer than 32 characters.");
  72. if (IsOverLength(device.ModelNumber, 32))
  73. retVal.Add("ModelNumber cannot be longer than 32 characters.");
  74. if (IsOverLength(device.FriendlyName, 64))
  75. retVal.Add("FriendlyName cannot be longer than 64 characters.");
  76. if (IsOverLength(device.Manufacturer, 64))
  77. retVal.Add("Manufacturer cannot be longer than 64 characters.");
  78. if (IsOverLength(device.SerialNumber, 64))
  79. retVal.Add("SerialNumber cannot be longer than 64 characters.");
  80. if (IsOverLength(device.ModelDescription, 128))
  81. retVal.Add("ModelDescription cannot be longer than 128 characters.");
  82. if (String.IsNullOrEmpty(device.FriendlyName))
  83. retVal.Add("FriendlyName is required.");
  84. if (String.IsNullOrEmpty(device.Manufacturer))
  85. retVal.Add("Manufacturer is required.");
  86. if (String.IsNullOrEmpty(device.ModelName))
  87. retVal.Add("ModelName is required.");
  88. if (device.Icons.Any())
  89. ValidateIcons(device, retVal);
  90. ValidateChildDevices(device, retVal);
  91. return retVal;
  92. }
  93. /// <summary>
  94. /// Validates the specified device and throws an <see cref="System.InvalidOperationException"/> if there are any validation errors.
  95. /// </summary>
  96. /// <param name="device">The <see cref="SsdpDevice"/> to validate.</param>
  97. /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception>
  98. /// <exception cref="System.InvalidOperationException">Thrown if the device object does not pass validation.</exception>
  99. public void ThrowIfDeviceInvalid(SsdpDevice device)
  100. {
  101. var errors = this.GetValidationErrors(device);
  102. if (errors != null && errors.Any()) throw new InvalidOperationException("Invalid device settings : " + String.Join(Environment.NewLine, errors));
  103. }
  104. #endregion
  105. #region Private Methods
  106. private static void ValidateUpc(SsdpDevice device, List<string> retVal)
  107. {
  108. if (device.Upc.Length != 12)
  109. retVal.Add("Upc, if provided, should be 12 digits.");
  110. foreach (char c in device.Upc)
  111. {
  112. if (!Char.IsDigit(c))
  113. {
  114. retVal.Add("Upc, if provided, should contain only digits (numeric characters).");
  115. break;
  116. }
  117. }
  118. }
  119. private static void ValidateUdn(SsdpDevice device, List<string> retVal)
  120. {
  121. if (!device.Udn.StartsWith("uuid:", StringComparison.OrdinalIgnoreCase))
  122. retVal.Add("UDN must begin with uuid:. Correct format is uuid:<uuid>");
  123. else if (device.Udn.Substring(5).Trim() != device.Uuid)
  124. retVal.Add("UDN incorrect. Correct format is uuid:<uuid>");
  125. }
  126. private static void ValidateIcons(SsdpDevice device, List<string> retVal)
  127. {
  128. if (device.Icons.Any((di) => di.Url == null))
  129. retVal.Add("Device icon is missing URL.");
  130. if (device.Icons.Any((di) => String.IsNullOrEmpty(di.MimeType)))
  131. retVal.Add("Device icon is missing mime type.");
  132. if (device.Icons.Any((di) => di.Width <= 0 || di.Height <= 0))
  133. retVal.Add("Device icon has zero (or negative) height, width or both.");
  134. if (device.Icons.Any((di) => di.ColorDepth <= 0))
  135. retVal.Add("Device icon has zero (or negative) colordepth.");
  136. }
  137. private void ValidateChildDevices(SsdpDevice device, List<string> retVal)
  138. {
  139. foreach (var childDevice in device.Devices)
  140. {
  141. foreach (var validationError in this.GetValidationErrors(childDevice))
  142. {
  143. retVal.Add("Embedded Device : " + childDevice.Uuid + ": " + validationError);
  144. }
  145. }
  146. }
  147. private static bool IsOverLength(string value, int maxLength)
  148. {
  149. return !String.IsNullOrEmpty(value) && value.Length > maxLength;
  150. }
  151. #endregion
  152. }
  153. }