Merge pull request #3647 from Mailaender/ip2geo
Show locations of client and servers using a local GeoIP database
This commit is contained in:
3
AUTHORS
3
AUTHORS
@@ -65,6 +65,9 @@ Also thanks to:
|
||||
* Vladimir Komarov (VrKomarov)
|
||||
* Wuschel
|
||||
|
||||
Using GeoLite data created by MaxMind and
|
||||
distributed under the CC BY-SA 3.0 license.
|
||||
|
||||
Finally, special thanks goes to the original teams
|
||||
at Westwood Studios and EA for creating the classic
|
||||
games that inspired the creation of OpenRA.
|
||||
|
||||
165
GeoIP/COPYING
Normal file
165
GeoIP/COPYING
Normal file
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
63
GeoIP/Country.cs
Normal file
63
GeoIP/Country.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (C) 2008 MaxMind Inc. All Rights Reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace GeoIP
|
||||
{
|
||||
public class Country
|
||||
{
|
||||
String code;
|
||||
String name;
|
||||
|
||||
/*
|
||||
* Creates a new Country.
|
||||
*
|
||||
* @param code the country code.
|
||||
* @param name the country name.
|
||||
*/
|
||||
public Country(String code, String name)
|
||||
{
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the ISO two-letter country code of this country.
|
||||
*
|
||||
* @return the country code.
|
||||
*/
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the name of this country.
|
||||
*
|
||||
* @return the country name.
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
106
GeoIP/DatabaseInfo.cs
Normal file
106
GeoIP/DatabaseInfo.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (C) 2008 MaxMind Inc. All Rights Reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace GeoIP
|
||||
{
|
||||
public class DatabaseInfo
|
||||
{
|
||||
public static int COUNTRY_EDITION = 1;
|
||||
public static int REGION_EDITION_REV0 = 7;
|
||||
public static int REGION_EDITION_REV1 = 3;
|
||||
public static int CITY_EDITION_REV0 = 6;
|
||||
public static int CITY_EDITION_REV1 = 2;
|
||||
public static int ORG_EDITION = 5;
|
||||
public static int ISP_EDITION = 4;
|
||||
public static int PROXY_EDITION = 8;
|
||||
public static int ASNUM_EDITION = 9;
|
||||
public static int NETSPEED_EDITION = 10;
|
||||
public static int DOMAIN_EDITION = 11;
|
||||
public static int COUNTRY_EDITION_V6 = 12;
|
||||
public static int ASNUM_EDITION_V6 = 21;
|
||||
public static int ISP_EDITION_V6 = 22;
|
||||
public static int ORG_EDITION_V6 = 23;
|
||||
public static int DOMAIN_EDITION_V6 = 24;
|
||||
public static int CITY_EDITION_REV1_V6 = 30;
|
||||
public static int CITY_EDITION_REV0_V6 = 31;
|
||||
public static int NETSPEED_EDITION_REV1 = 32;
|
||||
public static int NETSPEED_EDITION_REV1_V6 = 33;
|
||||
|
||||
private String info;
|
||||
/**
|
||||
* Creates a new DatabaseInfo object given the database info String.
|
||||
* @param info
|
||||
*/
|
||||
|
||||
public DatabaseInfo(String info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public int getType()
|
||||
{
|
||||
if ((info == null) | (info == ""))
|
||||
{
|
||||
return COUNTRY_EDITION;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the type code from the database info string and then
|
||||
// subtract 105 from the value to preserve compatability with
|
||||
// databases from April 2003 and earlier.
|
||||
return Convert.ToInt32(info.Substring(4, 3)) - 105;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date of the database.
|
||||
*
|
||||
* @return the date of the database.
|
||||
*/
|
||||
public DateTime getDate()
|
||||
{
|
||||
for (int i=0; i<info.Length-9; i++)
|
||||
{
|
||||
if (Char.IsWhiteSpace(info[i]) == true)
|
||||
{
|
||||
var dateString = info.Substring(i+1, 8);
|
||||
try
|
||||
{
|
||||
return DateTime.ParseExact(dateString,"yyyyMMdd",null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.Write(e.Message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return DateTime.Now;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
GeoIP/GeoIP.csproj
Normal file
44
GeoIP/GeoIP.csproj
Normal file
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>10.0.0</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{021DDD6A-A608-424C-9A9A-252D8A9989E0}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>GeoIP</RootNamespace>
|
||||
<AssemblyName>GeoIP</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Country.cs" />
|
||||
<Compile Include="DatabaseInfo.cs" />
|
||||
<Compile Include="Location.cs" />
|
||||
<Compile Include="LookupService.cs" />
|
||||
<Compile Include="Region.cs" />
|
||||
<Compile Include="RegionName.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
67
GeoIP/Location.cs
Normal file
67
GeoIP/Location.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (C) 2008 MaxMind Inc. All Rights Reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace GeoIP
|
||||
{
|
||||
public class Location
|
||||
{
|
||||
public String countryCode;
|
||||
public String countryName;
|
||||
public String region;
|
||||
public String city;
|
||||
public String postalCode;
|
||||
public double latitude;
|
||||
public double longitude;
|
||||
public int dma_code;
|
||||
public int area_code;
|
||||
public String regionName;
|
||||
public int metro_code;
|
||||
|
||||
private static double EARTH_DIAMETER = 2 * 6378.2;
|
||||
private static double PI = 3.14159265;
|
||||
private static double RAD_CONVERT = PI / 180;
|
||||
|
||||
public double distance (Location loc)
|
||||
{
|
||||
double delta_lat, delta_lon;
|
||||
double temp;
|
||||
|
||||
double lat1 = latitude;
|
||||
double lon1 = longitude;
|
||||
double lat2 = loc.latitude;
|
||||
double lon2 = loc.longitude;
|
||||
|
||||
// convert degrees to radians
|
||||
lat1 *= RAD_CONVERT;
|
||||
lat2 *= RAD_CONVERT;
|
||||
|
||||
// find the deltas
|
||||
delta_lat = lat2 - lat1;
|
||||
delta_lon = (lon2 - lon1) * RAD_CONVERT;
|
||||
|
||||
// Find the great circle distance
|
||||
temp = Math.Pow(Math.Sin(delta_lat/2), 2) + Math.Cos(lat1) * Math.Cos(lat2) * Math.Pow(Math.Sin(delta_lon/2), 2);
|
||||
return EARTH_DIAMETER * Math.Atan2(Math.Sqrt(temp), Math.Sqrt(1-temp));
|
||||
}
|
||||
}
|
||||
}
|
||||
999
GeoIP/LookupService.cs
Normal file
999
GeoIP/LookupService.cs
Normal file
@@ -0,0 +1,999 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (C) 2008 MaxMind Inc. All Rights Reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace GeoIP
|
||||
{
|
||||
public class LookupService
|
||||
{
|
||||
private FileStream file = null;
|
||||
private DatabaseInfo databaseInfo = null;
|
||||
private Object ioLock = new Object();
|
||||
byte databaseType = Convert.ToByte(DatabaseInfo.COUNTRY_EDITION);
|
||||
int[] databaseSegments;
|
||||
int recordLength;
|
||||
int dboptions;
|
||||
byte[] dbbuffer;
|
||||
|
||||
private static Country UNKNOWN_COUNTRY = new Country("--", "Unknown Location");
|
||||
private static int COUNTRY_BEGIN = 16776960;
|
||||
private static int STRUCTURE_INFO_MAX_SIZE = 20;
|
||||
private static int DATABASE_INFO_MAX_SIZE = 100;
|
||||
private static int FULL_RECORD_LENGTH = 100;//???
|
||||
private static int SEGMENT_RECORD_LENGTH = 3;
|
||||
private static int STANDARD_RECORD_LENGTH = 3;
|
||||
private static int ORG_RECORD_LENGTH = 4;
|
||||
private static int MAX_RECORD_LENGTH = 4;
|
||||
private static int MAX_ORG_RECORD_LENGTH = 1000;//???
|
||||
private static int FIPS_RANGE = 360;
|
||||
private static int STATE_BEGIN_REV0 = 16700000;
|
||||
private static int STATE_BEGIN_REV1 = 16000000;
|
||||
private static int US_OFFSET = 1;
|
||||
private static int CANADA_OFFSET = 677;
|
||||
private static int WORLD_OFFSET = 1353;
|
||||
public static int GEOIP_STANDARD = 0;
|
||||
public static int GEOIP_MEMORY_CACHE = 1;
|
||||
public static int GEOIP_UNKNOWN_SPEED = 0;
|
||||
public static int GEOIP_DIALUP_SPEED = 1;
|
||||
public static int GEOIP_CABLEDSL_SPEED = 2;
|
||||
public static int GEOIP_CORPORATE_SPEED = 3;
|
||||
|
||||
private static String[] countryCode = {
|
||||
"--",
|
||||
"AP", "EU", "AD", "AE", "AF", "AG", "AI", "AL", "AM", "CW",
|
||||
"AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AZ", "BA", "BB",
|
||||
"BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BM", "BN", "BO",
|
||||
"BR", "BS", "BT", "BV", "BW", "BY", "BZ", "CA", "CC", "CD",
|
||||
"CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR",
|
||||
"CU", "CV", "CX", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO",
|
||||
"DZ", "EC", "EE", "EG", "EH", "ER", "ES", "ET", "FI", "FJ",
|
||||
"FK", "FM", "FO", "FR", "SX", "GA", "GB", "GD", "GE", "GF",
|
||||
"GH", "GI", "GL", "GM", "GN", "GP", "GQ", "GR", "GS", "GT",
|
||||
"GU", "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", "ID",
|
||||
"IE", "IL", "IN", "IO", "IQ", "IR", "IS", "IT", "JM", "JO",
|
||||
"JP", "KE", "KG", "KH", "KI", "KM", "KN", "KP", "KR", "KW",
|
||||
"KY", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT",
|
||||
"LU", "LV", "LY", "MA", "MC", "MD", "MG", "MH", "MK", "ML",
|
||||
"MM", "MN", "MO", "MP", "MQ", "MR", "MS", "MT", "MU", "MV",
|
||||
"MW", "MX", "MY", "MZ", "NA", "NC", "NE", "NF", "NG", "NI",
|
||||
"NL", "NO", "NP", "NR", "NU", "NZ", "OM", "PA", "PE", "PF",
|
||||
"PG", "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", "PW",
|
||||
"PY", "QA", "RE", "RO", "RU", "RW", "SA", "SB", "SC", "SD",
|
||||
"SE", "SG", "SH", "SI", "SJ", "SK", "SL", "SM", "SN", "SO",
|
||||
"SR", "ST", "SV", "SY", "SZ", "TC", "TD", "TF", "TG", "TH",
|
||||
"TJ", "TK", "TM", "TN", "TO", "TL", "TR", "TT", "TV", "TW",
|
||||
"TZ", "UA", "UG", "UM", "US", "UY", "UZ", "VA", "VC", "VE",
|
||||
"VG", "VI", "VN", "VU", "WF", "WS", "YE", "YT", "RS", "ZA",
|
||||
"ZM", "ME", "ZW", "A1", "A2", "O1", "AX", "GG", "IM", "JE",
|
||||
"BL", "MF", "BQ", "SS", "O1" };
|
||||
|
||||
private static String[] countryName = {
|
||||
"Unknown Location",
|
||||
"Asia/Pacific Region", "Europe", "Andorra", "United Arab Emirates", "Afghanistan", "Antigua and Barbuda", "Anguilla", "Albania", "Armenia", "Curacao",
|
||||
"Angola", "Antarctica", "Argentina", "American Samoa", "Austria", "Australia", "Aruba", "Azerbaijan", "Bosnia and Herzegovina", "Barbados",
|
||||
"Bangladesh", "Belgium", "Burkina Faso", "Bulgaria", "Bahrain", "Burundi", "Benin", "Bermuda", "Brunei Darussalam", "Bolivia",
|
||||
"Brazil", "Bahamas", "Bhutan", "Bouvet Island", "Botswana", "Belarus", "Belize", "Canada", "Cocos (Keeling) Islands", "Congo, The Democratic Republic of the",
|
||||
"Central African Republic", "Congo", "Switzerland", "Cote D'Ivoire", "Cook Islands", "Chile", "Cameroon", "China", "Colombia", "Costa Rica",
|
||||
"Cuba", "Cape Verde", "Christmas Island", "Cyprus", "Czech Republic", "Germany", "Djibouti", "Denmark", "Dominica", "Dominican Republic",
|
||||
"Algeria", "Ecuador", "Estonia", "Egypt", "Western Sahara", "Eritrea", "Spain", "Ethiopia", "Finland", "Fiji",
|
||||
"Falkland Islands (Malvinas)", "Micronesia, Federated States of", "Faroe Islands", "France", "Sint Maarten (Dutch part)", "Gabon", "United Kingdom", "Grenada", "Georgia", "French Guiana",
|
||||
"Ghana", "Gibraltar", "Greenland", "Gambia", "Guinea", "Guadeloupe", "Equatorial Guinea", "Greece", "South Georgia and the South Sandwich Islands", "Guatemala",
|
||||
"Guam", "Guinea-Bissau", "Guyana", "Hong Kong", "Heard Island and McDonald Islands", "Honduras", "Croatia", "Haiti", "Hungary", "Indonesia",
|
||||
"Ireland", "Israel", "India", "British Indian Ocean Territory", "Iraq", "Iran, Islamic Republic of", "Iceland", "Italy", "Jamaica", "Jordan",
|
||||
"Japan", "Kenya", "Kyrgyzstan", "Cambodia", "Kiribati", "Comoros", "Saint Kitts and Nevis", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait",
|
||||
"Cayman Islands", "Kazakhstan", "Lao People's Democratic Republic", "Lebanon", "Saint Lucia", "Liechtenstein", "Sri Lanka", "Liberia", "Lesotho", "Lithuania",
|
||||
"Luxembourg", "Latvia", "Libya", "Morocco", "Monaco", "Moldova, Republic of", "Madagascar", "Marshall Islands", "Macedonia", "Mali",
|
||||
"Myanmar", "Mongolia", "Macau", "Northern Mariana Islands", "Martinique", "Mauritania", "Montserrat", "Malta", "Mauritius", "Maldives",
|
||||
"Malawi", "Mexico", "Malaysia", "Mozambique", "Namibia", "New Caledonia", "Niger", "Norfolk Island", "Nigeria", "Nicaragua",
|
||||
"Netherlands", "Norway", "Nepal", "Nauru", "Niue", "New Zealand", "Oman", "Panama", "Peru", "French Polynesia",
|
||||
"Papua New Guinea", "Philippines", "Pakistan", "Poland", "Saint Pierre and Miquelon", "Pitcairn Islands", "Puerto Rico", "Palestinian Territory", "Portugal", "Palau",
|
||||
"Paraguay", "Qatar", "Reunion", "Romania", "Russian Federation", "Rwanda", "Saudi Arabia", "Solomon Islands", "Seychelles", "Sudan",
|
||||
"Sweden", "Singapore", "Saint Helena", "Slovenia", "Svalbard and Jan Mayen", "Slovakia", "Sierra Leone", "San Marino", "Senegal", "Somalia", "Suriname",
|
||||
"Sao Tome and Principe", "El Salvador", "Syrian Arab Republic", "Swaziland", "Turks and Caicos Islands", "Chad", "French Southern Territories", "Togo", "Thailand",
|
||||
"Tajikistan", "Tokelau", "Turkmenistan", "Tunisia", "Tonga", "Timor-Leste", "Turkey", "Trinidad and Tobago", "Tuvalu", "Taiwan",
|
||||
"Tanzania, United Republic of", "Ukraine", "Uganda", "United States Minor Outlying Islands", "United States", "Uruguay", "Uzbekistan", "Holy See (Vatican City State)", "Saint Vincent and the Grenadines", "Venezuela",
|
||||
"Virgin Islands, British", "Virgin Islands, U.S.", "Vietnam", "Vanuatu", "Wallis and Futuna", "Samoa", "Yemen", "Mayotte", "Serbia", "South Africa",
|
||||
"Zambia", "Montenegro", "Zimbabwe", "Anonymous Proxy", "Satellite Provider", "Other", "Aland Islands", "Guernsey", "Isle of Man", "Jersey",
|
||||
"Saint Barthelemy", "Saint Martin", "Bonaire, Saint Eustatius and Saba", "South Sudan", "Other" };
|
||||
|
||||
public LookupService(String databaseFile, int options)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (ioLock)
|
||||
this.file = new FileStream(databaseFile, FileMode.Open, FileAccess.Read);
|
||||
dboptions = options;
|
||||
init();
|
||||
}
|
||||
catch(System.SystemException)
|
||||
{
|
||||
Console.WriteLine("cannot open file " + databaseFile);
|
||||
}
|
||||
}
|
||||
|
||||
public LookupService(String databaseFile):this(databaseFile, GEOIP_STANDARD) { }
|
||||
|
||||
private void init()
|
||||
{
|
||||
int i, j;
|
||||
byte[] delim = new byte[3];
|
||||
byte[] buf = new byte[SEGMENT_RECORD_LENGTH];
|
||||
databaseType = (byte)DatabaseInfo.COUNTRY_EDITION;
|
||||
recordLength = STANDARD_RECORD_LENGTH;
|
||||
|
||||
lock (ioLock)
|
||||
{
|
||||
file.Seek(-3,SeekOrigin.End);
|
||||
for (i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++)
|
||||
{
|
||||
file.Read(delim,0,3);
|
||||
if (delim[0] == 255 && delim[1] == 255 && delim[2] == 255)
|
||||
{
|
||||
databaseType = Convert.ToByte(file.ReadByte());
|
||||
if (databaseType >= 106)
|
||||
{
|
||||
// Backward compatibility with databases from April 2003 and earlier
|
||||
databaseType -= 105;
|
||||
}
|
||||
// Determine the database type.
|
||||
if (databaseType == DatabaseInfo.REGION_EDITION_REV0)
|
||||
{
|
||||
databaseSegments = new int[1];
|
||||
databaseSegments[0] = STATE_BEGIN_REV0;
|
||||
recordLength = STANDARD_RECORD_LENGTH;
|
||||
}
|
||||
else if (databaseType == DatabaseInfo.REGION_EDITION_REV1)
|
||||
{
|
||||
databaseSegments = new int[1];
|
||||
databaseSegments[0] = STATE_BEGIN_REV1;
|
||||
recordLength = STANDARD_RECORD_LENGTH;
|
||||
}
|
||||
else if (databaseType == DatabaseInfo.CITY_EDITION_REV0 ||
|
||||
databaseType == DatabaseInfo.CITY_EDITION_REV1 ||
|
||||
databaseType == DatabaseInfo.ORG_EDITION ||
|
||||
databaseType == DatabaseInfo.ORG_EDITION_V6 ||
|
||||
databaseType == DatabaseInfo.ISP_EDITION ||
|
||||
databaseType == DatabaseInfo.ISP_EDITION_V6 ||
|
||||
databaseType == DatabaseInfo.ASNUM_EDITION ||
|
||||
databaseType == DatabaseInfo.ASNUM_EDITION_V6 ||
|
||||
databaseType == DatabaseInfo.NETSPEED_EDITION_REV1 ||
|
||||
databaseType == DatabaseInfo.NETSPEED_EDITION_REV1_V6 ||
|
||||
databaseType == DatabaseInfo.CITY_EDITION_REV0_V6 ||
|
||||
databaseType == DatabaseInfo.CITY_EDITION_REV1_V6)
|
||||
{
|
||||
databaseSegments = new int[1];
|
||||
databaseSegments[0] = 0;
|
||||
if (databaseType == DatabaseInfo.CITY_EDITION_REV0 ||
|
||||
databaseType == DatabaseInfo.CITY_EDITION_REV1 ||
|
||||
databaseType == DatabaseInfo.ASNUM_EDITION_V6 ||
|
||||
databaseType == DatabaseInfo.NETSPEED_EDITION_REV1 ||
|
||||
databaseType == DatabaseInfo.NETSPEED_EDITION_REV1_V6 ||
|
||||
databaseType == DatabaseInfo.CITY_EDITION_REV0_V6 ||
|
||||
databaseType == DatabaseInfo.CITY_EDITION_REV1_V6 ||
|
||||
databaseType == DatabaseInfo.ASNUM_EDITION)
|
||||
{
|
||||
recordLength = STANDARD_RECORD_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
recordLength = ORG_RECORD_LENGTH;
|
||||
}
|
||||
file.Read(buf,0,SEGMENT_RECORD_LENGTH);
|
||||
for (j = 0; j < SEGMENT_RECORD_LENGTH; j++)
|
||||
databaseSegments[0] += (unsignedByteToInt(buf[j]) << (j * 8));
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
file.Seek(-4,SeekOrigin.Current);
|
||||
}
|
||||
}
|
||||
if ((databaseType == DatabaseInfo.COUNTRY_EDITION) ||
|
||||
(databaseType == DatabaseInfo.COUNTRY_EDITION_V6) ||
|
||||
(databaseType == DatabaseInfo.PROXY_EDITION) ||
|
||||
(databaseType == DatabaseInfo.NETSPEED_EDITION))
|
||||
{
|
||||
databaseSegments = new int[1];
|
||||
databaseSegments[0] = COUNTRY_BEGIN;
|
||||
recordLength = STANDARD_RECORD_LENGTH;
|
||||
}
|
||||
if ((dboptions & GEOIP_MEMORY_CACHE) == 1)
|
||||
{
|
||||
int l = (int) file.Length;
|
||||
dbbuffer = new byte[l];
|
||||
file.Seek(0,SeekOrigin.Begin);
|
||||
file.Read(dbbuffer,0,l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void close()
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (ioLock) { file.Close(); }
|
||||
file = null;
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
public Country getCountry(IPAddress ipAddress)
|
||||
{
|
||||
return getCountry(bytestoLong(ipAddress.GetAddressBytes()));
|
||||
}
|
||||
|
||||
public Country getCountryV6(String ipAddress)
|
||||
{
|
||||
IPAddress addr;
|
||||
try
|
||||
{
|
||||
addr = IPAddress.Parse(ipAddress);
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
return UNKNOWN_COUNTRY;
|
||||
}
|
||||
return getCountryV6(addr);
|
||||
}
|
||||
|
||||
public Country getCountry(String ipAddress)
|
||||
{
|
||||
IPAddress addr;
|
||||
try
|
||||
{
|
||||
addr = IPAddress.Parse(ipAddress);
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
return UNKNOWN_COUNTRY;
|
||||
}
|
||||
return getCountry(bytestoLong(addr.GetAddressBytes()));
|
||||
}
|
||||
|
||||
public Country getCountryV6(IPAddress ipAddress)
|
||||
{
|
||||
if (file == null)
|
||||
{
|
||||
throw new Exception("Database has been closed.");
|
||||
}
|
||||
if ((databaseType == DatabaseInfo.CITY_EDITION_REV1) |
|
||||
(databaseType == DatabaseInfo.CITY_EDITION_REV0))
|
||||
{
|
||||
var l = getLocation(ipAddress);
|
||||
if (l == null)
|
||||
return UNKNOWN_COUNTRY;
|
||||
else
|
||||
return new Country(l.countryCode, l.countryName);
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret = SeekCountryV6(ipAddress) - COUNTRY_BEGIN;
|
||||
if (ret == 0)
|
||||
return UNKNOWN_COUNTRY;
|
||||
else
|
||||
return new Country(countryCode[ret], countryName[ret]);
|
||||
}
|
||||
}
|
||||
|
||||
public Country getCountry(long ipAddress)
|
||||
{
|
||||
if (file == null)
|
||||
throw new Exception("Database has been closed.");
|
||||
if ((databaseType == DatabaseInfo.CITY_EDITION_REV1) |
|
||||
(databaseType == DatabaseInfo.CITY_EDITION_REV0))
|
||||
{
|
||||
var l = getLocation(ipAddress);
|
||||
if (l == null)
|
||||
return UNKNOWN_COUNTRY;
|
||||
else
|
||||
return new Country(l.countryCode, l.countryName);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ret = SeekCountry(ipAddress) - COUNTRY_BEGIN;
|
||||
if (ret == 0)
|
||||
return UNKNOWN_COUNTRY;
|
||||
else
|
||||
return new Country(countryCode[ret], countryName[ret]);
|
||||
}
|
||||
}
|
||||
|
||||
public int getID(String ipAddress)
|
||||
{
|
||||
IPAddress addr;
|
||||
try
|
||||
{
|
||||
addr = IPAddress.Parse(ipAddress);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
return 0;
|
||||
}
|
||||
return getID(bytestoLong(addr.GetAddressBytes()));
|
||||
}
|
||||
|
||||
public int getID(IPAddress ipAddress)
|
||||
{
|
||||
|
||||
return getID(bytestoLong(ipAddress.GetAddressBytes()));
|
||||
}
|
||||
|
||||
public int getID(long ipAddress)
|
||||
{
|
||||
if (file == null)
|
||||
throw new Exception("Database has been closed.");
|
||||
int ret = SeekCountry(ipAddress) - databaseSegments[0];
|
||||
return ret;
|
||||
}
|
||||
|
||||
public DatabaseInfo getDatabaseInfo()
|
||||
{
|
||||
if (databaseInfo != null)
|
||||
return databaseInfo;
|
||||
try
|
||||
{
|
||||
// Synchronize since we're accessing the database file.
|
||||
lock (ioLock)
|
||||
{
|
||||
bool hasStructureInfo = false;
|
||||
byte [] delim = new byte[3];
|
||||
// Advance to part of file where database info is stored.
|
||||
file.Seek(-3,SeekOrigin.End);
|
||||
for (int i=0; i<STRUCTURE_INFO_MAX_SIZE; i++)
|
||||
{
|
||||
file.Read(delim,0,3);
|
||||
if (delim[0] == 255 && delim[1] == 255 && delim[2] == 255)
|
||||
{
|
||||
hasStructureInfo = true;
|
||||
break;
|
||||
}
|
||||
file.Seek(-4,SeekOrigin.Current);
|
||||
}
|
||||
if (hasStructureInfo)
|
||||
file.Seek(-6,SeekOrigin.Current);
|
||||
else
|
||||
{
|
||||
// No structure info, must be pre Sep 2002 database, go back to end.
|
||||
file.Seek(-3,SeekOrigin.End);
|
||||
}
|
||||
// Find the database info string.
|
||||
for (int i=0; i<DATABASE_INFO_MAX_SIZE; i++)
|
||||
{
|
||||
file.Read(delim,0,3);
|
||||
if (delim[0]==0 && delim[1]==0 && delim[2]==0)
|
||||
{
|
||||
byte[] dbInfo = new byte[i];
|
||||
char[] dbInfo2 = new char[i];
|
||||
file.Read(dbInfo,0,i);
|
||||
for (int a0 = 0;a0 < i;a0++)
|
||||
dbInfo2[a0] = Convert.ToChar(dbInfo[a0]);
|
||||
// Create the database info object using the string.
|
||||
this.databaseInfo = new DatabaseInfo(new String(dbInfo2));
|
||||
return databaseInfo;
|
||||
}
|
||||
file.Seek(-4,SeekOrigin.Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
return new DatabaseInfo("");
|
||||
}
|
||||
|
||||
public Region getRegion(IPAddress ipAddress)
|
||||
{
|
||||
return getRegion(bytestoLong(ipAddress.GetAddressBytes()));
|
||||
}
|
||||
|
||||
public Region getRegion(String str)
|
||||
{
|
||||
IPAddress addr;
|
||||
try
|
||||
{
|
||||
addr = IPAddress.Parse(str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
return null;
|
||||
}
|
||||
return getRegion(bytestoLong(addr.GetAddressBytes()));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
public Region getRegion(long ipnum)
|
||||
{
|
||||
Region record = new Region();
|
||||
int seek_region = 0;
|
||||
if (databaseType == DatabaseInfo.REGION_EDITION_REV0)
|
||||
{
|
||||
seek_region = SeekCountry(ipnum) - STATE_BEGIN_REV0;
|
||||
char [] ch = new char[2];
|
||||
if (seek_region >= 1000)
|
||||
{
|
||||
record.countryCode = "US";
|
||||
record.countryName = "United States";
|
||||
ch[0] = (char)(((seek_region - 1000)/26) + 65);
|
||||
ch[1] = (char)(((seek_region - 1000)%26) + 65);
|
||||
record.region = new String(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
record.countryCode = countryCode[seek_region];
|
||||
record.countryName = countryName[seek_region];
|
||||
record.region = "";
|
||||
}
|
||||
}
|
||||
else if (databaseType == DatabaseInfo.REGION_EDITION_REV1)
|
||||
{
|
||||
seek_region = SeekCountry(ipnum) - STATE_BEGIN_REV1;
|
||||
char [] ch = new char[2];
|
||||
if (seek_region < US_OFFSET)
|
||||
{
|
||||
record.countryCode = "";
|
||||
record.countryName = "";
|
||||
record.region = "";
|
||||
} else if (seek_region < CANADA_OFFSET)
|
||||
{
|
||||
record.countryCode = "US";
|
||||
record.countryName = "United States";
|
||||
ch[0] = (char)(((seek_region - US_OFFSET)/26) + 65);
|
||||
ch[1] = (char)(((seek_region - US_OFFSET)%26) + 65);
|
||||
record.region = new String(ch);
|
||||
} else if (seek_region < WORLD_OFFSET)
|
||||
{
|
||||
record.countryCode = "CA";
|
||||
record.countryName = "Canada";
|
||||
ch[0] = (char)(((seek_region - CANADA_OFFSET)/26) + 65);
|
||||
ch[1] = (char)(((seek_region - CANADA_OFFSET)%26) + 65);
|
||||
record.region = new String(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
record.countryCode = countryCode[(seek_region - WORLD_OFFSET) / FIPS_RANGE];
|
||||
record.countryName = countryName[(seek_region - WORLD_OFFSET) / FIPS_RANGE];
|
||||
record.region = "";
|
||||
}
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
public Location getLocation(IPAddress addr)
|
||||
{
|
||||
return getLocation(bytestoLong(addr.GetAddressBytes()));
|
||||
}
|
||||
|
||||
public Location getLocationV6(String str)
|
||||
{
|
||||
IPAddress addr;
|
||||
try
|
||||
{
|
||||
addr = IPAddress.Parse(str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
return null;
|
||||
}
|
||||
|
||||
return getLocationV6(addr);
|
||||
}
|
||||
|
||||
public Location getLocation(String str)
|
||||
{
|
||||
IPAddress addr;
|
||||
try
|
||||
{
|
||||
addr = IPAddress.Parse(str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
return null;
|
||||
}
|
||||
|
||||
return getLocation(bytestoLong(addr.GetAddressBytes()));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
public Location getLocationV6(IPAddress addr)
|
||||
{
|
||||
int record_pointer;
|
||||
byte[] record_buf = new byte[FULL_RECORD_LENGTH];
|
||||
char[] record_buf2 = new char[FULL_RECORD_LENGTH];
|
||||
int record_buf_offset = 0;
|
||||
Location record = new Location();
|
||||
int str_length = 0;
|
||||
int j, Seek_country;
|
||||
double latitude = 0, longitude = 0;
|
||||
|
||||
try
|
||||
{
|
||||
Seek_country = SeekCountryV6(addr);
|
||||
if (Seek_country == databaseSegments[0])
|
||||
return null;
|
||||
|
||||
record_pointer = Seek_country + ((2 * recordLength - 1) * databaseSegments[0]);
|
||||
if ((dboptions & GEOIP_MEMORY_CACHE) == 1)
|
||||
Array.Copy(dbbuffer, record_pointer, record_buf, 0, Math.Min(dbbuffer.Length - record_pointer, FULL_RECORD_LENGTH));
|
||||
else
|
||||
{
|
||||
lock (ioLock)
|
||||
{
|
||||
file.Seek(record_pointer,SeekOrigin.Begin);
|
||||
file.Read(record_buf,0,FULL_RECORD_LENGTH);
|
||||
}
|
||||
}
|
||||
for (int a0 = 0;a0 < FULL_RECORD_LENGTH;a0++)
|
||||
record_buf2[a0] = Convert.ToChar(record_buf[a0]);
|
||||
|
||||
// get country
|
||||
record.countryCode = countryCode[unsignedByteToInt(record_buf[0])];
|
||||
record.countryName = countryName[unsignedByteToInt(record_buf[0])];
|
||||
record_buf_offset++;
|
||||
|
||||
// get region
|
||||
while (record_buf[record_buf_offset + str_length] != '\0')
|
||||
str_length++;
|
||||
if (str_length > 0)
|
||||
record.region = new String(record_buf2, record_buf_offset, str_length);
|
||||
record_buf_offset += str_length + 1;
|
||||
str_length = 0;
|
||||
|
||||
// get region_name
|
||||
record.regionName = RegionName.getRegionName( record.countryCode, record.region );
|
||||
|
||||
// get city
|
||||
while (record_buf[record_buf_offset + str_length] != '\0')
|
||||
str_length++;
|
||||
if (str_length > 0)
|
||||
record.city = new String(record_buf2, record_buf_offset, str_length);
|
||||
record_buf_offset += (str_length + 1);
|
||||
str_length = 0;
|
||||
|
||||
// get postal code
|
||||
while (record_buf[record_buf_offset + str_length] != '\0')
|
||||
str_length++;
|
||||
if (str_length > 0)
|
||||
record.postalCode = new String(record_buf2, record_buf_offset, str_length);
|
||||
record_buf_offset += (str_length + 1);
|
||||
|
||||
// get latitude
|
||||
for (j = 0; j < 3; j++)
|
||||
latitude += (unsignedByteToInt(record_buf[record_buf_offset + j]) << (j * 8));
|
||||
record.latitude = (float)latitude/10000 - 180;
|
||||
record_buf_offset += 3;
|
||||
|
||||
// get longitude
|
||||
for (j = 0; j < 3; j++)
|
||||
longitude += (unsignedByteToInt(record_buf[record_buf_offset + j]) << (j * 8));
|
||||
record.longitude = (float)longitude/10000 - 180;
|
||||
|
||||
record.metro_code = record.dma_code = 0;
|
||||
record.area_code = 0;
|
||||
if (databaseType == DatabaseInfo.CITY_EDITION_REV1
|
||||
||databaseType == DatabaseInfo.CITY_EDITION_REV1_V6)
|
||||
{
|
||||
// get metro_code
|
||||
int metroarea_combo = 0;
|
||||
if (record.countryCode == "US")
|
||||
{
|
||||
record_buf_offset += 3;
|
||||
for (j = 0; j < 3; j++)
|
||||
metroarea_combo += (unsignedByteToInt(record_buf[record_buf_offset + j]) << (j * 8));
|
||||
record.metro_code = record.dma_code = metroarea_combo/1000;
|
||||
record.area_code = metroarea_combo % 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
Console.WriteLine("IO Exception while seting up segments");
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
public Location getLocation(long ipnum)
|
||||
{
|
||||
int record_pointer;
|
||||
byte[] record_buf = new byte[FULL_RECORD_LENGTH];
|
||||
char[] record_buf2 = new char[FULL_RECORD_LENGTH];
|
||||
int record_buf_offset = 0;
|
||||
Location record = new Location();
|
||||
int str_length = 0;
|
||||
int j, Seek_country;
|
||||
double latitude = 0, longitude = 0;
|
||||
|
||||
try
|
||||
{
|
||||
Seek_country = SeekCountry(ipnum);
|
||||
if (Seek_country == databaseSegments[0])
|
||||
return null;
|
||||
|
||||
record_pointer = Seek_country + ((2 * recordLength - 1) * databaseSegments[0]);
|
||||
if ((dboptions & GEOIP_MEMORY_CACHE) == 1)
|
||||
Array.Copy(dbbuffer, record_pointer, record_buf, 0, Math.Min(dbbuffer.Length - record_pointer, FULL_RECORD_LENGTH));
|
||||
else
|
||||
{
|
||||
lock (ioLock)
|
||||
{
|
||||
file.Seek(record_pointer,SeekOrigin.Begin);
|
||||
file.Read(record_buf,0,FULL_RECORD_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
for (int a0 = 0;a0 < FULL_RECORD_LENGTH;a0++)
|
||||
record_buf2[a0] = Convert.ToChar(record_buf[a0]);
|
||||
|
||||
// get country
|
||||
record.countryCode = countryCode[unsignedByteToInt(record_buf[0])];
|
||||
record.countryName = countryName[unsignedByteToInt(record_buf[0])];
|
||||
record_buf_offset++;
|
||||
|
||||
// get region
|
||||
while (record_buf[record_buf_offset + str_length] != '\0')
|
||||
str_length++;
|
||||
if (str_length > 0)
|
||||
record.region = new String(record_buf2, record_buf_offset, str_length);
|
||||
record_buf_offset += str_length + 1;
|
||||
str_length = 0;
|
||||
|
||||
// get region_name
|
||||
record.regionName = RegionName.getRegionName(record.countryCode, record.region);
|
||||
|
||||
// get city
|
||||
while (record_buf[record_buf_offset + str_length] != '\0')
|
||||
str_length++;
|
||||
if (str_length > 0)
|
||||
record.city = new String(record_buf2, record_buf_offset, str_length);
|
||||
|
||||
record_buf_offset += (str_length + 1);
|
||||
str_length = 0;
|
||||
|
||||
// get postal code
|
||||
while (record_buf[record_buf_offset + str_length] != '\0')
|
||||
str_length++;
|
||||
if (str_length > 0)
|
||||
record.postalCode = new String(record_buf2, record_buf_offset, str_length);
|
||||
record_buf_offset += (str_length + 1);
|
||||
|
||||
// get latitude
|
||||
for (j = 0; j < 3; j++)
|
||||
latitude += (unsignedByteToInt(record_buf[record_buf_offset + j]) << (j * 8));
|
||||
record.latitude = (float) latitude/10000 - 180;
|
||||
record_buf_offset += 3;
|
||||
|
||||
// get longitude
|
||||
for (j = 0; j < 3; j++)
|
||||
longitude += (unsignedByteToInt(record_buf[record_buf_offset + j]) << (j * 8));
|
||||
record.longitude = (float) longitude/10000 - 180;
|
||||
|
||||
record.metro_code = record.dma_code = 0;
|
||||
record.area_code = 0;
|
||||
if (databaseType == DatabaseInfo.CITY_EDITION_REV1)
|
||||
{
|
||||
// get metro_code
|
||||
int metroarea_combo = 0;
|
||||
if (record.countryCode == "US"){
|
||||
record_buf_offset += 3;
|
||||
for (j = 0; j < 3; j++)
|
||||
metroarea_combo += (unsignedByteToInt(record_buf[record_buf_offset + j]) << (j * 8));
|
||||
record.metro_code = record.dma_code = metroarea_combo/1000;
|
||||
record.area_code = metroarea_combo % 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
Console.WriteLine("IO Exception while seting up segments");
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
public String getOrg(IPAddress addr)
|
||||
{
|
||||
return getOrg(bytestoLong(addr.GetAddressBytes()));
|
||||
}
|
||||
|
||||
public String getOrgV6(String str)
|
||||
{
|
||||
IPAddress addr;
|
||||
try
|
||||
{
|
||||
addr = IPAddress.Parse(str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
return null;
|
||||
}
|
||||
return getOrgV6(addr);
|
||||
}
|
||||
|
||||
public String getOrg(String str)
|
||||
{
|
||||
IPAddress addr;
|
||||
try
|
||||
{
|
||||
addr = IPAddress.Parse(str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
return null;
|
||||
}
|
||||
return getOrg(bytestoLong(addr.GetAddressBytes()));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
public String getOrgV6( IPAddress addr)
|
||||
{
|
||||
int Seek_org;
|
||||
int record_pointer;
|
||||
int str_length = 0;
|
||||
byte[] buf = new byte[MAX_ORG_RECORD_LENGTH];
|
||||
char[] buf2 = new char[MAX_ORG_RECORD_LENGTH];
|
||||
String org_buf;
|
||||
|
||||
try
|
||||
{
|
||||
Seek_org = SeekCountryV6(addr);
|
||||
if (Seek_org == databaseSegments[0])
|
||||
return null;
|
||||
|
||||
record_pointer = Seek_org + (2 * recordLength - 1) * databaseSegments[0];
|
||||
if ((dboptions & GEOIP_MEMORY_CACHE) == 1)
|
||||
Array.Copy(dbbuffer, record_pointer, buf, 0, Math.Min(dbbuffer.Length - record_pointer, MAX_ORG_RECORD_LENGTH));
|
||||
else
|
||||
{
|
||||
lock (ioLock)
|
||||
{
|
||||
file.Seek(record_pointer,SeekOrigin.Begin);
|
||||
file.Read(buf,0,MAX_ORG_RECORD_LENGTH);
|
||||
}
|
||||
}
|
||||
while (buf[str_length] != 0)
|
||||
{
|
||||
buf2[str_length] = Convert.ToChar(buf[str_length]);
|
||||
str_length++;
|
||||
}
|
||||
buf2[str_length] = '\0';
|
||||
org_buf = new String(buf2,0,str_length);
|
||||
return org_buf;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
Console.WriteLine("IO Exception");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
public String getOrg(long ipnum)
|
||||
{
|
||||
int Seek_org;
|
||||
int record_pointer;
|
||||
int str_length = 0;
|
||||
byte [] buf = new byte[MAX_ORG_RECORD_LENGTH];
|
||||
char [] buf2 = new char[MAX_ORG_RECORD_LENGTH];
|
||||
String org_buf;
|
||||
|
||||
try
|
||||
{
|
||||
Seek_org = SeekCountry(ipnum);
|
||||
if (Seek_org == databaseSegments[0])
|
||||
return null;
|
||||
|
||||
record_pointer = Seek_org + (2 * recordLength - 1) * databaseSegments[0];
|
||||
if ((dboptions & GEOIP_MEMORY_CACHE) == 1)
|
||||
Array.Copy(dbbuffer, record_pointer, buf, 0, Math.Min(dbbuffer.Length - record_pointer, MAX_ORG_RECORD_LENGTH));
|
||||
else
|
||||
{
|
||||
lock (ioLock)
|
||||
{
|
||||
file.Seek(record_pointer,SeekOrigin.Begin);
|
||||
file.Read(buf,0,MAX_ORG_RECORD_LENGTH);
|
||||
}
|
||||
}
|
||||
while (buf[str_length] != 0)
|
||||
{
|
||||
buf2[str_length] = Convert.ToChar(buf[str_length]);
|
||||
str_length++;
|
||||
}
|
||||
buf2[str_length] = '\0';
|
||||
org_buf = new String(buf2, 0, str_length);
|
||||
return org_buf;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
Console.WriteLine("IO Exception");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
int SeekCountryV6(IPAddress ipAddress)
|
||||
{
|
||||
byte [] v6vec = ipAddress.GetAddressBytes();
|
||||
byte [] buf = new byte[2 * MAX_RECORD_LENGTH];
|
||||
int [] x = new int[2];
|
||||
int offset = 0;
|
||||
|
||||
for (int depth = 127; depth >= 0; depth--)
|
||||
{
|
||||
try
|
||||
{
|
||||
if ((dboptions & GEOIP_MEMORY_CACHE) == 1)
|
||||
{
|
||||
for (int i = 0;i < (2 * MAX_RECORD_LENGTH);i++)
|
||||
buf[i] = dbbuffer[i+(2 * recordLength * offset)];
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (ioLock)
|
||||
{
|
||||
file.Seek(2 * recordLength * offset,SeekOrigin.Begin);
|
||||
file.Read(buf,0,2 * MAX_RECORD_LENGTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
Console.WriteLine("IO Exception");
|
||||
}
|
||||
for (int i = 0; i<2; i++)
|
||||
{
|
||||
x[i] = 0;
|
||||
for (int j = 0; j<recordLength; j++)
|
||||
{
|
||||
int y = buf[(i*recordLength)+j];
|
||||
if (y < 0)
|
||||
{
|
||||
y+= 256;
|
||||
}
|
||||
x[i] += (y << (j * 8));
|
||||
}
|
||||
}
|
||||
|
||||
int bnum = 127 - depth;
|
||||
int idx = bnum >> 3;
|
||||
int b_mask = 1 << ( bnum & 7 ^ 7 );
|
||||
if ((v6vec[idx] & b_mask) > 0)
|
||||
{
|
||||
if (x[1] >= databaseSegments[0])
|
||||
{
|
||||
return x[1];
|
||||
}
|
||||
offset = x[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x[0] >= databaseSegments[0])
|
||||
{
|
||||
return x[0];
|
||||
}
|
||||
offset = x[0];
|
||||
}
|
||||
}
|
||||
|
||||
// shouldn't reach here
|
||||
Console.WriteLine("Error Seeking country while Seeking " + ipAddress);
|
||||
return 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
int SeekCountry(long ipAddress)
|
||||
{
|
||||
byte [] buf = new byte[2 * MAX_RECORD_LENGTH];
|
||||
int [] x = new int[2];
|
||||
int offset = 0;
|
||||
for (int depth = 31; depth >= 0; depth--)
|
||||
{
|
||||
try
|
||||
{
|
||||
if ((dboptions & GEOIP_MEMORY_CACHE) == 1)
|
||||
{
|
||||
for (int i = 0;i < (2 * MAX_RECORD_LENGTH);i++)
|
||||
{
|
||||
buf[i] = dbbuffer[i+(2 * recordLength * offset)];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (ioLock)
|
||||
{
|
||||
file.Seek(2 * recordLength * offset,SeekOrigin.Begin);
|
||||
file.Read(buf, 0, 2 * MAX_RECORD_LENGTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
Console.WriteLine("IO Exception");
|
||||
}
|
||||
for (int i = 0; i<2; i++)
|
||||
{
|
||||
x[i] = 0;
|
||||
for (int j = 0; j<recordLength; j++)
|
||||
{
|
||||
int y = buf[(i*recordLength)+j];
|
||||
if (y < 0)
|
||||
y+= 256;
|
||||
x[i] += (y << (j * 8));
|
||||
}
|
||||
}
|
||||
|
||||
if ((ipAddress & (1 << depth)) > 0)
|
||||
{
|
||||
if (x[1] >= databaseSegments[0])
|
||||
return x[1];
|
||||
offset = x[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x[0] >= databaseSegments[0])
|
||||
return x[0];
|
||||
offset = x[0];
|
||||
}
|
||||
}
|
||||
|
||||
// shouldn't reach here
|
||||
Console.WriteLine("Error Seeking country while Seeking " + ipAddress);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long swapbytes(long ipAddress)
|
||||
{
|
||||
return (((ipAddress>>0) & 255) << 24) | (((ipAddress>>8) & 255) << 16)
|
||||
| (((ipAddress>>16) & 255) << 8) | (((ipAddress>>24) & 255) << 0);
|
||||
}
|
||||
|
||||
static long bytestoLong(byte [] address)
|
||||
{
|
||||
long ipnum = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
long y = address[i];
|
||||
if (y < 0)
|
||||
y += 256;
|
||||
ipnum += y << ((3-i)*8);
|
||||
}
|
||||
return ipnum;
|
||||
}
|
||||
|
||||
static int unsignedByteToInt(byte b)
|
||||
{
|
||||
return (int) b & 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
GeoIP/Region.cs
Normal file
53
GeoIP/Region.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (C) 2008 MaxMind Inc. All Rights Reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
public class Region
|
||||
{
|
||||
public String countryCode;
|
||||
public String countryName;
|
||||
public String region;
|
||||
|
||||
public Region() { }
|
||||
|
||||
public Region(String countryCode,String countryName,String region)
|
||||
{
|
||||
this.countryCode = countryCode;
|
||||
this.countryName = countryName;
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public String getcountryCode()
|
||||
{
|
||||
return countryCode;
|
||||
}
|
||||
|
||||
public String getcountryName()
|
||||
{
|
||||
return countryName;
|
||||
}
|
||||
|
||||
public String getregion()
|
||||
{
|
||||
return region;
|
||||
}
|
||||
}
|
||||
4489
GeoIP/RegionName.cs
Normal file
4489
GeoIP/RegionName.cs
Normal file
File diff suppressed because it is too large
Load Diff
12
Makefile
12
Makefile
@@ -71,6 +71,13 @@ fileformats_LIBS = $(COMMON_LIBS) thirdparty/Tao/Tao.Sdl.dll System.Windows.Form
|
||||
PROGRAMS = fileformats
|
||||
fileformats: $(fileformats_TARGET)
|
||||
|
||||
geoip_SRCS := $(shell find GeoIP/ -iname '*.cs')
|
||||
geoip_TARGET = GeoIP.dll
|
||||
geoip_KIND = library
|
||||
geoip_LIBS = $(COMMON_LIBS)
|
||||
PROGRAMS += geoip
|
||||
geoip: $(geoip_TARGET)
|
||||
|
||||
game_SRCS := $(shell find OpenRA.Game/ -iname '*.cs')
|
||||
game_TARGET = OpenRA.Game.exe
|
||||
game_KIND = winexe
|
||||
@@ -125,8 +132,8 @@ STD_MOD_DEPS = $(STD_MOD_LIBS) $(ralint_TARGET)
|
||||
mod_ra_SRCS := $(shell find OpenRA.Mods.RA/ -iname '*.cs')
|
||||
mod_ra_TARGET = mods/ra/OpenRA.Mods.RA.dll
|
||||
mod_ra_KIND = library
|
||||
mod_ra_DEPS = $(STD_MOD_DEPS) $(utility_TARGET)
|
||||
mod_ra_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(utility_TARGET)
|
||||
mod_ra_DEPS = $(STD_MOD_DEPS) $(utility_TARGET) $(geoip_TARGET)
|
||||
mod_ra_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(utility_TARGET) $(geoip_TARGET)
|
||||
mod_ra_EXTRA_CMDS = mono --debug RALint.exe ra
|
||||
PROGRAMS += mod_ra
|
||||
mod_ra: $(mod_ra_TARGET)
|
||||
@@ -292,6 +299,7 @@ install-core: default
|
||||
@$(INSTALL_PROGRAM) $(mod_d2k_TARGET) "$(DATA_INSTALL_DIR)/mods/d2k"
|
||||
|
||||
@$(INSTALL_DATA) "global mix database.dat" "$(DATA_INSTALL_DIR)/global mix database.dat"
|
||||
@$(INSTALL_DATA) "GeoIP.dat" "$(DATA_INSTALL_DIR)/GeoIP.dat"
|
||||
@$(INSTALL_DATA) AUTHORS "$(DATA_INSTALL_DIR)/AUTHORS"
|
||||
|
||||
@$(CP_R) glsl "$(DATA_INSTALL_DIR)"
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
<Compile Include="Support\Log.cs" />
|
||||
<Compile Include="Support\Stopwatch.cs" />
|
||||
<Compile Include="Support\Timer.cs" />
|
||||
<Compile Include="Thirdparty\Random.cs" />
|
||||
<Compile Include="Random.cs" />
|
||||
<Compile Include="TypeDictionary.cs" />
|
||||
<Compile Include="WPos.cs" />
|
||||
<Compile Include="WVec.cs" />
|
||||
|
||||
@@ -16,7 +16,7 @@ using Mono.Nat.Upnp;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
class UPnP
|
||||
public class UPnP
|
||||
{
|
||||
public static INatDevice NatDevice;
|
||||
|
||||
|
||||
@@ -75,6 +75,10 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\thirdparty\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Nat">
|
||||
<HintPath>..\thirdparty\Mono.Nat.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Activities\LegacyCaptureActor.cs" />
|
||||
@@ -478,6 +482,10 @@
|
||||
<Project>{F33337BE-CB69-4B24-850F-07D23E408DDF}</Project>
|
||||
<Name>OpenRA.Utility</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GeoIP\GeoIP.csproj">
|
||||
<Project>{021DDD6A-A608-424C-9A9A-252D8A9989E0}</Project>
|
||||
<Name>GeoIP</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||
|
||||
@@ -27,9 +27,16 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
var latency = widget.Get<LabelWidget>("LATENCY");
|
||||
var latencyFont = Game.Renderer.Fonts[latency.Font];
|
||||
|
||||
var latencyPrefix = widget.Get<LabelWidget>("LATENCY_PREFIX");
|
||||
var latencyPrefixFont = Game.Renderer.Fonts[latencyPrefix.Font];
|
||||
|
||||
var ip = widget.Get<LabelWidget>("IP");
|
||||
var ipFont = Game.Renderer.Fonts[ip.Font];
|
||||
|
||||
var location = widget.Get<LabelWidget>("LOCATION");
|
||||
var locationFont = Game.Renderer.Fonts[location.Font];
|
||||
|
||||
var locationOffset = location.Bounds.Y;
|
||||
var ipOffset = ip.Bounds.Y;
|
||||
var latencyOffset = latency.Bounds.Y;
|
||||
var tooltipHeight = widget.Bounds.Height;
|
||||
@@ -39,27 +46,42 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
tooltipContainer.IsVisible = () => (orderManager.LobbyInfo.ClientWithIndex(clientIndex) != null);
|
||||
tooltipContainer.BeforeRender = () =>
|
||||
{
|
||||
var width = Math.Max(adminFont.Measure(admin.GetText()).X, Math.Max(ipFont.Measure(ip.GetText()).X, latencyFont.Measure(latency.GetText()).X));
|
||||
var latencyPrefixSize = latencyPrefix.Bounds.X + latencyPrefixFont.Measure(latencyPrefix.GetText()+" ").X;
|
||||
var width = Math.Max(locationFont.Measure(location.GetText()).X, (Math.Max(adminFont.Measure(admin.GetText()).X,
|
||||
Math.Max(ipFont.Measure(ip.GetText()).X, latencyPrefixSize + latencyFont.Measure(latency.GetText()).X))));
|
||||
widget.Bounds.Width = width + 2 * margin;
|
||||
latency.Bounds.Width = widget.Bounds.Width;
|
||||
ip.Bounds.Width = widget.Bounds.Width;
|
||||
admin.Bounds.Width = widget.Bounds.Width;
|
||||
location.Bounds.Width = widget.Bounds.Width;
|
||||
|
||||
ip.Bounds.Y = ipOffset;
|
||||
latency.Bounds.Y = latencyOffset;
|
||||
location.Bounds.Y = locationOffset;
|
||||
widget.Bounds.Height = tooltipHeight;
|
||||
|
||||
if (admin.IsVisible())
|
||||
{
|
||||
ip.Bounds.Y += admin.Bounds.Height;
|
||||
latency.Bounds.Y += admin.Bounds.Height;
|
||||
location.Bounds.Y += admin.Bounds.Height;
|
||||
widget.Bounds.Height += admin.Bounds.Height;
|
||||
}
|
||||
|
||||
latencyPrefix.Bounds.Y = latency.Bounds.Y;
|
||||
latency.Bounds.X = latencyPrefixSize;
|
||||
};
|
||||
|
||||
admin.IsVisible = () => orderManager.LobbyInfo.ClientWithIndex(clientIndex).IsAdmin;
|
||||
latency.GetText = () => "Latency: {0}".F(LobbyUtils.LatencyDescription(orderManager.LobbyInfo.ClientWithIndex(clientIndex).Latency));
|
||||
ip.GetText = () => LobbyUtils.DescriptiveIpAddress(orderManager.LobbyInfo.ClientWithIndex(clientIndex).IpAddress);
|
||||
latency.GetText = () => LobbyUtils.LatencyDescription(orderManager.LobbyInfo.ClientWithIndex(clientIndex).Latency);
|
||||
latency.GetColor = () => LobbyUtils.LatencyColor(orderManager.LobbyInfo.ClientWithIndex(clientIndex).Latency);
|
||||
var ipAddress = orderManager.LobbyInfo.ClientWithIndex(clientIndex).IpAddress;
|
||||
if ((ipAddress == null || ipAddress == "127.0.0.1") && UPnP.NatDevice != null)
|
||||
ipAddress = UPnP.NatDevice.GetExternalIP().ToString();
|
||||
var cachedDescriptiveIP = LobbyUtils.DescriptiveIpAddress(ipAddress);
|
||||
ip.GetText = () => cachedDescriptiveIP;
|
||||
var cachedCountryLookup = LobbyUtils.LookupCountry(ipAddress);
|
||||
location.GetText = () => cachedCountryLookup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,6 +194,13 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
return ip;
|
||||
}
|
||||
|
||||
public static string LookupCountry(string ip)
|
||||
{
|
||||
var ip2geo = new GeoIP.LookupService("GeoIP.dat", GeoIP.LookupService.GEOIP_MEMORY_CACHE);
|
||||
var country = ip2geo.getCountry(ip);
|
||||
return country.getName();
|
||||
}
|
||||
|
||||
public static void SetupClientWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, bool visible)
|
||||
{
|
||||
parent.Get("ADMIN_INDICATOR").IsVisible = () => c.IsAdmin;
|
||||
|
||||
@@ -238,6 +238,11 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
version.GetText = () => GenerateModsLabel(game);
|
||||
version.IsVisible = () => !game.CompatibleVersion();
|
||||
|
||||
var location = item.Get<LabelWidget>("LOCATION");
|
||||
var cachedServerLocation = LobbyUtils.LookupCountry(game.Address.Split(':')[0]);
|
||||
location.GetText = () => cachedServerLocation;
|
||||
location.IsVisible = () => game.CompatibleVersion();
|
||||
|
||||
if (!canJoin)
|
||||
{
|
||||
title.GetColor = () => Color.Gray;
|
||||
@@ -246,6 +251,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
state.GetColor = () => Color.Gray;
|
||||
ip.GetColor = () => Color.Gray;
|
||||
version.GetColor = () => Color.Gray;
|
||||
location.GetColor = () => Color.Gray;
|
||||
}
|
||||
|
||||
if (!Filtered(game))
|
||||
|
||||
192
OpenRA.sln
192
OpenRA.sln
@@ -35,6 +35,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Mods.D2k", "OpenRA.M
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Mods.TS", "OpenRA.Mods.TS\OpenRA.Mods.TS.csproj", "{5457CBF5-4CE4-421E-A8BF-9FD6C9732E1D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeoIP", "GeoIP\GeoIP.csproj", "{021DDD6A-A608-424C-9A9A-252D8A9989E0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -51,6 +53,14 @@ Global
|
||||
{00038B75-405B-44F5-8691-BD2546DBE224}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{00038B75-405B-44F5-8691-BD2546DBE224}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{00038B75-405B-44F5-8691-BD2546DBE224}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{021DDD6A-A608-424C-9A9A-252D8A9989E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{021DDD6A-A608-424C-9A9A-252D8A9989E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{021DDD6A-A608-424C-9A9A-252D8A9989E0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{021DDD6A-A608-424C-9A9A-252D8A9989E0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{021DDD6A-A608-424C-9A9A-252D8A9989E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{021DDD6A-A608-424C-9A9A-252D8A9989E0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{021DDD6A-A608-424C-9A9A-252D8A9989E0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{021DDD6A-A608-424C-9A9A-252D8A9989E0}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{0C4AEC1A-E7D5-4114-8CCD-3EEC82872981}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0C4AEC1A-E7D5-4114-8CCD-3EEC82872981}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0C4AEC1A-E7D5-4114-8CCD-3EEC82872981}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
@@ -157,6 +167,188 @@ Global
|
||||
EndGlobalSection
|
||||
GlobalSection(MonoDevelopProperties) = preSolution
|
||||
StartupItem = OpenRA.Game\OpenRA.Game.csproj
|
||||
Policies = $0
|
||||
$0.DotNetNamingPolicy = $1
|
||||
$1.DirectoryNamespaceAssociation = None
|
||||
$1.ResourceNamePolicy = FileFormatDefault
|
||||
$0.TextStylePolicy = $2
|
||||
$2.TabsToSpaces = False
|
||||
$2.NoTabsAfterNonTabs = True
|
||||
$2.inheritsSet = VisualStudio
|
||||
$2.inheritsScope = text/plain
|
||||
$2.scope = text/x-csharp
|
||||
$0.CSharpFormattingPolicy = $3
|
||||
$3.IndentSwitchBody = True
|
||||
$3.AnonymousMethodBraceStyle = NextLine
|
||||
$3.PropertyBraceStyle = NextLine
|
||||
$3.PropertyGetBraceStyle = NextLine
|
||||
$3.PropertySetBraceStyle = NextLine
|
||||
$3.EventBraceStyle = NextLine
|
||||
$3.EventAddBraceStyle = NextLine
|
||||
$3.EventRemoveBraceStyle = NextLine
|
||||
$3.StatementBraceStyle = NextLine
|
||||
$3.ArrayInitializerBraceStyle = NextLine
|
||||
$3.BeforeMethodDeclarationParentheses = False
|
||||
$3.BeforeMethodCallParentheses = False
|
||||
$3.BeforeConstructorDeclarationParentheses = False
|
||||
$3.BeforeDelegateDeclarationParentheses = False
|
||||
$3.NewParentheses = False
|
||||
$3.inheritsSet = Mono
|
||||
$3.inheritsScope = text/x-csharp
|
||||
$3.scope = text/x-csharp
|
||||
$0.TextStylePolicy = $4
|
||||
$4.inheritsSet = VisualStudio
|
||||
$4.inheritsScope = text/plain
|
||||
$4.scope = text/plain
|
||||
$0.TextStylePolicy = $5
|
||||
$5.inheritsSet = null
|
||||
$5.scope = text/microsoft-resx
|
||||
$0.XmlFormattingPolicy = $6
|
||||
$6.inheritsSet = null
|
||||
$6.scope = text/microsoft-resx
|
||||
$0.TextStylePolicy = $7
|
||||
$7.inheritsSet = Mono
|
||||
$7.inheritsScope = text/plain
|
||||
$7.scope = application/xml
|
||||
$0.XmlFormattingPolicy = $8
|
||||
$8.inheritsSet = Mono
|
||||
$8.inheritsScope = application/xml
|
||||
$8.scope = application/xml
|
||||
$0.StandardHeader = $9
|
||||
$9.Text = @#region Copyright & License Information\n/*\n * Copyright 2007-${Year} The OpenRA Developers (see AUTHORS)\n * This file is part of OpenRA, which is free software. It is made\n * available to you under the terms of the GNU General Public License\n * as published by the Free Software Foundation. For more information,\n * see COPYING.\n */\n#endregion
|
||||
$9.IncludeInNewFiles = True
|
||||
$0.NameConventionPolicy = $10
|
||||
$10.Rules = $11
|
||||
$11.NamingRule = $12
|
||||
$12.Name = Namespaces
|
||||
$12.AffectedEntity = Namespace
|
||||
$12.VisibilityMask = VisibilityMask
|
||||
$12.NamingStyle = PascalCase
|
||||
$12.IncludeInstanceMembers = True
|
||||
$12.IncludeStaticEntities = True
|
||||
$11.NamingRule = $13
|
||||
$13.Name = Types
|
||||
$13.AffectedEntity = Class, Struct, Enum, Delegate
|
||||
$13.VisibilityMask = Public
|
||||
$13.NamingStyle = PascalCase
|
||||
$13.IncludeInstanceMembers = True
|
||||
$13.IncludeStaticEntities = True
|
||||
$11.NamingRule = $14
|
||||
$14.Name = Interfaces
|
||||
$14.RequiredPrefixes = $15
|
||||
$15.String = I
|
||||
$14.AffectedEntity = Interface
|
||||
$14.VisibilityMask = Public
|
||||
$14.NamingStyle = PascalCase
|
||||
$14.IncludeInstanceMembers = True
|
||||
$14.IncludeStaticEntities = True
|
||||
$11.NamingRule = $16
|
||||
$16.Name = Attributes
|
||||
$16.RequiredSuffixes = $17
|
||||
$17.String = Attribute
|
||||
$16.AffectedEntity = CustomAttributes
|
||||
$16.VisibilityMask = Public
|
||||
$16.NamingStyle = PascalCase
|
||||
$16.IncludeInstanceMembers = True
|
||||
$16.IncludeStaticEntities = True
|
||||
$11.NamingRule = $18
|
||||
$18.Name = Event Arguments
|
||||
$18.RequiredSuffixes = $19
|
||||
$19.String = EventArgs
|
||||
$18.AffectedEntity = CustomEventArgs
|
||||
$18.VisibilityMask = Public
|
||||
$18.NamingStyle = PascalCase
|
||||
$18.IncludeInstanceMembers = True
|
||||
$18.IncludeStaticEntities = True
|
||||
$11.NamingRule = $20
|
||||
$20.Name = Exceptions
|
||||
$20.RequiredSuffixes = $21
|
||||
$21.String = Exception
|
||||
$20.AffectedEntity = CustomExceptions
|
||||
$20.VisibilityMask = VisibilityMask
|
||||
$20.NamingStyle = PascalCase
|
||||
$20.IncludeInstanceMembers = True
|
||||
$20.IncludeStaticEntities = True
|
||||
$11.NamingRule = $22
|
||||
$22.Name = Methods
|
||||
$22.AffectedEntity = Methods
|
||||
$22.VisibilityMask = Protected, Public
|
||||
$22.NamingStyle = PascalCase
|
||||
$22.IncludeInstanceMembers = True
|
||||
$22.IncludeStaticEntities = True
|
||||
$11.NamingRule = $23
|
||||
$23.Name = Static Readonly Fields
|
||||
$23.AffectedEntity = ReadonlyField
|
||||
$23.VisibilityMask = Protected, Public
|
||||
$23.NamingStyle = PascalCase
|
||||
$23.IncludeInstanceMembers = False
|
||||
$23.IncludeStaticEntities = True
|
||||
$11.NamingRule = $24
|
||||
$24.Name = Fields
|
||||
$24.AffectedEntity = Field
|
||||
$24.VisibilityMask = Protected, Public
|
||||
$24.NamingStyle = PascalCase
|
||||
$24.IncludeInstanceMembers = True
|
||||
$24.IncludeStaticEntities = True
|
||||
$11.NamingRule = $25
|
||||
$25.Name = ReadOnly Fields
|
||||
$25.AffectedEntity = ReadonlyField
|
||||
$25.VisibilityMask = Protected, Public
|
||||
$25.NamingStyle = PascalCase
|
||||
$25.IncludeInstanceMembers = True
|
||||
$25.IncludeStaticEntities = False
|
||||
$11.NamingRule = $26
|
||||
$26.Name = Constant Fields
|
||||
$26.AffectedEntity = ConstantField
|
||||
$26.VisibilityMask = Protected, Public
|
||||
$26.NamingStyle = PascalCase
|
||||
$26.IncludeInstanceMembers = True
|
||||
$26.IncludeStaticEntities = True
|
||||
$11.NamingRule = $27
|
||||
$27.Name = Properties
|
||||
$27.AffectedEntity = Property
|
||||
$27.VisibilityMask = Protected, Public
|
||||
$27.NamingStyle = PascalCase
|
||||
$27.IncludeInstanceMembers = True
|
||||
$27.IncludeStaticEntities = True
|
||||
$11.NamingRule = $28
|
||||
$28.Name = Events
|
||||
$28.AffectedEntity = Event
|
||||
$28.VisibilityMask = Protected, Public
|
||||
$28.NamingStyle = PascalCase
|
||||
$28.IncludeInstanceMembers = True
|
||||
$28.IncludeStaticEntities = True
|
||||
$11.NamingRule = $29
|
||||
$29.Name = Enum Members
|
||||
$29.AffectedEntity = EnumMember
|
||||
$29.VisibilityMask = VisibilityMask
|
||||
$29.NamingStyle = PascalCase
|
||||
$29.IncludeInstanceMembers = True
|
||||
$29.IncludeStaticEntities = True
|
||||
$11.NamingRule = $30
|
||||
$30.Name = Parameters
|
||||
$30.AffectedEntity = Parameter
|
||||
$30.VisibilityMask = VisibilityMask
|
||||
$30.NamingStyle = CamelCase
|
||||
$30.IncludeInstanceMembers = True
|
||||
$30.IncludeStaticEntities = True
|
||||
$11.NamingRule = $31
|
||||
$31.Name = Type Parameters
|
||||
$31.RequiredPrefixes = $32
|
||||
$32.String = T
|
||||
$31.AffectedEntity = TypeParameter
|
||||
$31.VisibilityMask = VisibilityMask
|
||||
$31.NamingStyle = PascalCase
|
||||
$31.IncludeInstanceMembers = True
|
||||
$31.IncludeStaticEntities = True
|
||||
$0.VersionControlPolicy = $33
|
||||
$33.inheritsSet = Mono
|
||||
$0.ChangeLogPolicy = $34
|
||||
$34.UpdateMode = None
|
||||
$34.MessageStyle = $35
|
||||
$35.LineAlign = 0
|
||||
$34.inheritsSet = Mono
|
||||
outputpath = .
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -68,6 +68,12 @@ Container@SERVERBROWSER_PANEL:
|
||||
Y:40
|
||||
Align:Right
|
||||
Height:25
|
||||
Label@LOCATION:
|
||||
Width:140
|
||||
X:PARENT_RIGHT-150
|
||||
Y:40
|
||||
Align:Right
|
||||
Height:25
|
||||
Label@PROGRESS_LABEL:
|
||||
X:(PARENT_RIGHT - WIDTH) / 2
|
||||
Y:PARENT_BOTTOM / 2 - HEIGHT
|
||||
|
||||
@@ -130,7 +130,7 @@ Background@SPAWN_TOOLTIP:
|
||||
Background@CLIENT_TOOLTIP:
|
||||
Logic:ClientTooltipLogic
|
||||
Background:panel-black
|
||||
Height:35
|
||||
Height:47
|
||||
Width:5
|
||||
Children:
|
||||
Label@ADMIN:
|
||||
@@ -145,8 +145,18 @@ Background@CLIENT_TOOLTIP:
|
||||
Height:10
|
||||
Font:TinyBold
|
||||
Align:Center
|
||||
Label@LATENCY:
|
||||
Label@LOCATION:
|
||||
Y:17
|
||||
Height:10
|
||||
Font:TinyBold
|
||||
Align:Center
|
||||
Label@LATENCY_PREFIX:
|
||||
X:10
|
||||
Y:29
|
||||
Height:10
|
||||
Font:TinyBold
|
||||
Text:Latency:
|
||||
Label@LATENCY:
|
||||
Y:29
|
||||
Height:10
|
||||
Font:TinyBold
|
||||
@@ -60,7 +60,7 @@ Background@SPAWN_TOOLTIP:
|
||||
Background@CLIENT_TOOLTIP:
|
||||
Logic:ClientTooltipLogic
|
||||
Background:dialog3
|
||||
Height:39
|
||||
Height:51
|
||||
Width:7
|
||||
Children:
|
||||
Label@ADMIN:
|
||||
@@ -75,8 +75,18 @@ Background@CLIENT_TOOLTIP:
|
||||
Height:10
|
||||
Font:TinyBold
|
||||
Align:Center
|
||||
Label@LATENCY:
|
||||
Label@LOCATION:
|
||||
Y:19
|
||||
Height:10
|
||||
Font:TinyBold
|
||||
Align:Center
|
||||
Label@LATENCY_PREFIX:
|
||||
X:10
|
||||
Y:31
|
||||
Height:10
|
||||
Font:TinyBold
|
||||
Text:Latency:
|
||||
Label@LATENCY:
|
||||
Y:31
|
||||
Height:10
|
||||
Font:TinyBold
|
||||
@@ -95,6 +95,12 @@ Background@JOINSERVER_BG:
|
||||
Y:40
|
||||
Align:Right
|
||||
Height:25
|
||||
Label@LOCATION:
|
||||
Width:140
|
||||
X:PARENT_RIGHT-150
|
||||
Y:40
|
||||
Align:Right
|
||||
Height:25
|
||||
Label@PROGRESS_LABEL:
|
||||
X:(PARENT_RIGHT - WIDTH) / 2
|
||||
Y:PARENT_BOTTOM / 2 - HEIGHT
|
||||
|
||||
@@ -60,7 +60,7 @@ Background@SPAWN_TOOLTIP:
|
||||
Background@CLIENT_TOOLTIP:
|
||||
Logic:ClientTooltipLogic
|
||||
Background:dialog4
|
||||
Height:39
|
||||
Height:51
|
||||
Width:7
|
||||
Children:
|
||||
Label@ADMIN:
|
||||
@@ -75,8 +75,18 @@ Background@CLIENT_TOOLTIP:
|
||||
Height:10
|
||||
Font:TinyBold
|
||||
Align:Center
|
||||
Label@LATENCY:
|
||||
Label@LOCATION:
|
||||
Y:19
|
||||
Height:10
|
||||
Font:TinyBold
|
||||
Align:Center
|
||||
Label@LATENCY_PREFIX:
|
||||
X:10
|
||||
Y:31
|
||||
Height:10
|
||||
Font:TinyBold
|
||||
Text:Latency:
|
||||
Label@LATENCY:
|
||||
Y:31
|
||||
Height:10
|
||||
Font:TinyBold
|
||||
@@ -34,7 +34,7 @@ FILES=('OpenRA.Game.exe' 'OpenRA.Editor.exe' 'OpenRA.Utility.exe' \
|
||||
'cg' 'glsl' 'mods/ra' 'mods/cnc' 'mods/d2k' \
|
||||
'AUTHORS' 'COPYING' 'HACKING' 'INSTALL' 'CHANGELOG' \
|
||||
'README.html' 'CONTRIBUTING.html' 'DOCUMENTATION.html' \
|
||||
'global mix database.dat')
|
||||
'global mix database.dat' 'GeoIP.dll' 'GeoIP.dat')
|
||||
|
||||
echo "Copying files..."
|
||||
for i in "${FILES[@]}"; do
|
||||
|
||||
@@ -94,6 +94,8 @@ Section "Client" Client
|
||||
File "${SRCDIR}\Tao.*.dll"
|
||||
File "${SRCDIR}\SharpFont.dll"
|
||||
File "${SRCDIR}\global mix database.dat"
|
||||
File "${SRCDIR}\GeoIP.dll
|
||||
File "${SRCDIR}\GeoIP.dat"
|
||||
|
||||
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
|
||||
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
|
||||
@@ -281,6 +283,8 @@ Function ${UN}Clean
|
||||
Delete $INSTDIR\cgGL.dll
|
||||
Delete $INSTDIR\zlib1.dll
|
||||
Delete "$INSTDIR\global mix database.dat"
|
||||
Delete $INSTDIR\GeoIP.dat
|
||||
Delete $INSTDIR\GeoIP.dll
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenRA"
|
||||
Delete $INSTDIR\uninstaller.exe
|
||||
RMDir $INSTDIR
|
||||
|
||||
Reference in New Issue
Block a user