diff --git a/src/NzbDrone.Common.Test/Http/HttpRequestBuilderFixture.cs b/src/NzbDrone.Common.Test/Http/HttpRequestBuilderFixture.cs index 4af9bb501..74e7e12a0 100644 --- a/src/NzbDrone.Common.Test/Http/HttpRequestBuilderFixture.cs +++ b/src/NzbDrone.Common.Test/Http/HttpRequestBuilderFixture.cs @@ -1,4 +1,5 @@ using System; +using System.Text; using FluentAssertions; using NUnit.Framework; using NzbDrone.Common.Http; @@ -9,6 +10,12 @@ namespace NzbDrone.Common.Test.Http [TestFixture] public class HttpRequestBuilderFixture : TestBase { + [OneTimeSetUp] + public void RegisterEncodingProvider() + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + } + [TestCase("http://host/{seg}/some", "http://host/dir/some")] [TestCase("http://host/some/{seg}", "http://host/some/dir")] public void should_add_single_segment_url_segments(string url, string result) @@ -36,5 +43,70 @@ public void should_remove_duplicated_slashes() request.Url.FullUri.Should().Be("http://domain/v1/"); } + + [Test] + public void should_encode_form_parameters_with_utf8_by_default() + { + var builder = new HttpRequestBuilder("http://domain/login") + .Post() + .AddFormParameter("username", "Привет"); + + var request = builder.Build(); + var body = Encoding.UTF8.GetString(request.ContentData); + + // UTF-8 encoding: Привет = %D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82 + body.Should().Contain("username=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82"); + } + + [Test] + public void should_encode_form_parameters_with_windows_1251_for_cyrillic() + { + var windows1251 = Encoding.GetEncoding("windows-1251"); + + var builder = new HttpRequestBuilder("http://domain/login") + .Post() + .SetEncoding(windows1251) + .AddFormParameter("username", "Привет"); + + var request = builder.Build(); + var body = windows1251.GetString(request.ContentData); + + // Windows-1251 encoding: Привет = %CF%F0%E8%E2%E5%F2 + body.Should().Contain("username=%CF%F0%E8%E2%E5%F2"); + } + + [Test] + public void should_encode_form_parameters_with_iso_8859_1_for_extended_latin() + { + var iso88591 = Encoding.GetEncoding("iso-8859-1"); + + var builder = new HttpRequestBuilder("http://domain/login") + .Post() + .SetEncoding(iso88591) + .AddFormParameter("username", "café"); + + var request = builder.Build(); + var body = iso88591.GetString(request.ContentData); + + // ISO-8859-1 encoding: é = %E9 + body.Should().Contain("username=caf%E9"); + } + + [Test] + public void should_encode_form_parameters_ascii_same_regardless_of_encoding() + { + var windows1251 = Encoding.GetEncoding("windows-1251"); + + var builder = new HttpRequestBuilder("http://domain/login") + .Post() + .SetEncoding(windows1251) + .AddFormParameter("username", "testuser") + .AddFormParameter("password", "pass123"); + + var request = builder.Build(); + var body = windows1251.GetString(request.ContentData); + + body.Should().Be("username=testuser&password=pass123"); + } } } diff --git a/src/NzbDrone.Common/Http/HttpRequestBuilder.cs b/src/NzbDrone.Common/Http/HttpRequestBuilder.cs index e5bc3e36b..807ef0032 100644 --- a/src/NzbDrone.Common/Http/HttpRequestBuilder.cs +++ b/src/NzbDrone.Common/Http/HttpRequestBuilder.cs @@ -231,7 +231,7 @@ protected virtual void ApplyFormData(HttpRequest request) } else { - var parameters = FormData.Select(v => string.Format("{0}={1}", v.Name, Uri.EscapeDataString(Encoding.GetString(v.ContentData)))); + var parameters = FormData.Select(v => string.Format("{0}={1}", v.Name, Encoding.GetString(v.ContentData).UrlEncode(Encoding))); var urlencoded = string.Join("&", parameters); var body = Encoding.GetBytes(urlencoded); diff --git a/src/NzbDrone.Core/Indexers/Definitions/FunFile.cs b/src/NzbDrone.Core/Indexers/Definitions/FunFile.cs index 927dcc21c..91646cb37 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/FunFile.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/FunFile.cs @@ -58,6 +58,7 @@ protected override async Task DoLogin() }; var authLoginRequest = requestBuilder + .SetEncoding(Encoding) .AddFormParameter("username", Settings.Username) .AddFormParameter("password", Settings.Password) .AddFormParameter("returnto", "") diff --git a/src/NzbDrone.Core/Indexers/Definitions/PornoLab.cs b/src/NzbDrone.Core/Indexers/Definitions/PornoLab.cs index 8952375ff..ab9723ee7 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PornoLab.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PornoLab.cs @@ -57,6 +57,7 @@ protected override async Task DoLogin() }; var authLoginRequest = requestBuilder + .SetEncoding(Encoding) .AddFormParameter("login_username", Settings.Username) .AddFormParameter("login_password", Settings.Password) .AddFormParameter("login", "Login") diff --git a/src/NzbDrone.Core/Indexers/Definitions/PreToMe.cs b/src/NzbDrone.Core/Indexers/Definitions/PreToMe.cs index 10c8276e7..043e5000a 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PreToMe.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PreToMe.cs @@ -66,6 +66,7 @@ protected override async Task DoLogin() }; var authLoginRequest = requestBuilder + .SetEncoding(Encoding) .SetCookies(loginPage.GetCookies()) .AddFormParameter("username", Settings.Username) .AddFormParameter("password", Settings.Password) diff --git a/src/NzbDrone.Core/Indexers/Definitions/RuTracker.cs b/src/NzbDrone.Core/Indexers/Definitions/RuTracker.cs index dc1ebfa14..86ad314c7 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/RuTracker.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/RuTracker.cs @@ -91,6 +91,7 @@ protected override async Task DoLogin() Cookies = null; var authLoginRequest = requestBuilder.Post() + .SetEncoding(Encoding) .AddFormParameter("login_username", Settings.Username) .AddFormParameter("login_password", Settings.Password) .AddFormParameter("login", "Login") diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentBytes.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentBytes.cs index efda3329b..08b04ff48 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentBytes.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentBytes.cs @@ -58,6 +58,7 @@ protected override async Task DoLogin() Cookies = null; var authLoginRequest = requestBuilder + .SetEncoding(Encoding) .AddFormParameter("username", Settings.Username) .AddFormParameter("password", Settings.Password) .AddFormParameter("returnto", "/")