File Coverage

blib/lib/Mail/Exchange/PropertyContainer.pm
Criterion Covered Total %
statement 100 146 68.4
branch 24 46 52.1
condition 30 77 38.9
subroutine 14 15 93.3
pod 0 3 0.0
total 168 287 58.5


line stmt bran cond sub pod time code
1             package Mail::Exchange::PropertyContainer;
2              
3 5     5   30 use strict;
  5         7  
  5         209  
4 5     5   27 use warnings;
  5         9  
  5         167  
5 5     5   137 use 5.008;
  5         14  
  5         159  
6              
7 5     5   23 use Exporter;
  5         7  
  5         158  
8 5     5   23 use Encode;
  5         9  
  5         421  
9 5     5   27 use Mail::Exchange::PidTagIDs;
  5         10  
  5         20331  
10 5     5   51 use Mail::Exchange::PidTagDefs;
  5         9  
  5         699  
11 5     5   6290 use OLE::Storage_Lite;
  5         198695  
  5         303  
12              
13 5     5   51 use vars qw($VERSION @ISA);
  5         11  
  5         6659  
14             @ISA=qw(Exporter);
15              
16             $VERSION = "0.04";
17              
18             sub new {
19 2     2 0 5 my $class=shift;
20 2         5 my $file=shift;
21              
22 2         6 my $self={};
23 2         6 bless($self, $class);
24              
25 2         12 $self->{_properties}={};
26 2         8 $self;
27             }
28              
29             # set a property. Property can be a Pid, a Lid, or a string. It should NOT
30             # be an index in a named property list.
31              
32             sub set {
33 83     83 0 101 my $self=shift;
34 83         85 my $property=shift;
35 83         88 my $value=shift;
36 83         86 my $flags=shift;
37 83         82 my $type=shift;
38 83         85 my $guid=shift;
39 83         79 my $namedproperties = shift;
40              
41 83         183 my $normalized=$self->_propertyid($property, $type, $guid, $namedproperties);
42 83         321 $self->{_properties}{$normalized}{val} = $value;
43 83         160 $self->{_properties}{$normalized}{flg} = $flags;
44 83         155 1;
45             }
46              
47             sub get {
48 10     10 0 15 my $self=shift;
49 10         16 my $property=shift;
50 10         15 my $namedproperties = shift;
51              
52 10         32 my $normalized=$self->_propertyid($property, undef, undef, $namedproperties);
53 10 50       26 if (wantarray) {
54 0         0 return ($self->{_properties}{$normalized}{val}, $self->{_properties}{$normalized}{flg});
55             } else {
56 10         78 return $self->{_properties}{$normalized}{val};
57             }
58             }
59              
60             sub _OlePropertyStreamlist {
61 0     0   0 my $self=shift;
62 0         0 my $unicode=shift;
63 0         0 my $header=shift;
64              
65 0         0 my @streams=();
66 0         0 my $propertystr=$header;
67              
68 0         0 foreach my $property(sort {$a <=> $b } keys %{$self->{_properties}}) {
  0         0  
  0         0  
69 0         0 my $type;
70 0 0       0 if ($property & 0x8000) {
71 0         0 $type=$self->{_namedProperties}->getType($property);
72             } else {
73 0         0 $type=$PidTagDefs{$property}{type};
74             }
75 0 0       0 die "no type for $property" unless $type;
76             # my $data=$self->get($property);
77 0         0 my $data=$self->{_properties}{$property}{val};
78 0   0     0 my $flags=$self->{_properties}{$property}{flg} || 6;
79              
80 0 0 0     0 if ($type==0x000d || $type==0x001e || $type==0x001f
      0        
      0        
      0        
81             || $type==0x0048 || $type==0x0102) {
82             # At this point, data is in utf-8, so we need to
83             # turn it into whatever the message wants it to be.
84 0         0 my $length;
85 0 0 0     0 if (($type == 0x001E || $type == 0x001F) && $unicode) {
    0 0        
      0        
      0        
86 0         0 $data=Encode::encode("UCS2LE", $data);
87 0         0 $type=0x001F;
88 0         0 $length=(length($data)+2);
89             } elsif (($type == 0x001E || $type == 0x001F) && !$unicode) {
90 0         0 $data=Encode::encode("latin-1", $data);
91 0         0 $type=0x001E;
92 0         0 $length=(length($data)+1);
93             } else {
94 0         0 $length=(length($data));
95             }
96 0         0 my $streamname=sprintf("__substg1.0_%04X%04X", $property, $type);
97 0         0 my $stream=OLE::Storage_Lite::PPS::File->
98             new(Encode::encode("UCS2LE", $streamname), $data);
99 0         0 push(@streams, $stream);
100 0         0 $data=$length;
101             }
102 0         0 eval {
103 0         0 $propertystr.=pack("VVQ", ($property<<16|$type),
104             $flags, $data);
105             };
106 0 0       0 if ($@) {
107 0         0 $propertystr.=pack("VVVV", ($property<<16|$type),
108             $flags, $data&0xffffffff, $data/4294967296.0);
109             }
110             }
111 0         0 my $stream=OLE::Storage_Lite::PPS::File->
112             new(Encode::encode("UCS2LE", "__properties_version1.0"), $propertystr);
113 0         0 push(@streams, $stream);
114              
115 0         0 return @streams;
116             }
117              
118              
119             # returns the internal hash index id of a property,
120             # which is the upper 2 bytes of the official ID, without the
121             # lower 2 bytes that encode the type. This function can
122             # be given a Pid, in which case it returns the Pid itself,
123             # or a LID or Name, in which case it returns 0x8000 plus
124             # the index of this property in the property stream.
125              
126             sub _propertyid {
127 93     93   101 my $self=shift;
128 93         100 my $property=shift;
129 93         95 my $type=shift;
130 93         88 my $guid=shift;
131 93         118 my $namedProperties=shift;
132              
133 93 50       293 if ($property =~ /^[0-9]/) {
    0          
134 93 100       184 if ($property & 0xffff0000) {
135 15         20 $type=$property&0xffff;
136 15         22 $property>>=16;
137             }
138 93 50 33     382 if (defined($type) && $type==0x1e) {
139             # Map String8 to UCS-String, we'll care
140             # about (non)-Unicode when writing stuff out.
141 0         0 $type=0x1f;
142             }
143 93 100       185 if ($property & 0x8000) {
144             # map PidLids to indexes
145 31         98 $property=$namedProperties->namedPropertyIndex(
146             $property, $type, $guid);
147             } else {
148             # This is for when we're parsing and encounter
149             # an unknown property type. Remember it so
150             # we can use it / write it out later.
151 62 50 33     231 if (!$PidTagDefs{$property} && $type) {
152 0         0 $PidTagDefs{$property}{type}=$type;
153             }
154             }
155 93         232 return $property;
156             } elsif ($namedProperties) {
157             # @@@ map guid name to guid ID ?
158 0         0 my $id=$namedProperties->namedPropertyIndex($property, $type, $guid);
159 0         0 return $id;
160             }
161 0         0 die ("can't make sense of $property");
162             }
163              
164             sub _parseProperties {
165 2     2   4 my $self=shift;
166 2         3 my $file=shift;
167 2         4 my $dir=shift;
168 2         4 my $headersize=shift;
169 2         4 my $namedProperties=shift;
170              
171 2         9 my $data=substr($file->{Data}, $headersize); # ignore header
172 2         6 while ($data) {
173 78         81 my ($tag, $flags, $value, $v1, $v2);
174 78         101 eval {
175 78         171 ($tag, $flags, $value)=unpack("VVQ", $data);
176 78         150 $v1=$value&0xffffffff;
177             };
178 78 50       149 if ($@) {
179 0         0 ($tag, $flags, $v1, $v2)=unpack("VVVV", $data);
180 0         0 $value=$v2*4294967296+$v1;
181             }
182 78         85 my $type = $tag&0xffff;
183 78         85 my $ptag = ($tag>>16)&0xffff;
184              
185             # If it's a named property, we will have created it when
186             # parsing __nameid, but we don't know the type yet, so
187             # we have to set it here.
188 78 100       176 if ($ptag & 0x8000) {
189 27         89 $namedProperties->setType($ptag, $type);
190             }
191 78 50       125 if ($type & 0x1000) {
192 0         0 die("Multiple properties not implemented");
193             }
194 78 50       138 if ($type==0x0002) { $value=$v1&0xffff; }
  0         0  
195 78 100 66     749 if ($type==0x0003 || $type==0x0004 || $type==0x000a || $type==0x000b
      66        
      66        
      66        
      66        
      66        
      66        
      66        
      33        
      33        
      66        
196             || $type==0x000d || $type==0x001e || $type==0x001f || $type==0x0048
197             || $type==0x00FB || $type==0x00FD || $type==0x00FE || $type==0x0102) {
198 73         92 $value=$v1;
199             }
200 78 100 33     717 if ($type==0x000d || $type==0x001E || $type==0x001F
      66        
      66        
      100        
201             || $type==0x0048 || $type==0x0102) {
202 28         155 my $streamname=Encode::encode("UCS2LE",
203             sprintf("__substg1.0_%08X", $tag));
204 28         986 my $found=0;
205 28         36 foreach $file (@{$dir->{Child}}) {
  28         67  
206 254 100       526 if ($file->{Name} eq $streamname) {
207 28         30 $found=1;
208 28         54 $value=$file->{Data};
209 28 100       52 if ($type == 0x1f) {
210 17         175 $value=Encode::decode("UCS2LE", $value);
211             }
212 28         529 last;
213             }
214             }
215 28 50       59 die "stream for $tag not found" unless $found;
216             }
217 78 100       147 if ($ptag & 0x8000) {
218 27         78 $ptag=$namedProperties->LidForID($ptag);
219             }
220 78         230 $self->set($ptag, $value, $flags, $type);
221 78         235 $data=substr($data, 16);
222             }
223             }