File Coverage

blib/lib/Net/ParseWhois/Domain/Registrar.pm
Criterion Covered Total %
statement 10 239 4.1
branch 2 172 1.1
condition 0 19 0.0
subroutine 3 28 10.7
pod 0 27 0.0
total 15 485 3.0


line stmt bran cond sub pod time code
1             # Program: Net::ParseWhois base registrar class
2             # Version: 0.2
3             # Purpose: Provides the base class definition for all the registrar
4             # sub-classes. Methods defined here are over-ridden by the child
5             # classes as needed for each particular registrar. By default,
6             # this base class attempts to parse output of a Network Solutions
7             # WHOIS server.
8             # Updated: 11/21/2005 by Jeff Mercer
9              
10             package Net::ParseWhois::Domain::Registrar;
11             require 5.004;
12 1     1   4 use strict;
  1         2  
  1         2662  
13              
14             $Net::ParseWhois::Domain::Registrar::VERSION = 0.2;
15             @Net::ParseWhois::Domain::Registrar::ISA = qw(Net::ParseWhois::Domain);
16              
17             # used by new to import vals into $self->{} in specific registrar classes
18 1     1 0 4 sub my_data {}
19              
20             sub registrar_data {
21             {
22 0     0 0 0 'whois.dotster.com' => {
23             'registrar_tag' => 'DOTSTER, INC.',
24             'referral_tag' => 'http://www.dotster.com/help/whois',
25             'class' => 'Dotster' },
26             'whois.register.com' => {
27             'registrar_tag' => 'REGISTER.COM, INC.',
28             'referral_tag' => 'www.register.com',
29             'class' => 'Register' },
30             'whois.networksolutions.com' => {
31             'registrar_tag' => 'NETWORK SOLUTIONS, INC.',
32             'referral_tag' => 'www.networksolutions.com',
33             'class' => 'Netsol' },
34             'whois.opensrs.net' => {
35             'registrar_tag' => 'TUCOWS.COM, INC.',
36             'referral_tag' => 'www.opensrs.net',
37             'class' => 'OpenSRS' },
38             'whois.domaindiscover.com' => {
39             'registrar_tag' => 'TIERRANET, INC.',
40             'referral_tag' => 'www.domaindiscover.com',
41             'class' => 'DomainDiscover' },
42             'whois.bulkregister.com' => {
43             'registrar_tag' => 'BULKREGISTER.COM, INC.',
44             'referral_tag' => 'www.bulkregister.com',
45             'class' => 'BulkRegister' },
46             'rs.domainbank.net' => {
47             'registrar_tag' => 'DOMAIN BANK, INC.',
48             'referral_tag' => 'www.domainbank.net',
49             'class' => 'DomainBank' },
50             'whois.registrars.com' => {
51             'registrar_tag' => 'INTERNET DOMAIN REGISTRARS',
52             'referral_tag' => 'www.registrars.com',
53             'class' => 'Registrars' },
54             'whois.corenic.net' => {
55             'registrar_tag' => 'CORE INTERNET COUNCIL OF REGISTRARS',
56             'referral_tag' => 'www.corenic.net',
57             'class' => 'CoreNic' },
58             'whois.melbourneit.com' => {
59             'registrar_tag' => 'MELBOURNE IT, LTD. D/B/A INTERNET NAMES WORLDWIDE',
60             'referral_tag' => 'www.InternetNamesWW.com',
61             'class' => 'INameWW' },
62             'whois.easyspace.com' => {
63             'registrar_tag' => 'EASYSPACE LTD',
64             'referral_tag' => 'www.easyspace.com',
65             'class' => 'Easyspace' },
66             'whois.publicinterestregistry.net' => {
67             'registrar_tag' => 'PUBLIC INTEREST REGISTRY',
68             'referral_tag' => 'www.pir.org',
69             'class' => 'PIR' },
70             'whois.srsplus.com' => {
71             'registrar_tag' => 'TLDs, LLC',
72             'referral_tag' => 'www.srsplus.com',
73             'class' => 'SRSPlus' },
74             'whois.godaddy.com' => {
75             'registrar_tag' => 'GO DADDY SOFTWARE, INC.',
76             'referral_tag' => 'www.godaddy.com',
77             'class' => 'GoDaddy' },
78             'whois.enom.com' => {
79             'registrar_tag' => 'ENOM, INC.',
80             'referral_tag' => 'www.enom.com',
81             'class' => 'Enom' },
82             'whois.namesecure.com' => {
83             'registrar_tag' => 'NAMESECURE LLC',
84             'referral_tag' => 'www.namesecure.com',
85             'class' => 'NameSecure' },
86             'whois.namejuice.com' => {
87             'registrar_tag' => 'DOMAIN REGISTRY GROUP INC',
88             'referral_tag' => 'www.namejuice.com',
89             'class' => 'NameJuice' },
90             'whois.namescout.com' => {
91             'registrar_tag' => 'NAMESCOUT CORP',
92             'referral_tag' => 'www.namescout.com',
93             'class' => 'NameScout' },
94             'unknown_registrar' => {
95             'registrar_tag' => 'Unknown',
96             'referral_tag' => 'n/a',
97             'class' => 'Unknown' }
98             }
99             # see perldoc Net::ParseWhois section 'REGISTRARS'
100             }
101              
102              
103             # Try and parse out all the garbage before the actual domain registration
104             # info. Mostly skipping useless legal boilerplate and the like. --jcm
105             sub parse_start {
106             # Initialization
107 0     0 0 0 my $self = shift;
108 0         0 my $text = shift;
109 0         0 my $t = shift @{ $text };
  0         0  
110 0 0       0 warn "DEBUG: parse_start() running\n" if $self->debug;
111              
112             # Keep going through raw text until we find our starting point
113 0   0     0 until (!defined $t || $t =~ /$self->{'regex_org_start'}/ ||
      0        
114 0         0 $t =~ /$self->{'regex_no_match'}/) { $t = shift @{$text}; }
  0         0  
115              
116             #trim leading whitespace
117 0         0 $t =~ s/^\s//;
118              
119             # Skip to next line if this line is blank
120 0 0       0 $t = shift @{ $text } if ($t eq '');
  0         0  
121              
122             # If we find a match for the start of registrant data...
123 0 0       0 if ($t =~ /$self->{'regex_org_start'}/) {
    0          
124             # Prep the next input line and mark as a Match
125 0         0 $t = shift @{ $text };
  0         0  
126 0         0 $self->{'MATCH'} = 1;
127             # since we have a referral, this should never get caught. --aai
128             } elsif ($t =~ /$self->{'regex_no_match'}/) {
129 0         0 $self->{'MATCH'} = 0;
130             }
131              
132             # Did we find a match?
133 0 0       0 if ($self->{'MATCH'} ) {
134             # Attempt to parse out registrant name, and tag if any
135 0 0       0 if ($t =~ /^(.*)$/) {
136 0         0 $self->{'NAME'} = $1;
137 0 0       0 if ($self->{'NAME'} =~ /^(.*)\s+\((\S+)\)$/) {
138 0         0 $self->{'NAME'} = $1;
139 0         0 $self->{'TAG'} = $2;
140             }
141             } else {
142 0         0 die "Registrant Name not found in returned information\n";
143             }
144             }
145              
146 0 0       0 warn "DEBUG: parse_start() ending\n" if $self->debug;
147             }
148              
149              
150             # Attempt to parse the organizational entity that has registered the domain.
151             # (I.E. the domain owner or registrant)
152             sub parse_org {
153             # Initialization
154 0     0 0 0 my $self = shift;
155 0         0 my $text = shift;
156 0         0 my (@t, $c, $t);
157 0         0 @t = ();
158 0 0       0 warn "DEBUG: parse_org() running\n" if $self->debug;
159              
160             # read in text until next empty line
161 0         0 push @t, shift @{ $text } while ${ $text }[0];
  0         0  
  0         0  
162              
163             # If a position for country info (in the registrant block) is defined
164 0 0       0 if ($self->{'my_country_position'}) {
165             # Extract country info
166 0         0 $t = $t[$#t - $self->{'my_country_position'}];
167             } else {
168             # Set $t to the last line in the array, which will be the
169             # last line before a blank line.
170 0         0 $t = $t[$#t];
171             }
172              
173             # Try and figure out appropriate country code, if available
174 0 0       0 if (!defined $t) {
    0          
    0          
    0          
    0          
175             # do nothing
176             # USA! USA!
177             } elsif ($t =~ /^(?:usa|u\.\s*s\.\s*a\.)$/i) {
178 0         0 pop @t;
179 0         0 $t = 'US';
180             } elsif ($self->code2country($t)) {
181 0         0 pop @t;
182 0         0 $t = uc $t;
183             } elsif ($c = $self->country2code($t)) {
184 0         0 pop @t;
185 0         0 $t = uc $c;
186             } elsif ($t =~ /,\s*([^,]+?)(?:\s+\d{5}(?:-\d{4})?)?$/) {
187             # TODO - regex is too rigid. lots of times this shouldn't be matched
188             # because a tel/fax line exists after address3/city,state zip ..
189 0 0       0 $t = $self->US_State->{uc $1} ? 'US' : undef;
190             } else {
191 0         0 undef $t;
192             }
193              
194             # Return registrant address and country info
195 0         0 $self->{ADDRESS} = [@t];
196 0         0 $self->{COUNTRY} = $t;
197              
198 0 0       0 warn "DEBUG: parse_org() ending\n" if $self->debug;
199             }
200              
201             # Try and parse out all the contacts data. This is rather loose in that it
202             # doesn't do any sub-parsing but just returns fat blocks of data. A future
203             # improvement would be to break it down into name, e-mail, address, etc.
204             # --jcm
205             sub parse_contacts {
206             # Initialization
207 0     0 0 0 my ($self, $text) = @_;
208 0         0 my ($done, $t, $blah, $ck);
209 0         0 my (@ctypes, @c);
210 0 0       0 warn "DEBUG: parse_contacts() running\n" if $self->debug;
211              
212             # As long as we have text to eat...
213 0         0 while (@{ $text }) {
  0         0  
214             # Check to see if all the contacts have been filled in
215 0         0 $done = 1;
216 0         0 foreach $ck (@{ $self->{'my_contacts'} }) {
  0         0  
217 0 0       0 warn "DEBUG: ck=$ck\n" if $self->debug;
218 0 0       0 unless ($self->{CONTACTS}->{uc($ck)}) { $done = 0; }
  0         0  
219             }
220 0 0       0 last if $done;
221              
222             # Grab next line of test, skip it if blank
223 0         0 $t = shift(@{ $text });
  0         0  
224 0 0       0 warn "DEBUG: t = $t\n" if $self->debug;
225 0 0       0 next if $t=~ /^$/;
226              
227              
228             # If this line is a contact header...
229 0 0       0 if ($t =~ /contact.*:$/i) {
230             # Figure out what contact type(s) it's for
231 0 0       0 warn "DEBUG: Matched against /contact.*:/ regex\n" if $self->debug;
232 0         0 @ctypes = ($t =~ /\b(\S+) contact/ig);
233 0         0 @c=();
234 0 0       0 if ($self->debug) {
235 0 0       0 printf "DEBUG: ctypes=%d\n", $#ctypes+1 if $self->debug;
236 0         0 foreach (@ctypes) {
237 0         0 warn "DEBUG: ctypes contains=$_\n";
238             }
239             }
240              
241             # Uh... Not sure what the point of this is. --jcm, 11/16/05
242 0 0       0 if ($self->{'my_contacts_extra_line'}) {
243 0         0 $blah = shift(@{ $text });
  0         0  
244             }
245              
246             # Eat all the text until the next contact line and
247             # store it in hash
248 0         0 while ( ${ $text }[0] ) {
  0         0  
249 0 0       0 warn "DEBUG: text[0]=${$text}[0]\n" if $self->debug;
  0         0  
250 0 0       0 last if ${ $text }[0] =~ /contact.*:$/i;
  0         0  
251 0         0 push @c, shift @{ $text };
  0         0  
252             }
253              
254             # Take our contacts hash and map it to our objects
255             # CONTACTS hash. Only I think this is foobar...
256 0 0       0 printf "DEBUG: c=%d\n", $#c+1 if $self->debug;
257 0         0 foreach (@ctypes) { @{$self->{CONTACTS}{uc $_}}=@c; }
  0         0  
  0         0  
258             }
259             }
260              
261 0 0       0 warn "DEBUG: parse_contacts() ending\n" if $self->debug;
262             }
263              
264             # Parse out the nameservers
265             sub parse_nameservers {
266             # Initialization
267 0     0 0 0 my ($self, $text) = @_;
268 0         0 my ($t, $dns, $key);
269 0         0 my (@s, @temp);
270 0 0       0 warn "DEBUG: parse_nameservers() running\n" if $self->debug;
271 0 0       0 warn "DEBUG: text = $text, size = $#{$text}\n" if $self->debug;
  0         0  
272              
273             # As long as there's text in the array...
274 0 0       0 warn "DEBUG: Starting text processing loop...\n" if $self->debug;
275 0         0 while (@{ $text }) {
  0         0  
276             # Done if we've got the nameservers already
277 0 0       0 if ($self->{SERVERS}) {
278 0 0       0 warn "DEBUG: Servers defined, we're done.\n" if $self->debug;
279 0         0 last;
280             }
281              
282             # Grab next line of text
283 0         0 $t = shift(@{ $text });
  0         0  
284 0 0       0 warn "DEBUG: t = $t\n" if $self->debug;
285              
286             # Skip to next line if current line is blank
287 0 0       0 next if $t =~ /^$/;
288              
289             # If we get a match for our nameserver regex pattern...
290 0 0       0 if ($t =~ /$self->{'regex_nameservers'}/) {
291 0 0       0 warn "DEBUG: Matched $self->{'regex_nameservers'} regex pattern\n" if $self->debug;
292              
293             # HMMM??
294 0 0       0 shift @{ $text } unless ${ $text }[0];
  0         0  
  0         0  
295              
296 0         0 while ($t = shift @{ $text }) {
  0         0  
297 0 0       0 if ($self->{'my_nameservers_noips'}) {
298 0         0 @temp = [ $t, $self->na ];
299 0         0 push @s, @temp;
300 0 0       0 warn "DEBUG: Nameserver with no IP\n" if $self->debug;
301             } else {
302 0         0 push @s, [split /\s+/, $t];
303 0 0       0 warn "DEBUG: Nameserver with IP\n" if $self->debug;
304             }
305             }
306 0         0 $self->{SERVERS} = \@s;
307              
308 0 0       0 if ($self->debug) {
309 0         0 foreach $dns (@s) { warn "DEBUG: DNS server = $dns\n"; }
  0         0  
310             }
311             }
312             }
313              
314 0 0       0 warn "DEBUG: parse_nameservers() ending\n" if $self->debug;
315             }
316              
317             # Parse out dates on when domain created, expires, and updated. Except
318             # NetSol doesn't give out when a domain was last updated. Some registrars
319             # might but that check is removed for now until script is stablized
320             # --jcm
321             # Ok, adding updated check back in, need to make sure it won't break for
322             # those registrars that don't provide the info (i.e. don't assume
323             # regex_expired exists!) --jcm, 11/16/05
324             #
325             sub parse_domain_stats {
326             # Initialization
327 0     0 0 0 my ($self, $text) = @_;
328 0         0 my $t;
329              
330 0 0       0 warn "DEBUG: parse_domain_stats() running\n" if $self->debug;
331              
332             # As long as there's text to read...
333 0         0 while (@{ $text}) {
  0         0  
334             # Done if all three stats are defined
335 0 0 0     0 last if ($self->{RECORD_CREATED} && $self->{RECORD_UPDATED} && $self->{RECORD_EXPIRES});
      0        
336              
337             # Grab next line of text, skip to next if blank
338 0         0 $t = shift(@{ $text });
  0         0  
339 0 0       0 next if $t=~ /^$/;
340              
341 0 0       0 warn "DEBUG: t = $t\nDEBUG: RECORD_CREATED = $self->{RECORD_CREATED}\nDEBUG: RECORD_UPDATED = $self->{RECORD_UPDATED}\nDEBUG: RECORD_EXPIRES = $self->{RECORD_EXPIRES}\n" if $self->debug;
342              
343             # If we match against any of our regex patterns, store the
344             # the result in the appropriate parameter.
345 0 0       0 if ($t =~ /$self->{'regex_created'}/) {
    0          
    0          
346 0         0 $self->{RECORD_CREATED} = $1;
347             } elsif ($t =~ /$self->{'regex_updated'}/) {
348 0         0 $self->{RECORD_UPDATED} = $1;
349             } elsif ($t =~ /$self->{'regex_expires'}/) {
350 0         0 $self->{RECORD_EXPIRES} = $1;
351             }
352             }
353              
354 0 0       0 warn "DEBUG: parse_domain_stats() ending\n" if $self->debug;
355             }
356              
357             # Parse out the domain name (which we already have of course, so not sure
358             # why we bother with this...) --jcm
359             sub parse_domain_name {
360             # Initialization
361 0     0 0 0 my $self = shift;
362 0         0 my $text = shift;
363 0         0 my $t;
364 0 0       0 warn "DEBUG: parse_domain_name() running\n" if $self->debug;
365              
366             # As long as there's text to read...
367 0         0 while (@{ $text}) {
  0         0  
368             # Done if the domain name has been found
369 0 0       0 last if ($self->{DOMAIN});
370              
371             # Grab next line of text, skip if it's blank
372 0         0 $t = shift(@{ $text });
  0         0  
373 0 0       0 next if $t=~ /^$/;
374              
375             # If we match our domain name regex pattern...
376 0 0       0 if ($t =~ /$self->{'regex_domain'}/) {
377             # Define our domain value accordingly.
378 0         0 $self->{DOMAIN} = $1;
379             }
380             }
381              
382 0 0       0 warn "DEBUG: parse_domain_name() ending\n" if $self->debug;
383             }
384              
385             # Create a new instance of this object class (Net::ParseWhois)
386             sub new {
387 1     1 0 47 my $class = shift;
388 1         2 my $ref = shift;
389 1 50       3 my %hash = %{ $ref } if ($ref);
  0         0  
390 1         4 my $obj = bless ( \%hash, $class );
391            
392 1 50       4 if (defined $obj->my_data) {
393 0         0 foreach my $field (@{ $obj->my_data }) {
  0         0  
394 0         0 $obj->{$field} = $obj->$field();
395             }
396             }
397              
398 1         3 return $obj;
399             }
400            
401             # Return a value of "Not applicable".
402             sub na {
403 0     0 0   return "n/a";
404             }
405              
406             # Subroutine to follow a referral on an object
407             sub follow_referral {
408             # Initialization
409 0     0 0   my $self = shift;
410 0 0         warn "DEBUG: follow_referral() running\n" if $self->debug;
411              
412             # Try and connect to whois server
413 0           $self->{'base_server_name'} = $self->whois_server;
414 0   0       my $sock = $self->_connect || die "unable to open connection\n";
415 0           my $text = $self->_send_to_sock( $sock );
416              
417             # Grab the raw whois text and store it for parsing by other routines
418 0           $self->{RAW_WHOIS_TEXT} = join("\n", @{ $text } );
  0            
419              
420             # If this was an unknown registrar...
421 0 0         if ($self->unknown_registrar) {
422             # don't parse, just return $self with raw data
423 0           $self->{MATCH} = 1;
424 0 0         warn "DEBUG: follow_referral() ending\n" if $self->debug;
425 0           return $self;
426             } else {
427             # Return with the parsed text (we hope)
428 0 0         warn "DEBUG: follow_referral() ending\n" if $self->debug;
429 0           $self->parse_text($text);
430             }
431             }
432              
433             # Return the current whois server
434             sub whois_server {
435 0     0 0   my $self = shift;
436 0 0         warn "DEBUG: whois_server() running\n" if $self->debug;
437              
438 0           return $self->{'whois_referral'};
439              
440 0 0         warn "DEBUG: whois_server() ending\n" if $self->debug;
441             }
442              
443             # Dump all of the registry data returned from the whois server
444             sub dump_text {
445             # Initialization
446 0     0 0   my $self = shift;
447 0           my $text = shift;
448 0 0         warn "DEBUG: dump_text() running\n" if $self->debug;
449              
450 0 0         if ($self->debug) {
451 0           warn "DEBUG: raw registry data:\n";
452 0           warn "DEBUG: ----------------------------------\n";
453 0           foreach (@{ $text }) { warn "DEBUG: \"$_\"\n"; }
  0            
  0            
454 0           warn "DEBUG: ----------------------------------\n";
455 0           warn "DEBUG: end registry data.\n";
456             }
457              
458 0 0         warn "DEBUG: dump_text() ending\n" if $self->debug;
459             }
460              
461             # This subroutine *should* be overloaded by the particular Registrar class
462             # being used. If not, then this code here runs and program exits.
463             sub parse_text {
464             # Initialization
465 0     0 0   my $self = shift;
466 0           my $text = shift;
467 0 0         warn "DEBUG: parse_text() running\n" if $self->debug;
468              
469 0 0         warn "DEBUG: \$self->parse_text NOT defined. Dumping data, and then dieing.\n" if $self->debug;
470              
471 0           foreach my $line (@{ $text }) {
  0            
472 0           print "$line\n";
473             }
474              
475             #TODO get rid of die ..
476 0           die "$self->parse_text not defined.\n";
477              
478 0           return $self;
479              
480 0 0         warn "DEBUG: parse_text() ending\n" if $self->debug;
481             }
482              
483              
484             # TODO
485             # all of the below is silly. Via these accessor methods we should also be
486             # setting the values, rather than using UPPERCASE hash keys in $self.
487             # or these should be named get_domain, get_name, etc.
488             # right .. ? --aai 12/05/00
489              
490             sub domain {
491 0     0 0   my $self = shift;
492 0 0         $self->{DOMAIN} || $self->na;
493             }
494              
495             sub name {
496 0     0 0   my $self = shift;
497 0 0         $self->{NAME} || $self->na;
498             }
499              
500             sub tag {
501 0     0 0   my $self = shift;
502 0 0         $self->{TAG} || $self->na;
503             }
504              
505             sub address {
506 0     0 0   my $self = shift;
507 0   0       my $addr = $self->{ADDRESS} || [ $self->na ];
508 0 0         wantarray ? @ $addr : join "\n", @$addr;
509             }
510              
511             sub country {
512 0     0 0   my $self = shift;
513 0 0         $self->{COUNTRY} || $self->na;
514             }
515              
516             sub contacts {
517 0     0 0   my $self = shift;
518 0 0         $self->{CONTACTS} || { $self->na };
519             }
520              
521             sub registrar {
522 0     0 0   my $self = shift;
523 0   0       return $self->{'registrar_tag'} || $self->na;
524             }
525              
526             sub servers {
527 0     0 0   my $self = shift;
528 0 0         if (!$self->{SERVERS}) { # TODO: yuck ..
529 0           my (@tmp, @ret);
530 0           push(@tmp, $self->na);
531 0           push(@tmp, $self->na);
532 0           my $ref = \@tmp;
533 0           push(@ret, $ref);
534 0           return \@ret;
535             }
536              
537 0           return $self->{SERVERS};
538             }
539              
540             sub record_created {
541 0     0 0   my $self = shift;
542 0 0         $self->{RECORD_CREATED} || $self->na;
543             }
544              
545             sub record_updated {
546 0     0 0   my $self = shift;
547 0 0         $self->{RECORD_UPDATED} || $self->na;
548             }
549              
550             sub record_expires {
551 0     0 0   my $self = shift;
552 0 0         $self->{RECORD_EXPIRES} || $self->na;
553             }
554              
555             sub raw_whois_text {
556 0     0 0   my $self = shift;
557 0 0         $self->{RAW_WHOIS_TEXT} || $self->na;
558             }
559              
560             sub unknown_registrar {
561 0     0 0   my $self = shift;
562 0 0         $self->{UNKNOWN_REGISTRAR} || '0';
563             }
564              
565             1;