File Coverage

blib/lib/IETF/ACE.pm
Criterion Covered Total %
statement 101 363 27.8
branch 18 130 13.8
condition 7 57 12.2
subroutine 11 24 45.8
pod 0 18 0.0
total 137 592 23.1


line stmt bran cond sub pod time code
1             package IETF::ACE;
2              
3 1     1   164878 use strict;
  1         2  
  1         34  
4 1     1   4 use diagnostics;
  1         1  
  1         8  
5              
6 1     1   26 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         8  
  1         73  
7              
8             require Exporter;
9 1     1   268323 use AutoLoader qw(AUTOLOAD);
  1         1519  
  1         7  
10              
11 1     1   527 use Unicode::String qw(utf8 ucs4 utf16);
  1         6238  
  1         75  
12 1     1   456 use MIME::Base64;
  1         486  
  1         3240  
13              
14             @ISA = qw(Exporter);
15              
16             # Items to export into callers namespace by default. Note: do not export
17             # names by default without a very good reason. Use EXPORT_OK instead.
18             # Do not simply export all your public functions/methods/constants.
19              
20             # This allows declaration use IETF::ACE ':all';
21             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
22             # will save memory.
23             %EXPORT_TAGS = ( 'all' => [ qw(
24             ) ] );
25              
26             @EXPORT_OK = qw (
27             @{ $EXPORT_TAGS{'all'} }
28             &UCS4toName
29             &UCS4toUPlus
30             &UTF5toUCS4
31             &GetCharFromUTF5
32             &UCS4toRACE
33             &RACEtoUCS4
34             &UCS4toLACE
35             &LACEtoUCS4
36             &Base32Encode
37             &Base32Decode
38             &CheckForSTD13Name
39             &CheckForBadSurrogates
40             &HexOut
41             &DebugOn
42             &DebugOff
43             &DebugOut
44             );
45              
46             @EXPORT = qw(
47             );
48              
49             $VERSION = '0.04';
50              
51             # Preloaded methods go here.
52              
53             # Autoload methods go after =cut, and are processed by the autosplit program.
54              
55             my @Formats = ('utf8', 'utf16', 'ucs4', 'utf5', 'race', 'lace', 'name', 'u+');
56             my $UTF5Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUV';
57             my $Base32Chars = 'abcdefghijklmnopqrstuvwxyz234567';
58             my $RACEPrefix = 'bq--';
59             my $LACEPrefix = 'lq--';
60              
61             my $Debug = 0;
62              
63             1;
64              
65             sub UCS4toName {
66 0     0 0 0 my $InString = shift(@_);
67 0         0 my @TheNames = ucs4($InString)->name;
68 0         0 my $NameString = join("\n", @TheNames) . "\n";
69 0         0 return $NameString;
70             }
71              
72             sub UCS4toUPlus {
73 0     0 0 0 my $InString = shift(@_);
74 0         0 my $TheHex = ucs4($InString)->hex . "\n";
75 0         0 $TheHex =~ s/ /\n/g;
76 0         0 $TheHex = uc($TheHex);
77 0         0 return $TheHex;
78             }
79              
80             sub UTF5toUCS4 {
81 0     0 0 0 my $InString = shift(@_);
82 0         0 my $OutString = '';
83 0         0 my ($ThisUCS4, $ThisCharString, @RevString, $Char, $WhichChar);
84 0         0 my ($TempNum, $TempChr, $TempPos);
85 0         0 until(length($InString) == 0) {
86 0         0 ($ThisCharString, $InString) = &GetCharFromUTF5($InString);
87 0         0 $ThisUCS4 = "\x00\x00\x00\x00";
88 0         0 @RevString = reverse(split(//, $ThisCharString));
89 0         0 $WhichChar = 0;
90 0         0 foreach $Char (@RevString) {
91 0         0 $TempNum = index($UTF5Chars, $Char) % 16;
92 0 0       0 if(($WhichChar % 2) == 1) { $TempNum *= 16 };
  0         0  
93 0         0 $TempChr = chr($TempNum);
94 0         0 $TempPos = (int($WhichChar / 2));
95 0 0       0 if($TempPos == 0) { $TempChr = "\x00" x 3 . $TempChr }
  0 0       0  
    0          
    0          
96 0         0 elsif($TempPos == 1) { $TempChr = "\x00" x 2 . $TempChr . "\x00" }
97 0         0 elsif($TempPos == 2) { $TempChr = "\x00" . $TempChr . "\x00" x 2 }
98 0         0 elsif($TempPos == 3) { $TempChr = $TempChr . "\x00" x 3 }
99 0         0 $ThisUCS4 = $ThisUCS4 | $TempChr;
100 0         0 $WhichChar += 1;
101             }
102 0         0 $OutString .= $ThisUCS4;
103             }
104 0         0 return $OutString;
105             }
106              
107             sub GetCharFromUTF5 {
108 0     0 0 0 my $InString = shift(@_);
109 0         0 my $FirstChar = substr($InString, 0, 1);
110 0 0       0 unless(grep(/[GHIJKLMNOPQRSTUV]/, $FirstChar))
111 0         0 { &DieOut("Found bad character string in UTF5 at $InString" .
112             " in GetCharFromUTF5\n") }
113 0         0 my $ThisCharString = $FirstChar;
114 0         0 $InString = substr($InString, 1);
115 0         0 until(grep(/[GHIJKLMNOPQRSTUV]/, substr($InString, 0, 1))) {
116 0         0 $ThisCharString .= substr($InString, 0, 1);
117 0         0 $InString = substr($InString, 1);
118 0 0       0 last if(length($InString) == 0);
119             }
120 0         0 return ($ThisCharString, $InString);
121             }
122              
123             sub UCS4toUTF5 {
124 0     0 0 0 my $InString = shift(@_);
125 0         0 my $OutString = '';
126 0         0 my ($ThisUCS4, $i, $Nibble, $HaveSeenFirst);
127 0         0 until(length($InString) == 0) {
128 0         0 $ThisUCS4 = substr($InString, 0, 4);
129 0         0 $InString = substr($InString, 4);
130 0         0 my @Octets = split(//, $ThisUCS4);
131 0         0 $HaveSeenFirst = 0;
132 0         0 foreach $i (0 .. 7) {
133 0 0       0 if(($i % 2) == 0)
134 0         0 { $Nibble = chr(ord($Octets[int($i / 2)] & "\xf0") >> 4) }
135             else
136 0         0 { $Nibble = $Octets[int($i / 2)] & "\x0f" };
137 0 0 0     0 next if(($Nibble eq "\x00") and !($HaveSeenFirst));
138 0 0       0 if($HaveSeenFirst)
139 0         0 { $OutString .= substr($UTF5Chars, ord($Nibble), 1) }
140             else {
141 0         0 $OutString .= substr($UTF5Chars, ord($Nibble)+16, 1);
142 0         0 $HaveSeenFirst = 1;
143             }
144             }
145             }
146 0         0 return $OutString;
147              
148             }
149              
150             sub UCS4toRACE {
151 1     1 0 28 my $InString = shift(@_);
152 1         2 my (@InArr, $InStr, $InputPointer, $DoStep3, @UpperUniq, %UpperSeen,
153             $U1, $U2, $N1, $CompString,
154             $PostBase32);
155              
156 1         4 &DebugOut("Hex of input to UCS4toRACE:\n", &HexOut($InString));
157             # Make an array of the UTF16 octets
158 1         8 @InArr = split(//, ucs4($InString)->utf16);
159 1         106 $InStr = join('', @InArr);
160 1         39 &DebugOut("Hex of UTF16 input to UCS4toRACE:\n", &HexOut($InStr));
161 1 50       4 if(&CheckForSTD13Name($InStr))
162 0         0 { &DieOut("Found all-STD13 name in input to UCS4toRACE\n") }
163              
164             # Prepare for steps 1 and 2 by making an array of the upper octets
165 1         4 for($InputPointer = 0; $InputPointer <= $#InArr; $InputPointer += 2) {
166 5 100       12 unless ($UpperSeen{$InArr[$InputPointer]}) {
167 1         2 $UpperSeen{$InArr[$InputPointer]} = 1;
168 1         4 push (@UpperUniq, $InArr[$InputPointer])
169             }
170             }
171 1 50       7 if($#UpperUniq == 0) { # Step 1
    0          
172 1         2 $U1 = $UpperUniq[0];
173 1         2 $DoStep3 = 0;
174             } elsif($#UpperUniq == 1) { # Step 2
175 0 0       0 if($UpperUniq[0] eq "\x00") {
    0          
176 0         0 $U1 = $UpperUniq[1];
177 0         0 $DoStep3 = 0;
178             } elsif($UpperUniq[1] eq "\x00") {
179 0         0 $U1 = $UpperUniq[0];
180 0         0 $DoStep3 = 0;
181 0         0 } else { $DoStep3 = 1 }
182 0         0 } else { $DoStep3 = 1 }
183             # Now output based on the value of $DoStep3
184 1 50       3 if($DoStep3) { # Step 3
185 0         0 &DebugOut("Not compressing in UCS4toRACE (using D8 format).\n");
186 0         0 $CompString = "\xd8" . join('', @InArr);
187             } else {
188 1 50 33     4 if(($U1 ge "\xd8") and ($U1 le "\xdc")) { # Step 4a
189 0         0 my $DieOrd = sprintf("%04lX", ord($U1));
190 0         0 &DieOut("Found invalid input to UCS4toRACE step 4a: $DieOrd.\n");
191             }
192 1         5 &DebugOut("Compressing in UCS4toRACE (first octet is ",
193             sprintf("%04lX", ord($U1)), ").\n");
194 1         1 $CompString = $U1; # Step 4b
195 1         2 $InputPointer = 0;
196 1         3 while($InputPointer <= $#InArr) { # Step 5a
197 5         5 $U2 = $InArr[$InputPointer++]; $N1 = $InArr[$InputPointer++]; # Step 5b
  5         6  
198 5 50 33     15 if(($U2 eq "\x00") and ($N1 eq "\x99")) # Step 5c
199 0         0 { &DieOut("Found U+0099 in input stream to UCS4toRACE step 5c.\n"); }
200 5 50 33     15 if( ($U2 eq $U1) and ($N1 ne "\xff") ) # Step 6
    0 0        
201 5         7 { $CompString .= $N1 }
202             elsif( ($U2 eq $U1) and ($N1 eq "\xff") ) # Step 7
203 0         0 { $CompString .= "\xff\x99" }
204 0         0 else { $CompString .= "\xff" . $N1 } # Step 8
205             }
206             }
207 1         3 &DebugOut("Hex of output before Base32Encode:\n", &HexOut($CompString));
208 1 50       3 if(length($CompString) >= 37)
209 0         0 { &DieOut("Length of compressed string was >= 37 in UCS4toRACE.\n") }
210 1         4 $PostBase32 = &Base32Encode($CompString);
211 1         4 return "$RACEPrefix$PostBase32";
212             }
213              
214             sub RACEtoUCS4 {
215 0     0 0 0 my $InString = lc(shift(@_));
216 0         0 my ($PostBase32, @DeArr, $i, $U1, $N1, $OutString, $LCheck,
217             $InputPointer, @UpperUniq, %UpperSeen);
218             # Strip any whitespace
219 0         0 $InString =~ s/\s*//g;
220             # Strip of the prefix string
221 0 0       0 unless(substr($InString, 0, length($RACEPrefix)) eq $RACEPrefix)
222 0         0 { &DieOut("The input to RACEtoUCS4 did not start with '$RACEPrefix'\n") }
223 0         0 $InString = substr($InString, length($RACEPrefix));
224 0         0 &DebugOut("The string after stripping in RACEtoUCS4: $InString\n");
225              
226 0         0 $PostBase32 = &Base32Decode($InString);
227 0         0 @DeArr = split(//, $PostBase32);
228              
229             # Reverse the compression
230 0         0 $U1 = $DeArr[0]; # Step 1a
231 0 0       0 if($#DeArr < 1) # Step 1b
232 0         0 { &DieOut("The output of Base32Decode was too short.\n") }
233            
234 0 0       0 unless ($U1 eq "\xd8") { # Step 1c
235 0         0 $i = 1;
236 0         0 until($i > $#DeArr) { # Step 2a
237 0         0 $N1 = $DeArr[$i++]; # Step 2b
238 0 0       0 unless($N1 eq "\xff") { # Step 2c
239 0 0 0     0 if(($U1 eq "\x00") and ($N1 eq "\x99")) # Step 3
240 0         0 { &DieOut("Found 0099 in the input to RACEtoUCS4, step 3.\n") }
241 0         0 $OutString .= $U1 . $N1; # Step 4
242             } else {
243 0 0       0 if($i > $#DeArr) # Step 5
244 0         0 { &DieOut("Input in RACE string at octet $i too short " .
245             "at step 5\n") }
246 0         0 $N1 = $DeArr[$i++]; # Step 6a
247 0 0       0 if($N1 eq "\x99") # Step 6b
248 0         0 { $OutString .= $U1 . "\xff" }
249             else # Step 7
250 0         0 { $OutString .= "\x00" . $N1 }
251             }
252             }
253 0 0       0 if((length($OutString) % 2) == 1) # Step 11
254 0         0 { &DieOut("The output of RACEtoUCS4 for compressed input was " .
255             "an odd number of characters at step 11.\n") }
256             } else { # Was not compressed
257 0         0 $LCheck = substr(join('', @DeArr), 1); # Step 8a
258 0 0       0 if((length($LCheck) % 2 ) == 1 ) # Step 8b
259 0         0 { &DieOut("The output of RACEtoUCS4 for uncompressed input was " .
260             "an odd number of characters at step 8b.\n") }
261             # Do the step 9 check to be sure the right length was used
262 0         0 my @CheckArr = split(//, $LCheck);
263 0         0 for($InputPointer = 0; $InputPointer <= $#CheckArr; $InputPointer += 2) {
264 0 0       0 unless ($UpperSeen{$CheckArr[$InputPointer]}) {
265 0         0 $UpperSeen{$CheckArr[$InputPointer]} = 1;
266 0         0 push (@UpperUniq, $CheckArr[$InputPointer])
267             }
268             }
269             # Should it have been compressed?
270 0 0 0     0 if( ($#UpperUniq == 0) or
      0        
      0        
271             ( ($#UpperUniq == 1) and
272             (($UpperUniq[0] eq "\x00") or ($UpperUniq[1] eq "\x00"))
273             )
274 0         0 ) { &DieOut("Input to RACEtoUCS4 failed during LCHECK format test " .
275             "in step 9.\n") }
276 0 0       0 if((length($LCheck) % 2) == 1) # Step 10a
277 0         0 { &DieOut("The output of RACEtoUCS4 for uncompressed input was " .
278             "an odd number of characters at step 10a.\n") }
279 0         0 $OutString = $LCheck
280             }
281 0         0 &DebugOut("Hex of output string:\n", &HexOut($OutString));
282 0 0       0 if(&CheckForSTD13Name($OutString))
283 0         0 { &DieOut("Found all-STD13 name before output of RACEtoUCS4\n") }
284 0 0       0 if(&CheckForBadSurrogates($OutString))
285 0         0 { &DieOut("Found bad surrogate before output of RACEtoUCS4\n") }
286 0         0 return utf16($OutString)->ucs4;
287             }
288              
289             sub UCS4toLACE {
290 0     0 0 0 my $InString = shift(@_);
291 0         0 my (@InArr, $InStr, $InputPointer, $High, $OutBuffer, $Count, $LowBuffer,
292             $i, $CompString, $PostBase32);
293              
294 0         0 &DebugOut("Hex of input to UCS4toLACE:\n", &HexOut($InString));
295             # Make an array of the UTF16 octets
296 0         0 @InArr = split(//, ucs4($InString)->utf16);
297 0         0 $InStr = join('', @InArr);
298 0         0 &DebugOut("Hex of UTF16 input to UCS4toLACE:\n", &HexOut($InStr));
299 0 0       0 if(&CheckForSTD13Name($InStr))
300 0         0 { &DieOut("Found all-STD13 name in input to UCS4toLACE\n") }
301              
302 0 0 0     0 if(((length($InStr) % 2) == 1) or (length($InStr) < 2)) # Step 1
303 0         0 { &DieOut("Odd length or too short on input to UCS4toLACE\n") }
304 0         0 $InputPointer = 0; # Step 2
305 0         0 my $OutputBuffer = '';
306 0         0 do {
307 0         0 $High = $InArr[$InputPointer]; # Step 3
308 0         0 $Count = 1; $LowBuffer = $InArr[$InputPointer+1];
  0         0  
309 0         0 for($i = $InputPointer + 2; $i <= $#InArr; $i+=2) { # Step 4
310 0 0       0 last unless($InArr[$i] eq $High);
311 0         0 $Count += 1;
312 0         0 $LowBuffer .= $InArr[$i+1];
313             }
314 0         0 $OutputBuffer .= sprintf("%c", $Count) . "$High$LowBuffer"; # Step 5a
315 0         0 $InputPointer = $InputPointer + (2 * $Count); # Step 5b
316             } while($InputPointer <= $#InArr); # Step 6
317              
318 0 0       0 if(length($OutputBuffer) <= length($InStr)) # Step 7a
319 0         0 { $CompString = $OutputBuffer }
320             else
321 0         0 { $CompString = "\xff" . $InStr; }
322              
323 0         0 &DebugOut("Hex of output before Base32Encode:\n", &HexOut($CompString));
324 0 0       0 if(length($CompString) >= 37)
325 0         0 { &DieOut("Length of compressed string was >= 37 in UCS4toLACE.\n") }
326 0         0 $PostBase32 = &Base32Encode($CompString);
327 0         0 return "$LACEPrefix$PostBase32";
328             }
329              
330             sub LACEtoUCS4 {
331 0     0 0 0 my $InString = lc(shift(@_));
332 0         0 my ($PostBase32, @DeArr, $Count, $InputPointer, $OutString, $LCheck,
333             $OutputBuffer, $CompBuffer, @LArr, $LPtr, $RunCount, $RunBuffer);
334 0         0 my $Low;
335 0         0 my $High;
336             # Strip any whitespace
337 0         0 $InString =~ s/\s*//g;
338             # Strip of the prefix string
339 0 0       0 unless(substr($InString, 0, length($LACEPrefix)) eq $LACEPrefix)
340 0         0 { &DieOut("The input to LACEtoUCS4 did not start with '$LACEPrefix'\n") }
341 0         0 $InString = substr($InString, length($LACEPrefix));
342 0         0 &DebugOut("The string after stripping in LACEtoUCS4: $InString\n");
343              
344 0         0 $PostBase32 = &Base32Decode($InString);
345 0         0 @DeArr = split(//, $PostBase32);
346              
347 0         0 $InputPointer = 0; # Step 1a
348 0 0       0 if($#DeArr < 1) # Step 1b
349 0         0 { &DieOut("The output of Base32Decode was too short.\n") }
350 0         0 $OutputBuffer = '';
351 0 0       0 unless ($DeArr[$InputPointer] eq "\xff") { # Step 2
352 0         0 do {
353 0         0 $Count = $DeArr[$InputPointer]; # Step 3a
354 0 0 0     0 if(($Count == 0) or ($Count > 36)) # Step 3b
355 0         0 { &DieOut("Got bad count ($Count) in LACEtoUCS4 step 3b.\n") };
356 0 0       0 if(++$InputPointer == $#DeArr) # Step 3c and 3d
357 0         0 { &DieOut("Got bad length input in LACEtoUCS4 step 3d.\n") };
358 0         0 $High = $DeArr[$InputPointer++]; # Step 4a and 4b
359 0         0 do {
360 0 0       0 if($InputPointer == $#DeArr) # Step 5a
361 0         0 { &DieOut("Got bad length input in LACEtoUCS4 step 5a.\n") };
362 0         0 $Low = $DeArr[$InputPointer++]; # Step 5c and 5c
363 0         0 $OutputBuffer .= $High . $Low; # Step 6
364             } until(--$Count > 0); # Step 7
365             } while($InputPointer < $#DeArr); # Step 8
366 0 0       0 if(length($OutputBuffer) > length($InString)) { # Step 9b
    0          
367 0         0 &DieOut("Wrong compression format found in LACEtoUCS4 step 9b.\n");
368             } elsif((length($OutputBuffer) % 2) == 1) { # Step 9c
369 0         0 &DieOut("Odd length output buffer found in LACEtoUCS4 step 9c.\n");
370 0         0 } else { $OutString = $OutputBuffer } # Step 9d
371             } else { # Step 10
372 0         0 $OutputBuffer = substr(join('', @DeArr), 1); # Step 10a
373 0 0       0 if((length($OutputBuffer) % 2 ) == 1 ) # Step 10b
374 0         0 { &DieOut("The output of LACEtoUCS4 for uncompressed input was " .
375             "an odd number of characters at step 10b.\n") }
376             # Step 11a
377 0         0 $CompBuffer = ''; @LArr = split(//, $OutputBuffer); $LPtr = 0;
  0         0  
  0         0  
378 0         0 do {
379 0         0 $High = $LArr[$LPtr++]; # Step 3
380 0         0 $RunCount = 1; $RunBuffer = $LArr[$LPtr++];
  0         0  
381 0         0 while(1) { # Step 4
382 0 0       0 last if($LArr[$LPtr] ne $High);
383 0         0 $LPtr +=1;
384 0         0 $RunCount += 1;
385 0         0 $RunBuffer .= $LArr[$LPtr++];
386             }
387 0         0 $CompBuffer .= sprintf("%c", $RunCount) . $High .
388             $RunBuffer; # Step 5
389             } while($LPtr <= $#LArr); # Step 6
390 0 0       0 if(length($CompBuffer) <= length($OutputBuffer)) { # Step 11b
391 0         0 &DieOut("Wrong compression format found in LACEtoUCS4 step 11b.\n");
392 0         0 } else { $OutString = $OutputBuffer } # Step 11c
393             }
394 0         0 &DebugOut("Hex of output string:\n", &HexOut($OutString));
395 0 0       0 if(&CheckForSTD13Name($OutString))
396 0         0 { &DieOut("Found all-STD13 name before output of LACEtoUCS4\n") }
397 0 0       0 if(&CheckForBadSurrogates($OutString))
398 0         0 { &DieOut("Found bad surrogate before output of LACEtoUCS4\n") }
399 0         0 return utf16($OutString)->ucs4;
400             }
401              
402             sub Base32Encode {
403 1     1 0 2 my($ToEncode) = shift(@_);
404 1         2 my ($i, $OutString, $CompBits, $FivePos, $FiveBitsString, $FiveIndex);
405            
406 1         2 &DebugOut("Hex of input to Base32Encode:\n", &HexOut($ToEncode));
407              
408             # Turn the compressed string into a string that represents the bits as
409             # 0 and 1. This is wasteful of space but easy to read and debug.
410 1         1 $CompBits = '';
411 1         4 foreach $i (split(//, $ToEncode)) { $CompBits .= unpack("B8", $i) };
  6         9  
412              
413             # Pad the value with enough 0's to make it a multiple of 5
414 1 50       5 if((length($CompBits) % 5) != 0)
415 1         3 { $CompBits .= '0' x (5 - (length($CompBits) % 5)) }; # Step 1a
416 1         4 &DebugOut("The compressed bits in Base32Encode after padding:\n"
417             . "$CompBits\n");
418 1         1 $FivePos = 0; # Step 1b
419 1         1 do {
420 10         10 $FiveBitsString = substr($CompBits, $FivePos, 5); # Step 2
421 10         16 $FiveIndex = unpack("N", pack("B32", ('0' x 27) . $FiveBitsString));
422 10         10 $OutString .= substr($Base32Chars, $FiveIndex, 1); # Step 3
423 10         15 $FivePos += 5; # Step 4a
424             } until($FivePos == length($CompBits)); # Step 4b
425 1         5 &DebugOut("Output of Base32Encode:\n$OutString\n");
426 1         3 return $OutString;
427             }
428              
429             sub Base32Decode {
430 0     0 0 0 my ($ToDecode) = shift(@_);
431 0         0 my ($InputCheck, $OutString, $DeCompBits, $DeCompIndex, @DeArr, $i,
432             $PaddingLen, $PaddingContent);
433 0         0 &DebugOut("Hex of input to Base32Decode:\n", &HexOut($ToDecode));
434              
435 0         0 $InputCheck = length($ToDecode) % 8; # Step 1
436 0 0 0     0 if(($InputCheck == 1) or
      0        
437             ($InputCheck == 3) or
438             ($InputCheck == 6))
439 0         0 { &DieOut("Input to Base32Decode was a bad mod length: $InputCheck\n") }
440              
441             # $DeCompBits is a string that represents the bits as
442             # 0 and 1. This is wasteful of space but easy to read and debug.
443 0         0 $DeCompBits = '';
444 0         0 my $InChar;
445 0         0 foreach $InChar (split(//, $ToDecode)) {
446 0         0 $DeCompIndex = pack("N", index($Base32Chars, $InChar));
447 0         0 $DeCompBits .= substr(unpack("B32", $DeCompIndex), 27);
448             }
449 0         0 &DebugOut("The decompressed bits in Base32Decode:\n$DeCompBits\n");
450 0         0 &DebugOut("The number of bits in Base32Decode: " ,
451             length($DeCompBits), "\n");
452              
453             # Step 5
454 0         0 my $Padding = length($DeCompBits) % 8;
455 0         0 $PaddingContent = substr($DeCompBits, (length($DeCompBits) - $Padding));
456 0         0 &DebugOut("The padding check in Base32Decode is \"$PaddingContent\"\n");
457 0 0       0 unless(index($PaddingContent, '1') == -1)
458 0         0 { &DieOut("Found non-zero padding in Base32Decode\n") }
459              
460             # Break the decompressed string into octets for returning
461 0         0 @DeArr = ();
462 0         0 for($i = 0; $i < int(length($DeCompBits) / 8); $i++) {
463 0         0 $DeArr[$i] =
464             chr(unpack("N", pack("B32", ('0' x 24) . substr($DeCompBits, $i * 8, 8))));
465             }
466 0         0 $OutString = join('', @DeArr);
467 0         0 &DebugOut("Hex of the decompressed array:\n", &HexOut("$OutString"));
468 0         0 return $OutString;
469             }
470              
471             sub CheckForSTD13Name {
472             # The input is in UTF-16
473 1     1 0 2 my $InCheck = shift(@_);
474 1         1 my (@CheckArr, $CheckPtr, $Lower, $Upper);
475 1         4 @CheckArr = split(//, $InCheck);
476 1         2 $CheckPtr = 0;
477 1         18 my $STD13Chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYX' .
478             '0123456789-';
479 1         3 until($CheckPtr > $#CheckArr) {
480 1         2 $Upper = $CheckArr[$CheckPtr++];
481 1         2 $Lower = $CheckArr[$CheckPtr++];
482 1 50 33     8 if(($Upper ne "\x00") or
483 1         3 (index($STD13Chars, $Lower) == -1) ) { return 0 }
484             }
485 0         0 return 1;
486             }
487              
488             sub CheckForBadSurrogates {
489             # The input is in UTF-16
490 0     0 0 0 my $InCheck = shift(@_);
491 0         0 my (@CheckArr, $CheckPtr, $Upper1, $Upper2);
492 0         0 @CheckArr = split(//, $InCheck);
493 0         0 $CheckPtr = 0;
494 0         0 my $HighSurr = "\xD8\xD9\xDA\xDB";
495 0         0 my $LowSurr = "\xDC\xDD\xDE\xDF";
496 0         0 until($CheckPtr > $#CheckArr) {
497             # Check for bad half-pair
498 0 0 0     0 if((($CheckPtr + 2 ) >= $#CheckArr) and
499             (index($HighSurr.$LowSurr, $CheckArr[$CheckPtr]) > -1 )) {
500 0         0 &DebugOut("Found bad half-pair in CheckForBadSurrogates: " .
501             sprintf("%2.2x", ord($CheckArr[$CheckPtr])));
502 0         0 return 1;
503             }
504 0 0       0 last unless(defined($CheckArr[$CheckPtr + 4]));
505 0         0 $Upper1 = $CheckArr[$CheckPtr += 2];
506 0         0 $Upper2 = $CheckArr[$CheckPtr += 2];
507 0 0 0     0 if( ((index($HighSurr, $Upper1) > -1) and
      0        
      0        
508             (index($LowSurr, $Upper2) == -1))
509             or
510             ((index($HighSurr, $Upper1) == -1) and
511             (index($LowSurr, $Upper2) > -1))) {
512 0         0 &DebugOut("Found bad pair in CheckForBadSurrogates: " .
513             sprintf("%2.2x", ord($Upper1)) . " and " .
514             sprintf("%2.2x", ord($Upper2)) . "\n");
515 0         0 return 1;
516             }
517             }
518 0         0 return 0;
519             }
520              
521             sub HexOut {
522 4     4 0 6 my $AllInStr = shift(@_);
523 4         5 my($HexIn, $HexOut, @AllOrd, $i, $j, $k, $OutReg, $SpOut);
524 0         0 my @HexIn;
525 0         0 my($OctetIn, $LineCount);
526 0         0 my @OctetIn;
527 4         5 my $OutString = '';
528 4         19 @AllOrd = split(//, $AllInStr);
529            
530 4         7 $HexIn[23] = '';
531 4         8 while(@AllOrd) {
532 4         10 for($i = 0; $i < 24; $i++) {
533 96         80 $OctetIn[$i] = shift(@AllOrd);
534 96 100       169 if(defined($OctetIn[$i])) {
535 42         53 $HexIn[$i] = sprintf('%2.2x', ord($OctetIn[$i]));
536 42         54 $LineCount = $i;
537             }
538             }
539 4         9 for($j = 0; $j <= $LineCount; $j++ ) {
540 42         38 $HexOut .= $HexIn[$j];
541 42 100       54 if(($j % 4) == 3) { $HexOut .= ' ' }
  9         8  
542 42 100 100     90 if((ord($OctetIn[$j]) < 20) or (ord($OctetIn[$j]) > 126))
543 26         36 { $OutReg .= '.' }
544 16         22 else { $OutReg .= $OctetIn[$j] }
545             }
546 4         9 for ($k=length($HexOut); $k < 56; $k++) { $SpOut .= ' ' }
  131         178  
547 4         10 $OutString .= "$HexOut$SpOut$OutReg\n" ;
548 4         5 $HexOut = ''; $OutReg = ''; $SpOut = '';
  4         5  
  4         6  
549             }
550 4         19 return $OutString
551             }
552              
553             sub DebugOn {
554 0     0 0 0 $Debug = 1;
555             }
556              
557             sub DebugOff {
558 0     0 0 0 $Debug = 0;
559             }
560              
561             sub DebugOut {
562             # Print out an error string if $Debug is set
563 7     7 0 12 my $DebugTemp = join('', @_);
564 7 50       18 if($Debug) { print STDERR $DebugTemp; }
  0            
565             }
566              
567             sub DieOut {
568 0     0 0   my $DieTemp = shift(@_);
569             # if(defined($ErrTmp)) { print STDERR $DieTemp; }
570 0           die;
571             }
572              
573              
574             __END__