/usr/share/eiskaltdcpp/luascripts/kml_geoip.lua is in eiskaltdcpp-scripts 2.2.9-4.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | kml_geoip = {}
function kml_geoip.find_record(ip_addr)
local offset = 0
local mask = 2147483648 -- 1<<31
local depth = 31
local record_length = 3
local x
repeat
local branch_offset
if ip_addr > mask then
-- Take the right-hand branch
ip_addr = ip_addr - mask
branch_offset = record_length
else
-- Take the left-hand branch
branch_offset = 0
end
local r_pos = record_length*2*offset + branch_offset
local locs = kml_geoip._locations
x = locs:byte(r_pos+3)*65536+locs:byte(r_pos+2)*256+locs:byte(r_pos+1)
if x >= kml_geoip._db_segments then
local netmask = 32 - depth
return x
end
mask = mask / 2
depth = depth - 1
offset = x
until depth == 0
assert(false)
end
function kml_geoip.extract_record(seek_record)
local record_length = 3
if seek_record == kml_geoip._db_segments then
return
end
-- one byte for country code; skip, because with a map it's superfluous
-- also, requires rather large/tedious lookup table; this is the +1 at end
-- other +1 is to adjust for Lua's 1-based position numbering
local record_ptr = seek_record + (2*record_length-1)*kml_geoip._db_segments+1+1
assert(record_ptr < string.len(kml_geoip._locations))
local function strcpy()
local null_s, null_e = string.find(kml_geoip._locations, '%z', record_ptr)
assert(null_s)
local str = string.sub(kml_geoip._locations, record_ptr, null_s - 1)
record_ptr = null_e + 1
return str
end
local function int32_repr(repr)
assert(repr)
return repr:byte(1)+repr:byte(2)*256+repr:byte(3)*65536
end
local region = strcpy()
local city = strcpy()
local postal_code = strcpy()
local latitude = int32_repr(string.sub(kml_geoip._locations, record_ptr+0, record_ptr+2))/10000-180
local longitude = int32_repr(string.sub(kml_geoip._locations, record_ptr+3, record_ptr+5))/10000-180
assert(region and city and postal_code and latitude and longitude)
return region, city, postal_code, latitude, longitude
end
function kml_geoip.ip_lookup(ip_addr)
return kml_geoip.extract_record(kml_geoip.find_record(ip_addr))
end
function kml_geoip.read_locations(filename)
local f = io.open(DC():GetConfigPath()..'/'..filename, 'rb')
local blocks = f:read('*a')
f:close()
-- find delimiter, as measured from EOF
local delim_pos = 2+string.len(blocks)
for i = 1,20 do
delim_pos = delim_pos - 4
if string.sub(blocks, delim_pos, delim_pos+2) == '\255\255\255' then
break
end
end
-- verify it's a city database
local dbType = blocks:byte(delim_pos+3)
local GEOIP_CITY_EDITION_REV0 = 6
local GEOIP_CITY_EDITION_REV1 = 2
assert(dbType == GEOIP_CITY_EDITION_REV0 or dbType == GEOIP_CITY_EDITION_REV1)
local db_segments = blocks:byte(delim_pos+4)+blocks:byte(delim_pos+5)*256+blocks:byte(delim_pos+6)*65536
return blocks, db_segments
end
-- KML writing
function kml_geoip.xml_escape(text)
-- http://www.w3.org/TR/xml/#syntax
local subst_table = {
['"'] = ' " ', ["'"] = ' ' ', ["<"] = ' < ',
[">"] = ' > ' , ["&"] = ' & ' }
local s, count = string.gsub(tostring(text), "[\"'<>&]", subst_table)
return s
end
function kml_geoip.write_placemark(hub, user, writer)
local function int32_str(str)
s, e, o1, o2, o3, o4 = string.find(str, '(%d+).(%d+).(%d+).(%d+)')
return o1*16777216+o2*65536+o3*256+o4
end
local function write_placemark_subtag(tag, text)
writer(string.format('<%s>%s</%s>', tag, text, tag), 2)
end
-- Don't pollute KMLs with users without geographical information
if user._ip == '' then return end
local region, city, postcode, lat, long = kml_geoip.ip_lookup(int32_str(user._ip))
-- Know their IP, but GeoIP database knows nothing of them.
if not region then return end
writer('<Placemark>', 1)
write_placemark_subtag('name', user._nick)
-- Write as much of an address as feasible.
local address = ''
if postcode ~= '' then
assert(region ~= '' and city ~= '')
address = string.format('%s, %s %s', city, region, postcode)
elseif city ~= '' then
assert(region ~= '')
address = string.format('%s %s', city, region)
elseif region ~= '' then
address = region
end
write_placemark_subtag('address', address)
write_placemark_subtag('description', string.format('%s (%s) on %s', user._nick, user._class, hub:getHubName()))
writer(string.format('<Point><coordinates>%f, %f</coordinates></Point>', long, lat), 2)
writer('</Placemark>', 1)
end
function kml_geoip.write_kml(hub, filename)
local f = io.open(filename, 'w')
local function write(handle, text, indentation_level)
if not indentation_level then indentation_level = 0 end
handle:write(string.rep('\t', indentation_level)..text.."\n")
end
writer = function(t,l) write(f, t, l) end
writer('<?xml version="1.0" encoding="UTF-8"?>')
writer('<kml xmlns="http://www.opengis.net/kml/2.2">\n<Document>')
for sid, user in pairs(hub._users) do
kml_geoip.write_placemark(hub, user, writer)
end
writer('</Document>\n</kml>')
f:close()
end
-- Initialization
-- Download http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
-- and gunzip it into the DC++ configuration diretory.
kml_geoip._locations, kml_geoip._db_segments = kml_geoip.read_locations('GeoLiteCity.dat')
-- Listeners
dcpp:setListener( "ownChatOut", "kml_geoip_ownchat",
function( hub, text )
if string.sub(text, 1, 1) ~= '/' then
return
end
_, _, cmd, param = string.find(text, '([^ ]*) +(.*)')
if cmd == "/help" then
-- logfunctions.injectSmallHelp(hub, param)
elseif cmd == "/kml" then
if not param then
hub:addLine("Missing parameter. Usage: /kml filename")
else
kml_geoip.write_kml(hub, param)
end
return true
end
end
)
DC():PrintDebug( " ** Loaded kml_geoip.lua **" )
|