Files
opensim/OpenSim/Framework/RegionURI.cs
2024-03-30 23:22:30 +00:00

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;
}
}
}
}
}