mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-12 08:52:32 +00:00
The [MS-PST] reader of MailCraft crates/pst at a44a450, moved as it is: the node database, heap, property and table contexts, folders, messages, recipients, attachments, named properties, recurrence blobs and list streams, with its limits, salvage and tests. Left in MailCraft for now: the compressed-RTF and RTF text reading and the code-page name table, which are not part of the .pst format. Co-authored-by: Martin Ball <98639962+benandez@users.noreply.github.com>
3.3 KiB
3.3 KiB
craft-pst
A reader of Outlook personal folders files (.pst), written from Microsoft's published
specification, [MS-PST], and nothing else. No dependencies, no unsafe.
Pst::open(reader)/Pst::open_with(reader, Limits)read over anyRead + Seek. The file is never held in memory: a page or a block is read when it is needed.- ANSI and Unicode files, with data blocks unencoded, permuted or cyclic-encoded. A file with a
password answers only after
Pst::unlock. - The layers are the specification's: the node database (B-trees of nodes and blocks, data
trees, subnode trees), the heap on a node, its B-tree, property contexts and table contexts,
and the messaging layer:
store,walk_folders,items,associated_items,message(properties, recipients, attachments, attached messages),attachment_reader,read_propertyfor large values, andnamed_properties. recurreads the appointment recurrence pattern and time zone blobs of [MS-OXOCAL];list_streamreads the entry stream of a distribution list.- A damaged file is salvaged: what cannot be read is left out and named (
Pst::damage,Message::left_out).
use craft_pst::Pst;
let mut pst = Pst::open(std::fs::File::open(path)?)?;
for entry in pst.walk_folders()?.folders {
for item in pst.items(entry.folder.id)?.items {
let message = pst.message(&craft_pst::MessageRef::item(item.id))?;
}
}
Hostile input
Nothing panics on any bytes. Every offset and length is checked against the file, every CRC,
signature and block id against what it guards; trees are bounded in depth and read each part
once. The caps of Limits are checked before anything is read or allocated; past a cap a part
is left out and named (Error::Cap).
| Limit | Default |
|---|---|
max_file_size |
100 GiB |
max_items |
2,000,000 |
max_folders / max_folder_depth |
10,000 / 64 |
max_item_bytes |
256 MiB |
max_attachment_bytes |
150 MiB |
max_recipients / max_attachments |
1,000 / 1,000 |
max_embedded_depth / max_embedded_messages |
8 / 10,000 |
max_btree_pages |
4,000,000 |
max_subnodes |
200,000 |
max_inline_value |
4 MiB (larger values are PropValue::Large, read on their own) |
max_listing_bytes |
1 GiB per folder walk or item listing |
max_summary_value |
64 KiB |
read_multiple / read_floor |
all reads together: 64 times the file size plus 256 MiB |
What is not here
- Writing. The
test-writerfeature has a writer of small synthetic files, for tests only. - Offline folders files with 4 KB pages (version 36) and files protected by Windows Information
Protection (version 37):
Error::UnsupportedVersion. - Text in a code page:
PropValue::String8carries the bytes, and the caller decodes them in the code page the message names. - Compressed RTF bodies (
PidTagRtfCompressed, [MS-OXRTFCP]): the property's bytes are returned as they are. - What an item means (mail, contact, appointment): that mapping is the caller's.
Origin
MailCraft crates/pst at
a44a450,
by Martin Ball and the MailCraft contributors (MIT OR Apache-2.0), with its tests. Left behind:
the RTF reading (rtf.rs) and the code-page name table, which are not part of the .pst format.