summaryrefslogtreecommitdiffstats
path: root/src/DotNetOAuth/Messaging
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOAuth/Messaging')
-rw-r--r--src/DotNetOAuth/Messaging/DictionaryXmlReader.cs81
-rw-r--r--src/DotNetOAuth/Messaging/DictionaryXmlWriter.cs273
-rw-r--r--src/DotNetOAuth/Messaging/DirectMessageChannel.cs21
-rw-r--r--src/DotNetOAuth/Messaging/IProtocolMessage.cs41
-rw-r--r--src/DotNetOAuth/Messaging/IProtocolMessageRequest.cs22
-rw-r--r--src/DotNetOAuth/Messaging/IndirectMessage.cs53
-rw-r--r--src/DotNetOAuth/Messaging/IndirectMessageEncoder.cs21
-rw-r--r--src/DotNetOAuth/Messaging/MessageScheme.cs35
-rw-r--r--src/DotNetOAuth/Messaging/MessageTransport.cs22
-rw-r--r--src/DotNetOAuth/Messaging/ProtocolMessageSerializer.cs111
10 files changed, 680 insertions, 0 deletions
diff --git a/src/DotNetOAuth/Messaging/DictionaryXmlReader.cs b/src/DotNetOAuth/Messaging/DictionaryXmlReader.cs
new file mode 100644
index 0000000..5eae4c7
--- /dev/null
+++ b/src/DotNetOAuth/Messaging/DictionaryXmlReader.cs
@@ -0,0 +1,81 @@
+//-----------------------------------------------------------------------
+// <copyright file="DictionaryXmlReader.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.IO;
+ using System.Linq;
+ using System.Xml;
+ using System.Xml.Linq;
+
+ /// <summary>
+ /// An XmlReader-looking object that actually reads from a dictionary.
+ /// </summary>
+ internal class DictionaryXmlReader {
+ /// <summary>
+ /// Creates an XmlReader that reads data out of a dictionary instead of XML.
+ /// </summary>
+ /// <param name="rootElement">The name of the root XML element.</param>
+ /// <param name="fields">The dictionary to read data from.</param>
+ /// <returns>The XmlReader that will read the data out of the given dictionary.</returns>
+ internal static XmlReader Create(XName rootElement, IDictionary<string, string> fields) {
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ return CreateRoundtripReader(rootElement, fields);
+ }
+
+ /// <summary>
+ /// Creates an <see cref="XmlReader"/> that will read values out of a dictionary.
+ /// </summary>
+ /// <param name="rootElement">The surrounding root XML element to generate.</param>
+ /// <param name="fields">The dictionary to list values from.</param>
+ /// <returns>The generated <see cref="XmlReader"/>.</returns>
+ private static XmlReader CreateRoundtripReader(XName rootElement, IDictionary<string, string> fields) {
+ MemoryStream stream = new MemoryStream();
+ XmlWriter writer = XmlWriter.Create(stream);
+ SerializeDictionaryToXml(writer, rootElement, fields);
+ writer.Flush();
+ stream.Seek(0, SeekOrigin.Begin);
+
+ // For debugging purposes.
+ StreamReader sr = new StreamReader(stream);
+ Trace.WriteLine(sr.ReadToEnd());
+ stream.Seek(0, SeekOrigin.Begin);
+
+ return XmlReader.Create(stream);
+ }
+
+ /// <summary>
+ /// Writes out the values in a dictionary as XML.
+ /// </summary>
+ /// <param name="writer">The <see cref="XmlWriter"/> to write out the XML to.</param>
+ /// <param name="rootElement">The name of the root element to use to surround the dictionary values.</param>
+ /// <param name="fields">The dictionary with values to serialize.</param>
+ private static void SerializeDictionaryToXml(XmlWriter writer, XName rootElement, IDictionary<string, string> fields) {
+ if (writer == null) {
+ throw new ArgumentNullException("writer");
+ }
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ writer.WriteStartElement(rootElement.LocalName, rootElement.NamespaceName);
+
+ // The elements must be serialized in alphabetical order so the DataContractSerializer will see them.
+ foreach (var pair in fields.OrderBy(pair => pair.Key, StringComparer.Ordinal)) {
+ writer.WriteStartElement(pair.Key, rootElement.NamespaceName);
+ writer.WriteValue(pair.Value);
+ writer.WriteEndElement();
+ }
+
+ writer.WriteEndElement();
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/DictionaryXmlWriter.cs b/src/DotNetOAuth/Messaging/DictionaryXmlWriter.cs
new file mode 100644
index 0000000..3be043f
--- /dev/null
+++ b/src/DotNetOAuth/Messaging/DictionaryXmlWriter.cs
@@ -0,0 +1,273 @@
+//-----------------------------------------------------------------------
+// <copyright file="DictionaryXmlWriter.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+ using System.Xml;
+
+ /// <summary>
+ /// An XmlWriter-looking object that actually saves data to a dictionary.
+ /// </summary>
+ internal class DictionaryXmlWriter {
+ /// <summary>
+ /// Creates an <see cref="XmlWriter"/> that actually writes to an IDictionary&lt;string, string&gt; instance.
+ /// </summary>
+ /// <param name="dictionary">The dictionary to save the written XML to.</param>
+ /// <returns>The XmlWriter that will save data to the given dictionary.</returns>
+ internal static XmlWriter Create(IDictionary<string, string> dictionary) {
+ return new PseudoXmlWriter(dictionary);
+ }
+
+ /// <summary>
+ /// Writes out a dictionary as if it were XML.
+ /// </summary>
+ private class PseudoXmlWriter : XmlWriter {
+ /// <summary>
+ /// The dictionary to write values to.
+ /// </summary>
+ private IDictionary<string, string> dictionary;
+
+ /// <summary>
+ /// The key being written at the moment.
+ /// </summary>
+ private string key;
+
+ /// <summary>
+ /// The value being written out at the moment.
+ /// </summary>
+ private StringBuilder value = new StringBuilder();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PseudoXmlWriter"/> class.
+ /// </summary>
+ /// <param name="dictionary">The dictionary that will be written to.</param>
+ internal PseudoXmlWriter(IDictionary<string, string> dictionary) {
+ if (dictionary == null) {
+ throw new ArgumentNullException("dictionary");
+ }
+
+ this.dictionary = dictionary;
+ }
+
+ /// <summary>
+ /// Gets the spoofed state of the <see cref="XmlWriter"/>.
+ /// </summary>
+ public override WriteState WriteState {
+ get { return WriteState.Element; }
+ }
+
+ /// <summary>
+ /// Prepares to write out a new key/value pair with the given key name to the dictionary.
+ /// </summary>
+ /// <param name="prefix">This parameter is ignored.</param>
+ /// <param name="localName">The key to store in the dictionary.</param>
+ /// <param name="ns">This parameter is ignored.</param>
+ public override void WriteStartElement(string prefix, string localName, string ns) {
+ this.key = localName;
+ this.value.Length = 0;
+ }
+
+ /// <summary>
+ /// Appends some text to the value that is to be stored in the dictionary.
+ /// </summary>
+ /// <param name="text">The text to append to the value.</param>
+ public override void WriteString(string text) {
+ if (!string.IsNullOrEmpty(this.key)) {
+ this.value.Append(text);
+ }
+ }
+
+ /// <summary>
+ /// Writes out a completed key/value to the dictionary.
+ /// </summary>
+ public override void WriteEndElement() {
+ if (this.key != null) {
+ this.dictionary[this.key] = this.value.ToString();
+ this.key = null;
+ this.value.Length = 0;
+ }
+ }
+
+ /// <summary>
+ /// Clears the internal key/value building state.
+ /// </summary>
+ /// <param name="prefix">This parameter is ignored.</param>
+ /// <param name="localName">This parameter is ignored.</param>
+ /// <param name="ns">This parameter is ignored.</param>
+ public override void WriteStartAttribute(string prefix, string localName, string ns) {
+ this.key = null;
+ }
+
+ /// <summary>
+ /// This method does not do anything.
+ /// </summary>
+ public override void WriteEndAttribute() { }
+
+ /// <summary>
+ /// This method does not do anything.
+ /// </summary>
+ public override void Close() { }
+
+ #region Unimplemented methods
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ public override void Flush() {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="ns">This parameter is ignored.</param>
+ /// <returns>None, since an exception is always thrown.</returns>
+ public override string LookupPrefix(string ns) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="buffer">This parameter is ignored.</param>
+ /// <param name="index">This parameter is ignored.</param>
+ /// <param name="count">This parameter is ignored.</param>
+ public override void WriteBase64(byte[] buffer, int index, int count) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="text">This parameter is ignored.</param>
+ public override void WriteCData(string text) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="ch">This parameter is ignored.</param>
+ public override void WriteCharEntity(char ch) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="buffer">This parameter is ignored.</param>
+ /// <param name="index">This parameter is ignored.</param>
+ /// <param name="count">This parameter is ignored.</param>
+ public override void WriteChars(char[] buffer, int index, int count) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="text">This parameter is ignored.</param>
+ public override void WriteComment(string text) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="name">This parameter is ignored.</param>
+ /// <param name="pubid">This parameter is ignored.</param>
+ /// <param name="sysid">This parameter is ignored.</param>
+ /// <param name="subset">This parameter is ignored.</param>
+ public override void WriteDocType(string name, string pubid, string sysid, string subset) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ public override void WriteEndDocument() {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="name">This parameter is ignored.</param>
+ public override void WriteEntityRef(string name) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ public override void WriteFullEndElement() {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="name">This parameter is ignored.</param>
+ /// <param name="text">This parameter is ignored.</param>
+ public override void WriteProcessingInstruction(string name, string text) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="data">This parameter is ignored.</param>
+ public override void WriteRaw(string data) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="buffer">This parameter is ignored.</param>
+ /// <param name="index">This parameter is ignored.</param>
+ /// <param name="count">This parameter is ignored.</param>
+ public override void WriteRaw(char[] buffer, int index, int count) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="standalone">This parameter is ignored.</param>
+ public override void WriteStartDocument(bool standalone) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ public override void WriteStartDocument() {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="lowChar">This parameter is ignored.</param>
+ /// <param name="highChar">This parameter is ignored.</param>
+ public override void WriteSurrogateCharEntity(char lowChar, char highChar) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="ws">This parameter is ignored.</param>
+ public override void WriteWhitespace(string ws) {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/DirectMessageChannel.cs b/src/DotNetOAuth/Messaging/DirectMessageChannel.cs
new file mode 100644
index 0000000..ee65c1f
--- /dev/null
+++ b/src/DotNetOAuth/Messaging/DirectMessageChannel.cs
@@ -0,0 +1,21 @@
+//-----------------------------------------------------------------------
+// <copyright file="DirectMessageChannel.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ /// <summary>
+ /// Manages sending direct messages to a remote party and receiving responses.
+ /// </summary>
+ internal class DirectMessageChannel {
+ /// <summary>
+ /// Sends a direct message and returns the response.
+ /// </summary>
+ /// <param name="message">The message to send.</param>
+ /// <returns>The message response.</returns>
+ public IProtocolMessage Send(IProtocolMessage message) {
+ throw new System.NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/IProtocolMessage.cs b/src/DotNetOAuth/Messaging/IProtocolMessage.cs
new file mode 100644
index 0000000..76079c0
--- /dev/null
+++ b/src/DotNetOAuth/Messaging/IProtocolMessage.cs
@@ -0,0 +1,41 @@
+//-----------------------------------------------------------------------
+// <copyright file="IProtocolMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+
+ /// <summary>
+ /// The interface that classes must implement to be serialized/deserialized
+ /// as OAuth messages.
+ /// </summary>
+ internal interface IProtocolMessage {
+ /// <summary>
+ /// Gets the version of the protocol this message is prepared to implement.
+ /// </summary>
+ Protocol Protocol { get; }
+
+ /// <summary>
+ /// Gets whether this is a direct or indirect message.
+ /// </summary>
+ MessageTransport Transport { get; }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ void EnsureValidMessage();
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/IProtocolMessageRequest.cs b/src/DotNetOAuth/Messaging/IProtocolMessageRequest.cs
new file mode 100644
index 0000000..c70f076
--- /dev/null
+++ b/src/DotNetOAuth/Messaging/IProtocolMessageRequest.cs
@@ -0,0 +1,22 @@
+//-----------------------------------------------------------------------
+// <copyright file="IProtocolMessageRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+
+ /// <summary>
+ /// Implemented by messages that are sent as requests.
+ /// </summary>
+ internal interface IProtocolMessageRequest : IProtocolMessage {
+ /// <summary>
+ /// Gets or sets the URL of the intended receiver of this message.
+ /// </summary>
+ Uri Recipient {
+ get;
+ set;
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/IndirectMessage.cs b/src/DotNetOAuth/Messaging/IndirectMessage.cs
new file mode 100644
index 0000000..9a2624e
--- /dev/null
+++ b/src/DotNetOAuth/Messaging/IndirectMessage.cs
@@ -0,0 +1,53 @@
+//-----------------------------------------------------------------------
+// <copyright file="IndirectMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System.Net;
+
+ /// <summary>
+ /// A protocol message that passes between Consumer and Service Provider
+ /// via the user agent using a redirect or form POST submission.
+ /// </summary>
+ /// <remarks>
+ /// An <see cref="IndirectMessage"/> instance describes the HTTP response that must
+ /// be sent to the user agent to initiate the message transfer.
+ /// </remarks>
+ public class IndirectMessage {
+ /// <summary>
+ /// Gets the headers that must be included in the response to the user agent.
+ /// </summary>
+ /// <remarks>
+ /// The headers in this collection are not meant to be a comprehensive list
+ /// of exactly what should be sent, but are meant to augment whatever headers
+ /// are generally included in a typical response.
+ /// </remarks>
+ public WebHeaderCollection Headers { get; internal set; }
+
+ /// <summary>
+ /// Gets the body of the HTTP response.
+ /// </summary>
+ public byte[] Body { get; internal set; }
+
+ /// <summary>
+ /// Gets the HTTP status code to use in the HTTP response.
+ /// </summary>
+ public HttpStatusCode Status { get; internal set; }
+
+ /// <summary>
+ /// Gets or sets a reference to the actual protocol message that
+ /// is being sent via the user agent.
+ /// </summary>
+ internal IProtocolMessage OriginalMessage { get; set; }
+
+ /// <summary>
+ /// Automatically sends the appropriate response to the user agent.
+ /// Requires a current HttpContext.
+ /// </summary>
+ public void Send() {
+ throw new System.NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/IndirectMessageEncoder.cs b/src/DotNetOAuth/Messaging/IndirectMessageEncoder.cs
new file mode 100644
index 0000000..54970d9
--- /dev/null
+++ b/src/DotNetOAuth/Messaging/IndirectMessageEncoder.cs
@@ -0,0 +1,21 @@
+//-----------------------------------------------------------------------
+// <copyright file="IndirectMessageEncoder.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ /// <summary>
+ /// A message encoder that prepares messages for transmittal.
+ /// </summary>
+ internal class IndirectMessageEncoder {
+ /// <summary>
+ /// Prepares a protocol message for sending
+ /// </summary>
+ /// <param name="message">The indirect message to send.</param>
+ /// <returns>The encoded message to send to the user agent.</returns>
+ internal IndirectMessage Encode(IProtocolMessage message) {
+ throw new System.NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/MessageScheme.cs b/src/DotNetOAuth/Messaging/MessageScheme.cs
new file mode 100644
index 0000000..ee91740
--- /dev/null
+++ b/src/DotNetOAuth/Messaging/MessageScheme.cs
@@ -0,0 +1,35 @@
+//-----------------------------------------------------------------------
+// <copyright file="MessageScheme.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ /// <summary>
+ /// The methods available for the Consumer to send direct messages to the Service Provider.
+ /// </summary>
+ /// <remarks>
+ /// See 1.0 spec section 5.2.
+ /// </remarks>
+ internal enum MessageScheme {
+ /// <summary>
+ /// In the HTTP Authorization header as defined in OAuth HTTP Authorization Scheme (OAuth HTTP Authorization Scheme).
+ /// </summary>
+ AuthorizationHeaderRequest,
+
+ /// <summary>
+ /// As the HTTP POST request body with a content-type of application/x-www-form-urlencoded.
+ /// </summary>
+ PostRequest,
+
+ /// <summary>
+ /// Added to the URLs in the query part (as defined by [RFC3986] (Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .) section 3).
+ /// </summary>
+ GetRequest,
+
+ /// <summary>
+ /// Response parameters are sent by the Service Provider to return Tokens and other information to the Consumer in the HTTP response body. The parameter names and values are first encoded as per Parameter Encoding (Parameter Encoding), and concatenated with the ‘&amp;’ character (ASCII code 38) as defined in [RFC3986] (Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .) Section 2.1.
+ /// </summary>
+ QueryStyleResponse,
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/MessageTransport.cs b/src/DotNetOAuth/Messaging/MessageTransport.cs
new file mode 100644
index 0000000..40e6897
--- /dev/null
+++ b/src/DotNetOAuth/Messaging/MessageTransport.cs
@@ -0,0 +1,22 @@
+//-----------------------------------------------------------------------
+// <copyright file="MessageTransport.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ /// <summary>
+ /// The type of transport mechanism used for a message: either direct or indirect.
+ /// </summary>
+ public enum MessageTransport {
+ /// <summary>
+ /// A message that is sent directly from the Consumer to the Service Provider, or vice versa.
+ /// </summary>
+ Direct,
+
+ /// <summary>
+ /// A message that is sent from one party to another via a redirect in the user agent.
+ /// </summary>
+ Indirect,
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/ProtocolMessageSerializer.cs b/src/DotNetOAuth/Messaging/ProtocolMessageSerializer.cs
new file mode 100644
index 0000000..0b28d49
--- /dev/null
+++ b/src/DotNetOAuth/Messaging/ProtocolMessageSerializer.cs
@@ -0,0 +1,111 @@
+//-----------------------------------------------------------------------
+// <copyright file="ProtocolMessageSerializer.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Runtime.Serialization;
+ using System.Xml;
+ using System.Xml.Linq;
+
+ /// <summary>
+ /// Serializes/deserializes OAuth messages for/from transit.
+ /// </summary>
+ /// <typeparam name="T">The specific <see cref="IProtocolMessage"/>-derived type
+ /// that will be serialized and deserialized using this class.</typeparam>
+ internal class ProtocolMessageSerializer<T> where T : IProtocolMessage {
+ /// <summary>
+ /// Backing field for the <see cref="RootElement"/> property
+ /// </summary>
+ private XName rootElement;
+
+ /// <summary>
+ /// The serializer that will be used as a reflection engine to extract
+ /// the OAuth message properties out of their containing <see cref="IProtocolMessage"/>
+ /// objects.
+ /// </summary>
+ private DataContractSerializer serializer;
+
+ /// <summary>
+ /// Initializes a new instance of the ProtocolMessageSerializer class.
+ /// </summary>
+ internal ProtocolMessageSerializer() {
+ this.serializer = new DataContractSerializer(
+ typeof(T), this.RootElement.LocalName, this.RootElement.NamespaceName);
+ }
+
+ /// <summary>
+ /// Gets the XML element that is used to surround all the XML values from the dictionary.
+ /// </summary>
+ private XName RootElement {
+ get {
+ if (this.rootElement == null) {
+ DataContractAttribute attribute = typeof(T).GetCustomAttributes(typeof(DataContractAttribute), false).OfType<DataContractAttribute>().Single();
+ this.rootElement = XName.Get("root", attribute.Namespace);
+ }
+
+ return this.rootElement;
+ }
+ }
+
+ /// <summary>
+ /// Reads the data from a message instance and returns a series of name=value pairs for the fields that must be included in the message.
+ /// </summary>
+ /// <param name="message">The message to be serialized.</param>
+ /// <returns>The dictionary of values to send for the message.</returns>
+ internal IDictionary<string, string> Serialize(T message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ var fields = new Dictionary<string, string>(StringComparer.Ordinal);
+ this.Serialize(fields, message);
+ return fields;
+ }
+
+ /// <summary>
+ /// Saves the [DataMember] properties of a message to an existing dictionary.
+ /// </summary>
+ /// <param name="fields">The dictionary to save values to.</param>
+ /// <param name="message">The message to pull values from.</param>
+ internal void Serialize(IDictionary<string, string> fields, T message) {
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ message.EnsureValidMessage();
+ using (XmlWriter writer = DictionaryXmlWriter.Create(fields)) {
+ this.serializer.WriteObjectContent(writer, message);
+ }
+ }
+
+ /// <summary>
+ /// Reads name=value pairs into an OAuth message.
+ /// </summary>
+ /// <param name="fields">The name=value pairs that were read in from the transport.</param>
+ /// <returns>The instantiated and initialized <see cref="IProtocolMessage"/> instance.</returns>
+ internal T Deserialize(IDictionary<string, string> fields) {
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ var reader = DictionaryXmlReader.Create(this.RootElement, fields);
+ T result;
+ try {
+ result = (T)this.serializer.ReadObject(reader, false);
+ } catch (SerializationException ex) {
+ // Missing required fields is one cause of this exception.
+ throw new ProtocolException(Strings.InvalidIncomingMessage, ex);
+ }
+ result.EnsureValidMessage();
+ return result;
+ }
+ }
+}