//----------------------------------------------------------------------- // // Copyright (c) Outercurve Foundation. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth.Messages { using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Globalization; using System.Text; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OAuth.ChannelElements; using Validation; /// /// A base class for all OAuth messages. /// [Serializable] public abstract class MessageBase : IDirectedProtocolMessage, IDirectResponseProtocolMessage { /// /// A store for extra name/value data pairs that are attached to this message. /// private Dictionary extraData = new Dictionary(); /// /// Gets a value indicating whether signing this message is required. /// private MessageProtections protectionRequired; /// /// Gets a value indicating whether this is a direct or indirect message. /// private MessageTransport transport; /// /// The URI to the remote endpoint to send this message to. /// private MessageReceivingEndpoint recipient; /// /// Backing store for the properties. /// private IDirectedProtocolMessage originatingRequest; /// /// Backing store for the properties. /// private bool incoming; #if DEBUG /// /// Initializes static members of the class. /// static MessageBase() { LowSecurityMode = true; } #endif /// /// Initializes a new instance of the class for direct response messages. /// /// The level of protection the message requires. /// The request that asked for this direct response. /// The OAuth version. protected MessageBase(MessageProtections protectionRequired, IDirectedProtocolMessage originatingRequest, Version version) { Requires.NotNull(originatingRequest, "originatingRequest"); Requires.NotNull(version, "version"); this.protectionRequired = protectionRequired; this.transport = MessageTransport.Direct; this.originatingRequest = originatingRequest; this.Version = version; } /// /// Initializes a new instance of the class for direct requests or indirect messages. /// /// The level of protection the message requires. /// A value indicating whether this message requires a direct or indirect transport. /// The URI that a directed message will be delivered to. /// The OAuth version. protected MessageBase(MessageProtections protectionRequired, MessageTransport transport, MessageReceivingEndpoint recipient, Version version) { Requires.NotNull(recipient, "recipient"); Requires.NotNull(version, "version"); this.protectionRequired = protectionRequired; this.transport = transport; this.recipient = recipient; this.Version = version; } #region IProtocolMessage Properties /// /// Gets the version of the protocol this message is prepared to implement. /// Version IMessage.Version { get { return this.Version; } } /// /// Gets the level of protection this message requires. /// MessageProtections IProtocolMessage.RequiredProtection { get { return this.RequiredProtection; } } /// /// Gets a value indicating whether this is a direct or indirect message. /// MessageTransport IProtocolMessage.Transport { get { return this.Transport; } } /// /// Gets the dictionary of additional name/value fields tacked on to this message. /// IDictionary IMessage.ExtraData { get { return this.ExtraData; } } #endregion #region IDirectedProtocolMessage Members /// /// Gets the URI to the Service Provider endpoint to send this message to. /// Uri IDirectedProtocolMessage.Recipient { get { return this.recipient != null ? this.recipient.Location : null; } } /// /// Gets the preferred method of transport for the message. /// HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods { get { return this.HttpMethods; } } #endregion #region IDirectResponseProtocolMessage Members /// /// Gets the originating request message that caused this response to be formed. /// IDirectedProtocolMessage IDirectResponseProtocolMessage.OriginatingRequest { get { return this.originatingRequest; } } #endregion /// /// Gets or sets a value indicating whether security sensitive strings are /// emitted from the ToString() method. /// internal static bool LowSecurityMode { get; set; } /// /// Gets a value indicating whether this message was deserialized as an incoming message. /// protected internal bool Incoming { get { return this.incoming; } } /// /// Gets the version of the protocol this message is prepared to implement. /// protected internal Version Version { get; private set; } /// /// Gets the level of protection this message requires. /// protected MessageProtections RequiredProtection { get { return this.protectionRequired; } } /// /// Gets a value indicating whether this is a direct or indirect message. /// protected MessageTransport Transport { get { return this.transport; } } /// /// Gets the dictionary of additional name/value fields tacked on to this message. /// protected IDictionary ExtraData { get { return this.extraData; } } /// /// Gets the preferred method of transport for the message. /// protected HttpDeliveryMethods HttpMethods { get { return this.recipient != null ? this.recipient.AllowedMethods : HttpDeliveryMethods.None; } } /// /// Gets or sets the URI to the Service Provider endpoint to send this message to. /// protected Uri Recipient { get { return this.recipient != null ? this.recipient.Location : null; } set { if (this.recipient != null) { this.recipient = new MessageReceivingEndpoint(value, this.recipient.AllowedMethods); } else if (value != null) { throw new InvalidOperationException(); } } } /// /// Gets the originating request message that caused this response to be formed. /// protected IDirectedProtocolMessage OriginatingRequest { get { return this.originatingRequest; } } #region IProtocolMessage Methods /// /// Checks the message state for conformity to the protocol specification /// and throws an exception if the message is invalid. /// void IMessage.EnsureValidMessage() { this.EnsureValidMessage(); } #endregion /// /// Returns a human-friendly string describing the message and all serializable properties. /// /// The channel that will carry this message. /// /// The string representation of this object. /// internal virtual string ToString(Channel channel) { Requires.NotNull(channel, "channel"); StringBuilder builder = new StringBuilder(); builder.AppendFormat(CultureInfo.InvariantCulture, "{0} message", GetType().Name); if (this.recipient != null) { builder.AppendFormat(CultureInfo.InvariantCulture, " as {0} to {1}", this.recipient.AllowedMethods, this.recipient.Location); } builder.AppendLine(); MessageDictionary dictionary = channel.MessageDescriptions.GetAccessor(this); foreach (var pair in dictionary) { string value = pair.Value; if (pair.Key == "oauth_signature" && !LowSecurityMode) { value = "xxxxxxxxxxxxx (not shown)"; } builder.Append('\t'); builder.Append(pair.Key); builder.Append(": "); builder.AppendLine(value); } return builder.ToString(); } /// /// Sets a flag indicating that this message is received (as opposed to sent). /// internal void SetAsIncoming() { this.incoming = true; } /// /// Checks the message state for conformity to the protocol specification /// and throws an exception if the message is invalid. /// protected virtual void EnsureValidMessage() { } } }