You've already forked linux-packaging-mono
360 lines
12 KiB
C#
360 lines
12 KiB
C#
//
|
|
// System.UriParser abstract class
|
|
//
|
|
// Author:
|
|
// Sebastien Pouliot <sebastien@ximian.com>
|
|
//
|
|
// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
|
|
using System.Collections;
|
|
using System.Globalization;
|
|
using System.Security.Permissions;
|
|
using System.Text;
|
|
|
|
namespace System {
|
|
public abstract class UriParser {
|
|
|
|
static object lock_object = new object ();
|
|
static Hashtable table;
|
|
|
|
internal string scheme_name;
|
|
private int default_port;
|
|
|
|
protected UriParser ()
|
|
{
|
|
}
|
|
|
|
// protected methods
|
|
protected internal virtual string GetComponents (Uri uri, UriComponents components, UriFormat format)
|
|
{
|
|
if ((format < UriFormat.UriEscaped) || (format > UriFormat.SafeUnescaped))
|
|
throw new ArgumentOutOfRangeException ("format");
|
|
|
|
if ((components & UriComponents.SerializationInfoString) != 0) {
|
|
if (components != UriComponents.SerializationInfoString)
|
|
throw new ArgumentOutOfRangeException ("components", "UriComponents.SerializationInfoString must not be combined with other UriComponents.");
|
|
|
|
if (!uri.IsAbsoluteUri)
|
|
return UriHelper.FormatRelative (uri.OriginalString, "", format);
|
|
|
|
components |= UriComponents.AbsoluteUri;
|
|
}
|
|
|
|
return GetComponentsHelper (uri, components, format);
|
|
}
|
|
|
|
internal string GetComponentsHelper (Uri uri, UriComponents components, UriFormat format)
|
|
{
|
|
UriElements elements = UriParseComponents.ParseComponents (uri.OriginalString.Trim (), UriKind.Absolute);
|
|
|
|
string scheme = scheme_name;
|
|
int dp = default_port;
|
|
|
|
if ((scheme == null) || (scheme == "*")) {
|
|
scheme = elements.scheme;
|
|
dp = Uri.GetDefaultPort (scheme);
|
|
} else if (String.Compare (scheme, elements.scheme, true) != 0) {
|
|
throw new SystemException ("URI Parser: scheme mismatch: " + scheme + " vs. " + elements.scheme);
|
|
}
|
|
|
|
var formatFlags = UriHelper.FormatFlags.None;
|
|
if (UriHelper.HasCharactersToNormalize (uri.OriginalString))
|
|
formatFlags |= UriHelper.FormatFlags.HasUriCharactersToNormalize;
|
|
|
|
if (uri.UserEscaped)
|
|
formatFlags |= UriHelper.FormatFlags.UserEscaped;
|
|
|
|
if (!string.IsNullOrEmpty (elements.host))
|
|
formatFlags |= UriHelper.FormatFlags.HasHost;
|
|
|
|
// it's easier to answer some case directly (as the output isn't identical
|
|
// when mixed with others components, e.g. leading slash, # ...)
|
|
switch (components) {
|
|
case UriComponents.Scheme:
|
|
return scheme;
|
|
case UriComponents.UserInfo:
|
|
return elements.user ?? "";
|
|
case UriComponents.Host:
|
|
return elements.host;
|
|
case UriComponents.Port: {
|
|
int p = elements.port;
|
|
if (p >= 0 && p != dp)
|
|
return p.ToString (CultureInfo.InvariantCulture);
|
|
return String.Empty;
|
|
}
|
|
case UriComponents.Path:
|
|
var path = elements.path;
|
|
if (scheme != Uri.UriSchemeMailto && scheme != Uri.UriSchemeNews)
|
|
path = IgnoreFirstCharIf (elements.path, '/');
|
|
return UriHelper.FormatAbsolute (path, scheme, UriComponents.Path, format, formatFlags);
|
|
case UriComponents.Query:
|
|
return UriHelper.FormatAbsolute (elements.query, scheme, UriComponents.Query, format, formatFlags);
|
|
case UriComponents.Fragment:
|
|
return UriHelper.FormatAbsolute (elements.fragment, scheme, UriComponents.Fragment, format, formatFlags);
|
|
case UriComponents.StrongPort: {
|
|
return elements.port >= 0
|
|
? elements.port.ToString (CultureInfo.InvariantCulture)
|
|
: dp.ToString (CultureInfo.InvariantCulture);
|
|
}
|
|
case UriComponents.SerializationInfoString:
|
|
components = UriComponents.AbsoluteUri;
|
|
break;
|
|
}
|
|
|
|
// now we deal with multiple flags...
|
|
|
|
StringBuilder sb = new StringBuilder ();
|
|
|
|
if ((components & UriComponents.Scheme) != 0) {
|
|
sb.Append (scheme);
|
|
sb.Append (elements.delimiter);
|
|
}
|
|
|
|
if ((components & UriComponents.UserInfo) != 0) {
|
|
string userinfo = elements.user;
|
|
if (userinfo != null) {
|
|
sb.Append (elements.user);
|
|
sb.Append ('@');
|
|
}
|
|
}
|
|
|
|
if ((components & UriComponents.Host) != 0)
|
|
sb.Append (elements.host);
|
|
|
|
// for StrongPort always show port - even if -1
|
|
// otherwise only display if ut's not the default port
|
|
if ((components & UriComponents.StrongPort) != 0) {
|
|
sb.Append (":");
|
|
if (elements.port >= 0) {
|
|
sb.Append (elements.port);
|
|
} else {
|
|
sb.Append (dp);
|
|
}
|
|
}
|
|
|
|
if ((components & UriComponents.Port) != 0) {
|
|
int p = elements.port;
|
|
if (p >= 0 && p != dp) {
|
|
sb.Append (":");
|
|
sb.Append (elements.port);
|
|
}
|
|
}
|
|
|
|
if ((components & UriComponents.Path) != 0) {
|
|
string path = elements.path;
|
|
if ((components & UriComponents.PathAndQuery) != 0 &&
|
|
(path.Length == 0 || !path.StartsWith ("/", StringComparison.Ordinal)) &&
|
|
elements.delimiter == Uri.SchemeDelimiter)
|
|
sb.Append ("/");
|
|
sb.Append (UriHelper.FormatAbsolute (path, scheme, UriComponents.Path, format, formatFlags));
|
|
}
|
|
|
|
if ((components & UriComponents.Query) != 0) {
|
|
string query = elements.query;
|
|
if (query != null) {
|
|
sb.Append ("?");
|
|
sb.Append (UriHelper.FormatAbsolute (query, scheme, UriComponents.Query, format, formatFlags));
|
|
}
|
|
}
|
|
|
|
if ((components & UriComponents.Fragment) != 0) {
|
|
string f = elements.fragment;
|
|
if (f != null) {
|
|
sb.Append ("#");
|
|
sb.Append (UriHelper.FormatAbsolute (f, scheme, UriComponents.Fragment, format, formatFlags));
|
|
}
|
|
}
|
|
return sb.ToString ();
|
|
}
|
|
|
|
protected internal virtual void InitializeAndValidate (Uri uri, out UriFormatException parsingError)
|
|
{
|
|
// bad boy, it should check null arguments.
|
|
if ((uri.Scheme != scheme_name) && (scheme_name != "*"))
|
|
// Here .NET seems to return "The Authority/Host could not be parsed", but it does not make sense.
|
|
parsingError = new UriFormatException ("The argument Uri's scheme does not match");
|
|
else
|
|
parsingError = null;
|
|
}
|
|
|
|
protected internal virtual bool IsBaseOf (Uri baseUri, Uri relativeUri)
|
|
{
|
|
if (baseUri == null)
|
|
throw new ArgumentNullException ("baseUri");
|
|
if (relativeUri == null)
|
|
throw new ArgumentNullException ("relativeUri");
|
|
|
|
// compare, not case sensitive, the scheme, host and port (+ user informations)
|
|
if (Uri.Compare (baseUri, relativeUri, UriComponents.SchemeAndServer | UriComponents.UserInfo, UriFormat.Unescaped, StringComparison.InvariantCultureIgnoreCase) != 0)
|
|
return false;
|
|
|
|
string base_string = baseUri.LocalPath;
|
|
int last_slash = base_string.LastIndexOf ('/') + 1; // keep the slash
|
|
return (String.Compare (base_string, 0, relativeUri.LocalPath, 0, last_slash, StringComparison.InvariantCultureIgnoreCase) == 0);
|
|
}
|
|
|
|
protected internal virtual bool IsWellFormedOriginalString (Uri uri)
|
|
{
|
|
// well formed according to RFC2396 and RFC2732
|
|
// see Uri.IsWellFormedOriginalString for some docs
|
|
|
|
// Though this class does not seem to do anything. Even null arguments aren't checked :/
|
|
return uri.IsWellFormedOriginalString ();
|
|
}
|
|
protected internal virtual UriParser OnNewUri ()
|
|
{
|
|
// nice time for init
|
|
return this;
|
|
}
|
|
|
|
[MonoTODO]
|
|
protected virtual void OnRegister (string schemeName, int defaultPort)
|
|
{
|
|
// unit tests shows that schemeName and defaultPort aren't usable from here
|
|
}
|
|
|
|
[MonoTODO]
|
|
protected internal virtual string Resolve (Uri baseUri, Uri relativeUri, out UriFormatException parsingError)
|
|
{
|
|
// used by Uri.ctor and Uri.TryCreate
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
// internal properties
|
|
|
|
internal string SchemeName {
|
|
get { return scheme_name; }
|
|
set { scheme_name = value; }
|
|
}
|
|
|
|
internal int DefaultPort {
|
|
get { return default_port; }
|
|
set { default_port = value; }
|
|
}
|
|
|
|
// private stuff
|
|
|
|
private string IgnoreFirstCharIf (string s, char c)
|
|
{
|
|
if (s.Length == 0)
|
|
return String.Empty;
|
|
if (s[0] == c)
|
|
return s.Substring (1);
|
|
return s;
|
|
}
|
|
|
|
// static methods
|
|
|
|
private static void CreateDefaults ()
|
|
{
|
|
if (table != null)
|
|
return;
|
|
|
|
Hashtable newtable = new Hashtable ();
|
|
InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeFile, -1);
|
|
InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeFtp, 21);
|
|
InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeGopher, 70);
|
|
InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeHttp, 80);
|
|
InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeHttps, 443);
|
|
InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeMailto, 25);
|
|
InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNetPipe, -1);
|
|
InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNetTcp, -1);
|
|
InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNews, -1);
|
|
InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNntp, 119);
|
|
// not defined in Uri.UriScheme* but a parser class exists
|
|
InternalRegister (newtable, new DefaultUriParser (), "ldap", 389);
|
|
|
|
lock (lock_object) {
|
|
if (table == null)
|
|
table = newtable;
|
|
else
|
|
newtable = null;
|
|
}
|
|
}
|
|
|
|
public static bool IsKnownScheme (string schemeName)
|
|
{
|
|
if (schemeName == null)
|
|
throw new ArgumentNullException ("schemeName");
|
|
if (schemeName.Length == 0)
|
|
throw new ArgumentOutOfRangeException ("schemeName");
|
|
|
|
CreateDefaults ();
|
|
string lc = schemeName.ToLower (CultureInfo.InvariantCulture);
|
|
return (table [lc] != null);
|
|
}
|
|
|
|
// *no* check version
|
|
private static void InternalRegister (Hashtable table, UriParser uriParser, string schemeName, int defaultPort)
|
|
{
|
|
uriParser.SchemeName = schemeName;
|
|
uriParser.DefaultPort = defaultPort;
|
|
|
|
// FIXME: MS doesn't seems to call most inherited parsers
|
|
if (uriParser is GenericUriParser) {
|
|
table.Add (schemeName, uriParser);
|
|
} else {
|
|
DefaultUriParser parser = new DefaultUriParser ();
|
|
parser.SchemeName = schemeName;
|
|
parser.DefaultPort = defaultPort;
|
|
table.Add (schemeName, parser);
|
|
}
|
|
|
|
// note: we cannot set schemeName and defaultPort inside OnRegister
|
|
uriParser.OnRegister (schemeName, defaultPort);
|
|
}
|
|
|
|
[SecurityPermission (SecurityAction.Demand, Infrastructure = true)]
|
|
public static void Register (UriParser uriParser, string schemeName, int defaultPort)
|
|
{
|
|
if (uriParser == null)
|
|
throw new ArgumentNullException ("uriParser");
|
|
if (schemeName == null)
|
|
throw new ArgumentNullException ("schemeName");
|
|
if ((defaultPort < -1) || (defaultPort >= UInt16.MaxValue))
|
|
throw new ArgumentOutOfRangeException ("defaultPort");
|
|
|
|
CreateDefaults ();
|
|
|
|
string lc = schemeName.ToLower (CultureInfo.InvariantCulture);
|
|
if (table [lc] != null) {
|
|
string msg = Locale.GetText ("Scheme '{0}' is already registred.");
|
|
throw new InvalidOperationException (msg);
|
|
}
|
|
InternalRegister (table, uriParser, lc, defaultPort);
|
|
}
|
|
|
|
internal static UriParser GetParser (string schemeName)
|
|
{
|
|
if (schemeName == null)
|
|
return null;
|
|
|
|
CreateDefaults ();
|
|
|
|
string lc = schemeName.ToLower (CultureInfo.InvariantCulture);
|
|
return (UriParser) table [lc];
|
|
}
|
|
}
|
|
}
|
|
|