line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Geo::SypexGeo; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.8'; |
4
|
|
|
|
|
|
|
|
5
|
2
|
|
|
2
|
|
28203
|
use strict; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
62
|
|
6
|
2
|
|
|
2
|
|
8
|
use warnings; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
54
|
|
7
|
2
|
|
|
2
|
|
14
|
use utf8; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
10
|
|
8
|
2
|
|
|
2
|
|
70
|
use v5.10; |
|
2
|
|
|
|
|
5
|
|
9
|
|
|
|
|
|
|
|
10
|
2
|
|
|
2
|
|
7
|
use Carp qw( croak ); |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
136
|
|
11
|
2
|
|
|
2
|
|
1185
|
use Encode; |
|
2
|
|
|
|
|
16850
|
|
|
2
|
|
|
|
|
150
|
|
12
|
2
|
|
|
2
|
|
1141
|
use Socket; |
|
2
|
|
|
|
|
7265
|
|
|
2
|
|
|
|
|
945
|
|
13
|
2
|
|
|
2
|
|
1031
|
use POSIX; |
|
2
|
|
|
|
|
10900
|
|
|
2
|
|
|
|
|
12
|
|
14
|
2
|
|
|
2
|
|
5650
|
use Text::Trim; |
|
2
|
|
|
|
|
1018
|
|
|
2
|
|
|
|
|
147
|
|
15
|
2
|
|
|
2
|
|
817
|
use Geo::SypexGeo::Info; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
82
|
|
16
|
|
|
|
|
|
|
|
17
|
2
|
|
|
|
|
10
|
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
|
|
1177
|
); |
|
2
|
|
|
|
|
2791
|
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
use constant { |
24
|
2
|
|
|
|
|
1587
|
HEADER_LENGTH => 40, |
25
|
2
|
|
|
2
|
|
250
|
}; |
|
2
|
|
|
|
|
3
|
|
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
|
16
|
my $class = shift; |
54
|
1
|
|
|
|
|
3
|
my $file = shift; |
55
|
|
|
|
|
|
|
|
56
|
1
|
|
|
|
|
8
|
my $self = fields::new( $class ); |
57
|
|
|
|
|
|
|
|
58
|
1
|
50
|
|
|
|
4321
|
open( my $fl, $file ) || croak( 'Could not open db file' ); |
59
|
1
|
|
|
|
|
8
|
binmode $fl, ':bytes'; |
60
|
|
|
|
|
|
|
|
61
|
1
|
|
|
|
|
23
|
read $fl, my $header, HEADER_LENGTH; |
62
|
1
|
50
|
|
|
|
7
|
croak 'File format is wrong' if substr( $header, 0, 3 ) ne 'SxG'; |
63
|
|
|
|
|
|
|
|
64
|
1
|
|
|
|
|
4
|
my $info_str = substr( $header, 3, HEADER_LENGTH - 3 ); |
65
|
1
|
|
|
|
|
12
|
my @info = unpack 'CNCCCnnNCnnNNnNn', $info_str; |
66
|
1
|
50
|
|
|
|
14
|
croak 'File header format is wrong' if $info[4] * $info[5] * $info[6] * $info[7] * $info[1] * $info[8] == 0; |
67
|
|
|
|
|
|
|
|
68
|
1
|
50
|
|
|
|
5
|
if ( $info[15] ) { |
69
|
1
|
|
|
|
|
3
|
read $fl, my $pack, $info[15]; |
70
|
1
|
|
|
|
|
9
|
$self->{pack} = [ split "\0", $pack ]; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
1
|
|
|
|
|
5
|
read $fl, $self->{b_idx_str}, $info[4] * 4; |
74
|
1
|
|
|
|
|
17
|
read $fl, $self->{m_idx_str}, $info[5] * 4; |
75
|
|
|
|
|
|
|
|
76
|
1
|
|
|
|
|
3
|
$self->{range} = $info[6]; |
77
|
1
|
|
|
|
|
3
|
$self->{b_idx_len} = $info[4]; |
78
|
1
|
|
|
|
|
3
|
$self->{m_idx_len} = $info[5]; |
79
|
1
|
|
|
|
|
2
|
$self->{db_items} = $info[7]; |
80
|
1
|
|
|
|
|
3
|
$self->{id_len} = $info[8]; |
81
|
1
|
|
|
|
|
4
|
$self->{block_len} = 3 + $self->{id_len}; |
82
|
1
|
|
|
|
|
2
|
$self->{max_region} = $info[9]; |
83
|
1
|
|
|
|
|
3
|
$self->{max_city} = $info[10]; |
84
|
1
|
|
|
|
|
2
|
$self->{max_country} = $info[13]; |
85
|
1
|
|
|
|
|
4
|
$self->{country_size} = $info[14]; |
86
|
|
|
|
|
|
|
|
87
|
1
|
|
|
|
|
6
|
$self->{db_begin} = tell $fl; |
88
|
|
|
|
|
|
|
|
89
|
1
|
|
|
|
|
5
|
$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
|
|
|
|
|
3
|
$self->{db_file} = $file; |
93
|
|
|
|
|
|
|
|
94
|
1
|
|
|
|
|
8
|
close $fl; |
95
|
|
|
|
|
|
|
|
96
|
1
|
|
|
|
|
8
|
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
|
|
|
|
|
3
|
my $seek = $self->get_num($ip); |
105
|
1
|
50
|
|
|
|
4
|
return unless $seek; |
106
|
|
|
|
|
|
|
|
107
|
1
|
|
|
|
|
5
|
my $info = $self->parse_info( $seek, $lang ); |
108
|
1
|
50
|
|
|
|
5
|
return unless $info; |
109
|
|
|
|
|
|
|
|
110
|
1
|
|
|
|
|
3
|
my $city; |
111
|
1
|
50
|
33
|
|
|
6
|
if ( $lang && $lang eq 'en' ) { |
112
|
0
|
|
|
|
|
0
|
$city = $info->[6]; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
else { |
115
|
1
|
|
|
|
|
3
|
$city = $info->[5]; |
116
|
|
|
|
|
|
|
} |
117
|
1
|
50
|
|
|
|
4
|
return unless $city; |
118
|
|
|
|
|
|
|
|
119
|
1
|
|
|
|
|
5
|
return decode_utf8($city); |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub get_country { |
123
|
1
|
|
|
1
|
0
|
518
|
my __PACKAGE__ $self = shift; |
124
|
1
|
|
|
|
|
2
|
my $ip = shift; |
125
|
|
|
|
|
|
|
|
126
|
1
|
|
|
|
|
5
|
my $seek = $self->get_num($ip); |
127
|
1
|
50
|
|
|
|
8
|
return unless $seek; |
128
|
|
|
|
|
|
|
|
129
|
1
|
|
|
|
|
5
|
my $info = $self->parse_info($seek); |
130
|
1
|
50
|
|
|
|
6
|
return unless $info; |
131
|
|
|
|
|
|
|
|
132
|
1
|
|
|
|
|
2
|
my $country; |
133
|
1
|
50
|
|
|
|
8
|
if ( $info->[1] =~ /\D/ ) { |
134
|
0
|
|
|
|
|
0
|
$country = $info->[1]; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
else { |
137
|
1
|
|
|
|
|
4
|
$country = $COUNTRY_ISO_MAP[ $info->[1] ]; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
1
|
|
|
|
|
5
|
return $country; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub parse { |
144
|
1
|
|
|
1
|
0
|
21
|
my __PACKAGE__ $self = shift; |
145
|
1
|
|
|
|
|
3
|
my $ip = shift; |
146
|
1
|
|
|
|
|
2
|
my $lang = shift; |
147
|
1
|
|
|
|
|
9
|
my $seek = $self->get_num($ip); |
148
|
1
|
50
|
|
|
|
7
|
return unless $seek; |
149
|
|
|
|
|
|
|
|
150
|
1
|
|
|
|
|
6
|
my $info = $self->parse_info($seek, $lang); |
151
|
1
|
|
|
|
|
11
|
return Geo::SypexGeo::Info->new($info, $lang); |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
sub get_num { |
155
|
3
|
|
|
3
|
0
|
6
|
my __PACKAGE__ $self = shift; |
156
|
3
|
|
|
|
|
5
|
my $ip = shift; |
157
|
|
|
|
|
|
|
|
158
|
3
|
|
|
|
|
4
|
my $ip1n; |
159
|
|
|
|
|
|
|
{ |
160
|
2
|
|
|
2
|
|
12
|
no warnings; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
2511
|
|
|
3
|
|
|
|
|
4
|
|
161
|
3
|
|
|
|
|
16
|
$ip1n = int $ip; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
3
|
50
|
33
|
|
|
41
|
return undef if !$ip1n || $ip1n == 10 || $ip1n == 127 || $ip1n >= $self->{b_idx_len}; |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
165
|
3
|
|
|
|
|
11
|
my $ipn = ip2long( $ip ); |
166
|
3
|
|
|
|
|
11
|
$ipn = pack( 'N', $ipn ); |
167
|
|
|
|
|
|
|
|
168
|
3
|
|
|
|
|
18
|
my @blocks = unpack "NN", substr( $self->{b_idx_str} , ( $ip1n - 1 ) * 4, 8 ); |
169
|
|
|
|
|
|
|
|
170
|
3
|
|
|
|
|
5
|
my $min; |
171
|
|
|
|
|
|
|
my $max; |
172
|
|
|
|
|
|
|
|
173
|
3
|
50
|
|
|
|
12
|
if ( $blocks[1] - $blocks[0] > $self->{range} ) { |
174
|
|
|
|
|
|
|
my $part = $self->search_idx( |
175
|
|
|
|
|
|
|
$ipn, |
176
|
|
|
|
|
|
|
floor( $blocks[0] / $self->{'range'} ), |
177
|
3
|
|
|
|
|
41
|
floor( $blocks[1] / $self->{'range'} ) - 1 |
178
|
|
|
|
|
|
|
); |
179
|
|
|
|
|
|
|
|
180
|
3
|
50
|
|
|
|
13
|
$min = $part > 0 ? $part * $self->{range} : 0; |
181
|
3
|
50
|
|
|
|
11
|
$max = $part > $self->{m_idx_len} ? $self->{db_items} : ( $part + 1 ) * $self->{range}; |
182
|
|
|
|
|
|
|
|
183
|
3
|
50
|
|
|
|
17
|
$min = $blocks[0] if $min < $blocks[0]; |
184
|
3
|
50
|
|
|
|
11
|
$max = $blocks[1] if $max > $blocks[1]; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
else { |
187
|
0
|
|
|
|
|
0
|
$min = $blocks[0]; |
188
|
0
|
|
|
|
|
0
|
$max = $blocks[1]; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
3
|
|
|
|
|
5
|
my $len = $max - $min; |
192
|
|
|
|
|
|
|
|
193
|
3
|
50
|
|
|
|
115
|
open( my $fl, $self->{ 'db_file' } ) || croak( 'Could not open db file' ); |
194
|
3
|
|
|
|
|
15
|
binmode $fl, ':bytes'; |
195
|
3
|
|
|
|
|
13
|
seek $fl, $self->{db_begin} + $min * $self->{block_len}, 0; |
196
|
3
|
|
|
|
|
57
|
read $fl, my $buf, $len * $self->{block_len}; |
197
|
3
|
|
|
|
|
22
|
close $fl; |
198
|
|
|
|
|
|
|
|
199
|
3
|
|
|
|
|
13
|
return $self->search_db( $buf, $ipn, 0, $len - 1 ); |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub search_idx { |
203
|
3
|
|
|
3
|
0
|
4
|
my __PACKAGE__ $self = shift; |
204
|
3
|
|
|
|
|
6
|
my $ipn = shift; |
205
|
3
|
|
|
|
|
3
|
my $min = shift; |
206
|
3
|
|
|
|
|
4
|
my $max = shift; |
207
|
|
|
|
|
|
|
|
208
|
3
|
|
|
|
|
4
|
my $offset; |
209
|
3
|
|
|
|
|
12
|
while ( $max - $min > 8 ) { |
210
|
6
|
|
|
|
|
6
|
$offset = ( $min + $max ) >> 1; |
211
|
|
|
|
|
|
|
|
212
|
6
|
50
|
|
|
|
19
|
if ( encode_utf8($ipn) gt encode_utf8( substr( ( $self->{m_idx_str} ), $offset * 4, 4 ) ) ) { |
213
|
6
|
|
|
|
|
89
|
$min = $offset; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
else { |
216
|
0
|
|
|
|
|
0
|
$max = $offset; |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
|
220
|
3
|
|
66
|
|
|
9
|
while ( encode_utf8($ipn) gt encode_utf8( substr( $self->{m_idx_str}, $min * 4, 4 ) ) && $min++ < $max ) { |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
3
|
|
|
|
|
286
|
return $min; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
sub search_db { |
227
|
3
|
|
|
3
|
0
|
5
|
my __PACKAGE__ $self = shift; |
228
|
3
|
|
|
|
|
4
|
my $str = shift; |
229
|
3
|
|
|
|
|
5
|
my $ipn = shift; |
230
|
3
|
|
|
|
|
4
|
my $min = shift; |
231
|
3
|
|
|
|
|
3
|
my $max = shift; |
232
|
|
|
|
|
|
|
|
233
|
3
|
50
|
|
|
|
8
|
if( $max - $min > 1 ) { |
234
|
3
|
|
|
|
|
6
|
$ipn = substr( $ipn, 1 ); |
235
|
3
|
|
|
|
|
3
|
my $offset; |
236
|
3
|
|
|
|
|
9
|
while ( $max - $min > 8 ){ |
237
|
18
|
|
|
|
|
19
|
$offset = ( $min + $max ) >> 1; |
238
|
|
|
|
|
|
|
|
239
|
18
|
100
|
|
|
|
29
|
if ( encode_utf8( $ipn ) gt encode_utf8( substr( $str, $offset * $self->{block_len}, 3 ) ) ) { |
240
|
9
|
|
|
|
|
77
|
$min = $offset; |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
else { |
243
|
9
|
|
|
|
|
91
|
$max = $offset; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
3
|
|
66
|
|
|
8
|
while ( encode_utf8( $ipn ) ge encode_utf8( substr( $str, $min * $self->{block_len}, 3 ) ) && $min++ < $max ){} |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
else { |
250
|
0
|
|
|
|
|
0
|
return hex( bin2hex( substr( $str, $min * $self->{block_len} + 3 , 3 ) ) ); |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
3
|
|
|
|
|
139
|
return hex( bin2hex( substr( $str, $min * $self->{block_len} - $self->{id_len}, $self->{id_len} ) ) ); |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
sub bin2hex { |
257
|
3
|
|
|
3
|
0
|
6
|
my $str = shift; |
258
|
|
|
|
|
|
|
|
259
|
3
|
|
|
|
|
6
|
my $res = ''; |
260
|
3
|
|
|
|
|
8
|
for my $i ( 0 .. length( $str ) - 1 ) { |
261
|
9
|
|
|
|
|
37
|
$res .= sprintf( '%02s', sprintf( '%x', ord( substr( $str, $i, 1 ) ) ) ); |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
3
|
|
|
|
|
22
|
return $res; |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub ip2long { |
268
|
3
|
|
|
3
|
0
|
48
|
return unpack( 'l*', pack( 'l*', unpack( 'N*', inet_aton( shift ) ) ) ); |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
sub parse_info { |
272
|
3
|
|
|
3
|
0
|
5
|
my __PACKAGE__ $self = shift; |
273
|
3
|
|
|
|
|
3
|
my $seek = shift; |
274
|
|
|
|
|
|
|
|
275
|
3
|
|
|
|
|
4
|
my $info; |
276
|
|
|
|
|
|
|
|
277
|
3
|
50
|
|
|
|
11
|
if ( $seek < $self->{country_size} ) { |
278
|
0
|
0
|
|
|
|
0
|
open( my $fl, $self->{db_file} ) || croak('Could not open db file'); |
279
|
0
|
|
|
|
|
0
|
binmode $fl, ':bytes'; |
280
|
0
|
|
|
|
|
0
|
seek $fl, $seek + $self->{cities_begin}, 0; |
281
|
0
|
|
|
|
|
0
|
read $fl, my $buf, $self->{max_country}; |
282
|
0
|
|
|
|
|
0
|
close $fl; |
283
|
|
|
|
|
|
|
|
284
|
0
|
|
|
|
|
0
|
$info = extended_unpack( $self->{pack}[0], $buf ); |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
else { |
287
|
3
|
50
|
|
|
|
99
|
open( my $fl, $self->{db_file} ) || croak('Could not open db file'); |
288
|
3
|
|
|
|
|
16
|
binmode $fl, ':bytes'; |
289
|
3
|
|
|
|
|
10
|
seek $fl, $seek + $self->{cities_begin}, 0; |
290
|
3
|
|
|
|
|
25
|
read $fl, my $buf, $self->{max_city}; |
291
|
3
|
|
|
|
|
19
|
close $fl; |
292
|
|
|
|
|
|
|
|
293
|
3
|
|
|
|
|
10
|
$info = extended_unpack( $self->{pack}[2], $buf ); |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
3
|
50
|
|
|
|
9
|
if ($info) { |
297
|
3
|
|
|
|
|
8
|
return $info; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
else { |
300
|
0
|
|
|
|
|
0
|
return; |
301
|
|
|
|
|
|
|
} |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
sub extended_unpack { |
305
|
3
|
|
|
3
|
0
|
5
|
my $flags = shift; |
306
|
3
|
|
|
|
|
3
|
my $val = shift; |
307
|
|
|
|
|
|
|
|
308
|
3
|
|
|
|
|
5
|
my $pos = 0; |
309
|
3
|
|
|
|
|
5
|
my $result = []; |
310
|
|
|
|
|
|
|
|
311
|
3
|
|
|
|
|
15
|
my @flags_arr = split '/', $flags; |
312
|
|
|
|
|
|
|
|
313
|
3
|
|
|
|
|
5
|
foreach my $flag_str ( @flags_arr ) { |
314
|
21
|
|
|
|
|
53
|
my ( $type, $name ) = split ':', $flag_str; |
315
|
|
|
|
|
|
|
|
316
|
21
|
|
|
|
|
30
|
my $flag = substr $type, 0, 1; |
317
|
21
|
|
|
|
|
23
|
my $num = substr $type, 1, 1; |
318
|
|
|
|
|
|
|
|
319
|
21
|
|
|
|
|
14
|
my $len; |
320
|
|
|
|
|
|
|
|
321
|
21
|
50
|
|
|
|
103
|
if ( $flag eq 't' ) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
elsif ( $flag eq 'T' ) { |
324
|
3
|
|
|
|
|
4
|
$len = 1; |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
elsif ( $flag eq 's' ) { |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
elsif ( $flag eq 'n' ) { |
329
|
0
|
|
|
|
|
0
|
$len = $num; |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
elsif ( $flag eq 'S' ) { |
332
|
0
|
|
|
|
|
0
|
$len = 2; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
elsif ( $flag eq 'm' ) { |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
elsif ( $flag eq 'M' ) { |
337
|
6
|
|
|
|
|
6
|
$len = 3; |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
elsif ( $flag eq 'd' ) { |
340
|
0
|
|
|
|
|
0
|
$len = 8; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
elsif ( $flag eq 'c' ) { |
343
|
0
|
|
|
|
|
0
|
$len = $num; |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
elsif ( $flag eq 'b' ) { |
346
|
6
|
|
|
|
|
46
|
$len = index( $val, "\0", $pos ) - $pos; |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
else { |
349
|
6
|
|
|
|
|
7
|
$len = 4; |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
21
|
|
|
|
|
28
|
my $subval = substr( $val, $pos, $len ); |
353
|
|
|
|
|
|
|
|
354
|
21
|
|
|
|
|
13
|
my $res; |
355
|
|
|
|
|
|
|
|
356
|
21
|
50
|
|
|
|
158
|
if ( $flag eq 't' ) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
357
|
0
|
|
|
|
|
0
|
$res = ( unpack 'c', $subval )[0]; |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
elsif ( $flag eq 'T' ) { |
360
|
3
|
|
|
|
|
7
|
$res = ( unpack 'C', $subval )[0]; |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
elsif ( $flag eq 's' ) { |
363
|
0
|
|
|
|
|
0
|
$res = ( unpack 's', $subval )[0]; |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
elsif ( $flag eq 'S' ) { |
366
|
0
|
|
|
|
|
0
|
$res = ( unpack 'S', $subval )[0]; |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
elsif ( $flag eq 'm' ) { |
369
|
0
|
0
|
|
|
|
0
|
$res = ( unpack 'l', $subval . ( ord( substr( $subval, 2, 1 ) ) >> 7 ? "\xff" : "\0" ) )[0]; |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
elsif ( $flag eq 'M' ) { |
372
|
6
|
|
|
|
|
16
|
$res = ( unpack 'L', $subval . "\0" )[0]; |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
elsif ( $flag eq 'i' ) { |
375
|
0
|
|
|
|
|
0
|
$res = ( unpack 'l', $subval )[0]; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
elsif ( $flag eq 'I' ) { |
378
|
0
|
|
|
|
|
0
|
$res = ( unpack 'L', $subval )[0]; |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
elsif ( $flag eq 'f' ) { |
381
|
0
|
|
|
|
|
0
|
$res = ( unpack 'f', $subval )[0]; |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
elsif ( $flag eq 'd' ) { |
384
|
0
|
|
|
|
|
0
|
$res = ( unpack 'd', $subval )[0]; |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
elsif ( $flag eq 'n' ) { |
387
|
0
|
|
|
|
|
0
|
$res = ( unpack 's', $subval )[0] / ( 10 ** $num ); |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
elsif ( $flag eq 'N' ) { |
390
|
6
|
|
|
|
|
19
|
$res = ( unpack 'l', $subval )[0] / ( 10 ** $num ); |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
elsif ( $flag eq 'c' ) { |
393
|
0
|
|
|
|
|
0
|
$res = rtrim $subval; |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
elsif ( $flag eq 'b' ) { |
396
|
6
|
|
|
|
|
9
|
$res = $subval; |
397
|
6
|
|
|
|
|
6
|
$len++; |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
|
400
|
21
|
|
|
|
|
18
|
$pos += $len; |
401
|
|
|
|
|
|
|
|
402
|
21
|
|
|
|
|
43
|
push @$result, $res; |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
|
405
|
3
|
|
|
|
|
17
|
return $result; |
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
1; |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
=head1 NAME |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
Geo::SypexGeo - API to detect cities by IP thru Sypex Geo database v.2 |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
=head1 SYNOPSIS |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
use Geo::SypexGeo; |
417
|
|
|
|
|
|
|
my $geo = Geo::SypexGeo->new( './SxGeoCity.dat' ); |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# Method parse return Geo::SypexGeo::Info object |
420
|
|
|
|
|
|
|
$info = $geo->parse( '87.250.250.203', 'en' ) |
421
|
|
|
|
|
|
|
or die "Cant parse 87.250.250.203"; |
422
|
|
|
|
|
|
|
say $info->city(); |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
$info = $geo->parse('93.191.14.81') or die "Cant parse 93.191.14.81"; |
425
|
|
|
|
|
|
|
say $info->city(); |
426
|
|
|
|
|
|
|
say $info->country(); |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
my ( $latitude, $longitude ) = $info->coordinates(); |
429
|
|
|
|
|
|
|
say "Latitude: $latitude Longitude: $longitude"; |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
## deprecated method (will be removed in future versions) |
432
|
|
|
|
|
|
|
say $geo->get_city( '87.250.250.203', 'en' ); |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
## deprecated method (will be removed in future versions) |
435
|
|
|
|
|
|
|
say $geo->get_city('93.191.14.81'); |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
## deprecated method (will be removed in future versions) |
438
|
|
|
|
|
|
|
say $geo->get_country('93.191.14.81'); |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
=head1 DESCRIPTION |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
L is a database to detect cities by IP. |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
The database of IPs is included into distribution, but it is better to download latest version at L. |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
The database is availible with a names of the cities in Russian and English languages. |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
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. |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
=head1 SOURCE AVAILABILITY |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
The source code for this module is available from Github |
453
|
|
|
|
|
|
|
at https://github.com/kak-tus/Geo-SypexGeo |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
=head1 AUTHOR |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
Andrey Kuzmin, Ekak-tus@mail.ruE |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
=head1 CREDITS |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
vrag86 |
462
|
|
|
|
|
|
|
dimonchik-com |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
Copyright (C) 2014 by Andrey Kuzmin |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
=cut |