Class: MsgExtractor::Mapi::PropertyStore

Inherits:
Object
  • Object
show all
Defined in:
lib/msg_extractor/mapi/property_store.rb

Overview

Reads the MAPI properties of one storage: fixed-width values from the __properties_version1.0 stream, variable-width values from companion __substg1.0_XXXXYYYY streams. The properties stream header length depends on what kind of storage this is.

Constant Summary collapse

HEADER_SIZES =
{ root: 32, embedded: 24, attachment: 8, recipient: 8 }.freeze
SUBSTG_RE =
/\A__SUBSTG1\.0_([0-9A-F]{4})([0-9A-F]{4})\z/
VARIABLE_WIDTH_TYPES =

Variable-width types (PT_UNICODE, PT_STRING8, PT_BINARY, PT_OBJECT, PT_CLSID) must be sourced from a substg stream. If one appears only in rather than decoding garbage.

[
  MsgExtractor::Mapi::PT_UNICODE,
  MsgExtractor::Mapi::PT_STRING8,
  MsgExtractor::Mapi::PT_BINARY,
  MsgExtractor::Mapi::PT_OBJECT,
  MsgExtractor::Mapi::PT_CLSID
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cfbf, storage, kind) ⇒ PropertyStore

Returns a new instance of PropertyStore.



16
17
18
19
20
21
22
23
# File 'lib/msg_extractor/mapi/property_store.rb', line 16

def initialize(cfbf, storage, kind)
  @cfbf = cfbf
  @storage = storage
  @kind = kind
  @records = {} # id => [type, 8-byte value field]
  @streams = {} # id => [type, Cfbf::Entry]
  parse
end

Instance Attribute Details

#attachment_countObject (readonly)

These counts come directly from the file and are untrusted; the model layer iterates actual storages rather than relying on them.



12
13
14
# File 'lib/msg_extractor/mapi/property_store.rb', line 12

def attachment_count
  @attachment_count
end

#recipient_countObject (readonly)

These counts come directly from the file and are untrusted; the model layer iterates actual storages rather than relying on them.



12
13
14
# File 'lib/msg_extractor/mapi/property_store.rb', line 12

def recipient_count
  @recipient_count
end

Instance Method Details

#[](id) ⇒ Object



39
40
41
42
43
44
45
46
47
48
# File 'lib/msg_extractor/mapi/property_store.rb', line 39

def [](id)
  if (type_entry = @streams[id])
    type, entry = type_entry
    Decoders.decode(type, @cfbf.read_stream(entry), codepage: codepage)
  elsif (type_value = @records[id])
    type, value = type_value
    return nil if VARIABLE_WIDTH_TYPES.include?(type)
    Decoders.decode(type, value, codepage: codepage)
  end
end

#codepageObject



61
62
63
64
65
66
67
68
# File 'lib/msg_extractor/mapi/property_store.rb', line 61

def codepage
  @codepage ||=
    if (record = @records[PR_MESSAGE_CODEPAGE])
      record[1].unpack1("l<")
    else
      1252
    end
end

#internet_codepageObject



70
71
72
73
74
75
76
# File 'lib/msg_extractor/mapi/property_store.rb', line 70

def internet_codepage
  if (record = @records[PR_INTERNET_CPID])
    record[1].unpack1("l<")
  else
    codepage
  end
end

#key?(id) ⇒ Boolean

Returns:

  • (Boolean)


25
# File 'lib/msg_extractor/mapi/property_store.rb', line 25

def key?(id) = @streams.key?(id) || @records.key?(id)

#raw(id) ⇒ Object

Raw bytes without decoding (binary props, or the 8-byte record field).



51
52
53
54
55
56
57
# File 'lib/msg_extractor/mapi/property_store.rb', line 51

def raw(id)
  if (type_entry = @streams[id])
    @cfbf.read_stream(type_entry[1])
  elsif (type_value = @records[id])
    type_value[1]
  end
end

#type_of(id) ⇒ Object



59
# File 'lib/msg_extractor/mapi/property_store.rb', line 59

def type_of(id) = (@streams[id] || @records[id])&.first