exiftool_vendored 13.30.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 +57 -1
- data/bin/MANIFEST +5 -0
- data/bin/META.json +4 -3
- data/bin/META.yml +3 -2
- data/bin/README +47 -46
- data/bin/exiftool +88 -60
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +7 -5
- data/bin/lib/Image/ExifTool/Canon.pm +16 -5
- data/bin/lib/Image/ExifTool/Exif.pm +7 -4
- data/bin/lib/Image/ExifTool/FlashPix.pm +4 -159
- data/bin/lib/Image/ExifTool/FujiFilm.pm +13 -3
- data/bin/lib/Image/ExifTool/Geotag.pm +5 -3
- data/bin/lib/Image/ExifTool/GoPro.pm +23 -4
- data/bin/lib/Image/ExifTool/LNK.pm +24 -3
- data/bin/lib/Image/ExifTool/Lang/cs.pm +0 -1
- data/bin/lib/Image/ExifTool/Lang/de.pm +2 -2
- data/bin/lib/Image/ExifTool/Lang/fr.pm +2 -2
- 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/LigoGPS.pm +14 -6
- 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 +80 -39
- data/bin/lib/Image/ExifTool/NikonCustom.pm +40 -10
- data/bin/lib/Image/ExifTool/Olympus.pm +240 -35
- data/bin/lib/Image/ExifTool/PDF.pm +1 -0
- data/bin/lib/Image/ExifTool/Panasonic.pm +4 -4
- data/bin/lib/Image/ExifTool/Parrot.pm +1 -1
- data/bin/lib/Image/ExifTool/Pentax.pm +276 -56
- data/bin/lib/Image/ExifTool/Plot.pm +2 -3
- data/bin/lib/Image/ExifTool/QuickTime.pm +13 -5
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +41 -17
- data/bin/lib/Image/ExifTool/Sigma.pm +19 -1
- 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 +4374 -4262
- data/bin/lib/Image/ExifTool/TagNames.pod +259 -22
- data/bin/lib/Image/ExifTool/WriteExif.pl +14 -12
- data/bin/lib/Image/ExifTool/WritePDF.pl +1 -0
- data/bin/lib/Image/ExifTool/Writer.pl +142 -139
- data/bin/lib/Image/ExifTool/XMPStruct.pl +1 -1
- data/bin/lib/Image/ExifTool.pm +16 -7
- 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
@@ -111,7 +111,7 @@ my %insvLimit = (
|
|
111
111
|
The tags below are extracted from timed metadata in QuickTime and other
|
112
112
|
formats of video files when the ExtractEmbedded option is used. Although
|
113
113
|
most of these tags are combined into the single table below, ExifTool
|
114
|
-
currently reads
|
114
|
+
currently reads 110 different types of timed GPS metadata from video files.
|
115
115
|
},
|
116
116
|
GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
|
117
117
|
GPSLongitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")' },
|
@@ -210,8 +210,8 @@ my %insvLimit = (
|
|
210
210
|
ProcessProc => \&ProcessFMAS,
|
211
211
|
},
|
212
212
|
},{
|
213
|
-
Name => 'gpmd_Wolfbox', # Wolfbox G900 Dashcam
|
214
|
-
Condition => '$$valPt =~ /^.{136}0{16}HYTH/s',
|
213
|
+
Name => 'gpmd_Wolfbox', # Wolfbox G900 Dashcam and Redtiger F9 4K
|
214
|
+
Condition => '$$valPt =~ /^.{136}0{16}(HYTH|XXXX)/s',
|
215
215
|
SubDirectory => {
|
216
216
|
TagTable => 'Image::ExifTool::QuickTime::Stream',
|
217
217
|
ProcessProc => \&ProcessWolfbox,
|
@@ -317,6 +317,8 @@ my %insvLimit = (
|
|
317
317
|
ByteOrder => 'Little-Endian',
|
318
318
|
},
|
319
319
|
}],
|
320
|
+
# (have also seen unknown mett from Google Pixel with MetaType 'application/meta'
|
321
|
+
# and 'application/microvideo-image-meta')
|
320
322
|
mett => { # Parrot drones and iPhone/Android using ARCore
|
321
323
|
Name => 'mett',
|
322
324
|
SubDirectory => { TagTable => 'Image::ExifTool::Parrot::mett' },
|
@@ -2288,6 +2290,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2288
2290
|
$lon = ($lon - 2199.19873715495) / 2;
|
2289
2291
|
$ddd = 1;
|
2290
2292
|
} elsif (Get32u($dataPt,0) == 0x400000 and abs($lat) <= 90 and abs($lon) <= 180) {
|
2293
|
+
$debug and $et->FoundTag(GPSType => '17c');
|
2291
2294
|
# Transcend Drive Body Camera 70
|
2292
2295
|
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 4c 00 00 00 [..@.freeGPS L...]
|
2293
2296
|
# 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
@@ -3567,14 +3570,14 @@ sub ProcessFMAS($$$)
|
|
3567
3570
|
}
|
3568
3571
|
|
3569
3572
|
#------------------------------------------------------------------------------
|
3570
|
-
# Process GPS from Wolfbox G900 Dashcam
|
3573
|
+
# Process GPS from Wolfbox G900 Dashcam and Redtiger F9 4K
|
3571
3574
|
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
3572
3575
|
# Returns: 1 on success
|
3573
3576
|
sub ProcessWolfbox($$$)
|
3574
3577
|
{
|
3575
3578
|
my ($et, $dirInfo, $tagTbl) = @_;
|
3576
3579
|
my $dataPt = $$dirInfo{DataPt};
|
3577
|
-
return 0 if length($$dataPt) <
|
3580
|
+
return 0 if length($$dataPt) < 0xf8;
|
3578
3581
|
$et->VerboseDir('Wolfbox', undef, length($$dataPt));
|
3579
3582
|
# 0000: 65 00 00 00 00 00 00 00 31 01 01 00 e3 ff 00 00 [e.......1.......]
|
3580
3583
|
# 0010: 04 00 00 00 10 00 00 00 2a 00 00 00 00 00 00 00 [........*.......]
|
@@ -3593,22 +3596,43 @@ sub ProcessWolfbox($$$)
|
|
3593
3596
|
# 00e0: 0a 00 00 00 00 00 00 00 e8 03 00 00 00 00 00 00 [................]
|
3594
3597
|
# 00f0: 0a 00 00 00 00 00 00 00 4d 00 00 00 00 00 00 00 [........M.......]
|
3595
3598
|
# lat/lon at 0xb0/0xc0 and 0x128/0x138
|
3596
|
-
# h/m/s at 0x10 and 0xa0 and 0x148 (the first imprinted on the video,
|
3599
|
+
# h/m/s at 0x10 and 0xa0 and 0x148 (the first imprinted on the video, and
|
3600
|
+
# the latter 2 presumed UTC, but there is a 1 second offset for the Redtiger)
|
3597
3601
|
# spd at 0x48, dir at 0x58, alt at 0xe8
|
3602
|
+
# Redtiger F9 4K Dual Front and Rear Mini Dash Cam
|
3603
|
+
# 0000: 01 00 00 00 00 00 00 00 f4 ff 5d fe 24 00 00 00 [..........].$...]
|
3604
|
+
# 0010: 10 00 00 00 2d 00 00 00 25 00 00 00 00 00 00 00 [....-...%.......]
|
3605
|
+
# 0020: 01 00 00 00 00 00 00 00 44 eb 8f 00 00 00 00 00 [........D.......]
|
3606
|
+
# 0030: 10 27 00 00 00 00 00 00 1b 94 8a 04 00 00 00 00 [.'..............]
|
3607
|
+
# 0040: 10 27 00 00 00 00 00 00 8c 69 00 00 00 00 00 00 [.'.......i......]
|
3608
|
+
# 0050: e8 03 00 00 00 00 00 00 ba 47 00 00 00 00 00 00 [.........G......]
|
3609
|
+
# 0060: 64 00 00 00 00 00 00 00 19 00 00 00 05 00 00 00 [d...............]
|
3610
|
+
# 0070: e9 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
3611
|
+
# 0080: 00 00 00 00 00 00 00 00 30 30 30 30 30 30 30 30 [........00000000]
|
3612
|
+
# 0090: 30 30 30 30 30 30 30 30 58 58 58 58 00 00 00 00 [00000000XXXX....]
|
3613
|
+
# 00a0: 08 00 00 00 2d 00 00 00 24 00 00 00 00 00 00 00 [....-...$.......]
|
3614
|
+
# 00b0: 90 eb 8f 00 00 00 00 00 10 27 00 00 00 00 00 00 [.........'......]
|
3615
|
+
# 00c0: 20 94 8a 04 00 00 00 00 10 27 00 00 00 00 00 00 [ ........'......]
|
3616
|
+
# 00d0: 01 00 00 00 11 00 00 00 40 00 00 00 00 00 00 00 [........@.......]
|
3617
|
+
# 00e0: 64 00 00 00 00 00 00 00 8a 00 00 00 00 00 00 00 [d...............]
|
3618
|
+
# 00f0: 0a 00 00 00 00 00 00 00 4d 00 00 00 00 00 00 00 [........M.......]
|
3598
3619
|
SetByteOrder('II');
|
3599
|
-
my ($
|
3600
|
-
# offset 0xa0 also stores hh mm ss, but is out by 8 hours!
|
3620
|
+
my ($d,$mo,$yr,$h,$m,$s) = unpack('x104V3x44V3',$$dataPt);
|
3601
3621
|
my $time = sprintf '%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ', $yr, $mo, $d, $h, $m, $s;
|
3602
|
-
my ($
|
3603
|
-
|
3604
|
-
|
3622
|
+
my ($pos, @a);
|
3623
|
+
# 0=spd 1=dir 2=lat 3=lon 4=alt
|
3624
|
+
foreach $pos (0x48, 0x58, 0xb0, 0xc0, 0xe8) {
|
3625
|
+
my $val = Get64s($dataPt, $pos);
|
3626
|
+
my $scl = Get64s($dataPt, $pos + 8);
|
3627
|
+
push @a, $val / ($scl || 1);
|
3628
|
+
}
|
3629
|
+
ConvertLatLon($a[2], $a[3]);
|
3605
3630
|
$et->HandleTag($tagTbl, GPSDateTime => $time);
|
3606
|
-
$et->HandleTag($tagTbl, GPSLatitude => $
|
3607
|
-
$et->HandleTag($tagTbl, GPSLongitude => $
|
3608
|
-
$et->HandleTag($tagTbl, GPSSpeed => $
|
3609
|
-
$et->HandleTag($tagTbl, GPSTrack => $
|
3610
|
-
$et->HandleTag($tagTbl, GPSAltitude => $
|
3611
|
-
SetByteOrder('MM');
|
3631
|
+
$et->HandleTag($tagTbl, GPSLatitude => $a[2]);
|
3632
|
+
$et->HandleTag($tagTbl, GPSLongitude => $a[3]);
|
3633
|
+
$et->HandleTag($tagTbl, GPSSpeed => $a[0] * $knotsToKph);
|
3634
|
+
$et->HandleTag($tagTbl, GPSTrack => $a[1]);
|
3635
|
+
$et->HandleTag($tagTbl, GPSAltitude => $a[4]);
|
3612
3636
|
return 1;
|
3613
3637
|
}
|
3614
3638
|
|
@@ -19,7 +19,7 @@ use strict;
|
|
19
19
|
use vars qw($VERSION %sigmaLensTypes);
|
20
20
|
use Image::ExifTool::Exif;
|
21
21
|
|
22
|
-
$VERSION = '1.
|
22
|
+
$VERSION = '1.36';
|
23
23
|
|
24
24
|
# sigma LensType lookup (ref IB)
|
25
25
|
%sigmaLensTypes = (
|
@@ -704,6 +704,12 @@ $VERSION = '1.35';
|
|
704
704
|
Name => 'PictureMode',
|
705
705
|
Notes => 'same as ColorMode, but "Standard" when ColorMode is Sepia or B&W',
|
706
706
|
},
|
707
|
+
0x0047 => { #forum17338
|
708
|
+
Name => 'ExposureCompensation',
|
709
|
+
Writable => 'rational64s',
|
710
|
+
PrintConv => '$val and $val =~ s/^(\d)/\+$1/; $val',
|
711
|
+
PrintConvInv => '$val',
|
712
|
+
},
|
707
713
|
0x0048 => { #PH
|
708
714
|
Name => 'LensApertureRange',
|
709
715
|
Condition => '$$self{MakerNoteSigmaVer} >= 3',
|
@@ -797,6 +803,10 @@ $VERSION = '1.35';
|
|
797
803
|
0x0087 => 'ResolutionMode', #PH (Quattro models)
|
798
804
|
0x0088 => 'WhiteBalance', #PH (Quattro models)
|
799
805
|
0x008c => 'Firmware', #PH (Quattro models)
|
806
|
+
0x0113 => { #forum17338
|
807
|
+
Name => 'PictureModeStrength',
|
808
|
+
Writable => 'int32s',
|
809
|
+
},
|
800
810
|
0x011f => { #IB (FP DNG images)
|
801
811
|
Name => 'CameraCalibration',
|
802
812
|
Writable => 'float',
|
@@ -810,6 +820,14 @@ $VERSION = '1.35';
|
|
810
820
|
Name => 'WBSettings2',
|
811
821
|
SubDirectory => { TagTable => 'Image::ExifTool::Sigma::WBSettings2' },
|
812
822
|
},
|
823
|
+
0x0138 => { #forum17338
|
824
|
+
Name => 'Fade',
|
825
|
+
Writable => 'rational64u',
|
826
|
+
},
|
827
|
+
0x0139 => { #forum17338
|
828
|
+
Name => 'Vignette',
|
829
|
+
Writable => 'rational64u',
|
830
|
+
},
|
813
831
|
);
|
814
832
|
|
815
833
|
# WB settings (ref IB)
|
@@ -34,7 +34,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
|
|
34
34
|
use Image::ExifTool::Exif;
|
35
35
|
use Image::ExifTool::Minolta;
|
36
36
|
|
37
|
-
$VERSION = '3.
|
37
|
+
$VERSION = '3.74';
|
38
38
|
|
39
39
|
sub ProcessSRF($$$);
|
40
40
|
sub ProcessSR2($$$);
|
@@ -176,6 +176,7 @@ sub PrintInvLensSpec($;$$);
|
|
176
176
|
32888 => 'Sony FE 85mm F1.4 GM II', #JR
|
177
177
|
32889 => 'Sony FE 28-70mm F2 GM',
|
178
178
|
32890 => 'Sony FE 400-800mm F6.3-8 G OSS', #JR
|
179
|
+
32891 => 'Sony FE 50-150mm F2 GM', #github335
|
179
180
|
|
180
181
|
# (comment this out so LensID will report the LensModel, which is more useful)
|
181
182
|
# 32952 => 'Metabones Canon EF Speed Booster Ultra', #JR (corresponds to 184, but 'Advanced' mode, LensMount reported as E-mount)
|
@@ -201,7 +202,9 @@ sub PrintInvLensSpec($;$$);
|
|
201
202
|
33091 => 'Sony FE 400-800mm F6.3-8 G OSS + 1.4X Teleconverter', #JR
|
202
203
|
33092 => 'Sony FE 400-800mm F6.3-8 G OSS + 2X Teleconverter', #JR
|
203
204
|
|
204
|
-
49201 => 'Zeiss Touit 12mm F2.8', #JR (lens firmware Ver.02)
|
205
|
+
49201 => 'Zeiss Touit 12mm F2.8 or other Touit lens', #JR (lens firmware Ver.02) / github342 (firmware Ver.02.001)
|
206
|
+
49201.1 => 'Zeiss Touit 32mm F1.8', #github342 (firmware Ver.02.001)
|
207
|
+
49201.2 => 'Zeiss Touit 50mm F2.8', #github342 (firmware Ver.02.001) (NC)
|
205
208
|
49202 => 'Zeiss Touit 32mm F1.8', #JR (lens firmware Ver.02)
|
206
209
|
49203 => 'Zeiss Touit 50mm F2.8 Macro', #JR (lens firmware Ver.02)
|
207
210
|
49216 => 'Zeiss Batis 25mm F2', #JR
|
@@ -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
|