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