PageRenderTime 29ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/Acn/Acn/Slp/SlpUserAgent.cs

#
C# | 291 lines | 152 code | 49 blank | 90 comment | 24 complexity | 389a016d62a46dacdd8a7216bbd123fb MD5 | raw file
  1. #region Copyright © 2011 Oliver Waits
  2. //______________________________________________________________________________________________________________
  3. // Service Location Protocol
  4. // Copyright © 2011 Oliver Waits
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. //______________________________________________________________________________________________________________
  24. #endregion
  25. using System;
  26. using System.Collections.Generic;
  27. using System.Linq;
  28. using System.Text;
  29. using System.Threading;
  30. using Acn.Slp.Packets;
  31. using Acn.Slp.Sockets;
  32. using System.Net;
  33. using System.Text.RegularExpressions;
  34. namespace Acn.Slp
  35. {
  36. public class SlpUserAgent:SlpAgent
  37. {
  38. #region Setup and Initialisation
  39. public SlpUserAgent ()
  40. {
  41. }
  42. public SlpUserAgent(string scope)
  43. {
  44. Scope = scope;
  45. }
  46. #endregion
  47. #region Contents
  48. private DirectoryAgentInformation directoryAgent = null;
  49. public DirectoryAgentInformation DirectoryAgent
  50. {
  51. get { return directoryAgent; }
  52. protected set { directoryAgent = value; }
  53. }
  54. #endregion
  55. #region User Agent
  56. public override void Open()
  57. {
  58. base.Open();
  59. //Discover any DA's we should be talking to.
  60. SendDARequest();
  61. }
  62. public int Find(string serviceType)
  63. {
  64. if (socket == null)
  65. throw new InvalidOperationException("User agent not open. Please open the user agent first before calling Find.");
  66. return SendRequest(serviceType, Scope);
  67. }
  68. /// <summary>
  69. /// Sends an attribute request.
  70. /// This can either be to a specific URL or a general service type.
  71. /// Results will be returned via the AttributeReply event
  72. /// </summary>
  73. /// <param name="url">The URL.</param>
  74. /// <returns>
  75. /// An id for this request which can be used to match it to the reply
  76. /// </returns>
  77. public int RequestAttributes(string url)
  78. {
  79. if (socket == null)
  80. throw new InvalidOperationException("User agent not open. Please open the user agent first before calling.");
  81. return SendAttributeRequest(Scope, url);
  82. }
  83. /// <summary>
  84. /// Sends an attribute request.
  85. /// This can either be to a specific URL or a general service type.
  86. /// Results will be returned via the AttributeReply event
  87. /// This version targets a specific IP which can cut down the noise on replies.
  88. /// </summary>
  89. /// <param name="url">The URL.</param>
  90. /// <returns>
  91. /// An id for this request which can be used to match it to the reply
  92. /// </returns>
  93. public int RequestAttributes(IPEndPoint target, string url)
  94. {
  95. if (socket == null)
  96. throw new InvalidOperationException("User agent not open. Please open the user agent first before calling.");
  97. return SendAttributeRequest(target, Scope, url);
  98. }
  99. #endregion
  100. #region Messaging
  101. /// <summary>
  102. /// Sends a service request.
  103. /// </summary>
  104. /// <param name="serviceType">Type of the service.</param>
  105. /// <param name="scope">The scope.</param>
  106. /// <returns>The Id of the request</returns>
  107. private int SendRequest(string serviceType, string scope)
  108. {
  109. ServiceRequestPacket request = new ServiceRequestPacket();
  110. FillHeader(request.Header, NewTransactionId());
  111. request.ServiceType = serviceType;
  112. request.ScopeList = scope;
  113. if (DirectoryAgent == null)
  114. //Multicast the service request.
  115. socket.Send(request);
  116. else
  117. //Request the services directly from the DA.
  118. socket.Send(DirectoryAgent.EndPoint, request);
  119. return request.Header.XId;
  120. }
  121. /// <summary>
  122. /// Sends an attribute request.
  123. /// This can either be to a specific URL or a general service URL.
  124. /// Results will be returned via the AttributeReply event
  125. /// </summary>
  126. /// <param name="scope">The scope.</param>
  127. /// <param name="url">The URL.</param>
  128. /// <returns>
  129. /// An id for this request which can be used to mtch it to the reply
  130. /// </returns>
  131. private int SendAttributeRequest(string scope, string url)
  132. {
  133. AttributeRequestPacket request = PrepareAttributeRequest(scope, url);
  134. socket.Send(request);
  135. return request.Header.XId;
  136. }
  137. /// <summary>
  138. /// Sends an attribute request.
  139. /// This can either be to a specific URL or a general service URL.
  140. /// Results will be returned via the AttributeReply event
  141. /// </summary>
  142. /// <param name="target">The target endpoint.</param>
  143. /// <param name="scope">The scope.</param>
  144. /// <param name="url">The URL.</param>
  145. private int SendAttributeRequest(IPEndPoint target, string scope, string url)
  146. {
  147. AttributeRequestPacket request = PrepareAttributeRequest(scope, url);
  148. socket.Send(target, request);
  149. return request.Header.XId;
  150. }
  151. /// <summary>
  152. /// Prepares the attribute request object.
  153. /// </summary>
  154. /// <param name="scope">The scope.</param>
  155. /// <param name="url">The URL.</param>
  156. /// <returns></returns>
  157. private AttributeRequestPacket PrepareAttributeRequest(string scope, string url)
  158. {
  159. AttributeRequestPacket request = new AttributeRequestPacket();
  160. FillHeader(request.Header, NewTransactionId());
  161. request.ScopeList = scope;
  162. request.Url = url;
  163. return request;
  164. }
  165. protected override void ProcessPacket(NewPacketEventArgs packetInfo)
  166. {
  167. DirectoryAgentAdvertPacket daAdvert = packetInfo.Packet as DirectoryAgentAdvertPacket;
  168. if (daAdvert != null)
  169. DirectoryAgent = new DirectoryAgentInformation(daAdvert.Url, packetInfo.SourceEndPoint);
  170. ServiceReplyPacket serviceReply = packetInfo.Packet as ServiceReplyPacket;
  171. if (serviceReply != null)
  172. ProcessServiceReply(serviceReply, packetInfo.SourceEndPoint);
  173. AttributeReplyPacket attributeReply = packetInfo.Packet as AttributeReplyPacket;
  174. if (attributeReply != null)
  175. {
  176. ProcessAttributeReply(attributeReply, packetInfo.SourceEndPoint);
  177. }
  178. }
  179. #endregion
  180. #region Events
  181. public event EventHandler<ServiceFoundEventArgs> ServiceFound;
  182. protected void ProcessServiceReply(ServiceReplyPacket serviceReply,IPEndPoint ipAddress)
  183. {
  184. if (serviceReply.ErrorCode == SlpErrorCode.None && serviceReply.Urls.Count > 0)
  185. {
  186. if (ServiceFound != null)
  187. ServiceFound(this, new ServiceFoundEventArgs(serviceReply.Urls, ipAddress) { RequestId = serviceReply.Header.XId });
  188. }
  189. }
  190. /// <summary>
  191. /// Occurs when a reply to an attribute request is recieved.
  192. /// </summary>
  193. public event EventHandler<AttributeReplyEventArgs> AttributeReply;
  194. /// <summary>
  195. /// Processes the attribute reply.
  196. /// </summary>
  197. /// <param name="attributeReply">The attribute reply packet.</param>
  198. /// <param name="ipAddress">The ip address the reply originated from.</param>
  199. protected void ProcessAttributeReply(AttributeReplyPacket attributeReply, IPEndPoint ipAddress)
  200. {
  201. if (attributeReply.ErrorCode == SlpErrorCode.None)
  202. {
  203. AttributeReplyEventArgs args = new AttributeReplyEventArgs() { Address = ipAddress, RequestId = attributeReply.Header.XId};
  204. args.Attributes = SplitAttributeList(attributeReply.AttrList);
  205. if (AttributeReply != null)
  206. {
  207. AttributeReply(this, args);
  208. }
  209. }
  210. }
  211. // This regex matches a (key=value) string from the attribute list
  212. // This is much simplified compared to what the RFC calls for but it should work for most common cases
  213. static Regex attributePairMatch = new Regex(@"\(\s*([^\(\)\,\\\/\!\<\=\>\~]+)\s*=\s*([^\)]*)\s*\)", RegexOptions.Compiled);
  214. /// <summary>
  215. /// Splits a string attribute list into a dictionary of key value pairs.
  216. /// Only supports (key=value) lists not tags.
  217. /// </summary>
  218. /// <param name="attributeList">The attribute list.</param>
  219. /// <returns>A dictionary of string pairs</returns>
  220. static protected Dictionary<string, string> SplitAttributeList(string attributeList)
  221. {
  222. Dictionary<string, string> attributes = new Dictionary<string, string>();
  223. var pairs = attributeList.Split(',');
  224. foreach (string pair in pairs)
  225. {
  226. Match match = attributePairMatch.Match(pair);
  227. if (match.Success)
  228. {
  229. attributes[match.Groups[1].Value] = string.IsNullOrEmpty(match.Groups[2].Value) ? string.Empty : match.Groups[2].Value;
  230. }
  231. }
  232. return attributes;
  233. }
  234. #endregion
  235. #region IDispose
  236. public override void Dispose()
  237. {
  238. Close();
  239. }
  240. #endregion
  241. }
  242. }