Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
8 changes: 8 additions & 0 deletions ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 18 additions & 1 deletion XstExporter.md
Original file line number Diff line number Diff line change
Expand Up @@ -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=`<Outlook folder>`] [-se=<Sender>] [-su=<Subject>] [-mint=<Minimum time>] [-maxt=<Maximum time>] [-o] [-s] [-t=`<target directory>`] `<Outlook file name>`

XstExporter.Portable support the following options (filtering capabilties to be added in later release):
XstExporter.exe {-e|-p|-a|-h} [-f=`<Outlook folder>`] [-o] [-s] [-t=`<target directory>`] `<Outlook file name>`

Where:
Expand Down Expand Up @@ -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 directory name>`, --target=`<target directory name>`
The directory to which output is written. This may be an
Expand Down
2 changes: 1 addition & 1 deletion src/OldXstReader/XstMessageFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<XstRecipient> ToRecipients => Message.Recipients[RecipientType.To];
Expand Down
155 changes: 147 additions & 8 deletions src/XstExporter/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Program
"",
"Usage:",
"",
" XstExport.exe {-e|-p|-a|-h} [-f=<Outlook folder>] [-o] [-s]",
" XstExport.exe {-e|-p|-a|-h} [-f=<Outlook folder>] [-se=<Sender>] [-su=<Subject>] [-mint=<Minimum time>] [-maxt=<Maximum time>] [-o] [-s]",
" [-t=<target directory>] <Outlook file name>",
"",
"Where:",
Expand Down Expand Up @@ -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 directory name>, --target=<target directory name>",
" The directory to which output is written. This may be an",
Expand Down Expand Up @@ -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
{
Expand All @@ -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<string> outlookFiles = argParser.Parse(args);

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -192,7 +234,7 @@ static int Main(string[] args)
else
targetDir = exportDir;

ExportFolder(f, command, targetDir);
ExportFolder(f, command, targetDir, sender, subject, mintime, maxtime);
}
}
}
Expand All @@ -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)
{
Expand All @@ -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:
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions src/XstExporter/XstExporter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
<Version>2.0.1</Version>
<AssemblyVersion>2.0.1</AssemblyVersion>
<FileVersion>2.0.1</FileVersion>
<Version>2.0.2</Version>
<AssemblyVersion>2.0.2</AssemblyVersion>
<FileVersion>2.0.2</FileVersion>
<PackageLicenseExpression>MS-PL</PackageLicenseExpression>
</PropertyGroup>

Expand Down
13 changes: 12 additions & 1 deletion src/XstReader.Api/XstMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
/// <summary>

/// <summary>
/// The From Summary of the Message
/// </summary>
[DisplayName("Sender Name")]
[Category(@"Address Properties")]
[Description(@"Contains the display name of the sending mailbox owner.")]
public virtual string From => Properties[PropertyCanonicalName.PidTagSenderName]?.ValueAsStringSanitized;

/// <summary>
/// The From Summary of the Message
/// </summary>
[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;

/// <summary>
/// Indicates if the Message is sent in representation of other
Expand Down Expand Up @@ -535,6 +544,8 @@ private List<XstMessageBodyFormat> GetBodyFormats()
formatList.Add(XstMessageBodyFormat.Rtf);
if (IsBodyPlainText)
formatList.Add(XstMessageBodyFormat.PlainText);
if (formatList.Count == 0)
formatList.Add(XstMessageBodyFormat.PlainText);

return formatList;
}
Expand Down
6 changes: 3 additions & 3 deletions src/XstReader.Api/XstReader.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>XstReader</RootNamespace>
<Version>1.0.7</Version>
<AssemblyVersion>1.0.7</AssemblyVersion>
<FileVersion>1.0.7</FileVersion>
<Version>1.0.8</Version>
<AssemblyVersion>1.0.8</AssemblyVersion>
<FileVersion>1.0.8</FileVersion>
<AppDesignerFolder>Properties</AppDesignerFolder>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Authors>iluvadev</Authors>
Expand Down