PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/BlogEngine/DotNetSlave.BusinessLogic/Web/HttpHandlers/TrackbackHandler.cs

#
C# | 301 lines | 170 code | 38 blank | 93 comment | 29 complexity | a4f8d2bc95a604efee99a9988f2676d9 MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, BSD-3-Clause
  1. namespace BlogEngine.Core.Web.HttpHandlers
  2. {
  3. using System;
  4. using System.ComponentModel;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Text.RegularExpressions;
  8. using System.Web;
  9. /// <summary>
  10. /// Trackback Handler
  11. /// </summary>
  12. public class TrackbackHandler : IHttpHandler
  13. {
  14. #region Constants and Fields
  15. /// <summary>
  16. /// The HTML Regex.
  17. /// </summary>
  18. private static readonly Regex RegexHtml =
  19. new Regex(
  20. @"</?\w+((\s+\w+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)+\s*|\s*)/?>",
  21. RegexOptions.Singleline | RegexOptions.Compiled);
  22. /// <summary>
  23. /// Whether the source has link.
  24. /// </summary>
  25. private bool sourceHasLink;
  26. #endregion
  27. #region Events
  28. /// <summary>
  29. /// Occurs when a trackback is accepted as valid and added as a comment.
  30. /// </summary>
  31. public static event EventHandler<EventArgs> Accepted;
  32. /// <summary>
  33. /// Occurs when a hit is made to the trackback.axd handler.
  34. /// </summary>
  35. public static event EventHandler<CancelEventArgs> Received;
  36. /// <summary>
  37. /// Occurs when a trackback request is rejected because the sending
  38. /// website already made a trackback or pingback to the specific page.
  39. /// </summary>
  40. public static event EventHandler<EventArgs> Rejected;
  41. /// <summary>
  42. /// Occurs when the request comes from a spammer.
  43. /// </summary>
  44. public static event EventHandler<EventArgs> Spammed;
  45. #endregion
  46. #region Properties
  47. /// <summary>
  48. /// Gets a value indicating whether another request can use the <see cref = "T:System.Web.IHttpHandler"></see> instance.
  49. /// </summary>
  50. /// <value></value>
  51. /// <returns>true if the <see cref = "T:System.Web.IHttpHandler"></see> instance is reusable; otherwise, false.</returns>
  52. public bool IsReusable
  53. {
  54. get
  55. {
  56. return true;
  57. }
  58. }
  59. #endregion
  60. #region Public Methods
  61. /// <summary>
  62. /// Called when [spammed].
  63. /// </summary>
  64. /// <param name="url">The URL string.</param>
  65. public static void OnSpammed(string url)
  66. {
  67. if (Spammed != null)
  68. {
  69. Spammed(url, EventArgs.Empty);
  70. }
  71. }
  72. #endregion
  73. #region Implemented Interfaces
  74. #region IHttpHandler
  75. /// <summary>
  76. /// Enables processing of HTTP Web requests by a custom HttpHandler that
  77. /// implements the <see cref="T:System.Web.IHttpHandler"></see> interface.
  78. /// </summary>
  79. /// <param name="context">
  80. /// An <see cref="T:System.Web.HttpContext"></see>
  81. /// object that provides references to the intrinsic server objects
  82. /// (for example, Request, Response, Session, and Server) used to service HTTP requests.
  83. /// </param>
  84. public void ProcessRequest(HttpContext context)
  85. {
  86. if (!BlogSettings.Instance.IsCommentsEnabled || !BlogSettings.Instance.EnableTrackBackReceive)
  87. {
  88. context.Response.StatusCode = 404;
  89. context.Response.End();
  90. }
  91. var e = new CancelEventArgs();
  92. this.OnReceived(e);
  93. if (e.Cancel)
  94. {
  95. return;
  96. }
  97. var postId = context.Request.Params["id"];
  98. var title = context.Request.Params["title"];
  99. var excerpt = context.Request.Params["excerpt"];
  100. var blogName = context.Request.Params["blog_name"];
  101. var url = string.Empty;
  102. if (context.Request.Params["url"] != null)
  103. {
  104. url = context.Request.Params["url"].Split(',')[0];
  105. }
  106. Post post;
  107. if (!string.IsNullOrEmpty(title) && !string.IsNullOrEmpty(postId) && !string.IsNullOrEmpty(blogName) &&
  108. postId.Length == 36)
  109. {
  110. post = Post.GetPost(new Guid(postId));
  111. this.ExamineSourcePage(url, post.AbsoluteLink.ToString());
  112. var containsHtml = !string.IsNullOrEmpty(excerpt) &&
  113. (RegexHtml.IsMatch(excerpt) || RegexHtml.IsMatch(title) ||
  114. RegexHtml.IsMatch(blogName));
  115. if (IsFirstPingBack(post, url) && this.sourceHasLink && !containsHtml)
  116. {
  117. AddComment(url, post, blogName, title, excerpt);
  118. this.OnAccepted(url);
  119. context.Response.Write(
  120. "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?><response><error>0</error></response>");
  121. context.Response.End();
  122. }
  123. else if (!IsFirstPingBack(post, url))
  124. {
  125. this.OnRejected(url);
  126. context.Response.Write(
  127. "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?><response><error>Trackback already registered</error></response>");
  128. context.Response.End();
  129. }
  130. else if (!this.sourceHasLink || containsHtml)
  131. {
  132. OnSpammed(url);
  133. context.Response.Write(
  134. "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?><response><error>The source page does not link</error></response>");
  135. context.Response.End();
  136. }
  137. }
  138. else
  139. {
  140. context.Response.Redirect(Utils.RelativeWebRoot);
  141. }
  142. }
  143. #endregion
  144. #endregion
  145. #region Methods
  146. /// <summary>
  147. /// Called when [accepted].
  148. /// </summary>
  149. /// <param name="url">The URL string.</param>
  150. protected virtual void OnAccepted(string url)
  151. {
  152. if (Accepted != null)
  153. {
  154. Accepted(url, EventArgs.Empty);
  155. }
  156. }
  157. /// <summary>
  158. /// Raises the <see cref="Received"/> event.
  159. /// </summary>
  160. /// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
  161. protected virtual void OnReceived(CancelEventArgs e)
  162. {
  163. if (Received != null)
  164. {
  165. Received(null, e);
  166. }
  167. }
  168. /// <summary>
  169. /// Called when [rejected].
  170. /// </summary>
  171. /// <param name="url">The URL string.</param>
  172. protected virtual void OnRejected(string url)
  173. {
  174. if (Rejected != null)
  175. {
  176. Rejected(url, EventArgs.Empty);
  177. }
  178. }
  179. /// <summary>
  180. /// Insert the pingback as a comment on the post.
  181. /// </summary>
  182. /// <param name="sourceUrl">
  183. /// The source Url.
  184. /// </param>
  185. /// <param name="post">
  186. /// The post to comment on.
  187. /// </param>
  188. /// <param name="blogName">
  189. /// The blog Name.
  190. /// </param>
  191. /// <param name="title">
  192. /// The title.
  193. /// </param>
  194. /// <param name="excerpt">
  195. /// The excerpt.
  196. /// </param>
  197. private static void AddComment(string sourceUrl, Post post, string blogName, string title, string excerpt)
  198. {
  199. var comment = new Comment
  200. {
  201. Id = Guid.NewGuid(),
  202. Author = blogName,
  203. Website = new Uri(sourceUrl),
  204. Content = title + Environment.NewLine + Environment.NewLine + excerpt,
  205. Email = "trackback",
  206. Parent = post,
  207. DateCreated = DateTime.Now,
  208. IP = HttpContext.Current.Request.UserHostAddress,
  209. IsApproved = true
  210. };
  211. post.AddComment(comment);
  212. }
  213. /// <summary>
  214. /// Checks to see if the source has already pinged the target.
  215. /// If it has, there is no reason to add it again.
  216. /// </summary>
  217. /// <param name="post">
  218. /// The post to check.
  219. /// </param>
  220. /// <param name="sourceUrl">
  221. /// The source Url.
  222. /// </param>
  223. /// <returns>
  224. /// Whether is first ping back.
  225. /// </returns>
  226. private static bool IsFirstPingBack(Post post, string sourceUrl)
  227. {
  228. return
  229. !post.Comments.Any(
  230. comment =>
  231. comment.Website != null &&
  232. comment.Website.ToString().Equals(sourceUrl, StringComparison.OrdinalIgnoreCase)) &&
  233. !post.Comments.Any(
  234. comment =>
  235. comment.IP != null &&
  236. comment.IP == HttpContext.Current.Request.UserHostAddress);
  237. }
  238. /// <summary>
  239. /// Parse the HTML of the source page.
  240. /// </summary>
  241. /// <param name="sourceUrl">
  242. /// The source Url.
  243. /// </param>
  244. /// <param name="targetUrl">
  245. /// The target Url.
  246. /// </param>
  247. private void ExamineSourcePage(string sourceUrl, string targetUrl)
  248. {
  249. try
  250. {
  251. var remoteFile = new RemoteFile(new Uri(sourceUrl), true);
  252. var html = remoteFile.GetFileAsString();
  253. this.sourceHasLink = html.ToUpperInvariant().Contains(targetUrl.ToUpperInvariant());
  254. }
  255. catch (WebException)
  256. {
  257. this.sourceHasLink = false;
  258. // throw new ArgumentException("Trackback sender does not exists: " + sourceUrl, ex);
  259. }
  260. }
  261. #endregion
  262. }
  263. }