exiftool_vendored 13.31.0 → 13.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/Changes +38 -1
- data/bin/MANIFEST +5 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +47 -46
- data/bin/exiftool +81 -56
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +7 -5
- data/bin/lib/Image/ExifTool/Canon.pm +10 -2
- data/bin/lib/Image/ExifTool/Exif.pm +5 -3
- data/bin/lib/Image/ExifTool/FlashPix.pm +4 -159
- data/bin/lib/Image/ExifTool/FujiFilm.pm +10 -3
- data/bin/lib/Image/ExifTool/Geotag.pm +5 -3
- data/bin/lib/Image/ExifTool/GoPro.pm +14 -2
- data/bin/lib/Image/ExifTool/LNK.pm +4 -1
- data/bin/lib/Image/ExifTool/Lang/cs.pm +0 -1
- data/bin/lib/Image/ExifTool/Lang/de.pm +0 -1
- data/bin/lib/Image/ExifTool/Lang/fr.pm +0 -1
- data/bin/lib/Image/ExifTool/Lang/it.pm +0 -1
- data/bin/lib/Image/ExifTool/Lang/ja.pm +0 -1
- data/bin/lib/Image/ExifTool/Lang/nl.pm +0 -1
- data/bin/lib/Image/ExifTool/Lang/pl.pm +0 -1
- data/bin/lib/Image/ExifTool/Lang/zh_cn.pm +0 -1
- data/bin/lib/Image/ExifTool/Microsoft.pm +158 -1
- data/bin/lib/Image/ExifTool/Minolta.pm +1 -1
- data/bin/lib/Image/ExifTool/Nikon.pm +71 -37
- data/bin/lib/Image/ExifTool/NikonCustom.pm +40 -10
- data/bin/lib/Image/ExifTool/Olympus.pm +240 -35
- data/bin/lib/Image/ExifTool/Panasonic.pm +3 -3
- data/bin/lib/Image/ExifTool/Pentax.pm +271 -53
- data/bin/lib/Image/ExifTool/QuickTime.pm +11 -4
- data/bin/lib/Image/ExifTool/Sony.pm +5 -2
- data/bin/lib/Image/ExifTool/TNEF.pm +487 -0
- data/bin/lib/Image/ExifTool/TagLookup.pm +4373 -4264
- data/bin/lib/Image/ExifTool/TagNames.pod +249 -17
- data/bin/lib/Image/ExifTool/WriteExif.pl +14 -12
- data/bin/lib/Image/ExifTool/Writer.pl +15 -11
- data/bin/lib/Image/ExifTool/XMPStruct.pl +1 -1
- data/bin/lib/Image/ExifTool.pm +11 -4
- data/bin/lib/Image/ExifTool.pod +45 -44
- data/bin/perl-Image-ExifTool.spec +46 -45
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +3 -2
@@ -0,0 +1,487 @@
|
|
1
|
+
#------------------------------------------------------------------------------
|
2
|
+
# File: TNEF.pm
|
3
|
+
#
|
4
|
+
# Description: Read TNEF meta information
|
5
|
+
#
|
6
|
+
# Revisions: 2025-07-08 - P. Harvey Created
|
7
|
+
#
|
8
|
+
# References: 1) https://officeprotocoldoc.z19.web.core.windows.net/files/MS-OXTNEF/%5bMS-OXTNEF%5d.pdf
|
9
|
+
# 2) https://officeprotocoldoc.z19.web.core.windows.net/files/MS-OXCMSG/%5bMS-OXCMSG%5d.pdf
|
10
|
+
# 3) https://msopenspecs.azureedge.net/files/MS-OXPROPS/%5bMS-OXPROPS%5d.pdf
|
11
|
+
# 4) https://officeprotocoldoc.z19.web.core.windows.net/files/MS-OXCDATA/%5bMS-OXCDATA%5d.pdf
|
12
|
+
# 5) https://github.com/echo-devim/pyjacktrick/blob/main/mapi_constants.py
|
13
|
+
#------------------------------------------------------------------------------
|
14
|
+
|
15
|
+
package Image::ExifTool::TNEF;
|
16
|
+
|
17
|
+
use strict;
|
18
|
+
use vars qw($VERSION);
|
19
|
+
use Image::ExifTool qw(:DataAccess :Utils);
|
20
|
+
use Image::ExifTool::ASF;
|
21
|
+
use Image::ExifTool::Microsoft;
|
22
|
+
|
23
|
+
$VERSION = '1.00';
|
24
|
+
|
25
|
+
sub ProcessProps($$$);
|
26
|
+
|
27
|
+
# TNEF property types
|
28
|
+
my %propType = (
|
29
|
+
0x01 => 'null',
|
30
|
+
0x02 => 'int16s',
|
31
|
+
0x03 => 'int32s',
|
32
|
+
0x04 => 'float',
|
33
|
+
0x05 => 'double',
|
34
|
+
0x06 => 'int64s', # (currency / 10000)
|
35
|
+
0x07 => 'double', # (days since Dec 30, 1899)
|
36
|
+
0x0a => 'int32s', # (error code)
|
37
|
+
0x0b => 'int16s', # (boolean)
|
38
|
+
0x0d => 'undef', # (object)
|
39
|
+
0x14 => 'int64s',
|
40
|
+
0x1e => 'string', # (with terminating null)
|
41
|
+
0x1f => 'Unicode',# (with terminating null)
|
42
|
+
0x40 => 'int64u', # (time in 100 ns since 1601)
|
43
|
+
0x48 => 'GUID', # (16 bytes)
|
44
|
+
0x102 => 'undef', # (blob)
|
45
|
+
);
|
46
|
+
|
47
|
+
# byte count for non-integer fixed-size formats
|
48
|
+
my %fmtSize = (
|
49
|
+
null => 0,
|
50
|
+
float => 4,
|
51
|
+
double => 8,
|
52
|
+
GUID => 16,
|
53
|
+
);
|
54
|
+
|
55
|
+
my %dateInfo = (
|
56
|
+
Format => 'date',
|
57
|
+
Groups => { 2 => 'Time' },
|
58
|
+
PrintConv => '$self->ConvertDateTime($val)',
|
59
|
+
);
|
60
|
+
|
61
|
+
%Image::ExifTool::TNEF::Main = (
|
62
|
+
GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Other' },
|
63
|
+
VARS => { NO_LOOKUP => 1 },
|
64
|
+
NOTES => q{
|
65
|
+
Information extracted from Transport Neutral Encapsulation Format (TNEF)
|
66
|
+
files (eg. winmail.dat). But note that the exiftool application doesn't
|
67
|
+
process files with a .DAT extension by default when a directory name is
|
68
|
+
given, so in this case either specify the .DAT file(s) by name or add
|
69
|
+
C<-ext+ dat> to the command.
|
70
|
+
},
|
71
|
+
0x069007 => {
|
72
|
+
Name => 'CodePage',
|
73
|
+
Format => 'int32u',
|
74
|
+
SeparateTable => 'Microsoft CodePage',
|
75
|
+
# (ignore secondary code page)
|
76
|
+
RawConv => '$val=~s/ .*//;$$self{Charset} = $charsetName{"cp$val"}; $val',
|
77
|
+
PrintConv => \%Image::ExifTool::Microsoft::codePage,
|
78
|
+
},
|
79
|
+
0x089006 => {
|
80
|
+
Name => 'TNEFVersion',
|
81
|
+
Format => 'int8u',
|
82
|
+
ValueConv => 'my @a = reverse split " ", $val; "@a"',
|
83
|
+
PrintConv => '$val =~ tr/ /./; $val',
|
84
|
+
},
|
85
|
+
0x078008 => 'MessageClass',
|
86
|
+
0x008000 => 'From',
|
87
|
+
0x018004 => 'Subject',
|
88
|
+
0x038005 => { Name => 'SentDate', %dateInfo },
|
89
|
+
0x038006 => { Name => 'ReceivedDate', %dateInfo },
|
90
|
+
0x068007 => 'MessageStatus',
|
91
|
+
0x018009 => 'MessageID',
|
92
|
+
0x02800C => 'MessageBody',
|
93
|
+
0x04800D => {
|
94
|
+
Name => 'Priority',
|
95
|
+
Format => 'int16u', # (contrary to documentation which says int32u)
|
96
|
+
PrintConv => {
|
97
|
+
0 => 'Low',
|
98
|
+
1 => 'Normal',
|
99
|
+
2 => 'High',
|
100
|
+
},
|
101
|
+
},
|
102
|
+
0x038020 => { Name => 'MessageModifyDate', %dateInfo }, # (unclear what this really means)
|
103
|
+
0x069003 => {
|
104
|
+
Name => 'MessageProps',
|
105
|
+
SubDirectory => { TagTable => 'Image::ExifTool::TNEF::MsgProps' },
|
106
|
+
},
|
107
|
+
0x069004 => 'RecipientTable',
|
108
|
+
0x070600 => 'OriginalMessageClass',
|
109
|
+
0x060000 => 'Owner',
|
110
|
+
0x060001 => 'SentFor',
|
111
|
+
0x060002 => 'Delegate',
|
112
|
+
0x030006 => { Name => 'StartDate', %dateInfo },
|
113
|
+
0x030007 => { Name => 'EndDate', %dateInfo },
|
114
|
+
0x050008 => 'OwnerAppointmentID',
|
115
|
+
0x040009 => 'ResponseRequested',
|
116
|
+
0x06800F => { Name => 'AttachData', Binary => 1 },
|
117
|
+
0x018010 => 'AttachTitle',
|
118
|
+
0x068011 => { Name => 'AttachMetaFile', Binary => 1 },
|
119
|
+
0x038012 => { Name => 'AttachCreateDate', %dateInfo },
|
120
|
+
0x038013 => { Name => 'AttachModifyDate', %dateInfo },
|
121
|
+
0x069001 => 'AttachTransportFilename',
|
122
|
+
0x069002 => { Name => 'AttachRenderingData', Binary => 1 }, # (start of attachment)
|
123
|
+
0x069005 => { # (end of attachment)
|
124
|
+
Name => 'AttachInfo',
|
125
|
+
SubDirectory => { TagTable => 'Image::ExifTool::TNEF::AttachInfo' },
|
126
|
+
},
|
127
|
+
);
|
128
|
+
|
129
|
+
%Image::ExifTool::TNEF::MsgProps = (
|
130
|
+
GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Other' },
|
131
|
+
PROCESS_PROC => \&ProcessProps,
|
132
|
+
TAG_PREFIX => 'MsgProps',
|
133
|
+
VARS => { LONG_TAGS => 0, NO_LOOKUP => 1 }, # (suppress "long tags" warning in BuildTagLookup)
|
134
|
+
0x0002 => 'AlternateRecipientAllowed',
|
135
|
+
0x0039 => { Name => 'ClientSubmitTime', %dateInfo },
|
136
|
+
0x0040 => 'ReceivedByName',
|
137
|
+
0x0044 => 'ReceivedRepresentingName',
|
138
|
+
0x004d => { Name => 'OriginalAuthorName', Groups => { 2 => 'Author' } },
|
139
|
+
0x0055 => { Name => 'OriginalDeliveryTime', %dateInfo },
|
140
|
+
0x0070 => 'Subject',
|
141
|
+
0x0075 => 'ReceivedByAddressType',
|
142
|
+
0x0076 => 'ReceivedByEmailAddress',
|
143
|
+
0x0077 => 'ReceivedRepresentingAddressType',
|
144
|
+
0x0078 => 'ReceivedRepresentingEmailAddress',
|
145
|
+
0x007f => { Name => 'CorrelationKey', RawConv => '$$val' },
|
146
|
+
0x0c1a => 'SenderName',
|
147
|
+
0x0c1d => { Name => 'SenderSearchKey', RawConv => 'ref $val ? $$val : $val' },
|
148
|
+
0x0e06 => { Name => 'MessageDeliveryTime', %dateInfo },
|
149
|
+
0x0e1d => 'NormalizedSubject',
|
150
|
+
0x0e28 => 'PrimarySendAccount',
|
151
|
+
0x0e29 => 'NextSendAccount',
|
152
|
+
0x0f02 => { Name => 'DeliveryOrRenewTime', %dateInfo }, #5
|
153
|
+
0x1000 => { Name => 'MessageBodyText', Binary => 1 },
|
154
|
+
0x1007 => 'SyncBodyCount',
|
155
|
+
0x1008 => 'SyncBodyData',
|
156
|
+
0x1009 => {
|
157
|
+
Name => 'MessageBodyRTF',
|
158
|
+
Notes => 'RTF message body, decompressed if necessary',
|
159
|
+
RawConv => '$$val', # (ValueConv won't convert a scalar ref, so convert to scalar here)
|
160
|
+
ValueConv => 'my $dat = Image::ExifTool::TNEF::DecompressRTF($self,$val); \$dat',
|
161
|
+
},
|
162
|
+
0x1013 => { Name => 'MessageBodyHTML', Binary => 1 },
|
163
|
+
0x1035 => 'InternetMessageID',
|
164
|
+
0x10f4 => 'Hidden',
|
165
|
+
0x10f6 => 'ReadOnly',
|
166
|
+
0x3007 => { Name => 'CreateDate', %dateInfo },
|
167
|
+
0x3008 => { Name => 'ModifyDate', %dateInfo },
|
168
|
+
0x3fde => 'InternetCodePage',
|
169
|
+
0x3ff1 => 'LocalUserID',
|
170
|
+
0x3ff8 => { Name => 'CreatorName', Groups => { 2 => 'Author' } },
|
171
|
+
0x3ffa => 'LastModifierName',
|
172
|
+
0x3ffd => 'MessageCodePage',
|
173
|
+
0x4076 => { Name => 'SpamConfidenceLevel' },
|
174
|
+
# named properties that look interesting
|
175
|
+
'00020329_Author' => {
|
176
|
+
Name => 'Author',
|
177
|
+
Groups => { 2 => 'Author' },
|
178
|
+
Notes => q{
|
179
|
+
tag ID's for named properties are constructed from the property namespace
|
180
|
+
GUID with the ending "-0000-0000-C000-000000000046" removed, followed by the
|
181
|
+
string or numerical ID in hex, separated by an underscore
|
182
|
+
},
|
183
|
+
}, # (NC)
|
184
|
+
'00020329_LastAuthor' => { Name => 'LastAuthor', Groups => { 2 => 'Author' } }, # (NC)
|
185
|
+
'00062004_0000801A' => 'HomeAddress', # (NC)
|
186
|
+
'00062004_000080DA' => 'HomeAddressCountryCode', # (NC)
|
187
|
+
'00062008_00008554' => 'AppVersion',
|
188
|
+
);
|
189
|
+
|
190
|
+
# ref https://pkg.go.dev/github.com/axigenmessaging/tnef#section-readme
|
191
|
+
%Image::ExifTool::TNEF::AttachInfo = (
|
192
|
+
GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Other' },
|
193
|
+
PROCESS_PROC => \&ProcessProps,
|
194
|
+
TAG_PREFIX => 'Attach',
|
195
|
+
0x0e20 => 'AttachSize',
|
196
|
+
0x0e21 => 'AttachNum',
|
197
|
+
0x0ff8 => { Name => 'MappingSignature', Unknown => 1 },
|
198
|
+
0x3001 => 'AttachFileName',
|
199
|
+
0x3703 => 'AttachFileExtension',
|
200
|
+
0x3701 => 'AttachBinary',
|
201
|
+
0x3705 => {
|
202
|
+
Name => 'AttachMethod',
|
203
|
+
PrintConv => {
|
204
|
+
0 => 'Attachment Created',
|
205
|
+
1 => 'AttachData', # (contrary to documentation which says the AttachBinary tag)
|
206
|
+
2 => 'AttachLongPathName (recipients with access)',
|
207
|
+
4 => 'AttachLongPathName',
|
208
|
+
5 => 'Embedded Message',
|
209
|
+
6 => 'AttachBinary (object)',
|
210
|
+
7 => 'AttachLongPathName (using AttachmentProviderType)',
|
211
|
+
},
|
212
|
+
},
|
213
|
+
0x3707 => 'AttachLongFileName',
|
214
|
+
0x3708 => 'AttachPathName',
|
215
|
+
0x370d => 'AttachLongPathName',
|
216
|
+
0x370e => 'AttachMIMEType',
|
217
|
+
0x7ffb => {
|
218
|
+
Name => 'ExceptionStartTime',
|
219
|
+
%dateInfo,
|
220
|
+
Unknown => 1, # (because these values don't make sense in my samples)
|
221
|
+
},
|
222
|
+
0x7ffc => { Name => 'ExceptionEndTime', Unknown => 1, %dateInfo },
|
223
|
+
);
|
224
|
+
|
225
|
+
#------------------------------------------------------------------------------
|
226
|
+
# Decompress RTF text (ref https://metacpan.org/pod/Mail::Exchange::Message)
|
227
|
+
# Inputs: 0) ExifTool ref, 1) compressed RTF
|
228
|
+
# Returns: Decompressed RTF or '' on error
|
229
|
+
sub DecompressRTF($$)
|
230
|
+
{
|
231
|
+
my ($et, $cdat) = @_;
|
232
|
+
return '' unless length $cdat > 16;
|
233
|
+
my $comp = unpack('x8V', $cdat);
|
234
|
+
|
235
|
+
if ($comp == 0x414c454D) {
|
236
|
+
return substr($cdat, 16);
|
237
|
+
} elsif ($comp != 0x75465a4c) {
|
238
|
+
$et->Warn(sprintf('Unknown RTF compression 0x%x', $comp));
|
239
|
+
return '';
|
240
|
+
}
|
241
|
+
my $dict = '{\rtf1\ansi\mac\deff0\deftab720{\fonttbl;}'.
|
242
|
+
'{\f0\fnil \froman \fswiss \fmodern '.
|
243
|
+
'\fscript \fdecor MS Sans SerifSymbolArialTimes'.
|
244
|
+
' New RomanCourier{\colortbl\red0\green0\blue0'.
|
245
|
+
"\r\n".'\par \pard\plain\f0\fs20\b\i\u\tab\tx';
|
246
|
+
my $cpos = 16;
|
247
|
+
my $clen = length $cdat;
|
248
|
+
my $dpos = length $dict;
|
249
|
+
my $rtnVal = '';
|
250
|
+
while ($cpos < $clen) {
|
251
|
+
my $control = unpack('C', substr($cdat, $cpos++, 1));
|
252
|
+
my ($i, $j);
|
253
|
+
for ($i=0; $i<8 && $cpos<$clen; ++$i) {
|
254
|
+
if ($control & (1<<$i)) {
|
255
|
+
return $rtnVal if $cpos + 2 > $clen;
|
256
|
+
my $ref = unpack('n', substr($cdat, $cpos, 2));
|
257
|
+
$cpos += 2;
|
258
|
+
my $off = $ref >> 4;
|
259
|
+
my $len = ($ref & 0x0f) + 2;
|
260
|
+
return $rtnVal if $off == $dpos % 4096 or $off % 4096 >= length($dict);
|
261
|
+
for ($j=0; $j<$len; ++$j) {
|
262
|
+
my $ch = substr($dict, ($off++ % 4096), 1);
|
263
|
+
substr($dict, ($dpos++ % 4096), 1) = $ch;
|
264
|
+
$rtnVal .= $ch;
|
265
|
+
}
|
266
|
+
} else {
|
267
|
+
my $ch = substr($cdat, $cpos++, 1);
|
268
|
+
substr($dict, ($dpos++ % 4096), 1) = $ch;
|
269
|
+
$rtnVal .= $ch;
|
270
|
+
}
|
271
|
+
}
|
272
|
+
}
|
273
|
+
return $rtnVal;
|
274
|
+
}
|
275
|
+
|
276
|
+
#------------------------------------------------------------------------------
|
277
|
+
# Process TNEF message properties
|
278
|
+
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
279
|
+
# Returns: 1 on success
|
280
|
+
sub ProcessProps($$$)
|
281
|
+
{
|
282
|
+
my ($et, $dirInfo, $tagTbl) = @_;
|
283
|
+
my $dataPt = $$dirInfo{DataPt};
|
284
|
+
my $dataPos = $$dirInfo{DataPos};
|
285
|
+
my $dirLen = length $$dataPt;
|
286
|
+
return 0 unless $dirLen > 4;
|
287
|
+
my $entries = unpack('V', $$dataPt);
|
288
|
+
$et->VerboseDir('TNEF Properties', $entries);
|
289
|
+
my $pos = 4;
|
290
|
+
my $i;
|
291
|
+
for ($i=0; $i<$entries; ++$i) {
|
292
|
+
last if $pos + 4 > $dirLen;
|
293
|
+
my $type = Get16u($dataPt, $pos);
|
294
|
+
my $tag = Get16u($dataPt, $pos+2);
|
295
|
+
$pos += 4;
|
296
|
+
# handle named properties (bit 0x8000 set)
|
297
|
+
if ($tag & 0x8000) {
|
298
|
+
last if $pos + 24 > $dirLen;
|
299
|
+
my $uid = Image::ExifTool::ASF::GetGUID(substr($$dataPt, $pos, 16));
|
300
|
+
$uid =~ s/-0000-0000-C000-000000000046$//; # remove common suffix
|
301
|
+
my $idtype = Get32u($dataPt, $pos + 16);
|
302
|
+
my $num = Get32u($dataPt, $pos + 20);
|
303
|
+
$pos += 24;
|
304
|
+
if ($idtype == 0) { # number
|
305
|
+
$tag = $uid . sprintf('_%.8x', $num);
|
306
|
+
} elsif ($idtype == 1) { # string
|
307
|
+
last if $pos + $num > $dirLen or $num < 2;
|
308
|
+
# decode string (ignoring null terminator)
|
309
|
+
my $name = $et->Decode(substr($$dataPt, $pos, $num-2), 'UTF16');
|
310
|
+
$tag = "${uid}_$name";
|
311
|
+
AddTagToTable($tagTbl, $tag, {
|
312
|
+
Name => Image::ExifTool::MakeTagName($name)
|
313
|
+
}) unless $$tagTbl{$tag};
|
314
|
+
$pos += ($num + 3) & 0xfffffffc; # (padded to an even 4 bytes)
|
315
|
+
} else {
|
316
|
+
last; # error
|
317
|
+
}
|
318
|
+
}
|
319
|
+
my $count = 1;
|
320
|
+
my ($multi, $fmt);
|
321
|
+
if ($type & 0x1000) {
|
322
|
+
$multi = 1;
|
323
|
+
$type &= 0x0fff;
|
324
|
+
last if $pos + 4 > $dirLen;
|
325
|
+
$count = Get32u($dataPt, $pos);
|
326
|
+
$pos += 4;
|
327
|
+
}
|
328
|
+
$fmt = $propType{$type} or last;
|
329
|
+
while ($count) {
|
330
|
+
my $size = $fmtSize{$fmt};
|
331
|
+
my $val;
|
332
|
+
unless ($size) {
|
333
|
+
if ($fmt =~ /(\d+)/) {
|
334
|
+
$size = $count * $1 / 8;
|
335
|
+
} elsif ($fmt eq 'null') {
|
336
|
+
$val = ''; # ($size is already 0)
|
337
|
+
} else {
|
338
|
+
# skip 1 count for "special case" stupidity
|
339
|
+
$pos += 4 unless $multi;
|
340
|
+
last if $pos + 4 > $dirLen;
|
341
|
+
$size = Get32u($dataPt, $pos);
|
342
|
+
$pos += 4;
|
343
|
+
last if $pos + $size > $dirLen;
|
344
|
+
$val = substr($$dataPt, $pos, $size);
|
345
|
+
}
|
346
|
+
}
|
347
|
+
if (not defined $val) {
|
348
|
+
$val = ReadValue($dataPt, $pos, $fmt, $count, $size);
|
349
|
+
if ($type == 0x06 or $type == 0x07 or $type == 0x0b or $type == 0x40) {
|
350
|
+
my @a = split ' ', $val;
|
351
|
+
if ($type == 0x06) { # currency
|
352
|
+
$_ = $_ / 10000 foreach @a;
|
353
|
+
} elsif ($type == 0x07) { # OLE date
|
354
|
+
# convert time from days since Dec 30, 1899
|
355
|
+
foreach (@a) {
|
356
|
+
$_ = ($_ - 25569) * 24 * 3600 if $_ != 0;
|
357
|
+
$_ = Image::ExifTool::ConvertUnixTime($_);
|
358
|
+
}
|
359
|
+
} elsif ($type == 0x0b) { # boolean
|
360
|
+
$_ = $_ ? 'True' : 'False' foreach @a;
|
361
|
+
} elsif ($type == 0x40) { # SYSTIME
|
362
|
+
# convert time from 100-ns intervals since Jan 1, 1601
|
363
|
+
$_ = Image::ExifTool::ConvertUnixTime($_/1e7-11644473600,1) foreach @a;
|
364
|
+
}
|
365
|
+
$val = @a > 1 ? \@a : $a[0];
|
366
|
+
}
|
367
|
+
$count = 1; # (read them all already)
|
368
|
+
} elsif ($fmt eq 'GUID') {
|
369
|
+
$val = Image::ExifTool::ASF::GetGUID($val);
|
370
|
+
} elsif ($fmt eq 'Unicode') {
|
371
|
+
($val = $et->Decode($val, 'UTF16')) =~ s/\0+$//;
|
372
|
+
} elsif ($fmt eq 'string') {
|
373
|
+
$val =~ s/\0+$//;
|
374
|
+
# convert from specified code page if supported
|
375
|
+
$val = $et->Decode($val, $$et{Charset}) if $$et{Charset};
|
376
|
+
} elsif ($fmt eq 'undef' and length $val) {
|
377
|
+
my $copy = $val;
|
378
|
+
$val = \$copy; # change to a binary data reference
|
379
|
+
}
|
380
|
+
$et->HandleTag($tagTbl, $tag, $val,
|
381
|
+
DataPt => $dataPt,
|
382
|
+
DataPos => $dataPos,
|
383
|
+
Start => $pos,
|
384
|
+
Size => $size,
|
385
|
+
Format => sprintf('%s, type 0x%.2x', $fmt, $type),
|
386
|
+
Index => $i,
|
387
|
+
);
|
388
|
+
$pos += ($size + 3) & 0xfffffffc;
|
389
|
+
--$count;
|
390
|
+
}
|
391
|
+
}
|
392
|
+
$et->Warn('Error parsing message properties') unless $i == $entries;
|
393
|
+
return 1;
|
394
|
+
}
|
395
|
+
|
396
|
+
#------------------------------------------------------------------------------
|
397
|
+
# Extract EXIF information from a TNEF image
|
398
|
+
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
|
399
|
+
# Returns: 1 on success, 0 if this wasn't a valid TNEF file
|
400
|
+
sub ProcessTNEF($$)
|
401
|
+
{
|
402
|
+
my ($et, $dirInfo) = @_;
|
403
|
+
my $raf = $$dirInfo{RAF};
|
404
|
+
my ($buff, $tagTablePtr);
|
405
|
+
|
406
|
+
# verify this is a valid TNEF file (read TNEFHeader and TNEFVersion)
|
407
|
+
return 0 unless $raf->Read($buff, 0x15) == 0x15 and $raf->Seek(6, 0);
|
408
|
+
return 0 unless $buff =~ /^\x78\x9f\x3e\x22..\x01\x06\x90\x08\0/s;
|
409
|
+
$et->SetFileType('TNEF');
|
410
|
+
SetByteOrder('II');
|
411
|
+
my $tagTbl = GetTagTable('Image::ExifTool::TNEF::Main');
|
412
|
+
# read through the attributes
|
413
|
+
while ($raf->Read($buff, 9) == 9) {
|
414
|
+
# (ignore the attrLevel byte: 1 for message, 2 for attachment)
|
415
|
+
my ($tag, $len) = unpack('x1VV', $buff);
|
416
|
+
# increment document number for each attachment
|
417
|
+
$$et{DOC_NUM} = ++$$et{DOC_COUNT} if $tag == 0x069002;
|
418
|
+
$raf->Read($buff, $len) == $len or last;
|
419
|
+
my $tagInfo = $$tagTbl{$tag};
|
420
|
+
my ($val, $fmt);
|
421
|
+
if ($tagInfo and $$tagInfo{Format}) {
|
422
|
+
$fmt = $$tagInfo{Format};
|
423
|
+
if ($fmt eq 'date' and length($buff) >= 12) {
|
424
|
+
my @date = unpack('v6', $buff);
|
425
|
+
$val = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2d', @date);
|
426
|
+
}
|
427
|
+
} else {
|
428
|
+
$val = $buff;
|
429
|
+
}
|
430
|
+
$et->HandleTag($tagTbl, $tag, $val,
|
431
|
+
DataPt => \$buff,
|
432
|
+
DataPos => $raf->Tell() - $len,
|
433
|
+
Format => $fmt,
|
434
|
+
);
|
435
|
+
delete $$et{DOC_NUM} if $tag == 0x069005; # end of attachment
|
436
|
+
$raf->Seek(2, 1); # skip checksum
|
437
|
+
}
|
438
|
+
delete $$et{DOC_NUM};
|
439
|
+
return 1;
|
440
|
+
}
|
441
|
+
|
442
|
+
1; # end
|
443
|
+
|
444
|
+
__END__
|
445
|
+
|
446
|
+
=head1 NAME
|
447
|
+
|
448
|
+
Image::ExifTool::TNEF - Read TNEF meta information
|
449
|
+
|
450
|
+
=head1 SYNOPSIS
|
451
|
+
|
452
|
+
This module is used by Image::ExifTool
|
453
|
+
|
454
|
+
=head1 DESCRIPTION
|
455
|
+
|
456
|
+
This module contains definitions required by Image::ExifTool to read TNEF
|
457
|
+
(Transport Neutral Encapsulation Format) files.
|
458
|
+
|
459
|
+
=head1 AUTHOR
|
460
|
+
|
461
|
+
Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
|
462
|
+
|
463
|
+
This library is free software; you can redistribute it and/or modify it
|
464
|
+
under the same terms as Perl itself.
|
465
|
+
|
466
|
+
=head1 REFERENCES
|
467
|
+
|
468
|
+
=over 4
|
469
|
+
|
470
|
+
=item L<https://officeprotocoldoc.z19.web.core.windows.net/files/MS-OXTNEF/%5bMS-OXTNEF%5d.pdf>
|
471
|
+
|
472
|
+
=item L<https://officeprotocoldoc.z19.web.core.windows.net/files/MS-OXCMSG/%5bMS-OXCMSG%5d.pdf>
|
473
|
+
|
474
|
+
=item L<https://msopenspecs.azureedge.net/files/MS-OXPROPS/%5bMS-OXPROPS%5d.pdf>
|
475
|
+
|
476
|
+
=item L<https://officeprotocoldoc.z19.web.core.windows.net/files/MS-OXCDATA/%5bMS-OXCDATA%5d.pdf>
|
477
|
+
|
478
|
+
=item L<https://github.com/echo-devim/pyjacktrick/blob/main/mapi_constants.py>
|
479
|
+
|
480
|
+
=back
|
481
|
+
|
482
|
+
=head1 SEE ALSO
|
483
|
+
|
484
|
+
L<Image::ExifTool::TagNames/TNEF Tags>,
|
485
|
+
L<Image::ExifTool(3pm)|Image::ExifTool>
|
486
|
+
|
487
|
+
=cut
|