using System;
using System.Configuration;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Web.Mvc;
using System.Xml;
using Neo.LegitimateLicences.Logic.eNatis;
using Newtonsoft.Json;
namespace Neo.LegitimateLicences.ApplicationRequest.Controllers
{
///
/// Standalone test controller for eNatis API connection testing
/// Does not require authentication or database access
///
public class ENatisTestController : Controller
{
#region eNatis Login Test
private static string _cookie = string.Empty;
private static string loginURL = ConfigurationManager.AppSettings["eNatisLoginURL"];
private static string eNatisQueryURL = ConfigurationManager.AppSettings["eNatisQueryURL"];
///
/// Test page to display eNatis login test interface
///
///
[AllowAnonymous]
public ActionResult Test()
{
return View();
}
///
/// Execute eNatis login test - exact same logic as ApplicationRequestController.eNatisLogin()
///
///
[HttpPost]
[AllowAnonymous]
public JsonResult TestLogin()
{
// Delegate to the shared ENatisSession so the test page uses the same
// serialised, session-reusing path as the rest of the app. The try/finally
// around ForceLogout guarantees we don't leak a session even if an
// exception bubbles up — which was the primary leak source on the test page.
try
{
string result;
try { ENatisSession.EnsureSession(); result = "SUCCESS"; }
catch (MaxSessionsInUseException) { result = "MAX_SESSIONS_IN_USE"; }
var cookie = ENatisSession.Cookie;
var cookieInfo = string.IsNullOrEmpty(cookie) ? "No cookie received" : "Cookie received: " + cookie.Substring(0, Math.Min(50, cookie.Length)) + "...";
return Json(new
{
Success = true,
RequestState = result,
CookieInfo = cookieInfo,
Message = "eNatis login test completed successfully"
});
}
catch (Exception ex)
{
// Build comprehensive error message including inner exceptions
var errorBuilder = new StringBuilder();
errorBuilder.AppendLine($"Error: {ex.Message}");
errorBuilder.AppendLine($"Type: {ex.GetType().Name}");
// Handle AggregateException (common with async operations)
if (ex is AggregateException aggregateEx)
{
errorBuilder.AppendLine();
errorBuilder.AppendLine("Inner Exceptions:");
int innerIndex = 1;
foreach (var innerEx in aggregateEx.InnerExceptions)
{
errorBuilder.AppendLine($" [{innerIndex}] {innerEx.GetType().Name}: {innerEx.Message}");
if (innerEx.InnerException != null)
{
errorBuilder.AppendLine($" -> {innerEx.InnerException.GetType().Name}: {innerEx.InnerException.Message}");
}
innerIndex++;
}
}
// Handle regular inner exceptions
else if (ex.InnerException != null)
{
errorBuilder.AppendLine();
errorBuilder.AppendLine("Inner Exception:");
errorBuilder.AppendLine($" Type: {ex.InnerException.GetType().Name}");
errorBuilder.AppendLine($" Message: {ex.InnerException.Message}");
// Check for nested inner exceptions
var currentInner = ex.InnerException.InnerException;
int depth = 2;
while (currentInner != null && depth < 5) // Limit depth to avoid infinite loops
{
errorBuilder.AppendLine($" [{depth}] {currentInner.GetType().Name}: {currentInner.Message}");
currentInner = currentInner.InnerException;
depth++;
}
}
var fullError = errorBuilder.ToString();
return Json(new
{
Success = false,
Error = fullError,
ErrorMessage = ex.Message,
ErrorType = ex.GetType().Name,
InnerException = ex.InnerException != null ? ex.InnerException.Message : null,
InnerExceptionType = ex.InnerException != null ? ex.InnerException.GetType().Name : null,
StackTrace = ex.StackTrace,
Message = "eNatis login test failed"
});
}
finally
{
// Always release the eNatis session after the test. Without this,
// every test click leaked a session, exhausting MAX_SESSIONS_IN_USE
// after ~5 clicks.
try { ENatisSession.ForceLogout(); } catch { /* swallow — best-effort */ }
}
}
// Legacy login implementation kept below for reference; replaced by ENatisSession.
#if FALSE
private string eNatisLoginLegacy()
{
string certificatePath = ConfigurationManager.AppSettings["eNatisCertificatePath"];
string certificatePassword = ConfigurationManager.AppSettings["eNatisCertificatePassword"];
// Validate certificate path exists
if (string.IsNullOrEmpty(certificatePath))
{
throw new Exception("eNatisCertificatePath is not configured in web.config");
}
if (!System.IO.File.Exists(certificatePath))
{
throw new Exception($"Certificate file not found at path: {certificatePath}. Please verify the certificate path is correct and the file is accessible on the server.");
}
// Set up HttpClientHandler with the certificate
var handler = new WebRequestHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual
};
// Load the certificate with error handling
X509Certificate2 certificate;
try
{
certificate = new X509Certificate2(certificatePath, certificatePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
}
catch (Exception certEx)
{
throw new Exception($"Failed to load certificate from {certificatePath}. Error: {certEx.Message}. Please verify the certificate file is valid and the password is correct.", certEx);
}
handler.ClientCertificates.Add(certificate);
// Support both TLS 1.1 and TLS 1.2 for eNatis connectivity
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12 | System.Net.SecurityProtocolType.Tls11;
// Allow all SSL certificate validation (for testing purposes)
System.Net.ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true;
// Set default connection limit
System.Net.ServicePointManager.DefaultConnectionLimit = 10;
// Ensure we're using the latest security protocols
System.Net.ServicePointManager.Expect100Continue = false;
// The XML content to send in the POST request
var xmlContent = $@"
{ConfigurationManager.AppSettings["eNatisOpModInd"]}
{ConfigurationManager.AppSettings["eNatisESTxanID"]}
{ConfigurationManager.AppSettings["eNatisESUserN"]}
{ConfigurationManager.AppSettings["eNatisESUID"]}
{ConfigurationManager.AppSettings["eNatisEStermID"]}
{ConfigurationManager.AppSettings["eNatisUsername"]}
{ConfigurationManager.AppSettings["eNatisPassword"]}
{ConfigurationManager.AppSettings["eNatisTxanType"]}
";
var content = new StringContent(xmlContent, Encoding.UTF8, "application/xml");
using (var client = new HttpClient(handler))
{
// Example: Add Authorization token if needed
var username = ConfigurationManager.AppSettings["eNatisUsername"];
var password = ConfigurationManager.AppSettings["eNatisPassword"];
var credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{username}:{password}"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
// Perform the POST request - try TLS 1.1 first, fallback to TLS 1.2 if it fails
HttpResponseMessage response;
try
{
response = client.PostAsync(loginURL, content).Result;
}
catch (Exception tlsEx)
{
// If TLS 1.1 fails, try TLS 1.2
if (tlsEx.Message.Contains("SSL/TLS") || tlsEx.Message.Contains("secure channel"))
{
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
// Create a new handler and client for TLS 1.2
var handler2 = new WebRequestHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual
};
var certificate2 = new X509Certificate2(certificatePath, certificatePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
handler2.ClientCertificates.Add(certificate2);
using (var client2 = new HttpClient(handler2))
{
client2.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
response = client2.PostAsync(loginURL, content).Result;
}
}
else
{
throw;
}
}
// Check if the response contains the "Set-Cookie" header
if (response.Headers.Contains("Set-Cookie"))
{
// Retrieve the cookies from the "Set-Cookie" header
var cookies = response.Headers.GetValues("Set-Cookie");
foreach (var c in cookies)
{
_cookie = c;
}
}
// Read and output the response
var responseContent = response.Content.ReadAsStringAsync().Result;
// Load the XML into an XmlDocument
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(responseContent);
// Convert the XmlDocument to a JSON string
string json = JsonConvert.SerializeXmlNode(xmlDoc);
// Parse the JSON into a dynamic object
dynamic jsonObj = JsonConvert.DeserializeObject(json);
//Get the status of the request
var requestState = jsonObj.X4000Resp.TxState.Value;
return requestState;
}
}
#endif
#endregion
}
}