From 6ee24de2271ef869de306272735301df809a3bf1 Mon Sep 17 00:00:00 2001 From: misya Date: Wed, 14 May 2025 10:06:51 +0800 Subject: [PATCH] fix access to path --- Areas/MMS/Controllers/MarineController.cs | 151 +++++++++++++++++--- Areas/MMS/Models/NetworkAccessService.cs | 125 ++++++++-------- Areas/MMS/Models/PDFGenerator/TarBallPDF.cs | 2 +- Program.cs | 2 +- 4 files changed, 200 insertions(+), 80 deletions(-) diff --git a/Areas/MMS/Controllers/MarineController.cs b/Areas/MMS/Controllers/MarineController.cs index 15e7efe..5e9a2dd 100644 --- a/Areas/MMS/Controllers/MarineController.cs +++ b/Areas/MMS/Controllers/MarineController.cs @@ -13,7 +13,7 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers { private readonly MMSSystemContext _context;//Used in TarBallForm and GeneratePdfResponse to query the database. private readonly NetworkShareAccess _networkAccessService;//used in GetImage and GeneratePdfResponse - private const string PhotoBasePath = "\\\\192.168.12.42\\images\\marine\\manual_tarball";//used in GetImage and GeneratePdfResponse + private const string PhotoBasePath = @"\\192.168.12.42\images\marine\manual_tarball";//used in GetImage and GeneratePdfResponse public MarineController(MMSSystemContext context, NetworkShareAccess networkAccessService) { @@ -77,32 +77,119 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers public IActionResult GetImage(string fileName) { + if (string.IsNullOrEmpty(fileName)) + { + return BadRequest("Filename cannot be empty"); + } + + // Sanitize filename to prevent path traversal attacks + var sanitizedFileName = Path.GetFileName(fileName); + if (sanitizedFileName != fileName) + { + return BadRequest("Invalid filename"); + } + + int retryCount = 0; + const int maxRetries = 3; + bool connectionSuccess = false; + + // Retry loop for network connection + while (retryCount < maxRetries && !connectionSuccess) + { + try + { + Console.WriteLine($"Attempt {retryCount + 1} to connect to network share..."); + + // Connect to network share + _networkAccessService.ConnectToNetworkPath(); + connectionSuccess = true; + + Console.WriteLine("Network share connected successfully"); + } + catch (Exception ex) + { + retryCount++; + Console.WriteLine($"Connection attempt {retryCount} failed: {ex.Message}"); + + if (retryCount >= maxRetries) + { + Console.WriteLine($"Max connection attempts reached. Last error: {ex}"); + return StatusCode(503, $"Could not establish connection to image server after {maxRetries} attempts"); + } + + // Wait before retrying (1s, 2s, 3s) + Thread.Sleep(1000 * retryCount); + } + } + try { - // Connect to the network path - _networkAccessService.ConnectToNetworkPath(); + string imagePath = Path.Combine(PhotoBasePath, sanitizedFileName); + Console.WriteLine($"Attempting to access image at: {imagePath}"); - string imagePath = Path.Combine(PhotoBasePath, fileName); + // Verify file exists + if (!System.IO.File.Exists(imagePath)) + { + Console.WriteLine($"Image not found at path: {imagePath}"); + return NotFound($"Image '{sanitizedFileName}' not found on server"); + } - if (System.IO.File.Exists(imagePath)) + // Verify file is an image + if (!IsImageValid(imagePath)) { - byte[] imageBytes = System.IO.File.ReadAllBytes(imagePath); - return File(imageBytes, "image/jpeg"); + Console.WriteLine($"Invalid image file at path: {imagePath}"); + return BadRequest("The requested file is not a valid image"); } - else + + // Read and return the image + byte[] imageBytes = System.IO.File.ReadAllBytes(imagePath); + Console.WriteLine($"Successfully read image: {sanitizedFileName} ({imageBytes.Length} bytes)"); + + // Determine content type based on extension + string contentType = "image/jpeg"; // default + string extension = Path.GetExtension(sanitizedFileName)?.ToLower(); + + if (extension == ".png") { - return NotFound("Image not found."); + contentType = "image/png"; } + else if (extension == ".gif") + { + contentType = "image/gif"; + } + + return File(imageBytes, contentType); + } + catch (UnauthorizedAccessException ex) + { + Console.WriteLine($"Access denied to image: {ex}"); + return StatusCode(403, "Access to the image was denied"); + } + catch (IOException ex) + { + Console.WriteLine($"IO error accessing image: {ex}"); + return StatusCode(503, "Error accessing image file"); } catch (Exception ex) { - Console.WriteLine($"Error retrieving image: {ex.Message}"); - return StatusCode(500, "Error retrieving image."); + Console.WriteLine($"Unexpected error: {ex}"); + return StatusCode(500, "An unexpected error occurred while processing the image"); } finally { - // Disconnect from the network path - _networkAccessService.DisconnectFromNetworkShare(); + try + { + if (connectionSuccess) + { + Console.WriteLine("Disconnecting from network share..."); + _networkAccessService.DisconnectFromNetworkShare(); + } + } + catch (Exception ex) + { + Console.WriteLine($"Warning: Error disconnecting from share: {ex.Message}"); + // Don't fail the request because of disconnect issues + } } } @@ -113,13 +200,41 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers public IActionResult ViewPDF(int id) { - return GeneratePdfResponse(id, false); + try + { + // Add timeout for safety + var task = Task.Run(() => GeneratePdfResponse(id, false)); + if (task.Wait(TimeSpan.FromSeconds(30))) // 30 second timeout + { + return task.Result; + } + return StatusCode(500, "PDF generation took too long"); + } + catch (Exception ex) + { + Console.WriteLine($"PDF VIEW ERROR: {ex}"); + return StatusCode(500, $"Error showing PDF: {ex.Message}"); + } } private IActionResult GeneratePdfResponse(int id, bool forceDownload) { Console.WriteLine($"Requested ID in {(forceDownload ? "GenerateReport" : "ViewPDF")}: {id}"); + // Add this temporary check at the start of GeneratePdfResponse + try + { + Console.WriteLine("Testing network connection..."); + _networkAccessService.ConnectToNetworkPath(); + Console.WriteLine("Network connected successfully!"); + _networkAccessService.DisconnectFromNetworkShare(); + } + catch (Exception ex) + { + Console.WriteLine($"NETWORK ERROR: {ex.Message}"); + return StatusCode(500, $"Cannot access network: {ex.Message}"); + } + try { // Connect to the network path @@ -256,8 +371,12 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers } catch (Exception ex) { - Console.WriteLine($"Error: {ex.Message}"); - return StatusCode(500, $"PDF generation failed: {ex.Message}"); + Console.WriteLine($"Error: {ex}"); + // Include inner exception if available + string errorMessage = ex.InnerException != null + ? $"{ex.Message} (Inner: {ex.InnerException.Message})" + : ex.Message; + return StatusCode(500, $"PDF generation failed: {errorMessage}"); } finally { diff --git a/Areas/MMS/Models/NetworkAccessService.cs b/Areas/MMS/Models/NetworkAccessService.cs index e0a1c74..68c1dcd 100644 --- a/Areas/MMS/Models/NetworkAccessService.cs +++ b/Areas/MMS/Models/NetworkAccessService.cs @@ -2,24 +2,32 @@ using System.IO; using System.Runtime.InteropServices; using System.Text; +using System.ComponentModel; public class NetworkShareAccess : IDisposable { private readonly string _networkPath; private readonly string _username; private readonly string _password; + private bool _isConnected = false; - // Constructor to initialize the network path, username, and password public NetworkShareAccess(string networkPath, string username, string password) { - _networkPath = networkPath; - _username = username; - _password = password; + // Validate and normalize the network path + _networkPath = networkPath?.Trim().Replace('/', '\\') ?? throw new ArgumentNullException(nameof(networkPath)); + if (!_networkPath.StartsWith(@"\\")) + { + throw new ArgumentException("Network path must start with \\\\"); + } + + _username = username ?? throw new ArgumentNullException(nameof(username)); + _password = password ?? throw new ArgumentNullException(nameof(password)); } - // Public method to connect to the network path public void ConnectToNetworkPath() { + if (_isConnected) return; + var netResource = new NetResource { Scope = ResourceScope.GlobalNetwork, @@ -28,59 +36,78 @@ public class NetworkShareAccess : IDisposable RemoteName = _networkPath }; - var result = WNetAddConnection2(netResource, _password, _username, 0);//ERROR HERE + int result = WNetAddConnection2(netResource, _password, _username, 0); if (result != 0) { - Console.WriteLine($"Error connecting to network path: {result}"); - throw new IOException($"Error connecting to network path: {result}"); + string errorMessage = GetNetworkErrorDescription(result); + Console.WriteLine($"Failed to connect to {_networkPath}. Error {result}: {errorMessage}"); + throw new IOException($"Failed to connect to network path. Error {result}: {errorMessage}"); } + _isConnected = true; + Console.WriteLine($"Successfully connected to {_networkPath}"); } - // Public method to disconnect from the network share public void DisconnectFromNetworkShare() { - // Check if the network path is connected - var buffer = new StringBuilder(512); - int bufferSize = buffer.Capacity; - int connectionStatus = WNetGetConnection(_networkPath, buffer, ref bufferSize); + if (!_isConnected) return; - if (connectionStatus != 0) + try { - Console.WriteLine($"Network path {_networkPath} is not connected. Skipping disconnection."); - return; // Exit if the connection does not exist + int result = WNetCancelConnection2(_networkPath, 0, true); + if (result != 0) + { + string errorMessage = GetNetworkErrorDescription(result); + Console.WriteLine($"Failed to disconnect from {_networkPath}. Error {result}: {errorMessage}"); + throw new IOException($"Failed to disconnect from network path. Error {result}: {errorMessage}"); + } + + Console.WriteLine($"Successfully disconnected from {_networkPath}"); } - - // Proceed with disconnection - var result = WNetCancelConnection2(_networkPath, 0, true); - - if (result != 0) + finally { - throw new IOException($"Error disconnecting from network path: {result}"); + _isConnected = false; } } - // Dispose method to ensure proper cleanup public void Dispose() { - DisconnectFromNetworkShare(); + try + { + DisconnectFromNetworkShare(); + } + catch (Exception ex) + { + Console.WriteLine($"Error during disposal: {ex.Message}"); + // Suppress disposal errors + } } - // P/Invoke for adding a network connection - [DllImport("mpr.dll")] + private string GetNetworkErrorDescription(int errorCode) + { + return errorCode switch + { + 5 => "Access denied", + 53 => "Network path not found", + 67 => "Network name not found", + 85 => "Network connection already exists", + 86 => "Invalid password", + 1219 => "Multiple connections to a server or shared resource not allowed", + _ => new Win32Exception(errorCode).Message + }; + } + + [DllImport("mpr.dll", CharSet = CharSet.Unicode)] private static extern int WNetAddConnection2(NetResource netResource, string password, string username, int flags); - // P/Invoke for canceling a network connection - [DllImport("mpr.dll")] + [DllImport("mpr.dll", CharSet = CharSet.Unicode)] private static extern int WNetCancelConnection2(string name, int flags, bool force); - // P/Invoke for checking if a network connection exists - [DllImport("mpr.dll")] + [DllImport("mpr.dll", CharSet = CharSet.Unicode)] private static extern int WNetGetConnection(string localName, StringBuilder remoteName, ref int length); - // Struct for network resource - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private class NetResource { public ResourceScope Scope; @@ -93,33 +120,7 @@ public class NetworkShareAccess : IDisposable public string Provider; } - // Enum for resource scope - private enum ResourceScope - { - Connected = 1, - GlobalNetwork, - Remembered, - Recent, - Context - } - - // Enum for resource type - private enum ResourceType - { - Any = 0, - Disk = 1, - Print = 2, - Reserved = 8 - } - - // Enum for resource display type - private enum ResourceDisplayType - { - Generic = 0x0, - Domain = 0x01, - Server = 0x02, - Share = 0x03, - File = 0x04, - Group = 0x05 - } -} + private enum ResourceScope { Connected = 1, GlobalNetwork, Remembered, Recent, Context } + private enum ResourceType { Any = 0, Disk = 1, Print = 2, Reserved = 8 } + private enum ResourceDisplayType { Generic = 0x0, Domain = 0x01, Server = 0x02, Share = 0x03, File = 0x04, Group = 0x05 } +} \ No newline at end of file diff --git a/Areas/MMS/Models/PDFGenerator/TarBallPDF.cs b/Areas/MMS/Models/PDFGenerator/TarBallPDF.cs index 34803d7..4c941f0 100644 --- a/Areas/MMS/Models/PDFGenerator/TarBallPDF.cs +++ b/Areas/MMS/Models/PDFGenerator/TarBallPDF.cs @@ -86,7 +86,7 @@ namespace PSTW_CentralSystem.Areas.MMS.Models.PDFGenerator column.Item() .AlignMiddle() .AlignCenter() - .Image("assets/images/pstw-logo.png") + .Image("wwwroot/assets/images/pstw-logo.jpg") .FitArea(); }); diff --git a/Program.cs b/Program.cs index d06e4f2..ca64bc5 100644 --- a/Program.cs +++ b/Program.cs @@ -23,7 +23,7 @@ internal class Program builder.Services.AddControllersWithViews(); builder.Services.AddRazorPages(); builder.Services.AddScoped(provider => - new NetworkShareAccess("\\\\192.168.12.42\\images\\marine\\manual_tarball", "installer", "mms@pstw")); + new NetworkShareAccess(@"\\192.168.12.42\images\marine\manual_tarball", "installer", "mms@pstw")); Log.Logger = new LoggerConfiguration()