| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Parse::HTTP::UserAgent; | 
| 2 |  |  |  |  |  |  | $Parse::HTTP::UserAgent::VERSION = '0.40_02'; # TRIAL | 
| 3 |  |  |  |  |  |  |  | 
| 4 | 2 |  |  | 2 |  | 78935 | $Parse::HTTP::UserAgent::VERSION = '0.4002';use strict; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 53 |  | 
| 5 | 2 |  |  | 2 |  | 8 | use warnings; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 65 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 | 2 |  |  |  |  | 619 | use base qw( | 
| 8 |  |  |  |  |  |  | Parse::HTTP::UserAgent::Base::IS | 
| 9 |  |  |  |  |  |  | Parse::HTTP::UserAgent::Base::Parsers | 
| 10 |  |  |  |  |  |  | Parse::HTTP::UserAgent::Base::Dumper | 
| 11 |  |  |  |  |  |  | Parse::HTTP::UserAgent::Base::Accessors | 
| 12 | 2 |  |  | 2 |  | 9 | ); | 
|  | 2 |  |  |  |  | 3 |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 | 2 |  |  |  |  | 7 | use overload '""',    => 'name', | 
| 15 |  |  |  |  |  |  | '0+',    => 'version', | 
| 16 |  |  |  |  |  |  | fallback => 1, | 
| 17 | 2 |  |  | 2 |  | 10 | ; | 
|  | 2 |  |  |  |  | 3 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 | 2 |  |  | 2 |  | 647 | use version; | 
|  | 2 |  |  |  |  | 2735 |  | 
|  | 2 |  |  |  |  | 8 |  | 
| 20 | 2 |  |  | 2 |  | 135 | use Carp qw( croak ); | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 90 |  | 
| 21 | 2 |  |  | 2 |  | 11 | use Parse::HTTP::UserAgent::Constants qw(:all); | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 554 |  | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | BEGIN { | 
| 24 | 2 | 50 |  | 2 |  | 4063 | constant->import( DEBUG => 0 ) if not defined &DEBUG; | 
| 25 |  |  |  |  |  |  | } | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | my %OSFIX = ( | 
| 28 |  |  |  |  |  |  | 'WinNT4.0'       => 'Windows NT 4.0', | 
| 29 |  |  |  |  |  |  | 'WinNT'          => 'Windows NT', | 
| 30 |  |  |  |  |  |  | 'Windows 4.0'    => 'Windows 95', | 
| 31 |  |  |  |  |  |  | 'Win95'          => 'Windows 95', | 
| 32 |  |  |  |  |  |  | 'Win98'          => 'Windows 98', | 
| 33 |  |  |  |  |  |  | 'Windows 4.10'   => 'Windows 98', | 
| 34 |  |  |  |  |  |  | 'Win 9x 4.90'    => 'Windows Me', | 
| 35 |  |  |  |  |  |  | 'Windows NT 5.0' => 'Windows 2000', | 
| 36 |  |  |  |  |  |  | 'Windows NT 5.1' => 'Windows XP', | 
| 37 |  |  |  |  |  |  | 'Windows XP 5.1' => 'Windows XP', # huh? | 
| 38 |  |  |  |  |  |  | 'Windows NT 5.2' => 'Windows Server 2003', | 
| 39 |  |  |  |  |  |  | 'Windows NT 6.0' => 'Windows Vista / Server 2008', | 
| 40 |  |  |  |  |  |  | 'Windows NT 6.1' => 'Windows 7', | 
| 41 |  |  |  |  |  |  | 'Windows NT 6.2' => 'Windows 8', | 
| 42 |  |  |  |  |  |  | 'Windows NT 6.3' => 'Windows 8.1', | 
| 43 |  |  |  |  |  |  | ); | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | sub new { | 
| 46 | 554 |  |  | 554 | 1 | 540659 | my $class = shift; | 
| 47 | 554 |  | 33 |  |  | 1491 | my $ua    = shift || croak 'No user agent string specified'; | 
| 48 | 554 |  | 50 |  |  | 2151 | my $opt   = shift || {}; | 
| 49 | 554 | 50 |  |  |  | 1673 | croak 'Options must be a hash reference' if ref $opt ne 'HASH'; | 
| 50 | 554 |  |  |  |  | 1327 | my $self  = [ map { undef } 0..MAXID ]; | 
|  | 14958 |  |  |  |  | 23916 |  | 
| 51 | 554 |  |  |  |  | 1359 | bless $self, $class; | 
| 52 | 554 |  |  |  |  | 1164 | @{ $self }[ UA_STRING, UA_STRING_ORIGINAL ] = ($ua) x 2; | 
|  | 554 |  |  |  |  | 1665 |  | 
| 53 | 554 | 50 |  |  |  | 1517 | $self->[IS_EXTENDED] = exists $opt->{extended} ? $opt->{extended} : 1; | 
| 54 | 554 | 50 |  |  |  | 1266 | $self->_normalize( $opt->{normalize} ) if $opt->{normalize}; | 
| 55 | 554 |  |  |  |  | 1521 | $self->_parse; | 
| 56 | 554 |  |  |  |  | 1453 | return $self; | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | sub as_hash { | 
| 60 | 277 |  |  | 277 | 1 | 933 | my $self = shift; | 
| 61 | 277 |  |  |  |  | 385 | my %struct; | 
| 62 | 277 |  |  |  |  | 480 | foreach my $id ( $self->_object_ids ) { | 
| 63 | 6371 |  |  |  |  | 15709 | (my $name = $id) =~ s{ \A UA_ }{}xms; | 
| 64 | 6371 |  |  |  |  | 17294 | $struct{ lc $name } = $self->[ $self->$id() ]; | 
| 65 |  |  |  |  |  |  | } | 
| 66 | 277 |  |  |  |  | 3820 | return %struct; | 
| 67 |  |  |  |  |  |  | } | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | sub trim { | 
| 70 | 1352 |  |  | 1352 | 1 | 1809 | my $self = shift; | 
| 71 | 1352 |  |  |  |  | 1730 | my $s    = shift; | 
| 72 | 1352 | 50 |  |  |  | 2079 | return $s if ! $s; | 
| 73 | 1352 |  |  |  |  | 2485 | $s =~ s{ \A \s+    }{}xms; | 
| 74 | 1352 |  |  |  |  | 2328 | $s =~ s{    \s+ \z }{}xms; | 
| 75 | 1352 |  |  |  |  | 2677 | return $s; | 
| 76 |  |  |  |  |  |  | } | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | sub _normalize { | 
| 79 | 0 |  |  | 0 |  | 0 | my $self = shift; | 
| 80 | 0 |  |  |  |  | 0 | my $nopt = shift; | 
| 81 | 0 |  |  |  |  | 0 | my $type = ref $nopt; | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | my @o = ! $type            ? ':all' | 
| 84 | 0 | 0 |  |  |  | 0 | :   $type eq 'ARRAY' ? @{ $nopt } | 
|  | 0 | 0 |  |  |  | 0 |  | 
| 85 |  |  |  |  |  |  | :                      croak "Normalization option $nopt is invalid"; | 
| 86 |  |  |  |  |  |  |  | 
| 87 | 0 |  |  |  |  | 0 | my %mode      = map { $_ => 1 } @o; | 
|  | 0 |  |  |  |  | 0 |  | 
| 88 | 0 |  |  |  |  | 0 | my @all       = qw( plus_to_space trim_spaces ); | 
| 89 | 0 | 0 |  |  |  | 0 | @mode{ @all } = (1) x @all if delete $mode{':all'}; | 
| 90 |  |  |  |  |  |  |  | 
| 91 | 0 |  |  |  |  | 0 | my $s = \$self->[UA_STRING]; | 
| 92 | 0 | 0 |  |  |  | 0 | ${$s} =~ s{[+]}{ }xmsg if $mode{plus_to_space}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 93 | 0 | 0 |  |  |  | 0 | ${$s} =~ s<\s+>< >xmsg if $mode{trim_spaces}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 94 | 0 |  |  |  |  | 0 | return; | 
| 95 |  |  |  |  |  |  | } | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | sub _parse { | 
| 98 | 554 |  |  | 554 |  | 849 | my $self = shift; | 
| 99 | 554 | 50 |  |  |  | 1165 | return $self if $self->[IS_PARSED]; | 
| 100 | 554 |  |  |  |  | 1180 | $self->_do_parse( $self->_pre_parse ); | 
| 101 | 554 |  |  |  |  | 1184 | $self->[IS_PARSED] = 1; | 
| 102 | 554 | 50 |  |  |  | 1785 | $self->_post_parse if ! $self->[UA_UNKNOWN]; | 
| 103 | 554 |  |  |  |  | 716 | return; | 
| 104 |  |  |  |  |  |  | } | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | sub _pre_parse { | 
| 107 | 554 |  |  | 554 |  | 799 | my $self  = shift; | 
| 108 | 554 |  |  |  |  | 826 | my $ua    = $self->[UA_STRING]; | 
| 109 | 554 |  |  |  |  | 1214 | my $uc_ua = uc $ua; | 
| 110 |  |  |  |  |  |  |  | 
| 111 | 554 |  |  |  |  | 1436 | $self->[IS_MAXTHON] = index($uc_ua, 'MAXTHON')  != NO_IMATCH; | 
| 112 | 554 |  |  |  |  | 1034 | $self->[IS_TRIDENT] = index($uc_ua, 'TRIDENT/') != NO_IMATCH; | 
| 113 |  |  |  |  |  |  |  | 
| 114 | 554 |  |  |  |  | 755 | my @parts; | 
| 115 | 554 |  |  |  |  | 681 | my $i     = 0; | 
| 116 | 554 |  |  |  |  | 770 | my $depth = 0; | 
| 117 |  |  |  |  |  |  |  | 
| 118 | 554 |  |  |  |  | 4544 | foreach my $token ( split RE_SPLIT_PARSE, $ua ) { | 
| 119 | 3000 | 100 |  |  |  | 5210 | if ( $token eq '(' ) { | 
| 120 | 680 | 100 |  |  |  | 1226 | $i++ if ++$depth == 1; | 
| 121 | 680 |  |  |  |  | 1101 | next; | 
| 122 |  |  |  |  |  |  | } | 
| 123 | 2320 | 100 |  |  |  | 3514 | if ( $token eq ')' ) { | 
| 124 | 678 | 100 |  |  |  | 1478 | $i++ if --$depth == 0; | 
| 125 | 678 |  |  |  |  | 960 | next; | 
| 126 |  |  |  |  |  |  | } | 
| 127 | 1642 |  | 100 |  |  | 1894 | push @{ $parts[$i] ||= [] }, $token; | 
|  | 1642 |  |  |  |  | 5565 |  | 
| 128 |  |  |  |  |  |  | } | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | # Hopefully the above code was successful and now we can set the actual | 
| 131 |  |  |  |  |  |  | # tokens to use inside parsers. | 
| 132 | 554 | 50 |  |  |  | 1046 | my($moz)    = join ' ', @{ shift(@parts) || []  }; | 
|  | 554 |  |  |  |  | 1773 |  | 
| 133 | 554 | 100 |  |  |  | 1009 | my($thing)  = join ' ', @{ shift(@parts) || []  }; | 
|  | 554 |  |  |  |  | 1412 |  | 
| 134 | 554 | 100 |  |  |  | 952 | my($extra)  = join ' ', @{ shift(@parts) || []  }; | 
|  | 554 |  |  |  |  | 1500 |  | 
| 135 | 554 |  |  |  |  | 1034 | my(@others) = map { @{ $_ } } @parts; | 
|  | 250 |  |  |  |  | 315 |  | 
|  | 250 |  |  |  |  | 527 |  | 
| 136 |  |  |  |  |  |  |  | 
| 137 | 554 | 100 |  |  |  | 3143 | $thing = $thing ? [ split RE_SC_WS, $thing ] : []; | 
| 138 | 554 | 100 |  |  |  | 1772 | $extra = [ split RE_WHITESPACE, $extra ] if $extra; | 
| 139 |  |  |  |  |  |  |  | 
| 140 | 554 |  |  |  |  | 745 | $self->_debug_pre_parse( $moz, $thing, $extra, @others ) if DEBUG; | 
| 141 | 554 |  |  |  |  | 2129 | return $moz, $thing, $extra, @others; | 
| 142 |  |  |  |  |  |  | } | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | sub _do_parse { | 
| 145 | 554 |  |  | 554 |  | 1169 | my($self, $m, $t, $e, @o) = @_; | 
| 146 |  |  |  |  |  |  |  | 
| 147 | 554 |  | 100 |  |  | 1881 | my $c = $t->[0] && $t->[0] eq 'compatible'; | 
| 148 |  |  |  |  |  |  |  | 
| 149 | 554 | 100 | 66 |  |  | 1187 | if ( $c | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 150 | 212 |  |  |  |  | 1208 | && shift @{$t}                     # just inline removal of "compatible" | 
| 151 |  |  |  |  |  |  | && ( ! $e || $self->[IS_TRIDENT] ) # older versions don't have junk outside, while newer might have | 
| 152 |  |  |  |  |  |  | && ! $self->[IS_MAXTHON]           # be sure that this is not the faker | 
| 153 |  |  |  |  |  |  | ) { | 
| 154 | 150 |  |  |  |  | 544 | my($n, $v) = split RE_WHITESPACE, $t->[0]; | 
| 155 | 150 | 100 | 100 |  |  | 575 | if ( $n eq 'MSIE' && index($m, q{ }) == NO_IMATCH ) { | 
| 156 | 120 |  |  |  |  | 404 | return $self->_parse_msie($m, $t, $e, $n, $v); | 
| 157 |  |  |  |  |  |  | } | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  |  | 
| 160 | 434 | 100 |  |  |  | 869 | if ( $self->[IS_TRIDENT] ) { | 
| 161 |  |  |  |  |  |  | # http://blogs.msdn.com/b/ieinternals/archive/2013/09/21/internet-explorer-11-user-agent-string-ua-string-sniffing-compatibility-with-gecko-webkit.aspx | 
| 162 |  |  |  |  |  |  | my %msie11 = map { | 
| 163 | 164 | 100 |  |  |  | 448 | index( $_, 'Windows')  != NO_IMATCH ? ( windows => 1 ) | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | : index( $_, 'Trident/') != NO_IMATCH ? ( trident => 1 ) | 
| 165 |  |  |  |  |  |  | : index( $_, 'rv:')      != NO_IMATCH ? ( version => 1 ) | 
| 166 |  |  |  |  |  |  | : () | 
| 167 | 26 |  |  |  |  | 41 | } @{ $t }; | 
|  | 26 |  |  |  |  | 50 |  | 
| 168 | 26 |  |  |  |  | 60 | my $msie_matched = keys %msie11; | 
| 169 |  |  |  |  |  |  |  | 
| 170 | 26 | 100 | 66 |  |  | 131 | if ( $msie_matched == 3 ){ | 
|  |  | 100 | 66 |  |  |  |  | 
| 171 | 12 |  |  |  |  | 56 | return $self->_parse_msie_11($m, $t, $e); | 
| 172 |  |  |  |  |  |  | } | 
| 173 |  |  |  |  |  |  | elsif ( ! $self->[IS_MAXTHON] && $msie_matched == 2 && ! $msie11{version} ) { | 
| 174 |  |  |  |  |  |  | # another weird case. robot? | 
| 175 | 2 |  |  |  |  | 5 | my(@buf, $vstr); | 
| 176 | 2 |  |  |  |  | 5 | for my $junk ( @{ $t } ) { | 
|  | 2 |  |  |  |  | 5 |  | 
| 177 | 8 | 100 |  |  |  | 17 | if ( index( $junk, 'MSIE') != NO_IMATCH ) { | 
| 178 | 2 |  |  |  |  | 6 | $vstr = $junk; | 
| 179 | 2 |  |  |  |  | 4 | next; | 
| 180 |  |  |  |  |  |  | } | 
| 181 | 6 |  |  |  |  | 12 | push @buf, $junk; | 
| 182 |  |  |  |  |  |  | } | 
| 183 |  |  |  |  |  |  |  | 
| 184 | 2 |  |  |  |  | 12 | my $rv = $self->_parse_msie($m, \@buf, $e, split( RE_WHITESPACE, $vstr ) ); | 
| 185 | 2 |  |  |  |  | 7 | return $rv; | 
| 186 |  |  |  |  |  |  | } | 
| 187 |  |  |  |  |  |  | # fall back to the dispatch table below | 
| 188 |  |  |  |  |  |  | } | 
| 189 |  |  |  |  |  |  |  | 
| 190 | 420 | 100 |  |  |  | 1525 | my $rv =  $self->[IS_MAXTHON]          ? [ maxthon    => $m, $t, $e, @o        ] | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | : $self->_is_opera_pre($m)     ? [ opera_pre  => $m, $t, $e            ] | 
| 192 |  |  |  |  |  |  | : $self->_is_opera_post($e)    ? [ opera_post => $m, $t, $e, $c        ] | 
| 193 |  |  |  |  |  |  | : $self->_is_opera_ff($e)      ? [ opera_pre  => "$e->[2]/$e->[3]", $t ] | 
| 194 |  |  |  |  |  |  | : $self->_is_ff($e)            ? [ firefox    => $m, $t, $e, @o        ] | 
| 195 |  |  |  |  |  |  | : $self->_is_safari($e, \@o)   ? [ safari     => $m, $t, $e, @o        ] | 
| 196 |  |  |  |  |  |  | : $self->_is_chrome($e, \@o)   ? [ chrome     => $m, $t, $e, @o        ] | 
| 197 |  |  |  |  |  |  | : $self->_is_android($t,\@o)   ? [ android    => $m, $t, $e, @o        ] | 
| 198 |  |  |  |  |  |  | : $self->_is_suspicious_ff($e) ? [ ff_suspect => $m, $t, $e, @o        ] | 
| 199 |  |  |  |  |  |  | : undef; | 
| 200 |  |  |  |  |  |  |  | 
| 201 | 420 | 100 |  |  |  | 1017 | if ( $rv ) { | 
| 202 | 234 |  |  |  |  | 301 | my $pname  = shift @{ $rv }; | 
|  | 234 |  |  |  |  | 405 |  | 
| 203 | 234 |  |  |  |  | 468 | my $method = '_parse_' . $pname; | 
| 204 | 234 |  |  |  |  | 346 | my $rvx    = $self->$method( @{ $rv } ); | 
|  | 234 |  |  |  |  | 967 |  | 
| 205 | 234 | 100 |  |  |  | 484 | if ( $rvx ) { | 
| 206 | 232 |  | 66 |  |  | 763 | $self->[UA_PARSER] ||= $pname; | 
| 207 | 232 |  |  |  |  | 589 | return $rvx; | 
| 208 |  |  |  |  |  |  | } | 
| 209 |  |  |  |  |  |  | } | 
| 210 |  |  |  |  |  |  |  | 
| 211 | 188 | 50 |  |  |  | 594 | return $self->_extended_probe($m, $t, $e, $c, @o) if $self->[IS_EXTENDED]; | 
| 212 |  |  |  |  |  |  |  | 
| 213 | 0 |  |  |  |  | 0 | $self->[UA_UNKNOWN] = 1; # give up | 
| 214 | 0 |  |  |  |  | 0 | return; | 
| 215 |  |  |  |  |  |  | } | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | sub _post_parse { | 
| 218 | 554 |  |  | 554 |  | 775 | my $self = shift; | 
| 219 | 554 | 100 |  |  |  | 1356 | $self->[UA_VERSION] = $self->_numify( $self->[UA_VERSION_RAW] ) | 
| 220 |  |  |  |  |  |  | if $self->[UA_VERSION_RAW]; | 
| 221 |  |  |  |  |  |  |  | 
| 222 | 554 |  |  |  |  | 934 | my @buf; | 
| 223 | 554 |  |  |  |  | 713 | foreach my $e ( @{ $self->[UA_EXTRAS] } ) { | 
|  | 554 |  |  |  |  | 1252 |  | 
| 224 | 860 | 100 |  |  |  | 1851 | if ( $self->_is_strength( $e ) ) { | 
| 225 | 76 |  |  |  |  | 137 | $self->[UA_STRENGTH] = $e ; | 
| 226 | 76 |  |  |  |  | 156 | next; | 
| 227 |  |  |  |  |  |  | } | 
| 228 | 784 |  |  |  |  | 1514 | push @buf, $e; | 
| 229 |  |  |  |  |  |  | } | 
| 230 |  |  |  |  |  |  |  | 
| 231 | 554 | 100 |  |  |  | 1537 | $self->[UA_EXTRAS] = @buf ? [ @buf ] : undef; | 
| 232 |  |  |  |  |  |  |  | 
| 233 | 554 | 100 |  |  |  | 1134 | if ( $self->[UA_TOOLKIT] ) { | 
| 234 | 258 |  |  |  |  | 459 | my $v = $self->[UA_TOOLKIT][TK_ORIGINAL_VERSION]; | 
| 235 | 258 | 100 |  |  |  | 320 | push @{ $self->[UA_TOOLKIT] }, defined $v ? $self->_numify( $v ) : 0; | 
|  | 258 |  |  |  |  | 689 |  | 
| 236 |  |  |  |  |  |  | } | 
| 237 |  |  |  |  |  |  |  | 
| 238 | 554 | 100 |  |  |  | 1101 | if( $self->[UA_MOZILLA] ) { | 
| 239 | 88 |  |  |  |  | 156 | $self->[UA_MOZILLA] =~ tr/a-z://d; | 
| 240 | 88 |  |  |  |  | 177 | $self->[UA_MOZILLA] = [ $self->[UA_MOZILLA], | 
| 241 |  |  |  |  |  |  | $self->_numify( $self->[UA_MOZILLA] ) ]; | 
| 242 |  |  |  |  |  |  | } | 
| 243 |  |  |  |  |  |  |  | 
| 244 | 554 | 100 |  |  |  | 992 | if ( $self->[UA_OS] ) { | 
| 245 | 490 |  | 66 |  |  | 1507 | $self->[UA_OS] = $OSFIX{ $self->[UA_OS] } || $self->[UA_OS]; | 
| 246 |  |  |  |  |  |  | } | 
| 247 |  |  |  |  |  |  |  | 
| 248 | 554 |  |  |  |  | 1169 | foreach my $robo ( LIST_ROBOTS ) { # regex??? | 
| 249 | 4908 | 100 |  |  |  | 10140 | next if lc $robo ne lc $self->[UA_NAME]; | 
| 250 | 18 |  |  |  |  | 29 | $self->[UA_ROBOT] = 1; | 
| 251 | 18 |  |  |  |  | 29 | last; | 
| 252 |  |  |  |  |  |  | } | 
| 253 | 554 |  |  |  |  | 928 | return; | 
| 254 |  |  |  |  |  |  | } | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | sub _extended_probe { | 
| 257 | 188 |  |  | 188 |  | 486 | my($self, @args) = @_; | 
| 258 |  |  |  |  |  |  |  | 
| 259 | 188 | 100 | 66 |  |  | 413 | return if $self->_is_gecko             && $self->_parse_gecko(    @args ); | 
| 260 | 138 | 100 | 66 |  |  | 369 | return if $self->_is_netscape( @args ) && $self->_parse_netscape( @args ); | 
| 261 | 112 | 100 | 66 |  |  | 301 | return if $self->_is_docomo(   @args ) && $self->_parse_docomo(   @args ); | 
| 262 | 110 | 100 |  |  |  | 290 | return if $self->_is_generic(  @args ); | 
| 263 | 26 | 100 | 66 |  |  | 73 | return if $self->_is_emacs(    @args ) && $self->_parse_emacs(    @args ); | 
| 264 | 22 | 100 | 66 |  |  | 66 | return if $self->_is_moz_only( @args ) && $self->_parse_moz_only( @args ); | 
| 265 | 2 | 50 | 33 |  |  | 23 | return if $self->_is_hotjava(  @args ) && $self->_parse_hotjava(  @args ); | 
| 266 |  |  |  |  |  |  |  | 
| 267 | 0 |  |  |  |  | 0 | $self->[UA_UNKNOWN] = 1; | 
| 268 | 0 |  |  |  |  | 0 | return; | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | sub _object_ids { | 
| 272 | 554 |  |  | 554 |  | 10619 | return grep { $_ =~ RE_OBJECT_ID } keys %Parse::HTTP::UserAgent::; | 
|  | 74954 |  |  |  |  | 122381 |  | 
| 273 |  |  |  |  |  |  | } | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | sub _numify { | 
| 276 | 916 |  |  | 916 |  | 1252 | my $self = shift; | 
| 277 | 916 |  | 50 |  |  | 1645 | my $v    = shift || return 0; | 
| 278 | 916 |  |  |  |  | 1219 | my @removed; | 
| 279 |  |  |  |  |  |  |  | 
| 280 | 916 | 100 |  |  |  | 4014 | if ( | 
| 281 |  |  |  |  |  |  | $v =~ s{( | 
| 282 |  |  |  |  |  |  | pre      | | 
| 283 |  |  |  |  |  |  | rel      | | 
| 284 |  |  |  |  |  |  | alpha    | | 
| 285 |  |  |  |  |  |  | beta     | | 
| 286 |  |  |  |  |  |  | \-stable | | 
| 287 |  |  |  |  |  |  | gold     | | 
| 288 |  |  |  |  |  |  | [ab]\d+  | | 
| 289 |  |  |  |  |  |  | a\-XXXX  | | 
| 290 |  |  |  |  |  |  | dev      | | 
| 291 |  |  |  |  |  |  | [+] | 
| 292 |  |  |  |  |  |  | )}{}xmsig | 
| 293 |  |  |  |  |  |  | ){ | 
| 294 | 46 |  |  |  |  | 80 | push @removed, $1 if INSIDE_VERBOSE_TEST; | 
| 295 |  |  |  |  |  |  | } | 
| 296 |  |  |  |  |  |  |  | 
| 297 | 916 | 100 |  |  |  | 3174 | if ( | 
| 298 |  |  |  |  |  |  | $v =~ s{( | 
| 299 |  |  |  |  |  |  | (?:[^0-9]+)? # usually dash | 
| 300 |  |  |  |  |  |  | rc           # nonsense | 
| 301 |  |  |  |  |  |  | [\-_.]?      # usually dash | 
| 302 |  |  |  |  |  |  | ([0-9])      # teh candidate revision | 
| 303 |  |  |  |  |  |  | )}{.0.$2}xmsi    # yeah, hacky | 
| 304 |  |  |  |  |  |  | ) { | 
| 305 | 2 |  |  |  |  | 5 | push @removed, $1 if INSIDE_VERBOSE_TEST; | 
| 306 |  |  |  |  |  |  | } | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | # workaround another stupidity (1.2.3-4) | 
| 309 | 916 | 100 |  |  |  | 2009 | if ( my $rc = $v =~ tr/-/./ ) { | 
| 310 | 2 |  |  |  |  | 4 | push @removed, '-' x $rc if INSIDE_VERBOSE_TEST; | 
| 311 |  |  |  |  |  |  | } | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | # convert _ to . | 
| 314 |  |  |  |  |  |  | # version.pm has changed its interpretation of versions with underlines | 
| 315 |  |  |  |  |  |  | # cf. https://bugs.debian.org/825611 | 
| 316 | 916 | 100 |  |  |  | 1705 | if ( my $rc = $v =~ tr/_/./ ) { | 
| 317 | 2 |  |  |  |  | 4 | push @removed, '-' x $rc if INSIDE_VERBOSE_TEST; | 
| 318 |  |  |  |  |  |  | } | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | # Finally, be aggressive to prevent dying on bogus stuff. | 
| 321 |  |  |  |  |  |  | # It's interesting how people provide highly stupid version "numbers". | 
| 322 |  |  |  |  |  |  | # Version parameters are probably more stupid than the UA string itself. | 
| 323 | 916 | 100 |  |  |  | 2006 | if ( $v =~ s<([^0-9._v])><.>xmsg ) { | 
| 324 | 2 |  |  |  |  | 6 | push @removed, $1 if INSIDE_VERBOSE_TEST; | 
| 325 |  |  |  |  |  |  | } | 
| 326 |  |  |  |  |  |  |  | 
| 327 | 916 | 50 |  |  |  | 1838 | if ( $v =~ s<([.]{2,})><.>xmsg ) { | 
| 328 | 0 |  |  |  |  | 0 | push @removed, $1 if INSIDE_VERBOSE_TEST; | 
| 329 |  |  |  |  |  |  | } | 
| 330 |  |  |  |  |  |  |  | 
| 331 | 916 |  |  |  |  | 1073 | if ( INSIDE_VERBOSE_TEST ) { | 
| 332 |  |  |  |  |  |  | if ( @removed ) { | 
| 333 |  |  |  |  |  |  | my $r = join q{','}, @removed; | 
| 334 |  |  |  |  |  |  | require Test::More; | 
| 335 |  |  |  |  |  |  | Test::More::diag("[DEBUG] _numify: removed '$r' from version string"); | 
| 336 |  |  |  |  |  |  | } | 
| 337 |  |  |  |  |  |  | } | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | # Gecko revisions like: "20080915000512" will cause an | 
| 340 |  |  |  |  |  |  | #   integer overflow warning. use bigint? | 
| 341 |  |  |  |  |  |  | local $SIG{__WARN__} = sub { | 
| 342 | 2 |  |  | 2 |  | 5 | my $msg = shift; | 
| 343 | 2 | 50 | 33 |  |  | 22 | warn "$msg\n" if $msg !~ RE_WARN_OVERFLOW && $msg !~ RE_WARN_INVALID; | 
| 344 | 916 |  |  |  |  | 5229 | }; | 
| 345 |  |  |  |  |  |  | # if version::vpp is used it'll identify 420 as a v-string | 
| 346 |  |  |  |  |  |  | # add a floating point to fool it | 
| 347 | 916 | 100 |  |  |  | 2350 | $v .= q{.0} if index($v, q{.}) == NO_IMATCH; | 
| 348 | 916 |  |  |  |  | 1564 | (my $check = $v) =~ tr/0-9//cd; | 
| 349 | 916 | 50 |  |  |  | 1624 | return 0 if ! $check; # A string parsed as version (i.e.: AppleWebKit/en_SG) | 
| 350 | 916 |  |  |  |  | 1096 | my $rv; | 
| 351 |  |  |  |  |  |  | eval { | 
| 352 | 916 |  |  |  |  | 7985 | $rv = version->new("$v")->numify; | 
| 353 | 916 |  |  |  |  | 3046 | 1; | 
| 354 | 916 | 50 |  |  |  | 1220 | } or do { | 
| 355 | 0 |  | 0 |  |  | 0 | my $error = $@ || '[unknown error while parsing version]'; | 
| 356 | 0 |  |  |  |  | 0 | if ( INSIDE_UNIT_TEST ) { | 
| 357 | 0 |  |  |  |  | 0 | chomp $error; | 
| 358 | 0 |  |  |  |  | 0 | if ( INSIDE_VERBOSE_TEST ) { | 
| 359 |  |  |  |  |  |  | Test::More::diag( "[FATAL] _numify: version said: $error for '$v'" ); | 
| 360 |  |  |  |  |  |  | Test::More::diag( | 
| 361 |  |  |  |  |  |  | sprintf '[FATAL] _numify: UA with bogus version (%s) is: %s', | 
| 362 |  |  |  |  |  |  | $v, $self->[UA_STRING] | 
| 363 |  |  |  |  |  |  | ); | 
| 364 |  |  |  |  |  |  | Test::More::diag( '[FATAL] _numify: ' . $self->dumper ); | 
| 365 |  |  |  |  |  |  | } | 
| 366 | 0 |  |  |  |  | 0 | croak $error; | 
| 367 |  |  |  |  |  |  | } | 
| 368 |  |  |  |  |  |  | else { | 
| 369 |  |  |  |  |  |  | croak $error; | 
| 370 |  |  |  |  |  |  | } | 
| 371 |  |  |  |  |  |  | }; | 
| 372 | 916 |  |  |  |  | 4795 | return $rv; | 
| 373 |  |  |  |  |  |  | } | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  | sub _debug_pre_parse { | 
| 376 | 0 |  |  | 0 |  |  | my($self, $moz, $thing, $extra, @others) = @_; | 
| 377 |  |  |  |  |  |  |  | 
| 378 | 0 |  |  |  |  |  | my $raw = [ | 
| 379 |  |  |  |  |  |  | { qw/ name moz    value / => $moz     }, | 
| 380 |  |  |  |  |  |  | { qw/ name thing  value / => $thing   }, | 
| 381 |  |  |  |  |  |  | { qw/ name extra  value / => $extra   }, | 
| 382 |  |  |  |  |  |  | { qw/ name others value / => \@others }, | 
| 383 |  |  |  |  |  |  | ]; | 
| 384 | 0 |  |  |  |  |  | my $pok = print "-------------- PRE PARSE DUMP --------------\n" | 
| 385 |  |  |  |  |  |  | . $self->dumper(args => $raw) | 
| 386 |  |  |  |  |  |  | . "--------------------------------------------\n"; | 
| 387 | 0 |  |  |  |  |  | return; | 
| 388 |  |  |  |  |  |  | } | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | 1; | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | __END__ |