/BlogEngine/DotNetSlave.BusinessLogic/Web/HttpHandlers/SyndicationHandler.cs
C# | 375 lines | 232 code | 49 blank | 94 comment | 34 complexity | 4347dd124ea782957c961db457643ead MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, BSD-3-Clause
- namespace BlogEngine.Core.Web.HttpHandlers
- {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Text;
- using System.Web;
- using System.Xml;
-
- using BlogEngine.Core.Web.HttpModules;
-
- /// <summary>
- /// Implements a custom handler to synchronously process HTTP Web requests for a syndication feed.
- /// </summary>
- /// <remarks>
- /// This handler can generate syndication feeds in a variety of formats and filtering
- /// options based on the query string parmaeters provided.
- /// </remarks>
- /// <seealso cref="IHttpHandler"/>
- /// <seealso cref="SyndicationGenerator"/>
- public class SyndicationHandler : IHttpHandler
- {
- #region Properties
-
- /// <summary>
- /// Gets a value indicating whether another request can use the <see cref = "T:System.Web.IHttpHandler"></see> instance.
- /// </summary>
- /// <returns>true if the <see cref = "T:System.Web.IHttpHandler"></see> instance is reusable; otherwise, false.</returns>
- public bool IsReusable
- {
- get
- {
- return false;
- }
- }
-
- #endregion
-
- #region Implemented Interfaces
-
- #region IHttpHandler
-
- /// <summary>
- /// Enables processing of HTTP Web requests by a custom HttpHandler that implements
- /// the <see cref="T:System.Web.IHttpHandler"></see> interface.
- /// </summary>
- /// <param name="context">
- /// An <see cref="T:System.Web.HttpContext"></see> object that provides references
- /// to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.
- /// </param>
- public void ProcessRequest(HttpContext context)
- {
- if (!Security.IsAuthorizedTo(Rights.ViewPublicPosts))
- {
- context.Response.StatusCode = 401;
- return;
- }
-
- var title = RetrieveTitle(context);
- var format = RetrieveFormat(context);
- var list = GenerateItemList(context);
- list = CleanList(list);
-
- if (string.IsNullOrEmpty(context.Request.QueryString["post"]))
- {
- // Shorten the list to the number of posts stated in the settings, except for the comment feed.
- var max = Math.Min(BlogSettings.Instance.PostsPerFeed, list.Count);
- list = list.FindAll(item => item.IsVisible);
-
- list = list.GetRange(0, max);
- }
-
- SetHeaderInformation(context, list, format);
-
- if (BlogSettings.Instance.EnableHttpCompression)
- {
- CompressionModule.CompressResponse(context);
- }
-
- var generator = new SyndicationGenerator(BlogSettings.Instance, Category.Categories);
- generator.WriteFeed(format, context.Response.OutputStream, list, title);
- }
-
- #endregion
-
- #endregion
-
- #region Methods
-
- /// <summary>
- /// Cleans the list.
- /// </summary>
- /// <param name="list">The list of IPublishable.</param>
- /// <returns>The cleaned list of IPublishable.</returns>
- private static List<IPublishable> CleanList(List<IPublishable> list)
- {
- return list.FindAll(item => item.IsVisible);
- }
-
- /// <summary>
- /// A converter delegate used for converting Results to Posts.
- /// </summary>
- /// <param name="item">
- /// The publishable item.
- /// </param>
- /// <returns>
- /// Converts to publishable interface.
- /// </returns>
- private static IPublishable ConvertToIPublishable(IPublishable item)
- {
- return item;
- }
-
- /// <summary>
- /// Generates the list of feed items based on the URL.
- /// </summary>
- /// <param name="context">
- /// The context.
- /// </param>
- /// <returns>
- /// A list of IPublishable.
- /// </returns>
- private static List<IPublishable> GenerateItemList(HttpContext context)
- {
- if (!string.IsNullOrEmpty(context.Request.QueryString["category"]))
- {
- // All posts in the specified category
- var categoryId = new Guid(context.Request.QueryString["category"]);
- return Post.GetPostsByCategory(categoryId).ConvertAll(ConvertToIPublishable);
- }
-
- if (!string.IsNullOrEmpty(context.Request.QueryString["tag"]))
- {
- // All posts with the specified tag
- return Post.GetPostsByTag(context.Request.QueryString["tag"].Trim()).ConvertAll(ConvertToIPublishable);
- }
-
- if (!string.IsNullOrEmpty(context.Request.QueryString["author"]))
- {
- // All posts by the specified author
- var author = context.Request.QueryString["author"];
- return Post.GetPostsByAuthor(author).ConvertAll(ConvertToIPublishable);
- }
-
- if (!string.IsNullOrEmpty(context.Request.QueryString["post"]))
- {
- // All comments of the specified post
- var post = Post.GetPost(new Guid(context.Request.QueryString["post"]));
- return post.Comments.ConvertAll(ConvertToIPublishable);
- }
-
- if (!string.IsNullOrEmpty(context.Request.QueryString["comments"]))
- {
- // The recent comments added to any post.
- return RecentComments();
- }
-
- if (!string.IsNullOrEmpty(context.Request.QueryString["q"]))
- {
- // Searches posts and pages
- return Search.Hits(context.Request.QueryString["q"], false);
- }
-
- if (!string.IsNullOrEmpty(context.Request.QueryString["apml"]))
- {
- // Finds matches to an APML file in both posts and pages
- try
- {
- using (var client = new WebClient())
- {
- client.Credentials = CredentialCache.DefaultNetworkCredentials;
- client.Encoding = Encoding.Default;
- using (var stream = client.OpenRead(context.Request.QueryString["apml"]))
- {
- var doc = new XmlDocument();
- if (stream != null)
- {
- doc.Load(stream);
- }
-
- var list = Search.ApmlMatches(doc, 30);
- list.Sort((i1, i2) => i2.DateCreated.CompareTo(i1.DateCreated));
- return list;
- }
- }
- }
- catch (Exception ex)
- {
- context.Response.Clear();
- context.Response.Write(ex.Message);
- context.Response.ContentType = "text/plain";
- context.Response.AppendHeader("Content-Disposition", "inline; filename=\"error.txt\"");
- context.Response.End();
- }
- }
-
- // The latest posts
- return Post.Posts.ConvertAll(ConvertToIPublishable);
- }
-
- /// <summary>
- /// Creates a list of the most recent comments
- /// </summary>
- /// <returns>
- /// A list of IPublishable.
- /// </returns>
- private static List<IPublishable> RecentComments()
- {
- var temp = Post.Posts.SelectMany(post => post.ApprovedComments).ToList();
-
- temp.Sort();
- temp.Reverse();
- var list = temp.ToList();
-
- return list.ConvertAll(ConvertToIPublishable);
- }
-
- /// <summary>
- /// Retrieves the syndication format from the urL parameters.
- /// </summary>
- /// <param name="context">
- /// The HTTP context.
- /// </param>
- /// <returns>
- /// The syndication format.
- /// </returns>
- private static SyndicationFormat RetrieveFormat(HttpContext context)
- {
- var query = context.Request.QueryString["format"];
- var format = BlogSettings.Instance.SyndicationFormat;
- if (!string.IsNullOrEmpty(query))
- {
- format = context.Request.QueryString["format"];
- }
-
- try
- {
- return (SyndicationFormat)Enum.Parse(typeof(SyndicationFormat), format, true);
- }
- catch (ArgumentException)
- {
- return SyndicationFormat.None;
- }
- }
-
- /// <summary>
- /// Retrieves the title.
- /// </summary>
- /// <param name="context">The context.</param>
- /// <returns>The title string.</returns>
- private static string RetrieveTitle(HttpContext context)
- {
- var title = BlogSettings.Instance.Name;
- string subTitle = null;
-
- if (!string.IsNullOrEmpty(context.Request.QueryString["category"]))
- {
- if (context.Request.QueryString["category"].Length != 36)
- {
- StopServing(context);
- }
-
- var categoryId = new Guid(context.Request.QueryString["category"]);
- var currentCategory = Category.GetCategory(categoryId);
- if (currentCategory == null)
- {
- StopServing(context);
- }
-
- if (currentCategory != null)
- {
- subTitle = currentCategory.Title;
- }
- }
-
- if (!string.IsNullOrEmpty(context.Request.QueryString["author"]))
- {
- subTitle = context.Request.QueryString["author"];
- }
-
- if (!string.IsNullOrEmpty(context.Request.QueryString["post"]))
- {
- if (context.Request.QueryString["post"].Length != 36)
- {
- StopServing(context);
- }
-
- var post = Post.GetPost(new Guid(context.Request.QueryString["post"]));
- if (post == null)
- {
- StopServing(context);
- }
-
- if (post != null)
- {
- subTitle = post.Title;
- }
- }
-
- if (!string.IsNullOrEmpty(context.Request.QueryString["comments"]))
- {
- subTitle = "Comments";
- }
-
- if (subTitle != null)
- {
- return string.Format("{0} - {1}", title, subTitle);
- }
-
- return title;
- }
-
- /// <summary>
- /// Sets the response header information.
- /// </summary>
- /// <param name="context">
- /// An <see cref="HttpContext"/> object that provides references to the intrinsic server objects (for example, <b>Request</b>, <b>Response</b>, <b>Session</b>, and <b>Server</b>) used to service HTTP requests.
- /// </param>
- /// <param name="items">
- /// The collection of <see cref="IPublishable"/> instances used when setting the response header details.
- /// </param>
- /// <param name="format">
- /// The format of the syndication feed being generated.
- /// </param>
- /// <exception cref="ArgumentNullException">
- /// The <paramref name="context"/> is a null reference (Nothing in Visual Basic) -or- the <paramref name="items"/> is a null reference (Nothing in Visual Basic).
- /// </exception>
- private static void SetHeaderInformation(
- HttpContext context, IEnumerable<IPublishable> items, SyndicationFormat format)
- {
- var lastModified = new DateTime(1900, 1, 3); // don't use DateTime.MinValue here, as Mono doesn't like it
- foreach (var item in items)
- {
- if (item.DateModified.AddHours(-BlogSettings.Instance.Timezone) > lastModified)
- {
- lastModified = item.DateModified.AddHours(-BlogSettings.Instance.Timezone);
- }
- }
-
- switch (format)
- {
- case SyndicationFormat.Atom:
- context.Response.ContentType = "application/atom+xml";
- context.Response.AppendHeader("Content-Disposition", "inline; filename=atom.xml");
- break;
-
- case SyndicationFormat.Rss:
- context.Response.ContentType = "application/rss+xml";
- context.Response.AppendHeader("Content-Disposition", "inline; filename=rss.xml");
- break;
- }
-
- if (Utils.SetConditionalGetHeaders(lastModified))
- {
- context.Response.End();
- }
- }
-
- /// <summary>
- /// The stop serving.
- /// </summary>
- /// <param name="context">
- /// The context.
- /// </param>
- private static void StopServing(HttpContext context)
- {
- context.Response.Clear();
- context.Response.StatusCode = 404;
- context.Response.End();
- }
-
- #endregion
- }
- }