//-----------------------------------------------------------------------
//
// Copyright (c) Outercurve Foundation. All rights reserved.
//
//-----------------------------------------------------------------------
namespace DotNetOpenAuth {
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Drawing.Design;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using DotNetOpenAuth.Logging;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Yadis;
///
/// The locations the YADIS protocol describes can contain a reference
/// to an XRDS document.
///
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xrds", Justification = "Correct spelling")]
[Flags]
public enum XrdsUrlLocations {
///
/// The XRDS document should not be advertised anywhere.
///
///
/// When the XRDS document is not referenced from anywhere,
/// the XRDS content is only available when
/// is true
/// and the discovering client includes an
/// "Accept: application/xrds+xml" HTTP header.
///
None = 0x0,
///
/// Indicates XRDS document referencing from an HTTP protocol header (outside the HTML).
///
HttpHeader = 0x1,
///
/// Indicates XRDS document referencing from within an HTML page's <HEAD> tag.
///
HtmlMeta = 0x2,
///
/// Indicates XRDS document referencing in both HTTP headers and HTML HEAD tags.
///
Both = 0x3,
}
///
/// An ASP.NET control that advertises an XRDS document and even responds to specially
/// crafted requests to retrieve it.
///
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xrds", Justification = "Correct spelling")]
[DefaultProperty("XrdsLocation")]
[ToolboxData("<{0}:XrdsPublisher runat=server>{0}:XrdsPublisher>")]
public class XrdsPublisher : Control {
///
/// The view state key to ues for storing the value of the property.
///
private const string XrdsUrlViewStateKey = "XrdsUrl";
///
/// The default value for the property.
///
private const XrdsUrlLocations XrdsAdvertisementDefault = XrdsUrlLocations.HttpHeader;
///
/// The view state key to ues for storing the value of the property.
///
private const string XrdsAdvertisementViewStateKey = "XrdsAdvertisement";
///
/// The default value for the property.
///
private const bool XrdsAutoAnswerDefault = true;
///
/// The view state key to ues for storing the value of the property.
///
private const string XrdsAutoAnswerViewStateKey = "XrdsAutoAnswer";
///
/// The default value for the property.
///
private const bool EnabledDefault = true;
///
/// The view state key to ues for storing the value of the property.
///
private const string EnabledViewStateKey = "Enabled";
///
/// Initializes a new instance of the class.
///
public XrdsPublisher() {
Reporting.RecordFeatureUse(this);
}
#region Properties
///
/// Gets or sets the location of the XRDS document.
///
[SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Property grid")]
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xrds", Justification = "Correct spelling")]
[Category("Behavior"), Bindable(true)]
[UrlProperty, Editor("System.Web.UI.Design.UrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
public string XrdsUrl {
get {
return (string)ViewState[XrdsUrlViewStateKey];
}
set {
UriUtil.ValidateResolvableUrl(Page, DesignMode, value);
ViewState[XrdsUrlViewStateKey] = value;
}
}
///
/// Gets or sets where the XRDS document URL is advertised in the web response.
///
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xrds", Justification = "Correct spelling")]
[Category("Behavior"), DefaultValue(XrdsAdvertisementDefault), Bindable(true)]
[Description("Where the XRDS document URL is advertised in the web response.")]
public XrdsUrlLocations XrdsAdvertisement {
get {
return ViewState[XrdsAdvertisementViewStateKey] == null ?
XrdsAdvertisementDefault : (XrdsUrlLocations)ViewState[XrdsAdvertisementViewStateKey];
}
set {
ViewState[XrdsAdvertisementViewStateKey] = value;
}
}
///
/// Gets or sets a value indicating whether a specially crafted YADIS
/// search for an XRDS document is immediately answered by this control.
///
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xrds", Justification = "Correct spelling")]
[Category("Behavior"), DefaultValue(XrdsAutoAnswerDefault), Bindable(true)]
[Description("Whether XRDS requests should be immediately answered with the XRDS document if it is served by this web application.")]
public bool XrdsAutoAnswer {
get {
return ViewState[XrdsAutoAnswerViewStateKey] == null ?
XrdsAutoAnswerDefault : (bool)ViewState[XrdsAutoAnswerViewStateKey];
}
set {
ViewState[XrdsAutoAnswerViewStateKey] = value;
}
}
///
/// Gets or sets a value indicating whether the XRDS document is advertised.
///
[Category("Behavior"), DefaultValue(EnabledDefault)]
public bool Enabled {
get {
return ViewState[EnabledViewStateKey] == null ?
EnabledDefault : (bool)ViewState[EnabledViewStateKey];
}
set {
ViewState[EnabledViewStateKey] = value;
}
}
#endregion
///
/// Detects YADIS requests for the XRDS document and responds immediately
/// if is true.
///
/// The object that contains the event data.
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
if (!this.Enabled) {
return;
}
if (!this.Page.IsPostBack) {
if (this.XrdsAutoAnswer && !string.IsNullOrEmpty(this.XrdsUrl) &&
this.XrdsUrl.StartsWith("~/", StringComparison.Ordinal)) {
// Check for the presence of an accept types header that is looking
// for the XRDS document specifically.
if (this.Page.Request.AcceptTypes != null && Array.IndexOf(this.Page.Request.AcceptTypes, ContentTypes.Xrds) >= 0) {
// Respond to the caller immediately with an XRDS document
// and avoid sending the whole web page's contents to the
// client since it isn't interested anyway.
// We do NOT simply send a 301 redirect here because that would
// alter the Claimed Identifier.
Logger.Yadis.InfoFormat("Transferring request from {0} to {1} to respond to XRDS discovery request.", this.Page.Request.Url.AbsoluteUri, this.XrdsUrl);
this.Page.Server.Transfer(this.XrdsUrl);
}
}
}
}
///
/// Renders the HTTP Header and/or HTML HEAD tags.
///
/// The object that receives the server control content.
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Assume(System.Boolean,System.String,System.String)", Justification = "Code contracts"), SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Uri(Uri, string) accepts second arguments that Uri(Uri, new Uri(string)) does not that we must support.")]
protected override void Render(HtmlTextWriter writer) {
Assumes.True(writer != null, "Missing contract.");
if (this.Enabled && this.Visible && !string.IsNullOrEmpty(this.XrdsUrl)) {
Uri xrdsAddress = new Uri(MessagingUtilities.GetRequestUrlFromContext(), Page.Response.ApplyAppPathModifier(this.XrdsUrl));
if ((this.XrdsAdvertisement & XrdsUrlLocations.HttpHeader) != 0) {
Page.Response.AddHeader(Yadis.Yadis.HeaderName, xrdsAddress.AbsoluteUri);
}
if ((this.XrdsAdvertisement & XrdsUrlLocations.HtmlMeta) != 0) {
writer.WriteBeginTag("meta");
writer.WriteAttribute("http-equiv", Yadis.Yadis.HeaderName);
writer.WriteAttribute("content", xrdsAddress.AbsoluteUri);
writer.Write("/>");
writer.WriteLine();
}
}
}
}
}