//-----------------------------------------------------------------------
//
// Copyright (c) Andrew Arnott. All rights reserved.
//
//-----------------------------------------------------------------------
namespace DotNetOAuth.Messaging {
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using DotNetOAuth.ChannelElements;
using DotNetOAuth.Messaging.Reflection;
///
/// A grab-bag of utility methods useful for the channel stack of the protocol.
///
internal static class MessagingUtilities {
///
/// Adds a set of HTTP headers to an instance,
/// taking care to set some headers to the appropriate properties of
///
///
/// The headers to add.
/// The instance to set the appropriate values to.
internal static void ApplyHeadersToResponse(WebHeaderCollection headers, HttpResponse response) {
if (headers == null) {
throw new ArgumentNullException("headers");
}
if (response == null) {
throw new ArgumentNullException("response");
}
foreach (string headerName in headers) {
switch (headerName) {
case "Content-Type":
response.ContentType = headers[HttpResponseHeader.ContentType];
break;
// Add more special cases here as necessary.
default:
response.AddHeader(headerName, headers[headerName]);
break;
}
}
}
///
/// Copies the contents of one stream to another.
///
/// The stream to copy from, at the position where copying should begin.
/// The stream to copy to, at the position where bytes should be written.
///
/// Copying begins at the streams' current positions.
/// The positions are NOT reset after copying is complete.
///
internal static void CopyTo(this Stream copyFrom, Stream copyTo) {
if (copyFrom == null) {
throw new ArgumentNullException("copyFrom");
}
if (copyTo == null) {
throw new ArgumentNullException("copyTo");
}
if (!copyFrom.CanRead) {
throw new ArgumentException(MessagingStrings.StreamUnreadable, "copyFrom");
}
if (!copyTo.CanWrite) {
throw new ArgumentException(MessagingStrings.StreamUnwritable, "copyTo");
}
byte[] buffer = new byte[1024];
int readBytes;
while ((readBytes = copyFrom.Read(buffer, 0, 1024)) > 0) {
copyTo.Write(buffer, 0, readBytes);
}
}
///
/// Concatenates a list of name-value pairs as key=value&key=value,
/// taking care to properly encode each key and value for URL
/// transmission. No ? is prefixed to the string.
///
/// The dictionary of key/values to read from.
/// The formulated querystring style string.
internal static string CreateQueryString(IDictionary args) {
if (args == null) {
throw new ArgumentNullException("args");
}
if (args.Count == 0) {
return string.Empty;
}
StringBuilder sb = new StringBuilder(args.Count * 10);
foreach (var p in args) {
sb.Append(HttpUtility.UrlEncode(p.Key));
sb.Append('=');
sb.Append(HttpUtility.UrlEncode(p.Value));
sb.Append('&');
}
sb.Length--; // remove trailing &
return sb.ToString();
}
///
/// Adds a set of name-value pairs to the end of a given URL
/// as part of the querystring piece. Prefixes a ? or & before
/// first element as necessary.
///
/// The UriBuilder to add arguments to.
///
/// The arguments to add to the query.
/// If null, is not changed.
///
internal static void AppendQueryArgs(UriBuilder builder, IDictionary args) {
if (builder == null) {
throw new ArgumentNullException("builder");
}
if (args != null && args.Count > 0) {
StringBuilder sb = new StringBuilder(50 + (args.Count * 10));
if (!string.IsNullOrEmpty(builder.Query)) {
sb.Append(builder.Query.Substring(1));
sb.Append('&');
}
sb.Append(CreateQueryString(args));
builder.Query = sb.ToString();
}
}
///
/// Gets the original request URL, as seen from the browser before any URL rewrites on the server if any.
/// Cookieless session directory (if applicable) is also included.
///
/// The URL in the user agent's Location bar.
internal static Uri GetRequestUrlFromContext() {
HttpContext context = HttpContext.Current;
if (context == null) {
throw new InvalidOperationException(MessagingStrings.CurrentHttpContextRequired);
}
// We use Request.Url for the full path to the server, and modify it
// with Request.RawUrl to capture both the cookieless session "directory" if it exists
// and the original path in case URL rewriting is going on. We don't want to be
// fooled by URL rewriting because we're comparing the actual URL with what's in
// the return_to parameter in some cases.
// Response.ApplyAppPathModifier(builder.Path) would have worked for the cookieless
// session, but not the URL rewriting problem.
return new Uri(context.Request.Url, context.Request.RawUrl);
}
///
/// Extracts the recipient from an HttpRequestInfo.
///
/// The request to get recipient information from.
/// The recipient.
internal static MessageReceivingEndpoint GetRecipient(this HttpRequestInfo request) {
return new MessageReceivingEndpoint(request.Url, request.HttpMethod == "GET" ? HttpDeliveryMethod.GetRequest : HttpDeliveryMethod.PostRequest);
}
///
/// Copies some extra parameters into a message.
///
/// The message to copy the extra data into.
/// The extra data to copy into the message. May be null to do nothing.
internal static void AddNonOAuthParameters(this IProtocolMessage message, IDictionary extraParameters) {
if (message == null) {
throw new ArgumentNullException("message");
}
if (extraParameters != null) {
MessageDictionary messageDictionary = new MessageDictionary(message);
foreach (var pair in extraParameters) {
messageDictionary.Add(pair);
}
}
}
///
/// Converts a to an IDictionary<string, string>.
///
/// The NameValueCollection to convert. May be null.
/// The generated dictionary, or null if is null.
internal static Dictionary ToDictionary(this NameValueCollection nvc) {
if (nvc == null) {
return null;
}
var dictionary = new Dictionary();
foreach (string key in nvc) {
dictionary.Add(key, nvc[key]);
}
return dictionary;
}
///
/// Sorts the elements of a sequence in ascending order by using a specified comparer.
///
/// The type of the elements of source.
/// The type of the key returned by keySelector.
/// A sequence of values to order.
/// A function to extract a key from an element.
/// A comparison function to compare keys.
/// An System.Linq.IOrderedEnumerable<TElement> whose elements are sorted according to a key.
internal static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, Comparison comparer) {
return System.Linq.Enumerable.OrderBy(source, keySelector, new ComparisonHelper(comparer));
}
///
/// A class to convert a into an .
///
/// The type of objects being compared.
private class ComparisonHelper : IComparer {
///
/// The comparison method to use.
///
private Comparison comparison;
///
/// Initializes a new instance of the ComparisonHelper class.
///
/// The comparison method to use.
internal ComparisonHelper(Comparison comparison) {
if (comparison == null) {
throw new ArgumentNullException("comparison");
}
this.comparison = comparison;
}
#region IComparer Members
///
/// Compares two instances of .
///
/// The first object to compare.
/// The second object to compare.
/// Any of -1, 0, or 1 according to standard comparison rules.
public int Compare(T x, T y) {
return this.comparison(x, y);
}
#endregion
}
}
}