line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Geo::SypexGeo; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.6'; |
4
|
|
|
|
|
|
|
|
5
|
2
|
|
|
2
|
|
13381
|
use strict; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
44
|
|
6
|
2
|
|
|
2
|
|
6
|
use warnings; |
|
2
|
|
|
|
|
1
|
|
|
2
|
|
|
|
|
37
|
|
7
|
2
|
|
|
2
|
|
10
|
use utf8; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
6
|
|
8
|
2
|
|
|
2
|
|
49
|
use v5.10; |
|
2
|
|
|
|
|
4
|
|
9
|
|
|
|
|
|
|
|
10
|
2
|
|
|
2
|
|
8
|
use Carp qw( croak ); |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
89
|
|
11
|
2
|
|
|
2
|
|
910
|
use Encode; |
|
2
|
|
|
|
|
12964
|
|
|
2
|
|
|
|
|
106
|
|
12
|
2
|
|
|
2
|
|
985
|
use Socket; |
|
2
|
|
|
|
|
5495
|
|
|
2
|
|
|
|
|
647
|
|
13
|
2
|
|
|
2
|
|
815
|
use POSIX; |
|
2
|
|
|
|
|
7973
|
|
|
2
|
|
|
|
|
7
|
|
14
|
2
|
|
|
2
|
|
4226
|
use Text::Trim; |
|
2
|
|
|
|
|
793
|
|
|
2
|
|
|
|
|
89
|
|
15
|
2
|
|
|
2
|
|
614
|
use Geo::SypexGeo::Info; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
62
|
|
16
|
|
|
|
|
|
|
|
17
|
2
|
|
|
|
|
8
|
use fields qw( |
18
|
|
|
|
|
|
|
db_file b_idx_str m_idx_str range b_idx_len m_idx_len db_items id_len |
19
|
|
|
|
|
|
|
block_len max_region max_city db_begin regions_begin cities_begin |
20
|
|
|
|
|
|
|
max_country country_size pack |
21
|
2
|
|
|
2
|
|
850
|
); |
|
2
|
|
|
|
|
1951
|
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
use constant { |
24
|
2
|
|
|
|
|
1178
|
HEADER_LENGTH => 40, |
25
|
2
|
|
|
2
|
|
177
|
}; |
|
2
|
|
|
|
|
2
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
my @COUNTRY_ISO_MAP = ( |
28
|
|
|
|
|
|
|
'', 'ap', 'eu', 'ad', 'ae', 'af', 'ag', 'ai', 'al', 'am', 'cw', 'ao', |
29
|
|
|
|
|
|
|
'aq', 'ar', 'as', 'at', 'au', 'aw', 'az', 'ba', 'bb', 'bd', 'be', 'bf', |
30
|
|
|
|
|
|
|
'bg', 'bh', 'bi', 'bj', 'bm', 'bn', 'bo', 'br', 'bs', 'bt', 'bv', 'bw', |
31
|
|
|
|
|
|
|
'by', 'bz', 'ca', 'cc', 'cd', 'cf', 'cg', 'ch', 'ci', 'ck', 'cl', 'cm', |
32
|
|
|
|
|
|
|
'cn', 'co', 'cr', 'cu', 'cv', 'cx', 'cy', 'cz', 'de', 'dj', 'dk', 'dm', |
33
|
|
|
|
|
|
|
'do', 'dz', 'ec', 'ee', 'eg', 'eh', 'er', 'es', 'et', 'fi', 'fj', 'fk', |
34
|
|
|
|
|
|
|
'fm', 'fo', 'fr', 'sx', 'ga', 'gb', 'gd', 'ge', 'gf', 'gh', 'gi', 'gl', |
35
|
|
|
|
|
|
|
'gm', 'gn', 'gp', 'gq', 'gr', 'gs', 'gt', 'gu', 'gw', 'gy', 'hk', 'hm', |
36
|
|
|
|
|
|
|
'hn', 'hr', 'ht', 'hu', 'id', 'ie', 'il', 'in', 'io', 'iq', 'ir', 'is', |
37
|
|
|
|
|
|
|
'it', 'jm', 'jo', 'jp', 'ke', 'kg', 'kh', 'ki', 'km', 'kn', 'kp', 'kr', |
38
|
|
|
|
|
|
|
'kw', 'ky', 'kz', 'la', 'lb', 'lc', 'li', 'lk', 'lr', 'ls', 'lt', 'lu', |
39
|
|
|
|
|
|
|
'lv', 'ly', 'ma', 'mc', 'md', 'mg', 'mh', 'mk', 'ml', 'mm', 'mn', 'mo', |
40
|
|
|
|
|
|
|
'mp', 'mq', 'mr', 'ms', 'mt', 'mu', 'mv', 'mw', 'mx', 'my', 'mz', 'na', |
41
|
|
|
|
|
|
|
'nc', 'ne', 'nf', 'ng', 'ni', 'nl', 'no', 'np', 'nr', 'nu', 'nz', 'om', |
42
|
|
|
|
|
|
|
'pa', 'pe', 'pf', 'pg', 'ph', 'pk', 'pl', 'pm', 'pn', 'pr', 'ps', 'pt', |
43
|
|
|
|
|
|
|
'pw', 'py', 'qa', 're', 'ro', 'ru', 'rw', 'sa', 'sb', 'sc', 'sd', 'se', |
44
|
|
|
|
|
|
|
'sg', 'sh', 'si', 'sj', 'sk', 'sl', 'sm', 'sn', 'so', 'sr', 'st', 'sv', |
45
|
|
|
|
|
|
|
'sy', 'sz', 'tc', 'td', 'tf', 'tg', 'th', 'tj', 'tk', 'tm', 'tn', 'to', |
46
|
|
|
|
|
|
|
'tl', 'tr', 'tt', 'tv', 'tw', 'tz', 'ua', 'ug', 'um', 'us', 'uy', 'uz', |
47
|
|
|
|
|
|
|
'va', 'vc', 've', 'vg', 'vi', 'vn', 'vu', 'wf', 'ws', 'ye', 'yt', 'rs', |
48
|
|
|
|
|
|
|
'za', 'zm', 'me', 'zw', 'a1', 'xk', 'o1', 'ax', 'gg', 'im', 'je', 'bl', |
49
|
|
|
|
|
|
|
'mf', 'bq', 'ss' |
50
|
|
|
|
|
|
|
); |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub new { |
53
|
1
|
|
|
1
|
0
|
7
|
my $class = shift; |
54
|
1
|
|
|
|
|
2
|
my $file = shift; |
55
|
|
|
|
|
|
|
|
56
|
1
|
|
|
|
|
3
|
my $self = fields::new( $class ); |
57
|
|
|
|
|
|
|
|
58
|
1
|
50
|
|
|
|
2498
|
open( my $fl, $file ) || croak( 'Could not open db file' ); |
59
|
1
|
|
|
|
|
5
|
binmode $fl, ':bytes'; |
60
|
|
|
|
|
|
|
|
61
|
1
|
|
|
|
|
15
|
read $fl, my $header, HEADER_LENGTH; |
62
|
1
|
50
|
|
|
|
4
|
croak 'File format is wrong' if substr( $header, 0, 3 ) ne 'SxG'; |
63
|
|
|
|
|
|
|
|
64
|
1
|
|
|
|
|
2
|
my $info_str = substr( $header, 3, HEADER_LENGTH - 3 ); |
65
|
1
|
|
|
|
|
6
|
my @info = unpack 'CNCCCnnNCnnNNnNn', $info_str; |
66
|
1
|
50
|
|
|
|
7
|
croak 'File header format is wrong' if $info[4] * $info[5] * $info[6] * $info[7] * $info[1] * $info[8] == 0; |
67
|
|
|
|
|
|
|
|
68
|
1
|
50
|
|
|
|
3
|
if ( $info[15] ) { |
69
|
1
|
|
|
|
|
1
|
read $fl, my $pack, $info[15]; |
70
|
1
|
|
|
|
|
6
|
$self->{pack} = [ split "\0", $pack ]; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
1
|
|
|
|
|
3
|
read $fl, $self->{b_idx_str}, $info[4] * 4; |
74
|
1
|
|
|
|
|
10
|
read $fl, $self->{m_idx_str}, $info[5] * 4; |
75
|
|
|
|
|
|
|
|
76
|
1
|
|
|
|
|
2
|
$self->{range} = $info[6]; |
77
|
1
|
|
|
|
|
1
|
$self->{b_idx_len} = $info[4]; |
78
|
1
|
|
|
|
|
2
|
$self->{m_idx_len} = $info[5]; |
79
|
1
|
|
|
|
|
1
|
$self->{db_items} = $info[7]; |
80
|
1
|
|
|
|
|
2
|
$self->{id_len} = $info[8]; |
81
|
1
|
|
|
|
|
2
|
$self->{block_len} = 3 + $self->{id_len}; |
82
|
1
|
|
|
|
|
2
|
$self->{max_region} = $info[9]; |
83
|
1
|
|
|
|
|
1
|
$self->{max_city} = $info[10]; |
84
|
1
|
|
|
|
|
2
|
$self->{max_country} = $info[13]; |
85
|
1
|
|
|
|
|
1
|
$self->{country_size} = $info[14]; |
86
|
|
|
|
|
|
|
|
87
|
1
|
|
|
|
|
4
|
$self->{db_begin} = tell $fl; |
88
|
|
|
|
|
|
|
|
89
|
1
|
|
|
|
|
2
|
$self->{regions_begin} = $self->{db_begin} + $self->{db_items} * $self->{block_len}; |
90
|
1
|
|
|
|
|
2
|
$self->{cities_begin} = $self->{regions_begin} + $info[11]; |
91
|
|
|
|
|
|
|
|
92
|
1
|
|
|
|
|
1
|
$self->{db_file} = $file; |
93
|
|
|
|
|
|
|
|
94
|
1
|
|
|
|
|
6
|
close $fl; |
95
|
|
|
|
|
|
|
|
96
|
1
|
|
|
|
|
4
|
return $self; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
sub get_city { |
100
|
1
|
|
|
1
|
0
|
2
|
my __PACKAGE__ $self = shift; |
101
|
1
|
|
|
|
|
2
|
my $ip = shift; |
102
|
1
|
|
|
|
|
1
|
my $lang = shift; |
103
|
|
|
|
|
|
|
|
104
|
1
|
|
|
|
|
2
|
my $seek = $self->get_num($ip); |
105
|
1
|
50
|
|
|
|
3
|
return unless $seek; |
106
|
|
|
|
|
|
|
|
107
|
1
|
|
|
|
|
3
|
my $info = $self->parse_info( $seek, $lang ); |
108
|
1
|
50
|
|
|
|
3
|
return unless $info; |
109
|
|
|
|
|
|
|
|
110
|
1
|
|
|
|
|
1
|
my $city; |
111
|
1
|
50
|
33
|
|
|
3
|
if ( $lang && $lang eq 'en' ) { |
112
|
0
|
|
|
|
|
0
|
$city = $info->[6]; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
else { |
115
|
1
|
|
|
|
|
2
|
$city = $info->[5]; |
116
|
|
|
|
|
|
|
} |
117
|
1
|
50
|
|
|
|
2
|
return unless $city; |
118
|
|
|
|
|
|
|
|
119
|
1
|
|
|
|
|
3
|
return decode_utf8($city); |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub get_country { |
123
|
1
|
|
|
1
|
0
|
246
|
my __PACKAGE__ $self = shift; |
124
|
1
|
|
|
|
|
1
|
my $ip = shift; |
125
|
|
|
|
|
|
|
|
126
|
1
|
|
|
|
|
3
|
my $seek = $self->get_num($ip); |
127
|
1
|
50
|
|
|
|
4
|
return unless $seek; |
128
|
|
|
|
|
|
|
|
129
|
1
|
|
|
|
|
3
|
my $info = $self->parse_info($seek); |
130
|
1
|
50
|
|
|
|
3
|
return unless $info; |
131
|
|
|
|
|
|
|
|
132
|
1
|
|
|
|
|
2
|
my $country = $COUNTRY_ISO_MAP[ $info->[1] ]; |
133
|
1
|
|
|
|
|
2
|
return $country; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub parse { |
137
|
1
|
|
|
1
|
0
|
5
|
my __PACKAGE__ $self = shift; |
138
|
1
|
|
|
|
|
2
|
my $ip = shift; |
139
|
1
|
|
|
|
|
1
|
my $lang = shift; |
140
|
1
|
|
|
|
|
4
|
my $seek = $self->get_num($ip); |
141
|
1
|
50
|
|
|
|
3
|
return unless $seek; |
142
|
|
|
|
|
|
|
|
143
|
1
|
|
|
|
|
3
|
my $info = $self->parse_info($seek, $lang); |
144
|
1
|
|
|
|
|
7
|
return Geo::SypexGeo::Info->new($info, $lang); |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
sub get_num { |
148
|
3
|
|
|
3
|
0
|
3
|
my __PACKAGE__ $self = shift; |
149
|
3
|
|
|
|
|
3
|
my $ip = shift; |
150
|
|
|
|
|
|
|
|
151
|
3
|
|
|
|
|
2
|
my $ip1n; |
152
|
|
|
|
|
|
|
{ |
153
|
2
|
|
|
2
|
|
8
|
no warnings; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
1955
|
|
|
3
|
|
|
|
|
2
|
|
154
|
3
|
|
|
|
|
9
|
$ip1n = int $ip; |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
3
|
50
|
33
|
|
|
25
|
return undef if !$ip1n || $ip1n == 10 || $ip1n == 127 || $ip1n >= $self->{b_idx_len}; |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
158
|
3
|
|
|
|
|
5
|
my $ipn = ip2long( $ip ); |
159
|
3
|
|
|
|
|
7
|
$ipn = pack( 'N', $ipn ); |
160
|
|
|
|
|
|
|
|
161
|
3
|
|
|
|
|
8
|
my @blocks = unpack "NN", substr( $self->{b_idx_str} , ( $ip1n - 1 ) * 4, 8 ); |
162
|
|
|
|
|
|
|
|
163
|
3
|
|
|
|
|
3
|
my $min; |
164
|
|
|
|
|
|
|
my $max; |
165
|
|
|
|
|
|
|
|
166
|
3
|
50
|
|
|
|
5
|
if ( $blocks[1] - $blocks[0] > $self->{range} ) { |
167
|
|
|
|
|
|
|
my $part = $self->search_idx( |
168
|
|
|
|
|
|
|
$ipn, |
169
|
|
|
|
|
|
|
floor( $blocks[0] / $self->{'range'} ), |
170
|
3
|
|
|
|
|
22
|
floor( $blocks[1] / $self->{'range'} ) - 1 |
171
|
|
|
|
|
|
|
); |
172
|
|
|
|
|
|
|
|
173
|
3
|
50
|
|
|
|
6
|
$min = $part > 0 ? $part * $self->{range} : 0; |
174
|
3
|
50
|
|
|
|
6
|
$max = $part > $self->{m_idx_len} ? $self->{db_items} : ( $part + 1 ) * $self->{range}; |
175
|
|
|
|
|
|
|
|
176
|
3
|
50
|
|
|
|
4
|
$min = $blocks[0] if $min < $blocks[0]; |
177
|
3
|
50
|
|
|
|
11
|
$max = $blocks[1] if $max > $blocks[1]; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
else { |
180
|
0
|
|
|
|
|
0
|
$min = $blocks[0]; |
181
|
0
|
|
|
|
|
0
|
$max = $blocks[1]; |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
3
|
|
|
|
|
2
|
my $len = $max - $min; |
185
|
|
|
|
|
|
|
|
186
|
3
|
50
|
|
|
|
67
|
open( my $fl, $self->{ 'db_file' } ) || croak( 'Could not open db file' ); |
187
|
3
|
|
|
|
|
9
|
binmode $fl, ':bytes'; |
188
|
3
|
|
|
|
|
7
|
seek $fl, $self->{db_begin} + $min * $self->{block_len}, 0; |
189
|
3
|
|
|
|
|
35
|
read $fl, my $buf, $len * $self->{block_len}; |
190
|
3
|
|
|
|
|
14
|
close $fl; |
191
|
|
|
|
|
|
|
|
192
|
3
|
|
|
|
|
9
|
return $self->search_db( $buf, $ipn, 0, $len - 1 ); |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub search_idx { |
196
|
3
|
|
|
3
|
0
|
2
|
my __PACKAGE__ $self = shift; |
197
|
3
|
|
|
|
|
3
|
my $ipn = shift; |
198
|
3
|
|
|
|
|
2
|
my $min = shift; |
199
|
3
|
|
|
|
|
2
|
my $max = shift; |
200
|
|
|
|
|
|
|
|
201
|
3
|
|
|
|
|
3
|
my $offset; |
202
|
3
|
|
|
|
|
6
|
while ( $max - $min > 8 ) { |
203
|
6
|
|
|
|
|
4
|
$offset = ( $min + $max ) >> 1; |
204
|
|
|
|
|
|
|
|
205
|
6
|
50
|
|
|
|
11
|
if ( encode_utf8($ipn) gt encode_utf8( substr( ( $self->{m_idx_str} ), $offset * 4, 4 ) ) ) { |
206
|
6
|
|
|
|
|
48
|
$min = $offset; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
else { |
209
|
0
|
|
|
|
|
0
|
$max = $offset; |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
3
|
|
66
|
|
|
5
|
while ( encode_utf8($ipn) gt encode_utf8( substr( $self->{m_idx_str}, $min * 4, 4 ) ) && $min++ < $max ) { |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
3
|
|
|
|
|
165
|
return $min; |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
sub search_db { |
220
|
3
|
|
|
3
|
0
|
3
|
my __PACKAGE__ $self = shift; |
221
|
3
|
|
|
|
|
3
|
my $str = shift; |
222
|
3
|
|
|
|
|
4
|
my $ipn = shift; |
223
|
3
|
|
|
|
|
2
|
my $min = shift; |
224
|
3
|
|
|
|
|
2
|
my $max = shift; |
225
|
|
|
|
|
|
|
|
226
|
3
|
50
|
|
|
|
6
|
if( $max - $min > 1 ) { |
227
|
3
|
|
|
|
|
4
|
$ipn = substr( $ipn, 1 ); |
228
|
3
|
|
|
|
|
3
|
my $offset; |
229
|
3
|
|
|
|
|
5
|
while ( $max - $min > 8 ){ |
230
|
18
|
|
|
|
|
14
|
$offset = ( $min + $max ) >> 1; |
231
|
|
|
|
|
|
|
|
232
|
18
|
100
|
|
|
|
22
|
if ( encode_utf8( $ipn ) gt encode_utf8( substr( $str, $offset * $self->{block_len}, 3 ) ) ) { |
233
|
9
|
|
|
|
|
50
|
$min = $offset; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
else { |
236
|
9
|
|
|
|
|
59
|
$max = $offset; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
3
|
|
66
|
|
|
5
|
while ( encode_utf8( $ipn ) ge encode_utf8( substr( $str, $min * $self->{block_len}, 3 ) ) && $min++ < $max ){} |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
else { |
243
|
0
|
|
|
|
|
0
|
return hex( bin2hex( substr( $str, $min * $self->{block_len} + 3 , 3 ) ) ); |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
3
|
|
|
|
|
89
|
return hex( bin2hex( substr( $str, $min * $self->{block_len} - $self->{id_len}, $self->{id_len} ) ) ); |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
sub bin2hex { |
250
|
3
|
|
|
3
|
0
|
3
|
my $str = shift; |
251
|
|
|
|
|
|
|
|
252
|
3
|
|
|
|
|
3
|
my $res = ''; |
253
|
3
|
|
|
|
|
7
|
for my $i ( 0 .. length( $str ) - 1 ) { |
254
|
9
|
|
|
|
|
20
|
$res .= sprintf( '%02s', sprintf( '%x', ord( substr( $str, $i, 1 ) ) ) ); |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
3
|
|
|
|
|
12
|
return $res; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
sub ip2long { |
261
|
3
|
|
|
3
|
0
|
25
|
return unpack( 'l*', pack( 'l*', unpack( 'N*', inet_aton( shift ) ) ) ); |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
sub parse_info { |
265
|
3
|
|
|
3
|
0
|
4
|
my __PACKAGE__ $self = shift; |
266
|
3
|
|
|
|
|
2
|
my $seek = shift; |
267
|
|
|
|
|
|
|
|
268
|
3
|
|
|
|
|
2
|
my $info; |
269
|
|
|
|
|
|
|
|
270
|
3
|
50
|
|
|
|
6
|
if ( $seek < $self->{country_size} ) { |
271
|
0
|
0
|
|
|
|
0
|
open( my $fl, $self->{db_file} ) || croak('Could not open db file'); |
272
|
0
|
|
|
|
|
0
|
binmode $fl, ':bytes'; |
273
|
0
|
|
|
|
|
0
|
seek $fl, $seek + $self->{cities_begin}, 0; |
274
|
0
|
|
|
|
|
0
|
read $fl, my $buf, $self->{max_country}; |
275
|
0
|
|
|
|
|
0
|
close $fl; |
276
|
|
|
|
|
|
|
|
277
|
0
|
|
|
|
|
0
|
$info = extended_unpack( $self->{pack}[0], $buf ); |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
else { |
280
|
3
|
50
|
|
|
|
59
|
open( my $fl, $self->{db_file} ) || croak('Could not open db file'); |
281
|
3
|
|
|
|
|
8
|
binmode $fl, ':bytes'; |
282
|
3
|
|
|
|
|
7
|
seek $fl, $seek + $self->{cities_begin}, 0; |
283
|
3
|
|
|
|
|
19
|
read $fl, my $buf, $self->{max_city}; |
284
|
3
|
|
|
|
|
12
|
close $fl; |
285
|
|
|
|
|
|
|
|
286
|
3
|
|
|
|
|
7
|
$info = extended_unpack( $self->{pack}[2], $buf ); |
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
|
289
|
3
|
50
|
|
|
|
7
|
if ($info) { |
290
|
3
|
|
|
|
|
5
|
return $info; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
else { |
293
|
0
|
|
|
|
|
0
|
return; |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
sub extended_unpack { |
298
|
3
|
|
|
3
|
0
|
3
|
my $flags = shift; |
299
|
3
|
|
|
|
|
3
|
my $val = shift; |
300
|
|
|
|
|
|
|
|
301
|
3
|
|
|
|
|
3
|
my $pos = 0; |
302
|
3
|
|
|
|
|
4
|
my $result = []; |
303
|
|
|
|
|
|
|
|
304
|
3
|
|
|
|
|
11
|
my @flags_arr = split '/', $flags; |
305
|
|
|
|
|
|
|
|
306
|
3
|
|
|
|
|
5
|
foreach my $flag_str ( @flags_arr ) { |
307
|
21
|
|
|
|
|
57
|
my ( $type, $name ) = split ':', $flag_str; |
308
|
|
|
|
|
|
|
|
309
|
21
|
|
|
|
|
23
|
my $flag = substr $type, 0, 1; |
310
|
21
|
|
|
|
|
15
|
my $num = substr $type, 1, 1; |
311
|
|
|
|
|
|
|
|
312
|
21
|
|
|
|
|
13
|
my $len; |
313
|
|
|
|
|
|
|
|
314
|
21
|
50
|
|
|
|
72
|
if ( $flag eq 't' ) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
elsif ( $flag eq 'T' ) { |
317
|
3
|
|
|
|
|
2
|
$len = 1; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
elsif ( $flag eq 's' ) { |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
elsif ( $flag eq 'n' ) { |
322
|
0
|
|
|
|
|
0
|
$len = $num; |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
elsif ( $flag eq 'S' ) { |
325
|
0
|
|
|
|
|
0
|
$len = 2; |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
elsif ( $flag eq 'm' ) { |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
elsif ( $flag eq 'M' ) { |
330
|
6
|
|
|
|
|
4
|
$len = 3; |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
elsif ( $flag eq 'd' ) { |
333
|
0
|
|
|
|
|
0
|
$len = 8; |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
elsif ( $flag eq 'c' ) { |
336
|
0
|
|
|
|
|
0
|
$len = $num; |
337
|
|
|
|
|
|
|
} |
338
|
|
|
|
|
|
|
elsif ( $flag eq 'b' ) { |
339
|
6
|
|
|
|
|
32
|
$len = index( $val, "\0", $pos ) - $pos; |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
else { |
342
|
6
|
|
|
|
|
4
|
$len = 4; |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
|
345
|
21
|
|
|
|
|
19
|
my $subval = substr( $val, $pos, $len ); |
346
|
|
|
|
|
|
|
|
347
|
21
|
|
|
|
|
11
|
my $res; |
348
|
|
|
|
|
|
|
|
349
|
21
|
50
|
|
|
|
90
|
if ( $flag eq 't' ) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
350
|
0
|
|
|
|
|
0
|
$res = ( unpack 'c', $subval )[0]; |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
elsif ( $flag eq 'T' ) { |
353
|
3
|
|
|
|
|
4
|
$res = ( unpack 'C', $subval )[0]; |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
elsif ( $flag eq 's' ) { |
356
|
0
|
|
|
|
|
0
|
$res = ( unpack 's', $subval )[0]; |
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
elsif ( $flag eq 'S' ) { |
359
|
0
|
|
|
|
|
0
|
$res = ( unpack 'S', $subval )[0]; |
360
|
|
|
|
|
|
|
} |
361
|
|
|
|
|
|
|
elsif ( $flag eq 'm' ) { |
362
|
0
|
0
|
|
|
|
0
|
$res = ( unpack 'l', $subval . ( ord( substr( $subval, 2, 1 ) ) >> 7 ? "\xff" : "\0" ) )[0]; |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
elsif ( $flag eq 'M' ) { |
365
|
6
|
|
|
|
|
11
|
$res = ( unpack 'L', $subval . "\0" )[0]; |
366
|
|
|
|
|
|
|
} |
367
|
|
|
|
|
|
|
elsif ( $flag eq 'i' ) { |
368
|
0
|
|
|
|
|
0
|
$res = ( unpack 'l', $subval )[0]; |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
elsif ( $flag eq 'I' ) { |
371
|
0
|
|
|
|
|
0
|
$res = ( unpack 'L', $subval )[0]; |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
elsif ( $flag eq 'f' ) { |
374
|
0
|
|
|
|
|
0
|
$res = ( unpack 'f', $subval )[0]; |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
elsif ( $flag eq 'd' ) { |
377
|
0
|
|
|
|
|
0
|
$res = ( unpack 'd', $subval )[0]; |
378
|
|
|
|
|
|
|
} |
379
|
|
|
|
|
|
|
elsif ( $flag eq 'n' ) { |
380
|
0
|
|
|
|
|
0
|
$res = ( unpack 's', $subval )[0] / ( 10 ** $num ); |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
elsif ( $flag eq 'N' ) { |
383
|
6
|
|
|
|
|
13
|
$res = ( unpack 'l', $subval )[0] / ( 10 ** $num ); |
384
|
|
|
|
|
|
|
} |
385
|
|
|
|
|
|
|
elsif ( $flag eq 'c' ) { |
386
|
0
|
|
|
|
|
0
|
$res = rtrim $subval; |
387
|
|
|
|
|
|
|
} |
388
|
|
|
|
|
|
|
elsif ( $flag eq 'b' ) { |
389
|
6
|
|
|
|
|
4
|
$res = $subval; |
390
|
6
|
|
|
|
|
6
|
$len++; |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
21
|
|
|
|
|
10
|
$pos += $len; |
394
|
|
|
|
|
|
|
|
395
|
21
|
|
|
|
|
32
|
push @$result, $res; |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
|
398
|
3
|
|
|
|
|
9
|
return $result; |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
1; |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
=head1 NAME |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
Geo::SypexGeo - API to detect cities by IP thru Sypex Geo database v.2 |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=head1 SYNOPSIS |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
use Geo::SypexGeo; |
410
|
|
|
|
|
|
|
my $geo = Geo::SypexGeo->new( './SxGeoCity.dat' ); |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# Method parse return Geo::SypexGeo::Info object |
413
|
|
|
|
|
|
|
$info = $geo->parse( '87.250.250.203', 'en' ) |
414
|
|
|
|
|
|
|
or die "Cant parse 87.250.250.203"; |
415
|
|
|
|
|
|
|
say $info->city(); |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
$info = $geo->parse('93.191.14.81') or die "Cant parse 93.191.14.81"; |
418
|
|
|
|
|
|
|
say $info->city(); |
419
|
|
|
|
|
|
|
say $info->country(); |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
my ( $latitude, $longitude ) = $info->coordinates(); |
422
|
|
|
|
|
|
|
say "Latitude: $latitude Longitude: $longitude"; |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
## deprecated method (will be removed in future versions) |
425
|
|
|
|
|
|
|
say $geo->get_city( '87.250.250.203', 'en' ); |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
## deprecated method (will be removed in future versions) |
428
|
|
|
|
|
|
|
say $geo->get_city('93.191.14.81'); |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
## deprecated method (will be removed in future versions) |
431
|
|
|
|
|
|
|
say $geo->get_country('93.191.14.81'); |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=head1 DESCRIPTION |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
L is a database to detect cities by IP. |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
The database of IPs is included into distribution, but it is better to download latest version at L. |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
The database is availible with a names of the cities in Russian and English languages. |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
This module now is detect only city name and don't use any features to speed up of detection. In the future I plan to add more functionality. |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=head1 SOURCE AVAILABILITY |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
The source code for this module is available from Github |
446
|
|
|
|
|
|
|
at https://github.com/kak-tus/Geo-SypexGeo |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
=head1 AUTHOR |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
Andrey Kuzmin, Ekak-tus@mail.ruE |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
=head1 CREDITS |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
vrag86 |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
Copyright (C) 2014 by Andrey Kuzmin |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=cut |