PageRenderTime 60ms CodeModel.GetById 40ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 1ms

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

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