diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..412eeda78 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/RestSharp.Mono.sln b/RestSharp.Mono.sln index 5b7756f49..44fe621c3 100644 --- a/RestSharp.Mono.sln +++ b/RestSharp.Mono.sln @@ -1,52 +1,52 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestSharp", "RestSharp\RestSharp.csproj", "{2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestSharp.Tests", "RestSharp.Tests\RestSharp.Tests.csproj", "{1464E4AC-18BB-4F23-8A0B-68196F9E1871}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuGet", "NuGet", "{E709A928-A45C-4622-A35C-CCD8EE44CA80}" - ProjectSection(SolutionItems) = preProject - restsharp.nuspec = restsharp.nuspec - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Debug|x86.ActiveCfg = Debug|Any CPU - {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Release|Any CPU.Build.0 = Release|Any CPU - {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Release|x86.ActiveCfg = Release|Any CPU - {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Debug|x86.ActiveCfg = Debug|Any CPU - {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Release|Any CPU.Build.0 = Release|Any CPU - {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Release|x86.ActiveCfg = Release|Any CPU - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = RestSharp\RestSharp.csproj - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestSharp", "RestSharp\RestSharp.csproj", "{2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestSharp.Tests", "RestSharp.Tests\RestSharp.Tests.csproj", "{1464E4AC-18BB-4F23-8A0B-68196F9E1871}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuGet", "NuGet", "{E709A928-A45C-4622-A35C-CCD8EE44CA80}" + ProjectSection(SolutionItems) = preProject + restsharp.nuspec = restsharp.nuspec + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Debug|x86.ActiveCfg = Debug|Any CPU + {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Release|Any CPU.Build.0 = Release|Any CPU + {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Release|x86.ActiveCfg = Release|Any CPU + {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Debug|x86.ActiveCfg = Debug|Any CPU + {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Release|Any CPU.Build.0 = Release|Any CPU + {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Release|x86.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = RestSharp\RestSharp.csproj + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/RestSharp.Tests/JsonTests.cs b/RestSharp.Tests/JsonTests.cs index 16c5d6080..9e3b177c7 100644 --- a/RestSharp.Tests/JsonTests.cs +++ b/RestSharp.Tests/JsonTests.cs @@ -170,19 +170,19 @@ public void Can_Deserialize_Null_Elements_to_Nullable_Values() Assert.Null(output.Id); Assert.Null(output.StartDate); Assert.Null(output.UniqueId); - } - - [Fact] - public void Can_Deserialize_Empty_Elements_to_Nullable_Values() - { - var doc = CreateJsonWithEmptyValues(); - - var json = new JsonDeserializer(); - var output = json.Deserialize(new RestResponse { Content = doc }); - - Assert.Null(output.Id); - Assert.Null(output.StartDate); - Assert.Null(output.UniqueId); + } + + [Fact] + public void Can_Deserialize_Empty_Elements_to_Nullable_Values() + { + var doc = CreateJsonWithEmptyValues(); + + var json = new JsonDeserializer(); + var output = json.Deserialize(new RestResponse { Content = doc }); + + Assert.Null(output.Id); + Assert.Null(output.StartDate); + Assert.Null(output.UniqueId); } [Fact] @@ -632,17 +632,17 @@ public void Can_Deserialize_To_Dictionary_String_String_With_Dynamic_Values () Assert.Equal ("{\"Name\":\"ThingBlue\",\"Color\":\"Blue\"}", bd["ThingBlue"]); } - [Fact] - public void Can_Deserialize_Decimal_With_Four_Zeros_After_Floating_Point() - { - const string json = "{\"Value\":0.00005557}"; - var response = new RestResponse() {Content = json}; - var d = new JsonDeserializer(); - var result = d.Deserialize(response); - - Assert.Equal(result.Value, .00005557m); - } - + [Fact] + public void Can_Deserialize_Decimal_With_Four_Zeros_After_Floating_Point() + { + const string json = "{\"Value\":0.00005557}"; + var response = new RestResponse() {Content = json}; + var d = new JsonDeserializer(); + var result = d.Deserialize(response); + + Assert.Equal(result.Value, .00005557m); + } + [Fact] public void Can_Deserialize_Object_Type_Property_With_Primitive_Vale() { @@ -801,16 +801,16 @@ private string CreateJsonWithNullValues() doc["UniqueId"] = null; return doc.ToString(); - } - - private string CreateJsonWithEmptyValues() - { - var doc = new JsonObject(); - doc["Id"] = ""; - doc["StartDate"] = ""; - doc["UniqueId"] = ""; - - return doc.ToString(); + } + + private string CreateJsonWithEmptyValues() + { + var doc = new JsonObject(); + doc["Id"] = ""; + doc["StartDate"] = ""; + doc["UniqueId"] = ""; + + return doc.ToString(); } private string CreateJsonWithoutEmptyValues() diff --git a/RestSharp.Tests/NamespacedXmlTests.cs b/RestSharp.Tests/NamespacedXmlTests.cs index 0ef88574d..cd08ea8e6 100644 --- a/RestSharp.Tests/NamespacedXmlTests.cs +++ b/RestSharp.Tests/NamespacedXmlTests.cs @@ -14,11 +14,11 @@ // limitations under the License. #endregion -using System; -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Xml.Linq; -using RestSharp.Deserializers; -using RestSharp.Tests.SampleClasses.Lastfm; +using RestSharp.Deserializers; +using RestSharp.Tests.SampleClasses.Lastfm; using Xunit; namespace RestSharp.Tests @@ -161,27 +161,27 @@ public void Can_Deserialize_Names_With_Underscores_With_Namespace() { } [Fact] - public void Can_Deserialize_List_Of_Primitives_With_Namespace() { - var doc = CreateListOfPrimitivesXml(); - var response = new RestResponse { Content = doc }; - - var d = new XmlDeserializer(); - d.Namespace = "http://restsharp.org"; - var a = d.Deserialize>(response); - - Assert.Equal(2, a.Count); - Assert.Equal("first", a[0].Value); - Assert.Equal("second", a[1].Value); + public void Can_Deserialize_List_Of_Primitives_With_Namespace() { + var doc = CreateListOfPrimitivesXml(); + var response = new RestResponse { Content = doc }; + + var d = new XmlDeserializer(); + d.Namespace = "http://restsharp.org"; + var a = d.Deserialize>(response); + + Assert.Equal(2, a.Count); + Assert.Equal("first", a[0].Value); + Assert.Equal("second", a[1].Value); } - private static string CreateListOfPrimitivesXml() { - var doc = new XDocument(); - var ns = XNamespace.Get("http://restsharp.org"); - var root = new XElement(ns + "artists"); - root.Add(new XElement(ns + "artist", "first")); - root.Add(new XElement(ns + "artist", "second")); - doc.Add(root); - return doc.ToString(); + private static string CreateListOfPrimitivesXml() { + var doc = new XDocument(); + var ns = XNamespace.Get("http://restsharp.org"); + var root = new XElement(ns + "artists"); + root.Add(new XElement(ns + "artist", "first")); + root.Add(new XElement(ns + "artist", "second")); + doc.Add(root); + return doc.ToString(); } private static string CreateUnderscoresXml() { diff --git a/RestSharp.Tests/SampleClasses/misc.cs b/RestSharp.Tests/SampleClasses/misc.cs index 0be211fc3..ed5d029e6 100644 --- a/RestSharp.Tests/SampleClasses/misc.cs +++ b/RestSharp.Tests/SampleClasses/misc.cs @@ -1,207 +1,207 @@ -#region Licensed -// Copyright 2010 John Sheehan -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#endregion - -using System; -using System.Collections.Generic; -using RestSharp.Serializers; - -namespace RestSharp.Tests -{ - public class PersonForXml - { - public string Name { get; set; } - public DateTime StartDate { get; set; } - public int Age { get; set; } - public decimal Percent { get; set; } - public long BigNumber { get; set; } - public bool IsCool { get; set; } - public List Friends { get; set; } - public Friend BestFriend { get; set; } - - protected string Ignore { get; set; } - public string IgnoreProxy { get { return Ignore; } } - - protected string ReadOnly { get { return null; } } - public string ReadOnlyProxy { get { return ReadOnly; } } - - public FoeList Foes { get; set; } - - public Guid UniqueId { get; set; } - public Guid EmptyGuid { get; set; } - - public Uri Url { get; set; } - public Uri UrlPath { get; set; } - - public Order Order { get; set; } - - public Disposition Disposition { get; set; } - - } - - public class IncomingInvoice - { - public int ConceptId { get; set; } - } - - public class PersonForJson - { - public string Name { get; set; } - public DateTime StartDate { get; set; } - public int Age { get; set; } - public decimal Percent { get; set; } - public long BigNumber { get; set; } - public bool IsCool { get; set; } - public List Friends { get; set; } - public Friend BestFriend { get; set; } - public Guid Guid { get; set; } - public Guid EmptyGuid { get; set; } - public Uri Url { get; set; } - public Uri UrlPath { get; set; } - - protected string Ignore { get; set; } - public string IgnoreProxy { get { return Ignore; } } - - protected string ReadOnly { get { return null; } } - public string ReadOnlyProxy { get { return ReadOnly; } } - - public Dictionary Foes { get; set; } - - public Order Order { get; set; } - - public Disposition Disposition { get; set; } - } - - public enum Order - { - First, - Second, - Third - } - - public enum Disposition - { - Friendly, - SoSo, - SteerVeryClear - } - - public class Friend - { - public string Name { get; set; } - public int Since { get; set; } - } - - public class Foe - { - public string Nickname { get; set; } - } - - public class FoeList : List - { - public string Team { get; set; } - } - - public class Birthdate - { - public DateTime Value { get; set; } - } - - public class OrderedProperties - { - [SerializeAs(Index = 2)] - public string Name { get; set; } - [SerializeAs(Index = 3)] - public int Age { get; set; } - [SerializeAs(Index = 1)] - public DateTime StartDate { get; set; } - } - - public class ObjectProperties - { - public object ObjectProperty { get; set; } - } - - public class DatabaseCollection : List - { - } - - public class Database - { - public string Name { get; set; } - public string InitialCatalog { get; set; } - public string DataSource { get; set; } - } - - public class Generic - { - public T Data { get; set; } - } - - public class GenericWithList - { - public List Items { get; set; } - } - - public class GuidList - { - public List Ids { get; set; } - } - - public class DateTimeTestStructure - { - public DateTime DateTime { get; set; } - public DateTime? NullableDateTimeWithNull { get; set; } - public DateTime? NullableDateTimeWithValue { get; set; } - public DateTimeOffset DateTimeOffset { get; set; } - public DateTimeOffset? NullableDateTimeOffsetWithNull { get; set; } - public DateTimeOffset? NullableDateTimeOffsetWithValue { get; set; } - } - - public class Iso8601DateTimeTestStructure - { - public DateTime DateTimeLocal { get; set; } - public DateTime DateTimeUtc { get; set; } - public DateTime DateTimeWithOffset { get; set; } - } - - public class TimeSpanTestStructure - { - public TimeSpan Tick { get; set; } - public TimeSpan Millisecond { get; set; } - public TimeSpan Second { get; set; } - public TimeSpan Minute { get; set; } - public TimeSpan Hour { get; set; } - public TimeSpan? NullableWithoutValue { get; set; } - public TimeSpan? NullableWithValue { get; set; } - } - - public class JsonEnumsTestStructure - { - public Disposition Upper { get; set; } - public Disposition Lower { get; set; } - public Disposition CamelCased { get; set; } - public Disposition Underscores { get; set; } - public Disposition LowerUnderscores { get; set; } - public Disposition Dashes { get; set; } - public Disposition LowerDashes { get; set; } - public Disposition Integer { get; set; } - } - - public class DecimalNumber - { - public decimal Value { get; set; } - } -} +#region Licensed +// Copyright 2010 John Sheehan +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +using System; +using System.Collections.Generic; +using RestSharp.Serializers; + +namespace RestSharp.Tests +{ + public class PersonForXml + { + public string Name { get; set; } + public DateTime StartDate { get; set; } + public int Age { get; set; } + public decimal Percent { get; set; } + public long BigNumber { get; set; } + public bool IsCool { get; set; } + public List Friends { get; set; } + public Friend BestFriend { get; set; } + + protected string Ignore { get; set; } + public string IgnoreProxy { get { return Ignore; } } + + protected string ReadOnly { get { return null; } } + public string ReadOnlyProxy { get { return ReadOnly; } } + + public FoeList Foes { get; set; } + + public Guid UniqueId { get; set; } + public Guid EmptyGuid { get; set; } + + public Uri Url { get; set; } + public Uri UrlPath { get; set; } + + public Order Order { get; set; } + + public Disposition Disposition { get; set; } + + } + + public class IncomingInvoice + { + public int ConceptId { get; set; } + } + + public class PersonForJson + { + public string Name { get; set; } + public DateTime StartDate { get; set; } + public int Age { get; set; } + public decimal Percent { get; set; } + public long BigNumber { get; set; } + public bool IsCool { get; set; } + public List Friends { get; set; } + public Friend BestFriend { get; set; } + public Guid Guid { get; set; } + public Guid EmptyGuid { get; set; } + public Uri Url { get; set; } + public Uri UrlPath { get; set; } + + protected string Ignore { get; set; } + public string IgnoreProxy { get { return Ignore; } } + + protected string ReadOnly { get { return null; } } + public string ReadOnlyProxy { get { return ReadOnly; } } + + public Dictionary Foes { get; set; } + + public Order Order { get; set; } + + public Disposition Disposition { get; set; } + } + + public enum Order + { + First, + Second, + Third + } + + public enum Disposition + { + Friendly, + SoSo, + SteerVeryClear + } + + public class Friend + { + public string Name { get; set; } + public int Since { get; set; } + } + + public class Foe + { + public string Nickname { get; set; } + } + + public class FoeList : List + { + public string Team { get; set; } + } + + public class Birthdate + { + public DateTime Value { get; set; } + } + + public class OrderedProperties + { + [SerializeAs(Index = 2)] + public string Name { get; set; } + [SerializeAs(Index = 3)] + public int Age { get; set; } + [SerializeAs(Index = 1)] + public DateTime StartDate { get; set; } + } + + public class ObjectProperties + { + public object ObjectProperty { get; set; } + } + + public class DatabaseCollection : List + { + } + + public class Database + { + public string Name { get; set; } + public string InitialCatalog { get; set; } + public string DataSource { get; set; } + } + + public class Generic + { + public T Data { get; set; } + } + + public class GenericWithList + { + public List Items { get; set; } + } + + public class GuidList + { + public List Ids { get; set; } + } + + public class DateTimeTestStructure + { + public DateTime DateTime { get; set; } + public DateTime? NullableDateTimeWithNull { get; set; } + public DateTime? NullableDateTimeWithValue { get; set; } + public DateTimeOffset DateTimeOffset { get; set; } + public DateTimeOffset? NullableDateTimeOffsetWithNull { get; set; } + public DateTimeOffset? NullableDateTimeOffsetWithValue { get; set; } + } + + public class Iso8601DateTimeTestStructure + { + public DateTime DateTimeLocal { get; set; } + public DateTime DateTimeUtc { get; set; } + public DateTime DateTimeWithOffset { get; set; } + } + + public class TimeSpanTestStructure + { + public TimeSpan Tick { get; set; } + public TimeSpan Millisecond { get; set; } + public TimeSpan Second { get; set; } + public TimeSpan Minute { get; set; } + public TimeSpan Hour { get; set; } + public TimeSpan? NullableWithoutValue { get; set; } + public TimeSpan? NullableWithValue { get; set; } + } + + public class JsonEnumsTestStructure + { + public Disposition Upper { get; set; } + public Disposition Lower { get; set; } + public Disposition CamelCased { get; set; } + public Disposition Underscores { get; set; } + public Disposition LowerUnderscores { get; set; } + public Disposition Dashes { get; set; } + public Disposition LowerDashes { get; set; } + public Disposition Integer { get; set; } + } + + public class DecimalNumber + { + public decimal Value { get; set; } + } +} diff --git a/RestSharp.Tests/XmlTests.cs b/RestSharp.Tests/XmlTests.cs index c5c942967..29abaf5b7 100644 --- a/RestSharp.Tests/XmlTests.cs +++ b/RestSharp.Tests/XmlTests.cs @@ -640,41 +640,41 @@ public void Can_Deserialize_Mixture_Of_Empty_Elements_With_Attributes_And_Popula Assert.Null(output.Id); Assert.Null(output.StartDate); Assert.Equal(new Guid(GuidString), output.UniqueId); - } - - [Fact] - public void Can_Deserialize_DateTimeOffset() - { - var culture = CultureInfo.InvariantCulture; - var doc = new XDocument(culture); - - DateTimeOffset DateTimeOffset = new DateTimeOffset(2013, 02, 08, 9, 18, 22, TimeSpan.FromHours(10)); - DateTimeOffset? NullableDateTimeOffsetWithValue = new DateTimeOffset(2013, 02, 08, 9, 18, 23, TimeSpan.FromHours(10)); - - var root = new XElement("Dates"); - root.Add(new XElement("DateTimeOffset", DateTimeOffset)); - root.Add(new XElement("NullableDateTimeOffsetWithNull", string.Empty)); - root.Add(new XElement("NullableDateTimeOffsetWithValue", NullableDateTimeOffsetWithValue)); - - doc.Add(root); - - var xml = new XmlDeserializer - { - Culture = culture, - }; - - var response = new RestResponse { Content = doc.ToString() }; - - var d = new XmlDeserializer() - { - Culture = culture, - }; - var payload = d.Deserialize(response); - Assert.Equal(DateTimeOffset, payload.DateTimeOffset); - Assert.Null(payload.NullableDateTimeOffsetWithNull); - - Assert.True(payload.NullableDateTimeOffsetWithValue.HasValue); - Assert.Equal(NullableDateTimeOffsetWithValue, payload.NullableDateTimeOffsetWithValue); + } + + [Fact] + public void Can_Deserialize_DateTimeOffset() + { + var culture = CultureInfo.InvariantCulture; + var doc = new XDocument(culture); + + DateTimeOffset DateTimeOffset = new DateTimeOffset(2013, 02, 08, 9, 18, 22, TimeSpan.FromHours(10)); + DateTimeOffset? NullableDateTimeOffsetWithValue = new DateTimeOffset(2013, 02, 08, 9, 18, 23, TimeSpan.FromHours(10)); + + var root = new XElement("Dates"); + root.Add(new XElement("DateTimeOffset", DateTimeOffset)); + root.Add(new XElement("NullableDateTimeOffsetWithNull", string.Empty)); + root.Add(new XElement("NullableDateTimeOffsetWithValue", NullableDateTimeOffsetWithValue)); + + doc.Add(root); + + var xml = new XmlDeserializer + { + Culture = culture, + }; + + var response = new RestResponse { Content = doc.ToString() }; + + var d = new XmlDeserializer() + { + Culture = culture, + }; + var payload = d.Deserialize(response); + Assert.Equal(DateTimeOffset, payload.DateTimeOffset); + Assert.Null(payload.NullableDateTimeOffsetWithNull); + + Assert.True(payload.NullableDateTimeOffsetWithValue.HasValue); + Assert.Equal(NullableDateTimeOffsetWithValue, payload.NullableDateTimeOffsetWithValue); } private static string CreateUnderscoresXml() diff --git a/RestSharp.WindowsPhone.Mango/RestSharp.WindowsPhone.Mango.csproj b/RestSharp.WindowsPhone.Mango/RestSharp.WindowsPhone.Mango.csproj index e6d331b70..48766029f 100644 --- a/RestSharp.WindowsPhone.Mango/RestSharp.WindowsPhone.Mango.csproj +++ b/RestSharp.WindowsPhone.Mango/RestSharp.WindowsPhone.Mango.csproj @@ -1,298 +1,298 @@ - - - - Debug - AnyCPU - 10.0.20506 - 2.0 - {71647E33-714F-4975-8368-71B075045272} - {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - RestSharp.WindowsPhone - RestSharp.WindowsPhone - v4.0 - $(TargetFrameworkVersion) - WindowsPhone71 - Silverlight - false - true - true - - - true - full - false - Bin\Debug - TRACE;DEBUG;WINDOWS_PHONE;MANGO - true - true - prompt - 4 - Bin\Debug\RestSharp.WindowsPhone.xml - - - pdbonly - true - Bin\Release - TRACE;WINDOWS_PHONE - true - true - prompt - 4 - Bin\Release\RestSharp.WindowsPhone.xml - - - - - - - - - - - - - - Authenticators\HttpBasicAuthenticator.cs - - - Authenticators\IAuthenticator.cs - - - Authenticators\NtlmAuthenticator.cs - - - Authenticators\OAuth1Authenticator.cs - - - Authenticators\OAuth2Authenticator.cs - - - Authenticators\OAuth\Extensions\CollectionExtensions.cs - - - Authenticators\OAuth\Extensions\OAuthExtensions.cs - - - Authenticators\OAuth\Extensions\StringExtensions.cs - - - Authenticators\OAuth\Extensions\TimeExtensions.cs - - - Authenticators\OAuth\HttpPostParameter.cs - - - Authenticators\OAuth\HttpPostParameterType.cs - - - Authenticators\OAuth\OAuthParameterHandling.cs - - - Authenticators\OAuth\OAuthSignatureMethod.cs - - - Authenticators\OAuth\OAuthSignatureTreatment.cs - - - Authenticators\OAuth\OAuthTools.cs - - - Authenticators\OAuth\OAuthType.cs - - - Authenticators\OAuth\OAuthWebQueryInfo.cs - - - Authenticators\OAuth\OAuthWorkflow.cs - - - Authenticators\OAuth\WebPair.cs - - - Authenticators\OAuth\WebPairCollection.cs - - - Authenticators\OAuth\WebParameter.cs - - - Authenticators\OAuth\WebParameterCollection.cs - - - Authenticators\SimpleAuthenticator.cs - - - Compression\ZLib\Crc32.cs - - - Compression\ZLib\FlushType.cs - - - Compression\ZLib\GZipStream.cs - - - Compression\ZLib\Inflate.cs - - - Compression\ZLib\InfTree.cs - - - Compression\ZLib\ZLib.cs - - - Compression\ZLib\ZLibCodec.cs - - - Compression\ZLib\ZLibConstants.cs - - - Compression\ZLib\ZLibStream.cs - - - Deserializers\DeserializeAsAttribute.cs - - - Deserializers\DotNetXmlDeserializer.cs - - - Deserializers\IDeserializer.cs - - - Deserializers\JsonDeserializer.cs - - - Deserializers\XmlAttributeDeserializer.cs - - - Deserializers\XmlDeserializer.cs - - - Enum.cs - - - Extensions\MiscExtensions.cs - - - Extensions\ReflectionExtensions.cs - - - Extensions\StringExtensions.cs - - - Extensions\ResponseExtensions.cs - - - Extensions\XmlExtensions.cs - - - FileParameter.cs - - - Http.Async.cs - - - Http.cs - - - HttpCookie.cs - - - HttpFile.cs - - - HttpHeader.cs - - - HttpParameter.cs - - - HttpResponse.cs - - - IHttp.cs - - - IHttpFactory.cs - - - IHttpResponse.cs - - - IRestClient.cs - - - IRestRequest.cs - - - IRestResponse.cs - - - Parameter.cs - - - RestClient.Async.cs - - - RestClient.cs - - - RestClientExtensions.cs - - - RestRequest.cs - - - RestResponse.cs - - - RestResponseCookie.cs - - - Serializers\DotNetXmlSerializer.cs - - - Serializers\ISerializer.cs - - - Serializers\JsonSerializer.cs - - - Serializers\SerializeAsAttribute.cs - - - Serializers\XmlSerializer.cs - - - SharedAssemblyInfo.cs - - - SimpleJson.cs - - - Validation\Require.cs - - - Validation\Validate.cs - - - RestRequestAsyncHandle.cs - - - - - - Designer - - - - - + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {71647E33-714F-4975-8368-71B075045272} + {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + RestSharp.WindowsPhone + RestSharp.WindowsPhone + v4.0 + $(TargetFrameworkVersion) + WindowsPhone71 + Silverlight + false + true + true + + + true + full + false + Bin\Debug + TRACE;DEBUG;WINDOWS_PHONE;MANGO + true + true + prompt + 4 + Bin\Debug\RestSharp.WindowsPhone.xml + + + pdbonly + true + Bin\Release + TRACE;WINDOWS_PHONE + true + true + prompt + 4 + Bin\Release\RestSharp.WindowsPhone.xml + + + + + + + + + + + + + + Authenticators\HttpBasicAuthenticator.cs + + + Authenticators\IAuthenticator.cs + + + Authenticators\NtlmAuthenticator.cs + + + Authenticators\OAuth1Authenticator.cs + + + Authenticators\OAuth2Authenticator.cs + + + Authenticators\OAuth\Extensions\CollectionExtensions.cs + + + Authenticators\OAuth\Extensions\OAuthExtensions.cs + + + Authenticators\OAuth\Extensions\StringExtensions.cs + + + Authenticators\OAuth\Extensions\TimeExtensions.cs + + + Authenticators\OAuth\HttpPostParameter.cs + + + Authenticators\OAuth\HttpPostParameterType.cs + + + Authenticators\OAuth\OAuthParameterHandling.cs + + + Authenticators\OAuth\OAuthSignatureMethod.cs + + + Authenticators\OAuth\OAuthSignatureTreatment.cs + + + Authenticators\OAuth\OAuthTools.cs + + + Authenticators\OAuth\OAuthType.cs + + + Authenticators\OAuth\OAuthWebQueryInfo.cs + + + Authenticators\OAuth\OAuthWorkflow.cs + + + Authenticators\OAuth\WebPair.cs + + + Authenticators\OAuth\WebPairCollection.cs + + + Authenticators\OAuth\WebParameter.cs + + + Authenticators\OAuth\WebParameterCollection.cs + + + Authenticators\SimpleAuthenticator.cs + + + Compression\ZLib\Crc32.cs + + + Compression\ZLib\FlushType.cs + + + Compression\ZLib\GZipStream.cs + + + Compression\ZLib\Inflate.cs + + + Compression\ZLib\InfTree.cs + + + Compression\ZLib\ZLib.cs + + + Compression\ZLib\ZLibCodec.cs + + + Compression\ZLib\ZLibConstants.cs + + + Compression\ZLib\ZLibStream.cs + + + Deserializers\DeserializeAsAttribute.cs + + + Deserializers\DotNetXmlDeserializer.cs + + + Deserializers\IDeserializer.cs + + + Deserializers\JsonDeserializer.cs + + + Deserializers\XmlAttributeDeserializer.cs + + + Deserializers\XmlDeserializer.cs + + + Enum.cs + + + Extensions\MiscExtensions.cs + + + Extensions\ReflectionExtensions.cs + + + Extensions\StringExtensions.cs + + + Extensions\ResponseExtensions.cs + + + Extensions\XmlExtensions.cs + + + FileParameter.cs + + + Http.Async.cs + + + Http.cs + + + HttpCookie.cs + + + HttpFile.cs + + + HttpHeader.cs + + + HttpParameter.cs + + + HttpResponse.cs + + + IHttp.cs + + + IHttpFactory.cs + + + IHttpResponse.cs + + + IRestClient.cs + + + IRestRequest.cs + + + IRestResponse.cs + + + Parameter.cs + + + RestClient.Async.cs + + + RestClient.cs + + + RestClientExtensions.cs + + + RestRequest.cs + + + RestResponse.cs + + + RestResponseCookie.cs + + + Serializers\DotNetXmlSerializer.cs + + + Serializers\ISerializer.cs + + + Serializers\JsonSerializer.cs + + + Serializers\SerializeAsAttribute.cs + + + Serializers\XmlSerializer.cs + + + SharedAssemblyInfo.cs + + + SimpleJson.cs + + + Validation\Require.cs + + + Validation\Validate.cs + + + RestRequestAsyncHandle.cs + + + + + + Designer + + + + + + --> \ No newline at end of file diff --git a/RestSharp/Authenticators/OAuth/HttpPostParameterType.cs b/RestSharp/Authenticators/OAuth/HttpPostParameterType.cs index 114330a81..19d75efda 100644 --- a/RestSharp/Authenticators/OAuth/HttpPostParameterType.cs +++ b/RestSharp/Authenticators/OAuth/HttpPostParameterType.cs @@ -1,13 +1,13 @@ -using System; - -namespace RestSharp.Authenticators.OAuth -{ -#if !SILVERLIGHT && !WINDOWS_PHONE - [Serializable] -#endif - internal enum HttpPostParameterType - { - Field, - File - } +using System; + +namespace RestSharp.Authenticators.OAuth +{ +#if !SILVERLIGHT && !WINDOWS_PHONE + [Serializable] +#endif + internal enum HttpPostParameterType + { + Field, + File + } } \ No newline at end of file diff --git a/RestSharp/Authenticators/OAuth/OAuthParameterHandling.cs b/RestSharp/Authenticators/OAuth/OAuthParameterHandling.cs index 6c7c85997..ae5c42604 100644 --- a/RestSharp/Authenticators/OAuth/OAuthParameterHandling.cs +++ b/RestSharp/Authenticators/OAuth/OAuthParameterHandling.cs @@ -1,13 +1,13 @@ -using System; - -namespace RestSharp.Authenticators.OAuth -{ -#if !SILVERLIGHT && !WINDOWS_PHONE - [Serializable] -#endif - public enum OAuthParameterHandling - { - HttpAuthorizationHeader, - UrlOrPostParameters - } +using System; + +namespace RestSharp.Authenticators.OAuth +{ +#if !SILVERLIGHT && !WINDOWS_PHONE + [Serializable] +#endif + public enum OAuthParameterHandling + { + HttpAuthorizationHeader, + UrlOrPostParameters + } } \ No newline at end of file diff --git a/RestSharp/Authenticators/OAuth/OAuthSignatureMethod.cs b/RestSharp/Authenticators/OAuth/OAuthSignatureMethod.cs index c22a80555..3ba76ca65 100644 --- a/RestSharp/Authenticators/OAuth/OAuthSignatureMethod.cs +++ b/RestSharp/Authenticators/OAuth/OAuthSignatureMethod.cs @@ -1,14 +1,14 @@ -using System; - -namespace RestSharp.Authenticators.OAuth -{ -#if !SILVERLIGHT && !WINDOWS_PHONE - [Serializable] -#endif - public enum OAuthSignatureMethod - { - HmacSha1, - PlainText, - RsaSha1 - } +using System; + +namespace RestSharp.Authenticators.OAuth +{ +#if !SILVERLIGHT && !WINDOWS_PHONE + [Serializable] +#endif + public enum OAuthSignatureMethod + { + HmacSha1, + PlainText, + RsaSha1 + } } \ No newline at end of file diff --git a/RestSharp/Authenticators/OAuth/OAuthSignatureTreatment.cs b/RestSharp/Authenticators/OAuth/OAuthSignatureTreatment.cs index b416b33f0..a13b32c74 100644 --- a/RestSharp/Authenticators/OAuth/OAuthSignatureTreatment.cs +++ b/RestSharp/Authenticators/OAuth/OAuthSignatureTreatment.cs @@ -1,13 +1,13 @@ -using System; - -namespace RestSharp.Authenticators.OAuth -{ -#if !SILVERLIGHT && !WINDOWS_PHONE - [Serializable] -#endif - public enum OAuthSignatureTreatment - { - Escaped, - Unescaped - } +using System; + +namespace RestSharp.Authenticators.OAuth +{ +#if !SILVERLIGHT && !WINDOWS_PHONE + [Serializable] +#endif + public enum OAuthSignatureTreatment + { + Escaped, + Unescaped + } } \ No newline at end of file diff --git a/RestSharp/Authenticators/OAuth/OAuthTools.cs b/RestSharp/Authenticators/OAuth/OAuthTools.cs index b6a62e27f..c81c4510c 100644 --- a/RestSharp/Authenticators/OAuth/OAuthTools.cs +++ b/RestSharp/Authenticators/OAuth/OAuthTools.cs @@ -1,335 +1,335 @@ -using System; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using RestSharp.Authenticators.OAuth.Extensions; - -namespace RestSharp.Authenticators.OAuth -{ -#if !SILVERLIGHT && !WINDOWS_PHONE - [Serializable] -#endif - internal static class OAuthTools - { - private const string AlphaNumeric = Upper + Lower + Digit; - private const string Digit = "1234567890"; - private const string Lower = "abcdefghijklmnopqrstuvwxyz"; - private const string Unreserved = AlphaNumeric + "-._~"; - private const string Upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - private static readonly Random _random; - private static readonly object _randomLock = new object(); - -#if !SILVERLIGHT && !WINDOWS_PHONE - private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create(); -#endif - - static OAuthTools() - { -#if !SILVERLIGHT && !WINDOWS_PHONE - var bytes = new byte[4]; - _rng.GetNonZeroBytes(bytes); - _random = new Random(BitConverter.ToInt32(bytes, 0)); -#else - _random = new Random(); -#endif - } - - /// - /// All text parameters are UTF-8 encoded (per section 5.1). - /// - /// - private static readonly Encoding _encoding = Encoding.UTF8; - - /// - /// Generates a random 16-byte lowercase alphanumeric string. - /// - /// - /// - public static string GetNonce() - { - const string chars = (Lower + Digit); - - var nonce = new char[16]; - lock (_randomLock) - { - for (var i = 0; i < nonce.Length; i++) - { - nonce[i] = chars[_random.Next(0, chars.Length)]; - } - } - return new string(nonce); - } - - /// - /// Generates a timestamp based on the current elapsed seconds since '01/01/1970 0000 GMT" - /// - /// - /// - public static string GetTimestamp() - { - return GetTimestamp(DateTime.UtcNow); - } - - /// - /// Generates a timestamp based on the elapsed seconds of a given time since '01/01/1970 0000 GMT" - /// - /// - /// A specified point in time. - /// - public static string GetTimestamp(DateTime dateTime) - { - var timestamp = dateTime.ToUnixTime(); - return timestamp.ToString(); - } - - /// - /// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986. - /// - /// - private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" }; - - private static readonly string[] UriRfc3968EscapedHex = new[] {"%21", "%2A", "%27", "%28", "%29"}; - - /// - /// URL encodes a string based on section 5.1 of the OAuth spec. - /// Namely, percent encoding with [RFC3986], avoiding unreserved characters, - /// upper-casing hexadecimal characters, and UTF-8 encoding for text value pairs. - /// - /// The value to escape. - /// The escaped value. - /// - /// The method is supposed to take on - /// RFC 3986 behavior if certain elements are present in a .config file. Even if this - /// actually worked (which in my experiments it doesn't), we can't rely on every - /// host actually having this configuration element present. - /// - /// - /// - public static string UrlEncodeRelaxed(string value) - { - // Start with RFC 2396 escaping by calling the .NET method to do the work. - // This MAY sometimes exhibit RFC 3986 behavior (according to the documentation). - // If it does, the escaping we do that follows it will be a no-op since the - // characters we search for to replace can't possibly exist in the string. - StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value)); - - // Upgrade the escaping to RFC 3986, if necessary. - for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++) - { - string t = UriRfc3986CharsToEscape[i]; - escaped.Replace(t, UriRfc3968EscapedHex[i]); - } - - // Return the fully-RFC3986-escaped string. - return escaped.ToString(); - } - - /// - /// URL encodes a string based on section 5.1 of the OAuth spec. - /// Namely, percent encoding with [RFC3986], avoiding unreserved characters, - /// upper-casing hexadecimal characters, and UTF-8 encoding for text value pairs. - /// - /// - /// - public static string UrlEncodeStrict(string value) - { - // [JD]: We need to escape the apostrophe as well or the signature will fail - var original = value; - var ret = original.Where( - c => !Unreserved.Contains(c) && c != '%').Aggregate( - value, (current, c) => current.Replace( - c.ToString(), c.ToString().PercentEncode() - )); - - return ret.Replace("%%", "%25%"); // Revisit to encode actual %'s - } - - /// - /// Sorts a collection of key-value pairs by name, and then value if equal, - /// concatenating them into a single string. This string should be encoded - /// prior to, or after normalization is run. - /// - /// - /// - /// - public static string NormalizeRequestParameters(WebParameterCollection parameters) - { - var copy = SortParametersExcludingSignature(parameters); - var concatenated = copy.Concatenate("=", "&"); - return concatenated; - } - - /// - /// Sorts a by name, and then value if equal. - /// - /// A collection of parameters to sort - /// A sorted parameter collection - public static WebParameterCollection SortParametersExcludingSignature(WebParameterCollection parameters) - { - var copy = new WebParameterCollection(parameters); - var exclusions = copy.Where(n => n.Name.EqualsIgnoreCase("oauth_signature")); - - copy.RemoveAll(exclusions); - copy.ForEach(p => { p.Name = UrlEncodeStrict(p.Name); p.Value = UrlEncodeStrict(p.Value); }); - copy.Sort( - (x, y) => - string.CompareOrdinal(x.Name, y.Name) != 0 - ? string.CompareOrdinal(x.Name, y.Name) - : string.CompareOrdinal(x.Value, y.Value)); - return copy; - } - - /// - /// Creates a request URL suitable for making OAuth requests. - /// Resulting URLs must exclude port 80 or port 443 when accompanied by HTTP and HTTPS, respectively. - /// Resulting URLs must be lower case. - /// - /// - /// The original request URL - /// - public static string ConstructRequestUrl(Uri url) - { - if (url == null) - { - throw new ArgumentNullException("url"); - } - - var sb = new StringBuilder(); - - var requestUrl = "{0}://{1}".FormatWith(url.Scheme, url.Host); - var qualified = ":{0}".FormatWith(url.Port); - var basic = url.Scheme == "http" && url.Port == 80; - var secure = url.Scheme == "https" && url.Port == 443; - - sb.Append(requestUrl); - sb.Append(!basic && !secure ? qualified : ""); - sb.Append(url.AbsolutePath); - - return sb.ToString(); //.ToLower(); - } - - /// - /// Creates a request elements concatentation value to send with a request. - /// This is also known as the signature base. - /// - /// - /// - /// The request's HTTP method type - /// The request URL - /// The request's parameters - /// A signature base string - public static string ConcatenateRequestElements(string method, string url, WebParameterCollection parameters) - { - var sb = new StringBuilder(); - - // Separating &'s are not URL encoded - var requestMethod = method.ToUpper().Then("&"); - var requestUrl = UrlEncodeRelaxed(ConstructRequestUrl(url.AsUri())).Then("&"); - var requestParameters = UrlEncodeRelaxed(NormalizeRequestParameters(parameters)); - - sb.Append(requestMethod); - sb.Append(requestUrl); - sb.Append(requestParameters); - - return sb.ToString(); - } - - /// - /// Creates a signature value given a signature base and the consumer secret. - /// This method is used when the token secret is currently unknown. - /// - /// - /// The hashing method - /// The signature base - /// The consumer key - /// - public static string GetSignature(OAuthSignatureMethod signatureMethod, string signatureBase, string consumerSecret) - { - return GetSignature(signatureMethod, OAuthSignatureTreatment.Escaped, signatureBase, consumerSecret, null); - } - - /// - /// Creates a signature value given a signature base and the consumer secret. - /// This method is used when the token secret is currently unknown. - /// - /// - /// The hashing method - /// The treatment to use on a signature value - /// The signature base - /// The consumer key - /// - public static string GetSignature(OAuthSignatureMethod signatureMethod, OAuthSignatureTreatment signatureTreatment, string signatureBase, string consumerSecret) - { - return GetSignature(signatureMethod, signatureTreatment, signatureBase, consumerSecret, null); - } - - /// - /// Creates a signature value given a signature base and the consumer secret and a known token secret. - /// - /// - /// The hashing method - /// The signature base - /// The consumer secret - /// The token secret - /// - public static string GetSignature(OAuthSignatureMethod signatureMethod, string signatureBase, string consumerSecret, string tokenSecret) - { - return GetSignature(signatureMethod, OAuthSignatureTreatment.Escaped, consumerSecret, tokenSecret); - } - - /// - /// Creates a signature value given a signature base and the consumer secret and a known token secret. - /// - /// - /// The hashing method - /// The treatment to use on a signature value - /// The signature base - /// The consumer secret - /// The token secret - /// - public static string GetSignature(OAuthSignatureMethod signatureMethod, - OAuthSignatureTreatment signatureTreatment, - string signatureBase, - string consumerSecret, - string tokenSecret) - { - if (tokenSecret.IsNullOrBlank()) - { - tokenSecret = String.Empty; - } - - consumerSecret = UrlEncodeRelaxed(consumerSecret); - tokenSecret = UrlEncodeRelaxed(tokenSecret); - - string signature; - switch (signatureMethod) - { - case OAuthSignatureMethod.HmacSha1: - { - var crypto = new HMACSHA1(); - var key = "{0}&{1}".FormatWith(consumerSecret, tokenSecret); - - crypto.Key = _encoding.GetBytes(key); - signature = signatureBase.HashWith(crypto); - - break; - } - case OAuthSignatureMethod.PlainText: - { - signature = "{0}&{1}".FormatWith(consumerSecret, tokenSecret); - - break; - } - default: - throw new NotImplementedException("Only HMAC-SHA1 is currently supported."); - } - - var result = signatureTreatment == OAuthSignatureTreatment.Escaped - ? UrlEncodeRelaxed(signature) - : signature; - - return result; - } - } +using System; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using RestSharp.Authenticators.OAuth.Extensions; + +namespace RestSharp.Authenticators.OAuth +{ +#if !SILVERLIGHT && !WINDOWS_PHONE + [Serializable] +#endif + internal static class OAuthTools + { + private const string AlphaNumeric = Upper + Lower + Digit; + private const string Digit = "1234567890"; + private const string Lower = "abcdefghijklmnopqrstuvwxyz"; + private const string Unreserved = AlphaNumeric + "-._~"; + private const string Upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + private static readonly Random _random; + private static readonly object _randomLock = new object(); + +#if !SILVERLIGHT && !WINDOWS_PHONE + private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create(); +#endif + + static OAuthTools() + { +#if !SILVERLIGHT && !WINDOWS_PHONE + var bytes = new byte[4]; + _rng.GetNonZeroBytes(bytes); + _random = new Random(BitConverter.ToInt32(bytes, 0)); +#else + _random = new Random(); +#endif + } + + /// + /// All text parameters are UTF-8 encoded (per section 5.1). + /// + /// + private static readonly Encoding _encoding = Encoding.UTF8; + + /// + /// Generates a random 16-byte lowercase alphanumeric string. + /// + /// + /// + public static string GetNonce() + { + const string chars = (Lower + Digit); + + var nonce = new char[16]; + lock (_randomLock) + { + for (var i = 0; i < nonce.Length; i++) + { + nonce[i] = chars[_random.Next(0, chars.Length)]; + } + } + return new string(nonce); + } + + /// + /// Generates a timestamp based on the current elapsed seconds since '01/01/1970 0000 GMT" + /// + /// + /// + public static string GetTimestamp() + { + return GetTimestamp(DateTime.UtcNow); + } + + /// + /// Generates a timestamp based on the elapsed seconds of a given time since '01/01/1970 0000 GMT" + /// + /// + /// A specified point in time. + /// + public static string GetTimestamp(DateTime dateTime) + { + var timestamp = dateTime.ToUnixTime(); + return timestamp.ToString(); + } + + /// + /// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986. + /// + /// + private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" }; + + private static readonly string[] UriRfc3968EscapedHex = new[] {"%21", "%2A", "%27", "%28", "%29"}; + + /// + /// URL encodes a string based on section 5.1 of the OAuth spec. + /// Namely, percent encoding with [RFC3986], avoiding unreserved characters, + /// upper-casing hexadecimal characters, and UTF-8 encoding for text value pairs. + /// + /// The value to escape. + /// The escaped value. + /// + /// The method is supposed to take on + /// RFC 3986 behavior if certain elements are present in a .config file. Even if this + /// actually worked (which in my experiments it doesn't), we can't rely on every + /// host actually having this configuration element present. + /// + /// + /// + public static string UrlEncodeRelaxed(string value) + { + // Start with RFC 2396 escaping by calling the .NET method to do the work. + // This MAY sometimes exhibit RFC 3986 behavior (according to the documentation). + // If it does, the escaping we do that follows it will be a no-op since the + // characters we search for to replace can't possibly exist in the string. + StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value)); + + // Upgrade the escaping to RFC 3986, if necessary. + for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++) + { + string t = UriRfc3986CharsToEscape[i]; + escaped.Replace(t, UriRfc3968EscapedHex[i]); + } + + // Return the fully-RFC3986-escaped string. + return escaped.ToString(); + } + + /// + /// URL encodes a string based on section 5.1 of the OAuth spec. + /// Namely, percent encoding with [RFC3986], avoiding unreserved characters, + /// upper-casing hexadecimal characters, and UTF-8 encoding for text value pairs. + /// + /// + /// + public static string UrlEncodeStrict(string value) + { + // [JD]: We need to escape the apostrophe as well or the signature will fail + var original = value; + var ret = original.Where( + c => !Unreserved.Contains(c) && c != '%').Aggregate( + value, (current, c) => current.Replace( + c.ToString(), c.ToString().PercentEncode() + )); + + return ret.Replace("%%", "%25%"); // Revisit to encode actual %'s + } + + /// + /// Sorts a collection of key-value pairs by name, and then value if equal, + /// concatenating them into a single string. This string should be encoded + /// prior to, or after normalization is run. + /// + /// + /// + /// + public static string NormalizeRequestParameters(WebParameterCollection parameters) + { + var copy = SortParametersExcludingSignature(parameters); + var concatenated = copy.Concatenate("=", "&"); + return concatenated; + } + + /// + /// Sorts a by name, and then value if equal. + /// + /// A collection of parameters to sort + /// A sorted parameter collection + public static WebParameterCollection SortParametersExcludingSignature(WebParameterCollection parameters) + { + var copy = new WebParameterCollection(parameters); + var exclusions = copy.Where(n => n.Name.EqualsIgnoreCase("oauth_signature")); + + copy.RemoveAll(exclusions); + copy.ForEach(p => { p.Name = UrlEncodeStrict(p.Name); p.Value = UrlEncodeStrict(p.Value); }); + copy.Sort( + (x, y) => + string.CompareOrdinal(x.Name, y.Name) != 0 + ? string.CompareOrdinal(x.Name, y.Name) + : string.CompareOrdinal(x.Value, y.Value)); + return copy; + } + + /// + /// Creates a request URL suitable for making OAuth requests. + /// Resulting URLs must exclude port 80 or port 443 when accompanied by HTTP and HTTPS, respectively. + /// Resulting URLs must be lower case. + /// + /// + /// The original request URL + /// + public static string ConstructRequestUrl(Uri url) + { + if (url == null) + { + throw new ArgumentNullException("url"); + } + + var sb = new StringBuilder(); + + var requestUrl = "{0}://{1}".FormatWith(url.Scheme, url.Host); + var qualified = ":{0}".FormatWith(url.Port); + var basic = url.Scheme == "http" && url.Port == 80; + var secure = url.Scheme == "https" && url.Port == 443; + + sb.Append(requestUrl); + sb.Append(!basic && !secure ? qualified : ""); + sb.Append(url.AbsolutePath); + + return sb.ToString(); //.ToLower(); + } + + /// + /// Creates a request elements concatentation value to send with a request. + /// This is also known as the signature base. + /// + /// + /// + /// The request's HTTP method type + /// The request URL + /// The request's parameters + /// A signature base string + public static string ConcatenateRequestElements(string method, string url, WebParameterCollection parameters) + { + var sb = new StringBuilder(); + + // Separating &'s are not URL encoded + var requestMethod = method.ToUpper().Then("&"); + var requestUrl = UrlEncodeRelaxed(ConstructRequestUrl(url.AsUri())).Then("&"); + var requestParameters = UrlEncodeRelaxed(NormalizeRequestParameters(parameters)); + + sb.Append(requestMethod); + sb.Append(requestUrl); + sb.Append(requestParameters); + + return sb.ToString(); + } + + /// + /// Creates a signature value given a signature base and the consumer secret. + /// This method is used when the token secret is currently unknown. + /// + /// + /// The hashing method + /// The signature base + /// The consumer key + /// + public static string GetSignature(OAuthSignatureMethod signatureMethod, string signatureBase, string consumerSecret) + { + return GetSignature(signatureMethod, OAuthSignatureTreatment.Escaped, signatureBase, consumerSecret, null); + } + + /// + /// Creates a signature value given a signature base and the consumer secret. + /// This method is used when the token secret is currently unknown. + /// + /// + /// The hashing method + /// The treatment to use on a signature value + /// The signature base + /// The consumer key + /// + public static string GetSignature(OAuthSignatureMethod signatureMethod, OAuthSignatureTreatment signatureTreatment, string signatureBase, string consumerSecret) + { + return GetSignature(signatureMethod, signatureTreatment, signatureBase, consumerSecret, null); + } + + /// + /// Creates a signature value given a signature base and the consumer secret and a known token secret. + /// + /// + /// The hashing method + /// The signature base + /// The consumer secret + /// The token secret + /// + public static string GetSignature(OAuthSignatureMethod signatureMethod, string signatureBase, string consumerSecret, string tokenSecret) + { + return GetSignature(signatureMethod, OAuthSignatureTreatment.Escaped, consumerSecret, tokenSecret); + } + + /// + /// Creates a signature value given a signature base and the consumer secret and a known token secret. + /// + /// + /// The hashing method + /// The treatment to use on a signature value + /// The signature base + /// The consumer secret + /// The token secret + /// + public static string GetSignature(OAuthSignatureMethod signatureMethod, + OAuthSignatureTreatment signatureTreatment, + string signatureBase, + string consumerSecret, + string tokenSecret) + { + if (tokenSecret.IsNullOrBlank()) + { + tokenSecret = String.Empty; + } + + consumerSecret = UrlEncodeRelaxed(consumerSecret); + tokenSecret = UrlEncodeRelaxed(tokenSecret); + + string signature; + switch (signatureMethod) + { + case OAuthSignatureMethod.HmacSha1: + { + var crypto = new HMACSHA1(); + var key = "{0}&{1}".FormatWith(consumerSecret, tokenSecret); + + crypto.Key = _encoding.GetBytes(key); + signature = signatureBase.HashWith(crypto); + + break; + } + case OAuthSignatureMethod.PlainText: + { + signature = "{0}&{1}".FormatWith(consumerSecret, tokenSecret); + + break; + } + default: + throw new NotImplementedException("Only HMAC-SHA1 is currently supported."); + } + + var result = signatureTreatment == OAuthSignatureTreatment.Escaped + ? UrlEncodeRelaxed(signature) + : signature; + + return result; + } + } } \ No newline at end of file diff --git a/RestSharp/Authenticators/OAuth/OAuthType.cs b/RestSharp/Authenticators/OAuth/OAuthType.cs index 78badc2b9..33c6f0fda 100644 --- a/RestSharp/Authenticators/OAuth/OAuthType.cs +++ b/RestSharp/Authenticators/OAuth/OAuthType.cs @@ -1,15 +1,15 @@ -using System; - -namespace RestSharp.Authenticators.OAuth -{ -#if !SILVERLIGHT && !WINDOWS_PHONE - [Serializable] -#endif - public enum OAuthType - { - RequestToken, - AccessToken, - ProtectedResource, - ClientAuthentication - } +using System; + +namespace RestSharp.Authenticators.OAuth +{ +#if !SILVERLIGHT && !WINDOWS_PHONE + [Serializable] +#endif + public enum OAuthType + { + RequestToken, + AccessToken, + ProtectedResource, + ClientAuthentication + } } \ No newline at end of file diff --git a/RestSharp/Authenticators/OAuth/OAuthWebQueryInfo.cs b/RestSharp/Authenticators/OAuth/OAuthWebQueryInfo.cs index 35c82446b..865555f75 100644 --- a/RestSharp/Authenticators/OAuth/OAuthWebQueryInfo.cs +++ b/RestSharp/Authenticators/OAuth/OAuthWebQueryInfo.cs @@ -1,29 +1,29 @@ -using System; - -namespace RestSharp.Authenticators.OAuth -{ -#if !SILVERLIGHT && !WINDOWS_PHONE - [Serializable] -#endif - public class OAuthWebQueryInfo - { - public virtual string ConsumerKey { get; set; } - public virtual string Token { get; set; } - public virtual string Nonce { get; set; } - public virtual string Timestamp { get; set; } - public virtual string SignatureMethod { get; set; } - public virtual string Signature { get; set; } - public virtual string Version { get; set; } - public virtual string Callback { get; set; } - public virtual string Verifier { get; set; } - public virtual string ClientMode { get; set; } - public virtual string ClientUsername { get; set; } - public virtual string ClientPassword { get; set; } - public virtual string UserAgent { get; set; } - public virtual string WebMethod { get; set; } - public virtual OAuthParameterHandling ParameterHandling { get; set; } - public virtual OAuthSignatureTreatment SignatureTreatment { get; set; } - internal virtual string ConsumerSecret { get; set; } - internal virtual string TokenSecret { get; set; } - } +using System; + +namespace RestSharp.Authenticators.OAuth +{ +#if !SILVERLIGHT && !WINDOWS_PHONE + [Serializable] +#endif + public class OAuthWebQueryInfo + { + public virtual string ConsumerKey { get; set; } + public virtual string Token { get; set; } + public virtual string Nonce { get; set; } + public virtual string Timestamp { get; set; } + public virtual string SignatureMethod { get; set; } + public virtual string Signature { get; set; } + public virtual string Version { get; set; } + public virtual string Callback { get; set; } + public virtual string Verifier { get; set; } + public virtual string ClientMode { get; set; } + public virtual string ClientUsername { get; set; } + public virtual string ClientPassword { get; set; } + public virtual string UserAgent { get; set; } + public virtual string WebMethod { get; set; } + public virtual OAuthParameterHandling ParameterHandling { get; set; } + public virtual OAuthSignatureTreatment SignatureTreatment { get; set; } + internal virtual string ConsumerSecret { get; set; } + internal virtual string TokenSecret { get; set; } + } } \ No newline at end of file diff --git a/RestSharp/Authenticators/OAuth/OAuthWorkflow.cs b/RestSharp/Authenticators/OAuth/OAuthWorkflow.cs index 5798bbb15..f0fd3241a 100644 --- a/RestSharp/Authenticators/OAuth/OAuthWorkflow.cs +++ b/RestSharp/Authenticators/OAuth/OAuthWorkflow.cs @@ -1,404 +1,404 @@ -using System; -using System.Collections.Generic; -using RestSharp.Authenticators.OAuth.Extensions; -#if !WINDOWS_PHONE && !SILVERLIGHT -using RestSharp.Contrib; -#endif - -namespace RestSharp.Authenticators.OAuth -{ - /// - /// A class to encapsulate OAuth authentication flow. - /// - /// - internal class OAuthWorkflow - { - public virtual string Version { get; set; } - public virtual string ConsumerKey { get; set; } - public virtual string ConsumerSecret { get; set; } - public virtual string Token { get; set; } - public virtual string TokenSecret { get; set; } - public virtual string CallbackUrl { get; set; } - public virtual string Verifier { get; set; } - public virtual string SessionHandle { get; set; } - - public virtual OAuthSignatureMethod SignatureMethod { get; set; } - public virtual OAuthSignatureTreatment SignatureTreatment { get; set; } - public virtual OAuthParameterHandling ParameterHandling { get; set; } - - public virtual string ClientUsername { get; set; } - public virtual string ClientPassword { get; set; } - - /// - public virtual string RequestTokenUrl { get; set; } - - /// - public virtual string AccessTokenUrl { get; set; } - - /// - public virtual string AuthorizationUrl { get; set; } - - /// - /// Generates a instance to pass to an - /// for the purpose of requesting an - /// unauthorized request token. - /// - /// The HTTP method for the intended request - /// - /// - public OAuthWebQueryInfo BuildRequestTokenInfo(string method) - { - return BuildRequestTokenInfo(method, null); - } - - /// - /// Generates a instance to pass to an - /// for the purpose of requesting an - /// unauthorized request token. - /// - /// The HTTP method for the intended request - /// Any existing, non-OAuth query parameters desired in the request - /// - /// - public virtual OAuthWebQueryInfo BuildRequestTokenInfo(string method, WebParameterCollection parameters) - { - ValidateTokenRequestState(); - - if (parameters == null) - { - parameters = new WebParameterCollection(); - } - - var timestamp = OAuthTools.GetTimestamp(); - var nonce = OAuthTools.GetNonce(); - - AddAuthParameters(parameters, timestamp, nonce); - - var signatureBase = OAuthTools.ConcatenateRequestElements(method, RequestTokenUrl, parameters); - var signature = OAuthTools.GetSignature(SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret); - - var info = new OAuthWebQueryInfo - { - WebMethod = method, - ParameterHandling = ParameterHandling, - ConsumerKey = ConsumerKey, - SignatureMethod = SignatureMethod.ToRequestValue(), - SignatureTreatment = SignatureTreatment, - Signature = signature, - Timestamp = timestamp, - Nonce = nonce, - Version = Version ?? "1.0", - Callback = OAuthTools.UrlEncodeRelaxed(CallbackUrl ?? ""), - TokenSecret = TokenSecret, - ConsumerSecret = ConsumerSecret - }; - - return info; - } - - /// - /// Generates a instance to pass to an - /// for the purpose of exchanging a request token - /// for an access token authorized by the user at the Service Provider site. - /// - /// The HTTP method for the intended request - /// - public virtual OAuthWebQueryInfo BuildAccessTokenInfo(string method) - { - return BuildAccessTokenInfo(method, null); - } - - /// - /// Generates a instance to pass to an - /// for the purpose of exchanging a request token - /// for an access token authorized by the user at the Service Provider site. - /// - /// The HTTP method for the intended request - /// - /// Any existing, non-OAuth query parameters desired in the request - public virtual OAuthWebQueryInfo BuildAccessTokenInfo(string method, WebParameterCollection parameters) - { - ValidateAccessRequestState(); - - if (parameters == null) - { - parameters = new WebParameterCollection(); - } - - var uri = new Uri(AccessTokenUrl); - var timestamp = OAuthTools.GetTimestamp(); - var nonce = OAuthTools.GetNonce(); - - AddAuthParameters(parameters, timestamp, nonce); - - var signatureBase = OAuthTools.ConcatenateRequestElements(method, uri.ToString(), parameters); - var signature = OAuthTools.GetSignature(SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret, TokenSecret); - - var info = new OAuthWebQueryInfo - { - WebMethod = method, - ParameterHandling = ParameterHandling, - ConsumerKey = ConsumerKey, - Token = Token, - SignatureMethod = SignatureMethod.ToRequestValue(), - SignatureTreatment = SignatureTreatment, - Signature = signature, - Timestamp = timestamp, - Nonce = nonce, - Version = Version ?? "1.0", - Verifier = Verifier, - Callback = CallbackUrl, - TokenSecret = TokenSecret, - ConsumerSecret = ConsumerSecret, - }; - - return info; - } - - /// - /// Generates a instance to pass to an - /// for the purpose of exchanging user credentials - /// for an access token authorized by the user at the Service Provider site. - /// - /// The HTTP method for the intended request - /// - /// Any existing, non-OAuth query parameters desired in the request - public virtual OAuthWebQueryInfo BuildClientAuthAccessTokenInfo(string method, WebParameterCollection parameters) - { - ValidateClientAuthAccessRequestState(); - - if (parameters == null) - { - parameters = new WebParameterCollection(); - } - - var uri = new Uri(AccessTokenUrl); - var timestamp = OAuthTools.GetTimestamp(); - var nonce = OAuthTools.GetNonce(); - - AddXAuthParameters(parameters, timestamp, nonce); - - var signatureBase = OAuthTools.ConcatenateRequestElements(method, uri.ToString(), parameters); - var signature = OAuthTools.GetSignature(SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret); - - var info = new OAuthWebQueryInfo - { - WebMethod = method, - ParameterHandling = ParameterHandling, - ClientMode = "client_auth", - ClientUsername = ClientUsername, - ClientPassword = ClientPassword, - ConsumerKey = ConsumerKey, - SignatureMethod = SignatureMethod.ToRequestValue(), - SignatureTreatment = SignatureTreatment, - Signature = signature, - Timestamp = timestamp, - Nonce = nonce, - Version = Version ?? "1.0", - TokenSecret = TokenSecret, - ConsumerSecret = ConsumerSecret - }; - - return info; - } - - public virtual OAuthWebQueryInfo BuildProtectedResourceInfo(string method, WebParameterCollection parameters, string url) - { - ValidateProtectedResourceState(); - - if (parameters == null) - { - parameters = new WebParameterCollection(); - } - - // Include url parameters in query pool - var uri = new Uri(url); -#if !SILVERLIGHT && !WINDOWS_PHONE - var urlParameters = HttpUtility.ParseQueryString(uri.Query); -#else - var urlParameters = uri.Query.ParseQueryString(); -#endif - -#if !SILVERLIGHT && !WINDOWS_PHONE - foreach (var parameter in urlParameters.AllKeys) -#else - foreach (var parameter in urlParameters.Keys) -#endif - { - switch (method.ToUpperInvariant()) - { - case "POST": - parameters.Add(new HttpPostParameter(parameter, urlParameters[parameter])); - break; - default: - parameters.Add(parameter, urlParameters[parameter]); - break; - } - } - - var timestamp = OAuthTools.GetTimestamp(); - var nonce = OAuthTools.GetNonce(); - - AddAuthParameters(parameters, timestamp, nonce); - - var signatureBase = OAuthTools.ConcatenateRequestElements(method, url, parameters); - - var signature = OAuthTools.GetSignature( - SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret, TokenSecret - ); - - var info = new OAuthWebQueryInfo - { - WebMethod = method, - ParameterHandling = ParameterHandling, - ConsumerKey = ConsumerKey, - Token = Token, - SignatureMethod = SignatureMethod.ToRequestValue(), - SignatureTreatment = SignatureTreatment, - Signature = signature, - Timestamp = timestamp, - Nonce = nonce, - Version = Version ?? "1.0", - Callback = CallbackUrl, - ConsumerSecret = ConsumerSecret, - TokenSecret = TokenSecret - }; - - return info; - } - - private void ValidateTokenRequestState() - { - if (RequestTokenUrl.IsNullOrBlank()) - { - throw new ArgumentException("You must specify a request token URL"); - } - - if (ConsumerKey.IsNullOrBlank()) - { - throw new ArgumentException("You must specify a consumer key"); - } - - if (ConsumerSecret.IsNullOrBlank()) - { - throw new ArgumentException("You must specify a consumer secret"); - } - } - - private void ValidateAccessRequestState() - { - if (AccessTokenUrl.IsNullOrBlank()) - { - throw new ArgumentException("You must specify an access token URL"); - } - - if (ConsumerKey.IsNullOrBlank()) - { - throw new ArgumentException("You must specify a consumer key"); - } - - if (ConsumerSecret.IsNullOrBlank()) - { - throw new ArgumentException("You must specify a consumer secret"); - } - - if (Token.IsNullOrBlank()) - { - throw new ArgumentException("You must specify a token"); - } - } - - private void ValidateClientAuthAccessRequestState() - { - if (AccessTokenUrl.IsNullOrBlank()) - { - throw new ArgumentException("You must specify an access token URL"); - } - - if (ConsumerKey.IsNullOrBlank()) - { - throw new ArgumentException("You must specify a consumer key"); - } - - if (ConsumerSecret.IsNullOrBlank()) - { - throw new ArgumentException("You must specify a consumer secret"); - } - - if (ClientUsername.IsNullOrBlank() || ClientPassword.IsNullOrBlank()) - { - throw new ArgumentException("You must specify user credentials"); - } - } - - private void ValidateProtectedResourceState() - { - if (ConsumerKey.IsNullOrBlank()) - { - throw new ArgumentException("You must specify a consumer key"); - } - - if (ConsumerSecret.IsNullOrBlank()) - { - throw new ArgumentException("You must specify a consumer secret"); - } - } - - private void AddAuthParameters(ICollection parameters, string timestamp, string nonce) - { - var authParameters = new WebParameterCollection - { - new WebPair("oauth_consumer_key", ConsumerKey), - new WebPair("oauth_nonce", nonce), - new WebPair("oauth_signature_method", SignatureMethod.ToRequestValue()), - new WebPair("oauth_timestamp", timestamp), - new WebPair("oauth_version", Version ?? "1.0") - }; - - if (!Token.IsNullOrBlank()) - { - authParameters.Add(new WebPair("oauth_token", Token)); - } - - if (!CallbackUrl.IsNullOrBlank()) - { - authParameters.Add(new WebPair("oauth_callback", CallbackUrl)); - } - - if (!Verifier.IsNullOrBlank()) - { - authParameters.Add(new WebPair("oauth_verifier", Verifier)); - } - - if (!SessionHandle.IsNullOrBlank()) - { - authParameters.Add(new WebPair("oauth_session_handle", SessionHandle)); - } - - foreach (var authParameter in authParameters) - { - parameters.Add(authParameter); - } - } - - private void AddXAuthParameters(ICollection parameters, string timestamp, string nonce) - { - var authParameters = new WebParameterCollection - { - new WebPair("x_auth_username", ClientUsername), - new WebPair("x_auth_password", ClientPassword), - new WebPair("x_auth_mode", "client_auth"), - new WebPair("oauth_consumer_key", ConsumerKey), - new WebPair("oauth_signature_method", SignatureMethod.ToRequestValue()), - new WebPair("oauth_timestamp", timestamp), - new WebPair("oauth_nonce", nonce), - new WebPair("oauth_version", Version ?? "1.0") - }; - - foreach (var authParameter in authParameters) - { - parameters.Add(authParameter); - } - } - } +using System; +using System.Collections.Generic; +using RestSharp.Authenticators.OAuth.Extensions; +#if !WINDOWS_PHONE && !SILVERLIGHT +using RestSharp.Contrib; +#endif + +namespace RestSharp.Authenticators.OAuth +{ + /// + /// A class to encapsulate OAuth authentication flow. + /// + /// + internal class OAuthWorkflow + { + public virtual string Version { get; set; } + public virtual string ConsumerKey { get; set; } + public virtual string ConsumerSecret { get; set; } + public virtual string Token { get; set; } + public virtual string TokenSecret { get; set; } + public virtual string CallbackUrl { get; set; } + public virtual string Verifier { get; set; } + public virtual string SessionHandle { get; set; } + + public virtual OAuthSignatureMethod SignatureMethod { get; set; } + public virtual OAuthSignatureTreatment SignatureTreatment { get; set; } + public virtual OAuthParameterHandling ParameterHandling { get; set; } + + public virtual string ClientUsername { get; set; } + public virtual string ClientPassword { get; set; } + + /// + public virtual string RequestTokenUrl { get; set; } + + /// + public virtual string AccessTokenUrl { get; set; } + + /// + public virtual string AuthorizationUrl { get; set; } + + /// + /// Generates a instance to pass to an + /// for the purpose of requesting an + /// unauthorized request token. + /// + /// The HTTP method for the intended request + /// + /// + public OAuthWebQueryInfo BuildRequestTokenInfo(string method) + { + return BuildRequestTokenInfo(method, null); + } + + /// + /// Generates a instance to pass to an + /// for the purpose of requesting an + /// unauthorized request token. + /// + /// The HTTP method for the intended request + /// Any existing, non-OAuth query parameters desired in the request + /// + /// + public virtual OAuthWebQueryInfo BuildRequestTokenInfo(string method, WebParameterCollection parameters) + { + ValidateTokenRequestState(); + + if (parameters == null) + { + parameters = new WebParameterCollection(); + } + + var timestamp = OAuthTools.GetTimestamp(); + var nonce = OAuthTools.GetNonce(); + + AddAuthParameters(parameters, timestamp, nonce); + + var signatureBase = OAuthTools.ConcatenateRequestElements(method, RequestTokenUrl, parameters); + var signature = OAuthTools.GetSignature(SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret); + + var info = new OAuthWebQueryInfo + { + WebMethod = method, + ParameterHandling = ParameterHandling, + ConsumerKey = ConsumerKey, + SignatureMethod = SignatureMethod.ToRequestValue(), + SignatureTreatment = SignatureTreatment, + Signature = signature, + Timestamp = timestamp, + Nonce = nonce, + Version = Version ?? "1.0", + Callback = OAuthTools.UrlEncodeRelaxed(CallbackUrl ?? ""), + TokenSecret = TokenSecret, + ConsumerSecret = ConsumerSecret + }; + + return info; + } + + /// + /// Generates a instance to pass to an + /// for the purpose of exchanging a request token + /// for an access token authorized by the user at the Service Provider site. + /// + /// The HTTP method for the intended request + /// + public virtual OAuthWebQueryInfo BuildAccessTokenInfo(string method) + { + return BuildAccessTokenInfo(method, null); + } + + /// + /// Generates a instance to pass to an + /// for the purpose of exchanging a request token + /// for an access token authorized by the user at the Service Provider site. + /// + /// The HTTP method for the intended request + /// + /// Any existing, non-OAuth query parameters desired in the request + public virtual OAuthWebQueryInfo BuildAccessTokenInfo(string method, WebParameterCollection parameters) + { + ValidateAccessRequestState(); + + if (parameters == null) + { + parameters = new WebParameterCollection(); + } + + var uri = new Uri(AccessTokenUrl); + var timestamp = OAuthTools.GetTimestamp(); + var nonce = OAuthTools.GetNonce(); + + AddAuthParameters(parameters, timestamp, nonce); + + var signatureBase = OAuthTools.ConcatenateRequestElements(method, uri.ToString(), parameters); + var signature = OAuthTools.GetSignature(SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret, TokenSecret); + + var info = new OAuthWebQueryInfo + { + WebMethod = method, + ParameterHandling = ParameterHandling, + ConsumerKey = ConsumerKey, + Token = Token, + SignatureMethod = SignatureMethod.ToRequestValue(), + SignatureTreatment = SignatureTreatment, + Signature = signature, + Timestamp = timestamp, + Nonce = nonce, + Version = Version ?? "1.0", + Verifier = Verifier, + Callback = CallbackUrl, + TokenSecret = TokenSecret, + ConsumerSecret = ConsumerSecret, + }; + + return info; + } + + /// + /// Generates a instance to pass to an + /// for the purpose of exchanging user credentials + /// for an access token authorized by the user at the Service Provider site. + /// + /// The HTTP method for the intended request + /// + /// Any existing, non-OAuth query parameters desired in the request + public virtual OAuthWebQueryInfo BuildClientAuthAccessTokenInfo(string method, WebParameterCollection parameters) + { + ValidateClientAuthAccessRequestState(); + + if (parameters == null) + { + parameters = new WebParameterCollection(); + } + + var uri = new Uri(AccessTokenUrl); + var timestamp = OAuthTools.GetTimestamp(); + var nonce = OAuthTools.GetNonce(); + + AddXAuthParameters(parameters, timestamp, nonce); + + var signatureBase = OAuthTools.ConcatenateRequestElements(method, uri.ToString(), parameters); + var signature = OAuthTools.GetSignature(SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret); + + var info = new OAuthWebQueryInfo + { + WebMethod = method, + ParameterHandling = ParameterHandling, + ClientMode = "client_auth", + ClientUsername = ClientUsername, + ClientPassword = ClientPassword, + ConsumerKey = ConsumerKey, + SignatureMethod = SignatureMethod.ToRequestValue(), + SignatureTreatment = SignatureTreatment, + Signature = signature, + Timestamp = timestamp, + Nonce = nonce, + Version = Version ?? "1.0", + TokenSecret = TokenSecret, + ConsumerSecret = ConsumerSecret + }; + + return info; + } + + public virtual OAuthWebQueryInfo BuildProtectedResourceInfo(string method, WebParameterCollection parameters, string url) + { + ValidateProtectedResourceState(); + + if (parameters == null) + { + parameters = new WebParameterCollection(); + } + + // Include url parameters in query pool + var uri = new Uri(url); +#if !SILVERLIGHT && !WINDOWS_PHONE + var urlParameters = HttpUtility.ParseQueryString(uri.Query); +#else + var urlParameters = uri.Query.ParseQueryString(); +#endif + +#if !SILVERLIGHT && !WINDOWS_PHONE + foreach (var parameter in urlParameters.AllKeys) +#else + foreach (var parameter in urlParameters.Keys) +#endif + { + switch (method.ToUpperInvariant()) + { + case "POST": + parameters.Add(new HttpPostParameter(parameter, urlParameters[parameter])); + break; + default: + parameters.Add(parameter, urlParameters[parameter]); + break; + } + } + + var timestamp = OAuthTools.GetTimestamp(); + var nonce = OAuthTools.GetNonce(); + + AddAuthParameters(parameters, timestamp, nonce); + + var signatureBase = OAuthTools.ConcatenateRequestElements(method, url, parameters); + + var signature = OAuthTools.GetSignature( + SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret, TokenSecret + ); + + var info = new OAuthWebQueryInfo + { + WebMethod = method, + ParameterHandling = ParameterHandling, + ConsumerKey = ConsumerKey, + Token = Token, + SignatureMethod = SignatureMethod.ToRequestValue(), + SignatureTreatment = SignatureTreatment, + Signature = signature, + Timestamp = timestamp, + Nonce = nonce, + Version = Version ?? "1.0", + Callback = CallbackUrl, + ConsumerSecret = ConsumerSecret, + TokenSecret = TokenSecret + }; + + return info; + } + + private void ValidateTokenRequestState() + { + if (RequestTokenUrl.IsNullOrBlank()) + { + throw new ArgumentException("You must specify a request token URL"); + } + + if (ConsumerKey.IsNullOrBlank()) + { + throw new ArgumentException("You must specify a consumer key"); + } + + if (ConsumerSecret.IsNullOrBlank()) + { + throw new ArgumentException("You must specify a consumer secret"); + } + } + + private void ValidateAccessRequestState() + { + if (AccessTokenUrl.IsNullOrBlank()) + { + throw new ArgumentException("You must specify an access token URL"); + } + + if (ConsumerKey.IsNullOrBlank()) + { + throw new ArgumentException("You must specify a consumer key"); + } + + if (ConsumerSecret.IsNullOrBlank()) + { + throw new ArgumentException("You must specify a consumer secret"); + } + + if (Token.IsNullOrBlank()) + { + throw new ArgumentException("You must specify a token"); + } + } + + private void ValidateClientAuthAccessRequestState() + { + if (AccessTokenUrl.IsNullOrBlank()) + { + throw new ArgumentException("You must specify an access token URL"); + } + + if (ConsumerKey.IsNullOrBlank()) + { + throw new ArgumentException("You must specify a consumer key"); + } + + if (ConsumerSecret.IsNullOrBlank()) + { + throw new ArgumentException("You must specify a consumer secret"); + } + + if (ClientUsername.IsNullOrBlank() || ClientPassword.IsNullOrBlank()) + { + throw new ArgumentException("You must specify user credentials"); + } + } + + private void ValidateProtectedResourceState() + { + if (ConsumerKey.IsNullOrBlank()) + { + throw new ArgumentException("You must specify a consumer key"); + } + + if (ConsumerSecret.IsNullOrBlank()) + { + throw new ArgumentException("You must specify a consumer secret"); + } + } + + private void AddAuthParameters(ICollection parameters, string timestamp, string nonce) + { + var authParameters = new WebParameterCollection + { + new WebPair("oauth_consumer_key", ConsumerKey), + new WebPair("oauth_nonce", nonce), + new WebPair("oauth_signature_method", SignatureMethod.ToRequestValue()), + new WebPair("oauth_timestamp", timestamp), + new WebPair("oauth_version", Version ?? "1.0") + }; + + if (!Token.IsNullOrBlank()) + { + authParameters.Add(new WebPair("oauth_token", Token)); + } + + if (!CallbackUrl.IsNullOrBlank()) + { + authParameters.Add(new WebPair("oauth_callback", CallbackUrl)); + } + + if (!Verifier.IsNullOrBlank()) + { + authParameters.Add(new WebPair("oauth_verifier", Verifier)); + } + + if (!SessionHandle.IsNullOrBlank()) + { + authParameters.Add(new WebPair("oauth_session_handle", SessionHandle)); + } + + foreach (var authParameter in authParameters) + { + parameters.Add(authParameter); + } + } + + private void AddXAuthParameters(ICollection parameters, string timestamp, string nonce) + { + var authParameters = new WebParameterCollection + { + new WebPair("x_auth_username", ClientUsername), + new WebPair("x_auth_password", ClientPassword), + new WebPair("x_auth_mode", "client_auth"), + new WebPair("oauth_consumer_key", ConsumerKey), + new WebPair("oauth_signature_method", SignatureMethod.ToRequestValue()), + new WebPair("oauth_timestamp", timestamp), + new WebPair("oauth_nonce", nonce), + new WebPair("oauth_version", Version ?? "1.0") + }; + + foreach (var authParameter in authParameters) + { + parameters.Add(authParameter); + } + } + } } \ No newline at end of file diff --git a/RestSharp/Authenticators/OAuth/WebParameter.cs b/RestSharp/Authenticators/OAuth/WebParameter.cs index 1ca91091e..387c4eb97 100644 --- a/RestSharp/Authenticators/OAuth/WebParameter.cs +++ b/RestSharp/Authenticators/OAuth/WebParameter.cs @@ -1,21 +1,21 @@ -#if !Smartphone -using System; -using System.Diagnostics; - -#endif - -namespace RestSharp.Authenticators.OAuth -{ -#if !Smartphone - [DebuggerDisplay("{Name}:{Value}")] -#endif -#if !SILVERLIGHT && !WINDOWS_PHONE - [Serializable] -#endif - internal class WebParameter : WebPair - { - public WebParameter(string name, string value) : base(name, value) - { - } - } +#if !Smartphone +using System; +using System.Diagnostics; + +#endif + +namespace RestSharp.Authenticators.OAuth +{ +#if !Smartphone + [DebuggerDisplay("{Name}:{Value}")] +#endif +#if !SILVERLIGHT && !WINDOWS_PHONE + [Serializable] +#endif + internal class WebParameter : WebPair + { + public WebParameter(string name, string value) : base(name, value) + { + } + } } \ No newline at end of file diff --git a/RestSharp/Authenticators/OAuth1Authenticator.cs b/RestSharp/Authenticators/OAuth1Authenticator.cs index eb7705bfa..a13ce73a6 100644 --- a/RestSharp/Authenticators/OAuth1Authenticator.cs +++ b/RestSharp/Authenticators/OAuth1Authenticator.cs @@ -1,243 +1,243 @@ -using System; -using System.Linq; -using System.Text; -using RestSharp.Authenticators.OAuth; -using RestSharp.Authenticators.OAuth.Extensions; - -#if WINDOWS_PHONE -using System.Net; -#elif SILVERLIGHT -using System.Windows.Browser; -#else -using RestSharp.Contrib; -#endif - - -namespace RestSharp.Authenticators -{ - /// - public class OAuth1Authenticator : IAuthenticator - { - public virtual string Realm { get; set; } - public virtual OAuthParameterHandling ParameterHandling { get; set; } - public virtual OAuthSignatureMethod SignatureMethod { get; set; } - public virtual OAuthSignatureTreatment SignatureTreatment { get; set; } - - internal virtual OAuthType Type { get; set; } - internal virtual string ConsumerKey { get; set; } - internal virtual string ConsumerSecret { get; set; } - internal virtual string Token { get; set; } - internal virtual string TokenSecret { get; set; } - internal virtual string Verifier { get; set; } - internal virtual string Version { get; set; } - internal virtual string CallbackUrl { get; set; } - internal virtual string SessionHandle { get; set; } - internal virtual string ClientUsername { get; set; } - internal virtual string ClientPassword { get; set; } - - public static OAuth1Authenticator ForRequestToken(string consumerKey, string consumerSecret) - { - var authenticator = new OAuth1Authenticator - { - ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader, - SignatureMethod = OAuthSignatureMethod.HmacSha1, - SignatureTreatment = OAuthSignatureTreatment.Escaped, - ConsumerKey = consumerKey, - ConsumerSecret = consumerSecret, - Type = OAuthType.RequestToken - }; - return authenticator; - } - - public static OAuth1Authenticator ForRequestToken(string consumerKey, string consumerSecret, string callbackUrl) - { - var authenticator = ForRequestToken(consumerKey, consumerSecret); - authenticator.CallbackUrl = callbackUrl; - return authenticator; - } - - public static OAuth1Authenticator ForAccessToken(string consumerKey, string consumerSecret, string token, string tokenSecret) - { - var authenticator = new OAuth1Authenticator - { - ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader, - SignatureMethod = OAuthSignatureMethod.HmacSha1, - SignatureTreatment = OAuthSignatureTreatment.Escaped, - ConsumerKey = consumerKey, - ConsumerSecret = consumerSecret, - Token = token, - TokenSecret = tokenSecret, - Type = OAuthType.AccessToken - }; - return authenticator; - } - - public static OAuth1Authenticator ForAccessToken(string consumerKey, string consumerSecret, string token, string tokenSecret, string verifier) - { - var authenticator = ForAccessToken(consumerKey, consumerSecret, token, tokenSecret); - authenticator.Verifier = verifier; - return authenticator; - } - - public static OAuth1Authenticator ForAccessTokenRefresh(string consumerKey, string consumerSecret, string token, string tokenSecret, string sessionHandle) - { - var authenticator = ForAccessToken(consumerKey, consumerSecret, token, tokenSecret); - authenticator.SessionHandle = sessionHandle; - return authenticator; - } - - public static OAuth1Authenticator ForAccessTokenRefresh(string consumerKey, string consumerSecret, string token, string tokenSecret, string verifier, string sessionHandle) - { - var authenticator = ForAccessToken(consumerKey, consumerSecret, token, tokenSecret); - authenticator.SessionHandle = sessionHandle; - authenticator.Verifier = verifier; - return authenticator; - } - - public static OAuth1Authenticator ForClientAuthentication(string consumerKey, string consumerSecret, string username, string password) - { - var authenticator = new OAuth1Authenticator - { - ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader, - SignatureMethod = OAuthSignatureMethod.HmacSha1, - SignatureTreatment = OAuthSignatureTreatment.Escaped, - ConsumerKey = consumerKey, - ConsumerSecret = consumerSecret, - ClientUsername = username, - ClientPassword = password, - Type = OAuthType.ClientAuthentication - }; - return authenticator; - } - - public static OAuth1Authenticator ForProtectedResource(string consumerKey, string consumerSecret, string accessToken, string accessTokenSecret) - { - var authenticator = new OAuth1Authenticator - { - Type = OAuthType.ProtectedResource, - ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader, - SignatureMethod = OAuthSignatureMethod.HmacSha1, - SignatureTreatment = OAuthSignatureTreatment.Escaped, - ConsumerKey = consumerKey, - ConsumerSecret = consumerSecret, - Token = accessToken, - TokenSecret = accessTokenSecret - }; - return authenticator; - } - - public void Authenticate(IRestClient client, IRestRequest request) - { - var workflow = new OAuthWorkflow - { - ConsumerKey = ConsumerKey, - ConsumerSecret = ConsumerSecret, - ParameterHandling = ParameterHandling, - SignatureMethod = SignatureMethod, - SignatureTreatment = SignatureTreatment, - Verifier = Verifier, - Version = Version, - CallbackUrl = CallbackUrl, - SessionHandle = SessionHandle, - Token = Token, - TokenSecret = TokenSecret, - ClientUsername = ClientUsername, - ClientPassword = ClientPassword - }; - - AddOAuthData(client, request, workflow); - } - - private void AddOAuthData(IRestClient client, IRestRequest request, OAuthWorkflow workflow) - { - var url = client.BuildUri(request).ToString(); - var queryStringStart = url.IndexOf('?'); - if (queryStringStart != -1) - url = url.Substring(0, queryStringStart); - - OAuthWebQueryInfo oauth; - var method = request.Method.ToString().ToUpperInvariant(); - - var parameters = new WebParameterCollection(); - - // include all GET and POST parameters before generating the signature - // according to the RFC 5849 - The OAuth 1.0 Protocol - // http://tools.ietf.org/html/rfc5849#section-3.4.1 - // if this change causes trouble we need to introduce a flag indicating the specific OAuth implementation level, - // or implement a seperate class for each OAuth version - foreach (var p in client.DefaultParameters.Where(p => p.Type == ParameterType.GetOrPost)) - { - parameters.Add( new WebPair( p.Name, p.Value.ToString() ) ); - } - foreach (var p in request.Parameters.Where(p => p.Type == ParameterType.GetOrPost)) - { - parameters.Add(new WebPair(p.Name, p.Value.ToString())); - } - - switch (Type) - { - case OAuthType.RequestToken: - workflow.RequestTokenUrl = url; - oauth = workflow.BuildRequestTokenInfo(method, parameters); - break; - case OAuthType.AccessToken: - workflow.AccessTokenUrl = url; - oauth = workflow.BuildAccessTokenInfo(method, parameters); - break; - case OAuthType.ClientAuthentication: - workflow.AccessTokenUrl = url; - oauth = workflow.BuildClientAuthAccessTokenInfo(method, parameters); - break; - case OAuthType.ProtectedResource: - oauth = workflow.BuildProtectedResourceInfo(method, parameters, url); - break; - default: - throw new ArgumentOutOfRangeException(); - } - - switch (ParameterHandling) - { - case OAuthParameterHandling.HttpAuthorizationHeader: - parameters.Add("oauth_signature", oauth.Signature); - request.AddHeader("Authorization", GetAuthorizationHeader(parameters)); - break; - case OAuthParameterHandling.UrlOrPostParameters: - parameters.Add("oauth_signature", oauth.Signature); - foreach (var parameter in parameters.Where(parameter => !parameter.Name.IsNullOrBlank() && (parameter.Name.StartsWith("oauth_") || parameter.Name.StartsWith("x_auth_")))) - { - request.AddParameter(parameter.Name, HttpUtility.UrlDecode(parameter.Value)); - } - break; - default: - throw new ArgumentOutOfRangeException(); - } - } - - private string GetAuthorizationHeader(WebPairCollection parameters) - { - var sb = new StringBuilder("OAuth "); - if (!Realm.IsNullOrBlank()) - { - sb.Append("realm=\"{0}\",".FormatWith(OAuthTools.UrlEncodeRelaxed(Realm))); - } - - parameters.Sort((l, r) => l.Name.CompareTo(r.Name)); - - var parameterCount = 0; - var oathParameters = parameters.Where(parameter => - !parameter.Name.IsNullOrBlank() && - !parameter.Value.IsNullOrBlank() && - (parameter.Name.StartsWith("oauth_") || parameter.Name.StartsWith("x_auth_")) - ).ToList(); - foreach (var parameter in oathParameters) - { - parameterCount++; - var format = parameterCount < oathParameters.Count ? "{0}=\"{1}\"," : "{0}=\"{1}\""; - sb.Append(format.FormatWith(parameter.Name, parameter.Value)); - } - - var authorization = sb.ToString(); - return authorization; - } - } -} +using System; +using System.Linq; +using System.Text; +using RestSharp.Authenticators.OAuth; +using RestSharp.Authenticators.OAuth.Extensions; + +#if WINDOWS_PHONE +using System.Net; +#elif SILVERLIGHT +using System.Windows.Browser; +#else +using RestSharp.Contrib; +#endif + + +namespace RestSharp.Authenticators +{ + /// + public class OAuth1Authenticator : IAuthenticator + { + public virtual string Realm { get; set; } + public virtual OAuthParameterHandling ParameterHandling { get; set; } + public virtual OAuthSignatureMethod SignatureMethod { get; set; } + public virtual OAuthSignatureTreatment SignatureTreatment { get; set; } + + internal virtual OAuthType Type { get; set; } + internal virtual string ConsumerKey { get; set; } + internal virtual string ConsumerSecret { get; set; } + internal virtual string Token { get; set; } + internal virtual string TokenSecret { get; set; } + internal virtual string Verifier { get; set; } + internal virtual string Version { get; set; } + internal virtual string CallbackUrl { get; set; } + internal virtual string SessionHandle { get; set; } + internal virtual string ClientUsername { get; set; } + internal virtual string ClientPassword { get; set; } + + public static OAuth1Authenticator ForRequestToken(string consumerKey, string consumerSecret) + { + var authenticator = new OAuth1Authenticator + { + ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader, + SignatureMethod = OAuthSignatureMethod.HmacSha1, + SignatureTreatment = OAuthSignatureTreatment.Escaped, + ConsumerKey = consumerKey, + ConsumerSecret = consumerSecret, + Type = OAuthType.RequestToken + }; + return authenticator; + } + + public static OAuth1Authenticator ForRequestToken(string consumerKey, string consumerSecret, string callbackUrl) + { + var authenticator = ForRequestToken(consumerKey, consumerSecret); + authenticator.CallbackUrl = callbackUrl; + return authenticator; + } + + public static OAuth1Authenticator ForAccessToken(string consumerKey, string consumerSecret, string token, string tokenSecret) + { + var authenticator = new OAuth1Authenticator + { + ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader, + SignatureMethod = OAuthSignatureMethod.HmacSha1, + SignatureTreatment = OAuthSignatureTreatment.Escaped, + ConsumerKey = consumerKey, + ConsumerSecret = consumerSecret, + Token = token, + TokenSecret = tokenSecret, + Type = OAuthType.AccessToken + }; + return authenticator; + } + + public static OAuth1Authenticator ForAccessToken(string consumerKey, string consumerSecret, string token, string tokenSecret, string verifier) + { + var authenticator = ForAccessToken(consumerKey, consumerSecret, token, tokenSecret); + authenticator.Verifier = verifier; + return authenticator; + } + + public static OAuth1Authenticator ForAccessTokenRefresh(string consumerKey, string consumerSecret, string token, string tokenSecret, string sessionHandle) + { + var authenticator = ForAccessToken(consumerKey, consumerSecret, token, tokenSecret); + authenticator.SessionHandle = sessionHandle; + return authenticator; + } + + public static OAuth1Authenticator ForAccessTokenRefresh(string consumerKey, string consumerSecret, string token, string tokenSecret, string verifier, string sessionHandle) + { + var authenticator = ForAccessToken(consumerKey, consumerSecret, token, tokenSecret); + authenticator.SessionHandle = sessionHandle; + authenticator.Verifier = verifier; + return authenticator; + } + + public static OAuth1Authenticator ForClientAuthentication(string consumerKey, string consumerSecret, string username, string password) + { + var authenticator = new OAuth1Authenticator + { + ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader, + SignatureMethod = OAuthSignatureMethod.HmacSha1, + SignatureTreatment = OAuthSignatureTreatment.Escaped, + ConsumerKey = consumerKey, + ConsumerSecret = consumerSecret, + ClientUsername = username, + ClientPassword = password, + Type = OAuthType.ClientAuthentication + }; + return authenticator; + } + + public static OAuth1Authenticator ForProtectedResource(string consumerKey, string consumerSecret, string accessToken, string accessTokenSecret) + { + var authenticator = new OAuth1Authenticator + { + Type = OAuthType.ProtectedResource, + ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader, + SignatureMethod = OAuthSignatureMethod.HmacSha1, + SignatureTreatment = OAuthSignatureTreatment.Escaped, + ConsumerKey = consumerKey, + ConsumerSecret = consumerSecret, + Token = accessToken, + TokenSecret = accessTokenSecret + }; + return authenticator; + } + + public void Authenticate(IRestClient client, IRestRequest request) + { + var workflow = new OAuthWorkflow + { + ConsumerKey = ConsumerKey, + ConsumerSecret = ConsumerSecret, + ParameterHandling = ParameterHandling, + SignatureMethod = SignatureMethod, + SignatureTreatment = SignatureTreatment, + Verifier = Verifier, + Version = Version, + CallbackUrl = CallbackUrl, + SessionHandle = SessionHandle, + Token = Token, + TokenSecret = TokenSecret, + ClientUsername = ClientUsername, + ClientPassword = ClientPassword + }; + + AddOAuthData(client, request, workflow); + } + + private void AddOAuthData(IRestClient client, IRestRequest request, OAuthWorkflow workflow) + { + var url = client.BuildUri(request).ToString(); + var queryStringStart = url.IndexOf('?'); + if (queryStringStart != -1) + url = url.Substring(0, queryStringStart); + + OAuthWebQueryInfo oauth; + var method = request.Method.ToString().ToUpperInvariant(); + + var parameters = new WebParameterCollection(); + + // include all GET and POST parameters before generating the signature + // according to the RFC 5849 - The OAuth 1.0 Protocol + // http://tools.ietf.org/html/rfc5849#section-3.4.1 + // if this change causes trouble we need to introduce a flag indicating the specific OAuth implementation level, + // or implement a seperate class for each OAuth version + foreach (var p in client.DefaultParameters.Where(p => p.Type == ParameterType.GetOrPost)) + { + parameters.Add( new WebPair( p.Name, p.Value.ToString() ) ); + } + foreach (var p in request.Parameters.Where(p => p.Type == ParameterType.GetOrPost)) + { + parameters.Add(new WebPair(p.Name, p.Value.ToString())); + } + + switch (Type) + { + case OAuthType.RequestToken: + workflow.RequestTokenUrl = url; + oauth = workflow.BuildRequestTokenInfo(method, parameters); + break; + case OAuthType.AccessToken: + workflow.AccessTokenUrl = url; + oauth = workflow.BuildAccessTokenInfo(method, parameters); + break; + case OAuthType.ClientAuthentication: + workflow.AccessTokenUrl = url; + oauth = workflow.BuildClientAuthAccessTokenInfo(method, parameters); + break; + case OAuthType.ProtectedResource: + oauth = workflow.BuildProtectedResourceInfo(method, parameters, url); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + switch (ParameterHandling) + { + case OAuthParameterHandling.HttpAuthorizationHeader: + parameters.Add("oauth_signature", oauth.Signature); + request.AddHeader("Authorization", GetAuthorizationHeader(parameters)); + break; + case OAuthParameterHandling.UrlOrPostParameters: + parameters.Add("oauth_signature", oauth.Signature); + foreach (var parameter in parameters.Where(parameter => !parameter.Name.IsNullOrBlank() && (parameter.Name.StartsWith("oauth_") || parameter.Name.StartsWith("x_auth_")))) + { + request.AddParameter(parameter.Name, HttpUtility.UrlDecode(parameter.Value)); + } + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private string GetAuthorizationHeader(WebPairCollection parameters) + { + var sb = new StringBuilder("OAuth "); + if (!Realm.IsNullOrBlank()) + { + sb.Append("realm=\"{0}\",".FormatWith(OAuthTools.UrlEncodeRelaxed(Realm))); + } + + parameters.Sort((l, r) => l.Name.CompareTo(r.Name)); + + var parameterCount = 0; + var oathParameters = parameters.Where(parameter => + !parameter.Name.IsNullOrBlank() && + !parameter.Value.IsNullOrBlank() && + (parameter.Name.StartsWith("oauth_") || parameter.Name.StartsWith("x_auth_")) + ).ToList(); + foreach (var parameter in oathParameters) + { + parameterCount++; + var format = parameterCount < oathParameters.Count ? "{0}=\"{1}\"," : "{0}=\"{1}\""; + sb.Append(format.FormatWith(parameter.Name, parameter.Value)); + } + + var authorization = sb.ToString(); + return authorization; + } + } +} diff --git a/RestSharp/Deserializers/XmlDeserializer.cs b/RestSharp/Deserializers/XmlDeserializer.cs index dff1c938b..64634b31c 100644 --- a/RestSharp/Deserializers/XmlDeserializer.cs +++ b/RestSharp/Deserializers/XmlDeserializer.cs @@ -22,7 +22,7 @@ using RestSharp.Extensions; using System.Globalization; -using System.Xml; +using System.Xml; using System.ComponentModel; namespace RestSharp.Deserializers @@ -169,33 +169,33 @@ private void Map(object x, XElement root) } prop.SetValue(x, value, null); - } - else if (type == typeof(DateTimeOffset)) - { - var toConvert = value.ToString(); - if (!string.IsNullOrEmpty(toConvert)) - { - DateTimeOffset deserialisedValue; - try - { - deserialisedValue = XmlConvert.ToDateTimeOffset(toConvert); - prop.SetValue(x, deserialisedValue, null); - } - catch (Exception) - { - object result; - if (TryGetFromString(toConvert, out result, type)) - { - prop.SetValue(x, result, null); - } - else - { - //fallback to parse - deserialisedValue = DateTimeOffset.Parse(toConvert); - prop.SetValue(x, deserialisedValue, null); - } - } - } + } + else if (type == typeof(DateTimeOffset)) + { + var toConvert = value.ToString(); + if (!string.IsNullOrEmpty(toConvert)) + { + DateTimeOffset deserialisedValue; + try + { + deserialisedValue = XmlConvert.ToDateTimeOffset(toConvert); + prop.SetValue(x, deserialisedValue, null); + } + catch (Exception) + { + object result; + if (TryGetFromString(toConvert, out result, type)) + { + prop.SetValue(x, result, null); + } + else + { + //fallback to parse + deserialisedValue = DateTimeOffset.Parse(toConvert); + prop.SetValue(x, deserialisedValue, null); + } + } + } } else if (type == typeof(Decimal)) { @@ -237,40 +237,40 @@ private void Map(object x, XElement root) prop.SetValue(x, list, null); } else - { - //fallback to type converters if possible - object result; - if (TryGetFromString(value.ToString(), out result, type)) - { - prop.SetValue(x, result, null); - } - else - { - // nested property classes - if (root != null) - { - var element = GetElementByName(root, name); - if (element != null) - { - var item = CreateAndMap(type, element); - prop.SetValue(x, item, null); - } - } + { + //fallback to type converters if possible + object result; + if (TryGetFromString(value.ToString(), out result, type)) + { + prop.SetValue(x, result, null); + } + else + { + // nested property classes + if (root != null) + { + var element = GetElementByName(root, name); + if (element != null) + { + var item = CreateAndMap(type, element); + prop.SetValue(x, item, null); + } + } } } } - } - - private static bool TryGetFromString(string inputString, out object result, Type type) - { - var converter = TypeDescriptor.GetConverter(type); - if (converter.CanConvertFrom(typeof(string))) - { - result = (converter.ConvertFromInvariantString(inputString)); - return true; - } - result = null; - return false; + } + + private static bool TryGetFromString(string inputString, out object result, Type type) + { + var converter = TypeDescriptor.GetConverter(type); + if (converter.CanConvertFrom(typeof(string))) + { + result = (converter.ConvertFromInvariantString(inputString)); + return true; + } + result = null; + return false; } private void PopulateListFromElements(Type t, IEnumerable elements, IList list) diff --git a/RestSharp/Extensions/MiscExtensions.cs b/RestSharp/Extensions/MiscExtensions.cs index 66cfd54a4..0e179771a 100644 --- a/RestSharp/Extensions/MiscExtensions.cs +++ b/RestSharp/Extensions/MiscExtensions.cs @@ -1,128 +1,128 @@ -#region License -// Copyright 2010 John Sheehan -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#endregion - -using System.Globalization; -using System.IO; -using System.Text; - -namespace RestSharp.Extensions -{ - /// - /// Extension method overload! - /// - public static class MiscExtensions - { -#if !WINDOWS_PHONE - /// - /// Save a byte array to a file - /// - /// Bytes to save - /// Full path to save file to - public static void SaveAs(this byte[] input, string path) - { - File.WriteAllBytes(path, input); - } -#endif - - /// - /// Read a stream into a byte array - /// - /// Stream to read - /// byte[] - public static byte[] ReadAsBytes(this Stream input) - { - byte[] buffer = new byte[16 * 1024]; - using (MemoryStream ms = new MemoryStream()) - { - int read; - while ((read = input.Read(buffer, 0, buffer.Length)) > 0) - { - ms.Write(buffer, 0, read); - } - return ms.ToArray(); - } - } - - /// - /// Copies bytes from one stream to another - /// - /// The input stream. - /// The output stream. - public static void CopyTo(this Stream input, Stream output) - { - var buffer = new byte[32768]; - while(true) - { - var read = input.Read(buffer, 0, buffer.Length); - if(read <= 0) - return; - output.Write(buffer, 0, read); - } - } - - /// - /// Converts a byte array to a string, using its byte order mark to convert it to the right encoding. - /// http://www.shrinkrays.net/code-snippets/csharp/an-extension-method-for-converting-a-byte-array-to-a-string.aspx - /// - /// An array of bytes to convert - /// The byte as a string. - public static string AsString(this byte[] buffer) - { - if (buffer == null) return ""; - - // Ansi as default - Encoding encoding = Encoding.UTF8; - -#if FRAMEWORK - return encoding.GetString(buffer); -#else - if (buffer == null || buffer.Length == 0) - return ""; - - /* - EF BB BF UTF-8 - FF FE UTF-16 little endian - FE FF UTF-16 big endian - FF FE 00 00 UTF-32, little endian - 00 00 FE FF UTF-32, big-endian - */ - - if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf) - { - encoding = Encoding.UTF8; - } - else if (buffer[0] == 0xfe && buffer[1] == 0xff) - { - encoding = Encoding.Unicode; - } - else if (buffer[0] == 0xfe && buffer[1] == 0xff) - { - encoding = Encoding.BigEndianUnicode; // utf-16be - } - - using (MemoryStream stream = new MemoryStream()) - { - stream.Write(buffer, 0, buffer.Length); - stream.Seek(0, SeekOrigin.Begin); - using (StreamReader reader = new StreamReader(stream, encoding)) - { - return reader.ReadToEnd(); - } - } -#endif - } - } +#region License +// Copyright 2010 John Sheehan +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +using System.Globalization; +using System.IO; +using System.Text; + +namespace RestSharp.Extensions +{ + /// + /// Extension method overload! + /// + public static class MiscExtensions + { +#if !WINDOWS_PHONE + /// + /// Save a byte array to a file + /// + /// Bytes to save + /// Full path to save file to + public static void SaveAs(this byte[] input, string path) + { + File.WriteAllBytes(path, input); + } +#endif + + /// + /// Read a stream into a byte array + /// + /// Stream to read + /// byte[] + public static byte[] ReadAsBytes(this Stream input) + { + byte[] buffer = new byte[16 * 1024]; + using (MemoryStream ms = new MemoryStream()) + { + int read; + while ((read = input.Read(buffer, 0, buffer.Length)) > 0) + { + ms.Write(buffer, 0, read); + } + return ms.ToArray(); + } + } + + /// + /// Copies bytes from one stream to another + /// + /// The input stream. + /// The output stream. + public static void CopyTo(this Stream input, Stream output) + { + var buffer = new byte[32768]; + while(true) + { + var read = input.Read(buffer, 0, buffer.Length); + if(read <= 0) + return; + output.Write(buffer, 0, read); + } + } + + /// + /// Converts a byte array to a string, using its byte order mark to convert it to the right encoding. + /// http://www.shrinkrays.net/code-snippets/csharp/an-extension-method-for-converting-a-byte-array-to-a-string.aspx + /// + /// An array of bytes to convert + /// The byte as a string. + public static string AsString(this byte[] buffer) + { + if (buffer == null) return ""; + + // Ansi as default + Encoding encoding = Encoding.UTF8; + +#if FRAMEWORK + return encoding.GetString(buffer); +#else + if (buffer == null || buffer.Length == 0) + return ""; + + /* + EF BB BF UTF-8 + FF FE UTF-16 little endian + FE FF UTF-16 big endian + FF FE 00 00 UTF-32, little endian + 00 00 FE FF UTF-32, big-endian + */ + + if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf) + { + encoding = Encoding.UTF8; + } + else if (buffer[0] == 0xfe && buffer[1] == 0xff) + { + encoding = Encoding.Unicode; + } + else if (buffer[0] == 0xfe && buffer[1] == 0xff) + { + encoding = Encoding.BigEndianUnicode; // utf-16be + } + + using (MemoryStream stream = new MemoryStream()) + { + stream.Write(buffer, 0, buffer.Length); + stream.Seek(0, SeekOrigin.Begin); + using (StreamReader reader = new StreamReader(stream, encoding)) + { + return reader.ReadToEnd(); + } + } +#endif + } + } } \ No newline at end of file diff --git a/RestSharp/RestClient.cs b/RestSharp/RestClient.cs index ee453a26d..4baaa08cd 100644 --- a/RestSharp/RestClient.cs +++ b/RestSharp/RestClient.cs @@ -466,7 +466,7 @@ private IRestResponse Deserialize(IRestRequest request, IRestResponse raw) response = raw.toAsyncResponse(); response.Request = request; - // Only attempt to deserialize if the request has a chance of containing a valid entry + // Only attempt to deserialize if the request has a chance of containing a valid entry if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.Created || response.StatusCode == HttpStatusCode.NonAuthoritativeInformation)