fix access to path

This commit is contained in:
misya 2025-05-14 10:06:51 +08:00
parent 0b298c7965
commit 6ee24de227
4 changed files with 200 additions and 80 deletions

View File

@ -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 MMSSystemContext _context;//Used in TarBallForm and GeneratePdfResponse to query the database.
private readonly NetworkShareAccess _networkAccessService;//used in GetImage and GeneratePdfResponse 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) public MarineController(MMSSystemContext context, NetworkShareAccess networkAccessService)
{ {
@ -76,33 +76,120 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
} }
public IActionResult GetImage(string fileName) 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 try
{ {
// Connect to the network path Console.WriteLine($"Attempt {retryCount + 1} to connect to network share...");
// Connect to network share
_networkAccessService.ConnectToNetworkPath(); _networkAccessService.ConnectToNetworkPath();
connectionSuccess = true;
string imagePath = Path.Combine(PhotoBasePath, fileName); Console.WriteLine("Network share connected successfully");
if (System.IO.File.Exists(imagePath))
{
byte[] imageBytes = System.IO.File.ReadAllBytes(imagePath);
return File(imageBytes, "image/jpeg");
} }
else catch (Exception ex)
{ {
return NotFound("Image not found."); 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
{
string imagePath = Path.Combine(PhotoBasePath, sanitizedFileName);
Console.WriteLine($"Attempting to access image at: {imagePath}");
// 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");
}
// Verify file is an image
if (!IsImageValid(imagePath))
{
Console.WriteLine($"Invalid image file at path: {imagePath}");
return BadRequest("The requested file is not a valid image");
}
// 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")
{
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($"Unexpected error: {ex}");
return StatusCode(500, "An unexpected error occurred while processing the image");
}
finally
{
try
{
if (connectionSuccess)
{
Console.WriteLine("Disconnecting from network share...");
_networkAccessService.DisconnectFromNetworkShare();
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Error retrieving image: {ex.Message}"); Console.WriteLine($"Warning: Error disconnecting from share: {ex.Message}");
return StatusCode(500, "Error retrieving image."); // Don't fail the request because of disconnect issues
} }
finally
{
// Disconnect from the network path
_networkAccessService.DisconnectFromNetworkShare();
} }
} }
@ -113,13 +200,41 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
public IActionResult ViewPDF(int id) 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) private IActionResult GeneratePdfResponse(int id, bool forceDownload)
{ {
Console.WriteLine($"Requested ID in {(forceDownload ? "GenerateReport" : "ViewPDF")}: {id}"); 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 try
{ {
// Connect to the network path // Connect to the network path
@ -256,8 +371,12 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Error: {ex.Message}"); Console.WriteLine($"Error: {ex}");
return StatusCode(500, $"PDF generation failed: {ex.Message}"); // 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 finally
{ {

View File

@ -2,24 +2,32 @@
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.ComponentModel;
public class NetworkShareAccess : IDisposable public class NetworkShareAccess : IDisposable
{ {
private readonly string _networkPath; private readonly string _networkPath;
private readonly string _username; private readonly string _username;
private readonly string _password; 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) public NetworkShareAccess(string networkPath, string username, string password)
{ {
_networkPath = networkPath; // Validate and normalize the network path
_username = username; _networkPath = networkPath?.Trim().Replace('/', '\\') ?? throw new ArgumentNullException(nameof(networkPath));
_password = password; 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() public void ConnectToNetworkPath()
{ {
if (_isConnected) return;
var netResource = new NetResource var netResource = new NetResource
{ {
Scope = ResourceScope.GlobalNetwork, Scope = ResourceScope.GlobalNetwork,
@ -28,59 +36,78 @@ public class NetworkShareAccess : IDisposable
RemoteName = _networkPath RemoteName = _networkPath
}; };
var result = WNetAddConnection2(netResource, _password, _username, 0);//ERROR HERE int result = WNetAddConnection2(netResource, _password, _username, 0);
if (result != 0) if (result != 0)
{ {
Console.WriteLine($"Error connecting to network path: {result}"); string errorMessage = GetNetworkErrorDescription(result);
throw new IOException($"Error connecting to network path: {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() public void DisconnectFromNetworkShare()
{ {
// Check if the network path is connected if (!_isConnected) return;
var buffer = new StringBuilder(512);
int bufferSize = buffer.Capacity;
int connectionStatus = WNetGetConnection(_networkPath, buffer, ref bufferSize);
if (connectionStatus != 0) try
{ {
Console.WriteLine($"Network path {_networkPath} is not connected. Skipping disconnection."); int result = WNetCancelConnection2(_networkPath, 0, true);
return; // Exit if the connection does not exist
}
// Proceed with disconnection
var result = WNetCancelConnection2(_networkPath, 0, true);
if (result != 0) if (result != 0)
{ {
throw new IOException($"Error disconnecting from network path: {result}"); 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}");
}
finally
{
_isConnected = false;
} }
} }
// Dispose method to ensure proper cleanup
public void Dispose() public void Dispose()
{
try
{ {
DisconnectFromNetworkShare(); DisconnectFromNetworkShare();
} }
catch (Exception ex)
{
Console.WriteLine($"Error during disposal: {ex.Message}");
// Suppress disposal errors
}
}
// P/Invoke for adding a network connection private string GetNetworkErrorDescription(int errorCode)
[DllImport("mpr.dll")] {
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); private static extern int WNetAddConnection2(NetResource netResource, string password, string username, int flags);
// P/Invoke for canceling a network connection [DllImport("mpr.dll", CharSet = CharSet.Unicode)]
[DllImport("mpr.dll")]
private static extern int WNetCancelConnection2(string name, int flags, bool force); private static extern int WNetCancelConnection2(string name, int flags, bool force);
// P/Invoke for checking if a network connection exists [DllImport("mpr.dll", CharSet = CharSet.Unicode)]
[DllImport("mpr.dll")]
private static extern int WNetGetConnection(string localName, StringBuilder remoteName, ref int length); private static extern int WNetGetConnection(string localName, StringBuilder remoteName, ref int length);
// Struct for network resource [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
[StructLayout(LayoutKind.Sequential)]
private class NetResource private class NetResource
{ {
public ResourceScope Scope; public ResourceScope Scope;
@ -93,33 +120,7 @@ public class NetworkShareAccess : IDisposable
public string Provider; public string Provider;
} }
// Enum for resource scope private enum ResourceScope { Connected = 1, GlobalNetwork, Remembered, Recent, Context }
private enum ResourceScope 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 }
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
}
} }

View File

@ -86,7 +86,7 @@ namespace PSTW_CentralSystem.Areas.MMS.Models.PDFGenerator
column.Item() column.Item()
.AlignMiddle() .AlignMiddle()
.AlignCenter() .AlignCenter()
.Image("assets/images/pstw-logo.png") .Image("wwwroot/assets/images/pstw-logo.jpg")
.FitArea(); .FitArea();
}); });

View File

@ -23,7 +23,7 @@ internal class Program
builder.Services.AddControllersWithViews(); builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages(); builder.Services.AddRazorPages();
builder.Services.AddScoped<NetworkShareAccess>(provider => builder.Services.AddScoped<NetworkShareAccess>(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() Log.Logger = new LoggerConfiguration()