summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj4
-rw-r--r--src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs50
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs76
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/MultiPartPostPartTests.cs99
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs9
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/TestDirectedMessage.cs4
-rw-r--r--src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs7
-rw-r--r--src/DotNetOpenAuth.sln10
-rw-r--r--src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs1
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj1
-rw-r--r--src/DotNetOpenAuth/GlobalSuppressions.cs1
-rw-r--r--src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs3
-rw-r--r--src/DotNetOpenAuth/Messaging/Channel.cs54
-rw-r--r--src/DotNetOpenAuth/Messaging/EmptyDictionary.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/EmptyList.cs4
-rw-r--r--src/DotNetOpenAuth/Messaging/EnumerableCache.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs7
-rw-r--r--src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs5
-rw-r--r--src/DotNetOpenAuth/Messaging/IMessageWithBinaryData.cs152
-rw-r--r--src/DotNetOpenAuth/Messaging/ITamperProtectionChannelBindingElement.cs100
-rw-r--r--src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs1
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs9
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingStrings.resx3
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs95
-rw-r--r--src/DotNetOpenAuth/Messaging/MultipartPostPart.cs10
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs3
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs39
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs22
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementChain.cs4
-rw-r--r--src/DotNetOpenAuth/OAuth/ConsumerBase.cs22
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs27
-rw-r--r--src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs9
-rw-r--r--src/DotNetOpenAuth/OAuth/OAuthStrings.resx3
-rw-r--r--src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs8
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs1
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/XriIdentifier.cs4
-rw-r--r--src/DotNetOpenAuth/Reporting.cs6
-rw-r--r--src/DotNetOpenAuth/Yadis/Yadis.cs2
-rw-r--r--src/version.txt2
49 files changed, 710 insertions, 176 deletions
diff --git a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj
index daf6327..3f76f78 100644
--- a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj
+++ b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj
@@ -80,7 +80,9 @@
<Reference Include="Microsoft.Build.Utilities.v3.5">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
- <Reference Include="Microsoft.Contracts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.Contracts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=736440c9b414ea16, processorArchitecture=MSIL">
+ <Private>False</Private>
+ </Reference>
<Reference Include="Microsoft.Web.Administration, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>$(SystemRoot)\System32\inetsrv\Microsoft.Web.Administration.dll</HintPath>
diff --git a/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs b/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs
index 48b5d5c..23db9a6 100644
--- a/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs
+++ b/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
using System.Linq;
using System.Text;
-using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
-using System.IO;
+using Microsoft.Build.Utilities;
namespace DotNetOpenAuth.BuildTasks {
public class GetBuildVersion : Task {
@@ -52,18 +54,54 @@ namespace DotNetOpenAuth.BuildTasks {
return string.Empty;
}
- string headContent = string.Empty;
+ string commitId = string.Empty;
+
+ // First try asking Git for the HEAD commit id
+ try {
+ string cmdPath = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe");
+ ProcessStartInfo psi = new ProcessStartInfo(cmdPath, "/c git rev-parse HEAD");
+ psi.WindowStyle = ProcessWindowStyle.Hidden;
+ psi.RedirectStandardOutput = true;
+ psi.UseShellExecute = false;
+ Process git = Process.Start(psi);
+ commitId = git.StandardOutput.ReadLine();
+ git.WaitForExit();
+ if (git.ExitCode != 0) {
+ commitId = string.Empty;
+ }
+ if (commitId != null) {
+ commitId = commitId.Trim();
+ if (commitId.Length == 40) {
+ return commitId;
+ }
+ }
+ } catch (InvalidOperationException) {
+ } catch (Win32Exception) {
+ }
+
+ // Failing being able to use the git command to figure out the HEAD commit ID, try the filesystem directly.
try {
- headContent = File.ReadAllText(Path.Combine(this.GitRepoRoot, @".git/HEAD")).Trim();
+ string headContent = File.ReadAllText(Path.Combine(this.GitRepoRoot, @".git/HEAD")).Trim();
if (headContent.StartsWith("ref:", StringComparison.Ordinal)) {
string refName = headContent.Substring(5).Trim();
- headContent = File.ReadAllText(Path.Combine(this.GitRepoRoot, @".git/" + refName)).Trim();
+ string refPath = Path.Combine(this.GitRepoRoot, ".git/" + refName);
+ if (File.Exists(refPath)) {
+ commitId = File.ReadAllText(refPath).Trim();
+ } else {
+ string packedRefPath = Path.Combine(this.GitRepoRoot, ".git/packed-refs");
+ string matchingLine = File.ReadAllLines(packedRefPath).FirstOrDefault(line => line.EndsWith(refName));
+ if (matchingLine != null) {
+ commitId = matchingLine.Substring(0, matchingLine.IndexOf(' '));
+ }
+ }
+ } else {
+ commitId = headContent;
}
} catch (FileNotFoundException) {
} catch (DirectoryNotFoundException) {
}
- return headContent.Trim();
+ return commitId.Trim();
}
private Version ReadVersionFromFile() {
diff --git a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs
index ce0928e..8a8ae25 100644
--- a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs
@@ -11,8 +11,10 @@ namespace DotNetOpenAuth.Test.Messaging
using System.Collections.Specialized;
using System.IO;
using System.Net;
+ using System.Text.RegularExpressions;
using System.Web;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Test.Mocks;
using NUnit.Framework;
[TestFixture]
@@ -139,5 +141,79 @@ namespace DotNetOpenAuth.Test.Messaging
Assert.AreEqual("%C2%80", MessagingUtilities.EscapeUriDataStringRfc3986("\u0080"));
Assert.AreEqual("%E3%80%81", MessagingUtilities.EscapeUriDataStringRfc3986("\u3001"));
}
+
+ /// <summary>
+ /// Verifies the overall format of the multipart POST is correct.
+ /// </summary>
+ [TestCase]
+ public void PostMultipart() {
+ var httpHandler = new TestWebRequestHandler();
+ bool callbackTriggered = false;
+ httpHandler.Callback = req => {
+ Match m = Regex.Match(req.ContentType, "multipart/form-data; boundary=(.+)");
+ Assert.IsTrue(m.Success, "Content-Type HTTP header not set correctly.");
+ string boundary = m.Groups[1].Value;
+ string expectedEntity = "--{0}\r\nContent-Disposition: form-data; name=\"a\"\r\n\r\nb\r\n--{0}--\r\n";
+ expectedEntity = string.Format(expectedEntity, boundary);
+ string actualEntity = httpHandler.RequestEntityAsString;
+ Assert.AreEqual(expectedEntity, actualEntity);
+ callbackTriggered = true;
+ Assert.AreEqual(req.ContentLength, actualEntity.Length);
+ IncomingWebResponse resp = new CachedDirectWebResponse();
+ return resp;
+ };
+ var request = (HttpWebRequest)WebRequest.Create("http://someserver");
+ var parts = new[] {
+ MultipartPostPart.CreateFormPart("a", "b"),
+ };
+ request.PostMultipart(httpHandler, parts);
+ Assert.IsTrue(callbackTriggered);
+ }
+
+ /// <summary>
+ /// Verifies proper behavior of GetHttpVerb
+ /// </summary>
+ [TestCase]
+ public void GetHttpVerbTest() {
+ Assert.AreEqual("GET", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.GetRequest));
+ Assert.AreEqual("POST", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PostRequest));
+ Assert.AreEqual("HEAD", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.HeadRequest));
+ Assert.AreEqual("DELETE", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.DeleteRequest));
+ Assert.AreEqual("PUT", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PutRequest));
+
+ Assert.AreEqual("GET", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest));
+ Assert.AreEqual("POST", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest));
+ Assert.AreEqual("HEAD", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.HeadRequest | HttpDeliveryMethods.AuthorizationHeaderRequest));
+ Assert.AreEqual("DELETE", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.DeleteRequest | HttpDeliveryMethods.AuthorizationHeaderRequest));
+ Assert.AreEqual("PUT", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PutRequest | HttpDeliveryMethods.AuthorizationHeaderRequest));
+ }
+
+ /// <summary>
+ /// Verifies proper behavior of GetHttpVerb on invalid input.
+ /// </summary>
+ [TestCase, ExpectedException(typeof(ArgumentException))]
+ public void GetHttpVerbOutOfRangeTest() {
+ MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PutRequest | HttpDeliveryMethods.PostRequest);
+ }
+
+ /// <summary>
+ /// Verifies proper behavior of GetHttpDeliveryMethod
+ /// </summary>
+ [TestCase]
+ public void GetHttpDeliveryMethodTest() {
+ Assert.AreEqual(HttpDeliveryMethods.GetRequest, MessagingUtilities.GetHttpDeliveryMethod("GET"));
+ Assert.AreEqual(HttpDeliveryMethods.PostRequest, MessagingUtilities.GetHttpDeliveryMethod("POST"));
+ Assert.AreEqual(HttpDeliveryMethods.HeadRequest, MessagingUtilities.GetHttpDeliveryMethod("HEAD"));
+ Assert.AreEqual(HttpDeliveryMethods.PutRequest, MessagingUtilities.GetHttpDeliveryMethod("PUT"));
+ Assert.AreEqual(HttpDeliveryMethods.DeleteRequest, MessagingUtilities.GetHttpDeliveryMethod("DELETE"));
+ }
+
+ /// <summary>
+ /// Verifies proper behavior of GetHttpDeliveryMethod for an unexpected input
+ /// </summary>
+ [TestCase, ExpectedException(typeof(ArgumentException))]
+ public void GetHttpDeliveryMethodOutOfRangeTest() {
+ MessagingUtilities.GetHttpDeliveryMethod("UNRECOGNIZED");
+ }
}
}
diff --git a/src/DotNetOpenAuth.Test/Messaging/MultiPartPostPartTests.cs b/src/DotNetOpenAuth.Test/Messaging/MultiPartPostPartTests.cs
deleted file mode 100644
index a293895..0000000
--- a/src/DotNetOpenAuth.Test/Messaging/MultiPartPostPartTests.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="MultipartPostPartTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.Messaging {
- using System.CodeDom.Compiler;
- using System.Collections.Generic;
- using System.Diagnostics.Contracts;
- using System.IO;
- using System.Net;
- using DotNetOpenAuth.Messaging;
- using NUnit.Framework;
-
- [TestFixture]
- public class MultipartPostPartTests : TestBase {
- /// <summary>
- /// Verifies that the Length property matches the length actually serialized.
- /// </summary>
- [TestCase]
- public void FormDataSerializeMatchesLength() {
- var part = MultipartPostPart.CreateFormPart("a", "b");
- VerifyLength(part);
- }
-
- /// <summary>
- /// Verifies that the length property matches the length actually serialized.
- /// </summary>
- [TestCase]
- public void FileSerializeMatchesLength() {
- using (TempFileCollection tfc = new TempFileCollection()) {
- string file = tfc.AddExtension(".txt");
- File.WriteAllText(file, "sometext");
- var part = MultipartPostPart.CreateFormFilePart("someformname", file, "text/plain");
- VerifyLength(part);
- }
- }
-
- /// <summary>
- /// Verifies MultiPartPost sends the right number of bytes.
- /// </summary>
- [TestCase]
- public void MultiPartPostAscii() {
- using (TempFileCollection tfc = new TempFileCollection()) {
- string file = tfc.AddExtension("txt");
- File.WriteAllText(file, "sometext");
- this.VerifyFullPost(new List<MultipartPostPart> {
- MultipartPostPart.CreateFormPart("a", "b"),
- MultipartPostPart.CreateFormFilePart("SomeFormField", file, "text/plain"),
- });
- }
- }
-
- /// <summary>
- /// Verifies MultiPartPost sends the right number of bytes.
- /// </summary>
- [TestCase]
- public void MultiPartPostMultiByteCharacters() {
- using (TempFileCollection tfc = new TempFileCollection()) {
- string file = tfc.AddExtension("txt");
- File.WriteAllText(file, "\x1020\x818");
- this.VerifyFullPost(new List<MultipartPostPart> {
- MultipartPostPart.CreateFormPart("a", "\x987"),
- MultipartPostPart.CreateFormFilePart("SomeFormField", file, "text/plain"),
- });
- }
- }
-
- private static void VerifyLength(MultipartPostPart part) {
- Contract.Requires(part != null);
-
- var expectedLength = part.Length;
- var ms = new MemoryStream();
- var sw = new StreamWriter(ms);
- part.Serialize(sw);
- sw.Flush();
- var actualLength = ms.Length;
- Assert.AreEqual(expectedLength, actualLength);
- }
-
- private void VerifyFullPost(List<MultipartPostPart> parts) {
- var request = (HttpWebRequest)WebRequest.Create("http://localhost");
- var handler = new Mocks.TestWebRequestHandler();
- bool posted = false;
- handler.Callback = req => {
- foreach (string header in req.Headers) {
- TestUtilities.TestLogger.InfoFormat("{0}: {1}", header, req.Headers[header]);
- }
- TestUtilities.TestLogger.InfoFormat(handler.RequestEntityAsString);
- Assert.AreEqual(req.ContentLength, handler.RequestEntityStream.Length);
- posted = true;
- return null;
- };
- request.PostMultipart(handler, parts);
- Assert.IsTrue(posted, "HTTP POST never sent.");
- }
- }
-}
diff --git a/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs b/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs
index a293895..08524b2 100644
--- a/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs
@@ -38,6 +38,15 @@ namespace DotNetOpenAuth.Test.Messaging {
}
/// <summary>
+ /// Verifies file multiparts identify themselves as files and not merely form-data.
+ /// </summary>
+ [TestCase]
+ public void FilePartAsFile() {
+ var part = MultipartPostPart.CreateFormFilePart("somename", "somefile", "plain/text", new MemoryStream());
+ Assert.AreEqual("file", part.ContentDisposition);
+ }
+
+ /// <summary>
/// Verifies MultiPartPost sends the right number of bytes.
/// </summary>
[TestCase]
diff --git a/src/DotNetOpenAuth.Test/Mocks/TestDirectedMessage.cs b/src/DotNetOpenAuth.Test/Mocks/TestDirectedMessage.cs
index 342a303..1b3c2b2 100644
--- a/src/DotNetOpenAuth.Test/Mocks/TestDirectedMessage.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/TestDirectedMessage.cs
@@ -11,11 +11,11 @@ namespace DotNetOpenAuth.Test.Mocks {
internal class TestDirectedMessage : TestMessage, IDirectedProtocolMessage {
internal TestDirectedMessage() {
- this.HttpMethods = HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest;
+ this.HttpMethods = HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.HeadRequest;
}
internal TestDirectedMessage(MessageTransport transport) : base(transport) {
- this.HttpMethods = HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest;
+ this.HttpMethods = HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.HeadRequest;
}
#region IDirectedProtocolMessage Members
diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs
index 5f068d1..c7b9f89 100644
--- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs
+++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs
@@ -227,6 +227,11 @@ namespace DotNetOpenAuth.Test.ChannelElements {
this.ParameterizedRequestTest(HttpDeliveryMethods.PostRequest);
}
+ [TestCase]
+ public void RequestUsingHead() {
+ this.ParameterizedRequestTest(HttpDeliveryMethods.HeadRequest);
+ }
+
/// <summary>
/// Verifies that messages asking for special HTTP status codes get them.
/// </summary>
@@ -323,7 +328,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
this.webRequestHandler.Callback = (req) => {
Assert.IsNotNull(req);
HttpRequestInfo reqInfo = ConvertToRequestInfo(req, this.webRequestHandler.RequestEntityStream);
- Assert.AreEqual(scheme == HttpDeliveryMethods.PostRequest ? "POST" : "GET", reqInfo.HttpMethod);
+ Assert.AreEqual(MessagingUtilities.GetHttpVerb(scheme), reqInfo.HttpMethod);
var incomingMessage = this.channel.ReadFromRequest(reqInfo) as TestMessage;
Assert.IsNotNull(incomingMessage);
Assert.AreEqual(request.Age, incomingMessage.Age);
diff --git a/src/DotNetOpenAuth.sln b/src/DotNetOpenAuth.sln
index d55996c..55d43e8 100644
--- a/src/DotNetOpenAuth.sln
+++ b/src/DotNetOpenAuth.sln
@@ -185,6 +185,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdWebRingSsoRelyingPart
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdWebRingSsoProvider", "..\samples\OpenIdWebRingSsoProvider\OpenIdWebRingSsoProvider.csproj", "{0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}"
EndProject
+Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "OpenIdRelyingPartyWebFormsVB", "..\samples\OpenIdRelyingPartyWebFormsVB\OpenIdRelyingPartyWebFormsVB.vbproj", "{F289B925-4307-4BEC-B411-885CE70E3379}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
CodeAnalysis|Any CPU = CodeAnalysis|Any CPU
@@ -315,6 +317,12 @@ Global
{0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F289B925-4307-4BEC-B411-885CE70E3379}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
+ {F289B925-4307-4BEC-B411-885CE70E3379}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
+ {F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -332,6 +340,8 @@ Global
{BBACD972-014D-478F-9B07-56B9E1D4CC73} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}
{B64A1E7E-6A15-4B91-AF13-7D48F7DA5942} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}
{0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}
+ {6EB90284-BD15-461C-BBF2-131CF55F7C8B} = {8A5CEDB9-7F8A-4BE2-A1B9-97130F453277}
+ {F289B925-4307-4BEC-B411-885CE70E3379} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}
{6EC36418-DBC5-4AD1-A402-413604AA7A08} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6}
{9ADBE36D-9960-48F6-82E9-B4AC559E9AC3} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6}
{7ADCCD5C-AC2B-4340-9410-FE3A31A48191} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6}
diff --git a/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs b/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs
index 5ac88da..0335af5 100644
--- a/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs
+++ b/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs
@@ -126,6 +126,7 @@ namespace DotNetOpenAuth.Configuration {
/// be present.
/// </remarks>
private static T CreateInstanceFromXaml(Stream xaml) {
+ Contract.Ensures(Contract.Result<T>() != null);
return (T)XamlReader.Load(xaml);
}
}
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index ee271f3..3bfecae 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -327,6 +327,7 @@ http://opensource.org/licenses/ms-pl.html
<Compile Include="OAuth\ConsumerSecuritySettings.cs" />
<Compile Include="OAuth\DesktopConsumer.cs" />
<Compile Include="GlobalSuppressions.cs" />
+ <Compile Include="Messaging\IMessageWithBinaryData.cs" />
<Compile Include="OAuth\Messages\ITokenSecretContainingMessage.cs" />
<Compile Include="Messaging\ChannelEventArgs.cs" />
<Compile Include="Messaging\ITamperProtectionChannelBindingElement.cs" />
diff --git a/src/DotNetOpenAuth/GlobalSuppressions.cs b/src/DotNetOpenAuth/GlobalSuppressions.cs
index a3343ba..e436846 100644
--- a/src/DotNetOpenAuth/GlobalSuppressions.cs
+++ b/src/DotNetOpenAuth/GlobalSuppressions.cs
@@ -56,3 +56,4 @@
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.OnOutgoingResponse(DotNetOpenAuth.OpenId.Provider.IAuthenticationRequest)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.OnIncomingRequest(DotNetOpenAuth.OpenId.Provider.IRequest)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.ApplySecuritySettings(DotNetOpenAuth.OpenId.Provider.ProviderSecuritySettings)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly")]
diff --git a/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs b/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs
index b37c93a..dd34d90 100644
--- a/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs
@@ -153,10 +153,13 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>The seekable Stream instance that contains a copy of what was returned in the HTTP response.</returns>
private static MemoryStream CacheNetworkStreamAndClose(HttpWebResponse response, int maximumBytesToRead) {
Contract.Requires<ArgumentNullException>(response != null);
+ Contract.Ensures(Contract.Result<MemoryStream>() != null);
// Now read and cache the network stream
Stream networkStream = response.GetResponseStream();
MemoryStream cachedStream = new MemoryStream(response.ContentLength < 0 ? 4 * 1024 : Math.Min((int)response.ContentLength, maximumBytesToRead));
+ Contract.Assume(networkStream.CanRead, "HttpWebResponse.GetResponseStream() always returns a readable stream."); // CC missing
+ Contract.Assume(cachedStream.CanWrite, "This is a MemoryStream -- it's always writable."); // CC missing
networkStream.CopyTo(cachedStream);
cachedStream.Seek(0, SeekOrigin.Begin);
diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs
index ef6486a..1bddf02 100644
--- a/src/DotNetOpenAuth/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth/Messaging/Channel.cs
@@ -580,7 +580,15 @@ namespace DotNetOpenAuth.Messaging {
fields = request.QueryStringBeforeRewriting.ToDictionary();
}
- return (IDirectedProtocolMessage)this.Receive(fields, request.GetRecipient());
+ MessageReceivingEndpoint recipient;
+ try {
+ recipient = request.GetRecipient();
+ } catch (ArgumentException ex) {
+ Logger.Messaging.WarnFormat("Unrecognized HTTP request: " + ex.ToString());
+ return null;
+ }
+
+ return (IDirectedProtocolMessage)this.Receive(fields, recipient);
}
/// <summary>
@@ -819,6 +827,24 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Prepares to send a request to the Service Provider as the query string in a HEAD request.
+ /// </summary>
+ /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
+ /// <returns>The web request ready to send.</returns>
+ /// <remarks>
+ /// This method is simply a standard HTTP HEAD request with the message parts serialized to the query string.
+ /// This method satisfies OAuth 1.0 section 5.2, item #3.
+ /// </remarks>
+ protected virtual HttpWebRequest InitializeRequestAsHead(IDirectedProtocolMessage requestMessage) {
+ Contract.Requires<ArgumentNullException>(requestMessage != null);
+ Contract.Requires<ArgumentException>(requestMessage.Recipient != null, MessagingStrings.DirectedMessageMissingRecipient);
+
+ HttpWebRequest request = this.InitializeRequestAsGet(requestMessage);
+ request.Method = "HEAD";
+ return request;
+ }
+
+ /// <summary>
/// Prepares to send a request to the Service Provider as the payload of a POST request.
/// </summary>
/// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
@@ -838,7 +864,18 @@ namespace DotNetOpenAuth.Messaging {
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient);
httpRequest.CachePolicy = this.CachePolicy;
httpRequest.Method = "POST";
- this.SendParametersInEntity(httpRequest, fields);
+
+ var requestMessageWithBinaryData = requestMessage as IMessageWithBinaryData;
+ if (requestMessageWithBinaryData != null && requestMessageWithBinaryData.SendAsMultipart) {
+ var multiPartFields = new List<MultipartPostPart>(requestMessageWithBinaryData.BinaryData);
+
+ // When sending multi-part, all data gets send as multi-part -- even the non-binary data.
+ multiPartFields.AddRange(fields.Select(field => MultipartPostPart.CreateFormPart(field.Key, field.Value)));
+ this.SendParametersInEntityAsMultipart(httpRequest, multiPartFields);
+ } else {
+ ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || requestMessageWithBinaryData.BinaryData.Count == 0, MessagingStrings.BinaryDataRequiresMultipart);
+ this.SendParametersInEntity(httpRequest, fields);
+ }
return httpRequest;
}
@@ -914,6 +951,19 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Sends the given parameters in the entity stream of an HTTP request in multi-part format.
+ /// </summary>
+ /// <param name="httpRequest">The HTTP request.</param>
+ /// <param name="fields">The parameters to send.</param>
+ /// <remarks>
+ /// This method calls <see cref="HttpWebRequest.GetRequestStream()"/> and closes
+ /// the request stream, but does not call <see cref="HttpWebRequest.GetResponse"/>.
+ /// </remarks>
+ protected void SendParametersInEntityAsMultipart(HttpWebRequest httpRequest, IEnumerable<MultipartPostPart> fields) {
+ httpRequest.PostMultipartNoGetResponse(this.WebRequestHandler, fields);
+ }
+
+ /// <summary>
/// Verifies the integrity and applicability of an incoming message.
/// </summary>
/// <param name="message">The message just received.</param>
diff --git a/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs b/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs
index caf292a..9db5169 100644
--- a/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs
+++ b/src/DotNetOpenAuth/Messaging/EmptyDictionary.cs
@@ -154,7 +154,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="T:System.NotSupportedException">
/// The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
/// </exception>
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts generates the code FxCop is complaining about.")]
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")]
public void Add(KeyValuePair<TKey, TValue> item) {
throw new NotSupportedException();
}
diff --git a/src/DotNetOpenAuth/Messaging/EmptyList.cs b/src/DotNetOpenAuth/Messaging/EmptyList.cs
index ba918a4..68cdabd 100644
--- a/src/DotNetOpenAuth/Messaging/EmptyList.cs
+++ b/src/DotNetOpenAuth/Messaging/EmptyList.cs
@@ -99,7 +99,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="T:System.NotSupportedException">
/// The <see cref="T:System.Collections.Generic.IList`1"/> is read-only.
/// </exception>
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts generates the code FxCop is complaining about.")]
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")]
public void RemoveAt(int index) {
throw new ArgumentOutOfRangeException("index");
}
@@ -115,7 +115,7 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="T:System.NotSupportedException">
/// The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
/// </exception>
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts generates the code FxCop is complaining about.")]
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")]
public void Add(T item) {
throw new NotSupportedException();
}
diff --git a/src/DotNetOpenAuth/Messaging/EnumerableCache.cs b/src/DotNetOpenAuth/Messaging/EnumerableCache.cs
index 71825bc..29ec533 100644
--- a/src/DotNetOpenAuth/Messaging/EnumerableCache.cs
+++ b/src/DotNetOpenAuth/Messaging/EnumerableCache.cs
@@ -35,6 +35,8 @@ namespace DotNetOpenAuth.Messaging {
/// to avoid double-caching.</para>
/// </remarks>
public static IEnumerable<T> CacheGeneratedResults<T>(this IEnumerable<T> sequence) {
+ Contract.Requires<ArgumentNullException>(sequence != null);
+
// Don't create a cache for types that don't need it.
if (sequence is IList<T> ||
sequence is ICollection<T> ||
diff --git a/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs b/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs
index 17c8f7a..1443fff 100644
--- a/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs
+++ b/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs
@@ -46,8 +46,13 @@ namespace DotNetOpenAuth.Messaging {
DeleteRequest = 0x10,
/// <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>
+ HeadRequest = 0x20,
+
+ /// <summary>
/// The flags that control HTTP verbs.
/// </summary>
- HttpVerbMask = PostRequest | GetRequest | PutRequest | DeleteRequest,
+ HttpVerbMask = PostRequest | GetRequest | PutRequest | DeleteRequest | HeadRequest,
}
}
diff --git a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs
index af03ba9..3420de9 100644
--- a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs
+++ b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs
@@ -187,6 +187,8 @@ namespace DotNetOpenAuth.Messaging {
/// </remarks>
IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request) {
Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Ensures(Contract.Result<IncomingWebResponse>() != null);
+ Contract.Ensures(Contract.Result<IncomingWebResponse>().ResponseStream != null);
throw new System.NotImplementedException();
}
@@ -209,6 +211,9 @@ namespace DotNetOpenAuth.Messaging {
IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request, DirectWebRequestOptions options) {
Contract.Requires<ArgumentNullException>(request != null);
Contract.Requires<NotSupportedException>(((IDirectWebRequestHandler)this).CanSupport(options), MessagingStrings.DirectWebRequestOptionsNotSupported);
+ Contract.Ensures(Contract.Result<IncomingWebResponse>() != null);
+ Contract.Ensures(Contract.Result<IncomingWebResponse>().ResponseStream != null);
+
////ErrorUtilities.VerifySupported(((IDirectWebRequestHandler)this).CanSupport(options), string.Format(MessagingStrings.DirectWebRequestOptionsNotSupported, options, this.GetType().Name));
throw new System.NotImplementedException();
}
diff --git a/src/DotNetOpenAuth/Messaging/IMessageWithBinaryData.cs b/src/DotNetOpenAuth/Messaging/IMessageWithBinaryData.cs
new file mode 100644
index 0000000..f411cf5
--- /dev/null
+++ b/src/DotNetOpenAuth/Messaging/IMessageWithBinaryData.cs
@@ -0,0 +1,152 @@
+//-----------------------------------------------------------------------
+// <copyright file="IMessageWithBinaryData.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// The interface that classes must implement to be serialized/deserialized
+ /// as protocol or extension messages that uses POST multi-part data for binary content.
+ /// </summary>
+ [ContractClass(typeof(IMessageWithBinaryDataContract))]
+ public interface IMessageWithBinaryData : IDirectedProtocolMessage {
+ /// <summary>
+ /// Gets the parts of the message that carry binary data.
+ /// </summary>
+ /// <value>A list of parts. Never null.</value>
+ IList<MultipartPostPart> BinaryData { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether this message should be sent as multi-part POST.
+ /// </summary>
+ bool SendAsMultipart { get; }
+ }
+
+ /// <summary>
+ /// The contract class for the <see cref="IMessageWithBinaryData"/> interface.
+ /// </summary>
+ [ContractClassFor(typeof(IMessageWithBinaryData))]
+ internal sealed class IMessageWithBinaryDataContract : IMessageWithBinaryData {
+ #region IMessageWithBinaryData Members
+
+ /// <summary>
+ /// Gets the parts of the message that carry binary data.
+ /// </summary>
+ /// <value>A list of parts. Never null.</value>
+ IList<MultipartPostPart> IMessageWithBinaryData.BinaryData {
+ get {
+ Contract.Ensures(Contract.Result<IList<MultipartPostPart>>() != null);
+ throw new NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this message should be sent as multi-part POST.
+ /// </summary>
+ bool IMessageWithBinaryData.SendAsMultipart {
+ get { throw new NotImplementedException(); }
+ }
+
+ #endregion
+
+ #region IMessage Properties
+
+ /// <summary>
+ /// Gets the version of the protocol or extension this message is prepared to implement.
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// Implementations of this interface should ensure that this property never returns null.
+ /// </remarks>
+ Version IMessage.Version {
+ get {
+ Contract.Ensures(Contract.Result<Version>() != null);
+ return default(Version); // dummy return
+ }
+ }
+
+ /// <summary>
+ /// Gets the extra, non-standard Protocol parameters included in the message.
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// Implementations of this interface should ensure that this property never returns null.
+ /// </remarks>
+ IDictionary<string, string> IMessage.ExtraData {
+ get {
+ Contract.Ensures(Contract.Result<IDictionary<string, string>>() != null);
+ return default(IDictionary<string, string>);
+ }
+ }
+
+ #endregion
+
+ #region IDirectedProtocolMessage Members
+
+ /// <summary>
+ /// Gets the preferred method of transport for the message.
+ /// </summary>
+ /// <remarks>
+ /// For indirect messages this will likely be GET+POST, which both can be simulated in the user agent:
+ /// the GET with a simple 301 Redirect, and the POST with an HTML form in the response with javascript
+ /// to automate submission.
+ /// </remarks>
+ HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets the URL of the intended receiver of this message.
+ /// </summary>
+ Uri IDirectedProtocolMessage.Recipient {
+ get { throw new NotImplementedException(); }
+ }
+
+ #endregion
+
+ #region IProtocolMessage Members
+
+ /// <summary>
+ /// Gets the level of protection this message requires.
+ /// </summary>
+ MessageProtections IProtocolMessage.RequiredProtection {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ MessageTransport IProtocolMessage.Transport {
+ get { throw new NotImplementedException(); }
+ }
+
+ #endregion
+
+ #region IMessage methods
+
+ /// <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 IMessage.EnsureValidMessage() {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/ITamperProtectionChannelBindingElement.cs b/src/DotNetOpenAuth/Messaging/ITamperProtectionChannelBindingElement.cs
index ba25c12..87ea553 100644
--- a/src/DotNetOpenAuth/Messaging/ITamperProtectionChannelBindingElement.cs
+++ b/src/DotNetOpenAuth/Messaging/ITamperProtectionChannelBindingElement.cs
@@ -6,12 +6,14 @@
namespace DotNetOpenAuth.Messaging {
using System;
+ using System.Diagnostics.Contracts;
using DotNetOpenAuth.OAuth.ChannelElements;
/// <summary>
/// An interface that must be implemented by message transforms/validators in order
/// to be included in the channel stack.
/// </summary>
+ [ContractClass(typeof(ITamperProtectionChannelBindingElementContract))]
public interface ITamperProtectionChannelBindingElement : IChannelBindingElement {
/// <summary>
/// Gets or sets the delegate that will initialize the non-serialized properties necessary on a
@@ -25,4 +27,102 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>The cloned instance.</returns>
ITamperProtectionChannelBindingElement Clone();
}
+
+ /// <summary>
+ /// Contract class for the <see cref="ITamperProtectionChannelBindingElement"/> interface.
+ /// </summary>
+ [ContractClassFor(typeof(ITamperProtectionChannelBindingElement))]
+ internal abstract class ITamperProtectionChannelBindingElementContract : ITamperProtectionChannelBindingElement {
+ #region ITamperProtectionChannelBindingElement Properties
+
+ /// <summary>
+ /// Gets or sets the delegate that will initialize the non-serialized properties necessary on a
+ /// signable message so that its signature can be correctly calculated or verified.
+ /// </summary>
+ Action<ITamperResistantOAuthMessage> ITamperProtectionChannelBindingElement.SignatureCallback {
+ get { throw new NotImplementedException(); }
+ set { throw new NotImplementedException(); }
+ }
+
+ #endregion
+
+ #region IChannelBindingElement Members
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ /// <remarks>
+ /// This property is set by the channel when it is first constructed.
+ /// </remarks>
+ Channel IChannelBindingElement.Channel {
+ get { throw new NotImplementedException(); }
+ set { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Gets the protection commonly offered (if any) by this binding element.
+ /// </summary>
+ /// <remarks>
+ /// This value is used to assist in sorting binding elements in the channel stack.
+ /// </remarks>
+ MessageProtections IChannelBindingElement.Protection {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Prepares a message for sending based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The message to prepare for sending.</param>
+ /// <returns>
+ /// The protections (if any) that this binding element applied to the message.
+ /// Null if this binding element did not even apply to this binding element.
+ /// </returns>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ MessageProtections? IChannelBindingElement.ProcessOutgoingMessage(IProtocolMessage message) {
+ Contract.Requires<ArgumentNullException>(message != null);
+ Contract.Requires<InvalidOperationException>(((IChannelBindingElement)this).Channel != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Performs any transformation on an incoming message that may be necessary and/or
+ /// validates an incoming message based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The incoming message to process.</param>
+ /// <returns>
+ /// The protections (if any) that this binding element applied to the message.
+ /// Null if this binding element did not even apply to this binding element.
+ /// </returns>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the binding element rules indicate that this message is invalid and should
+ /// NOT be processed.
+ /// </exception>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ MessageProtections? IChannelBindingElement.ProcessIncomingMessage(IProtocolMessage message) {
+ Contract.Requires<ArgumentNullException>(message != null);
+ Contract.Requires<InvalidOperationException>(((IChannelBindingElement)this).Channel != null);
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #region ITamperProtectionChannelBindingElement Methods
+
+ /// <summary>
+ /// Clones this instance.
+ /// </summary>
+ /// <returns>The cloned instance.</returns>
+ ITamperProtectionChannelBindingElement ITamperProtectionChannelBindingElement.Clone() {
+ Contract.Ensures(Contract.Result<ITamperProtectionChannelBindingElement>() != null);
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
}
diff --git a/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs b/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs
index e26a672..e39a047 100644
--- a/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs
+++ b/src/DotNetOpenAuth/Messaging/MessageReceivingEndpoint.cs
@@ -24,6 +24,7 @@ namespace DotNetOpenAuth.Messaging {
: this(new Uri(locationUri), method) {
Contract.Requires<ArgumentNullException>(locationUri != null);
Contract.Requires<ArgumentOutOfRangeException>(method != HttpDeliveryMethods.None);
+ Contract.Requires<ArgumentOutOfRangeException>((method & HttpDeliveryMethods.HttpVerbMask) != 0, MessagingStrings.GetOrPostFlagsRequired);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
index e5b725b..0bbac42 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
@@ -70,6 +70,15 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Looks up a localized string similar to Unable to send all message data because some of it requires multi-part POST, but IMessageWithBinaryData.SendAsMultipart was false..
+ /// </summary>
+ internal static string BinaryDataRequiresMultipart {
+ get {
+ return ResourceManager.GetString("BinaryDataRequiresMultipart", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to HttpContext.Current is null. There must be an ASP.NET request in process for this operation to succeed..
/// </summary>
internal static string CurrentHttpContextRequired {
diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
index 3d4e317..34385d4 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
+++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
@@ -297,4 +297,7 @@
<data name="StreamMustHaveKnownLength" xml:space="preserve">
<value>The stream must have a known length.</value>
</data>
+ <data name="BinaryDataRequiresMultipart" xml:space="preserve">
+ <value>Unable to send all message data because some of it requires multi-part POST, but IMessageWithBinaryData.SendAsMultipart was false.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
index be5927f..c29ec8c 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
@@ -141,7 +141,7 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
- /// Sends an multipart HTTP POST request (useful for posting files).
+ /// Sends a multipart HTTP POST request (useful for posting files).
/// </summary>
/// <param name="request">The HTTP request.</param>
/// <param name="requestHandler">The request handler.</param>
@@ -152,14 +152,60 @@ namespace DotNetOpenAuth.Messaging {
Contract.Requires<ArgumentNullException>(requestHandler != null);
Contract.Requires<ArgumentNullException>(parts != null);
+ PostMultipartNoGetResponse(request, requestHandler, parts);
+ return requestHandler.GetResponse(request);
+ }
+
+ /// <summary>
+ /// Assembles a message comprised of the message on a given exception and all inner exceptions.
+ /// </summary>
+ /// <param name="exception">The exception.</param>
+ /// <returns>The assembled message.</returns>
+ public static string ToStringDescriptive(this Exception exception) {
+ // The input being null is probably bad, but since this method is called
+ // from a catch block, we don't really want to throw a new exception and
+ // hide the details of this one.
+ if (exception == null) {
+ Logger.Messaging.Error("MessagingUtilities.GetAllMessages called with null input.");
+ }
+
+ StringBuilder message = new StringBuilder();
+ while (exception != null) {
+ message.Append(exception.Message);
+ exception = exception.InnerException;
+ if (exception != null) {
+ message.Append(" ");
+ }
+ }
+
+ return message.ToString();
+ }
+
+ /// <summary>
+ /// Sends a multipart HTTP POST request (useful for posting files) but doesn't call GetResponse on it.
+ /// </summary>
+ /// <param name="request">The HTTP request.</param>
+ /// <param name="requestHandler">The request handler.</param>
+ /// <param name="parts">The parts to include in the POST entity.</param>
+ internal static void PostMultipartNoGetResponse(this HttpWebRequest request, IDirectWebRequestHandler requestHandler, IEnumerable<MultipartPostPart> parts) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentNullException>(requestHandler != null);
+ Contract.Requires<ArgumentNullException>(parts != null);
+
Reporting.RecordFeatureUse("MessagingUtilities.PostMultipart");
+ parts = parts.CacheGeneratedResults();
string boundary = Guid.NewGuid().ToString();
+ string initialPartLeadingBoundary = string.Format(CultureInfo.InvariantCulture, "--{0}\r\n", boundary);
string partLeadingBoundary = string.Format(CultureInfo.InvariantCulture, "\r\n--{0}\r\n", boundary);
string finalTrailingBoundary = string.Format(CultureInfo.InvariantCulture, "\r\n--{0}--\r\n", boundary);
request.Method = "POST";
request.ContentType = "multipart/form-data; boundary=" + boundary;
- request.ContentLength = parts.Sum(p => partLeadingBoundary.Length + p.Length) + finalTrailingBoundary.Length;
+ long contentLength = parts.Sum(p => partLeadingBoundary.Length + p.Length) + finalTrailingBoundary.Length;
+ if (parts.Any()) {
+ contentLength -= 2; // the initial part leading boundary has no leading \r\n
+ }
+ request.ContentLength = contentLength;
// Setting the content-encoding to "utf-8" causes Google to reply
// with a 415 UnsupportedMediaType. But adding it doesn't buy us
@@ -169,8 +215,10 @@ namespace DotNetOpenAuth.Messaging {
var requestStream = requestHandler.GetRequestStream(request);
try {
StreamWriter writer = new StreamWriter(requestStream, Channel.PostEntityEncoding);
+ bool firstPart = true;
foreach (var part in parts) {
- writer.Write(partLeadingBoundary);
+ writer.Write(firstPart ? initialPartLeadingBoundary : partLeadingBoundary);
+ firstPart = false;
part.Serialize(writer);
part.Dispose();
}
@@ -185,33 +233,6 @@ namespace DotNetOpenAuth.Messaging {
requestStream.Dispose();
}
}
-
- return requestHandler.GetResponse(request);
- }
-
- /// <summary>
- /// Assembles a message comprised of the message on a given exception and all inner exceptions.
- /// </summary>
- /// <param name="exception">The exception.</param>
- /// <returns>The assembled message.</returns>
- public static string ToStringDescriptive(this Exception exception) {
- // The input being null is probably bad, but since this method is called
- // from a catch block, we don't really want to throw a new exception and
- // hide the details of this one.
- if (exception == null) {
- Logger.Messaging.Error("MessagingUtilities.GetAllMessages called with null input.");
- }
-
- StringBuilder message = new StringBuilder();
- while (exception != null) {
- message.Append(exception.Message);
- exception = exception.InnerException;
- if (exception != null) {
- message.Append(" ");
- }
- }
-
- return message.ToString();
}
/// <summary>
@@ -646,6 +667,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="request">The request to get recipient information from.</param>
/// <returns>The recipient.</returns>
+ /// <exception cref="ArgumentException">Thrown if the HTTP request is something we can't handle.</exception>
internal static MessageReceivingEndpoint GetRecipient(this HttpRequestInfo request) {
return new MessageReceivingEndpoint(request.UrlBeforeRewriting, GetHttpDeliveryMethod(request.HttpMethod));
}
@@ -655,6 +677,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="httpVerb">The HTTP verb.</param>
/// <returns>A <see cref="HttpDeliveryMethods"/> enum value that is within the <see cref="HttpDeliveryMethods.HttpVerbMask"/>.</returns>
+ /// <exception cref="ArgumentException">Thrown if the HTTP request is something we can't handle.</exception>
internal static HttpDeliveryMethods GetHttpDeliveryMethod(string httpVerb) {
if (httpVerb == "GET") {
return HttpDeliveryMethods.GetRequest;
@@ -664,6 +687,8 @@ namespace DotNetOpenAuth.Messaging {
return HttpDeliveryMethods.PutRequest;
} else if (httpVerb == "DELETE") {
return HttpDeliveryMethods.DeleteRequest;
+ } else if (httpVerb == "HEAD") {
+ return HttpDeliveryMethods.HeadRequest;
} else {
throw ErrorUtilities.ThrowArgumentNamed("httpVerb", MessagingStrings.UnsupportedHttpVerb, httpVerb);
}
@@ -675,14 +700,16 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="httpMethod">The HTTP method.</param>
/// <returns>An HTTP verb, such as GET, POST, PUT, or DELETE.</returns>
internal static string GetHttpVerb(HttpDeliveryMethods httpMethod) {
- if ((httpMethod & HttpDeliveryMethods.GetRequest) != 0) {
+ if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.GetRequest) {
return "GET";
- } else if ((httpMethod & HttpDeliveryMethods.PostRequest) != 0) {
+ } else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.PostRequest) {
return "POST";
- } else if ((httpMethod & HttpDeliveryMethods.PutRequest) != 0) {
+ } else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.PutRequest) {
return "PUT";
- } else if ((httpMethod & HttpDeliveryMethods.DeleteRequest) != 0) {
+ } else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.DeleteRequest) {
return "DELETE";
+ } else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.HeadRequest) {
+ return "HEAD";
} else if ((httpMethod & HttpDeliveryMethods.AuthorizationHeaderRequest) != 0) {
return "GET"; // if AuthorizationHeaderRequest is specified without an explicit HTTP verb, assume GET.
} else {
diff --git a/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs b/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs
index 7ef89a4..f9a5988 100644
--- a/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs
+++ b/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs
@@ -116,6 +116,10 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="contentType">Type of the content in HTTP Content-Type format.</param>
/// <returns>The constructed part.</returns>
public static MultipartPostPart CreateFormFilePart(string name, string filePath, string contentType) {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(name));
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(filePath));
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(contentType));
+
string fileName = Path.GetFileName(filePath);
return CreateFormFilePart(name, fileName, contentType, File.OpenRead(filePath));
}
@@ -130,11 +134,11 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>The constructed part.</returns>
public static MultipartPostPart CreateFormFilePart(string name, string fileName, string contentType, Stream content) {
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(name));
- Contract.Requires<ArgumentException>(fileName != null);
- Contract.Requires<ArgumentException>(contentType != null);
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(fileName));
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(contentType));
Contract.Requires<ArgumentException>(content != null);
- var part = new MultipartPostPart("form-data");
+ var part = new MultipartPostPart("file");
part.ContentAttributes["name"] = name;
part.ContentAttributes["filename"] = fileName;
part.PartHeaders[HttpRequestHeader.ContentType] = contentType;
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
index 49e30cb..f6eed24 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
@@ -269,7 +269,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// Sets a named value in the message.
/// </summary>
/// <param name="item">The name-value pair to add. The name is the serialized form of the key.</param>
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts generates the code FxCop is complaining about.")]
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Code Contracts ccrewrite does this.")]
public void Add(KeyValuePair<string, string> item) {
this.Add(item.Key, item.Value);
}
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
index 32d2fec..08e2411 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
@@ -286,6 +286,9 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <param name="messagePartEncoder">The message part encoder type.</param>
/// <returns>An instance of the desired encoder.</returns>
private static IMessagePartEncoder GetEncoder(Type messagePartEncoder) {
+ Contract.Requires<ArgumentNullException>(messagePartEncoder != null);
+ Contract.Ensures(Contract.Result<IMessagePartEncoder>() != null);
+
IMessagePartEncoder encoder;
if (!encoders.TryGetValue(messagePartEncoder, out encoder)) {
encoder = encoders[messagePartEncoder] = (IMessagePartEncoder)Activator.CreateInstance(messagePartEncoder);
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
index 8c5980f..b0e938f 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
@@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
+ using System.Linq;
using System.Net;
using System.Text;
using System.Web;
@@ -87,7 +88,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </summary>
/// <param name="message">The message with data to encode.</param>
/// <returns>A dictionary of name-value pairs with their strings encoded.</returns>
- internal static IDictionary<string, string> GetUriEscapedParameters(MessageDictionary message) {
+ internal static IDictionary<string, string> GetUriEscapedParameters(IEnumerable<KeyValuePair<string, string>> message) {
var encodedDictionary = new Dictionary<string, string>();
UriEscapeParameters(message, encodedDictionary);
return encodedDictionary;
@@ -155,8 +156,16 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
}
}
+ MessageReceivingEndpoint recipient;
+ try {
+ recipient = request.GetRecipient();
+ } catch (ArgumentException ex) {
+ Logger.OAuth.WarnFormat("Unrecognized HTTP request: " + ex.ToString());
+ return null;
+ }
+
// Deserialize the message using all the data we've collected.
- var message = (IDirectedProtocolMessage)this.Receive(fields, request.GetRecipient());
+ var message = (IDirectedProtocolMessage)this.Receive(fields, recipient);
// Add receiving HTTP transport information required for signature generation.
var signedMessage = message as ITamperResistantOAuthMessage;
@@ -194,9 +203,13 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
if ((transmissionMethod & HttpDeliveryMethods.AuthorizationHeaderRequest) != 0) {
httpRequest = this.InitializeRequestAsAuthHeader(request);
} else if ((transmissionMethod & HttpDeliveryMethods.PostRequest) != 0) {
+ var requestMessageWithBinaryData = request as IMessageWithBinaryData;
+ ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || !requestMessageWithBinaryData.SendAsMultipart, OAuthStrings.MultipartPostMustBeUsedWithAuthHeader);
httpRequest = this.InitializeRequestAsPost(request);
} else if ((transmissionMethod & HttpDeliveryMethods.GetRequest) != 0) {
httpRequest = InitializeRequestAsGet(request);
+ } else if ((transmissionMethod & HttpDeliveryMethods.HeadRequest) != 0) {
+ httpRequest = InitializeRequestAsHead(request);
} else if ((transmissionMethod & HttpDeliveryMethods.PutRequest) != 0) {
httpRequest = this.InitializeRequestAsPut(request);
} else if ((transmissionMethod & HttpDeliveryMethods.DeleteRequest) != 0) {
@@ -264,7 +277,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </summary>
/// <param name="source">The dictionary with names and values to encode.</param>
/// <param name="destination">The dictionary to add the encoded pairs to.</param>
- private static void UriEscapeParameters(IDictionary<string, string> source, IDictionary<string, string> destination) {
+ private static void UriEscapeParameters(IEnumerable<KeyValuePair<string, string>> source, IDictionary<string, string> destination) {
Contract.Requires<ArgumentNullException>(source != null);
Contract.Requires<ArgumentNullException>(destination != null);
@@ -341,12 +354,22 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
if (hasEntity) {
// WARNING: We only set up the request stream for the caller if there is
// extra data. If there isn't any extra data, the caller must do this themselves.
- if (requestMessage.ExtraData.Count > 0) {
- SendParametersInEntity(httpRequest, requestMessage.ExtraData);
+ var requestMessageWithBinaryData = requestMessage as IMessageWithBinaryData;
+ if (requestMessageWithBinaryData != null && requestMessageWithBinaryData.SendAsMultipart) {
+ // Include the binary data in the multipart entity, and any standard text extra message data.
+ // The standard declared message parts are included in the authorization header.
+ var multiPartFields = new List<MultipartPostPart>(requestMessageWithBinaryData.BinaryData);
+ multiPartFields.AddRange(requestMessage.ExtraData.Select(field => MultipartPostPart.CreateFormPart(field.Key, field.Value)));
+ this.SendParametersInEntityAsMultipart(httpRequest, multiPartFields);
} else {
- // We'll assume the content length is zero since the caller may not have
- // anything. They're responsible to change it when the add the payload if they have one.
- httpRequest.ContentLength = 0;
+ ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || requestMessageWithBinaryData.BinaryData.Count == 0, MessagingStrings.BinaryDataRequiresMultipart);
+ if (requestMessage.ExtraData.Count > 0) {
+ this.SendParametersInEntity(httpRequest, requestMessage.ExtraData);
+ } else {
+ // We'll assume the content length is zero since the caller may not have
+ // anything. They're responsible to change it when the add the payload if they have one.
+ httpRequest.ContentLength = 0;
+ }
}
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs
index 004e7d5..b45da66 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs
@@ -10,6 +10,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
using System.Collections.Specialized;
using System.Diagnostics.Contracts;
using System.Globalization;
+ using System.Linq;
using System.Text;
using System.Web;
using DotNetOpenAuth.Messaging;
@@ -157,7 +158,26 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
signatureBaseStringElements.Add(message.HttpMethod.ToUpperInvariant());
- var encodedDictionary = OAuthChannel.GetUriEscapedParameters(messageDictionary);
+ // For multipart POST messages, only include the message parts that are NOT
+ // in the POST entity (those parts that may appear in an OAuth authorization header).
+ var encodedDictionary = new Dictionary<string, string>();
+ IEnumerable<KeyValuePair<string, string>> partsToInclude = Enumerable.Empty<KeyValuePair<string, string>>();
+ var binaryMessage = message as IMessageWithBinaryData;
+ if (binaryMessage != null && binaryMessage.SendAsMultipart) {
+ HttpDeliveryMethods authHeaderInUseFlags = HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest;
+ ErrorUtilities.VerifyProtocol((binaryMessage.HttpMethods & authHeaderInUseFlags) == authHeaderInUseFlags, OAuthStrings.MultipartPostMustBeUsedWithAuthHeader);
+
+ // Include the declared keys in the signature as those will be signable.
+ // Cache in local variable to avoid recalculating DeclaredKeys in the delegate.
+ ICollection<string> declaredKeys = messageDictionary.DeclaredKeys;
+ partsToInclude = messageDictionary.Where(pair => declaredKeys.Contains(pair.Key));
+ } else {
+ partsToInclude = messageDictionary;
+ }
+
+ foreach (var pair in OAuthChannel.GetUriEscapedParameters(partsToInclude)) {
+ encodedDictionary[pair.Key] = pair.Value;
+ }
// An incoming message will already have included the query and form parameters
// in the message dictionary, but an outgoing message COULD have SOME parameters
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementChain.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementChain.cs
index bdb0219..67c5205 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementChain.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementChain.cs
@@ -20,7 +20,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <summary>
/// The various signing binding elements that may be applicable to a message in preferred use order.
/// </summary>
- private ITamperProtectionChannelBindingElement[] signers;
+ private readonly ITamperProtectionChannelBindingElement[] signers;
/// <summary>
/// Initializes a new instance of the <see cref="SigningBindingElementChain"/> class.
@@ -93,6 +93,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </returns>
public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) {
foreach (IChannelBindingElement signer in this.signers) {
+ ErrorUtilities.VerifyInternal(signer.Channel != null, "A binding element's Channel property is unexpectedly null.");
MessageProtections? result = signer.ProcessOutgoingMessage(message);
if (result.HasValue) {
return result;
@@ -113,6 +114,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </returns>
public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) {
foreach (IChannelBindingElement signer in this.signers) {
+ ErrorUtilities.VerifyInternal(signer.Channel != null, "A binding element's Channel property is unexpectedly null.");
MessageProtections? result = signer.ProcessIncomingMessage(message);
if (result.HasValue) {
return result;
diff --git a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
index 6c0ce42..48f54d7 100644
--- a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
+++ b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OAuth {
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
+ using System.Linq;
using System.Net;
using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
@@ -111,6 +112,27 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
+ /// Prepares an authorized request that carries an HTTP multi-part POST, allowing for binary data.
+ /// </summary>
+ /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
+ /// <param name="accessToken">The access token that permits access to the protected resource.</param>
+ /// <param name="binaryData">Extra parameters to include in the message. Must not be null, but may be empty.</param>
+ /// <returns>The initialized WebRequest object.</returns>
+ public HttpWebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken, IEnumerable<MultipartPostPart> binaryData) {
+ Contract.Requires<ArgumentNullException>(endpoint != null);
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(accessToken));
+ Contract.Requires<ArgumentNullException>(binaryData != null);
+
+ AccessProtectedResourceRequest message = this.CreateAuthorizingMessage(endpoint, accessToken);
+ foreach (MultipartPostPart part in binaryData) {
+ message.BinaryData.Add(part);
+ }
+
+ HttpWebRequest wr = this.OAuthChannel.InitializeRequest(message);
+ return wr;
+ }
+
+ /// <summary>
/// Prepares an HTTP request that has OAuth authorization already attached to it.
/// </summary>
/// <param name="message">The OAuth authorization message to attach to the HTTP request.</param>
diff --git a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs
index b60fda4..f3231f0 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.OAuth.Messages {
using System;
+ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using DotNetOpenAuth.Messaging;
@@ -13,7 +14,12 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// A message attached to a request for protected resources that provides the necessary
/// credentials to be granted access to those resources.
/// </summary>
- public class AccessProtectedResourceRequest : SignedMessageBase, ITokenContainingMessage {
+ public class AccessProtectedResourceRequest : SignedMessageBase, ITokenContainingMessage, IMessageWithBinaryData {
+ /// <summary>
+ /// A store for the binary data that is carried in the message.
+ /// </summary>
+ private List<MultipartPostPart> binaryData = new List<MultipartPostPart>();
+
/// <summary>
/// Initializes a new instance of the <see cref="AccessProtectedResourceRequest"/> class.
/// </summary>
@@ -43,5 +49,24 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// </remarks>
[MessagePart("oauth_token", IsRequired = true)]
public string AccessToken { get; set; }
+
+ #region IMessageWithBinaryData Members
+
+ /// <summary>
+ /// Gets the parts of the message that carry binary data.
+ /// </summary>
+ /// <value>A list of parts. Never null.</value>
+ public IList<MultipartPostPart> BinaryData {
+ get { return this.binaryData; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this message should be sent as multi-part POST.
+ /// </summary>
+ public bool SendAsMultipart {
+ get { return this.HttpMethod == "POST" && this.BinaryData.Count > 0; }
+ }
+
+ #endregion
}
}
diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs
index bb4d5d9..fb4c9a1 100644
--- a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs
+++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs
@@ -124,6 +124,15 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
+ /// Looks up a localized string similar to Cannot send OAuth message as multipart POST without an authorization HTTP header because sensitive data would not be signed..
+ /// </summary>
+ internal static string MultipartPostMustBeUsedWithAuthHeader {
+ get {
+ return ResourceManager.GetString("MultipartPostMustBeUsedWithAuthHeader", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface..
/// </summary>
internal static string OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface {
diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx
index bbeeda9..34b314b 100644
--- a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx
+++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx
@@ -138,6 +138,9 @@
<data name="MinimumConsumerVersionRequirementNotMet" xml:space="preserve">
<value>This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}.</value>
</data>
+ <data name="MultipartPostMustBeUsedWithAuthHeader" xml:space="preserve">
+ <value>Cannot send OAuth message as multipart POST without an authorization HTTP header because sensitive data would not be signed.</value>
+ </data>
<data name="OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface" xml:space="preserve">
<value>Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface.</value>
</data>
diff --git a/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs b/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs
index 9014762..6205f55 100644
--- a/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs
+++ b/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OAuth {
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Linq;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth.ChannelElements;
@@ -94,6 +95,7 @@ namespace DotNetOpenAuth.OAuth {
/// </summary>
/// <returns>The created signing element.</returns>
internal ITamperProtectionChannelBindingElement CreateTamperProtectionElement() {
+ Contract.Requires(this.TamperProtectionElements != null);
return new SigningBindingElementChain(this.TamperProtectionElements.Select(el => (ITamperProtectionChannelBindingElement)el.Clone()).ToArray());
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs b/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs
index d7dca9a..580bdfd 100644
--- a/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs
@@ -23,7 +23,7 @@ namespace DotNetOpenAuth.OpenId.Behaviors {
/// to the originally requested extension and format.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sreg", Justification = "Abbreviation")]
- public class AXFetchAsSregTransform : IRelyingPartyBehavior, IProviderBehavior {
+ public sealed class AXFetchAsSregTransform : IRelyingPartyBehavior, IProviderBehavior {
/// <summary>
/// Initializes static members of the <see cref="AXFetchAsSregTransform"/> class.
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
index 4fa70a0..c516e8f 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.ChannelElements {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
@@ -76,6 +77,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// Implementations that provide message protection must honor the
/// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
/// </remarks>
+ [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "It doesn't look too bad to me. :)")]
public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) {
var extendableMessage = message as IProtocolMessageWithExtensions;
if (extendableMessage != null) {
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs
index 67efe8e..76a1c9b 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs
@@ -70,7 +70,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
/// <remarks>
/// The user's preferred languages as a [BCP 47] language priority list, represented as a comma-separated list of BCP 47 basic language ranges in descending priority order. For instance, the value "fr-CA,fr-FR,en-CA" represents the preference for French spoken in Canada, French spoken in France, followed by English spoken in Canada.
/// </remarks>
- [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "By design")]
+ [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "By design.")]
[MessagePart("lang", AllowEmpty = false)]
public CultureInfo[] LanguagePreference { get; set; }
diff --git a/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs b/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs
index b6ed2f7..e96f362 100644
--- a/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs
+++ b/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
@@ -266,6 +267,7 @@ namespace DotNetOpenAuth.OpenId {
/// an alternative plan.
/// </remarks>
/// <exception cref="ProtocolException">Thrown if the certificate chain is invalid or unverifiable.</exception>
+ [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "By design")]
private static void VerifyCertChain(List<X509Certificate2> certs) {
var chain = new X509Chain();
foreach (var cert in certs) {
@@ -418,6 +420,7 @@ namespace DotNetOpenAuth.OpenId {
/// <summary>
/// A description of a web server that hosts host-meta documents.
/// </summary>
+ [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "By design")]
public class HostMetaProxy {
/// <summary>
/// Initializes a new instance of the <see cref="HostMetaProxy"/> class.
diff --git a/src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs b/src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs
index f637b37..eb2bf98 100644
--- a/src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs
+++ b/src/DotNetOpenAuth/OpenId/IIdentifierDiscoveryService.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
@@ -27,6 +28,7 @@ namespace DotNetOpenAuth.OpenId {
/// <returns>
/// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty.
/// </returns>
+ [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "By design")]
[Pure]
IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain);
}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs b/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs
index 6b89e92..8e4cb9d 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs
@@ -32,14 +32,14 @@ namespace DotNetOpenAuth.OpenId.Messages {
#pragma warning restore 0414
/// <summary>
- /// Backing store for the <see cref="Incoming"/> property.
+ /// Backing store for the <see cref="ExtraData"/> property.
/// </summary>
- private bool incoming;
+ private readonly Dictionary<string, string> extraData = new Dictionary<string, string>();
/// <summary>
- /// Backing store for the <see cref="ExtraData"/> property.
+ /// Backing store for the <see cref="Incoming"/> property.
/// </summary>
- private Dictionary<string, string> extraData = new Dictionary<string, string>();
+ private bool incoming;
/// <summary>
/// Initializes a new instance of the <see cref="RequestBase"/> class.
diff --git a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
index 611101d..10c69a3 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
@@ -25,6 +25,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <summary>
/// Offers services for a web page that is acting as an OpenID identity server.
/// </summary>
+ [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "By design")]
[ContractVerification(true)]
public sealed class OpenIdProvider : IDisposable {
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs
index 6e141ad..b6a1b76 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAuthenticationResponse.cs
@@ -38,7 +38,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
this.VerifyDiscoveryMatchesAssertion(relyingParty);
- Logger.OpenId.InfoFormat("Received identity assertion for {0} via {1}.", this.ClaimedIdentifier, this.Provider.Uri);
+ Logger.OpenId.InfoFormat("Received identity assertion for {0} via {1}.", this.Response.ClaimedIdentifier, this.Provider.Uri);
}
#region IAuthenticationResponse Properties
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
index 8499178..f0a1574 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
@@ -106,7 +106,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <param name="context">The context, or namespace, within which the <paramref name="nonce"/> must be unique.</param>
/// <param name="nonce">A series of random characters.</param>
- /// <param name="timestamp">The timestamp that together with the nonce string make it unique.
+ /// <param name="timestampUtc">The timestamp that together with the nonce string make it unique.
/// The timestamp may also be used by the data store to clear out old nonces.</param>
/// <returns>
/// True if the nonce+timestamp (combination) was not previously in the database.
@@ -119,8 +119,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// is retrieved or set using the
/// <see cref="StandardExpirationBindingElement.MaximumMessageAge"/> property.
/// </remarks>
- public bool StoreNonce(string context, string nonce, DateTime timestamp) {
- return this.nonceStore.StoreNonce(context, nonce, timestamp);
+ public bool StoreNonce(string context, string nonce, DateTime timestampUtc) {
+ return this.nonceStore.StoreNonce(context, nonce, timestampUtc);
}
#endregion
diff --git a/src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs b/src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs
index e338542..b1a3430 100644
--- a/src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs
+++ b/src/DotNetOpenAuth/OpenId/XriDiscoveryProxyService.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
@@ -21,6 +22,7 @@ namespace DotNetOpenAuth.OpenId {
/// <summary>
/// The discovery service for XRI identifiers that uses an XRI proxy resolver for discovery.
/// </summary>
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xri", Justification = "Acronym")]
public class XriDiscoveryProxyService : IIdentifierDiscoveryService {
/// <summary>
/// The magic URL that will provide us an XRDS document for a given XRI identifier.
diff --git a/src/DotNetOpenAuth/OpenId/XriIdentifier.cs b/src/DotNetOpenAuth/OpenId/XriIdentifier.cs
index 6af41fa..729f603 100644
--- a/src/DotNetOpenAuth/OpenId/XriIdentifier.cs
+++ b/src/DotNetOpenAuth/OpenId/XriIdentifier.cs
@@ -94,7 +94,9 @@ namespace DotNetOpenAuth.OpenId {
public override bool Equals(object obj) {
XriIdentifier other = obj as XriIdentifier;
if (obj != null && other == null && Identifier.EqualityOnStrings) { // test hook to enable MockIdentifier comparison
- other = Identifier.Parse(obj.ToString()) as XriIdentifier;
+ string objString = obj.ToString();
+ ErrorUtilities.VerifyInternal(!string.IsNullOrEmpty(objString), "Identifier.ToString() returned a null or empty string.");
+ other = Identifier.Parse(objString) as XriIdentifier;
}
if (other == null) {
return false;
diff --git a/src/DotNetOpenAuth/Reporting.cs b/src/DotNetOpenAuth/Reporting.cs
index 4e4bbf5..2235986 100644
--- a/src/DotNetOpenAuth/Reporting.cs
+++ b/src/DotNetOpenAuth/Reporting.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth {
using System;
using System.Collections.Generic;
using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
@@ -88,6 +89,8 @@ namespace DotNetOpenAuth {
/// <summary>
/// Initializes static members of the <see cref="Reporting"/> class.
/// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "We do more than field initialization here.")]
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Reporting MUST NOT cause unhandled exceptions.")]
static Reporting() {
Enabled = DotNetOpenAuthSection.Configuration.Reporting.Enabled;
if (Enabled) {
@@ -446,6 +449,7 @@ namespace DotNetOpenAuth {
/// <summary>
/// Sends the stats report asynchronously, and careful to not throw any unhandled exceptions.
/// </summary>
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Unhandled exceptions MUST NOT be thrown from here.")]
private static void SendStatsAsync() {
// Do it on a background thread since it could take a while and we
// don't want to slow down this request we're borrowing.
@@ -635,6 +639,7 @@ namespace DotNetOpenAuth {
/// </summary>
public void Dispose() {
this.Dispose(true);
+ GC.SuppressFinalize(this);
}
#endregion
@@ -775,6 +780,7 @@ namespace DotNetOpenAuth {
/// </summary>
public void Dispose() {
this.Dispose(true);
+ GC.SuppressFinalize(this);
}
#endregion
diff --git a/src/DotNetOpenAuth/Yadis/Yadis.cs b/src/DotNetOpenAuth/Yadis/Yadis.cs
index 589a155..f1c8be3 100644
--- a/src/DotNetOpenAuth/Yadis/Yadis.cs
+++ b/src/DotNetOpenAuth/Yadis/Yadis.cs
@@ -138,6 +138,8 @@ namespace DotNetOpenAuth.Yadis {
internal static IncomingWebResponse Request(IDirectWebRequestHandler requestHandler, Uri uri, bool requireSsl, params string[] acceptTypes) {
Contract.Requires<ArgumentNullException>(requestHandler != null);
Contract.Requires<ArgumentNullException>(uri != null);
+ Contract.Ensures(Contract.Result<IncomingWebResponse>() != null);
+ Contract.Ensures(Contract.Result<IncomingWebResponse>().ResponseStream != null);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.CachePolicy = IdentifierDiscoveryCachePolicy;
diff --git a/src/version.txt b/src/version.txt
index 1809198..1545d96 100644
--- a/src/version.txt
+++ b/src/version.txt
@@ -1 +1 @@
-3.4.0
+3.5.0