554 lines
19 KiB
C#
554 lines
19 KiB
C#
/*
|
|
* Copyright (c) Contributors, http://opensimulator.org/
|
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
*
|
|
* - Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
* - Neither the name of the openmetaverse.org nor the names
|
|
* of its contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
using System;
|
|
using System.Net;
|
|
using OpenMetaverse;
|
|
|
|
namespace OpenSim.Framework
|
|
{
|
|
public class RegionURI
|
|
{
|
|
private static readonly byte[] schemaSep = osUTF8.GetASCIIBytes("://");
|
|
private static readonly byte[] altschemaSep = osUTF8.GetASCIIBytes("|!!");
|
|
private static readonly byte[] nameSep = osUTF8.GetASCIIBytes(":/ ");
|
|
private static readonly byte[] altnameSep = osUTF8.GetASCIIBytes(":/ +|");
|
|
private static readonly byte[] escapePref = osUTF8.GetASCIIBytes("+%");
|
|
private static readonly byte[] altPortSepPref = osUTF8.GetASCIIBytes(":|");
|
|
private static readonly byte[] forbidonname = osUTF8.GetASCIIBytes(".,:;\\/");
|
|
|
|
public enum URIFlags : int
|
|
{
|
|
None = 0,
|
|
//Valid = 1 << 0,
|
|
HasHost = 1 << 1,
|
|
HasResolvedHost = 1 << 2,
|
|
HasUserName = 1 << 3,
|
|
HasUserPass = 1 << 4,
|
|
HasRegionName = 1 << 5,
|
|
HasCoords = 1 << 6,
|
|
|
|
IsLocalGrid = 1 << 7 // this must be set externally
|
|
}
|
|
|
|
public URIFlags Flags;
|
|
public IPAddress IP;
|
|
public string originalURI = string.Empty;
|
|
public string Schema = "http://";
|
|
public string Host = string.Empty;
|
|
public int Port = 80;
|
|
public string RegionName = string.Empty;
|
|
public string Username = string.Empty;
|
|
public string UserPass = string.Empty;
|
|
public int X = 127;
|
|
public int Y = 127;
|
|
public int Z = 2;
|
|
|
|
public RegionURI(string _originalURI)
|
|
{
|
|
originalURI = _originalURI;
|
|
Parse(_originalURI);
|
|
if (!HasHost && HasRegionName)
|
|
Flags |= URIFlags.IsLocalGrid;
|
|
}
|
|
|
|
public RegionURI(string _originalURI, GridInfo gi)
|
|
{
|
|
originalURI = _originalURI;
|
|
Parse(_originalURI);
|
|
|
|
if(!HasHost)
|
|
{
|
|
if(!HasRegionName)
|
|
Flags = URIFlags.None;
|
|
else
|
|
Flags |= URIFlags.IsLocalGrid;
|
|
return;
|
|
}
|
|
if(gi == null)
|
|
return;
|
|
|
|
if (gi.IsLocalGrid(HostUrl) == 1)
|
|
{
|
|
Host = string.Empty;
|
|
Flags &= ~URIFlags.HasHost;
|
|
Flags |= URIFlags.IsLocalGrid;
|
|
return;
|
|
}
|
|
if (!ResolveDNS())
|
|
{
|
|
Flags = URIFlags.None;
|
|
}
|
|
}
|
|
|
|
public void Parse(string inputURI)
|
|
{
|
|
Flags = URIFlags.None;
|
|
if (string.IsNullOrWhiteSpace(inputURI))
|
|
return;
|
|
|
|
osUTF8Slice input = new osUTF8Slice(inputURI);
|
|
input.SelfTrimStart((byte)' ');
|
|
input.SelfTrimStart((byte)'+');
|
|
|
|
int firstDot = input.IndexOf((byte)'.');
|
|
if (firstDot == 0)
|
|
return;
|
|
|
|
osUTF8Slice tmpSlice;
|
|
|
|
int indx = input.IndexOf(schemaSep);
|
|
if (indx == 0)
|
|
return;
|
|
if (indx < 0)
|
|
indx = input.IndexOf(altschemaSep);
|
|
if (indx == 0)
|
|
return;
|
|
if (indx > 0)
|
|
{
|
|
if (indx < 2 || input.Length < indx + 4 || (firstDot > 0 && indx > firstDot))
|
|
return;
|
|
|
|
bool issecure = false;
|
|
tmpSlice = input.SubUTF8(0, indx).Clone();
|
|
tmpSlice.ToASCIILowerSelf();
|
|
|
|
if (tmpSlice.EndsWith((byte)'s'))
|
|
{
|
|
issecure = true;
|
|
tmpSlice.SelfTrimEnd((byte)'s');
|
|
}
|
|
|
|
switch (tmpSlice.ToString())
|
|
{
|
|
case "http":
|
|
case "hg":
|
|
case "hop":
|
|
case "surl":
|
|
case "x-grid-info":
|
|
// only https has this defined
|
|
if (issecure)
|
|
{
|
|
Schema = "https://";
|
|
Port = 443;
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
indx += 3;
|
|
input.SubUTF8Self(indx);
|
|
firstDot -= indx;
|
|
}
|
|
|
|
int namestart = 0;
|
|
if (firstDot > 0)
|
|
{
|
|
int hostend = -1;
|
|
osUTF8Slice hosttmp = input.Clone();
|
|
indx = input.IndexOfAny(altPortSepPref);
|
|
if (indx > 0)
|
|
{
|
|
if (indx < firstDot)
|
|
return;
|
|
|
|
hostend = indx;
|
|
++indx;
|
|
int tmpport = 0;
|
|
byte c;
|
|
while (indx < input.Length)
|
|
{
|
|
c = input[indx];
|
|
if (c < (byte)'0' || c > (byte)'9')
|
|
break;
|
|
tmpport *= 10;
|
|
tmpport += c - (byte)'0';
|
|
++indx;
|
|
}
|
|
if (indx > hostend + 1)
|
|
{
|
|
if (tmpport > 64 * 1024)
|
|
return;
|
|
Port = tmpport;
|
|
}
|
|
input.SubUTF8Self(indx);
|
|
input.SelfTrimStart(altnameSep);
|
|
}
|
|
else
|
|
{
|
|
indx = input.IndexOfAny(altnameSep);
|
|
if (indx < 0)
|
|
{
|
|
hostend = input.Length;
|
|
namestart = -1;
|
|
}
|
|
else
|
|
{
|
|
hostend = indx;
|
|
namestart = indx + 1;
|
|
}
|
|
}
|
|
|
|
if (hostend <= 0)
|
|
return;
|
|
|
|
hosttmp.SubUTF8Self(0, hostend);
|
|
indx = hosttmp.IndexOf((byte)'@');
|
|
if (indx >= 0)
|
|
{
|
|
if (indx > 0)
|
|
{
|
|
tmpSlice = hosttmp.SubUTF8(0, indx);
|
|
int indx2 = tmpSlice.IndexOfAny(escapePref);
|
|
if (indx2 >= 0)
|
|
{
|
|
Username = Uri.UnescapeDataString(tmpSlice.ToString());
|
|
Username = Username.Replace('+', ' ');
|
|
}
|
|
else
|
|
Username = tmpSlice.ToString();
|
|
if (Username.Length > 0)
|
|
Flags |= URIFlags.HasUserName;
|
|
}
|
|
++indx;
|
|
hosttmp.SubUTF8Self(indx);
|
|
}
|
|
if (hosttmp.Length == 0)
|
|
{
|
|
Flags = URIFlags.None;
|
|
return;
|
|
}
|
|
|
|
indx = hosttmp.IndexOfAny(escapePref);
|
|
if (indx >= 0)
|
|
{
|
|
string blabla = Uri.UnescapeDataString(hosttmp.ToString());
|
|
blabla = blabla.Replace('+', ' ');
|
|
hosttmp = new osUTF8Slice(blabla);
|
|
}
|
|
hosttmp.ToASCIILowerSelf();
|
|
Host = hosttmp.ToString();
|
|
UriHostNameType type = Uri.CheckHostName(Host);
|
|
if (type == UriHostNameType.Unknown || type == UriHostNameType.Basic)
|
|
{
|
|
Flags = URIFlags.None;
|
|
return;
|
|
}
|
|
|
|
Flags |= URIFlags.HasHost;
|
|
}
|
|
|
|
if (namestart < 0 || input.Length == 0)
|
|
return;
|
|
|
|
input.SubUTF8Self(namestart);
|
|
input.SelfTrimStart((byte)' ');
|
|
input.SelfTrimStart((byte)'+');
|
|
|
|
int firstCoord = input.IndexOf((byte)'/');
|
|
if (firstCoord == 0)
|
|
{
|
|
Flags = URIFlags.None;
|
|
return;
|
|
}
|
|
if (firstCoord < 0)
|
|
firstCoord = input.IndexOf((byte)'(');
|
|
|
|
if (firstCoord > 0)
|
|
tmpSlice = input.SubUTF8(0, firstCoord);
|
|
else
|
|
tmpSlice = input;
|
|
indx = tmpSlice.IndexOfAny(escapePref);
|
|
if (indx >= 0)
|
|
{
|
|
string blabla = Uri.UnescapeDataString(tmpSlice.ToString());
|
|
blabla = blabla.Replace('+', ' ');
|
|
tmpSlice = new osUTF8Slice(blabla);
|
|
}
|
|
|
|
tmpSlice.SelfTrimEnd((byte)' ');
|
|
if (tmpSlice.Length <= 0)
|
|
return;
|
|
|
|
if(tmpSlice.IndexOfAny(forbidonname) > 0)
|
|
{
|
|
Flags = URIFlags.None;
|
|
return;
|
|
}
|
|
|
|
RegionName = tmpSlice.ToString();
|
|
Flags |= URIFlags.HasRegionName;
|
|
|
|
if (firstCoord > 0)
|
|
{
|
|
if (input[firstCoord] == (byte)'/')
|
|
{
|
|
++firstCoord;
|
|
int tmp = 0;
|
|
tmpSlice = input.SubUTF8(firstCoord);
|
|
int indx2 = 0;
|
|
while (indx2 < tmpSlice.Length)
|
|
{
|
|
byte c = tmpSlice[indx2];
|
|
if (c < (byte)'0' || c > (byte)'9')
|
|
break;
|
|
tmp *= 10;
|
|
tmp += c - (byte)'0';
|
|
++indx2;
|
|
}
|
|
if (indx2 == 0)
|
|
{
|
|
Flags = URIFlags.None;
|
|
return;
|
|
}
|
|
X = tmp;
|
|
tmpSlice.SubUTF8Self(indx2);
|
|
indx = tmpSlice.IndexOf((byte)'/');
|
|
if (indx >= 0)
|
|
{
|
|
++indx;
|
|
tmpSlice.SubUTF8Self(indx);
|
|
indx2 = 0;
|
|
tmp = 0;
|
|
while (indx2 < tmpSlice.Length)
|
|
{
|
|
byte c = tmpSlice[indx2];
|
|
if (c < (byte)'0' || c > (byte)'9')
|
|
break;
|
|
tmp *= 10;
|
|
tmp += c - (byte)'0';
|
|
++indx2;
|
|
}
|
|
if (indx2 == 0)
|
|
{
|
|
Flags = URIFlags.None;
|
|
return;
|
|
}
|
|
Y = tmp;
|
|
tmpSlice.SubUTF8Self(indx2);
|
|
indx = tmpSlice.IndexOf((byte)'/');
|
|
if (indx >= 0)
|
|
{
|
|
++indx;
|
|
tmpSlice.SubUTF8Self(indx);
|
|
indx2 = 0;
|
|
tmp = 0;
|
|
int sig = 1;
|
|
if (tmpSlice[indx2] == (byte)'-')
|
|
{
|
|
sig = -1;
|
|
indx2++;
|
|
}
|
|
while (indx2 < tmpSlice.Length)
|
|
{
|
|
byte c = tmpSlice[indx2];
|
|
if (c < (byte)'0' || c > (byte)'9')
|
|
break;
|
|
tmp *= 10;
|
|
tmp += c - (byte)'0';
|
|
++indx2;
|
|
}
|
|
if (indx2 == 0)
|
|
{
|
|
Flags = URIFlags.None;
|
|
return;
|
|
}
|
|
Z = sig * tmp;
|
|
}
|
|
}
|
|
}
|
|
else // (,,) case
|
|
{
|
|
++firstCoord;
|
|
int tmp = 0;
|
|
tmpSlice = input.SubUTF8(firstCoord);
|
|
int indx2 = tmpSlice.IndexOf((byte)')');
|
|
if (indx2 == 0)
|
|
return;
|
|
if (indx2 > 0)
|
|
tmpSlice.SubUTF8Self(0, indx2);
|
|
|
|
indx2 = 0;
|
|
tmpSlice.SelfTrimStart((byte)' ');
|
|
tmpSlice.SelfTrimStart((byte)'+');
|
|
|
|
while (indx2 < tmpSlice.Length)
|
|
{
|
|
byte c = tmpSlice[indx2];
|
|
if (c < (byte)'0' || c > (byte)'9')
|
|
break;
|
|
tmp *= 10;
|
|
tmp += c - (byte)'0';
|
|
++indx2;
|
|
}
|
|
if (indx2 == 0)
|
|
{
|
|
Flags = URIFlags.None;
|
|
return;
|
|
}
|
|
X = tmp;
|
|
tmpSlice.SubUTF8Self(indx2);
|
|
indx = tmpSlice.IndexOf((byte)',');
|
|
if (indx >= 0)
|
|
{
|
|
++indx;
|
|
tmpSlice.SubUTF8Self(indx);
|
|
tmpSlice.SelfTrimStart((byte)' ');
|
|
tmpSlice.SelfTrimStart((byte)'+');
|
|
indx2 = 0;
|
|
tmp = 0;
|
|
while (indx2 < tmpSlice.Length)
|
|
{
|
|
byte c = tmpSlice[indx2];
|
|
if (c < (byte)'0' || c > (byte)'9')
|
|
break;
|
|
tmp *= 10;
|
|
tmp += c - (byte)'0';
|
|
++indx2;
|
|
}
|
|
if (indx2 == 0)
|
|
{
|
|
Flags = URIFlags.None;
|
|
return;
|
|
}
|
|
Y = tmp;
|
|
tmpSlice.SubUTF8Self(indx2);
|
|
indx = tmpSlice.IndexOf((byte)',');
|
|
if (indx >= 0)
|
|
{
|
|
++indx;
|
|
tmpSlice.SubUTF8Self(indx);
|
|
tmpSlice.SelfTrimStart((byte)' ');
|
|
tmpSlice.SelfTrimStart((byte)'+');
|
|
indx2 = 0;
|
|
tmp = 0;
|
|
int sig = 1;
|
|
if (tmpSlice[indx2] == (byte)'-')
|
|
{
|
|
sig = -1;
|
|
indx2++;
|
|
}
|
|
while (indx2 < tmpSlice.Length)
|
|
{
|
|
byte c = tmpSlice[indx2];
|
|
if (c < (byte)'0' || c > (byte)'9')
|
|
break;
|
|
tmp *= 10;
|
|
tmp += c - (byte)'0';
|
|
++indx2;
|
|
}
|
|
if (indx2 == 0)
|
|
{
|
|
Flags = URIFlags.None;
|
|
return;
|
|
}
|
|
Z = sig * tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
public bool ResolveDNS()
|
|
{
|
|
if ((Flags & URIFlags.HasHost) != 0)
|
|
{
|
|
IPAddress ip = Util.GetHostFromDNS(Host);
|
|
if (ip != null)
|
|
{
|
|
IP = ip;
|
|
Flags |= URIFlags.HasResolvedHost;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool IsValid
|
|
{
|
|
get { return (Flags & (URIFlags.HasHost | URIFlags.HasRegionName)) != 0; }
|
|
}
|
|
|
|
public bool HasHost
|
|
{
|
|
get { return (Flags & URIFlags.HasHost) != 0; }
|
|
}
|
|
|
|
public bool HasRegionName
|
|
{
|
|
get { return (Flags & URIFlags.HasRegionName) != 0; }
|
|
}
|
|
|
|
public string HostUrl
|
|
{
|
|
get { return (Flags & URIFlags.HasHost) != 0 ? (Schema + Host + ":" + Port) : ""; }
|
|
}
|
|
|
|
public string HostUrlEndSlash
|
|
{
|
|
get { return (Flags & URIFlags.HasHost) != 0 ? (Schema + Host + ":" + Port + "/") : ""; }
|
|
}
|
|
|
|
public string RegionUrlAndName
|
|
{
|
|
get
|
|
{
|
|
string ret = (Flags & URIFlags.HasHost) != 0 ? (Schema + Host + ":" + Port + "/") : "";
|
|
if ((Flags & URIFlags.HasRegionName) != 0)
|
|
ret += RegionName;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
public string RegionHostPortSpaceName
|
|
{
|
|
get
|
|
{
|
|
string ret = (Flags & URIFlags.HasHost) != 0 ? (Host + ":" + Port + "/ ") : ""; // space needed for compatibility
|
|
if ((Flags & URIFlags.HasRegionName) != 0)
|
|
ret += RegionName;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
// this needs to be set before get
|
|
public bool IsLocalGrid
|
|
{
|
|
get { return (Flags & URIFlags.IsLocalGrid) != 0; }
|
|
set
|
|
{
|
|
if(value)
|
|
{
|
|
Host = string.Empty;
|
|
Flags &= ~URIFlags.HasHost;
|
|
Flags |= URIFlags.IsLocalGrid;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |