File Coverage

blib/lib/Net/Domain/Info/IDN.pm
Criterion Covered Total %
statement 78 191 40.8
branch 0 74 0.0
condition 0 26 0.0
subroutine 26 40 65.0
pod 6 6 100.0
total 110 337 32.6


line stmt bran cond sub pod time code
1             package Net::Whois::Info;
2              
3 3     3   20 use Class::Easy;
  3         5  
  3         96  
4              
5             1;
6              
7             # package code below is included because Claus Färber does stupid
8             # things like «developer» module versions, which can't be installed
9             # automatically via cpan.
10              
11             # more info at: http://search.cpan.org/perldoc?Net%3A%3AIDN%3A%3AEncode
12              
13             package Net::IDN::Punycode;
14              
15 3     3   1290 use strict;
  3         8  
  3         126  
16 3     3   4633 use utf8;
  3         35  
  3         17  
17 3     3   98 use warnings;
  3         5  
  3         130  
18             require 5.006_000;
19              
20 3     3   3970 use integer;
  3         31  
  3         17  
21              
22             our $DEBUG = 0;
23              
24 3     3   157 use constant BASE => 36;
  3         6  
  3         355  
25 3     3   17 use constant TMIN => 1;
  3         7  
  3         142  
26 3     3   17 use constant TMAX => 26;
  3         3  
  3         19580  
27 3     3   33 use constant SKEW => 38;
  3         7  
  3         256  
28 3     3   17 use constant DAMP => 700;
  3         7  
  3         389  
29 3     3   17 use constant INITIAL_BIAS => 72;
  3         7  
  3         159  
30 3     3   16 use constant INITIAL_N => 128;
  3         5  
  3         7666  
31              
32             my $Delimiter = chr 0x2D;
33             my $BasicRE = qr/[\x00-\x7f]/;
34              
35             sub _digit_value {
36 0     0     my $code = shift;
37 0 0         return ord($code) - ord("A") if $code =~ /[A-Z]/;
38 0 0         return ord($code) - ord("a") if $code =~ /[a-z]/;
39 0 0         return ord($code) - ord("0") + 26 if $code =~ /[0-9]/;
40 0           return;
41             }
42              
43             sub _code_point {
44 0     0     my $digit = shift;
45 0 0 0       return $digit + ord('a') if 0 <= $digit && $digit <= 25;
46 0 0 0       return $digit + ord('0') - 26 if 26 <= $digit && $digit <= 36;
47 0           die 'NOT COME HERE';
48             }
49              
50             sub _adapt {
51 0     0     my($delta, $numpoints, $firsttime) = @_;
52 0 0         $delta = $firsttime ? $delta / DAMP : $delta / 2;
53 0           $delta += $delta / $numpoints;
54 0           my $k = 0;
55 0           while ($delta > ((BASE - TMIN) * TMAX) / 2) {
56 0           $delta /= BASE - TMIN;
57 0           $k += BASE;
58             }
59 0           return $k + (((BASE - TMIN + 1) * $delta) / ($delta + SKEW));
60             }
61              
62             sub decode_punycode {
63 0     0 1   my $code = shift;
64              
65 0           my $n = INITIAL_N;
66 0           my $i = 0;
67 0           my $bias = INITIAL_BIAS;
68 0           my @output;
69              
70 0 0         if ($code =~ s/(.*)$Delimiter//o) {
71 0           push @output, map ord, split //, $1;
72 0 0         return die ('non-basic code point') unless $1 =~ /^$BasicRE*$/o;
73             }
74              
75 0           while ($code) {
76 0           my $oldi = $i;
77 0           my $w = 1;
78             LOOP:
79 0           for (my $k = BASE; 1; $k += BASE) {
80 0           my $cp = substr($code, 0, 1, '');
81 0           my $digit = _digit_value($cp);
82 0 0         defined $digit or return die ("invalid punycode input");
83 0           $i += $digit * $w;
84 0 0         my $t = ($k <= $bias) ? TMIN
    0          
85             : ($k >= $bias + TMAX) ? TMAX : $k - $bias;
86 0 0         last LOOP if $digit < $t;
87 0           $w *= (BASE - $t);
88             }
89 0           $bias = _adapt($i - $oldi, @output + 1, $oldi == 0);
90 0 0         warn "bias becomes $bias" if $DEBUG;
91 0           $n += $i / (@output + 1);
92 0           $i = $i % (@output + 1);
93 0           splice(@output, $i, 0, $n);
94 0 0         warn join " ", map sprintf('%04x', $_), @output if $DEBUG;
95 0           $i++;
96             }
97 0           return join '', map chr, @output;
98             }
99              
100             sub encode_punycode {
101 0     0 1   my $input = shift;
102             # my @input = split //, $input; # doesn't work in 5.6.x!
103 0           my @input = map substr($input, $_, 1), 0..length($input)-1;
104              
105 0           my $n = INITIAL_N;
106 0           my $delta = 0;
107 0           my $bias = INITIAL_BIAS;
108              
109 0           my @output;
110 0           my @basic = grep /$BasicRE/, @input;
111 0           my $h = my $b = @basic;
112 0 0         push @output, @basic, $Delimiter if $b > 0;
113 0 0         warn "basic codepoints: (@output)" if $DEBUG;
114              
115 0           while ($h < @input) {
116 0           my $m = _min(grep { $_ >= $n } map ord, @input);
  0            
117 0 0         warn sprintf "next code point to insert is %04x", $m if $DEBUG;
118 0           $delta += ($m - $n) * ($h + 1);
119 0           $n = $m;
120 0           for my $i (@input) {
121 0           my $c = ord($i);
122 0 0         $delta++ if $c < $n;
123 0 0         if ($c == $n) {
124 0           my $q = $delta;
125             LOOP:
126 0           for (my $k = BASE; 1; $k += BASE) {
127 0 0         my $t = ($k <= $bias) ? TMIN :
    0          
128             ($k >= $bias + TMAX) ? TMAX : $k - $bias;
129 0 0         last LOOP if $q < $t;
130 0           my $cp = _code_point($t + (($q - $t) % (BASE - $t)));
131 0           push @output, chr($cp);
132 0           $q = ($q - $t) / (BASE - $t);
133             }
134 0           push @output, chr(_code_point($q));
135 0           $bias = _adapt($delta, $h + 1, $h == $b);
136 0 0         warn "bias becomes $bias" if $DEBUG;
137 0           $delta = 0;
138 0           $h++;
139             }
140             }
141 0           $delta++;
142 0           $n++;
143             }
144 0           return join '', @output;
145             }
146              
147             sub _min {
148 0     0     my $min = shift;
149 0 0         for (@_) { $min = $_ if $_ <= $min }
  0            
150 0           return $min;
151             }
152              
153             1;
154              
155             package Net::IDN::Encode;
156              
157 3     3   27 use strict;
  3         6  
  3         141  
158 3     3   16 use utf8;
  3         7  
  3         22  
159 3     3   85 use warnings;
  3         5  
  3         372  
160             require 5.006_000;
161              
162             our $VERSION = '0.99_20080919';
163             $VERSION = eval $VERSION;
164              
165             our $IDNA_prefix = 'xn--';
166              
167             sub _to_ascii {
168 3     3   4273 use bytes;
  3         35  
  3         17  
169 3     3   174 no warnings qw(utf8); # needed for perl v5.6.x
  3         7  
  3         1034  
170              
171 0     0     my ($label,%param) = @_;
172              
173 0 0         if($label =~ m/[^\x00-\x7F]/) {
174 0           $label = Net::IDN::Nameprep::nameprep ($label);
175             }
176              
177 0 0         if($param{'UseSTD3ASCIIRules'}) {
178 0 0 0       die 'Invalid domain name (toASCII, step 3)' if
      0        
179             $label =~ m/^-/ ||
180             $label =~ m/-$/ ||
181             $label =~ m/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]/;
182             }
183              
184 0 0         if($label =~ m/[^\x00-\x7F]/) {
185 0 0         die 'Invalid label (toASCII, step 5)' if $label =~ m/^$IDNA_prefix/;
186 0           return $IDNA_prefix.Net::IDN::Punycode::encode_punycode ($label);
187             } else {
188 0           return $label;
189             }
190             }
191              
192             sub _to_unicode {
193 3     3   17 use bytes;
  3         7  
  3         13  
194              
195 0     0     my ($label,%param) = @_;
196 0           my $orig = $label;
197              
198 0   0       return eval {
199             if($label =~ m/[^\x00-\x7F]/) {
200             $label = Net::IDN::Nameprep::nameprep ($label);
201             }
202              
203             my $save3 = $label;
204             die unless $label =~ s/^$IDNA_prefix//;
205              
206             $label = Net::IDN::Punycode::decode_punycode ($label);
207            
208             my $save6 = _to_ascii($label,%param);
209              
210             die unless uc($save6) eq uc($save3);
211              
212             $label;
213             } || $orig;
214             }
215              
216             sub _domain {
217 3     3   1081 use utf8;
  3         8  
  3         19  
218 0     0     my ($domain,$_to_function,@param) = @_;
219 0 0         return undef unless $domain;
220 0 0 0       return join '.',
221 0           grep { die 'Invalid domain name' if length($_) > 63 && !m/[^\x00-\x7F]/; 1 }
  0            
222 0           map { $_to_function->($_, @param, 'UseSTD3ASCIIRules' => 1) }
223             split /[\.。.。]/, $domain;
224             }
225              
226             sub _email {
227 3     3   894 use utf8;
  3         8  
  3         15  
228 0     0     my ($email,$_to_function,@param) = @_;
229 0 0         return undef unless $email;
230              
231 0 0         $email =~ m/^([^"\@@]+|"(?:(?:[^"]|\\.)*[^\\])?")(?:[\@@]
232             (?:([^\[\]]*)|(\[.*\]))?)?$/x || die "Invalid email address";
233 0           my($local_part,$domain,$domain_literal) = ($1,$2,$3);
234              
235 0 0         $local_part =~ m/[^\x00-\x7F]/ && die "Invalid email address";
236 0 0 0       $domain_literal =~ m/[^\x00-\x7F]/ && die "Invalid email address" if $domain_literal;
237              
238 0 0         $domain = _domain($domain,$_to_function,@param) if $domain;
239              
240 0 0 0       return ($domain || $domain_literal)
      0        
241             ? ($local_part.'@'.($domain || $domain_literal))
242             : ($local_part);
243             }
244              
245 0     0 1   sub domain_to_ascii { _domain(shift,\&_to_ascii) }
246 0     0 1   sub domain_to_unicode { _domain(shift,\&_to_unicode) }
247              
248 0     0 1   sub email_to_ascii { _email(shift,\&_to_ascii) }
249 0     0 1   sub email_to_unicode { _email(shift,\&_to_unicode) }
250              
251             1;
252              
253             package Net::IDN::Nameprep;
254              
255 3     3   3248 use strict;
  3         8  
  3         141  
256 3     3   17 use utf8;
  3         7  
  3         16  
257 3     3   97 use warnings;
  3         7  
  3         259  
258             require 5.006_000;
259              
260 3     3   12964 use Unicode::Stringprep;
  3         306522  
  3         471  
261              
262 3     3   45 use Unicode::Stringprep::Mapping;
  3         7  
  3         73  
263 3     3   22 use Unicode::Stringprep::Prohibited;
  3         7  
  3         648  
264              
265             *nameprep = Unicode::Stringprep->new(
266             3.2,
267             [
268             @Unicode::Stringprep::Mapping::B1,
269             @Unicode::Stringprep::Mapping::B2
270             ],
271             'KC',
272             [
273             @Unicode::Stringprep::Prohibited::C12,
274             @Unicode::Stringprep::Prohibited::C22,
275             @Unicode::Stringprep::Prohibited::C3,
276             @Unicode::Stringprep::Prohibited::C4,
277             @Unicode::Stringprep::Prohibited::C5,
278             @Unicode::Stringprep::Prohibited::C6,
279             @Unicode::Stringprep::Prohibited::C7,
280             @Unicode::Stringprep::Prohibited::C8,
281             @Unicode::Stringprep::Prohibited::C9
282             ],
283             1,
284             );
285              
286             1;