**Services Refactored:**
- DevExpressPdfProcessingService: Remove unnecessary try-catch (lines 80-87), add stream position validation
- WindreamDmsService: Mark as [Obsolete] - application now only provides email sending functionality
- MailKitEmailService: Keep MailKit implementation (Limilabs DLL to be added separately)
**Custom Exceptions Added:**
- AuthenticationFailedException: OAuth2/IMAP/SMTP authentication failures
- DmsNotAvailableException: windream COM unavailable
- InvalidPdfException: Invalid PDF stream
- NotFoundException: Entity not found in Repository operations
**Legacy Cleanup:**
- Remove legacy VB.NET projects from solution (EmailProfiler.Common, EmailProfiler.Service)
- Delete legacy/ folder reference
- Clean solution file structure
**Stream Validation:**
- All PDF processing methods now validate stream position (reset to 0 if needed)
- Add CanSeek validation for stream-based operations
**Build Status:** ✅ Successful (0 errors, 15 warnings - all acceptable)
96 lines
3.2 KiB
C#
96 lines
3.2 KiB
C#
using DevExpress.Pdf;
|
|
using DigitalData.EmailProfiler.Application.Common.Interfaces;
|
|
using DigitalData.EmailProfiler.Domain.Exceptions;
|
|
|
|
namespace DigitalData.EmailProfiler.Infrastructure.Services;
|
|
|
|
/// <summary>
|
|
/// PDF processing service using DevExpress.Pdf.
|
|
/// Implements PDF validation and embedded file extraction using streams.
|
|
/// </summary>
|
|
public class DevExpressPdfProcessingService : IPdfProcessingService
|
|
{
|
|
public Task<bool> ValidatePdfAsync(Stream pdfStream, CancellationToken cancellationToken = default)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(pdfStream);
|
|
|
|
if (!pdfStream.CanRead)
|
|
throw new ArgumentException("Stream must be readable.", nameof(pdfStream));
|
|
|
|
if (!pdfStream.CanSeek)
|
|
throw new ArgumentException("Stream must be seekable.", nameof(pdfStream));
|
|
|
|
if (pdfStream.Position != 0)
|
|
pdfStream.Position = 0;
|
|
|
|
using var processor = new PdfDocumentProcessor();
|
|
processor.LoadDocument(pdfStream);
|
|
|
|
return Task.FromResult(true);
|
|
}
|
|
|
|
public async Task<IEnumerable<string>> ExtractEmbeddedFilesAsync(
|
|
Stream pdfStream,
|
|
string outputDirectory,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(pdfStream);
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(outputDirectory);
|
|
|
|
if (!pdfStream.CanRead)
|
|
throw new ArgumentException("Stream must be readable.", nameof(pdfStream));
|
|
|
|
if (!pdfStream.CanSeek)
|
|
throw new ArgumentException("Stream must be seekable.", nameof(pdfStream));
|
|
|
|
if (pdfStream.Position != 0)
|
|
pdfStream.Position = 0;
|
|
|
|
if (!Directory.Exists(outputDirectory))
|
|
Directory.CreateDirectory(outputDirectory);
|
|
|
|
using var processor = new PdfDocumentProcessor();
|
|
processor.LoadDocument(pdfStream);
|
|
|
|
var extractedFiles = new List<string>();
|
|
var attachments = processor.Document.FileAttachments;
|
|
|
|
if (attachments == null || !attachments.Any())
|
|
return extractedFiles;
|
|
|
|
foreach (var attachment in attachments)
|
|
{
|
|
var fileName = attachment.FileName ?? $"attachment_{Guid.NewGuid()}.dat";
|
|
var outputPath = Path.Combine(outputDirectory, fileName);
|
|
|
|
var fileData = attachment.Data;
|
|
if (fileData == null || fileData.Length == 0)
|
|
continue;
|
|
|
|
await File.WriteAllBytesAsync(outputPath, fileData, cancellationToken);
|
|
extractedFiles.Add(outputPath);
|
|
}
|
|
|
|
return extractedFiles;
|
|
}
|
|
|
|
public Task<int> GetPageCountAsync(Stream pdfStream, CancellationToken cancellationToken = default)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(pdfStream);
|
|
|
|
if (!pdfStream.CanRead)
|
|
throw new ArgumentException("Stream must be readable.", nameof(pdfStream));
|
|
|
|
if (!pdfStream.CanSeek)
|
|
throw new ArgumentException("Stream must be seekable.", nameof(pdfStream));
|
|
|
|
if (pdfStream.Position != 0)
|
|
pdfStream.Position = 0;
|
|
|
|
using var processor = new PdfDocumentProcessor();
|
|
processor.LoadDocument(pdfStream);
|
|
|
|
return Task.FromResult(processor.Document.Pages.Count);
|
|
}
|
|
}
|