| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Email::Outlook::Message::Base; |
|
2
|
|
|
|
|
|
|
=head1 NAME |
|
3
|
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
Email::Outlook::Message::Base - Base parser for .msg files. |
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
This is an internal module of Email::Outlook::Message. |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 METHODS |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=over 8 |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=item B |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=item B |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=item B |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=item B |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=item B |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=back |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 AUTHOR |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
Matijs van Zuijlen, C |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
Copyright 2002--2020 by Matijs van Zuijlen |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
This module is free software; you can redistribute it and/or modify |
|
35
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=cut |
|
38
|
9
|
|
|
9
|
|
68
|
use strict; |
|
|
9
|
|
|
|
|
16
|
|
|
|
9
|
|
|
|
|
259
|
|
|
39
|
9
|
|
|
9
|
|
45
|
use warnings; |
|
|
9
|
|
|
|
|
19
|
|
|
|
9
|
|
|
|
|
187
|
|
|
40
|
9
|
|
|
9
|
|
41
|
use Encode; |
|
|
9
|
|
|
|
|
18
|
|
|
|
9
|
|
|
|
|
603
|
|
|
41
|
9
|
|
|
9
|
|
4101
|
use IO::String; |
|
|
9
|
|
|
|
|
31997
|
|
|
|
9
|
|
|
|
|
311
|
|
|
42
|
9
|
|
|
9
|
|
4883
|
use POSIX; |
|
|
9
|
|
|
|
|
53899
|
|
|
|
9
|
|
|
|
|
84
|
|
|
43
|
9
|
|
|
9
|
|
23807
|
use Carp; |
|
|
9
|
|
|
|
|
30
|
|
|
|
9
|
|
|
|
|
483
|
|
|
44
|
9
|
|
|
9
|
|
6118
|
use OLE::Storage_Lite; |
|
|
9
|
|
|
|
|
168090
|
|
|
|
9
|
|
|
|
|
482
|
|
|
45
|
9
|
|
|
9
|
|
97
|
use vars qw($VERSION); |
|
|
9
|
|
|
|
|
24
|
|
|
|
9
|
|
|
|
|
18626
|
|
|
46
|
|
|
|
|
|
|
$VERSION = "0.921"; |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
my $DIR_TYPE = 1; |
|
49
|
|
|
|
|
|
|
my $FILE_TYPE = 2; |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# Variable encodings |
|
52
|
|
|
|
|
|
|
my $ENCODING_UNICODE = '001F'; |
|
53
|
|
|
|
|
|
|
my $ENCODING_ASCII = '001E'; |
|
54
|
|
|
|
|
|
|
my $ENCODING_BINARY = '0102'; |
|
55
|
|
|
|
|
|
|
my $ENCODING_DIRECTORY = '000D'; |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
our $VARIABLE_ENCODINGS = { |
|
58
|
|
|
|
|
|
|
'000D' => 'Directory', |
|
59
|
|
|
|
|
|
|
'001F' => 'Unicode', |
|
60
|
|
|
|
|
|
|
'001E' => 'Ascii?', |
|
61
|
|
|
|
|
|
|
'0102' => 'Binary', |
|
62
|
|
|
|
|
|
|
}; |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# Fixed encodings |
|
65
|
|
|
|
|
|
|
my $ENCODING_INTEGER16 = '0002'; |
|
66
|
|
|
|
|
|
|
my $ENCODING_INTEGER32 = '0003'; |
|
67
|
|
|
|
|
|
|
my $ENCODING_BOOLEAN = '000B'; |
|
68
|
|
|
|
|
|
|
my $ENCODING_DATE = '0040'; |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# |
|
71
|
|
|
|
|
|
|
# Descriptions partially based on mapitags.h |
|
72
|
|
|
|
|
|
|
# |
|
73
|
|
|
|
|
|
|
our $skipproperties = { |
|
74
|
|
|
|
|
|
|
# Envelope properties |
|
75
|
|
|
|
|
|
|
'0002' => "Alternate Recipient Allowed", |
|
76
|
|
|
|
|
|
|
'000B' => "Conversation Key", |
|
77
|
|
|
|
|
|
|
'0017' => "Importance", #TODO: Use this. |
|
78
|
|
|
|
|
|
|
'001A' => "Message Class", |
|
79
|
|
|
|
|
|
|
'0023' => "Originator Delivery Report Requested", |
|
80
|
|
|
|
|
|
|
'0026' => "Priority", #TODO: Use this. |
|
81
|
|
|
|
|
|
|
'0029' => "Read Receipt Requested", #TODO: Use this. |
|
82
|
|
|
|
|
|
|
'0036' => "Sensitivity", # As assessed by the Sender |
|
83
|
|
|
|
|
|
|
'003B' => "Sent Representing Search Key", |
|
84
|
|
|
|
|
|
|
'003D' => "Subject Prefix", |
|
85
|
|
|
|
|
|
|
'003F' => "Received By EntryId", |
|
86
|
|
|
|
|
|
|
'0040' => "Received By Name", |
|
87
|
|
|
|
|
|
|
# TODO: These two fields are part of the Sender field. |
|
88
|
|
|
|
|
|
|
'0041' => "Sent Representing EntryId", |
|
89
|
|
|
|
|
|
|
'0042' => "Sent Representing Name", |
|
90
|
|
|
|
|
|
|
'0043' => "Received Representing EntryId", |
|
91
|
|
|
|
|
|
|
'0044' => "Received Representing Name", |
|
92
|
|
|
|
|
|
|
'0046' => "Read Receipt EntryId", |
|
93
|
|
|
|
|
|
|
'0051' => "Received By Search Key", |
|
94
|
|
|
|
|
|
|
'0052' => "Received Representing Search Key", |
|
95
|
|
|
|
|
|
|
'0053' => "Read Receipt Search Key", |
|
96
|
|
|
|
|
|
|
# TODO: These two fields are part of the Sender field. |
|
97
|
|
|
|
|
|
|
'0064' => "Sent Representing Address Type", |
|
98
|
|
|
|
|
|
|
'0065' => "Sent Representing Email Address", |
|
99
|
|
|
|
|
|
|
'0070' => "Conversation Topic", |
|
100
|
|
|
|
|
|
|
'0071' => "Conversation Index", |
|
101
|
|
|
|
|
|
|
'0075' => "Received By Address Type", |
|
102
|
|
|
|
|
|
|
'0076' => "Received By Email Address", |
|
103
|
|
|
|
|
|
|
'0077' => "Received Representing Address Type", |
|
104
|
|
|
|
|
|
|
'0078' => "Received Representing Email Address", |
|
105
|
|
|
|
|
|
|
'007F' => "TNEF Correlation Key", |
|
106
|
|
|
|
|
|
|
# Recipient properties |
|
107
|
|
|
|
|
|
|
'0C15' => "Recipient Type", |
|
108
|
|
|
|
|
|
|
# Sender properties |
|
109
|
|
|
|
|
|
|
'0C19' => "Sender Entry Id", |
|
110
|
|
|
|
|
|
|
'0C1D' => "Sender Search Key", |
|
111
|
|
|
|
|
|
|
'0C1E' => "Sender Address Type", |
|
112
|
|
|
|
|
|
|
# Non-transmittable properties |
|
113
|
|
|
|
|
|
|
'0E02' => "Display Bcc", |
|
114
|
|
|
|
|
|
|
'0E06' => "Message Delivery Time", |
|
115
|
|
|
|
|
|
|
'0E07' => "Message Flags", |
|
116
|
|
|
|
|
|
|
'0E0A' => "Sent Mail EntryId", |
|
117
|
|
|
|
|
|
|
'0E0F' => "Responsibility", |
|
118
|
|
|
|
|
|
|
'0E1B' => "Has Attachments", |
|
119
|
|
|
|
|
|
|
'0E1D' => "Normalized Subject", |
|
120
|
|
|
|
|
|
|
'0E1F' => "RTF In Sync", |
|
121
|
|
|
|
|
|
|
'0E20' => "Attachment Size", |
|
122
|
|
|
|
|
|
|
'0E21' => "Attachment Number", |
|
123
|
|
|
|
|
|
|
'0E23' => "Internet Article Number", |
|
124
|
|
|
|
|
|
|
'0E27' => "Security Descriptor", |
|
125
|
|
|
|
|
|
|
'0E79' => "Trust Sender", |
|
126
|
|
|
|
|
|
|
'0FF4' => "Access", |
|
127
|
|
|
|
|
|
|
'0FF6' => "Instance Key", |
|
128
|
|
|
|
|
|
|
'0FF7' => "Access Level", |
|
129
|
|
|
|
|
|
|
'0FF9' => "Record Key", |
|
130
|
|
|
|
|
|
|
'0FFE' => "Object Type", |
|
131
|
|
|
|
|
|
|
'0FFF' => "EntryId", |
|
132
|
|
|
|
|
|
|
# Content properties |
|
133
|
|
|
|
|
|
|
'1006' => "RTF Sync Body CRC", |
|
134
|
|
|
|
|
|
|
'1007' => "RTF Sync Body Count", |
|
135
|
|
|
|
|
|
|
'1008' => "RTF Sync Body Tag", |
|
136
|
|
|
|
|
|
|
'1010' => "RTF Sync Prefix Count", |
|
137
|
|
|
|
|
|
|
'1011' => "RTF Sync Trailing Count", |
|
138
|
|
|
|
|
|
|
'1046' => "Original Message ID", |
|
139
|
|
|
|
|
|
|
'1080' => "Icon Index", |
|
140
|
|
|
|
|
|
|
'1081' => "Last Verb Executed", |
|
141
|
|
|
|
|
|
|
'1082' => "Last Verb Execution Time", |
|
142
|
|
|
|
|
|
|
'10F3' => "URL Component Name", |
|
143
|
|
|
|
|
|
|
'10F4' => "Attribute Hidden", |
|
144
|
|
|
|
|
|
|
'10F5' => "Attribute System", |
|
145
|
|
|
|
|
|
|
'10F6' => "Attribute Read Only", |
|
146
|
|
|
|
|
|
|
# 'Common property' |
|
147
|
|
|
|
|
|
|
'3000' => "Row Id", |
|
148
|
|
|
|
|
|
|
'3001' => "Display Name", |
|
149
|
|
|
|
|
|
|
'3002' => "Address Type", |
|
150
|
|
|
|
|
|
|
'3007' => "Creation Time", |
|
151
|
|
|
|
|
|
|
'3008' => "Last Modification Time", |
|
152
|
|
|
|
|
|
|
'300B' => "Search Key", |
|
153
|
|
|
|
|
|
|
# Message store info |
|
154
|
|
|
|
|
|
|
'340D' => "Store Support Mask", |
|
155
|
|
|
|
|
|
|
'3414' => "Message Store Provider", |
|
156
|
|
|
|
|
|
|
# Attachment properties |
|
157
|
|
|
|
|
|
|
'3702' => "Attachment Encoding", |
|
158
|
|
|
|
|
|
|
'3703' => "Attachment Extension", |
|
159
|
|
|
|
|
|
|
# TODO: Use the following to distinguish between nested msg and other OLE |
|
160
|
|
|
|
|
|
|
# stores. |
|
161
|
|
|
|
|
|
|
'3705' => "Attachment Method", |
|
162
|
|
|
|
|
|
|
'3709' => "Attachment Rendering", # Icon as WMF |
|
163
|
|
|
|
|
|
|
'370A' => "Tag identifying application that supplied the attachment", |
|
164
|
|
|
|
|
|
|
'370B' => "Attachment Rendering Position", |
|
165
|
|
|
|
|
|
|
'3713' => "Attachment Content Location", #TODO: Use this? |
|
166
|
|
|
|
|
|
|
# 3900 -- 39FF: 'Address book' |
|
167
|
|
|
|
|
|
|
'3900' => "Address Book Display Type", |
|
168
|
|
|
|
|
|
|
'39FF' => "Address Book 7 Bit Display Name", |
|
169
|
|
|
|
|
|
|
# Mail User Object |
|
170
|
|
|
|
|
|
|
'3A00' => "Account", |
|
171
|
|
|
|
|
|
|
'3A20' => "Transmittable Display Name", |
|
172
|
|
|
|
|
|
|
'3A40' => "Send Rich Info", |
|
173
|
|
|
|
|
|
|
# 'Display table properties' |
|
174
|
|
|
|
|
|
|
'3FF8' => "Creator Name", |
|
175
|
|
|
|
|
|
|
'3FF9' => "Creator EntryId", |
|
176
|
|
|
|
|
|
|
'3FFA' => "Last Modifier Name", |
|
177
|
|
|
|
|
|
|
'3FFB' => "Last Modifier EntryId", |
|
178
|
|
|
|
|
|
|
'3FFD' => "Message Code Page", |
|
179
|
|
|
|
|
|
|
# 'Transport-defined envelope property' |
|
180
|
|
|
|
|
|
|
'4019' => "Sender Flags", |
|
181
|
|
|
|
|
|
|
'401A' => "Sent Representing Flags", |
|
182
|
|
|
|
|
|
|
'401B' => "Received By Flags", |
|
183
|
|
|
|
|
|
|
'401C' => "Received Representing Flags", |
|
184
|
|
|
|
|
|
|
'4029' => "Read Receipt Address Type", |
|
185
|
|
|
|
|
|
|
'402A' => "Read Receipt Email Address", |
|
186
|
|
|
|
|
|
|
'402B' => "Read Receipt Name", |
|
187
|
|
|
|
|
|
|
'5FF6' => "Recipient Display Name", |
|
188
|
|
|
|
|
|
|
'5FF7' => "Recipient EntryId", |
|
189
|
|
|
|
|
|
|
'5FFD' => "Recipient Flags", |
|
190
|
|
|
|
|
|
|
'5FFF' => "Recipient Track Status", |
|
191
|
|
|
|
|
|
|
# 'Provider-defined internal non-transmittable property' |
|
192
|
|
|
|
|
|
|
'664A' => "Has Named Properties", |
|
193
|
|
|
|
|
|
|
'6740' => "Sent Mail Server EntryId", |
|
194
|
|
|
|
|
|
|
}; |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub new { |
|
197
|
16
|
|
|
16
|
1
|
51
|
my ($class, $pps, $verbose) = @_; |
|
198
|
16
|
|
|
|
|
75
|
my $self = bless { |
|
199
|
|
|
|
|
|
|
_pps_file_entries => {}, |
|
200
|
|
|
|
|
|
|
_pps => $pps |
|
201
|
|
|
|
|
|
|
}, $class; |
|
202
|
16
|
|
|
|
|
90
|
$self->_set_verbosity($verbose); |
|
203
|
16
|
|
|
|
|
93
|
$self->_process_pps($pps); |
|
204
|
16
|
|
|
|
|
51
|
return $self; |
|
205
|
|
|
|
|
|
|
} |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
sub mapi_property_names { |
|
208
|
28
|
|
|
28
|
1
|
54
|
my $self = shift; |
|
209
|
28
|
|
|
|
|
50
|
return keys %{$self->{_pps_file_entries}}; |
|
|
28
|
|
|
|
|
227
|
|
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
sub get_mapi_property { |
|
213
|
215
|
|
|
215
|
1
|
856
|
my ($self, $code) = @_; |
|
214
|
215
|
|
|
|
|
504
|
return $self->{_pps_file_entries}->{$code}; |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub set_mapi_property { |
|
218
|
680
|
|
|
680
|
1
|
1081
|
my ($self, $code, $data) = @_; |
|
219
|
680
|
|
|
|
|
1466
|
$self->{_pps_file_entries}->{$code} = $data; |
|
220
|
680
|
|
|
|
|
976
|
return; |
|
221
|
|
|
|
|
|
|
} |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub property { |
|
224
|
52
|
|
|
52
|
1
|
118
|
my ($self, $name) = @_; |
|
225
|
52
|
|
|
|
|
110
|
my $map = $self->_property_map; |
|
226
|
|
|
|
|
|
|
# TODO: Prepare reverse map instead of doing dumb lookup. |
|
227
|
52
|
|
|
|
|
83
|
foreach my $code (keys %{$map}) { |
|
|
52
|
|
|
|
|
150
|
|
|
228
|
209
|
|
|
|
|
296
|
my $key = $map->{$code}; |
|
229
|
209
|
100
|
|
|
|
371
|
next unless $key eq $name; |
|
230
|
52
|
|
|
|
|
114
|
my $prop = $self->get_mapi_property($code); |
|
231
|
52
|
100
|
|
|
|
130
|
if ($prop) { |
|
232
|
42
|
|
|
|
|
58
|
my ($encoding, $data) = @{$prop}; |
|
|
42
|
|
|
|
|
92
|
|
|
233
|
42
|
|
|
|
|
112
|
return $self->_decode_mapi_property($encoding, $data); |
|
234
|
|
|
|
|
|
|
} else { |
|
235
|
10
|
|
|
|
|
60
|
return; |
|
236
|
|
|
|
|
|
|
} |
|
237
|
|
|
|
|
|
|
} |
|
238
|
0
|
|
|
|
|
0
|
return; |
|
239
|
|
|
|
|
|
|
} |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub _decode_mapi_property { |
|
242
|
204
|
|
|
204
|
|
331
|
my ($self, $encoding, $data) = @_; |
|
243
|
|
|
|
|
|
|
|
|
244
|
204
|
100
|
100
|
|
|
540
|
if ($encoding eq $ENCODING_ASCII or $encoding eq $ENCODING_UNICODE) { |
|
245
|
164
|
100
|
|
|
|
334
|
if ($encoding eq $ENCODING_UNICODE) { |
|
246
|
44
|
|
|
|
|
111
|
$data = decode("UTF-16LE", $data); |
|
247
|
|
|
|
|
|
|
} |
|
248
|
164
|
|
|
|
|
14097
|
$data =~ s/ \000 $ //sgx; |
|
249
|
164
|
|
|
|
|
629
|
return $data; |
|
250
|
|
|
|
|
|
|
} |
|
251
|
|
|
|
|
|
|
|
|
252
|
40
|
100
|
|
|
|
131
|
if ($encoding eq $ENCODING_BINARY) { |
|
253
|
14
|
|
|
|
|
55
|
return $data; |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
|
|
256
|
26
|
100
|
|
|
|
79
|
if ($encoding eq $ENCODING_DATE) { |
|
257
|
16
|
|
|
|
|
68
|
my @a = OLE::Storage_Lite::OLEDate2Local $data; |
|
258
|
16
|
|
|
|
|
374
|
return $self->_format_date(\@a); |
|
259
|
|
|
|
|
|
|
} |
|
260
|
|
|
|
|
|
|
|
|
261
|
10
|
50
|
|
|
|
40
|
if ($encoding eq $ENCODING_INTEGER16) { |
|
262
|
0
|
|
|
|
|
0
|
return unpack("v", substr($data, 0, 2)); |
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
|
|
265
|
10
|
50
|
|
|
|
46
|
if ($encoding eq $ENCODING_INTEGER32) { |
|
266
|
10
|
|
|
|
|
61
|
return unpack("V", substr($data, 0, 4)); |
|
267
|
|
|
|
|
|
|
} |
|
268
|
|
|
|
|
|
|
|
|
269
|
0
|
0
|
|
|
|
0
|
if ($encoding eq $ENCODING_BOOLEAN) { |
|
270
|
0
|
|
|
|
|
0
|
return unpack("C", substr($data, 0, 1)); |
|
271
|
|
|
|
|
|
|
} |
|
272
|
|
|
|
|
|
|
|
|
273
|
0
|
|
|
|
|
0
|
warn "Unhandled encoding $encoding\n"; |
|
274
|
0
|
|
|
|
|
0
|
return $data; |
|
275
|
|
|
|
|
|
|
} |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
sub _process_pps { |
|
278
|
28
|
|
|
28
|
|
71
|
my ($self, $pps) = @_; |
|
279
|
28
|
|
|
|
|
51
|
foreach my $child (@{$pps->{Child}}) { |
|
|
28
|
|
|
|
|
86
|
|
|
280
|
470
|
100
|
|
|
|
1029
|
if ($child->{Type} == $DIR_TYPE) { |
|
|
|
50
|
|
|
|
|
|
|
281
|
28
|
|
|
|
|
97
|
$self->_process_subdirectory($child); |
|
282
|
|
|
|
|
|
|
} elsif ($child->{Type} == $FILE_TYPE) { |
|
283
|
442
|
|
|
|
|
1162
|
$self->_process_pps_file_entry($child); |
|
284
|
|
|
|
|
|
|
} else { |
|
285
|
0
|
|
|
|
|
0
|
carp "Unknown entry type: $child->{Type}"; |
|
286
|
|
|
|
|
|
|
} |
|
287
|
|
|
|
|
|
|
} |
|
288
|
28
|
|
|
|
|
169
|
$self->_check_pps_file_entries($self->_property_map); |
|
289
|
28
|
|
|
|
|
58
|
return; |
|
290
|
|
|
|
|
|
|
} |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
sub _get_pps_name { |
|
293
|
482
|
|
|
482
|
|
664
|
my ($self, $pps) = @_; |
|
294
|
482
|
|
|
|
|
948
|
my $name = OLE::Storage_Lite::Ucs2Asc($pps->{Name}); |
|
295
|
482
|
|
|
|
|
7473
|
$name =~ s/ \W / /gx; |
|
296
|
482
|
|
|
|
|
855
|
return $name; |
|
297
|
|
|
|
|
|
|
} |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
sub _parse_item_name { |
|
300
|
442
|
|
|
442
|
|
757
|
my ($self, $name) = @_; |
|
301
|
|
|
|
|
|
|
|
|
302
|
442
|
100
|
|
|
|
1139
|
if ($name =~ / ^ __substg1 [ ] 0_ (....) (....) $ /x) { |
|
303
|
414
|
|
|
|
|
964
|
my ($property, $encoding) = ($1, $2); |
|
304
|
414
|
|
|
|
|
1022
|
return ($property, $encoding); |
|
305
|
|
|
|
|
|
|
} else { |
|
306
|
28
|
|
|
|
|
61
|
return (undef, undef); |
|
307
|
|
|
|
|
|
|
} |
|
308
|
|
|
|
|
|
|
} |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
sub _warn_about_unknown_directory { |
|
311
|
12
|
|
|
12
|
|
42
|
my ($self, $pps) = @_; |
|
312
|
|
|
|
|
|
|
|
|
313
|
12
|
|
|
|
|
35
|
my $name = $self->_get_pps_name($pps); |
|
314
|
12
|
50
|
|
|
|
49
|
if ($name eq '__nameid_version1 0') { |
|
315
|
|
|
|
|
|
|
# TODO: Use this data to access so-called named properties. |
|
316
|
|
|
|
|
|
|
$self->{VERBOSE} |
|
317
|
12
|
50
|
|
|
|
174
|
and warn "Skipping DIR entry $name (Introductory stuff)\n"; |
|
318
|
|
|
|
|
|
|
} else { |
|
319
|
0
|
|
|
|
|
0
|
warn "Unknown DIR entry $name\n"; |
|
320
|
|
|
|
|
|
|
} |
|
321
|
12
|
|
|
|
|
35
|
return; |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
sub _warn_about_unknown_file { |
|
325
|
0
|
|
|
0
|
|
0
|
my ($self, $pps) = @_; |
|
326
|
|
|
|
|
|
|
|
|
327
|
0
|
|
|
|
|
0
|
my $name = $self->_get_pps_name($pps); |
|
328
|
|
|
|
|
|
|
|
|
329
|
0
|
0
|
|
|
|
0
|
if ($name eq 'Olk10SideProps_0001') { |
|
330
|
|
|
|
|
|
|
$self->{VERBOSE} |
|
331
|
0
|
0
|
|
|
|
0
|
and warn "Skipping FILE entry $name (Properties)\n"; |
|
332
|
|
|
|
|
|
|
} else { |
|
333
|
0
|
|
|
|
|
0
|
warn "Unknown FILE entry $name\n"; |
|
334
|
|
|
|
|
|
|
} |
|
335
|
0
|
|
|
|
|
0
|
return; |
|
336
|
|
|
|
|
|
|
} |
|
337
|
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
# |
|
339
|
|
|
|
|
|
|
# Generic processor for a file entry: Inserts the entry's data into the |
|
340
|
|
|
|
|
|
|
# $self's mapi property list. |
|
341
|
|
|
|
|
|
|
# |
|
342
|
|
|
|
|
|
|
sub _process_pps_file_entry { |
|
343
|
442
|
|
|
442
|
|
821
|
my ($self, $pps) = @_; |
|
344
|
442
|
|
|
|
|
677
|
my $name = $self->_get_pps_name($pps); |
|
345
|
442
|
|
|
|
|
822
|
my ($property, $encoding) = $self->_parse_item_name($name); |
|
346
|
|
|
|
|
|
|
|
|
347
|
442
|
100
|
|
|
|
824
|
if (defined $property) { |
|
|
|
50
|
|
|
|
|
|
|
348
|
414
|
|
|
|
|
1091
|
$self->set_mapi_property($property, [$encoding, $pps->{Data}]); |
|
349
|
|
|
|
|
|
|
} elsif ($name eq '__properties_version1 0') { |
|
350
|
28
|
|
|
|
|
124
|
$self->_process_property_stream ($pps->{Data}); |
|
351
|
|
|
|
|
|
|
} else { |
|
352
|
0
|
|
|
|
|
0
|
$self->_warn_about_unknown_file($pps); |
|
353
|
|
|
|
|
|
|
} |
|
354
|
442
|
|
|
|
|
754
|
return; |
|
355
|
|
|
|
|
|
|
} |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
sub _process_property_stream { |
|
358
|
28
|
|
|
28
|
|
67
|
my ($self, $data) = @_; |
|
359
|
28
|
|
|
|
|
122
|
my ($n, $len) = ($self->_property_stream_header_length, length $data) ; |
|
360
|
|
|
|
|
|
|
|
|
361
|
28
|
|
|
|
|
89
|
while ($n + 16 <= $len) { |
|
362
|
680
|
|
|
|
|
1243
|
my @f = unpack "v4", substr $data, $n, 8; |
|
363
|
|
|
|
|
|
|
|
|
364
|
680
|
|
|
|
|
1287
|
my $encoding = sprintf("%04X", $f[0]); |
|
365
|
|
|
|
|
|
|
|
|
366
|
680
|
100
|
|
|
|
1312
|
unless ($VARIABLE_ENCODINGS->{$encoding}) { |
|
367
|
266
|
|
|
|
|
482
|
my $property = sprintf("%04X", $f[1]); |
|
368
|
266
|
|
|
|
|
425
|
my $propdata = substr $data, $n+8, 8; |
|
369
|
266
|
|
|
|
|
659
|
$self->set_mapi_property($property, [$encoding, $propdata]); |
|
370
|
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
} continue { |
|
372
|
680
|
|
|
|
|
1144
|
$n += 16 ; |
|
373
|
|
|
|
|
|
|
} |
|
374
|
28
|
|
|
|
|
58
|
return; |
|
375
|
|
|
|
|
|
|
} |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
sub _check_pps_file_entries { |
|
378
|
28
|
|
|
28
|
|
63
|
my ($self, $map) = @_; |
|
379
|
|
|
|
|
|
|
|
|
380
|
28
|
|
|
|
|
135
|
foreach my $property ($self->mapi_property_names) { |
|
381
|
680
|
100
|
|
|
|
1206
|
if (my $key = $map->{$property}) { |
|
382
|
162
|
|
|
|
|
327
|
$self->_use_property($key, $property); |
|
383
|
|
|
|
|
|
|
} else { |
|
384
|
518
|
|
|
|
|
849
|
$self->_warn_about_skipped_property($property); |
|
385
|
|
|
|
|
|
|
} |
|
386
|
|
|
|
|
|
|
} |
|
387
|
28
|
|
|
|
|
86
|
return; |
|
388
|
|
|
|
|
|
|
} |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
sub _use_property { |
|
391
|
162
|
|
|
162
|
|
628
|
my ($self, $key, $property) = @_; |
|
392
|
162
|
|
|
|
|
256
|
my ($encoding, $data) = @{$self->get_mapi_property($property)}; |
|
|
162
|
|
|
|
|
313
|
|
|
393
|
162
|
|
|
|
|
328
|
$self->{$key} = $self->_decode_mapi_property($encoding, $data); |
|
394
|
162
|
|
|
|
|
534
|
$self->{"${key}_ENCODING"} = $encoding; |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
$self->{VERBOSE} |
|
397
|
162
|
50
|
|
|
|
339
|
and $self->_log_property("Using ", $property, $key); |
|
398
|
162
|
|
|
|
|
297
|
return; |
|
399
|
|
|
|
|
|
|
} |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
sub _warn_about_skipped_property { |
|
402
|
518
|
|
|
518
|
|
708
|
my ($self, $property) = @_; |
|
403
|
|
|
|
|
|
|
|
|
404
|
518
|
50
|
|
|
|
1002
|
return unless $self->{VERBOSE}; |
|
405
|
|
|
|
|
|
|
|
|
406
|
0
|
|
0
|
|
|
0
|
my $meaning = $skipproperties->{$property} || "UNKNOWN"; |
|
407
|
|
|
|
|
|
|
|
|
408
|
0
|
|
|
|
|
0
|
$self->_log_property("Skipping", $property, $meaning); |
|
409
|
0
|
|
|
|
|
0
|
return; |
|
410
|
|
|
|
|
|
|
} |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
sub _log_property { |
|
413
|
0
|
|
|
0
|
|
0
|
my ($self, $message, $property, $meaning) = @_; |
|
414
|
|
|
|
|
|
|
|
|
415
|
0
|
|
|
|
|
0
|
my ($encoding, $data) = @{$self->get_mapi_property($property)}; |
|
|
0
|
|
|
|
|
0
|
|
|
416
|
0
|
|
|
|
|
0
|
my $value = $self->_decode_mapi_property($encoding, $data); |
|
417
|
0
|
|
|
|
|
0
|
$value = substr($value, 0, 50); |
|
418
|
|
|
|
|
|
|
|
|
419
|
0
|
0
|
|
|
|
0
|
if ($encoding eq $ENCODING_BINARY) { |
|
420
|
0
|
0
|
|
|
|
0
|
if ($value =~ / [[:print:]] /x) { |
|
421
|
0
|
|
|
|
|
0
|
$value =~ s/ [^[:print:]] /./gx; |
|
422
|
|
|
|
|
|
|
} else { |
|
423
|
0
|
|
|
|
|
0
|
$value =~ s/ . / sprintf("%02x ", ord($&)) /sgex; |
|
|
0
|
|
|
|
|
0
|
|
|
424
|
|
|
|
|
|
|
} |
|
425
|
|
|
|
|
|
|
} |
|
426
|
|
|
|
|
|
|
|
|
427
|
0
|
0
|
|
|
|
0
|
if (length($value) > 45) { |
|
428
|
0
|
|
|
|
|
0
|
$value = substr($value, 0, 41) . " ..."; |
|
429
|
|
|
|
|
|
|
} |
|
430
|
|
|
|
|
|
|
|
|
431
|
0
|
|
|
|
|
0
|
warn "$message property $encoding:$property ($meaning): $value\n"; |
|
432
|
0
|
|
|
|
|
0
|
return; |
|
433
|
|
|
|
|
|
|
} |
|
434
|
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
sub _set_verbosity { |
|
436
|
28
|
|
|
28
|
|
67
|
my ($self, $verbosity) = @_; |
|
437
|
28
|
50
|
|
|
|
138
|
$self->{VERBOSE} = $verbosity ? 1 : 0; |
|
438
|
28
|
|
|
|
|
54
|
return; |
|
439
|
|
|
|
|
|
|
} |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
# |
|
442
|
|
|
|
|
|
|
# Format a gmt date according to RFC822 |
|
443
|
|
|
|
|
|
|
# |
|
444
|
|
|
|
|
|
|
sub _format_date { |
|
445
|
31
|
|
|
31
|
|
70
|
my ($self, $datearr) = @_; |
|
446
|
31
|
|
|
|
|
55
|
my $day = qw(Sun Mon Tue Wed Thu Fri Sat)[strftime("%w", @{$datearr})]; |
|
|
31
|
|
|
|
|
1525
|
|
|
447
|
31
|
|
|
|
|
115
|
my $month = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)[strftime("%m", @{$datearr}) - 1]; |
|
|
31
|
|
|
|
|
756
|
|
|
448
|
31
|
|
|
|
|
127
|
return strftime("$day, %d $month %Y %H:%M:%S +0000", @{$datearr}); |
|
|
31
|
|
|
|
|
834
|
|
|
449
|
|
|
|
|
|
|
} |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
1; |