diff --git a/BTCPayServer.Abstractions/Contracts/BaseDbContextFactory.cs b/BTCPayServer.Abstractions/Contracts/BaseDbContextFactory.cs
index 2f74cdb53..5cc4df6c5 100644
--- a/BTCPayServer.Abstractions/Contracts/BaseDbContextFactory.cs
+++ b/BTCPayServer.Abstractions/Contracts/BaseDbContextFactory.cs
@@ -24,7 +24,9 @@ namespace BTCPayServer.Abstractions.Contracts
class CustomNpgsqlMigrationsSqlGenerator : NpgsqlMigrationsSqlGenerator
{
+#pragma warning disable EF1001 // Internal EF Core API usage.
public CustomNpgsqlMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies, Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal.INpgsqlOptions opts) : base(dependencies, opts)
+#pragma warning restore EF1001 // Internal EF Core API usage.
{
}
diff --git a/BTCPayServer.Client/BTCPayServer.Client.csproj b/BTCPayServer.Client/BTCPayServer.Client.csproj
index bda4683e7..f03fd5713 100644
--- a/BTCPayServer.Client/BTCPayServer.Client.csproj
+++ b/BTCPayServer.Client/BTCPayServer.Client.csproj
@@ -2,6 +2,7 @@
netstandard2.1
+ 10.0
BTCPay Server
Copyright © BTCPay Server 2020
A client library for BTCPay Server Greenfield API
diff --git a/BTCPayServer.Rating/Providers/HttpClientRequestMaker.cs b/BTCPayServer.Rating/Providers/HttpClientRequestMaker.cs
index 16584b0db..0f0053964 100644
--- a/BTCPayServer.Rating/Providers/HttpClientRequestMaker.cs
+++ b/BTCPayServer.Rating/Providers/HttpClientRequestMaker.cs
@@ -10,104 +10,59 @@ namespace BTCPayServer.Services.Rates
{
internal class HttpClientRequestMaker : IAPIRequestMaker
{
- class InternalHttpWebRequest : IHttpWebRequest
+#nullable enable
+ internal class InternalHttpWebRequest : IHttpWebRequest
{
- internal readonly HttpWebRequest Request;
+ internal readonly HttpRequestMessage Request;
+ internal HttpResponseMessage? Response;
+ private string? contentType;
- public Uri RequestUri => Request.RequestUri;
-
- public string Method
+ public InternalHttpWebRequest(string method, Uri fullUri)
{
- get
- {
- return Request.Method;
- }
- set
- {
- Request.Method = value;
- }
- }
-
- public int Timeout
- {
- get
- {
- return Request.Timeout;
- }
- set
- {
- Request.Timeout = value;
- }
- }
-
- public int ReadWriteTimeout
- {
- get
- {
- return Request.ReadWriteTimeout;
- }
- set
- {
- Request.ReadWriteTimeout = value;
- }
- }
-
- public InternalHttpWebRequest(Uri fullUri)
- {
- Request = ((WebRequest.Create(fullUri) as HttpWebRequest) ?? throw new NullReferenceException("Failed to create HttpWebRequest"));
- Request.KeepAlive = false;
+ Request = new HttpRequestMessage(new HttpMethod(method), fullUri);
}
public void AddHeader(string header, string value)
{
- switch (header.ToStringLowerInvariant())
+ switch (header.ToLowerInvariant())
{
case "content-type":
- Request.ContentType = value;
- break;
- case "content-length":
- Request.ContentLength = value.ConvertInvariant(0L);
- break;
- case "user-agent":
- Request.UserAgent = value;
- break;
- case "accept":
- Request.Accept = value;
- break;
- case "connection":
- Request.Connection = value;
+ contentType = value;
break;
default:
- Request.Headers[header] = value;
+ Request.Headers.TryAddWithoutValidation(header, value);
break;
}
}
+ public Uri RequestUri
+ {
+ get { return Request.RequestUri!; }
+ }
+
+ public string Method
+ {
+ get { return Request.Method.Method; }
+ set { Request.Method = new HttpMethod(value); }
+ }
+
+ public int Timeout { get; set; }
+
+ public int ReadWriteTimeout
+ {
+ get => Timeout;
+ set => Timeout = value;
+ }
+
+
public Task WriteAllAsync(byte[] data, int index, int length)
{
- throw new NotImplementedException();
- }
-
- public HttpRequestMessage ToHttpRequestMessage()
- {
- var httpRequest = new HttpRequestMessage(HttpMethod.Get, Request.RequestUri);
- CopyHeadersFrom(httpRequest, Request);
- return httpRequest;
- }
-
- internal void CopyHeadersFrom(HttpRequestMessage message, HttpWebRequest request)
- {
- foreach (string headerName in request.Headers)
- {
- string[] headerValues = request.Headers.GetValues(headerName);
- if (!message.Headers.TryAddWithoutValidation(headerName, headerValues))
- {
- if (message.Content != null)
- message.Content.Headers.TryAddWithoutValidation(headerName, headerValues);
- }
- }
+ Request.Content = new ByteArrayContent(data, index, length);
+ Request.Content.Headers.Add("content-type", contentType);
+ return Task.CompletedTask;
}
}
+#nullable restore
class InternalHttpWebResponse : IHttpWebResponse
{
public InternalHttpWebResponse(HttpResponseMessage httpResponseMessage)
@@ -164,44 +119,61 @@ namespace BTCPayServer.Services.Rates
{
url = "/" + url;
}
- string uri2 = (baseUrl ?? api.BaseUrl) + url;
- if (method == null)
- {
- method = api.RequestMethod;
- }
- Uri uri = api.ProcessRequestUrl(new UriBuilder(uri2), payload, method);
- InternalHttpWebRequest request = new InternalHttpWebRequest(uri)
- {
- Method = method
- };
+
+ // prepare the request
+ string fullUrl = (baseUrl ?? api.BaseUrl) + url;
+ method ??= api.RequestMethod;
+ Uri uri = api.ProcessRequestUrl(new UriBuilder(fullUrl), payload, method);
+ InternalHttpWebRequest request = new InternalHttpWebRequest(method, uri);
+ request.AddHeader("accept-language", "en-US,en;q=0.5");
request.AddHeader("content-type", api.RequestContentType);
- request.AddHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36");
- int num3 = request.Timeout = (request.ReadWriteTimeout = (int)api.RequestTimeout.TotalMilliseconds);
+ request.AddHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36");
+ request.Timeout = (int)api.RequestTimeout.TotalMilliseconds;
await api.ProcessRequestAsync(request, payload);
+
+ // send the request
+ var response = request.Response;
+ string responseString;
+ using var cancel = new CancellationTokenSource(request.Timeout);
try
{
- RequestStateChanged?.Invoke(this, RequestMakerState.Begin, uri.AbsoluteUri);
- using var webHttpRequest = request.ToHttpRequestMessage();
- using var webHttpResponse = await _httpClient.SendAsync(webHttpRequest, _cancellationToken);
- string text = await webHttpResponse.Content.ReadAsStringAsync();
- if (!webHttpResponse.IsSuccessStatusCode)
+ RequestStateChanged?.Invoke(this, RequestMakerState.Begin, uri.AbsoluteUri);// when start make a request we send the uri, this helps developers to track the http requests.
+ response = await _httpClient.SendAsync(request.Request, cancel.Token);
+ if (response == null)
{
- if (string.IsNullOrWhiteSpace(text))
- {
- throw new APIException($"{webHttpResponse.StatusCode.ConvertInvariant(0)} - {webHttpResponse.StatusCode}");
- }
- throw new APIException(text);
+ throw new APIException("Unknown response from server");
}
- api.ProcessResponse(new InternalHttpWebResponse(webHttpResponse));
+ responseString = await response.Content.ReadAsStringAsync();
- RequestStateChanged?.Invoke(this, RequestMakerState.Finished, text);
- return text;
+ if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.Created)
+ {
+ // 404 maybe return empty responseString
+ if (string.IsNullOrWhiteSpace(responseString))
+ {
+ throw new APIException(string.Format("{0} - {1}", response.StatusCode.ConvertInvariant(), response.StatusCode));
+ }
+
+ throw new APIException(responseString);
+ }
+
+ api.ProcessResponse(new InternalHttpWebResponse(response));
+ RequestStateChanged?.Invoke(this, RequestMakerState.Finished, responseString);
}
- catch (Exception arg)
+ catch (OperationCanceledException ex) when (cancel.IsCancellationRequested)
{
- RequestStateChanged?.Invoke(this, RequestMakerState.Error, arg);
+ RequestStateChanged?.Invoke(this, RequestMakerState.Error, ex);
+ throw new TimeoutException("APIRequest timeout", ex);
+ }
+ catch (Exception ex)
+ {
+ RequestStateChanged?.Invoke(this, RequestMakerState.Error, ex);
throw;
}
+ finally
+ {
+ response?.Dispose();
+ }
+ return responseString;
}
}
}
diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs
index 2d119c5d5..cd3de749b 100644
--- a/BTCPayServer.Tests/UnitTest1.cs
+++ b/BTCPayServer.Tests/UnitTest1.cs
@@ -2881,8 +2881,8 @@ namespace BTCPayServer.Tests
//verify file is available and the same
- var net = new System.Net.WebClient();
- var data = await net.DownloadStringTaskAsync(new Uri(viewFilesViewModel.DirectUrlByFiles[fileId]));
+ using var net = new HttpClient();
+ var data = await net.GetStringAsync(new Uri(viewFilesViewModel.DirectUrlByFiles[fileId]));
Assert.Equal(fileContent, data);
//create a temporary link to file
@@ -2901,7 +2901,7 @@ namespace BTCPayServer.Tests
.Replace("", string.Empty)
.Replace("target='_blank'>", string.Empty);
//verify tmpfile is available and the same
- data = await net.DownloadStringTaskAsync(new Uri(url));
+ data = await net.GetStringAsync(new Uri(url));
Assert.Equal(fileContent, data);
diff --git a/BTCPayServer/Configuration/DefaultConfiguration.cs b/BTCPayServer/Configuration/DefaultConfiguration.cs
index 3fa664789..44493ceeb 100644
--- a/BTCPayServer/Configuration/DefaultConfiguration.cs
+++ b/BTCPayServer/Configuration/DefaultConfiguration.cs
@@ -1,3 +1,4 @@
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
@@ -134,12 +135,12 @@ namespace BTCPayServer.Configuration
builder.AppendLine("### NBXplorer settings ###");
foreach (var n in new BTCPayNetworkProvider(networkType).GetAll().OfType())
{
- builder.AppendLine($"#{n.CryptoCode}.explorer.url={n.NBXplorerNetwork.DefaultSettings.DefaultUrl}");
- builder.AppendLine($"#{n.CryptoCode}.explorer.cookiefile={ n.NBXplorerNetwork.DefaultSettings.DefaultCookieFile}");
+ builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.explorer.url={n.NBXplorerNetwork.DefaultSettings.DefaultUrl}");
+ builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.explorer.cookiefile={ n.NBXplorerNetwork.DefaultSettings.DefaultCookieFile}");
if (n.SupportLightning)
{
- builder.AppendLine($"#{n.CryptoCode}.lightning=/root/.lightning/lightning-rpc");
- builder.AppendLine($"#{n.CryptoCode}.lightning=https://apitoken:API_TOKEN_SECRET@charge.example.com/");
+ builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.lightning=/root/.lightning/lightning-rpc");
+ builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.lightning=https://apitoken:API_TOKEN_SECRET@charge.example.com/");
}
}
return builder.ToString();
diff --git a/BTCPayServer/Controllers/AppsController.PointOfSale.cs b/BTCPayServer/Controllers/AppsController.PointOfSale.cs
index 75592d14b..2c3dc13d1 100644
--- a/BTCPayServer/Controllers/AppsController.PointOfSale.cs
+++ b/BTCPayServer/Controllers/AppsController.PointOfSale.cs
@@ -1,4 +1,5 @@
using System;
+using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
@@ -129,7 +130,7 @@ namespace BTCPayServer.Controllers
if (settings.ShowCustomAmount)
{
StringBuilder builder = new StringBuilder();
- builder.AppendLine($"");
vm.Example2 = builder.ToString();
}
diff --git a/BTCPayServer/Controllers/GreenField/LocalBTCPayServerClient.cs b/BTCPayServer/Controllers/GreenField/LocalBTCPayServerClient.cs
index 3c7cd6c96..54cc48a21 100644
--- a/BTCPayServer/Controllers/GreenField/LocalBTCPayServerClient.cs
+++ b/BTCPayServer/Controllers/GreenField/LocalBTCPayServerClient.cs
@@ -732,9 +732,9 @@ namespace BTCPayServer.Controllers.GreenField
return GetFromActionResult(await _storesController.GetStores());
}
- public override async Task GetStore(string storeId, CancellationToken token = default)
+ public override Task GetStore(string storeId, CancellationToken token = default)
{
- return GetFromActionResult(_storesController.GetStore(storeId));
+ return Task.FromResult(GetFromActionResult(_storesController.GetStore(storeId)));
}
public override async Task RemoveStore(string storeId, CancellationToken token = default)
diff --git a/BTCPayServer/Controllers/GreenField/StoreOnChainWalletsController.cs b/BTCPayServer/Controllers/GreenField/StoreOnChainWalletsController.cs
index fc8516e23..2ba353412 100644
--- a/BTCPayServer/Controllers/GreenField/StoreOnChainWalletsController.cs
+++ b/BTCPayServer/Controllers/GreenField/StoreOnChainWalletsController.cs
@@ -538,7 +538,8 @@ namespace BTCPayServer.Controllers.GreenField
derivationScheme = GetDerivationSchemeSettings(cryptoCode);
if (derivationScheme?.AccountDerivation is null)
{
- actionResult = NotFound();
+ actionResult = this.CreateAPIError("not-available",
+ $"{cryptoCode} doesn't have any derivation scheme set");
return true;
}
@@ -546,7 +547,7 @@ namespace BTCPayServer.Controllers.GreenField
return false;
}
- private DerivationSchemeSettings GetDerivationSchemeSettings(string cryptoCode)
+ private DerivationSchemeSettings? GetDerivationSchemeSettings(string cryptoCode)
{
var paymentMethod = Store
.GetSupportedPaymentMethods(_btcPayNetworkProvider)
diff --git a/BTCPayServer/Controllers/GreenField/UsersController.cs b/BTCPayServer/Controllers/GreenField/UsersController.cs
index de7c1beba..7bac69797 100644
--- a/BTCPayServer/Controllers/GreenField/UsersController.cs
+++ b/BTCPayServer/Controllers/GreenField/UsersController.cs
@@ -91,11 +91,12 @@ namespace BTCPayServer.Controllers.GreenField
}
if (request.Password is null)
ModelState.AddModelError(nameof(request.Password), "Password is missing");
-
if (!ModelState.IsValid)
{
return this.CreateValidationError(ModelState);
}
+ if (User.Identity is null)
+ throw new JsonHttpException(this.StatusCode(401));
var anyAdmin = (await _userManager.GetUsersInRoleAsync(Roles.ServerAdmin)).Any();
var policies = await _settingsRepository.GetSettingAsync() ?? new PoliciesSettings();
var isAuth = User.Identity.AuthenticationType == GreenFieldConstants.AuthenticationType;
diff --git a/BTCPayServer/Controllers/InvoiceController.UI.cs b/BTCPayServer/Controllers/InvoiceController.UI.cs
index 6dfb2d434..72f7a12f0 100644
--- a/BTCPayServer/Controllers/InvoiceController.UI.cs
+++ b/BTCPayServer/Controllers/InvoiceController.UI.cs
@@ -359,7 +359,7 @@ namespace BTCPayServer.Controllers
Html = "Refund successfully created!
Share the link to this page with a customer.
The customer needs to enter their address and claim the refund.
Once a customer claims the refund, you will get a notification and would need to approve and initiate it from your Store > Payouts.",
Severity = StatusMessageModel.StatusSeverity.Success
});
- (await ctx.Invoices.FindAsync(new[] { invoice.Id }, cancellationToken)).CurrentRefundId = ppId;
+ (await ctx.Invoices.FindAsync(new[] { invoice.Id }, cancellationToken))!.CurrentRefundId = ppId;
ctx.Refunds.Add(new RefundData()
{
InvoiceDataId = invoice.Id,
diff --git a/BTCPayServer/Controllers/RateController.cs b/BTCPayServer/Controllers/RateController.cs
index b43eb905e..cea64e559 100644
--- a/BTCPayServer/Controllers/RateController.cs
+++ b/BTCPayServer/Controllers/RateController.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
@@ -159,7 +160,7 @@ namespace BTCPayServer.Controllers
if (!first)
currencyPairsBuilder.Append(',');
first = false;
- currencyPairsBuilder.Append($"{baseCrypto}_{currencyCode}");
+ currencyPairsBuilder.Append(CultureInfo.InvariantCulture, $"{baseCrypto}_{currencyCode}");
}
return currencyPairsBuilder.ToString();
}
diff --git a/BTCPayServer/Data/StoreBlob.cs b/BTCPayServer/Data/StoreBlob.cs
index 0629d85f9..4dabe982b 100644
--- a/BTCPayServer/Data/StoreBlob.cs
+++ b/BTCPayServer/Data/StoreBlob.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Globalization;
using System.Linq;
using System.Text;
using BTCPayServer.Client.JsonConverters;
@@ -144,7 +145,7 @@ namespace BTCPayServer.Data
{
if (network.DefaultRateRules.Length != 0)
{
- builder.AppendLine($"// Default rate rules for {network.CryptoCode}");
+ builder.AppendLine(CultureInfo.InvariantCulture, $"// Default rate rules for {network.CryptoCode}");
foreach (var line in network.DefaultRateRules)
{
builder.AppendLine(line);
@@ -155,7 +156,7 @@ namespace BTCPayServer.Data
}
var preferredExchange = string.IsNullOrEmpty(PreferredExchange) ? CoinGeckoRateProvider.CoinGeckoName : PreferredExchange;
- builder.AppendLine($"X_X = {preferredExchange}(X_X);");
+ builder.AppendLine(CultureInfo.InvariantCulture, $"X_X = {preferredExchange}(X_X);");
BTCPayServer.Rating.RateRules.TryParse(builder.ToString(), out var rules);
rules.Spread = Spread;
diff --git a/BTCPayServer/Extensions.cs b/BTCPayServer/Extensions.cs
index 4f92d375a..8094d2ad9 100644
--- a/BTCPayServer/Extensions.cs
+++ b/BTCPayServer/Extensions.cs
@@ -91,7 +91,7 @@ namespace BTCPayServer
builder.Append(expiration.Days.ToString(CultureInfo.InvariantCulture));
if (expiration.Hours >= 1)
builder.Append(expiration.Hours.ToString("00", CultureInfo.InvariantCulture));
- builder.Append($"{expiration.Minutes.ToString("00", CultureInfo.InvariantCulture)}:{expiration.Seconds.ToString("00", CultureInfo.InvariantCulture)}");
+ builder.Append(CultureInfo.InvariantCulture, $"{expiration.Minutes.ToString("00", CultureInfo.InvariantCulture)}:{expiration.Seconds.ToString("00", CultureInfo.InvariantCulture)}");
return builder.ToString();
}
public static decimal RoundUp(decimal value, int precision)
diff --git a/BTCPayServer/HostedServices/TransactionLabelMarkerHostedService.cs b/BTCPayServer/HostedServices/TransactionLabelMarkerHostedService.cs
index 7f84e24be..9db69e835 100644
--- a/BTCPayServer/HostedServices/TransactionLabelMarkerHostedService.cs
+++ b/BTCPayServer/HostedServices/TransactionLabelMarkerHostedService.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
@@ -161,7 +162,7 @@ namespace BTCPayServer.HostedServices
var result = new StringBuilder();
foreach (var transactionLabel in TransactionLabels)
{
- result.AppendLine(
+ result.AppendLine(CultureInfo.InvariantCulture,
$"Adding {transactionLabel.Value.Count} labels to {transactionLabel.Key} in wallet {WalletId}");
}
diff --git a/BTCPayServer/Plugins/PluginService.cs b/BTCPayServer/Plugins/PluginService.cs
index c60934fec..228f3ef89 100644
--- a/BTCPayServer/Plugins/PluginService.cs
+++ b/BTCPayServer/Plugins/PluginService.cs
@@ -63,7 +63,10 @@ namespace BTCPayServer.Plugins
}
var filedest = Path.Join(dest, ext.Name);
Directory.CreateDirectory(Path.GetDirectoryName(filedest));
- new WebClient().DownloadFile(new Uri(ext.DownloadUrl), filedest);
+ using var resp2 = await _githubClient.GetAsync(ext.DownloadUrl);
+ using var fs = new FileStream(filedest, FileMode.Create, FileAccess.ReadWrite);
+ await resp2.Content.CopyToAsync(fs);
+ await fs.FlushAsync();
}
public void InstallPlugin(string plugin)
diff --git a/BTCPayServer/Services/BTCPayServerEnvironment.cs b/BTCPayServer/Services/BTCPayServerEnvironment.cs
index b13c91b6d..440bdcf0e 100644
--- a/BTCPayServer/Services/BTCPayServerEnvironment.cs
+++ b/BTCPayServer/Services/BTCPayServerEnvironment.cs
@@ -1,4 +1,5 @@
using System;
+using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
@@ -82,12 +83,12 @@ namespace BTCPayServer.Services
public override string ToString()
{
StringBuilder txt = new StringBuilder();
- txt.Append($"@Copyright BTCPayServer v{Version}");
+ txt.Append(CultureInfo.InvariantCulture, $"@Copyright BTCPayServer v{Version}");
if (AltcoinsVersion)
txt.Append($" (altcoins)");
if (!Environment.IsProduction() || !Build.Equals("Release", StringComparison.OrdinalIgnoreCase))
{
- txt.Append($" Environment: {Environment.EnvironmentName} Build: {Build}");
+ txt.Append(CultureInfo.InvariantCulture, $" Environment: {Environment.EnvironmentName} Build: {Build}");
}
return txt.ToString();
}
diff --git a/Build/Common.csproj b/Build/Common.csproj
index 6ee9e1d16..3831b2e3d 100644
--- a/Build/Common.csproj
+++ b/Build/Common.csproj
@@ -3,7 +3,7 @@
net6.0
$(TargetFrameworkOverride)
NU1701,CA1816,CA1308,CA1810,CA2208,CA1303,CA2000,CA2016,CA1835,CA2249,CA9998,CA1704
- 8.0
+ 10.0