Initial Commit
commit
117cfbc3f3
|
@ -0,0 +1,5 @@
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
/packages/
|
||||||
|
riderModule.iml
|
||||||
|
/_ReSharper.Caches/
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Rider ignored files
|
||||||
|
/modules.xml
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
/contentModel.xml
|
||||||
|
/.idea.ln.tools.mailpdfextract.iml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.tools.mailpdfextract", "ln.tools.mailpdfextract\ln.tools.mailpdfextract.csproj", "{BDBB4B03-EF53-4019-9695-48968CAFA6CF}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{BDBB4B03-EF53-4019-9695-48968CAFA6CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{BDBB4B03-EF53-4019-9695-48968CAFA6CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{BDBB4B03-EF53-4019-9695-48968CAFA6CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{BDBB4B03-EF53-4019-9695-48968CAFA6CF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
|
@ -0,0 +1,57 @@
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
|
namespace ln.tools.mailpdfextract;
|
||||||
|
|
||||||
|
public class HashingStream : Stream, IDisposable
|
||||||
|
{
|
||||||
|
public Stream BaseStream { get; }
|
||||||
|
|
||||||
|
SHA256 _sha256 = SHA256.Create();
|
||||||
|
|
||||||
|
public byte[] Hash
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
return _sha256.Hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashingStream(Stream baseStream)
|
||||||
|
{
|
||||||
|
BaseStream = baseStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Flush() => BaseStream.Flush();
|
||||||
|
public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException();
|
||||||
|
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
|
||||||
|
public override void SetLength(long value) => throw new NotSupportedException();
|
||||||
|
|
||||||
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
_sha256.TransformBlock(buffer, offset, count, null, 0);
|
||||||
|
BaseStream.Write(buffer, offset, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanRead => false;
|
||||||
|
public override bool CanSeek => false;
|
||||||
|
public override bool CanWrite => true;
|
||||||
|
public override long Length => BaseStream.Length;
|
||||||
|
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get => BaseStream.Position;
|
||||||
|
set => throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (BaseStream is Stream)
|
||||||
|
{
|
||||||
|
_sha256.TransformFinalBlock(null, 0, 0);
|
||||||
|
BaseStream?.Dispose();
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace ln.tools.mailpdfextract;
|
||||||
|
|
||||||
|
public class ImapCredentials
|
||||||
|
{
|
||||||
|
public string Hostname { get; set; } = "localhost";
|
||||||
|
public int Port { get; set; } = 143;
|
||||||
|
|
||||||
|
public string User { get; set; } = "nobody";
|
||||||
|
public string Password { get; set; } = "";
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using MailKit;
|
||||||
|
using MailKit.Net.Imap;
|
||||||
|
using MailKit.Security;
|
||||||
|
using MimeKit;
|
||||||
|
using UglyToad.PdfPig;
|
||||||
|
|
||||||
|
namespace ln.tools.mailpdfextract
|
||||||
|
{
|
||||||
|
|
||||||
|
public class MailPdfExtractApplication
|
||||||
|
{
|
||||||
|
public string OutputDirectory { get; set; }
|
||||||
|
|
||||||
|
public int MaxItemsPerRun { get; set; } = 1000;
|
||||||
|
|
||||||
|
private ImapCredentials _credentials;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public MailPdfExtractApplication(string outputDirectory, ImapCredentials credentials)
|
||||||
|
{
|
||||||
|
_credentials = credentials;
|
||||||
|
OutputDirectory = outputDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashSet<string> _mailIdSet = new HashSet<string>();
|
||||||
|
private HashSet<string> _pdfHashSet = new HashSet<string>();
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
|
||||||
|
ImapClient imapClient = new ImapClient();
|
||||||
|
imapClient.Connect(_credentials.Hostname, _credentials.Port, SecureSocketOptions.StartTls);
|
||||||
|
imapClient.Authenticate(_credentials.User, _credentials.Password);
|
||||||
|
|
||||||
|
int countItems = 0;
|
||||||
|
|
||||||
|
var inbox = imapClient.Inbox;
|
||||||
|
inbox.Open(FolderAccess.ReadOnly);
|
||||||
|
|
||||||
|
for (int n = 0; n < inbox.Count; n++)
|
||||||
|
{
|
||||||
|
var message = inbox.GetMessage(n);
|
||||||
|
if (!_mailIdSet.Contains(message.MessageId))
|
||||||
|
{
|
||||||
|
_mailIdSet.Add(message.MessageId);
|
||||||
|
Console.WriteLine($"Message: {message.Sender:32} | {message.Subject}");
|
||||||
|
|
||||||
|
foreach (var attachment in message.Attachments)
|
||||||
|
{
|
||||||
|
if (attachment.ContentType.MimeType.Equals("application/pdf"))
|
||||||
|
{
|
||||||
|
if (attachment.ContentDisposition.FileName is string pdfFileName)
|
||||||
|
{
|
||||||
|
string pdfFullFileName = Path.Combine(OutputDirectory, pdfFileName);
|
||||||
|
using (FileStream f = File.Create(pdfFullFileName))
|
||||||
|
{
|
||||||
|
((MimePart)attachment).Content.DecodeTo(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
File.SetCreationTime(pdfFullFileName, message.Date.Date);
|
||||||
|
File.SetLastWriteTime(pdfFullFileName, message.Date.Date);
|
||||||
|
|
||||||
|
PdfDocument document = PdfDocument.Open(pdfFullFileName);
|
||||||
|
if (document.Advanced.TryGetEmbeddedFiles(out var embeddedFiles))
|
||||||
|
{
|
||||||
|
foreach (var embeddedFile in embeddedFiles)
|
||||||
|
{
|
||||||
|
Console.WriteLine(" ***** EMBEDDED ***** {0}",embeddedFile.Name);
|
||||||
|
if (embeddedFile.Name.Equals("factur-x.xml"))
|
||||||
|
{
|
||||||
|
string xfacturFileName = Path.Combine(
|
||||||
|
OutputDirectory,
|
||||||
|
Path.GetFileNameWithoutExtension(pdfFileName) + ".factur-x.xml"
|
||||||
|
);
|
||||||
|
using (FileStream fs = new FileStream(xfacturFileName, FileMode.Create))
|
||||||
|
fs.Write(embeddedFile.Bytes.ToArray());
|
||||||
|
|
||||||
|
File.SetCreationTime(xfacturFileName, message.Date.Date);
|
||||||
|
File.SetLastWriteTime(xfacturFileName, message.Date.Date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
countItems++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (countItems >= MaxItemsPerRun)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
imapClient.Disconnect(true);
|
||||||
|
SaveIndeces();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
var fn = Path.Combine(OutputDirectory, ".mail.ids");
|
||||||
|
if (File.Exists(fn))
|
||||||
|
_mailIdSet = new HashSet<string>(File.ReadAllLines(fn).Where(l => (l.Trim().Length != 0)));
|
||||||
|
|
||||||
|
fn = Path.Combine(OutputDirectory, ".pdf.ids");
|
||||||
|
if (File.Exists(fn))
|
||||||
|
_pdfHashSet = new HashSet<string>(File.ReadAllLines(fn).Where(l => (l.Trim().Length != 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveIndeces()
|
||||||
|
{
|
||||||
|
var fn = Path.Combine(OutputDirectory, ".mail.ids");
|
||||||
|
File.WriteAllLines(fn, _mailIdSet.ToArray());
|
||||||
|
fn = Path.Combine(OutputDirectory, ".pdf.ids");
|
||||||
|
File.WriteAllLines(fn, _pdfHashSet.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void Main(string outputDirectory = ".", FileInfo credentialsFile = null, int maxDocuments = 0)
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(outputDirectory))
|
||||||
|
Directory.CreateDirectory(outputDirectory);
|
||||||
|
|
||||||
|
if (credentialsFile is null)
|
||||||
|
{
|
||||||
|
string fn = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),"mailpdfextract.json");
|
||||||
|
if (File.Exists(fn))
|
||||||
|
credentialsFile = new FileInfo(fn);
|
||||||
|
else
|
||||||
|
throw new FileNotFoundException("no credential file found! " + fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImapCredentials credentials = JsonSerializer.Deserialize<ImapCredentials>(File.ReadAllText(credentialsFile.FullName))!;
|
||||||
|
|
||||||
|
MailPdfExtractApplication app = new MailPdfExtractApplication(outputDirectory, credentials);
|
||||||
|
|
||||||
|
if (maxDocuments != 0)
|
||||||
|
app.MaxItemsPerRun = maxDocuments;
|
||||||
|
|
||||||
|
app.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="MailKit" Version="4.3.0" />
|
||||||
|
<PackageReference Include="PdfPig" Version="0.1.9-alpha-20240128-f886e" />
|
||||||
|
<PackageReference Include="System.CommandLine.DragonFruit" Version="0.4.0-alpha.22272.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
Loading…
Reference in New Issue