From aa99a3e74a7e9d62539bf1799266fd29cc33b251 Mon Sep 17 00:00:00 2001 From: Ben Clarke Date: Sun, 23 Jul 2023 09:11:35 +0000 Subject: [PATCH] Added capabilities to filter XstExporter based on sender email address, subject, min received time and max received time. --- README.md | 1 + ReleaseNotes.md | 8 ++ XstExporter.md | 19 ++- src/OldXstReader/XstMessageFormatter.cs | 2 +- src/XstExporter/Program.cs | 155 ++++++++++++++++++++++-- src/XstExporter/XstExporter.csproj | 6 +- src/XstReader.Api/XstMessage.cs | 13 +- src/XstReader.Api/XstReader.Api.csproj | 6 +- 8 files changed, 193 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 7c875ec..dfc8260 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ More information in [XstReader.md](./XstReader.md) A Command Line tool for exporting emails, attachments or properties from an Microsoft Outlook's .ost and .pst file: * With the ability to export from a subtree of Outlook folders * Can export attachments only, without the body of the email. +* Can filter what is exported based off either the sender, subject, minimum time or maximum time the email was received (only in the Windows version for now). * Is built over .Net Framework 4.6.1 (for Windows) * There is a *Portable* version based on .Net Core 2.1 (cross-platform) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index abce735..bedeb1e 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -19,6 +19,14 @@ **XstReader.Api v.1.0.2**: * Fixed error with intellisense in nuget package +## 2023-07-23 +**XstEporter v2.0.8: +* Added command line filters for XstExporter to be able to filter what is exported based off either the sender, subject, minimum time or maximum time the email was received. This only works with when exporting with '--email' or '--attachments' set ('--properties' will not work with the filters yes as the properties export is a lot faster and therefore wasn't a requirement just yet). + +**XstReader.Api v1.0.8 +* Fixed issue in GetBodyFormats where for items such as those in the folder "Recipient Cache" it wasn't getting the body format. It will now default to plaintext if no other formats found. +* Added Sender SMTP Address (PidTagSenderSmtpAddress) to the Message properties. + ## 2022-01-26 **All projects**: * New folder structure for code diff --git a/XstExporter.md b/XstExporter.md index 5e4f2ea..e58e1b0 100644 --- a/XstExporter.md +++ b/XstExporter.md @@ -7,8 +7,11 @@ The differences from the export capabilities of the UI are: the ability to expor In addition to XstExporter, XstExporter.Portable is also provided, which is a portable version based on .Net Core 2.1 that can be run on Windows, Mac, Linux etc. -Both versions support the following options: +XstExporter supports the following options: + XstExport.exe {-e|-p|-a|-h} [-f=``] [-se=] [-su=] [-mint=] [-maxt=] [-o] [-s] [-t=``] `` + +XstExporter.Portable support the following options (filtering capabilties to be added in later release): XstExporter.exe {-e|-p|-a|-h} [-f=``] [-o] [-s] [-t=``] `` Where: @@ -37,6 +40,20 @@ Where: -s, --subfolders If set, Outlook subfolder structure is preserved. Otherwise, all output goes to a single directory + + -se, --sender + If set, Outlook will export based on the specified sender. This filter only applies when --email or --attachments is used, it will not filter for --properties. + + -su, --subject + If set, Outlook will export based on the specified subject. This filter only applies when --email or --attachments is used, it will not filter for --properties. + + -mint, --mintime + If set, Outlook will export emails only after the minimum time specified based on the email received time. This filter only applies when --email or --attachments is used, it will not filter for --properties. + For example '2023-07-18 09:00:00' will export emails received after 9 AM on the 18th July 2023. + + -maxt, --maxtime + If set, Outlook will export emails only before the maximum time specified based on the email received time. This filter only applies when --email or --attachments is used, it will not filter for --properties. + For example '2023-07-18 09:00:00 will export emails received before 9 AM on the 18th July 2023. -t=``, --target=`` The directory to which output is written. This may be an diff --git a/src/OldXstReader/XstMessageFormatter.cs b/src/OldXstReader/XstMessageFormatter.cs index 0de7a4e..58c6e8e 100644 --- a/src/OldXstReader/XstMessageFormatter.cs +++ b/src/OldXstReader/XstMessageFormatter.cs @@ -43,7 +43,7 @@ public string ExportFileExtension : "txt"; private string _ExportFileName = null; - public string ExportFileName => _ExportFileName ?? (_ExportFileName = String.Format("{0:yyyy-MM-dd HHmm} {1}", Message?.Date, Message?.Subject).Truncate(150).ReplaceInvalidFileNameChars(" ")); + public string ExportFileName => _ExportFileName ?? String.Format("{0:yyyy-MM-dd HHmm} {1}", Message?.Date, Message?.Subject).Truncate(150).ReplaceInvalidFileNameChars(" "); private XstRecipient OriginatorRecipient => Message.Recipients[RecipientType.Originator].FirstOrDefault(); private IEnumerable ToRecipients => Message.Recipients[RecipientType.To]; diff --git a/src/XstExporter/Program.cs b/src/XstExporter/Program.cs index 16e7c82..791c05c 100644 --- a/src/XstExporter/Program.cs +++ b/src/XstExporter/Program.cs @@ -25,7 +25,7 @@ class Program "", "Usage:", "", - " XstExport.exe {-e|-p|-a|-h} [-f=] [-o] [-s]", + " XstExport.exe {-e|-p|-a|-h} [-f=] [-se=] [-su=] [-mint=] [-maxt=] [-o] [-s]", " [-t=] ", "", "Where:", @@ -54,6 +54,20 @@ class Program " -s, --subfolders", " If set, Outlook subfolder structure is preserved.", " Otherwise, all output goes to a single directory", + "", + " -se, --sender", + " If set, Outlook will export based on the specified sender. This filter only applies when --email or --attachments is used, it will not filter for --properties.", + "", + " -su, --subject", + " If set, Outlook will export based on the specified subject. This filter only applies when --email or --attachments is used, it will not filter for --properties.", + "", + " -mint, --mintime", + " If set, Outlook will export emails only after the minimum time specified. This filter only applies when --email or --attachments is used, it will not filter for --properties.", + " For example '2023-07-18 09:00:00' will export emails received after 9 AM on the 18th July 2023.", + "", + " -maxt, --maxtime", + " If set, Outlook will export emails only before the maximum time specified. This filter only applies when --email or --attachments is used, it will not filter for --properties.", + " For example '2023-07-18 09:00:00 will export emails received before 9 AM on the 18th July 2023.", "", " -t=, --target=", " The directory to which output is written. This may be an", @@ -82,6 +96,10 @@ static int Main(string[] args) bool only = false; bool subfolders = false; string exportDir = null; + string sender = null; + string subject = null; + string mintime = null; + string maxtime = null; try { @@ -94,6 +112,10 @@ static int Main(string[] args) { "o|only", v => only = true }, { "s|subfolders", v => subfolders = true }, { "t|target=", v => exportDir = v }, + { "se|sender=", v => sender = v }, + { "su|subject=", v => subject = v }, + { "mint|mintime=", v => mintime = v }, + { "maxt|maxtime=", v => maxtime = v }, }; List outlookFiles = argParser.Parse(args); @@ -132,6 +154,26 @@ static int Main(string[] args) ErrorCode = WindowsErrorCodes.ERROR_FILE_NOT_FOUND }; } + + DateTime mintime_datetime = new DateTime(); + if (mintime != null && !DateTime.TryParse(mintime, out mintime_datetime)) + { + throw new XstExportException + { + Description = "A valid datetime was not specified. Try specifying a valid date such as '2023-07-18' or datetime such as '2023-07-18 09:00:00'.", + ErrorCode = WindowsErrorCodes.ERROR_INVALID_PARAMETER + }; + } + + DateTime maxtime_datetime = new DateTime(); + if (maxtime != null && !DateTime.TryParse(maxtime, out maxtime_datetime)) + { + throw new XstExportException + { + Description = "A valid datetime was not specified. Try specifying a valid date such as '2023-07-18' or datetime such as '2023-07-18 09:00:00'.", + ErrorCode = WindowsErrorCodes.ERROR_INVALID_PARAMETER + }; + } if (exportDir != null) { @@ -192,7 +234,7 @@ static int Main(string[] args) else targetDir = exportDir; - ExportFolder(f, command, targetDir); + ExportFolder(f, command, targetDir, sender, subject, mintime, maxtime); } } } @@ -218,7 +260,7 @@ static int Main(string[] args) return 0; } - private static void ExportFolder(XstFolder folder, Command command, string exportDir) + private static void ExportFolder(XstFolder folder, Command command, string exportDir, string sender, string subject, string mintime, string maxtime) { if (folder.ContentCount == 0) { @@ -236,13 +278,13 @@ private static void ExportFolder(XstFolder folder, Command command, string expor switch (command) { case Command.Email: - ExtractEmailsInFolder(folder, exportDir); + ExtractEmailsInFolder(folder, exportDir, sender, subject, mintime, maxtime); break; case Command.Properties: ExtractPropertiesInFolder(folder, exportDir); break; case Command.Attachments: - ExtractAttachmentsInFolder(folder, exportDir); + ExtractAttachmentsInFolder(folder, exportDir, sender, subject, mintime, maxtime); break; case Command.Help: default: @@ -322,7 +364,7 @@ private static string RemoveInvalidChars(string filename) return filename.ReplaceInvalidFileNameChars(""); } - private static void ExtractEmailsInFolder(XstFolder folder, string exportDirectory) + private static void ExtractEmailsInFolder(XstFolder folder, string exportDirectory, string sender, string subject, string mintime, string maxtime) { XstMessage current = null; int good = 0, bad = 0; @@ -339,8 +381,57 @@ private static void ExtractEmailsInFolder(XstFolder folder, string exportDirecto current = m; formatter.Message = m; string fileName = formatter.ExportFileName; + + try + { + if (sender != null && formatter.Message.FromSmtpAddress.ToLower() != sender.ToLower()) + { + continue; + } + } + catch + { + continue; + } + + try + { + if (subject != null && !formatter.Message.Subject.ToLower().Contains(subject.ToLower())) + { + continue; + } + } + catch + { + continue; + } + + try + { + if (mintime != null && formatter.Message.ReceivedTime < DateTime.Parse(mintime)) + { + continue; + } + } + catch + { + continue; + } + + try + { + if (maxtime != null && formatter.Message.ReceivedTime > DateTime.Parse(maxtime)) + { + continue; + } + } + catch + { + continue; + } + for (int i = 1; ; i++) - { + { if (!usedNames.Contains(fileName)) { usedNames.Add(fileName); @@ -370,12 +461,60 @@ private static void ExtractPropertiesInFolder(XstFolder folder, string exportDir folder.Messages.SavePropertiesToFile(fileName); } - private static void ExtractAttachmentsInFolder(XstFolder folder, string exportDirectory) + private static void ExtractAttachmentsInFolder(XstFolder folder, string exportDirectory, string sender, string subject, string mintime, string maxtime) { int good = 0, bad = 0; foreach (var message in folder.Messages) { + try + { + if (sender != null && message.FromSmtpAddress.ToLower() != sender.ToLower()) + { + continue; + } + } + catch + { + continue; + } + + try + { + if (subject != null && !message.Subject.ToLower().Contains(subject.ToLower())) + { + continue; + } + } + catch + { + continue; + } + + try + { + if (mintime != null && message.ReceivedTime < DateTime.Parse(mintime)) + { + continue; + } + } + catch + { + continue; + } + + try + { + if (maxtime != null && message.ReceivedTime > DateTime.Parse(maxtime)) + { + continue; + } + } + catch + { + continue; + } + try { foreach (var att in message.Attachments) diff --git a/src/XstExporter/XstExporter.csproj b/src/XstExporter/XstExporter.csproj index c852c4b..3c4e883 100644 --- a/src/XstExporter/XstExporter.csproj +++ b/src/XstExporter/XstExporter.csproj @@ -3,9 +3,9 @@ Exe net48 - 2.0.1 - 2.0.1 - 2.0.1 + 2.0.2 + 2.0.2 + 2.0.2 MS-PL diff --git a/src/XstReader.Api/XstMessage.cs b/src/XstReader.Api/XstMessage.cs index 4b4be59..213e1a3 100644 --- a/src/XstReader.Api/XstMessage.cs +++ b/src/XstReader.Api/XstMessage.cs @@ -107,13 +107,22 @@ public override string DisplayName [Category(@"Message Properties")] [Description(@"Contains a list of the primary recipient display names, separated by semicolons, when an email message has primary recipients .")] public virtual string To => Properties[PropertyCanonicalName.PidTagDisplayTo]?.ValueAsStringSanitized; - /// + + /// /// The From Summary of the Message /// [DisplayName("Sender Name")] [Category(@"Address Properties")] [Description(@"Contains the display name of the sending mailbox owner.")] public virtual string From => Properties[PropertyCanonicalName.PidTagSenderName]?.ValueAsStringSanitized; + + /// + /// The From Summary of the Message + /// + [DisplayName("Sender SMTP Address")] + [Category(@"Address Properties")] + [Description(@"Contains the SMTP address of the sending mailbox owner.")] + public virtual string FromSmtpAddress => Properties[PropertyCanonicalName.PidTagSenderSmtpAddress]?.ValueAsStringSanitized; /// /// Indicates if the Message is sent in representation of other @@ -535,6 +544,8 @@ private List GetBodyFormats() formatList.Add(XstMessageBodyFormat.Rtf); if (IsBodyPlainText) formatList.Add(XstMessageBodyFormat.PlainText); + if (formatList.Count == 0) + formatList.Add(XstMessageBodyFormat.PlainText); return formatList; } diff --git a/src/XstReader.Api/XstReader.Api.csproj b/src/XstReader.Api/XstReader.Api.csproj index 784dc4a..d33f57a 100644 --- a/src/XstReader.Api/XstReader.Api.csproj +++ b/src/XstReader.Api/XstReader.Api.csproj @@ -3,9 +3,9 @@ netstandard2.0 XstReader - 1.0.7 - 1.0.7 - 1.0.7 + 1.0.8 + 1.0.8 + 1.0.8 Properties True iluvadev