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 _mailIdSet = new HashSet(); private HashSet _pdfHashSet = new HashSet(); 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(File.ReadAllLines(fn).Where(l => (l.Trim().Length != 0))); fn = Path.Combine(OutputDirectory, ".pdf.ids"); if (File.Exists(fn)) _pdfHashSet = new HashSet(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(File.ReadAllText(credentialsFile.FullName))!; MailPdfExtractApplication app = new MailPdfExtractApplication(outputDirectory, credentials); if (maxDocuments != 0) app.MaxItemsPerRun = maxDocuments; app.Start(); } } }