File Coverage

blib/lib/Mail/Exchange/NamedProperties.pm
Criterion Covered Total %
statement 63 119 52.9
branch 9 22 40.9
condition 10 18 55.5
subroutine 16 20 80.0
pod 1 8 12.5
total 99 187 52.9


line stmt bran cond sub pod time code
1             package Mail::Exchange::NamedProperties;
2              
3 5     5   27 use strict;
  5         10  
  5         191  
4 5     5   24 use warnings;
  5         9  
  5         178  
5 5     5   144 use 5.008;
  5         15  
  5         179  
6              
7 5     5   44 use Exporter;
  5         8  
  5         217  
8 5     5   29 use Encode;
  5         7  
  5         498  
9 5     5   30 use Mail::Exchange::PidTagDefs;
  5         7  
  5         635  
10 5     5   30 use Mail::Exchange::PidTagIDs;
  5         7  
  5         16398  
11 5     5   4998 use Mail::Exchange::PidLidDefs;
  5         23  
  5         1022  
12 5     5   52 use Mail::Exchange::PropertyContainer;
  5         9  
  5         241  
13 5     5   3459 use Mail::Exchange::CRC qw(crc);
  5         14  
  5         341  
14              
15 5     5   29 use vars qw($VERSION @ISA @EXPORT);
  5         10  
  5         6683  
16             @ISA=qw(Exporter);
17             @EXPORT=qw(GUIDEncode GUIDDecode);
18              
19             $VERSION = "0.04";
20              
21             sub new {
22 1     1 0 3 my $class=shift;
23 1         2 my $file=shift;
24              
25 1         1 my $self={};
26 1         3 bless($self, $class);
27              
28 1         7 $self->{namedprops}=[];
29              
30 1         4 return $self;
31             }
32              
33             sub OleContainer {
34 0     0 0 0 my $self=shift;
35              
36 0         0 my @guidlist=("??", "PS_MAPI", "PS_PUBLIC_STRINGS");
37 0         0 my $strstream="";
38 0         0 my $entrystream="";
39 0         0 my @nametoidstring;
40              
41 0         0 my $idx=0;
42 0         0 foreach my $str (@{$self->{namedprops}}) {
  0         0  
43 0         0 my $guididx=0;
44 0         0 while ($guididx <= $#guidlist) {
45 0 0       0 last if $guidlist[$guididx] eq $str->{guid};
46 0         0 $guididx++;
47             }
48 0 0       0 if ($guididx==$#guidlist+1) {
49 0         0 push(@guidlist, $str->{guid});
50             }
51 0         0 $str->{_guidindex}=$guididx;
52              
53 0 0       0 if ($str->{str} =~ /^\d/) {
54             ### this is a LID
55 0         0 $entrystream.=pack("VV", $str->{str},
56             $idx<<16 | $guididx<<1 | 0);
57              
58 0         0 my $nametoididx;
59 0         0 $nametoididx=($str->{str}^($guididx << 1))%0x1f;
60              
61 0         0 $nametoidstring[$nametoididx].=pack("VV",
62             $str->{str}, $idx<<16 | $guididx<<1 | 0);
63             } else {
64             ### this is a string named property
65              
66 0         0 $str->{_streampos}=length $strstream;
67 0         0 my $ucs=Encode::encode("UCS2LE", $str->{str});
68 0         0 $strstream.=pack("V", length($ucs)).$ucs;
69 0 0       0 if (length($strstream)%4) {
70 0         0 $strstream.="\0"x(4-length($strstream)%4);
71             }
72 0         0 $entrystream.=pack("VV", $str->{_streampos},
73             $idx<<16 | $guididx<<1 | 1);
74              
75 0         0 my $crc;
76 0         0 $crc=crc($ucs);
77              
78 0         0 my $nametoididx;
79 0         0 $nametoididx=($crc ^ (($guididx << 1) | 1))%0x1f;
80              
81 0         0 $nametoidstring[$nametoididx].=pack("VV",
82             $crc, $idx<<16 | $guididx<<1 | 1);
83             }
84 0         0 $idx++;
85             }
86              
87 0         0 my $GUIDStream =OLE::Storage_Lite::PPS::File->
88             new(Encode::encode("UCS2LE", "__substg1.0_00020102"), $self->_packGUIDlist(@guidlist));
89 0         0 my $EntryStream =OLE::Storage_Lite::PPS::File->
90             new(Encode::encode("UCS2LE", "__substg1.0_00030102"), $entrystream);
91 0         0 my $StringStream=OLE::Storage_Lite::PPS::File->
92             new(Encode::encode("UCS2LE", "__substg1.0_00040102"), $strstream);
93              
94 0         0 my @streams=($GUIDStream, $EntryStream, $StringStream);
95 0         0 for (my $i=0; $i<=0x1e; $i++) {
96 0 0       0 if ($nametoidstring[$i]) {
97 0         0 my $ntpstream=OLE::Storage_Lite::PPS::File->
98             new(Encode::encode("UCS2LE", sprintf("__substg1.0_10%02X0102", $i)),
99             $nametoidstring[$i]);
100 0         0 push(@streams, $ntpstream);
101             }
102             }
103              
104 0         0 my $dirname=Encode::encode("UCS2LE", sprintf("__nameid_version1.0"));
105 0         0 my @ltime=localtime();
106 0         0 my $dir=OLE::Storage_Lite::PPS::Dir->new($dirname, \@ltime, \@ltime, \@streams );
107 0         0 return $dir;
108             }
109              
110              
111             sub GUIDEncode {
112 0     0 0 0 my $str=shift;
113              
114 0 0       0 return undef unless $str =~ /^([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})$/i;
115 0         0 return pack("VvvnH12", hex($1), hex($2), hex($3), hex($4), $5);
116             }
117              
118             sub GUIDDecode {
119 27     27 0 48 my $guid=shift;
120              
121 27         90 my @f=unpack("VvvnH12", $guid);
122 27         131 return sprintf("%08x-%04x-%04x-%04x-%12s", @f);
123             }
124              
125             sub _packGUIDlist {
126 0     0   0 my $self=shift;
127 0         0 my @guidlist=@_;
128 0         0 my $str="";
129              
130 0         0 foreach my $i (3..$#guidlist) {
131 0         0 $str.=GUIDEncode($guidlist[$i]);
132             }
133 0         0 return $str;
134             }
135              
136             =head2 namedPropertyIndex
137              
138             =cut
139              
140             # There are a few special cases to consider.
141             # - Standard call, when creating a message, is with property name,
142             # property type, and guid. In this case, match it against
143             # what we have and return an appropriate index, or create a new
144             # entry if there's no match.
145             # - Also, when creating a message, the user may call us with a
146             # PidLid ID without name or type; in this case, try to find out
147             # about those from the PidLidDefs hash.
148             # - When parsing a message, we'll be called first with name and guid,
149             # but without a type, as type isn't present in __nameid. In this
150             # case, create an entry with an undef type; the parser will set
151             # the type later using setType.
152             # - Also, when parsing properties later, the parser will call us
153             # with a property name it knows to exist (because parsing __nameid
154             # will have created it), but the parser doesn't know the GUID. So
155             # if we're not given a guid (or type), and can't determine it from
156             # the PidLid definitions, ignore it and just use the string to look
157             # up the correct ID.
158              
159             sub namedPropertyIndex {
160 58     58 1 74 my $self=shift;
161              
162 58         86 my ($str, $type, $guid)=@_;
163 58 100 33     490 if ($str=~/^\d/ && $str>=0x8000 && $PidLidDefs{$str}) {
      66        
164 54 100       127 $type=$PidLidDefs{$str}{type} unless defined $type;
165 54 100       159 $guid=$PidLidDefs{$str}{guid} unless defined $guid;
166             }
167 58         66 foreach my $i (0..$#{$self->{namedprops}}) {
  58         140  
168 1527 100 33     3399 if ($self->{namedprops}[$i]{str} eq $str
      66        
      100        
      33        
169             && (!$type || $self->{namedprops}[$i]{type} == $type)
170             && (!$guid || $self->{namedprops}[$i]{guid} eq $guid)) {
171 6         26 return 0x8000 | $i;
172             }
173             }
174 52 50       124 die("named Property $str unknown, can't add without guid")
175             unless ($guid);
176 52         55 push(@{$self->{namedprops}}, {
  52         312  
177             str => $str, guid => $guid, type => $type,
178             _streampos => -1, _guidindex => -1, _crc => 0,
179             _streamidx => 0,
180             });
181 52         62 return 0x8000 | $#{$self->{namedprops}};
  52         159  
182             }
183              
184             sub LidForID {
185 27     27 0 32 my $self=shift;
186 27         29 my $id=shift;
187 27         90 return $self->{namedprops}[$id&0x7fff]{str};
188             }
189              
190             sub getType {
191 0     0 0 0 my $self=shift;
192 0         0 my $id=shift;
193              
194 0         0 return $self->{namedprops}[$id&0x7fff]{type};
195             }
196              
197             sub setType {
198 27     27 0 36 my $self=shift;
199 27         29 my $id=shift;
200 27         28 my $type=shift;
201              
202 27         85 $self->{namedprops}[$id&0x7fff]{type}=$type;
203             }
204              
205             1;