diff options
Diffstat (limited to 'Runtime/Managed/CrossDomainPolicyParser')
30 files changed, 6152 insertions, 0 deletions
diff --git a/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.csproj b/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.csproj new file mode 100644 index 0000000..07b5653 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.csproj @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +  <PropertyGroup> +    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> +    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> +    <ProductVersion>9.0.30729</ProductVersion> +    <SchemaVersion>2.0</SchemaVersion> +    <ProjectGuid>{31C2F345-D887-49DD-A1F6-741CABD74A42}</ProjectGuid> +    <OutputType>Library</OutputType> +    <AppDesignerFolder>Properties</AppDesignerFolder> +    <RootNamespace>CrossDomainPolicyParser</RootNamespace> +    <AssemblyName>CrossDomainPolicyParser</AssemblyName> +    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> +    <FileAlignment>512</FileAlignment> +  </PropertyGroup> +  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> +    <DebugSymbols>true</DebugSymbols> +    <DebugType>full</DebugType> +    <Optimize>false</Optimize> +    <OutputPath>bin\</OutputPath> +    <DefineConstants>DEBUG;TRACE</DefineConstants> +    <ErrorReport>prompt</ErrorReport> +    <WarningLevel>4</WarningLevel> +  </PropertyGroup> +  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> +    <DebugType>pdbonly</DebugType> +    <Optimize>true</Optimize> +    <OutputPath>bin\</OutputPath> +    <DefineConstants>TRACE</DefineConstants> +    <ErrorReport>prompt</ErrorReport> +    <WarningLevel>4</WarningLevel> +  </PropertyGroup> +  <ItemGroup> +    <Compile Include="Mono.Forks\AddressFamily.cs" /> +    <Compile Include="Mono.Forks\BaseDomainPolicy.cs" /> +    <Compile Include="Mono.Forks\CrossDomainPolicyManager.cs" /> +    <Compile Include="Mono.Forks\FlashCrossDomainPolicy.cs" /> +    <Compile Include="Mono.Forks\FlashCrossDomainPolicyParser.cs" /> +    <Compile Include="Mono.Forks\ICrossDomainPolicy.cs" /> +    <Compile Include="Mono.Forks\IPAddress.cs" /> +    <Compile Include="Mono.Forks\IPv6Address.cs" /> +    <Compile Include="Mono.Forks\Locale.cs" /> +    <Compile Include="Mono.Forks\MiniParser.cs" /> +    <Compile Include="Mono.Forks\NoAccessPolicy.cs" /> +    <Compile Include="Mono.Forks\PolicyDownloadPolicy.cs" /> +    <Compile Include="Mono.Forks\SiteOfOriginPolicy.cs" /> +    <Compile Include="Mono.Forks\UnityExtra.cs" /> +    <Compile Include="Mono.Forks\Uri.cs" /> +    <Compile Include="Mono.Forks\UriFormatException.cs" /> +    <Compile Include="Mono.Forks\UriHostNameType.cs" /> +    <Compile Include="Mono.Forks\UriKind.cs" /> +    <Compile Include="Mono.Forks\UriPartial.cs" /> +    <Compile Include="Properties\AssemblyInfo.cs" /> +    <Compile Include="UnityCrossDomainHelper.cs" /> +    <Compile Include="UriTools.cs" /> +  </ItemGroup> +  <ItemGroup> +    <Reference Include="UnityEngine"> +      <HintPath>..\..\..\build\ManagedAssemblies\UnityEngine.dll</HintPath> +      <Private>False</Private> +    </Reference> +  </ItemGroup> +  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> +  <!-- To modify your build process, add your task inside one of the targets below and uncomment it.  +       Other similar extension points exist, see Microsoft.Common.targets. +  <Target Name="BeforeBuild"> +  </Target> +  <Target Name="AfterBuild"> +  </Target> +  --> +</Project> diff --git a/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.jam b/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.jam new file mode 100644 index 0000000..e56959a --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.jam @@ -0,0 +1,46 @@ +SubDir TOP Runtime Managed CrossDomainPolicyParser ; + +ActiveProject CrossDomainPolicyParser ; + +NotFile CrossDomainPolicyParser ; +local csfiles = @($(TOP)/Runtime/Managed/CrossDomainPolicyParser/**/*.cs:W=$(TOP)/:X=Tests) ; + +# Globbing hack +if $(PLATFORM) in linux32 linux64 +{ +	csfiles = [ Split [ Shell "find $(TOP)/Runtime/Managed/CrossDomainPolicyParser -name '*.cs'" ] : "; +" ] ; +	csfiles = $(csfiles:W=$(TOP)/) ; +	csfiles = $(csfiles:X=Tests) ; +} +SEARCH on $(csfiles:G=CrossDomainPolicyParser) = $(TOP) ; + +local unityenginelocation_crossdomain = $(unityenginelocation_editor) ; +unityenginelocation_crossdomain ?= $(unityenginelocation_webplayer) ; + +local targetdirs ; +local tempdir ; +if $(OS) = NT +{ +	tempdir = $(TOP)/build/temp ; +	targetdirs += $(TOP)/build/WindowsEditor/Data/Managed ; +	targetdirs += $(TOP)/build/WindowsWebplayer/Data/lib ; +	targetdirs += $(TOP)/build/Windows64WebPlayer/Data/lib ; +	targetdirs += $(TOP)/build/Windows64Editor/Data/Managed ; +} else +{ +	tempdir = $(TOP)/build/temp ; +	targetdirs += $(TOP)/build/MacEditor/Unity.app/Contents/Frameworks/Managed ; +} + +local temp = $(tempdir)/CrossDomainPolicyParser.dll ; +MkDir $(tempdir) ; +Depends $(temp) : $(tempdir) ; +Depends $(temp) : $(unityenginelocation_crossdomain) ; + +BuildAssembly CrossDomainPolicyParser : 2.0 : $(temp) : $(csfiles) : : $(unityenginelocation_crossdomain:T) ; + +for targetdir in $(targetdirs) +{ +	CopyFile : $(targetdir)/CrossDomainPolicyParser.dll : $(temp) ; +} diff --git a/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.sln b/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.sln new file mode 100644 index 0000000..f8bd348 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossDomainPolicyParser", "CrossDomainPolicyParser.csproj", "{31C2F345-D887-49DD-A1F6-741CABD74A42}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossDomainPolicyParserTests", "Tests\CrossDomainPolicyParserTests.csproj", "{5C04DB87-3B34-43B4-B164-86CE4361DC7B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityEngine", "..\..\..\Projects\CSharp\UnityEngine.csproj", "{F0499708-3EB6-4026-8362-97E6FFC4E7C8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.TestableAssemblyMaker", "..\..\..\Tools\Unity.TestableAssemblyMaker\Unity.TestableAssemblyMaker.csproj", "{3C2CC5A1-0663-458A-AF4B-52248977DDF3}" +EndProject +Global +	GlobalSection(SolutionConfigurationPlatforms) = preSolution +		Debug|Any CPU = Debug|Any CPU +		Release|Any CPU = Release|Any CPU +	EndGlobalSection +	GlobalSection(ProjectConfigurationPlatforms) = postSolution +		{31C2F345-D887-49DD-A1F6-741CABD74A42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU +		{31C2F345-D887-49DD-A1F6-741CABD74A42}.Debug|Any CPU.Build.0 = Debug|Any CPU +		{31C2F345-D887-49DD-A1F6-741CABD74A42}.Release|Any CPU.ActiveCfg = Release|Any CPU +		{31C2F345-D887-49DD-A1F6-741CABD74A42}.Release|Any CPU.Build.0 = Release|Any CPU +		{3C2CC5A1-0663-458A-AF4B-52248977DDF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU +		{3C2CC5A1-0663-458A-AF4B-52248977DDF3}.Debug|Any CPU.Build.0 = Debug|Any CPU +		{3C2CC5A1-0663-458A-AF4B-52248977DDF3}.Release|Any CPU.ActiveCfg = Release|Any CPU +		{3C2CC5A1-0663-458A-AF4B-52248977DDF3}.Release|Any CPU.Build.0 = Release|Any CPU +		{5C04DB87-3B34-43B4-B164-86CE4361DC7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU +		{5C04DB87-3B34-43B4-B164-86CE4361DC7B}.Debug|Any CPU.Build.0 = Debug|Any CPU +		{5C04DB87-3B34-43B4-B164-86CE4361DC7B}.Release|Any CPU.ActiveCfg = Release|Any CPU +		{5C04DB87-3B34-43B4-B164-86CE4361DC7B}.Release|Any CPU.Build.0 = Release|Any CPU +		{F0499708-3EB6-4026-8362-97E6FFC4E7C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU +		{F0499708-3EB6-4026-8362-97E6FFC4E7C8}.Debug|Any CPU.Build.0 = Debug|Any CPU +		{F0499708-3EB6-4026-8362-97E6FFC4E7C8}.Release|Any CPU.ActiveCfg = Release|Any CPU +		{F0499708-3EB6-4026-8362-97E6FFC4E7C8}.Release|Any CPU.Build.0 = Release|Any CPU +	EndGlobalSection +	GlobalSection(MonoDevelopProperties) = preSolution +		StartupItem = CrossDomainPolicyParser.csproj +	EndGlobalSection +	GlobalSection(SolutionProperties) = preSolution +		HideSolutionNode = FALSE +	EndGlobalSection +EndGlobal diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/AddressFamily.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/AddressFamily.cs new file mode 100644 index 0000000..5277509 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/AddressFamily.cs @@ -0,0 +1,73 @@ +// AddressFamily.cs +// +// This code was automatically generated from +// ECMA CLI XML Library Specification. +// Generator: libgen.xsl [1.0; (C) Sergey Chaban (serge@wildwestsoftware.com)] +// Created: Wed, 5 Sep 2001 06:31:59 UTC +// Source file: AllTypes.xml +// URL: http://msdn.microsoft.com/net/ecma/AllTypes.xml +// +// (C) 2001 Ximian, Inc.  http://www.ximian.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. +// + + +namespace MonoForks.System.Net.Sockets { + + +#if ONLY_1_1 +	[Serializable] +#endif +	public enum AddressFamily { +		Unknown = -1, +		Unspecified = 0, +		Unix = 1, +		InterNetwork = 2, +		ImpLink = 3, +		Pup = 4, +		Chaos = 5, +		NS = 6, +		Ipx = 6, +		Iso = 7, +		Osi = 7, +		Ecma = 8, +		DataKit = 9, +		Ccitt = 10, +		Sna = 11, +		DecNet = 12, +		DataLink = 13, +		Lat = 14, +		HyperChannel = 15, +		AppleTalk = 16, +		NetBios = 17, +		VoiceView = 18, +		FireFox = 19, +		Banyan = 21, +		Atm = 22, +		InterNetworkV6 = 23, +		Cluster = 24, +		Ieee12844 = 25, +		Irda = 26, +		NetworkDesigners = 28, +		Max = 29, +	} +} diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/BaseDomainPolicy.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/BaseDomainPolicy.cs new file mode 100644 index 0000000..34fe6fa --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/BaseDomainPolicy.cs @@ -0,0 +1,147 @@ +// +// BaseDomainPolicy.cs +// +// Authors: +//	Atsushi Enomoto <atsushi@ximian.com> +//	Moonlight List (moonlight-list@lists.ximian.com) +// +// Copyright (C) 2009 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. +// +#define NET_2_1 +#if NET_2_1 + +using System; +using MonoForks.System; +using System.Collections.Generic; +using System.IO; +using MonoForks.System.Net; +#if !TEST +using MonoForks.System.Windows.Interop; +#endif + +// Base class for shared stuff between the Silverlight and Flash policies +// e.g. Headers and Domain comparison + +namespace MonoForks.System.Windows.Browser.Net { + +	abstract class BaseDomainPolicy : ICrossDomainPolicy { +#if TEST +		static public Uri ApplicationUri { get; set; } +#else +		static public Uri ApplicationUri { +			get { return PluginHost.RootUri; } +		} +#endif +		static string root; + +		static public string ApplicationRoot { +			get { +				if (root == null) +					root = CrossDomainPolicyManager.GetRoot (ApplicationUri); +				return root; +			} +		} + +		public class Headers { + +			class PrefixComparer : IEqualityComparer<string> { + +				public bool Equals (string x, string y) +				{ +					int check_length = x.Length - 1; +					if ((x.Length > 0) && (x [check_length] == '*')) +					return (String.Compare (x, 0, y, 0, check_length, StringComparison.OrdinalIgnoreCase) == 0); + +					return (String.Compare (x, y, StringComparison.OrdinalIgnoreCase) == 0); +				} + +				public int GetHashCode (string obj) +				{ +					return (obj == null) ? 0 : obj.GetHashCode (); +				} +			} + +			static PrefixComparer pc = new PrefixComparer (); + +			private List<string> list; + +			public Headers () +			{ +			} + +			public bool AllowAllHeaders { get; private set; } + +			public bool IsAllowed (string[] headers) +			{ +				if (AllowAllHeaders) +					return true; + +				if (headers == null || headers.Length == 0) +					return true; + +                foreach(var h in headers) +                { +                    bool found = false; +                    foreach(var item in list) +                        if (pc.Equals(item,h)) found = true; +                    if (!found) return false; +                } +				return true; +			} + +			public void SetHeaders (string raw) +			{ +				if (raw == "*") { +					AllowAllHeaders = true; +					list = null; +				} else if (raw != null) { +					string [] headers = raw.Split (','); +					list = new List<string> (headers.Length + 1); +					list.Add ("Content-Type"); +					for (int i = 0; i < headers.Length; i++) { +						string s = headers [i].Trim (); +						if (!String.IsNullOrEmpty (s)) +							list.Add (s); +					} +				} else { +					// without a specified 'http-request-headers' no header, expect Content-Type, is allowed +					AllowAllHeaders = false; +					list = new List<string> (1); +					list.Add ("Content-Type"); +				} +			} +		} + +		public bool IsAllowed (WebRequest request) +		{ +   			var keys = request.Headers.Keys; +                        string[] AllKeys = new string[keys.Count]; +                        keys.CopyTo(AllKeys,0); +                        return IsAllowed (request.RequestUri, AllKeys); +		} + +		abstract public bool IsAllowed (Uri uri, params string [] headerKeys); +	} +} + +#endif + diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/CrossDomainPolicyManager.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/CrossDomainPolicyManager.cs new file mode 100644 index 0000000..a73eadc --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/CrossDomainPolicyManager.cs @@ -0,0 +1,245 @@ +// +// CrossDomainPolicyManager.cs +// +// Authors: +//	Atsushi Enomoto <atsushi@ximian.com> +//	Moonlight List (moonlight-list@lists.ximian.com) +// +// Copyright (C) 2009 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. +// +#define NET_2_1 +#if NET_2_1 + +using System; +using System.Collections.Generic; +using System.IO; +using MonoForks.System.Windows.Interop; +using System.Security; +using System.Reflection; + +namespace MonoForks.System.Windows.Browser.Net +{ +	internal static class CrossDomainPolicyManager +	{ + +		public static string GetRoot(Uri uri) +		{ +			if ((uri.Scheme == "http" && uri.Port == 80) || (uri.Scheme == "https" && uri.Port == 443) || (uri.Port == -1)) +				return String.Format("{0}://{1}/", uri.Scheme, uri.DnsSafeHost); +			else +				return String.Format("{0}://{1}:{2}/", uri.Scheme, uri.DnsSafeHost, uri.Port); +		} + +		public const string ClientAccessPolicyFile = "/clientaccesspolicy.xml"; +		public const string CrossDomainFile = "/crossdomain.xml"; + +		const int Timeout = 10000; + +		// Web Access Policy + +		static Dictionary<string, ICrossDomainPolicy> policies = new Dictionary<string, ICrossDomainPolicy>(); + +		static internal ICrossDomainPolicy PolicyDownloadPolicy = new PolicyDownloadPolicy(); +		static ICrossDomainPolicy site_of_origin_policy = new SiteOfOriginPolicy(); +		static ICrossDomainPolicy no_access_policy = new NoAccessPolicy(); + +		static Uri GetRootUri(Uri uri) +		{ +			return new Uri(GetRoot(uri)); +		} + +		public static Uri GetSilverlightPolicyUri(Uri uri) +		{ +			return new Uri(GetRootUri(uri), CrossDomainPolicyManager.ClientAccessPolicyFile); +		} + +		public static Uri GetFlashPolicyUri(Uri uri) +		{ +			return new Uri(GetRootUri(uri), CrossDomainPolicyManager.CrossDomainFile); +		} + +		public static ICrossDomainPolicy GetCachedWebPolicy(Uri uri) +		{ +			//Debug.Log("Got to GetCachedWebPolicy"); +			//Debug.Log("debug1. uri: " + uri.ToString() + " pluginhost.sourceuri: " + PluginHost.SourceUri); +			//Debug.Log("debug1.1 pluginhost.rooturi: " + PluginHost.RootUri); +			// if we request an Uri from the same site then we return an "always positive" policy +			if (SiteOfOriginPolicy.HasSameOrigin(uri, PluginHost.SourceUri)) +				return site_of_origin_policy; + +			//Debug.Log("debug2"); + +			//if this is a request for a crossdomainfile, then we allow it. +			//TODO: Make this more secure. +			string postfix = ""; +			if (!uri.IsDefaultPort) postfix = ":" + uri.Port; + +			if (uri.ToString() == uri.Scheme + "://" + uri.Host + postfix + "/crossdomain.xml") return PolicyDownloadPolicy; + +			//Debug.Log("debug3"); +			// otherwise we search for an already downloaded policy for the web site +			string root = GetRoot(uri); +			ICrossDomainPolicy policy = null; +			policies.TryGetValue(root, out policy); +			// and we return it (if we have it) or null (if we dont) +			//Debug.Log("debug4: " + policy); +			return policy; +		} + +		private static void AddPolicy(Uri responseUri, ICrossDomainPolicy policy) +		{ +			string root = GetRoot(responseUri); +			try +			{ +				policies.Add(root, policy); +			} +			catch (ArgumentException) +			{ +				// it's possible another request already added this root +			} +		} + +		/* +				public static ICrossDomainPolicy BuildSilverlightPolicy (HttpWebResponse response) +				{ +					// return null if no Silverlight policy was found, since we offer a second chance with a flash policy +					if (response.StatusCode != HttpStatusCode.OK) +						return null; + +					ICrossDomainPolicy policy = null; +					try { +						policy = ClientAccessPolicy.FromStream (response.GetResponseStream ()); +						if (policy != null) +							policies.Add (GetRoot (response.ResponseUri), policy); +					} catch (Exception ex) { +						Console.WriteLine (String.Format ("CrossDomainAccessManager caught an exception while reading {0}: {1}",  +							response.ResponseUri, ex.Message)); +						// and ignore. +					} +					return policy; +				} + +				public static ICrossDomainPolicy BuildFlashPolicy (HttpWebResponse response) +				{ +					bool ok = response.StatusCode == HttpStatusCode.OK; +					return BuildFlashPolicy(ok, response.ResponseUri, response.GetResponseStream(), response.Headers); +				} +		*/ +		public static ICrossDomainPolicy BuildFlashPolicy(bool statuscodeOK, Uri uri, Stream responsestream, Dictionary<string, string> responseheaders) +		{ +			ICrossDomainPolicy policy = null; +			if (statuscodeOK) +			{ +				try +				{ +					policy = FlashCrossDomainPolicy.FromStream(responsestream); +				} +				catch (Exception ex) +				{ +					Log.Msg(String.Format("BuildFlashPolicy caught an exception while parsing {0}: {1}", +						uri, ex.Message)); +					throw; +				} +				if (policy != null) +				{ +					// see DRT# 864 and 865 +					Log.Msg("crossdomain.xml was succesfully parsed"); +					string site_control = null; +					responseheaders.TryGetValue("X-Permitted-Cross-Domain-Policies", out site_control); +					if (!String.IsNullOrEmpty(site_control)) +						(policy as FlashCrossDomainPolicy).SiteControl = site_control; +				} +			} + +			// the flash policy was the last chance, keep a NoAccess into the cache +			if (policy == null) +				policy = no_access_policy; + +			AddPolicy(uri, policy); +			return policy; +		} + +		public static void ClearCache() +		{ +			policies.Clear(); +		} + +		// Socket Policy +		// +		// - we connect once to a site for the entire application life time +		// - this returns us a policy file (silverlight format only) or else no access is granted +		// - this policy file +		// 	- can contain multiple policies +		// 	- can apply to multiple domains +		//	- can grant access to several resources + +		static readonly Dictionary<string, FlashCrossDomainPolicy> SocketPoliciesByIp = new Dictionary<string, FlashCrossDomainPolicy>(); +		const int PolicyPort = 843; + +		static public bool CheckSocketEndPoint(string connecting_to_ip, int port) +		{ +			return CheckSocketEndPoint(connecting_to_ip,port,PolicyPort); +		} +		 +		static public bool CheckSocketEndPoint(string connecting_to_ip, int port, int policyport) +		{ +			var policy = FlashCrossDomainPolicyFor(connecting_to_ip, policyport, 3000); +			return policy.IsSocketConnectionAllowed(port); +		} + +		public static FlashCrossDomainPolicy FlashCrossDomainPolicyFor(string connecting_to_ip, int policyport, int timeout) +		{ +			FlashCrossDomainPolicy cachedPolicy; +			if (SocketPoliciesByIp.TryGetValue(connecting_to_ip, out cachedPolicy)) +			{ +				Log.Msg(String.Format("Policy for host {0} found in the cache.", connecting_to_ip)); +				return cachedPolicy; +			} + +			try +			{ +				FlashCrossDomainPolicy policy = RetrieveFlashCrossDomainPolicyFrom(connecting_to_ip, policyport,timeout); +				policy.PolicyPort = policyport; +				SocketPoliciesByIp.Add(connecting_to_ip, policy); +				return policy; +			} +			catch (Exception ex) +			{ +				Log.Msg(String.Format("{0} caught an exception while checking endpoint {1}: {2}", typeof(CrossDomainPolicyManager).Name, connecting_to_ip, ex)); +				return FlashCrossDomainPolicy.DenyPolicy; +			} +		} + +		[SecuritySafeCritical] +		private static FlashCrossDomainPolicy RetrieveFlashCrossDomainPolicyFrom(string host, int port, int timeout) +		{ +			var type = Type.GetType("System.Net.Sockets.SocketPolicyClient, System"); +			var stream = (Stream)type.InvokeMember("GetPolicyStreamForIP", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic, null, null, new object[] { host, port, timeout }); + +            if (stream == null) throw new Exception("got back null stream from getpolicystream"); +			return FlashCrossDomainPolicy.FromStream(stream); +		} +	} +} + +#endif + diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/FlashCrossDomainPolicy.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/FlashCrossDomainPolicy.cs new file mode 100644 index 0000000..cbeb571 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/FlashCrossDomainPolicy.cs @@ -0,0 +1,213 @@ +// +// FlashCrossDomainPolicy.cs +// +// Author: +//	Atsushi Enomoto <atsushi@ximian.com> +//	Moonlight List (moonlight-list@lists.ximian.com) +// +// Copyright (C) 2009 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. +// +#define NET_2_1 +#if NET_2_1 + +using System; +using MonoForks.System; +using System.Collections.Generic; +using System.IO; +using MonoForks.System.Net; +using UnityEngine; + +namespace MonoForks.System.Windows.Browser.Net { + +	partial class FlashCrossDomainPolicy : BaseDomainPolicy { + +		private string site_control; +		public int PolicyPort { get; set; } + +		public FlashCrossDomainPolicy () +		{ +			AllowedAccesses = new List<AllowAccessFrom> (); +			AllowedHttpRequestHeaders = new List<AllowHttpRequestHeadersFrom> (); +			PolicyPort = 843; +		} +		 +		public static FlashCrossDomainPolicy DenyPolicy = new FlashCrossDomainPolicy(); + +		public List<AllowAccessFrom> AllowedAccesses { get; private set; } +		public List<AllowHttpRequestHeadersFrom> AllowedHttpRequestHeaders { get; private set; } + +		public string SiteControl { +			get { return String.IsNullOrEmpty (site_control) ? "all" : site_control; } +			set { site_control = value; } +		} + +		public bool IsSocketConnectionAllowed(int port) +		{ +			foreach(var allowed in AllowedAccesses) +			{ +				if (allowed.IsSocketConnectionAllowed (port, PolicyPort)) +                        return true; +			} +			return false; +		} + +		public override bool IsAllowed (Uri uri, string [] headerKeys) +		{ +            switch (SiteControl) { +			case "all": +			case "master-only": +			case "by-ftp-filename": +				break; +			default: +				// others, e.g. 'none', are not supported/accepted +				Log.Msg("rejected because SiteControl does not have a valid value"); +				return false; +			} +            bool any = false; +            if (AllowedAccesses.Count > 0) +            { +				foreach (var a in AllowedAccesses) +				{ +					if (a.IsAllowed(uri, headerKeys)) +					{ +						any = true; +					} +				} +            } +            if (!any) +            { +				Log.Msg("Rejected because there was no AllowedAcces entry in the crossdomain file allowing this request."); +                return false; +            } + +			if (AllowedHttpRequestHeaders.Count > 0) +                foreach(var h in AllowedHttpRequestHeaders) +                    if (h.IsRejected(uri,headerKeys)) return false; + +			return true; +		} + +		public class AllowAccessFrom { + +			public AllowAccessFrom () +			{ +				Secure = true;	// true by default +			} + +			public string Domain { get; set; } +			public bool AllowAnyPort { get; set; } +			public int [] ToPorts { get; set; } +			public bool Secure { get; set; } + +			public bool IsAllowed (Uri uri, string [] headerKeys) +			{ +				Log.Msg("Checking if "+uri+" is a valid domain"); +                if (!CheckDomain(uri)) return false; + +                if (!AllowAnyPort && ToPorts != null && Array.IndexOf(ToPorts, uri.Port) < 0) +                { +					Log.Msg("requested port: "+uri.Port+" is not allowed by specified portrange"); +                	return false; +                } + +				// if Secure is false then it allows applications from HTTP to download data from HTTPS servers +				if (!Secure) +					return true; +				// if Secure is true then only application on HTTPS servers can access data on HTTPS servers +				if (ApplicationUri.Scheme == Uri.UriSchemeHttps) +					return (uri.Scheme == Uri.UriSchemeHttps); +				// otherwise FILE/HTTP applications can access HTTP uris + +				Log.Msg("All requirements met, the request is approved"); +				return true; +			} + +			public bool IsSocketConnectionAllowed(int port, int policyport) +			{ +				if (policyport>1024 && port<1024) return false; + +				bool portok = false; +				 +				if (AllowAnyPort) portok = true; +				if (ToPorts != null) +				{ +					foreach (int allowedport in ToPorts) +					{ +						if (allowedport == port) +							portok = true; +					} +					if (!portok) return false; +				} +				//for now we only support socket policies that say all domains are fine. +                return (Domain == "*"); +			} +			 +            bool CheckDomain(Uri uri) +			{ +				Log.Msg("Checking request-host: "+uri.Host+" against valid domain: "+Domain); +                if (Domain == "*") return true; +                if (ApplicationUri.Host == Domain) return true; + +                if (Domain[0] != '*') return false; +                string match = Domain.Substring(1, Domain.Length - 1); +                if (uri.Host.EndsWith(match)) return true; +                 +                return false; +            } +		} + +		public class AllowHttpRequestHeadersFrom { + +			public AllowHttpRequestHeadersFrom () +			{ +				Headers = new Headers (); +			} + +			public string Domain { get; set; } +			public bool AllowAllHeaders { get; set; } +			public Headers Headers { get; private set; } +			public bool Secure { get; set; } + +			public bool IsRejected (Uri uri, string [] headerKeys) +			{ +				// "A Flash policy file must allow access to all domains to be used by the Silverlight runtime." +				// http://msdn.microsoft.com/en-us/library/cc645032(VS.95).aspx +				//if (Domain != "*") +				//	return false; + +				if (Headers.IsAllowed (headerKeys)) +					return false; + +				// if Secure is false then it allows applications from HTTP to download data from HTTPS servers +				if (!Secure) +					return true; +				// if Secure is true then only application on HTTPS servers can access data on HTTPS servers +				if (ApplicationUri.Scheme == Uri.UriSchemeHttps) +					return (uri.Scheme == Uri.UriSchemeHttps); +				// otherwise FILE/HTTP applications can access HTTP uris +				return true; +			} +		} +	} +} + +#endif diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/FlashCrossDomainPolicyParser.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/FlashCrossDomainPolicyParser.cs new file mode 100644 index 0000000..980089a --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/FlashCrossDomainPolicyParser.cs @@ -0,0 +1,265 @@ +// +// FlashCrossDomainPolicyParser.cs +// +// Author: +//	Atsushi Enomoto <atsushi@ximian.com> +//	Moonlight List (moonlight-list@lists.ximian.com) +// +// Copyright (C) 2009 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. +// + +#define NET_2_1 +#if NET_2_1 + +using System; +using System.IO; +using System.Collections; +using System.Text; +using MonoForks.Mono.Xml; + +/* + +Specification: http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html + +# This grammar is based on the xsd from Adobe, but the schema is wrong. +# It should have used interleave (all). Some crossdomain.xml are invalidated. +# (For example, try mono-xmltool --validate-xsd http://www.adobe.com/xml/schemas/PolicyFile.xsd http://twitter.com/crossdomain.xml) + +default namespace = "" + +grammar { + +start = cross-domain-policy + +cross-domain-policy = element cross-domain-policy { +  element site-control { +    attribute permitted-cross-domain-policies { +      "all" | "by-contract-type" | "by-ftp-filename" | "master-only" | "none" +    } +  }?, +  element allow-access-from { +    attribute domain { text }, +    attribute to-ports { text }?, +    attribute secure { xs:boolean }? +  }*, +  element allow-http-request-headers-from { +    attribute domain { text }, +    attribute headers { text }, +    attribute secure { xs:boolean }? +  }*, +  element allow-access-from-identity { +    element signatory { +      element certificate { +        attribute fingerprint { text }, +        attribute fingerprint-algorithm { text } +      } +    } +  }* +} + +} + +*/ + +namespace MonoForks.System.Windows.Browser.Net +{ + +	partial class FlashCrossDomainPolicy { + +		static public bool ReadBooleanAttribute(MiniParser.IAttrList attrs, string attribute) +		{ +			switch (attrs.GetValue(attribute)) +			{ +				case null: +				case "true": +					return true; +				case "false": +					return false; +				default: +					throw new Exception("Invalid boolean attribute: " + attribute); +			} +		} +		class Reader : MiniParser.IReader +		{ +			public Reader(Stream stream) +			{ +				this.stream = stream; +			} +			public int Read() +			{ +				return stream.ReadByte(); +			} + +			Stream stream; +		} + +		static public FlashCrossDomainPolicy FromStream(Stream originalStream) +		{ +			Log.Msg("received policy"); + +			var stream = StripTrailing0(originalStream); +			if (stream.Length == 0) +				throw new ArgumentException("Policy can't be constructed from empty stream."); + +			FlashCrossDomainPolicy cdp = new FlashCrossDomainPolicy (); +			Handler handler = new Handler(cdp); +			Reader r = new Reader(stream); +			MiniParser p = new MiniParser(); +			p.Parse(r, handler); +			 +			// if none supplied set a default for headers +			if (cdp.AllowedHttpRequestHeaders.Count == 0) +			{ +				var h = new AllowHttpRequestHeadersFrom() { Domain = "*", Secure = true }; +				h.Headers.SetHeaders(null); // defaults +				cdp.AllowedHttpRequestHeaders.Add(h); +			} +			Log.Msg("done parsing policy"); +			return cdp; +		} + +		private static MemoryStream StripTrailing0(Stream stream) +		{ +			//crazy inefficient, but only happens on very small streams. +			var dupe = new MemoryStream(); +			while(true) +			{ +				var b = stream.ReadByte(); +				if (b==0 || b==-1) break; +				dupe.WriteByte((byte)b); +			} +			dupe.Seek(0, SeekOrigin.Begin); +			return dupe; +		} + +		class Handler : MiniParser.IHandler +		{ +			public Handler(FlashCrossDomainPolicy cdp) +			{ +				this.cdp = cdp; +			} + + +			// IContentHandler + +			private string current = null; +			private Stack stack = new Stack(); +			FlashCrossDomainPolicy cdp; + +			public void OnStartElement(string name, MiniParser.IAttrList attrs) +			{ +				Log.Msg("Parsing: "+name); +				if (current == null) +				{ +					if (name != "cross-domain-policy") throw new Exception("Expected root element to be <cross-domain-policy>. found "+name+" instead"); +				} +				else +				{ +					if (current == "cross-domain-policy") +					{ +						switch (name) +						{ +							case "site-control": +								cdp.SiteControl = attrs.GetValue("permitted-cross-domain-policies"); +								break; +							case "allow-access-from": +								var a = new AllowAccessFrom() +								{ +									Domain = attrs.GetValue("domain"), +									Secure = ReadBooleanAttribute(attrs, "secure") +								}; +								 +								var p = attrs.GetValue ("to-ports"); +								if (p == "*") +									a.AllowAnyPort = true; +								else if (p != null) +								{ +									var ports = new ArrayList(); +									string[] ports_string =  p.Split (','); +									foreach(string portstring in ports_string) +									{ +										if (portstring.Contains("-")) +										{ +											string[] s = portstring.Split('-'); +											if (s.Length != 2) continue; +											int startport; +											int endport; +											if (!Int32.TryParse(s[0], out startport)) continue; +											if (!Int32.TryParse(s[1], out endport)) continue; +											for (int i = startport; i != endport; i++) +											{ +												ports.Add(i); +											} +										} +										else +										{ +											int port; +											if (Int32.TryParse(portstring, out port)) +												ports.Add(port); +										} +									} +									a.ToPorts = (int[]) ports.ToArray(typeof(int)); +								} +								cdp.AllowedAccesses.Add(a); +								break; +							case "allow-http-request-headers-from": +								var h = new AllowHttpRequestHeadersFrom() +								{ +									Domain = attrs.GetValue("domain"), +									Secure = ReadBooleanAttribute(attrs, "secure") +								}; +								h.Headers.SetHeaders(attrs.GetValue("headers")); +								cdp.AllowedHttpRequestHeaders.Add(h); +								break; +							default: +								break; +						} +					} +					else +					{ +						throw new Exception("Invalid element " + name); +					} +				} +				stack.Push(name); +				current = name; +				Log.Msg(name); +				// attributes +				int n = attrs.Length; +				for (int i = 0; i < n; i++) +					Log.Msg("  " + attrs.GetName(i) + ": " + attrs.GetValue(i)); +			} + +			public void OnEndElement(string name) +			{ +				stack.Pop(); +				current = stack.Count>0 ? (string)stack.Peek() : null; +			} + +			public void OnStartParsing(MiniParser parser) { } +			public void OnChars(string ch) { } +			public void OnEndParsing(MiniParser parser) { } +		} +	} +} + +#endif + diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/ICrossDomainPolicy.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/ICrossDomainPolicy.cs new file mode 100644 index 0000000..67c846f --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/ICrossDomainPolicy.cs @@ -0,0 +1,42 @@ +// +// System.Windows.Browser.Net.ICrossDomainPolicy interface +// +// Contact: +//   Moonlight List (moonlight-list@lists.ximian.com) +// +// Copyright (C) 2009 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. +// +#define NET_2_1 +#if NET_2_1 + +using MonoForks.System.Net; + +namespace MonoForks.System.Windows.Browser.Net { + +	interface ICrossDomainPolicy { + +		bool IsAllowed (WebRequest request); +	} +} + +#endif + diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/IPAddress.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/IPAddress.cs new file mode 100644 index 0000000..61b254f --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/IPAddress.cs @@ -0,0 +1,503 @@ +// +// System.Net.IPAddress.cs +// +// Author: +//   Miguel de Icaza (miguel@ximian.com) +//   Lawrence Pit (loz@cable.a2000.nl) +// +// (C) Ximian, Inc.  http://www.ximian.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; +using System.Globalization; +using MonoForks.System; +using MonoForks.System.Net; +using MonoForks.System.Net.Sockets; +using System.Runtime.InteropServices; + +namespace MonoForks.System.Net { + +	/// <remarks> +	///   Encapsulates an IP Address. +	/// </remarks> +	[Serializable] +	public class IPAddress { +		// Don't change the name of this field without also +		// changing socket-io.c in the runtime +		// The IP address is stored in little-endian order inside the int,  +		// meaning the lower order bytes contain the netid +		private long m_Address; +		private AddressFamily m_Family; +		private ushort[] m_Numbers;	/// ip6 Stored in network order (as ip4) +		private long m_ScopeId; + +		public static readonly IPAddress Any = new IPAddress(0); +		public static readonly IPAddress Broadcast = IPAddress.Parse ("255.255.255.255"); +		public static readonly IPAddress Loopback = IPAddress.Parse ("127.0.0.1"); +		public static readonly IPAddress None = IPAddress.Parse ("255.255.255.255"); +		public static readonly IPAddress IPv6Any = IPAddress.ParseIPV6 ("::"); +		public static readonly IPAddress IPv6Loopback = IPAddress.ParseIPV6 ("::1"); +		public static readonly IPAddress IPv6None = IPAddress.ParseIPV6 ("::"); + +		private static short SwapShort (short number) +		{ +			return (short) ( ((number >> 8) & 0xFF) | ((number << 8) & 0xFF00) ); +		} + +		private static int SwapInt (int number) +		{ +			return (((number >> 24) & 0xFF) +				  | ((number >> 08) & 0xFF00) +				  | ((number << 08) & 0xFF0000) +				  | ((number << 24))); +		} + +		private static long SwapLong(long number) +		{ +			return (((number >> 56) & 0xFF) +				  | ((number >> 40) & 0xFF00) +				  | ((number >> 24) & 0xFF0000) +				  | ((number >> 08) & 0xFF000000) +				  | ((number << 08) & 0xFF00000000) +				  | ((number << 24) & 0xFF0000000000) +				  | ((number << 40) & 0xFF000000000000) +				  | ((number << 56))); +		} + +		public static short HostToNetworkOrder(short host) { +			if (!BitConverter.IsLittleEndian) +				return(host); + +			return SwapShort (host); +		} + +		public static int HostToNetworkOrder(int host) { +			if (!BitConverter.IsLittleEndian) +				return(host); + +			return SwapInt (host); +		} +		 +		public static long HostToNetworkOrder(long host) { +			if (!BitConverter.IsLittleEndian) +				return(host); + +			return SwapLong (host); +		} + +		public static short NetworkToHostOrder(short network) { +			if (!BitConverter.IsLittleEndian) +				return(network); + +			return SwapShort (network); +		} + +		public static int NetworkToHostOrder(int network) { +			if (!BitConverter.IsLittleEndian) +				return(network); + +			return SwapInt (network); +		} + +		public static long NetworkToHostOrder(long network) { +			if (!BitConverter.IsLittleEndian) +				return(network); + +			return SwapLong (network); +		} +		 +		/// <summary> +		///   Constructor from a 32-bit constant with the address bytes in +		///   little-endian order (the lower order bytes contain the netid) +		/// </summary> +		public IPAddress (long addr) +		{ +			m_Address = addr; +			m_Family = AddressFamily.InterNetwork; +		} + +		public IPAddress (byte[] address) +		{ +			if (address == null) +				throw new ArgumentNullException ("address"); + +			int len = address.Length; + +#if NET_2_0 +			if (len != 16 && len != 4) +				throw new ArgumentException ("An invalid IP address was specified.", +					"address"); +#else +			if (len != 16) +				throw new ArgumentException ("address"); +#endif + +			if (len == 16) { +				m_Numbers = new ushort [8]; +				Buffer.BlockCopy(address, 0, m_Numbers, 0, 16); +				m_Family = AddressFamily.InterNetworkV6; +				m_ScopeId = 0; +			} else { +				m_Address = ((uint) address [3] << 24) + (address [2] << 16) + +					(address [1] << 8) + address [0]; +				m_Family = AddressFamily.InterNetwork; +			} +		} + +		public IPAddress(byte[] address, long scopeId) +		{ +			if (address == null) +				throw new ArgumentNullException ("address"); + +			if (address.Length != 16) +#if NET_2_0 +				throw new ArgumentException ("An invalid IP address was specified.", +					"address"); +#else +				throw new ArgumentException("address"); +#endif + +			m_Numbers = new ushort [8]; +			Buffer.BlockCopy(address, 0, m_Numbers, 0, 16); +			m_Family = AddressFamily.InterNetworkV6; +			m_ScopeId = scopeId; +		} + +		internal IPAddress(ushort[] address, long scopeId) +		{ +			m_Numbers = address; + +			for(int i=0; i<8; i++) +				m_Numbers[i] = (ushort)HostToNetworkOrder((short)m_Numbers[i]); + +			m_Family = AddressFamily.InterNetworkV6; +			m_ScopeId = scopeId; +		} + +		public static IPAddress Parse (string ipString) +		{ +			IPAddress ret; +			if (TryParse (ipString, out ret)) +				return ret; +			throw new FormatException ("An invalid IP address was specified."); +		} + +#if NET_2_0 +		public +#else +		internal +#endif +		static bool TryParse (string ipString, out IPAddress address) +		{ +			if (ipString == null) +				throw new ArgumentNullException ("ipString"); + +			if ((address = ParseIPV4 (ipString)) == null) +				if ((address = ParseIPV6 (ipString)) == null) +					return false; +			return true; +		} + +		private static IPAddress ParseIPV4 (string ip) +		{ +#if ONLY_1_1 +			if (ip.Length == 0 || ip == " ") +				return new IPAddress (0); +#endif + +			int pos = ip.IndexOf (' '); +			if (pos != -1) { +#if NET_2_0 +				string [] nets = ip.Substring (pos + 1).Split (new char [] {'.'}); +				if (nets.Length > 0) { +					string lastNet = nets [nets.Length - 1]; +					if (lastNet.Length == 0) +						return null; +#if NET_2_1 //workaround for smcs, as it generate code that can't access string.GetEnumerator () +					foreach (char c in lastNet.ToCharArray ()) +#else +					foreach (char c in lastNet) +#endif +						if (!Uri.IsHexDigit (c)) +							return null; +				} +#endif +				ip = ip.Substring (0, pos); +			} + +			if (ip.Length == 0 || ip [ip.Length - 1] == '.') +				return null; + +			string [] ips = ip.Split (new char [] {'.'}); +			if (ips.Length > 4) +				return null; +			 +			// Make the number in network order +			try { +				long a = 0; +				long val = 0; +				for (int i = 0; i < ips.Length; i++) { +					string subnet = ips [i]; +					if ((3 <= subnet.Length && subnet.Length <= 4) && +					    (subnet [0] == '0') && (subnet [1] == 'x' || subnet [1] == 'X')) { +						if (subnet.Length == 3) +							val = (byte) Uri.FromHex (subnet [2]); +						else  +							val = (byte) ((Uri.FromHex (subnet [2]) << 4) | Uri.FromHex (subnet [3])); +					} else if (subnet.Length == 0) +						return null; +					else if (subnet [0] == '0') { +						// octal +						val = 0; +						for (int j = 1; j < subnet.Length; j++) { +							if ('0' <= subnet [j] && subnet [j] <= '7') +								val = (val << 3) + subnet [j] - '0'; +							else +								return null; +						} +					} +					else { +#if NET_2_0 +						if (!Int64.TryParse (subnet, NumberStyles.None, null, out val)) +							return null; +#else +						val = long.Parse (subnet, NumberStyles.None); +#endif +					} + +					if (i == (ips.Length - 1))  +						i = 3; +					else if (val > 0xFF) +						return null; // e.g. 256.0.0.1 +					for (int j = 0; val > 0; j++, val /= 0x100) +						a |= (val & 0xFF) << ((i - j) << 3); +				} + +				return (new IPAddress (a)); +			} catch (Exception) { +				return null; +			} +		} +		 +		private static IPAddress ParseIPV6 (string ip) +		{ +			IPv6Address newIPv6Address; + +			if (IPv6Address.TryParse(ip, out newIPv6Address)) +				return  new IPAddress (newIPv6Address.Address, newIPv6Address.ScopeId); +			return null; +		} + +		[Obsolete("This property is obsolete. Use GetAddressBytes.")] +		public long Address  +		{ +			get { +				if(m_Family != AddressFamily.InterNetwork) +					throw new Exception("The attempted operation is not supported for the type of object referenced"); + +				return m_Address; +			} +			set { +				/* no need to do this test, ms.net accepts any value. +				if (value < 0 || value > 0x00000000FFFFFFFF) +					throw new ArgumentOutOfRangeException ( +						"the address must be between 0 and 0xFFFFFFFF"); +				*/ + +				if(m_Family != AddressFamily.InterNetwork) +					throw new Exception("The attempted operation is not supported for the type of object referenced"); + +				m_Address = value; +			} +		} + +		internal long InternalIPv4Address { +			get { return m_Address; } +		} + +#if NET_2_0 +		public bool IsIPv6LinkLocal { +			get { +				if (m_Family == AddressFamily.InterNetwork) +					return false; +				int v = NetworkToHostOrder ((short) m_Numbers [0]) & 0xFFF0; +				return 0xFE80 <= v && v < 0xFEC0; +			} +		} + +		public bool IsIPv6SiteLocal { +			get { +				if (m_Family == AddressFamily.InterNetwork) +					return false; +				int v = NetworkToHostOrder ((short) m_Numbers [0]) & 0xFFF0; +				return 0xFEC0 <= v && v < 0xFF00; +			} +		} + +		public bool IsIPv6Multicast { +			get { +				return m_Family != AddressFamily.InterNetwork && +					((ushort) NetworkToHostOrder ((short) m_Numbers [0]) & 0xFF00) == 0xFF00; +			} +		} +#endif + +		public long ScopeId { +			get { +				if(m_Family != AddressFamily.InterNetworkV6) +					throw new Exception("The attempted operation is not supported for the type of object referenced"); + +				return m_ScopeId; +			} +			set { +				if(m_Family != AddressFamily.InterNetworkV6) +					throw new Exception("The attempted operation is not supported for the type of object referenced"); + +				m_ScopeId = value; +			} +		} + +		public byte [] GetAddressBytes ()  +		{ +			if(m_Family == AddressFamily.InterNetworkV6) { +				byte [] addressBytes = new byte [16]; +				Buffer.BlockCopy (m_Numbers, 0, addressBytes, 0, 16); +				return addressBytes; +			} else { +				return new byte [4] { (byte)(m_Address & 0xFF), +						     (byte)((m_Address >> 8) & 0xFF), +						     (byte)((m_Address >> 16) & 0xFF), +						     (byte)(m_Address >> 24) };  +			} +		} + +		public AddressFamily AddressFamily  +		{ +			get { +				return m_Family; +			} +		} +		 +		 +		/// <summary> +		///   Used to tell whether an address is a loopback. +		///   All IP addresses of the form 127.X.Y.Z, where X, Y, and Z are in  +		///   the range 0-255, are loopback addresses. +		/// </summary> +		/// <param name="addr">Address to compare</param> +		/// <returns></returns> +		public static bool IsLoopback (IPAddress addr) +		{ +			if(addr.m_Family == AddressFamily.InterNetwork) +				return (addr.m_Address & 0xFF) == 127; +			else { +				for(int i=0; i<6; i++) { +					if(addr.m_Numbers[i] != 0) +						return false; +				} + +				return NetworkToHostOrder((short)addr.m_Numbers[7]) == 1; +			} +		} + +		/// <summary> +		///   Overrides System.Object.ToString to return +		///   this object rendered in a quad-dotted notation +		/// </summary> +		public override string ToString () +		{ +			if(m_Family == AddressFamily.InterNetwork) +				return ToString (m_Address); +			else +			{ +				ushort[] numbers = m_Numbers.Clone() as ushort[]; + +				for(int i=0; i<numbers.Length; i++) +					numbers[i] = (ushort)NetworkToHostOrder((short)numbers[i]); + +				IPv6Address v6 = new IPv6Address(numbers); +				v6.ScopeId = ScopeId; +				return v6.ToString(); +			} +		} + +		/// <summary> +		///   Returns this object rendered in a quad-dotted notation +		/// </summary> +		static string ToString (long addr) +		{ +			// addr is in network order +			return  (addr & 0xff).ToString () + "." + +				((addr >> 8) & 0xff).ToString () + "." + +				((addr >> 16) & 0xff).ToString () + "." + +				((addr >> 24) & 0xff).ToString (); +		} + +		/// <returns> +		///   Whether both objects are equal. +		/// </returns> +		public override bool Equals (object other) +		{ +			if (other is System.Net.IPAddress){ +				IPAddress otherAddr = other as IPAddress; + +				if(AddressFamily != otherAddr.AddressFamily) +					return false; + +				if(AddressFamily == AddressFamily.InterNetwork) { +					return m_Address == otherAddr.m_Address; +				} else { +					ushort[] vals = otherAddr.m_Numbers; + +					for(int i=0; i<8; i++) +						if(m_Numbers[i] != vals[i]) +							return false; + +					return true; +				} +			} +			return false; +		} + +		public override int GetHashCode () +		{ +			if(m_Family == AddressFamily.InterNetwork) +				return (int)m_Address; +			else +				return Hash (((((int) m_Numbers[0]) << 16) + m_Numbers [1]),  +					((((int) m_Numbers [2]) << 16) + m_Numbers [3]), +					((((int) m_Numbers [4]) << 16) + m_Numbers [5]), +					((((int) m_Numbers [6]) << 16) + m_Numbers [7])); +		} + +		private static int Hash (int i, int j, int k, int l)  +		{ +			return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25); +		} + +#pragma warning disable 169 +		// Added for serialization compatibility with MS.NET +		private int m_HashCode; +#pragma warning restore +		 +	} +} diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/IPv6Address.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/IPv6Address.cs new file mode 100644 index 0000000..4d9e7d3 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/IPv6Address.cs @@ -0,0 +1,478 @@ +// +// System.Net.IPv6Address.cs +// +// Author: +//   Lawrence Pit (loz@cable.a2000.nl) +// +// Note I: This class is not defined in the specs of .Net +// +// Note II : The name of this class is perhaps unfortunate as it turns +//           out that in ms.net there's an internal class called +//           IPv6Address in namespace System. +// + +// +// 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; +using System.Globalization; +using MonoForks.System; +using MonoForks.System.Net; +using MonoForks.System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Text; + +namespace MonoForks.System.Net { + +	/// <remarks> +	///   Encapsulates an IPv6 Address. +	///   See RFC 2373 for more info on IPv6 addresses. +	/// </remarks> +	[Serializable] +	internal class IPv6Address { +		private ushort [] address; +		private int prefixLength; +		private long scopeId = 0; + +		public static readonly IPv6Address Loopback = IPv6Address.Parse ("::1"); +		public static readonly IPv6Address Unspecified = IPv6Address.Parse ("::"); + +		public IPv6Address (ushort [] addr) +		{ +			if (addr == null) +				throw new ArgumentNullException ("addr");	 +			if (addr.Length != 8)	 +				throw new ArgumentException ("addr"); +			address = addr;			 +		} +		 +		public IPv6Address (ushort [] addr, int prefixLength) : this (addr) +		{ +			if (prefixLength < 0 || prefixLength > 128) +				throw new ArgumentException ("prefixLength"); +			this.prefixLength = prefixLength; +		} +	 +		public IPv6Address (ushort [] addr, int prefixLength, int scopeId) : this (addr, prefixLength) +		{ +			this.scopeId = scopeId; +		} + +		public static IPv6Address Parse (string ipString) +		{ +			if (ipString == null) +				throw new ArgumentNullException ("ipString"); + +			IPv6Address result; +			if (TryParse (ipString, out result)) +				return result; +			throw new FormatException ("Not a valid IPv6 address"); +		} + +		static int Fill (ushort [] addr, string ipString) +		{ +			int p = 0; +			int slot = 0; + +			if (ipString.Length == 0) +				return 0; +			 +			// Catch double uses of :: +			if (ipString.IndexOf ("::") != -1) +				return -1; + +			for (int i = 0; i < ipString.Length; i++){ +				char c = ipString [i]; +				int n; + +				if (c == ':'){ +					// Trailing : is not allowed. +					if (i == ipString.Length-1) +						return -1; +					 +					if (slot == 8) +						return -1; +					 +					addr [slot++] = (ushort) p; +					p = 0; +					continue; +				} if ('0' <= c && c <= '9') +					n = (int) (c - '0'); +				else if ('a' <= c && c <= 'f') +					n = (int) (c - 'a' + 10); +				else if ('A' <= c && c <= 'F') +					n = (int) (c - 'A' + 10); +				else  +					return -1; +				p = (p << 4) + n; +				if (p > UInt16.MaxValue) +					return -1; +			} + +			if (slot == 8) +				return -1; +			 +			addr [slot++] = (ushort) p; + +			return slot; +		} + +		static bool TryParse (string prefix, out int res) +		{ +#if NET_2_0 +			return Int32.TryParse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture, out res); +#else +			try { +				res = Int32.Parse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture); +			} catch (Exception) { +				res = -1; +				return false; +			} + +			return true; +#endif +		} +		 +		public static bool TryParse (string ipString, out IPv6Address result) +		{ +			result = null; +			if (ipString == null) +				return false; + +			if (ipString.Length > 2 &&  +			    ipString [0] == '[' &&  +			    ipString [ipString.Length - 1] == ']') +				ipString = ipString.Substring (1, ipString.Length - 2); + +			if (ipString.Length  < 2) +				return false; + +			int prefixLen = 0; +			int scopeId = 0; +			int pos = ipString.LastIndexOf ('/'); +			if (pos != -1) { +				string prefix = ipString.Substring (pos + 1); +				if (!TryParse (prefix , out prefixLen)) +					prefixLen = -1; +				if (prefixLen < 0 || prefixLen > 128) +					return false; +				ipString = ipString.Substring (0, pos); +			} else { +				pos = ipString.LastIndexOf ('%'); +				if (pos != -1) { +					string prefix = ipString.Substring (pos + 1); +					if (!TryParse (prefix, out scopeId)) +						scopeId = 0; +					ipString = ipString.Substring (0, pos); +				}			 +			} + +			// +			// At this point the prefix/suffixes have been removed +			// and we only have to deal with the ipv4 or ipv6 addressed +			// +			ushort [] addr = new ushort [8]; + +			// +			// Is there an ipv4 address at the end? +			// +			bool ipv4 = false; +			int pos2 = ipString.LastIndexOf (':'); +			if (pos2 == -1) +				return false; + +			int slots = 0; +			if (pos2 < (ipString.Length - 1)) { +				string ipv4Str = ipString.Substring (pos2 + 1); +				if (ipv4Str.IndexOf ('.') != -1) { +					IPAddress ip; +					 +					if (!IPAddress.TryParse (ipv4Str, out ip)) +						return false; +					 +					long a = ip.InternalIPv4Address; +					addr [6] = (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff))); +					addr [7] = (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff))); +					if (pos2 > 0 && ipString [pos2 - 1] == ':')  +						ipString = ipString.Substring (0, pos2 + 1); +					else +						ipString = ipString.Substring (0, pos2); +					ipv4 = true; +					slots = 2; +				} +			}	 + +			// +			// Only an ipv6 block remains, either: +			// "hexnumbers::hexnumbers", "hexnumbers::" or "hexnumbers" +			// +			int c = ipString.IndexOf ("::"); +			if (c != -1){ +				int right_slots = Fill (addr, ipString.Substring (c+2)); +				if (right_slots == -1){ +					return false; +				} + +				if (right_slots + slots > 8){ +					return false; +				} + +				int d = 8-slots-right_slots; +				for (int i = right_slots; i > 0; i--){ +					addr [i+d-1] = addr [i-1]; +					addr [i-1] = 0; +				} +				 +				int left_slots = Fill (addr, ipString.Substring (0, c)); +				if (left_slots == -1) +					return false; + +				if (left_slots + right_slots + slots > 7) +					return false; +			} else { +				if (Fill (addr, ipString) != 8-slots) +					return false; +			} + +			// Now check the results in the ipv6-address range only +			bool ipv6 = false; +			for (int i = 0; i < slots; i++){ +				if (addr [i] != 0 || i == 5 && addr [i] != 0xffff) +					ipv6 = true; +			} +			 +			// check IPv4 validity +			if (ipv4 && !ipv6) { +				for (int i = 0; i < 5; i++) { +					if (addr [i] != 0) +						return false; +				} + +				if (addr [5] != 0 && addr [5] != 0xffff) +					return false; +			} + +			result = new IPv6Address (addr, prefixLen, scopeId); +			return true; +		} + +		public ushort [] Address { +			get { return address; } +		} + +		public int PrefixLength { +			get { return this.prefixLength; } +		} +		 +		public long ScopeId { +			get { +				return scopeId; +			} +			set { +				scopeId = value; +			} +		} + +		public ushort this [int index] { +			get { return address [index]; } +		}		 + +		public AddressFamily AddressFamily { +			get { return AddressFamily.InterNetworkV6; } +		} + +		public static bool IsLoopback (IPv6Address addr) +		{ +			if (addr.address [7] != 1) +				return false; + +			int x = addr.address [6] >> 8; +			if (x != 0x7f && x != 0) +				return false; + +			for (int i = 0; i < 4; i++) { +				if (addr.address [i] != 0) +					return false; +			} + +			if (addr.address [5] != 0 && addr.address [5] != 0xffff) +				return false; + +			return true; +		} + +		private static ushort SwapUShort (ushort number) +		{ +			return (ushort) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) ); +		} + +		// Convert the address into a format expected by the IPAddress (long) ctor +		private int AsIPv4Int () +		{ +			return (SwapUShort (address [7]) << 16) + SwapUShort (address [6]); +		}			 + +		public bool IsIPv4Compatible () +		{ +			for (int i = 0; i < 6; i++)  +				if (address [i] != 0) +					return false; +			return (AsIPv4Int () > 1); +		} +		 +		public bool IsIPv4Mapped () +		{ +			for (int i = 0; i < 5; i++)  +				if (address [i] != 0) +					return false; +			return address [5] == 0xffff; +		} +		 +		/// <summary> +		///   Overrides System.Object.ToString to return +		///   this object rendered in a canonicalized notation +		/// </summary> +		public override string ToString () +		{ +			StringBuilder s = new StringBuilder (); + + +			if(IsIPv4Compatible() || IsIPv4Mapped()) +			{ +				s.Append("::"); + +				if(IsIPv4Mapped()) +					s.Append("ffff:"); + +				s.Append(new IPAddress( AsIPv4Int ()).ToString ()); + +				return s.ToString (); +			} +			else +			{ +				int bestChStart = -1; // Best chain start +				int bestChLen = 0; // Best chain length +				int currChLen = 0; // Current chain length + +				// Looks for the longest zero chain +				for (int i=0; i<8; i++) +				{ +					if (address[i] != 0) +					{ +						if ((currChLen > bestChLen)  +							&& (currChLen > 1)) +						{ +							bestChLen = currChLen; +							bestChStart = i - currChLen; +						} +						currChLen = 0; +					} +					else +						currChLen++; +				} +				if ((currChLen > bestChLen)  +					&& (currChLen > 1)) +				{ +					bestChLen = currChLen; +					bestChStart = 8 - currChLen; +				} + +				// makes the string +				if (bestChStart == 0) +					s.Append(":"); +				for (int i=0; i<8; i++) +				{ +					if (i == bestChStart) +					{ +						s.Append (":"); +						i += (bestChLen - 1); +						continue; +					} +					s.AppendFormat("{0:x}", address [i]); +					if (i < 7) s.Append (':'); +				} +			} +			if (scopeId != 0) +				s.Append ('%').Append (scopeId); +			return s.ToString (); +		} + +		public string ToString (bool fullLength) +		{ +			if (!fullLength) +				return ToString (); + +			StringBuilder sb = new StringBuilder (); +			for (int i=0; i < address.Length - 1; i++) { +				sb.AppendFormat ("{0:X4}:", address [i]); +			} +			sb.AppendFormat ("{0:X4}", address [address.Length - 1]); +			return sb.ToString (); +		} + +		/// <returns> +		///   Whether both objects are equal. +		/// </returns> +		public override bool Equals (object other) +		{ +			System.Net.IPv6Address ipv6 = other as System.Net.IPv6Address; +			if (ipv6 != null) { +				for (int i = 0; i < 8; i++)  +					if (this.address [i] != ipv6.address [i]) +						return false; +				return true; +			} +			 +			System.Net.IPAddress ipv4 = other as System.Net.IPAddress; +			if (ipv4 != null) { +				for (int i = 0; i < 5; i++)  +					if (address [i] != 0) +						return false; + +				if (address [5] != 0 && address [5] != 0xffff) +					return false; + +				long a = ipv4.InternalIPv4Address; +				if (address [6] != (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff))) || +				    address [7] != (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff)))) +					return false; + +				return true; +			} +			 +			return false; +		} + +		public override int GetHashCode () +		{ +			return Hash (((((int) address [0]) << 16) + address [1]),  +						((((int) address [2]) << 16) + address [3]), +						((((int) address [4]) << 16) + address [5]), +						((((int) address [6]) << 16) + address [7])); +		} +		 +		private static int Hash (int i, int j, int k, int l)  +		{ +			return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25); +		} +	} +} diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/Locale.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/Locale.cs new file mode 100644 index 0000000..27a4f39 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/Locale.cs @@ -0,0 +1,14 @@ +internal class MonoTODOAttribute : System.Attribute +{ +  public MonoTODOAttribute(string s) +  {  +} +} + +internal static class Locale +{ +   public static string GetText(string s) +   { +     return s; +   } +} diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/MiniParser.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/MiniParser.cs new file mode 100644 index 0000000..8934ffc --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/MiniParser.cs @@ -0,0 +1,628 @@ +// +// System.Security.Cryptography.MiniParser: Internal XML parser implementation +// +// Authors: +//	Sergey Chaban +// +// Copyright (c) 2001, 2002 Wild West Software +// Copyright (c) 2002 Sergey Chaban +// Copyright (C) 2004 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; +using System.Text; +using System.Collections; +using System.Globalization; + +namespace MonoForks.Mono.Xml +{ + +#if INSIDE_CORLIB +	internal +#else +//        [CLSCompliant(false)]     +	public +#endif +class MiniParser { + +	public interface IReader { +		int Read(); +	} + +	public interface IAttrList { +		int Length {get;} +		bool IsEmpty {get;} +		string GetName(int i); +		string GetValue(int i); +		string GetValue(string name); +		void ChangeValue(string name, string newValue); +		string[] Names {get;} +		string[] Values {get;} +	} + +	public interface IMutableAttrList : IAttrList { +		void Clear(); +		void Add(string name, string value); +		void CopyFrom(IAttrList attrs); +		void Remove(int i); +		void Remove(string name); +	} + +	public interface IHandler { +		void OnStartParsing(MiniParser parser); +		void OnStartElement(string name, IAttrList attrs); +		void OnEndElement(string name); +		void OnChars(string ch); +		void OnEndParsing(MiniParser parser); +	} + +	public class HandlerAdapter : IHandler { +		public HandlerAdapter() {} +		public void OnStartParsing(MiniParser parser) {} +		public void OnStartElement(string name, IAttrList attrs) {} +		public void OnEndElement(string name) {} +		public void OnChars(string ch) {} +		public void OnEndParsing(MiniParser parser) {} +	} + +	private enum CharKind : byte { +		LEFT_BR     = 0, +		RIGHT_BR    = 1, +		SLASH       = 2, +		PI_MARK     = 3, +		EQ          = 4, +		AMP         = 5, +		SQUOTE      = 6, +		DQUOTE      = 7, +		BANG        = 8, +		LEFT_SQBR   = 9, +		SPACE       = 0xA, +		RIGHT_SQBR  = 0xB, +		TAB         = 0xC, +		CR          = 0xD, +		EOL         = 0xE, +		CHARS       = 0xF, +		UNKNOWN = 0x1F +	} + +	private enum ActionCode : byte { +		START_ELEM               = 0, +		END_ELEM                 = 1, +		END_NAME                 = 2, +		SET_ATTR_NAME            = 3, +		SET_ATTR_VAL             = 4, +		SEND_CHARS               = 5, +		START_CDATA              = 6, +		END_CDATA                = 7, +		ERROR                    = 8, +		STATE_CHANGE             = 9, +		FLUSH_CHARS_STATE_CHANGE = 0xA, +		ACC_CHARS_STATE_CHANGE   = 0xB, +		ACC_CDATA                = 0xC, +		PROC_CHAR_REF            = 0xD, +		UNKNOWN = 0xF +	} + +	public class AttrListImpl : IMutableAttrList { +		protected ArrayList names; +		protected ArrayList values; + +		public AttrListImpl() : this(0) {} + +		public AttrListImpl(int initialCapacity) { +			if (initialCapacity <= 0) { +				names = new ArrayList(); +				values = new ArrayList(); +			} else { +				names = new ArrayList(initialCapacity); +				values = new ArrayList(initialCapacity); +			} +		} + +		public AttrListImpl(IAttrList attrs) +		: this(attrs != null ? attrs.Length : 0) { +			if (attrs != null) this.CopyFrom(attrs); +		} + +		public int Length { +			get {return names.Count;} +		} + +		public bool IsEmpty { +			get {return this.Length != 0;} +		} + +		public string GetName(int i) { +			string res = null; +			if (i >= 0 && i < this.Length) { +				res = names[i] as string; +			} +			return res; +		} + +		public string GetValue(int i) { +			string res = null; +			if (i >= 0 && i < this.Length) { +				res = values[i] as string; +			} +			return res; +		} + +		public string GetValue(string name) { +			return this.GetValue(names.IndexOf(name)); +		} + +		public void ChangeValue(string name, string newValue) { +			int i = names.IndexOf(name); +			if (i >= 0 && i < this.Length) { +				values[i] = newValue; +			} +		} + +		public string[] Names { +			get {return names.ToArray(typeof(string)) as string[];} +		} + +		public string[] Values { +			get {return values.ToArray(typeof(string)) as string[];} +		} + +		public void Clear() { +			names.Clear(); +			values.Clear(); +		} + +		public void Add(string name, string value) { +			names.Add(name); +			values.Add(value); +		} + +		public void Remove(int i) { +			if (i >= 0) { +				names.RemoveAt(i); +				values.RemoveAt(i); +			} +		} + +		public void Remove(string name) { +			this.Remove(names.IndexOf(name)); +		} + +		public void CopyFrom(IAttrList attrs) { +			if (attrs != null && ((object)this == (object)attrs)) { +				this.Clear(); +				int n = attrs.Length; +				for (int i = 0; i < n; i++) { +					this.Add(attrs.GetName(i), attrs.GetValue(i)); +				} +			} +		} +	} + +	public class XMLError : Exception { +		protected string descr; +		protected int line, column; +		public XMLError() : this("Unknown") {} +		public XMLError(string descr) : this(descr, -1, -1) {} +		public XMLError(string descr, int line, int column) +		: base(descr) { +			this.descr = descr; +			this.line = line; +			this.column = column; +		} +		public int Line {get {return line;}} +		public int Column {get {return column;}} +		public override string ToString() { +			return (String.Format("{0} @ (line = {1}, col = {2})", descr, line, column)); +		} +	} + +	private static readonly int INPUT_RANGE = 13; +	private static readonly ushort[] tbl = { +		(ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 1), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 0), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 128), +		(ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 2), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 133), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 16), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.FLUSH_CHARS_STATE_CHANGE << 8) | 4), +		(ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.END_ELEM << 8) | 0), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 2), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 2), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), +		(ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 5), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), +		(ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 4), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.END_NAME << 8) | 6), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.END_NAME << 8) | 7), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.END_NAME << 8) | 8), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), +		(ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 0), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), +		(ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 0), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 129), +		(ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.FLUSH_CHARS_STATE_CHANGE << 8) | 1), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.PROC_CHAR_REF << 8) | 10), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 7), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), +		(ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 9), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.START_ELEM << 8) | 6), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.START_ELEM << 8) | 7), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 8), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), +		(ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 9), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.SET_ATTR_NAME << 8) | 11), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 12), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 130), +		(ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 13), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.PROC_CHAR_REF << 8) | 10), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), +		(ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 11), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 132), +		(ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.SET_ATTR_NAME << 8) | 11), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 12), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 130), +		(ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.SEND_CHARS << 8) | 2), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 16), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 134), +		(ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.SET_ATTR_VAL << 8) | 17), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.PROC_CHAR_REF << 8) | 14), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), +		(ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.SET_ATTR_VAL << 8) | 17), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.PROC_CHAR_REF << 8) | 15), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), +		(ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.START_CDATA << 8) | 18), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 0), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), +		(ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.START_ELEM << 8) | 6), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.START_ELEM << 8) | 7), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 17), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 9), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), +		(ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.END_CDATA << 8) | 10), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), +		(ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), +		(ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.UNKNOWN << 8) | 255), +		0xFFFF +	}; + +	protected static string[] errors = { +		/* 0 */ "Expected element", +		/* 1 */ "Invalid character in tag", +		/* 2 */ "No '='", +		/* 3 */ "Invalid character entity", +		/* 4 */ "Invalid attr value", +		/* 5 */ "Empty tag", +		/* 6 */ "No end tag", +		/* 7 */ "Bad entity ref" +	}; + +	protected int line; +	protected int col; +	protected int[] twoCharBuff; +	protected bool splitCData; + +	public MiniParser() { +		twoCharBuff = new int[2]; +		splitCData = false; +		Reset(); +	} + +	public void Reset() { +		line = 0; +		col = 0; +	} + +	protected static bool StrEquals(string str, StringBuilder sb, int sbStart, int len) { +		if (len != str.Length) return false; +		for (int i = 0; i < len; i++) { +			if (str[i] != sb[sbStart + i]) return false; +		} +		return true; +	} + +	protected void FatalErr(string descr) { +		throw new XMLError(descr, this.line, this.col); +	} + +	protected static int Xlat(int charCode, int state) { +		int p = state * INPUT_RANGE; +		int n = Math.Min(tbl.Length - p, INPUT_RANGE); +		for (;--n >= 0;) { +			ushort code = tbl[p]; +			if (charCode == (code >> 12)) return (code & 0xFFF); +			p++; +		} +		return 0xFFF; +	} + +	public void Parse(IReader reader, IHandler handler) { +		if (reader == null) throw new ArgumentNullException("reader"); +		if (handler == null) handler = new HandlerAdapter(); + +		AttrListImpl attrList = new AttrListImpl(); +		string lastAttrName = null; +		Stack tagStack = new Stack(); +		string elementName = null; +		line = 1; +		col = 0; +		int currCh = 0; +		int stateCode = 0; +		StringBuilder sbChars = new StringBuilder(); +		bool seenCData = false; +		bool isComment = false; +		bool isDTD = false; +		int bracketSwitch = 0; + +		handler.OnStartParsing(this); + +		while (true) { +			++this.col; + +			currCh = reader.Read(); + +			if (currCh == -1) { +				if (stateCode != 0) { +					FatalErr("Unexpected EOF"); +				} +				break; +			} + +			int charCode = "<>/?=&'\"![ ]\t\r\n".IndexOf((char)currCh) & 0xF; +			if (charCode == (int)CharKind.CR) continue; // ignore +			// whitepace ::= (#x20 | #x9 | #xd | #xa)+ +			if (charCode == (int)CharKind.TAB) charCode = (int)CharKind.SPACE; // tab == space +			if (charCode == (int)CharKind.EOL) { +				this.col = 0; +				this.line++; +				charCode = (int)CharKind.SPACE; +			} + +			int actionCode = MiniParser.Xlat(charCode, stateCode); +			stateCode = actionCode & 0xFF; +			// Ignore newline inside attribute value. +			if (currCh == '\n' && (stateCode == 0xE || stateCode == 0xF)) continue; +			actionCode >>= 8; + +			if (stateCode >= 0x80) { +				if (stateCode == 0xFF) { +					FatalErr("State dispatch error."); +				} else { +					FatalErr(errors[stateCode ^ 0x80]); +				} +			} + +			switch (actionCode) { +				case (int)ActionCode.START_ELEM: +					handler.OnStartElement(elementName, attrList); +					if (currCh != '/') { +						tagStack.Push(elementName); +					} else { +						handler.OnEndElement(elementName); +					} +					attrList.Clear(); +					break; + +				case (int)ActionCode.END_ELEM: +					elementName = sbChars.ToString(); +					sbChars = new StringBuilder(); +					string endName = null; +					if (tagStack.Count == 0 || +						elementName != (endName = tagStack.Pop() as string)) { +						if (endName == null) { +							FatalErr("Tag stack underflow"); +						} else { +							FatalErr(String.Format("Expected end tag '{0}' but found '{1}'", elementName, endName)); +						} +					} +					handler.OnEndElement(elementName); +					break; + +				case (int)ActionCode.END_NAME: +					elementName = sbChars.ToString(); +					sbChars = new StringBuilder(); +					if (currCh != '/' && currCh != '>') break; +					goto case (int)ActionCode.START_ELEM; + +				case (int)ActionCode.SET_ATTR_NAME: +					lastAttrName = sbChars.ToString(); +					sbChars = new StringBuilder(); +					break; + +				case (int)ActionCode.SET_ATTR_VAL: +					if (lastAttrName == null) FatalErr("Internal error."); +					attrList.Add(lastAttrName, sbChars.ToString()); +					sbChars = new StringBuilder(); +					lastAttrName = null; +					break; + +				case (int)ActionCode.SEND_CHARS: +					handler.OnChars(sbChars.ToString()); +					sbChars = new StringBuilder(); +					break; + +				case (int)ActionCode.START_CDATA: +					string cdata = "CDATA["; +					isComment = false; +					isDTD = false; + +					if (currCh == '-') { +						currCh = reader.Read(); + +						if (currCh != '-') FatalErr("Invalid comment"); + +						this.col++; +						isComment = true; +						twoCharBuff[0] = -1; +						twoCharBuff[1] = -1; +					} else { +						if (currCh != '[') { +							isDTD = true; +							bracketSwitch = 0; +							break; +						} + +						for (int i = 0; i < cdata.Length; i++) { +							if (reader.Read() != cdata[i]) { +								this.col += i+1; +								break; +							} +						} +						this.col += cdata.Length; +						seenCData = true; +					} +					break; + +				case (int)ActionCode.END_CDATA: +					int n = 0; +					currCh = ']'; + +					while (currCh == ']') { +						currCh = reader.Read(); +						n++; +					} + +					if (currCh != '>') { +						for (int i = 0; i < n; i++) sbChars.Append(']'); +						sbChars.Append((char)currCh); +						stateCode = 0x12; +					} else { +						for (int i = 0; i < n-2; i++) sbChars.Append(']'); +						seenCData = false; +					} + +					this.col += n; +					break; + +				case (int)ActionCode.ERROR: +					FatalErr(String.Format("Error {0}", stateCode)); +					break; + +				case (int)ActionCode.STATE_CHANGE: +					break; + +				case (int)ActionCode.FLUSH_CHARS_STATE_CHANGE: +					sbChars = new StringBuilder(); +					if (currCh != '<') goto case (int)ActionCode.ACC_CHARS_STATE_CHANGE; +					break; + +				case (int)ActionCode.ACC_CHARS_STATE_CHANGE: +					sbChars.Append((char)currCh); +					break; + +				case (int)ActionCode.ACC_CDATA: +					if (isComment) { +						if (currCh == '>' +							&& twoCharBuff[0] == '-' +							&& twoCharBuff[1] == '-') { +							isComment = false; +							stateCode = 0; +						} else { +							twoCharBuff[0] = twoCharBuff[1]; +							twoCharBuff[1] = currCh; +						} +					} else if (isDTD) { +						if (currCh == '<' || currCh == '>') bracketSwitch ^= 1; +						if (currCh == '>' && bracketSwitch != 0) { +							isDTD = false; +							stateCode = 0; +						} +					} else { +						if (this.splitCData +							&& sbChars.Length > 0 +							&& seenCData) { +							handler.OnChars(sbChars.ToString()); +							sbChars = new StringBuilder(); +						} +						seenCData = false; +						sbChars.Append((char)currCh); +					} +					break; + +				case (int)ActionCode.PROC_CHAR_REF: +					currCh = reader.Read(); +					int cl = this.col + 1; +					if (currCh == '#') {    // character reference +						int r = 10; +						int chCode = 0; +						int nDigits = 0; +						currCh = reader.Read(); +						cl++; + +						if (currCh == 'x') { +							currCh = reader.Read(); +							cl++; +							r=16; +						} + +						NumberStyles style = r == 16 ? NumberStyles.HexNumber : NumberStyles.Integer; + +						while (true) { +							int x = -1; +							if (Char.IsNumber((char)currCh) || "abcdef".IndexOf(Char.ToLower((char)currCh)) != -1) { +								try { +									x = Int32.Parse(new string((char)currCh, 1), style); +								} catch (FormatException) {x = -1;} +							} +							if (x == -1) break; +							chCode *= r; +							chCode += x; +							nDigits++; +							currCh = reader.Read(); +							cl++; +						} + +						if (currCh == ';' && nDigits > 0) { +							sbChars.Append((char)chCode); +						} else { +							FatalErr("Bad char ref"); +						} +					} else { +						// entity reference +						string entityRefChars = "aglmopqstu"; // amp | apos | quot | gt | lt +						string entities = "&'\"><"; + +						int pos = 0; +						int entIdx = 0xF; +						int predShift = 0; + +						int sbLen = sbChars.Length; + +						while (true) { +							if (pos != 0xF) pos = entityRefChars.IndexOf((char)currCh) & 0xF; +							if (pos == 0xF) FatalErr(errors[7]); +							sbChars.Append((char)currCh); + +							int path = "\uFF35\u3F8F\u4F8F\u0F5F\uFF78\uE1F4\u2299\uEEFF\uEEFF\uFF4F"[pos]; +							int lBr = (path >> 4) & 0xF; +							int rBr = path & 0xF; +							int lPred = path >> 12; +							int rPred = (path >> 8) & 0xF; +							currCh = reader.Read(); +							cl++; +							pos = 0xF; +							if (lBr != 0xF && currCh == entityRefChars[lBr]) { +								if (lPred < 0xE) entIdx = lPred; +//								pred = lPred; +								predShift = 12; // left +							} else if (rBr != 0xF && currCh == entityRefChars[rBr]) { +								if (rPred < 0xE) entIdx = rPred; +//								pred = rPred; +								predShift = 8; // right +							} else if (currCh == ';') { +								if (entIdx != 0xF +									&& predShift != 0 +									&& ((path >> predShift) & 0xF) == 0xE) break; +								continue; // pos == 0xF +							} + +							pos=0; + +						} + +						int l = cl - this.col - 1; + +						if ((l > 0 && l < 5) +							&&(StrEquals("amp", sbChars, sbLen, l) +							|| StrEquals("apos", sbChars, sbLen, l) +							|| StrEquals("quot", sbChars, sbLen, l) +							|| StrEquals("lt", sbChars, sbLen, l) +							|| StrEquals("gt", sbChars, sbLen, l)) +							) { +								sbChars.Length = sbLen; +								sbChars.Append(entities[entIdx]); +						} else FatalErr(errors[7]); +					} + +					this.col = cl; +					break; + +				default: +					FatalErr(String.Format("Unexpected action code - {0}.", actionCode)); +					break; +			} +		} // while (true) + +		handler.OnEndParsing(this); + +	} // Parse + +} + +} diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/NoAccessPolicy.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/NoAccessPolicy.cs new file mode 100644 index 0000000..166a1d4 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/NoAccessPolicy.cs @@ -0,0 +1,46 @@ +// +// System.Windows.Browser.Net.PolicyDownloadPolicy class +// +// Contact: +//   Moonlight List (moonlight-list@lists.ximian.com) +// +// Copyright (C) 2009 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. +// +#define NET_2_1 +#if NET_2_1 + +using MonoForks.System; +using MonoForks.System.Net; + +namespace MonoForks.System.Windows.Browser.Net { + +	sealed class NoAccessPolicy : ICrossDomainPolicy { + +		public bool IsAllowed (WebRequest request) +		{ +			return false; +		} +	} +} + +#endif + diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/PolicyDownloadPolicy.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/PolicyDownloadPolicy.cs new file mode 100644 index 0000000..4ea405e --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/PolicyDownloadPolicy.cs @@ -0,0 +1,59 @@ +// +// System.Windows.Browser.Net.PolicyDownloadPolicy class +// +// Contact: +//   Moonlight List (moonlight-list@lists.ximian.com) +// +// Copyright (C) 2009 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. +// + +#define NET_2_1 +#if NET_2_1 + +using System; +using MonoForks.System; +using MonoForks.System.Net; + +namespace MonoForks.System.Windows.Browser.Net { + +	sealed class PolicyDownloadPolicy : ICrossDomainPolicy { + +		public bool IsAllowed (WebRequest request) +		{ +			return IsLocalPathPolicy (request.RequestUri); +		} + +		static public bool IsLocalPathPolicy (Uri uri) +		{ +			string local = uri.LocalPath; +			if (String.CompareOrdinal (local, CrossDomainPolicyManager.ClientAccessPolicyFile) == 0) +				return true; +			if (String.CompareOrdinal (local, CrossDomainPolicyManager.CrossDomainFile) == 0) +				return true; + +			return false; +		} +	} +} + +#endif + diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/SiteOfOriginPolicy.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/SiteOfOriginPolicy.cs new file mode 100644 index 0000000..d84fbf4 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/SiteOfOriginPolicy.cs @@ -0,0 +1,55 @@ +// +// System.Windows.Browser.Net.SiteOfOriginPolicy class +// +// Contact: +//   Moonlight List (moonlight-list@lists.ximian.com) +// +// Copyright (C) 2009 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. +// + +#define NET_2_1 +#if NET_2_1 + +using MonoForks.System; +using MonoForks.System.Net; + +namespace MonoForks.System.Windows.Browser.Net { + +	sealed class SiteOfOriginPolicy : ICrossDomainPolicy { + +		public bool IsAllowed (WebRequest request) +		{ +			// a WebRequest to the site of origin (SOO) is always granted +			return true; +		} +		 +		// helper to determine if two Uri share the same origin (policy wise) +		static public bool HasSameOrigin (Uri a, Uri b) +		{ +			return ((a.Scheme == b.Scheme) && (a.DnsSafeHost == b.DnsSafeHost) && +				((a.Port == -1) || (b.Port == -1) || (a.Port == b.Port))); +		} +	} +} + +#endif + diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UnityExtra.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UnityExtra.cs new file mode 100644 index 0000000..f085b16 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UnityExtra.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using MonoForks.System.Net; +using MonoForks.System; + +namespace MonoForks.System.Windows.Interop +{ +	public class PluginHost +	{ +		static public Uri SourceUri +		{ +			get +			{ +				return new Uri(UnityEngine.UnityCrossDomainHelper.GetWebSecurityHostUri()); +			} +		} +		static public Uri RootUri +		{ +			get +			{ +				return new Uri(GetRoot(SourceUri)); +			} +		} + +		private static string GetRoot(Uri uri) +		{ +			if ((uri.Scheme == "http" && uri.Port == 80) || (uri.Scheme == "https" && uri.Port == 443) || (uri.Port == -1)) +				return String.Format("{0}://{1}", uri.Scheme, uri.DnsSafeHost); +			else +				return String.Format("{0}://{1}:{2}", uri.Scheme, uri.DnsSafeHost, uri.Port); +		} + +	} +} + +namespace MonoForks.System.Net +{ +	internal class WebRequest +	{ +		public WebRequest(MonoForks.System.Uri requesturi, Dictionary<string,string> headers) +		{ +			this.RequestUri = requesturi; +			this.Headers = headers; +		} +		public MonoForks.System.Uri RequestUri { get; set; }  +		public Dictionary<string,string> Headers { get; set; }  +	} +} diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/Uri.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/Uri.cs new file mode 100644 index 0000000..0bf4011 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/Uri.cs @@ -0,0 +1,2225 @@ +// +// System.Uri +// +// Authors: +//    Lawrence Pit (loz@cable.a2000.nl) +//    Garrett Rooney (rooneg@electricjellyfish.net) +//    Ian MacLean (ianm@activestate.com) +//    Ben Maurer (bmaurer@users.sourceforge.net) +//    Atsushi Enomoto (atsushi@ximian.com) +//    Sebastien Pouliot  <sebastien@ximian.com> +//    Stephane Delcroix  <stephane@delcroix.org> +// +// (C) 2001 Garrett Rooney +// (C) 2003 Ian MacLean +// (C) 2003 Ben Maurer +// Copyright (C) 2003,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. +// +// See RFC 2396 for more info on URI's. +// +// TODO: optimize by parsing host string only once +// +//using System.ComponentModel; +using System.IO; +using MonoForks.System.Net; +using System; +using System.Runtime.Serialization; +using System.Text; +using System.Collections; +using System.Globalization; + +// +// Disable warnings on Obsolete methods being used +// +#pragma warning disable 612 + +namespace MonoForks.System { + +	[Serializable] +#if NET_2_0 +	[TypeConverter (typeof (UriTypeConverter))] +	public class Uri : ISerializable { +#else +	public class Uri : MarshalByRefObject, ISerializable { +#endif +		// NOTES: +		// o  scheme excludes the scheme delimiter +		// o  port is -1 to indicate no port is defined +		// o  path is empty or starts with / when scheme delimiter == "://" +		// o  query is empty or starts with ? char, escaped. +		// o  fragment is empty or starts with # char, unescaped. +		// o  all class variables are in escaped format when they are escapable, +		//    except cachedToString. +		// o  UNC is supported, as starts with "\\" for windows, +		//    or "//" with unix. + +		private bool isUnixFilePath; +		private string source; +		private string scheme = String.Empty; +		private string host = String.Empty; +		private int port = -1; +		private string path = String.Empty; +		private string query = String.Empty; +		private string fragment = String.Empty; +		private string userinfo = String.Empty; +		private bool isUnc; +		private bool isOpaquePart; +		private bool isAbsoluteUri = true; + +		private string [] segments; +		 +		private bool userEscaped; +		private string cachedAbsoluteUri; +		private string cachedToString; +		private string cachedLocalPath; +		private int cachedHashCode; + +		private static readonly string hexUpperChars = "0123456789ABCDEF"; +	 +		// Fields +		 +		public static readonly string SchemeDelimiter = "://"; +		public static readonly string UriSchemeFile = "file"; +		public static readonly string UriSchemeFtp = "ftp"; +		public static readonly string UriSchemeGopher = "gopher"; +		public static readonly string UriSchemeHttp = "http"; +		public static readonly string UriSchemeHttps = "https"; +		public static readonly string UriSchemeMailto = "mailto"; +		public static readonly string UriSchemeNews = "news"; +		public static readonly string UriSchemeNntp = "nntp"; +#if NET_2_0 +		public static readonly string UriSchemeNetPipe = "net.pipe"; +		public static readonly string UriSchemeNetTcp = "net.tcp"; +#endif + +		// Constructors		 + +#if NET_2_1 +		public Uri (string uriString) : this (uriString, UriKind.Absolute)  +		{ +		} +#else +		public Uri (string uriString) : this (uriString, false)  +		{ +		} +#endif +		protected Uri (SerializationInfo serializationInfo,  +			       StreamingContext streamingContext) : +			this (serializationInfo.GetString ("AbsoluteUri"), true) +		{ +		} + +#if NET_2_0 +		public Uri (string uriString, UriKind uriKind) +		{ +			source = uriString; +			ParseUri (uriKind); + +			switch (uriKind) { +			case UriKind.Absolute: +				if (!IsAbsoluteUri) +					throw new UriFormatException("Invalid URI: The format of the URI could not be " +						+ "determined."); +				break; +			case UriKind.Relative: +				if (IsAbsoluteUri) +					throw new UriFormatException("Invalid URI: The format of the URI could not be " +						+ "determined because the parameter 'uriString' represents an absolute URI."); +				break; +			case UriKind.RelativeOrAbsolute: +				break; +			default: +				string msg = Locale.GetText ("Invalid UriKind value '{0}'.", uriKind); +				throw new ArgumentException (msg); +			} +		} + +		// +		// An exception-less constructor, returns success +		// condition on the out parameter `success'. +		// +		Uri (string uriString, UriKind uriKind, out bool success) +		{ +			if (uriString == null) { +				success = false; +				return; +			} + +			if (uriKind != UriKind.RelativeOrAbsolute && +				uriKind != UriKind.Absolute && +				uriKind != UriKind.Relative) { +				string msg = Locale.GetText ("Invalid UriKind value '{0}'.", uriKind); +				throw new ArgumentException (msg); +			} + +			source = uriString; +			if (ParseNoExceptions (uriKind, uriString) != null) +				success = false; +			else { +				success = true; +				 +				switch (uriKind) { +				case UriKind.Absolute: +					if (!IsAbsoluteUri) +						success = false; +					break; +				case UriKind.Relative: +					if (IsAbsoluteUri) +						success = false; +					break; +				case UriKind.RelativeOrAbsolute: +					break; +				default: +					success = false; +					break; +				} +			} +		} + +		public Uri (Uri baseUri, Uri relativeUri) +		{ +			Merge (baseUri, relativeUri == null ? String.Empty : relativeUri.OriginalString); +			// FIXME: this should call UriParser.Resolve +		} + +		// note: doc says that dontEscape is always false but tests show otherwise +		[Obsolete] +		public Uri (string uriString, bool dontEscape)  +		{ +			userEscaped = dontEscape; +			source = uriString; +			ParseUri (UriKind.Absolute); +			if (!isAbsoluteUri) +				throw new UriFormatException("Invalid URI: The format of the URI could not be " +					+ "determined: " + uriString); +		} +#else +		public Uri (string uriString, bool dontEscape)  +		{ +			userEscaped = dontEscape; +			source = uriString; +			Parse (); +			if (!isAbsoluteUri) +				throw new UriFormatException("Invalid URI: The format of the URI could not be " +					+ "determined."); +		} +#endif + +		public Uri (Uri baseUri, string relativeUri)  +		{ +			Merge (baseUri, relativeUri); +			// FIXME: this should call UriParser.Resolve +		} + +#if NET_2_0 +		[Obsolete ("dontEscape is always false")] +#endif +		public Uri (Uri baseUri, string relativeUri, bool dontEscape)  +		{ +			userEscaped = dontEscape; +			Merge (baseUri, relativeUri); +		} + +		private void Merge (Uri baseUri, string relativeUri) +		{ +#if NET_2_0 +			if (baseUri == null) +				throw new ArgumentNullException ("baseUri"); +			if (!baseUri.IsAbsoluteUri) +				throw new ArgumentOutOfRangeException ("baseUri"); +			if (relativeUri == null) +				relativeUri = String.Empty; +#else +			if (baseUri == null) +				throw new NullReferenceException ("baseUri"); +#endif +			// See RFC 2396 Par 5.2 and Appendix C + +			// Check Windows UNC (for // it is scheme/host separator) +			if (relativeUri.Length >= 2 && relativeUri [0] == '\\' && relativeUri [1] == '\\') { +				source = relativeUri; +#if NET_2_0 +				ParseUri (UriKind.Absolute); +#else +				Parse (); +#endif +				return; +			} + +			int pos = relativeUri.IndexOf (':'); +			if (pos != -1) { + +				int pos2 = relativeUri.IndexOfAny (new char [] {'/', '\\', '?'}); + +				// pos2 < 0 ... e.g. mailto +				// pos2 > pos ... to block ':' in query part +				if (pos2 > pos || pos2 < 0) { +					// in some cases, it is equivanent to new Uri (relativeUri, dontEscape): +					// 1) when the URI scheme in the  +					// relative path is different from that +					// of the baseUri, or +					// 2) the URI scheme is non-standard +					// ones (non-standard URIs are always +					// treated as absolute here), or +					// 3) the relative URI path is absolute. +					if (String.CompareOrdinal (baseUri.Scheme, 0, relativeUri, 0, pos) != 0 || +					    !IsPredefinedScheme (baseUri.Scheme) || +					    relativeUri.Length > pos + 1 && +					    relativeUri [pos + 1] == '/') { +						source = relativeUri; +#if NET_2_0 +						ParseUri (UriKind.Absolute); +#else +						Parse (); +#endif +						return; +					} +					else +						relativeUri = relativeUri.Substring (pos + 1); +				} +			} + +			this.scheme = baseUri.scheme; +			this.host = baseUri.host; +			this.port = baseUri.port; +			this.userinfo = baseUri.userinfo; +			this.isUnc = baseUri.isUnc; +			this.isUnixFilePath = baseUri.isUnixFilePath; +			this.isOpaquePart = baseUri.isOpaquePart; + +			if (relativeUri == String.Empty) { +				this.path = baseUri.path; +				this.query = baseUri.query; +				this.fragment = baseUri.fragment; +				return; +			} +			 +			// 8 fragment +			// Note that in relative constructor, file URI cannot handle '#' as a filename character, but just regarded as a fragment identifier. +			pos = relativeUri.IndexOf ('#'); +			if (pos != -1) { +				if (userEscaped) +					fragment = relativeUri.Substring (pos); +				else +					fragment = "#" + EscapeString (relativeUri.Substring (pos+1)); +				relativeUri = relativeUri.Substring (0, pos); +			} + +			// 6 query +			pos = relativeUri.IndexOf ('?'); +			if (pos != -1) { +				query = relativeUri.Substring (pos); +				if (!userEscaped) +					query = EscapeString (query); +				relativeUri = relativeUri.Substring (0, pos); +			} + +			if (relativeUri.Length > 0 && relativeUri [0] == '/') { +				if (relativeUri.Length > 1 && relativeUri [1] == '/') { +					source = scheme + ':' + relativeUri; +#if NET_2_0 +					ParseUri (UriKind.Absolute); +#else +					Parse (); +#endif +					return; +				} else { +					path = relativeUri; +					if (!userEscaped) +						path = EscapeString (path); +					return; +				} +			} +			 +			// par 5.2 step 6 a) +			path = baseUri.path; +			if (relativeUri.Length > 0 || query.Length > 0) { +				pos = path.LastIndexOf ('/'); +				if (pos >= 0)  +					path = path.Substring (0, pos + 1); +			} + +			if(relativeUri.Length == 0) +				return; +	 +			// 6 b) +			path += relativeUri; + +			// 6 c) +			int startIndex = 0; +			while (true) { +				pos = path.IndexOf ("./", startIndex); +				if (pos == -1) +					break; +				if (pos == 0) +					path = path.Remove (0, 2); +				else if (path [pos - 1] != '.') +					path = path.Remove (pos, 2); +				else +					startIndex = pos + 1; +			} +			 +			// 6 d) +			if (path.Length > 1 &&  +			    path [path.Length - 1] == '.' && +			    path [path.Length - 2] == '/') +				path = path.Remove (path.Length - 1, 1); +			 +			// 6 e) +			startIndex = 0; +			while (true) { +				pos = path.IndexOf ("/../", startIndex); +				if (pos == -1) +					break; +				if (pos == 0) { +					startIndex = 3; +					continue; +				} +				int pos2 = path.LastIndexOf ('/', pos - 1); +				if (pos2 == -1) { +					startIndex = pos + 1; +				} else { +					if (path.Substring (pos2 + 1, pos - pos2 - 1) != "..") +						path = path.Remove (pos2 + 1, pos - pos2 + 3); +					else +						startIndex = pos + 1; +				} +			} +			 +			// 6 f) +			if (path.Length > 3 && path.EndsWith ("/..")) { +				pos = path.LastIndexOf ('/', path.Length - 4); +				if (pos != -1) +					if (path.Substring (pos + 1, path.Length - pos - 4) != "..") +						path = path.Remove (pos + 1, path.Length - pos - 1); +			} +			 +			if (!userEscaped) +				path = EscapeString (path); +		}		 +		 +		// Properties +		 +		public string AbsolutePath {  +			get { +#if NET_2_0 +				EnsureAbsoluteUri (); +				switch (Scheme) { +				case "mailto": +				case "file": +					// faster (mailto) and special (file) cases +					return path; +				default: +					if (path.Length == 0) { +						string start = Scheme + SchemeDelimiter; +						if (path.StartsWith (start)) +							return "/"; +						else +							return String.Empty; +					} +					return path; +				} +#else +				return path; +#endif +			} +		} + +		public string AbsoluteUri {  +			get {  +				EnsureAbsoluteUri (); +				if (cachedAbsoluteUri == null) { +					cachedAbsoluteUri = GetLeftPart (UriPartial.Path); +					if (query.Length > 0) +						cachedAbsoluteUri += query; +					if (fragment.Length > 0) +						cachedAbsoluteUri += fragment; +				} +				return cachedAbsoluteUri; +			}  +		} + +		public string Authority {  +			get {  +				EnsureAbsoluteUri (); +				return (GetDefaultPort (Scheme) == port) +				     ? host : host + ":" + port; +			}  +		} + +		public string Fragment {  +			get {  +				EnsureAbsoluteUri (); +				return fragment;  +			}  +		} + +		public string Host {  +			get {  +				EnsureAbsoluteUri (); +				return host;  +			}  +		} +#if !NET_2_1 +		public UriHostNameType HostNameType {  +			get { +				EnsureAbsoluteUri (); +				UriHostNameType ret = CheckHostName (Host); +				if (ret != UriHostNameType.Unknown) +					return ret; +#if NET_2_0 +				switch (Scheme) { +				case "mailto": +					return UriHostNameType.Basic; +				default: +					return (IsFile) ? UriHostNameType.Basic : ret; +				} +#else +				// looks it always returns Basic... +				return UriHostNameType.Basic; //.Unknown; +#endif +			}  +		} + +#endif // NET_2_1 + +		public bool IsDefaultPort {  +			get { +				EnsureAbsoluteUri (); +				return GetDefaultPort (Scheme) == port; +			} +		} + +		public bool IsFile {  +			get { +				EnsureAbsoluteUri (); +				return (Scheme == UriSchemeFile); +			} +		} + +#if !NET_2_1 +		public bool IsLoopback {  +			get { +				EnsureAbsoluteUri (); +				 +				if (Host.Length == 0) { +#if NET_2_0 +					return IsFile; +#else +					return false; +#endif +				} + +				if (host == "loopback" || host == "localhost")  +					return true; + +				IPAddress result; +				if (IPAddress.TryParse (host, out result)) +					if (IPAddress.Loopback.Equals (result)) +						return true; + +				IPv6Address result6; +				if (IPv6Address.TryParse (host, out result6)){ +					if (IPv6Address.IsLoopback (result6)) +						return true; +				} + +				return false; +			}  +		} + +#endif // NET_2_1 + +		public bool IsUnc { +			// rule: This should be true only if +			//   - uri string starts from "\\", or +			//   - uri string starts from "//" (Samba way) +			get {  +				EnsureAbsoluteUri (); +				return isUnc;  +			}  +		} + +		public string LocalPath {  +			get { +				EnsureAbsoluteUri (); +				if (cachedLocalPath != null) +					return cachedLocalPath; +				if (!IsFile) +					return AbsolutePath; + +				bool windows = (path.Length > 3 && path [1] == ':' && +						(path [2] == '\\' || path [2] == '/')); + +				if (!IsUnc) { +					string p = Unescape (path); +					bool replace = windows; +#if ONLY_1_1 +					replace |= (System.IO.Path.DirectorySeparatorChar == '\\'); +#endif +					if (replace) +						cachedLocalPath = p.Replace ('/', '\\'); +					else +						cachedLocalPath = p; +				} else { +					// support *nix and W32 styles +					if (path.Length > 1 && path [1] == ':') +						cachedLocalPath = Unescape (path.Replace (Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)); + +					// LAMESPEC: ok, now we cannot determine +					// if such URI like "file://foo/bar" is +					// Windows UNC or unix file path, so +					// they should be handled differently. +					else if (global::System.IO.Path.DirectorySeparatorChar == '\\') { +						string h = host; +						if (path.Length > 0) { +#if NET_2_0 +							if ((path.Length > 1) || (path[0] != '/')) { +								h += path.Replace ('/', '\\'); +							} +#else +							h += path.Replace ('/', '\\'); +#endif +						} +						cachedLocalPath = "\\\\" + Unescape (h); +					}  else +						cachedLocalPath = Unescape (path); +				} +				if (cachedLocalPath.Length == 0) +					cachedLocalPath = Path.DirectorySeparatorChar.ToString (); +				return cachedLocalPath; +			}  +		} + +		public string PathAndQuery {  +			get { +				EnsureAbsoluteUri (); +				return path + Query; +			}  +		} + +		public int Port {  +			get {  +				EnsureAbsoluteUri (); +				return port;  +			}  +		} + +		public string Query {  +			get {  +				EnsureAbsoluteUri (); +				return query;  +			} +		} + +		public string Scheme {  +			get {  +				EnsureAbsoluteUri (); +				return scheme;  +			}  +		} + +		public string [] Segments {  +			get {  +				EnsureAbsoluteUri (); +				if (segments != null) +					return segments; + +				if (path.Length == 0) { +					segments = new string [0]; +					return segments; +				} + +				string [] parts = path.Split ('/'); +				segments = parts; +				bool endSlash = path.EndsWith ("/"); +				if (parts.Length > 0 && endSlash) { +					string [] newParts = new string [parts.Length - 1]; +					Array.Copy (parts, 0, newParts, 0, parts.Length - 1); +					parts = newParts; +				} + +				int i = 0; +				if (IsFile && path.Length > 1 && path [1] == ':') { +					string [] newParts = new string [parts.Length + 1]; +					Array.Copy (parts, 1, newParts, 2, parts.Length - 1); +					parts = newParts; +					parts [0] = path.Substring (0, 2); +					parts [1] = String.Empty; +					i++; +				} +				 +				int end = parts.Length; +				for (; i < end; i++)  +					if (i != end - 1 || endSlash) +						parts [i] += '/'; + +				segments = parts; +				return segments; +			}  +		} + +		public bool UserEscaped {  +			get { return userEscaped; }  +		} + +		public string UserInfo {  +			get {  +				EnsureAbsoluteUri (); +				return userinfo;  +			} +		} +		 +//#if NET_2_0 +		[MonoTODO ("add support for IPv6 address")] +		public string DnsSafeHost { +			get { +				EnsureAbsoluteUri (); +				return Unescape (Host); +			} +		} + +		public bool IsAbsoluteUri { +			get { return isAbsoluteUri; } +		} +//#endif +		// LAMESPEC: source field is supplied in such case that this +		// property makes sense. For such case that source field is +		// not supplied (i.e. .ctor(Uri, string), this property +		// makes no sense. To avoid silly regression it just returns +		// ToString() value now. See bug #78374. +#if NET_2_0 +		public +#else +		internal +#endif +		string OriginalString { +			get { return source != null ? source : ToString (); } +		} + +		// Methods		 + +		public static UriHostNameType CheckHostName (string name)  +		{ +			if (name == null || name.Length == 0) +				return UriHostNameType.Unknown; + +			if (IsIPv4Address (name))  +				return UriHostNameType.IPv4; +				 +			if (IsDomainAddress (name)) +				return UriHostNameType.Dns;				 +				 +			IPv6Address addr; +			if (IPv6Address.TryParse (name, out addr)) +				return UriHostNameType.IPv6; +			 +			return UriHostNameType.Unknown; +		} +		 +		internal static bool IsIPv4Address (string name) +		{ +			string [] captures = name.Split (new char [] {'.'}); +			if (captures.Length != 4) +				return false; + +			for (int i = 0; i < 4; i++) { +				int length; + +				length = captures [i].Length; +				if (length == 0) +					return false; +				uint number; +#if NET_2_0 +				if (!UInt32.TryParse (captures [i], out number)) +					return false; +#else +				try { +					number = UInt32.Parse (captures [i]); +				} catch (Exception) { +					return false; +				} +#endif +				if (number > 255) +					return false; +			} +			return true; +		}			 +				 +		internal static bool IsDomainAddress (string name) +		{ +			int len = name.Length; +			 +			int count = 0; +			for (int i = 0; i < len; i++) { +				char c = name [i]; +				if (count == 0) { +					if (!Char.IsLetterOrDigit (c)) +						return false; +				} else if (c == '.') { +					count = 0; +				} else if (!Char.IsLetterOrDigit (c) && c != '-' && c != '_') { +					return false; +				} +				if (++count == 64) +					return false; +			} +			 +			return true; +		} +#if !NET_2_1 + +#if NET_2_0 +		[Obsolete("This method does nothing, it has been obsoleted")] +#endif +		protected virtual void Canonicalize () +		{ +			// +			// This is flagged in the Microsoft documentation as used +			// internally, no longer in use, and Obsolete. +			// +		} + +		[MonoTODO ("Find out what this should do")] +#if NET_2_0 +		[Obsolete] +#endif +		protected virtual void CheckSecurity () +		{ +		} + +#endif // NET_2_1 + +		// defined in RFC3986 as = ALPHA *( ALPHA / DIGIT / "+" / "-" / ".") +		public static bool CheckSchemeName (string schemeName)  +		{ +			if (schemeName == null || schemeName.Length == 0) +				return false; +			 +			if (!IsAlpha (schemeName [0])) +				return false; + +			int len = schemeName.Length; +			for (int i = 1; i < len; i++) { +				char c = schemeName [i]; +				if (!Char.IsDigit (c) && !IsAlpha (c) && c != '.' && c != '+' && c != '-') +					return false; +			} +			 +			return true; +		} + +		private static bool IsAlpha (char c) +		{ +#if NET_2_0 +			// as defined in rfc2234 +			// %x41-5A / %x61-7A (A-Z / a-z) +			int i = (int) c; +			return (((i >= 0x41) && (i <= 0x5A)) || ((i >= 0x61) && (i <= 0x7A))); +#else +			// Fx 1.x got this too large +			return Char.IsLetter (c); +#endif +		} + +		public override bool Equals (object comparant)  +		{ +			if (comparant == null)  +				return false; + +			Uri uri = comparant as Uri; +			if ((object) uri == null) { +				string s = comparant as String; +				if (s == null) +					return false; +				uri = new Uri (s); +			} + +			return InternalEquals (uri); +		} + +		// Assumes: uri != null +		bool InternalEquals (Uri uri) +		{ +#if NET_2_0 +			if (this.isAbsoluteUri != uri.isAbsoluteUri) +				return false; +			if (!this.isAbsoluteUri) +				return this.source == uri.source; +#endif + +			CultureInfo inv = CultureInfo.InvariantCulture; +			return this.scheme.ToLower (inv) == uri.scheme.ToLower (inv) +				&& this.host.ToLower (inv) == uri.host.ToLower (inv) +				&& this.port == uri.port +#if NET_2_0 +				&& this.query == uri.query +#else +				// Note: MS.NET 1.x has bug - ignores query check altogether +				&& this.query.ToLower (inv) == uri.query.ToLower (inv) +#endif +				&& this.path == uri.path; +		} + +#if NET_2_0 +		public static bool operator == (Uri u1, Uri u2) +		{ +			return object.Equals(u1, u2); +		} + +		public static bool operator != (Uri u1, Uri u2) +		{ +			return !(u1 == u2); +		} +#endif + +		public override int GetHashCode ()  +		{ +			if (cachedHashCode == 0) { +				CultureInfo inv = CultureInfo.InvariantCulture; +				if (isAbsoluteUri) { +					cachedHashCode = scheme.ToLower (inv).GetHashCode () +						^ host.ToLower (inv).GetHashCode () +						^ port +#if NET_2_0 +						^ query.GetHashCode () +#else +						^ query.ToLower (inv).GetHashCode () +#endif +						^ path.GetHashCode (); +				} +				else { +					cachedHashCode = source.GetHashCode (); +				} +			} +			return cachedHashCode; +		} +		 +		public string GetLeftPart (UriPartial part)  +		{ +			EnsureAbsoluteUri (); +			int defaultPort; +			switch (part) {				 +			case UriPartial.Scheme :  +				return scheme + GetOpaqueWiseSchemeDelimiter (); +			case UriPartial.Authority : +				if ((scheme == Uri.UriSchemeMailto) || (scheme == Uri.UriSchemeNews)) +					return String.Empty; +					 +				StringBuilder s = new StringBuilder (); +				s.Append (scheme); +				s.Append (GetOpaqueWiseSchemeDelimiter ()); +				if (path.Length > 1 && path [1] == ':' && (Uri.UriSchemeFile == scheme))  +					s.Append ('/');  // win32 file +				if (userinfo.Length > 0)  +					s.Append (userinfo).Append ('@'); +				s.Append (host); +				defaultPort = GetDefaultPort (scheme); +				if ((port != -1) && (port != defaultPort)) +					s.Append (':').Append (port);			  +				return s.ToString ();				 +			case UriPartial.Path : +				StringBuilder sb = new StringBuilder (); +				sb.Append (scheme); +				sb.Append (GetOpaqueWiseSchemeDelimiter ()); +				if (path.Length > 1 && path [1] == ':' && (Uri.UriSchemeFile == scheme))  +					sb.Append ('/');  // win32 file +				if (userinfo.Length > 0)  +					sb.Append (userinfo).Append ('@'); +				sb.Append (host); +				defaultPort = GetDefaultPort (scheme); +				if ((port != -1) && (port != defaultPort)) +					sb.Append (':').Append (port); + +				if (path.Length > 0) { +#if NET_2_0 +					switch (Scheme) { +					case "mailto": +					case "news": +						sb.Append (path); +						break; +					default: +						sb.Append (Reduce (path)); +						break; +					} +#else +					sb.Append (path); +#endif +				} +				return sb.ToString (); +			} +			return null; +		} + +		public static int FromHex (char digit)  +		{ +			if ('0' <= digit && digit <= '9') { +				return (int) (digit - '0'); +			} +				 +			if ('a' <= digit && digit <= 'f') +				return (int) (digit - 'a' + 10); + +			if ('A' <= digit && digit <= 'F') +				return (int) (digit - 'A' + 10); +				 +			throw new ArgumentException ("digit"); +		} + +		public static string HexEscape (char character)  +		{ +			if (character > 255) { +				throw new ArgumentOutOfRangeException ("character"); +			} +			 +			return "%" + hexUpperChars [((character & 0xf0) >> 4)]  +			           + hexUpperChars [((character & 0x0f))]; +		} + +		public static char HexUnescape (string pattern, ref int index)  +		{ +			if (pattern == null)  +				throw new ArgumentException ("pattern"); +				 +			if (index < 0 || index >= pattern.Length) +				throw new ArgumentOutOfRangeException ("index"); + +			if (!IsHexEncoding (pattern, index)) +				return pattern [index++]; + +			index++; +			int msb = FromHex (pattern [index++]); +			int lsb = FromHex (pattern [index++]); +			return (char) ((msb << 4) | lsb); +		} + +		public static bool IsHexDigit (char digit)  +		{ +			return (('0' <= digit && digit <= '9') || +			        ('a' <= digit && digit <= 'f') || +			        ('A' <= digit && digit <= 'F')); +		} + +		public static bool IsHexEncoding (string pattern, int index)  +		{ +			if ((index + 3) > pattern.Length) +				return false; + +			return ((pattern [index++] == '%') && +			        IsHexDigit (pattern [index++]) && +			        IsHexDigit (pattern [index])); +		} + +#if NET_2_0 +		// +		// Implemented by copying most of the MakeRelative code +		// +		public Uri MakeRelativeUri (Uri uri) +		{ +			if (uri == null) +				throw new ArgumentNullException ("uri"); + +			if (Host != uri.Host || Scheme != uri.Scheme) +				return uri; + +			string result = String.Empty; +			if (this.path != uri.path){ +				string [] segments = this.Segments; +				string [] segments2 = uri.Segments; +				 +				int k = 0; +				int max = Math.Min (segments.Length, segments2.Length); +				for (; k < max; k++) +					if (segments [k] != segments2 [k])  +						break; +				 +				for (int i = k + 1; i < segments.Length; i++) +					result += "../"; +				for (int i = k; i < segments2.Length; i++) +					result += segments2 [i]; +				 +			} +			uri.AppendQueryAndFragment (ref result); + +			return new Uri (result, UriKind.Relative); +		} + +		[Obsolete ("Use MakeRelativeUri(Uri uri) instead.")] +#endif +		public string MakeRelative (Uri toUri)  +		{ +			if ((this.Scheme != toUri.Scheme) || +			    (this.Authority != toUri.Authority)) +				return toUri.ToString (); + +			string result = String.Empty; +			if (this.path != toUri.path){ +				string [] segments = this.Segments; +				string [] segments2 = toUri.Segments; +				int k = 0; +				int max = Math.Min (segments.Length, segments2.Length); +				for (; k < max; k++) +					if (segments [k] != segments2 [k])  +						break; +				 +				for (int i = k + 1; i < segments.Length; i++) +					result += "../"; +				for (int i = k; i < segments2.Length; i++) +					result += segments2 [i]; +			} + +			// Important: MakeRelative does not append fragment or query. + +			return result; +		} + +		void AppendQueryAndFragment (ref string result) +		{ +			if (query.Length > 0) { +				string q = query [0] == '?' ? '?' + Unescape (query.Substring (1), false) : Unescape (query, false); +				result += q; +			} +			if (fragment.Length > 0) +				result += fragment; +		} +		 +		public override string ToString ()  +		{ +			if (cachedToString != null)  +				return cachedToString; + +			if (isAbsoluteUri) +				cachedToString = Unescape (GetLeftPart (UriPartial.Path), true); +			else { +				// Everything is contained in path in this case.  +				cachedToString = Unescape (path); +			} + +			AppendQueryAndFragment (ref cachedToString); +			return cachedToString; +		} + +#if NET_2_0 +		protected void GetObjectData (SerializationInfo info, StreamingContext context) +		{ +			info.AddValue ("AbsoluteUri", this.AbsoluteUri); +		} +#endif + +		void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) +		{ +			info.AddValue ("AbsoluteUri", this.AbsoluteUri); +		} + + +		// Internal Methods		 + +#if NET_2_0 +		[Obsolete] +#endif +		protected virtual void Escape () +		{ +			path = EscapeString (path); +		} + +#if NET_2_0 +		[Obsolete] +#endif +		protected static string EscapeString (string str)  +		{ +			return EscapeString (str, false, true, true); +		} +		 +		internal static string EscapeString (string str, bool escapeReserved, bool escapeHex, bool escapeBrackets)  +		{ +			if (str == null) +				return String.Empty; +			 +			StringBuilder s = new StringBuilder (); +			int len = str.Length;	 +			for (int i = 0; i < len; i++) { +				// reserved    = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," +				// mark        = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" +				// control     = <US-ASCII coded characters 00-1F and 7F hexadecimal> +				// space       = <US-ASCII coded character 20 hexadecimal> +				// delims      = "<" | ">" | "#" | "%" | <"> +				// unwise      = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`" + +				// check for escape code already placed in str,  +				// i.e. for encoding that follows the pattern  +				// "%hexhex" in a string, where "hex" is a digit from 0-9  +				// or a letter from A-F (case-insensitive). +				if (IsHexEncoding (str,i)) { +					// if ,yes , copy it as is +					s.Append(str.Substring (i, 3)); +					i += 2; +					continue; +				} + +				byte [] data = Encoding.UTF8.GetBytes (new char[] {str[i]}); +				int length = data.Length; +				for (int j = 0; j < length; j++) { +					char c = (char) data [j]; +					if ((c <= 0x20) || (c >= 0x7f) ||  +					    ("<>%\"{}|\\^`".IndexOf (c) != -1) || +					    (escapeHex && (c == '#')) || +					    (escapeBrackets && (c == '[' || c == ']')) || +					    (escapeReserved && (";/?:@&=+$,".IndexOf (c) != -1))) { +						s.Append (HexEscape (c)); +						continue; +					}	 +					s.Append (c); +				} +			} +			 +			return s.ToString (); +		} + +		// On .NET 1.x, this method is called from .ctor(). When overriden, we  +		// can avoid the "absolute uri" constraints of the .ctor() by +		// overriding with custom code. +#if NET_2_0 +		[Obsolete("The method has been deprecated. It is not used by the system.")] +#endif +		protected virtual void Parse () +		{ +#if !NET_2_0 +			ParseUri (UriKind.Absolute); +#endif +		} + +		private void ParseUri (UriKind kind) +		{ +			Parse (kind, source); + +			if (userEscaped) +				return; + +			host = EscapeString (host, false, true, false); +			if (host.Length > 1 && host [0] != '[' && host [host.Length - 1] != ']') { +				// host name present (but not an IPv6 address) +				host = host.ToLower (CultureInfo.InvariantCulture); +			} + +			if (path.Length > 0) { +				path = EscapeString (path); +			} +		} + +#if NET_2_0 +		[Obsolete] +#endif +		protected virtual string Unescape (string str) +		{ +			return Unescape (str, false); +		} +		 +		internal static string Unescape (string str, bool excludeSpecial)  +		{ +			if (str == null) +				return String.Empty; +			StringBuilder s = new StringBuilder (); +			int len = str.Length; +			for (int i = 0; i < len; i++) { +				char c = str [i]; +				if (c == '%') { +					char surrogate; +					char x = HexUnescapeMultiByte (str, ref i, out surrogate); +					if (excludeSpecial && x == '#') +						s.Append ("%23"); +					else if (excludeSpecial && x == '%') +						s.Append ("%25"); +					else if (excludeSpecial && x == '?') +						s.Append ("%3F"); +					else { +						s.Append (x); +						if (surrogate != char.MinValue) +							s.Append (surrogate); +					} +					i--; +				} else +					s.Append (c); +			} +			return s.ToString (); +		} + +		 +		// Private Methods +		 +		private void ParseAsWindowsUNC (string uriString) +		{ +			scheme = UriSchemeFile; +			port = -1; +			fragment = String.Empty; +			query = String.Empty; +			isUnc = true; + +			uriString = uriString.TrimStart (new char [] {'\\'}); +			int pos = uriString.IndexOf ('\\'); +			if (pos > 0) { +				path = uriString.Substring (pos); +				host = uriString.Substring (0, pos); +			} else { // "\\\\server" +				host = uriString; +				path = String.Empty; +			} +			path = path.Replace ("\\", "/"); +		} + +		// +		// Returns null on success, string with error on failure +		// +		private string ParseAsWindowsAbsoluteFilePath (string uriString) +		{ +			if (uriString.Length > 2 && uriString [2] != '\\' && uriString [2] != '/') +				return "Relative file path is not allowed."; +			scheme = UriSchemeFile; +			host = String.Empty; +			port = -1; +			path = uriString.Replace ("\\", "/"); +			fragment = String.Empty; +			query = String.Empty; + +			return null; +		} + +		private void ParseAsUnixAbsoluteFilePath (string uriString) +		{ +			isUnixFilePath = true; +			scheme = UriSchemeFile; +			port = -1; +			fragment = String.Empty; +			query = String.Empty; +			host = String.Empty; +			path = null; + +			if (uriString.Length >= 2 && uriString [0] == '/' && uriString [1] == '/') { +				uriString = uriString.TrimStart (new char [] {'/'}); +				// Now we don't regard //foo/bar as "foo" host. +				/*  +				int pos = uriString.IndexOf ('/'); +				if (pos > 0) { +					path = '/' + uriString.Substring (pos + 1); +					host = uriString.Substring (0, pos); +				} else { // "///server" +					host = uriString; +					path = String.Empty; +				} +				*/ +				path = '/' + uriString; +			} +			if (path == null) +				path = uriString; +		} + +		// +		// This parse method will throw exceptions on failure +		//   +		private void Parse (UriKind kind, string uriString) +		{			 +			if (uriString == null) +				throw new ArgumentNullException ("uriString"); + +			string s = ParseNoExceptions (kind, uriString); +			if (s != null) +				throw new UriFormatException (s); +		} + +		// +		// This parse method will not throw exceptions on failure +		// +		// Returns null on success, or a description of the error in the parsing +		// +		private string ParseNoExceptions (UriKind kind, string uriString) +		{ +			// +			// From RFC 2396 : +			// +			//      ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? +			//       12            3  4          5       6  7        8 9 +			//			 +			 +			uriString = uriString.Trim(); +			int len = uriString.Length; + +			if (len == 0){ +				if (kind == UriKind.Relative || kind == UriKind.RelativeOrAbsolute){ +					isAbsoluteUri = false; +					return null; +				} +			} +			 +			if (len <= 1 && (kind != UriKind.Relative)) +				return "Absolute URI is too short"; + +			int pos = 0; + +			// 1, 2 +			// Identify Windows path, unix path, or standard URI. +			pos = uriString.IndexOf (':'); +			if (pos == 0) { +				return "Invalid URI: The format of the URI could not be determined."; +			} else if (pos < 0) { +				// It must be Unix file path or Windows UNC +				if (uriString [0] == '/' && Path.DirectorySeparatorChar == '/'){ +					ParseAsUnixAbsoluteFilePath (uriString); +#if NET_2_1 +					isAbsoluteUri = false; +#else +					if (kind == UriKind.Relative) +						isAbsoluteUri = false; +#endif +					 +				} else if (uriString.Length >= 2 && uriString [0] == '\\' && uriString [1] == '\\') +					ParseAsWindowsUNC (uriString); +				else { +					/* Relative path */ +					isAbsoluteUri = false; +					path = uriString; +				} +				return null; +			} else if (pos == 1) { +				if (!IsAlpha (uriString [0])) +					return "URI scheme must start with a letter."; +				// This means 'a:' == windows full path. +				string msg = ParseAsWindowsAbsoluteFilePath (uriString); +				if (msg != null) +					return msg; +				return null; +			}  + +			// scheme +			scheme = uriString.Substring (0, pos).ToLower (CultureInfo.InvariantCulture); + +			// Check scheme name characters as specified in RFC2396. +			// Note: different checks in 1.x and 2.0 +			if (!CheckSchemeName (scheme))  +				return Locale.GetText ("URI scheme must start with a letter and must consist of one of alphabet, digits, '+', '-' or '.' character."); + +			// from here we're practically working on uriString.Substring(startpos,endpos-startpos) +			int startpos = pos + 1; +			int endpos = uriString.Length; + +			// 8 fragment +			pos = uriString.IndexOf ('#', startpos); +			if (!IsUnc && pos != -1) { +				if (userEscaped) +					fragment = uriString.Substring (pos); +				else +					fragment = "#" + EscapeString (uriString.Substring (pos+1)); + +				endpos = pos; +			} + +			// 6 query +			pos = uriString.IndexOf ('?', startpos, endpos-startpos); +			if (pos != -1) { +				query = uriString.Substring (pos, endpos-pos); +				endpos = pos; +				if (!userEscaped) +					query = EscapeString (query); +			} + +			// 3 +			if (IsPredefinedScheme (scheme) && scheme != UriSchemeMailto && scheme != UriSchemeNews && ( +				(endpos-startpos < 2) || +				(endpos-startpos >= 2 && uriString [startpos] == '/' && uriString [startpos+1] != '/')))				 +				return "Invalid URI: The Authority/Host could not be parsed."; +			 +			 +			bool startsWithSlashSlash = endpos-startpos >= 2 && uriString [startpos] == '/' && uriString [startpos+1] == '/'; +			bool unixAbsPath = scheme == UriSchemeFile && startsWithSlashSlash && (endpos-startpos == 2 || uriString [startpos+2] == '/'); +			bool windowsFilePath = false; +			if (startsWithSlashSlash) { +				if (kind == UriKind.Relative) +					return "Absolute URI when we expected a relative one"; +				 +				if (scheme != UriSchemeMailto && scheme != UriSchemeNews) +					startpos += 2; + +				if (scheme == UriSchemeFile) { +					int num_leading_slash = 2; +					for (int i = startpos; i < endpos; i++) { +						if (uriString [i] != '/') +							break; +						num_leading_slash++; +					} +					if (num_leading_slash >= 4) { +						unixAbsPath = false; +						while (startpos < endpos && uriString[startpos] == '/') { +							startpos++; +						} +					} else if (num_leading_slash >= 3) { +						startpos += 1; +					} +				} +				 +				if (endpos - startpos > 1 && uriString [startpos + 1] == ':') { +					unixAbsPath = false; +					windowsFilePath = true; +				} + +			} else if (!IsPredefinedScheme (scheme)) { +				path = uriString.Substring(startpos, endpos-startpos); +				isOpaquePart = true; +				return null; +			} + +			// 5 path +			if (unixAbsPath) { +				pos = -1; +			} else { +				pos = uriString.IndexOf ('/', startpos, endpos-startpos); +				if (pos == -1 && windowsFilePath) +					pos = uriString.IndexOf ('\\', startpos, endpos-startpos); +			} + +			if (pos == -1) { +				if ((scheme != Uri.UriSchemeMailto) && +#if ONLY_1_1 +				    (scheme != Uri.UriSchemeFile) && +#endif +				    (scheme != Uri.UriSchemeNews)) +					path = "/"; +			} else { +				path = uriString.Substring (pos, endpos-pos); +				endpos = pos; +			} + +			// 4.a user info +			pos = uriString.IndexOf ('@', startpos, endpos-startpos); +			if (pos != -1) { +				userinfo = uriString.Substring (startpos, pos-startpos); +				startpos = pos + 1; +			} + +			// 4.b port +			port = -1; +			if (unixAbsPath) +				pos = -1; +			else +				pos = uriString.LastIndexOf (':', endpos-1, endpos-startpos); +			if (pos != -1 && pos != endpos - 1) { +				string portStr = uriString.Substring(pos + 1, endpos - (pos + 1)); +				if (portStr.Length > 0 && portStr[portStr.Length - 1] != ']') { +#if NET_2_0 +					if (!Int32.TryParse (portStr, NumberStyles.Integer, CultureInfo.InvariantCulture, out port) || +					    port < 0 || port > UInt16.MaxValue) +						return "Invalid URI: Invalid port number"; +					endpos = pos; +#else +					try { +						port = (int) UInt32.Parse (portStr, CultureInfo.InvariantCulture); +						endpos = pos; +					} catch (Exception) { +						return "Invalid URI: Invalid port number"; +					} +#endif +				} else { +					if (port == -1) { +						port = GetDefaultPort (scheme); +					} +				} +			} else { +				if (port == -1) { +					port = GetDefaultPort (scheme); +				} +			} +			 +			// 4 authority +			uriString = uriString.Substring(startpos, endpos-startpos); +			host = uriString; + +			if (unixAbsPath) { +				path = Reduce ('/' + uriString); +				host = String.Empty; +			} else if (host.Length == 2 && host [1] == ':') { +				// windows filepath +				path = host + path; +				host = String.Empty; +			} else if (isUnixFilePath) { +				uriString = "//" + uriString; +				host = String.Empty; +			} else if (scheme == UriSchemeFile) { +				isUnc = true; +			} else if (scheme == UriSchemeNews) { +				// no host for 'news', misinterpreted path +				if (host.Length > 0) { +					path = host; +					host = String.Empty; +				} +			} else if (host.Length == 0 && +				   (scheme == UriSchemeHttp || scheme == UriSchemeGopher || scheme == UriSchemeNntp || +				    scheme == UriSchemeHttps || scheme == UriSchemeFtp)) { +				return "Invalid URI: The hostname could not be parsed"; +			} + +			bool badhost = ((host.Length > 0) && (CheckHostName (host) == UriHostNameType.Unknown)); +			if (!badhost && (host.Length > 1) && (host[0] == '[') && (host[host.Length - 1] == ']')) { +				IPv6Address ipv6addr; +				 +				if (IPv6Address.TryParse (host, out ipv6addr)) +					host = "[" + ipv6addr.ToString (true) + "]"; +				else +					badhost = true; +			} +#if NET_2_0 +			if (badhost && (Parser is DefaultUriParser || Parser == null)) +				return Locale.GetText ("Invalid URI: The hostname could not be parsed. (" + host + ")"); + +			UriFormatException ex = null; +			if (Parser != null) +				Parser.InitializeAndValidate (this, out ex); +			if (ex != null) +				return ex.Message; +#else +			if (badhost) +				return Locale.GetText ("Invalid URI: The hostname could not be parsed. (" + host + ")"); +#endif + +			if ((scheme != Uri.UriSchemeMailto) && +					(scheme != Uri.UriSchemeNews) && +					(scheme != Uri.UriSchemeFile)) { +				path = Reduce (path); +			} + +			return null; +		} + +		private static string Reduce (string path) +		{ +			// quick out, allocation-free, for a common case +			if (path == "/") +				return path; + +			// replace '\', %5C ('\') and %2f ('/') into '/' +			// other escaped values seems to survive this step +			StringBuilder res = new StringBuilder(); +			for (int i=0; i < path.Length; i++) { +				char c = path [i]; +				switch (c) { +				case '\\': +					res.Append ('/'); +					break; +				case '%': +					if (i < path.Length - 2) { +						char c1 = path [i + 1]; +						char c2 = Char.ToUpper (path [i + 2]); +						if (((c1 == '2') && (c2 == 'F')) || ((c1 == '5') && (c2 == 'C'))) { +							res.Append ('/'); +							i += 2; +						} else { +							res.Append (c); +						} +					} else { +						res.Append (c); +					} +					break; +				default: +					res.Append (c); +					break; +				} +			} +			path = res.ToString (); +			ArrayList result = new ArrayList (); + +			for (int startpos = 0; startpos < path.Length; ) { +				int endpos = path.IndexOf('/', startpos); +				if (endpos == -1) endpos = path.Length; +				string current = path.Substring (startpos, endpos-startpos); +				startpos = endpos + 1; +				if (current.Length == 0 || current == "." ) +					continue; + +				if (current == "..") { +					int resultCount = result.Count; +#if NET_2_0 +					// in 2.0 profile, skip leading ".." parts +					if (resultCount == 0) { +						continue; +					} + +					result.RemoveAt (resultCount - 1); +					continue; +#else +					// in 1.x profile, retain leading ".." parts, and only reduce +					// URI is previous part is not ".." +					if (resultCount > 0) { +						if ((string) result[resultCount - 1] != "..") { +							result.RemoveAt (resultCount - 1); +							continue; +						} +					} +#endif +				} + +				result.Add (current); +			} + +			if (result.Count == 0) +				return "/"; + +			res.Length = 0; +			if (path [0] == '/') +				res.Append ('/'); + +			bool first = true; +			foreach (string part in result) { +				if (first) { +					first = false; +				} else { +					res.Append ('/'); +				} +				res.Append(part); +			} + +			if (path.EndsWith ("/")) +				res.Append ('/'); +				 +			return res.ToString(); +		} + +		// A variant of HexUnescape() which can decode multi-byte escaped +		// sequences such as (e.g.) %E3%81%8B into a single character +		private static char HexUnescapeMultiByte (string pattern, ref int index, out char surrogate)  +		{ +			surrogate = char.MinValue; + +			if (pattern == null)  +				throw new ArgumentException ("pattern"); +				 +			if (index < 0 || index >= pattern.Length) +				throw new ArgumentOutOfRangeException ("index"); + +			if (!IsHexEncoding (pattern, index)) +				return pattern [index++]; + +			int orig_index = index++; +			int msb = FromHex (pattern [index++]); +			int lsb = FromHex (pattern [index++]); + +			// We might be dealing with a multi-byte character: +			// The number of ones at the top-end of the first byte will tell us +			// how many bytes will make up this character. +			int msb_copy = msb; +			int num_bytes = 0; +			while ((msb_copy & 0x8) == 0x8) { +				num_bytes++; +				msb_copy <<= 1; +			} + +			// We might be dealing with a single-byte character: +			// If there was only 0 or 1 leading ones then we're not dealing +			// with a multi-byte character. +			if (num_bytes <= 1) +				return (char) ((msb << 4) | lsb); + +			// Now that we know how many bytes *should* follow, we'll check them +			// to ensure we are dealing with a valid multi-byte character. +			byte [] chars = new byte [num_bytes]; +			bool all_invalid = false; +			chars[0] = (byte) ((msb << 4) | lsb); + +			for (int i = 1; i < num_bytes; i++) { +				if (!IsHexEncoding (pattern, index++)) { +					all_invalid = true; +					break; +				} + +				// All following bytes must be in the form 10xxxxxx +				int cur_msb = FromHex (pattern [index++]); +				if ((cur_msb & 0xc) != 0x8) { +					all_invalid = true; +					break; +				} + +				int cur_lsb = FromHex (pattern [index++]); +				chars[i] = (byte) ((cur_msb << 4) | cur_lsb); +			} + +			// If what looked like a multi-byte character is invalid, then we'll +			// just return the first byte as a single byte character. +			if (all_invalid) { +				index = orig_index + 3; +				return (char) chars[0]; +			} + +			// Otherwise, we're dealing with a valid multi-byte character. +			// We need to ignore the leading ones from the first byte: +			byte mask = (byte) 0xFF; +			mask >>= (num_bytes + 1); +			int result = chars[0] & mask; + +			// The result will now be built up from the following bytes. +			for (int i = 1; i < num_bytes; i++) { +				// Ignore upper two bits +				result <<= 6; +				result |= (chars[i] & 0x3F); +			} + +			if (result <= 0xFFFF) { +				return (char) result; +			} else { +				// We need to handle this as a UTF16 surrogate (i.e. return +				// two characters) +				result -= 0x10000; +				surrogate = (char) ((result & 0x3FF) | 0xDC00); +				return (char) ((result >> 10) | 0xD800); +			} +		} + +		private struct UriScheme  +		{ +			public string scheme; +			public string delimiter; +			public int defaultPort; + +			public UriScheme (string s, string d, int p)  +			{ +				scheme = s; +				delimiter = d; +				defaultPort = p; +			} +		}; + +		static UriScheme [] schemes = new UriScheme [] { +			new UriScheme (UriSchemeHttp, SchemeDelimiter, 80), +			new UriScheme (UriSchemeHttps, SchemeDelimiter, 443), +			new UriScheme (UriSchemeFtp, SchemeDelimiter, 21), +			new UriScheme (UriSchemeFile, SchemeDelimiter, -1), +			new UriScheme (UriSchemeMailto, ":", 25), +			new UriScheme (UriSchemeNews, ":", 119), +			new UriScheme (UriSchemeNntp, SchemeDelimiter, 119), +			new UriScheme (UriSchemeGopher, SchemeDelimiter, 70), +		}; +				 +		internal static string GetSchemeDelimiter (string scheme)  +		{ +			for (int i = 0; i < schemes.Length; i++)  +				if (schemes [i].scheme == scheme) +					return schemes [i].delimiter; +			return Uri.SchemeDelimiter; +		} +		 +		internal static int GetDefaultPort (string scheme) +		{ +#if NET_2_0 +			UriParser parser = UriParser.GetParser (scheme); +			if (parser == null) +				return -1; +			return parser.DefaultPort; +#else +			for (int i = 0; i < schemes.Length; i++)  +				if (schemes [i].scheme == scheme) +					return schemes [i].defaultPort; +			return -1; +#endif +		} + +		private string GetOpaqueWiseSchemeDelimiter () +		{ +			if (isOpaquePart) +				return ":"; +			else +				return GetSchemeDelimiter (scheme); +		} + +#if NET_2_0 +		[Obsolete] +#endif +		protected virtual bool IsBadFileSystemCharacter (char ch) +		{ +			// It does not always overlap with InvalidPathChars. +			int chInt = (int) ch; +			if (chInt < 32 || (chInt < 64 && chInt > 57)) +				return true; +			switch (chInt) { +			case 0: +			case 34: // " +			case 38: // & +			case 42: // * +			case 44: // , +			case 47: // / +			case 92: // \ +			case 94: // ^ +			case 124: // | +				return true; +			} + +			return false; +		} + +#if NET_2_0 +		[Obsolete] +#endif +		protected static bool IsExcludedCharacter (char ch) +		{ +			if (ch <= 32 || ch >= 127) +				return true; +			 +			if (ch == '"' || ch == '#' || ch == '%' || ch == '<' || +			    ch == '>' || ch == '[' || ch == '\\' || ch == ']' || +			    ch == '^' || ch == '`' || ch == '{' || ch == '|' || +			    ch == '}') +				return true; +			return false; +		} + +		internal static bool MaybeUri (string s) +		{ +			int p = s.IndexOf (':'); +			if (p == -1) +				return false; + +			if (p >= 10) +				return false; + +			return IsPredefinedScheme (s.Substring (0, p)); +		} +		 +		private static bool IsPredefinedScheme (string scheme) +		{ +			switch (scheme) { +			case "http": +			case "https": +			case "file": +			case "ftp": +			case "nntp": +			case "gopher": +			case "mailto": +			case "news": +#if NET_2_0 +			case "net.pipe": +			case "net.tcp": +#endif +				return true; +			default: +				return false; +			} +		} + +#if NET_2_0 +		[Obsolete] +#endif +		protected virtual bool IsReservedCharacter (char ch) +		{ +			if (ch == '$' || ch == '&' || ch == '+' || ch == ',' || +			    ch == '/' || ch == ':' || ch == ';' || ch == '=' || +			    ch == '@') +				return true; +			return false; +		} +#if NET_2_0 +		[NonSerialized] +		private UriParser parser; + +		private UriParser Parser { +			get { +				if (parser == null) { +					parser = UriParser.GetParser (Scheme); +					// no specific parser ? then use a default one +					if (parser == null) +						parser = new DefaultUriParser ("*"); +				} +				return parser; +			} +			set { parser = value; } +		} + +		public string GetComponents (UriComponents components, UriFormat format) +		{ +			return Parser.GetComponents (this, components, format); +		} + +		public bool IsBaseOf (Uri uri) +		{ +			return Parser.IsBaseOf (this, uri); +		} + +		public bool IsWellFormedOriginalString () +		{ +			// funny, but it does not use the Parser's IsWellFormedOriginalString(). +			return EscapeString (OriginalString) == OriginalString; +		} + +		// static methods + +		private const int MaxUriLength = 32766; + +		public static int Compare (Uri uri1, Uri uri2, UriComponents partsToCompare, UriFormat compareFormat, StringComparison comparisonType) +		{ +			if ((comparisonType < StringComparison.CurrentCulture) || (comparisonType > StringComparison.OrdinalIgnoreCase)) { +				string msg = Locale.GetText ("Invalid StringComparison value '{0}'", comparisonType); +				throw new ArgumentException ("comparisonType", msg); +			} + +			if ((uri1 == null) && (uri2 == null)) +				return 0; + +			string s1 = uri1.GetComponents (partsToCompare, compareFormat); +			string s2 = uri2.GetComponents (partsToCompare, compareFormat); +			return String.Compare (s1, s2, comparisonType); +		} + +		// +		// The rules for EscapeDataString +		// +		static bool NeedToEscapeDataChar (char b) +		{ +			return !((b >= 'A' && b <= 'Z') || +				 (b >= 'a' && b <= 'z') || +				 (b >= '0' && b <= '9') || +				 b == '_' || b == '~' || b == '!' || b == '\'' || +				 b == '(' || b == ')' || b == '*' || b == '-' || b == '.'); +		} +		 +		public static string EscapeDataString (string stringToEscape) +		{ +			if (stringToEscape == null) +				throw new ArgumentNullException ("stringToEscape"); + +			if (stringToEscape.Length > MaxUriLength) { +				string msg = Locale.GetText ("Uri is longer than the maximum {0} characters."); +				throw new UriFormatException (msg); +			} +			bool escape = false; +			foreach (char c in stringToEscape){ +				if (NeedToEscapeDataChar (c)){ +					escape = true; +					break; +				} +			} +			if (!escape){ +				return stringToEscape; +			} +			 +			StringBuilder sb = new StringBuilder (); +			byte [] bytes = Encoding.UTF8.GetBytes (stringToEscape); +			foreach (byte b in bytes){ +				if (NeedToEscapeDataChar ((char) b)) +					sb.Append (HexEscape ((char) b)); +				else +					sb.Append ((char) b); +			} +			return sb.ToString (); +		} + +		// +		// The rules for EscapeUriString +		// +		static bool NeedToEscapeUriChar (char b) +		{ +			return !((b >= 'A' && b <= 'Z') || +				 (b >= 'a' && b <= 'z') || +				 (b >= '&' && b <= ';') || +				 b == '!' || b == '#' || b == '$' || b == '=' || +				 b == '?' || b == '@' || b == '_' || b == '~'); +		} +		 +		public static string EscapeUriString (string stringToEscape) +		{ +			if (stringToEscape == null) +				throw new ArgumentNullException ("stringToEscape"); + +			if (stringToEscape.Length > MaxUriLength) { +				string msg = Locale.GetText ("Uri is longer than the maximum {0} characters."); +				throw new UriFormatException (msg); +			} + +			bool escape = false; +			foreach (char c in stringToEscape){ +				if (NeedToEscapeUriChar (c)){ +					escape = true; +					break; +				} +			} +			if (!escape) +				return stringToEscape; + +			StringBuilder sb = new StringBuilder (); +			byte [] bytes = Encoding.UTF8.GetBytes (stringToEscape); +			foreach (byte b in bytes){ +				if (NeedToEscapeUriChar ((char) b)) +					sb.Append (HexEscape ((char) b)); +				else +					sb.Append ((char) b); +			} +			return sb.ToString (); +		} + +		public static bool IsWellFormedUriString (string uriString, UriKind uriKind) +		{ +			if (uriString == null) +				return false; + +			Uri uri; +			if (Uri.TryCreate (uriString, uriKind, out uri)) +				return uri.IsWellFormedOriginalString (); +			return false; +		} + +		public static bool TryCreate (string uriString, UriKind uriKind, out Uri result) +		{ +			bool success; + +			Uri r = new Uri (uriString, uriKind, out success); +			if (success) { +				result = r; +				return true; +			} +			result = null; +			return false; +		} + +		// [MonoTODO ("rework code to avoid exception catching")] +		public static bool TryCreate (Uri baseUri, string relativeUri, out Uri result) +		{ +			try { +				// FIXME: this should call UriParser.Resolve +				result = new Uri (baseUri, relativeUri); +				return true; +			} catch (UriFormatException) { +				result = null; +				return false; +			} +		} + +		//[MonoTODO ("rework code to avoid exception catching")] +		public static bool TryCreate (Uri baseUri, Uri relativeUri, out Uri result) +		{ +			try { +				// FIXME: this should call UriParser.Resolve +				result = new Uri (baseUri, relativeUri.OriginalString); +				return true; +			} catch (UriFormatException) { +				result = null; +				return false; +			} +		} + +		public static string UnescapeDataString (string stringToUnescape) +		{ +			if (stringToUnescape == null) +				throw new ArgumentNullException ("stringToUnescape"); + +			if (stringToUnescape.IndexOf ('%') == -1 && stringToUnescape.IndexOf ('+') == -1) +				return stringToUnescape; + +			StringBuilder output = new StringBuilder (); +			long len = stringToUnescape.Length; +			MemoryStream bytes = new MemoryStream (); +			int xchar; + +			for (int i = 0; i < len; i++) { +				if (stringToUnescape [i] == '%' && i + 2 < len && stringToUnescape [i + 1] != '%') { +					if (stringToUnescape [i + 1] == 'u' && i + 5 < len) { +						if (bytes.Length > 0) { +							output.Append (GetChars (bytes, Encoding.UTF8)); +							bytes.SetLength (0); +						} + +						xchar = GetChar (stringToUnescape, i + 2, 4); +						if (xchar != -1) { +							output.Append ((char) xchar); +							i += 5; +						} +						else { +							output.Append ('%'); +						} +					} +					else if ((xchar = GetChar (stringToUnescape, i + 1, 2)) != -1) { +						bytes.WriteByte ((byte) xchar); +						i += 2; +					} +					else { +						output.Append ('%'); +					} +					continue; +				} + +				if (bytes.Length > 0) { +					output.Append (GetChars (bytes, Encoding.UTF8)); +					bytes.SetLength (0); +				} + +				output.Append (stringToUnescape [i]); +			} + +			if (bytes.Length > 0) { +				output.Append (GetChars (bytes, Encoding.UTF8)); +			} + +			bytes = null; +			return output.ToString (); +		} + +		private static int GetInt (byte b) +		{ +			char c = (char) b; +			if (c >= '0' && c <= '9') +				return c - '0'; + +			if (c >= 'a' && c <= 'f') +				return c - 'a' + 10; + +			if (c >= 'A' && c <= 'F') +				return c - 'A' + 10; + +			return -1; +		} + +		private static int GetChar (string str, int offset, int length) +		{ +			int val = 0; +			int end = length + offset; +			for (int i = offset; i < end; i++) { +				char c = str [i]; +				if (c > 127) +					return -1; + +				int current = GetInt ((byte) c); +				if (current == -1) +					return -1; +				val = (val << 4) + current; +			} + +			return val; +		} + +		private static char [] GetChars (MemoryStream b, Encoding e) +		{ +			return e.GetChars (b.GetBuffer (), 0, (int) b.Length); +		} +		 + +		private void EnsureAbsoluteUri () +		{ +			if (!IsAbsoluteUri) +				throw new InvalidOperationException ("This operation is not supported for a relative URI."); +		} +#else +		private void EnsureAbsoluteUri () +		{ +		} +#endif +	} +} diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriFormatException.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriFormatException.cs new file mode 100644 index 0000000..8506b93 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriFormatException.cs @@ -0,0 +1,72 @@ +// +// System.UriFormatException.cs +// +// Author: +//   Scott Sanders (scott@stonecobra.com) +//   Duncan Mak (duncan@ximian.com) +// +// (C) 2001 Scott Sanders +// (C) 2002 Ximian, Inc. +// Copyright (C) 2005, 2008 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; +using System.Globalization; +using System.Runtime.Serialization; + +namespace MonoForks.System { + +	[Serializable] +	public class UriFormatException : FormatException, ISerializable +	{ + +		// Constructors +		public UriFormatException () +			: base (Locale.GetText("Invalid URI format")) +		{ +		} + +		public UriFormatException (string message) +			: base (message) +		{ +		} +#if NET_2_1 +		public UriFormatException (string message, Exception exception) +			: base (message, exception) +		{ +		} +#endif +		protected UriFormatException (SerializationInfo info, StreamingContext context) +			: base (info, context) +		{			 +		} + +		// Methods + +		// This effectively kills the LinkDemand from Exception.GetObjectData (if someone +		// use the ISerializable interface to serialize the object). See unit tests. +		void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) +		{ +			base.GetObjectData (info, context); +		} +	} +} diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriHostNameType.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriHostNameType.cs new file mode 100644 index 0000000..0ae13ef --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriHostNameType.cs @@ -0,0 +1,62 @@ +// UriHostNameType.cs +// +// This code was automatically generated from +// ECMA CLI XML Library Specification. +// Generator: libgen.xsl [1.0; (C) Sergey Chaban (serge@wildwestsoftware.com)] +// Created: Wed, 5 Sep 2001 06:33:14 UTC +// Source file: AllTypes.xml +// URL: http://msdn.microsoft.com/net/ecma/AllTypes.xml +// +// (C) 2001 Ximian, Inc.  http://www.ximian.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. +// + + +namespace MonoForks.System { + + +	/// <summary> +	/// </summary> +	public enum UriHostNameType { + +		/// <summary> +		/// </summary> +		Unknown = 0, +		 +		/// <summary> +		/// </summary> +		Basic = 1, + +		/// <summary> +		/// </summary> +		Dns = 2, + +		/// <summary> +		/// </summary> +		IPv4 = 3, + +		/// <summary> +		/// </summary> +		IPv6 = 4, +	} // UriHostNameType + +} // System diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriKind.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriKind.cs new file mode 100644 index 0000000..2437c56 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriKind.cs @@ -0,0 +1,42 @@ +// +// System.UriKind enumeration +// +// 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. +// + +namespace MonoForks.System { + +#if NET_2_0 +	public +#else +	internal +#endif +	enum UriKind { + +		RelativeOrAbsolute, +		Absolute, +		Relative, +	} +} diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriPartial.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriPartial.cs new file mode 100644 index 0000000..f85bc23 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriPartial.cs @@ -0,0 +1,44 @@ +// UriPartial.cs +// +// This code was automatically generated from +// ECMA CLI XML Library Specification. +// Generator: libgen.xsl [1.0; (C) Sergey Chaban (serge@wildwestsoftware.com)] +// Created: Wed, 5 Sep 2001 06:33:21 UTC +// Source file: AllTypes.xml +// URL: http://msdn.microsoft.com/net/ecma/AllTypes.xml +// +// (C) 2001 Ximian, Inc.  http://www.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. +// + +namespace MonoForks.System { + +	public enum UriPartial { + +		Scheme = 0, +		Authority = 1, +		Path = 2, +#if NET_2_0 +		Query +#endif +	} +} diff --git a/Runtime/Managed/CrossDomainPolicyParser/Properties/AssemblyInfo.cs b/Runtime/Managed/CrossDomainPolicyParser/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..85cb64b --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following  +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CrossDomainPolicyParser")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("CrossDomainPolicyParser")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible  +// to COM components.  If you need to access a type in this assembly from  +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f17522fc-5e6b-43ea-baf9-8af61d9740f0")] + +// Version information for an assembly consists of the following four values: +// +//      Major Version +//      Minor Version  +//      Build Number +//      Revision +// +// You can specify all the values or you can default the Build and Revision Numbers  +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: InternalsVisibleTo("CrossDomainPolicyParserTests")] diff --git a/Runtime/Managed/CrossDomainPolicyParser/Tests/CrossDomainPolicyParserTests.csproj b/Runtime/Managed/CrossDomainPolicyParser/Tests/CrossDomainPolicyParserTests.csproj new file mode 100644 index 0000000..0e27635 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Tests/CrossDomainPolicyParserTests.csproj @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +  <PropertyGroup> +    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> +    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> +    <ProductVersion>9.0.30729</ProductVersion> +    <SchemaVersion>2.0</SchemaVersion> +    <ProjectGuid>{5C04DB87-3B34-43B4-B164-86CE4361DC7B}</ProjectGuid> +    <OutputType>Library</OutputType> +    <AppDesignerFolder>Properties</AppDesignerFolder> +    <RootNamespace>CrossDomainPolicyParserTests</RootNamespace> +    <AssemblyName>CrossDomainPolicyParserTests</AssemblyName> +    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> +    <FileAlignment>512</FileAlignment> +  </PropertyGroup> +  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> +    <DebugSymbols>true</DebugSymbols> +    <DebugType>full</DebugType> +    <Optimize>false</Optimize> +    <OutputPath>bin\Debug\</OutputPath> +    <DefineConstants>DEBUG;TRACE</DefineConstants> +    <ErrorReport>prompt</ErrorReport> +    <WarningLevel>4</WarningLevel> +  </PropertyGroup> +  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> +    <DebugType>pdbonly</DebugType> +    <Optimize>true</Optimize> +    <OutputPath>bin\Release\</OutputPath> +    <DefineConstants>TRACE</DefineConstants> +    <ErrorReport>prompt</ErrorReport> +    <WarningLevel>4</WarningLevel> +  </PropertyGroup> +  <ItemGroup> +    <Reference Include="System" /> +    <Reference Include="System.Core"> +      <RequiredTargetFramework>3.5</RequiredTargetFramework> +    </Reference> +    <Reference Include="System.Xml.Linq"> +      <RequiredTargetFramework>3.5</RequiredTargetFramework> +    </Reference> +    <Reference Include="System.Data.DataSetExtensions"> +      <RequiredTargetFramework>3.5</RequiredTargetFramework> +    </Reference> +    <Reference Include="System.Data" /> +    <Reference Include="System.Xml" /> +    <Reference Include="nunit.framework, Version=2.5.3.9345, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77"> +      <SpecificVersion>False</SpecificVersion> +      <HintPath>..\..\..\..\Tools\NUnit\bin\nunit.framework.dll</HintPath> +    </Reference> +  </ItemGroup> +  <ItemGroup> +    <Compile Include="FlashPolicyParserSocketTests.cs" /> +    <Compile Include="FlashPolicyParserTests.cs" /> +    <Compile Include="Properties\AssemblyInfo.cs" /> +    <Compile Include="UriToolsTests.cs" /> +  </ItemGroup> +  <ItemGroup> +    <ProjectReference Include="..\CrossDomainPolicyParser.csproj"> +      <Project>{31C2F345-D887-49DD-A1F6-741CABD74A42}</Project> +      <Name>CrossDomainPolicyParser</Name> +    </ProjectReference> +  </ItemGroup> +  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> +  <!-- To modify your build process, add your task inside one of the targets below and uncomment it.  +       Other similar extension points exist, see Microsoft.Common.targets. +  <Target Name="BeforeBuild"> +  </Target> +  <Target Name="AfterBuild"> +  </Target> +  --> +</Project>
\ No newline at end of file diff --git a/Runtime/Managed/CrossDomainPolicyParser/Tests/FlashPolicyParserSocketTests.cs b/Runtime/Managed/CrossDomainPolicyParser/Tests/FlashPolicyParserSocketTests.cs new file mode 100644 index 0000000..8e0f62b --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Tests/FlashPolicyParserSocketTests.cs @@ -0,0 +1,124 @@ +using System.IO; +using System.Text; +using MonoForks.System.Windows.Browser.Net; +using NUnit.Framework; + +namespace CrossDomainPolicyParserTests +{ +	[TestFixture] +	public class FlashPolicyParserSocketTests +	{ +		[Test] +		public void AllDomains_AllPorts_IsAllowed() +		{ +			string policy = @"<?xml version='1.0'?> +<cross-domain-policy> +	<allow-access-from domain=""*"" to-ports=""*"" /> +</cross-domain-policy>"; +			Assert.IsTrue(RequestAllowed(policy, 123)); +		} + +		[Test] +		public void AllDomains_AllPorts_Trailing0_IsAllowed() +		{ +			string policy = @"<?xml version='1.0'?> +<cross-domain-policy> +	<allow-access-from domain=""*"" to-ports=""*"" /> +</cross-domain-policy>" + "\0"; +			Assert.IsTrue(RequestAllowed(policy, 123)); +		} +		 +		[Test] +		public void AllDomains_UsingSpecificPorts_IsAllowed() +		{ +			string policy = @"<?xml version='1.0'?> +<cross-domain-policy> +	<allow-access-from domain=""*"" to-ports=""1010,1020"" /> +</cross-domain-policy>"; +			Assert.IsTrue(RequestAllowed(policy, 1020)); +		} +		 +		[Test] +		public void AllDomains_OutsideSpecificPorts_IsDisAllowed() +		{ +			string policy = @"<?xml version='1.0'?> +<cross-domain-policy> +	<allow-access-from domain=""*"" to-ports=""1010,1030"" /> +</cross-domain-policy>"; +			Assert.IsFalse(RequestAllowed(policy, 1020)); +		} +		 +		[Test] +		public void AllDomains_OutsidePortRange_IsDisAllowed() +		{ +			string policy = @"<?xml version='1.0'?> +<cross-domain-policy> +	<allow-access-from domain=""*"" to-ports=""1030-1040"" /> +</cross-domain-policy>"; +			Assert.IsFalse(RequestAllowed(policy, 1020)); +		}		 +		 +		[Test] +		public void AllDomains_InsidePortRange_IsAllowed() +		{ +			string policy = @"<?xml version='1.0'?> +<cross-domain-policy> +	<allow-access-from domain=""*"" to-ports=""1030-1040"" /> +</cross-domain-policy>"; +			Assert.IsTrue(RequestAllowed(policy, 1035)); +		} + +		[Test] +		public void PolicyReceivedFromHigherThan1024_DisallowsAccessToBelow1024Ports() +		{ +			string policy = @"<?xml version='1.0'?> +<cross-domain-policy> +	<allow-access-from domain=""*"" to-ports=""1000-1040"" /> +</cross-domain-policy>"; +			Assert.IsFalse(RequestAllowed(policy, 1010, 1300)); +		} + +		[Test] +		public void PolicyReceivedFromHigherThan1024_AllowsAccessToAbove1024Ports() +		{ +			string policy = @"<?xml version='1.0'?> +<cross-domain-policy> +	<allow-access-from domain=""*"" to-ports=""1000-1040"" /> +</cross-domain-policy>"; +			Assert.IsTrue(RequestAllowed(policy, 1035, 1300)); +		} + +		[Test] +		public void PolicyReceivedFromLowerThan1024_AllowsAccessToBelow1024Ports() +		{ +			string policy = @"<?xml version='1.0'?> +<cross-domain-policy> +	<allow-access-from domain=""*"" to-ports=""1000-1040"" /> +</cross-domain-policy>"; +			Assert.IsTrue(RequestAllowed(policy, 1010, 1000)); +		} +		[Test] +		public void PolicyReceivedFromLowerThan1024_AllowsAccessToAbove1024Ports() +		{ +			string policy = @"<?xml version='1.0'?> +<cross-domain-policy> +	<allow-access-from domain=""*"" to-ports=""1000-1040"" /> +</cross-domain-policy>"; +			Assert.IsTrue(RequestAllowed(policy, 1030, 1000)); +		} + +		private bool RequestAllowed(string xdomain, int port) +		{ +			return RequestAllowed(xdomain, port, 843); +		} + +		private bool RequestAllowed(string xdomain, int port, int policyport) +		{ +			var ms = new MemoryStream(Encoding.UTF8.GetBytes(xdomain)); +			var policy = FlashCrossDomainPolicy.FromStream(ms); +			policy.PolicyPort = policyport; +			return policy.IsSocketConnectionAllowed(port); +		}	 +		 +	} +}
\ No newline at end of file diff --git a/Runtime/Managed/CrossDomainPolicyParser/Tests/FlashPolicyParserTests.cs b/Runtime/Managed/CrossDomainPolicyParser/Tests/FlashPolicyParserTests.cs new file mode 100644 index 0000000..976ba92 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Tests/FlashPolicyParserTests.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using MonoForks.Mono.Xml; +using MonoForks.System.Net; +using NUnit.Framework; +using MonoForks.System.Windows.Browser.Net; +using UnityEngine; +using Uri = MonoForks.System.Uri; + +namespace CrossDomainPolicyParserTests +{ +	[TestFixture] +	public class FlashPolicyParserTests +	{ +		static string XDomainGlobal = +@"<?xml version=""1.0""?> +<!DOCTYPE cross-domain-policy SYSTEM ""http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd""> +<cross-domain-policy> +	<allow-access-from domain=""*"" /> +</cross-domain-policy>"; + +		string http_hosted = "http://www.host.com/coolgame.unity3d"; +		string https_hosted = "https://secure.host.net/coolgame.unity3d"; +		string file_hosted = "file:///coolgame.unity3"; + +		[Test] +		public void GlobalXDomainAcceptsRequestOnSameDomain() +		{ +			string requesturl = "http://www.mach8.nl/index.html"; + +			Assert.IsTrue(RequestAllowed(XDomainGlobal, requesturl, http_hosted)); +		} +		[Test] +		public void GlobalXDomainAcceptsRequestOnSubDomain() +		{ +			string requesturl = "http://subdomain.mach8.nl/index.html"; + +			Assert.IsTrue(RequestAllowed(XDomainGlobal, requesturl, http_hosted)); +		} + +		[Test] +		public void GlobalXDomainAllowsSecureRequestWhenHostedNonSecure() +		{ +			string requesturl = "https://www.mach8.nl/index.html"; + +			Assert.IsTrue(RequestAllowed(XDomainGlobal, requesturl, http_hosted)); +		} +		[Test] +		public void GlobalXDomainAcceptsSecureRequestWhenHostedSecure() +		{ +			string requesturl = "https://www.mach8.nl/index.html"; +			 +			Assert.IsTrue(RequestAllowed(XDomainGlobal, requesturl, https_hosted)); +		} +		[Test] +		public void GlobalXDomainDeniesNonSecureRequestWhenHostedSecure() +		{ +			string requesturl = "http://www.mach8.nl/index.html"; +			Assert.IsFalse(RequestAllowed(XDomainGlobal, requesturl, https_hosted)); +		} + +		[Test] +		public void AllDomain_Secure() +		{ +			string policy = @"<?xml version=""1.0""?> +<!DOCTYPE cross-domain-policy SYSTEM ""http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd""> +<cross-domain-policy> +	<allow-access-from domain=""*"" secure=""true""/> +</cross-domain-policy>"; + +			Assert.IsTrue(RequestAllowed(policy, "http://www.host.com", http_hosted)); +		} + +		[Test] +		public void WhenRequestURLMatchesWildCardAccessIsAllowed() +		{ +			string policy = @"<?xml version=""1.0""?> +<cross-domain-policy> +	<allow-access-from domain=""*.mydomain.nl"" /> +</cross-domain-policy>"; + +			Assert.IsTrue(RequestAllowed(policy, "http://subdomain.mydomain.nl", http_hosted)); +		} + +		[Test] +		public void WhenRequestURLDoesNotMatchWildCardAccessIsDisallowed() +		{ +			string policy = @"<?xml version=""1.0""?> +<cross-domain-policy> +	<allow-access-from domain=""*.mydomain.nl"" /> +</cross-domain-policy>"; + +			Assert.IsFalse(RequestAllowed(policy, "http://subdomain.myotherdomain.nl", http_hosted)); +		} + + +		[Test] +		public void AllDomains_NoDTD() +		{ +			string policy = @"<?xml version='1.0'?><cross-domain-policy><allow-access-from domain='*'/></cross-domain-policy>"; + +			Assert.IsTrue(RequestAllowed(policy, "http://www.host.com", http_hosted)); +		} + +		[Test] +		public void AllDomains_NoXmlHeader() +		{ +			string policy = @"<cross-domain-policy>  +	<allow-access-from domain=""*"" to-ports=""*""/>  +</cross-domain-policy> "; +			Assert.IsTrue(RequestAllowed(policy, "http://www.host.com", http_hosted)); +		} + +		[Test] +		public void AllDomains_PermittedCrossDomainPolicies_All() +		{ +			// 'all' is the default value +			// http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html#site-control-permitted-cross-domain-policies +			string policy = @"<?xml version='1.0'?> +<!DOCTYPE cross-domain-policy SYSTEM 'http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd'> +<cross-domain-policy> +	<site-control permitted-cross-domain-policies='all' /> +	<allow-access-from domain='*' /> +</cross-domain-policy>"; + +			Assert.IsTrue(RequestAllowed(policy, "http://www.host.com", http_hosted)); +		} + +		[Test] +		public void AllDomains_PermittedCrossDomainPolicies_MasterOnly() +		{ +			string policy = @"<?xml version='1.0'?> +<!DOCTYPE cross-domain-policy SYSTEM 'http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd'> +<cross-domain-policy> +	<site-control permitted-cross-domain-policies='master-only' /> +	<allow-access-from domain='*' /> +</cross-domain-policy>"; + +			Assert.IsTrue(RequestAllowed(policy, "http://www.host.com", http_hosted)); +		} + +		[Test] +		public void AllDomains_PermittedCrossDomainPolicies_None() +		{ +			string policy = @"<?xml version='1.0'?> +<!DOCTYPE cross-domain-policy SYSTEM 'http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd'> +<cross-domain-policy> +	<site-control permitted-cross-domain-policies='none' /> +	<allow-access-from domain='*' /> +</cross-domain-policy>"; +			Assert.IsFalse(RequestAllowed(policy, "http://www.host.com", http_hosted)); +		} + +		[Test] +		public void AllDomains_PermittedCrossDomainPolicies_ByContentType() +		{ +			string policy = @"<?xml version='1.0'?> +<!DOCTYPE cross-domain-policy SYSTEM 'http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd'> +<cross-domain-policy> +	<site-control permitted-cross-domain-policies='by-content-type' /> +	<allow-access-from domain='*' /> +</cross-domain-policy>"; +			Assert.IsFalse(RequestAllowed(policy, "http://www.host.com", http_hosted)); +		} + +		[Test] +		public void AllDomains_PermittedCrossDomainPolicies_ByFtpFilename() +		{ +			string policy = @"<?xml version='1.0'?> +<!DOCTYPE cross-domain-policy SYSTEM 'http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd'> +<cross-domain-policy> +	<site-control permitted-cross-domain-policies='by-ftp-filename' /> +	<allow-access-from domain='*' /> +</cross-domain-policy>"; +			Assert.IsTrue(RequestAllowed(policy, "http://www.host.com", http_hosted)); +		} + +		[Test] +		[ExpectedException(typeof(MiniParser.XMLError))] +		public void IllformedPolicyIsRejected() +		{ +			FlashCrossDomainPolicyFromString("bogus", "http://www.host.com"); +		} + +		[Test] +		[ExpectedException(typeof(ArgumentException))] +		public void EmptyPolicyStringIsRejected() +		{ +			FlashCrossDomainPolicyFromString("", "http://www.host.com"); +		} + +		private bool RequestAllowed(string xdomain, string requesturl, string hosturl) +		{ +			FlashCrossDomainPolicy policy = FlashCrossDomainPolicyFromString(xdomain, hosturl); +			var wr = new WebRequest(new Uri(requesturl), new Dictionary<string, string>()); +			return policy.IsAllowed(wr); +		} + +		private FlashCrossDomainPolicy FlashCrossDomainPolicyFromString(string xdomain, string hosturl) +		{ +			UnityCrossDomainHelper.SetWebSecurityHostUriDelegate(() => hosturl); + +			var ms = new MemoryStream(Encoding.UTF8.GetBytes(xdomain)); +			return FlashCrossDomainPolicy.FromStream(ms); +		} +	} +} diff --git a/Runtime/Managed/CrossDomainPolicyParser/Tests/Properties/AssemblyInfo.cs b/Runtime/Managed/CrossDomainPolicyParser/Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5fd4c99 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following  +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CrossDomainPolicyParserTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("CrossDomainPolicyParserTests")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible  +// to COM components.  If you need to access a type in this assembly from  +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c40d4596-1c43-4f37-b7fa-545a543577f3")] + +// Version information for an assembly consists of the following four values: +// +//      Major Version +//      Minor Version  +//      Build Number +//      Revision +// +// You can specify all the values or you can default the Build and Revision Numbers  +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Runtime/Managed/CrossDomainPolicyParser/Tests/UriToolsTests.cs b/Runtime/Managed/CrossDomainPolicyParser/Tests/UriToolsTests.cs new file mode 100644 index 0000000..7a29b00 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/Tests/UriToolsTests.cs @@ -0,0 +1,23 @@ +using CrossDomainPolicyParser; +using MonoForks.System; +using NUnit.Framework; + +namespace CrossDomainPolicyParserTests +{ +	[TestFixture] +	public class UriToolsTests +	{ +		[Test] +		public void MakeUriWorksForRelativeUri() +		{ +			Uri uri = UriTools.MakeUri("http://mydomain.com/mygame.unity3d", "test.png"); +			Assert.AreEqual("mydomain.com",uri.Host); +		} +		[Test] +		public void MakeUriWorksForAbsoluteUri() +		{ +			Uri uri = UriTools.MakeUri("http://mydomain.com/mygame.unity3d", "http://www.google.com/test.png"); +			Assert.AreEqual("www.google.com", uri.Host); +		} +	} +} diff --git a/Runtime/Managed/CrossDomainPolicyParser/UnityCrossDomainHelper.cs b/Runtime/Managed/CrossDomainPolicyParser/UnityCrossDomainHelper.cs new file mode 100644 index 0000000..e43ba59 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/UnityCrossDomainHelper.cs @@ -0,0 +1,213 @@ +using System; +using System.Reflection; +using System.Security; +using System.Text; +using CrossDomainPolicyParser; +using System.IO; +using System.Collections.Generic; +using MonoForks.System.Windows.Browser.Net; + +internal class Log +{ +	public delegate void LogDelegate(string msg); +	 +	static LogDelegate logger; +	 +	static public void SetLog(LogDelegate ld) +	{ +		logger = ld; +	} +	 +	static public void Msg(string msg) +	{ +		if (logger != null) logger(msg); +	} +} + +namespace UnityEngine +{ +    public class UnityCrossDomainHelper +    { +        public enum SecurityPolicy +        { +            DontKnowYet = 0, +            AllowAccess = 1, +            DenyAccess = 2 +        } + +    	public delegate string GetWebSecurityHostUriDelegate(); +    	private static GetWebSecurityHostUriDelegate getWebSecurityHostUriDelegate = DefaultGetWebSecurityHostUri; +    	static WWWPolicyProvider wwwPolicyProvider = new WWWPolicyProvider(); + +		static public string GetWebSecurityHostUri() +		{ +			return getWebSecurityHostUriDelegate();	 +		} + +		static internal void SetWebSecurityHostUriDelegate(GetWebSecurityHostUriDelegate d) +		{ +			getWebSecurityHostUriDelegate = d;	 +		} + +		static string DefaultGetWebSecurityHostUri() +		{ +			return Application.webSecurityHostUrl; +		} + +        public static void ClearCache() +        { +            wwwPolicyProvider.ClearCache(); +            CrossDomainPolicyManager.ClearCache(); +        } + +		interface IPolicyProvider +		{ +			Stream GetPolicy(string url); +		} + +		class WWWPolicyProvider : IPolicyProvider +		{ +			Dictionary<string,WWW> policyDownloads = new Dictionary<string, WWW>(); + +			public void ClearCache() +			{ +				policyDownloads.Clear(); +			} + +			public Stream GetPolicy(string policyurl) +			{ +				WWW downloadInProgress = null; +				policyDownloads.TryGetValue(policyurl, out downloadInProgress); + +				if (downloadInProgress != null) +				{ +					if (!downloadInProgress.isDone) return null; + +					bool statuscodeOK = downloadInProgress.error == null; +					 +					if (!statuscodeOK) throw new InvalidOperationException("Unable to download policy"); +					if (statuscodeOK) +					{ +						Log.Msg("Download had OK statuscode"); +						Log.Msg("Received the following crossdomain.xml"); +						Log.Msg("----------"); +						Log.Msg(downloadInProgress.text); +						Log.Msg("----------"); +						return new MemoryStream(downloadInProgress.bytes); +					} + +				} + +				//okay, we hadn't started downloading the policy, lets start the policy download. +				var www = new WWW(policyurl); +				policyDownloads.Add(policyurl, www); +				return null; +			} +		} + +		class WebRequestPolicyProvider : IPolicyProvider +		{ +			private MethodInfo methodinfo; + +			public WebRequestPolicyProvider(MethodInfo mi) +			{ +				methodinfo = mi; +			} + +			[System.Security.SecuritySafeCritical] +			public Stream GetPolicy(string policy_url) +			{ +				var proxy = System.Environment.GetEnvironmentVariable("UNITY_PROXYSERVER"); +				if (string.IsNullOrEmpty(proxy)) +					proxy = null; +				object result = methodinfo.Invoke(null, new object[] {policy_url, proxy}); +				return (Stream) result; +			} +		} + +		public static SecurityPolicy GetSecurityPolicy(string requesturi_string) +		{ +			return GetSecurityPolicy(requesturi_string, wwwPolicyProvider); +		} + +		public static bool GetSecurityPolicyForDotNetWebRequest(string requesturi_string, MethodInfo policyProvidingMethod) +		{ +			var provider = new WebRequestPolicyProvider(policyProvidingMethod); + +			return GetSecurityPolicy(requesturi_string, provider) == SecurityPolicy.AllowAccess; +		} + +		static SecurityPolicy GetSecurityPolicy(string requesturi_string, IPolicyProvider policyProvider) +        { +			var requesturi = UriTools.MakeUri(Application.webSecurityHostUrl, requesturi_string); +			if (requesturi.Scheme=="file") +			{ +				//Editor-In-WebMode is allowed to read files from file:// +				if (Application.isEditor) return SecurityPolicy.AllowAccess; + +				//Webplayer itself is allowed to read files from file:// as long as it is hosted on file:// itself as well. +				var hostedat = new MonoForks.System.Uri(Application.webSecurityHostUrl); +				if (Application.isWebPlayer && hostedat.Scheme == "file") return SecurityPolicy.AllowAccess; + +				//other scenarios of accessing file:// are not allowed +				return SecurityPolicy.DenyAccess; +			} +			 +            //todo: force absolute +            ICrossDomainPolicy policy = CrossDomainPolicyManager.GetCachedWebPolicy(requesturi); +			if (policy != null) +            { +                var request = new MonoForks.System.Net.WebRequest(requesturi, new Dictionary<string, string>()); +                SecurityPolicy allowed = policy.IsAllowed(request) ? SecurityPolicy.AllowAccess : SecurityPolicy.DenyAccess; +                return allowed; +			} +        	 +            if (ShouldEnableLogging()) +    			Log.SetLog(Console.WriteLine); +        	 +        	Log.Msg("Determining crossdomain.xml location for request: " + requesturi); +			var policyURI = CrossDomainPolicyManager.GetFlashPolicyUri(requesturi); + +        	Stream s; +			try +        	{ +				s = policyProvider.GetPolicy(policyURI.ToString()); +				if (s == null) return SecurityPolicy.DontKnowYet; +				CrossDomainPolicyManager.BuildFlashPolicy(true, policyURI, s, new Dictionary<string, string>()); +			} catch (InvalidOperationException) +			{ +				return SecurityPolicy.DenyAccess;				 +			} +			catch (MonoForks.Mono.Xml.MiniParser.XMLError xe) +			{ +				Debug.Log (string.Format ("Error reading crossdomain policy: {0}", xe.Message)); +				return SecurityPolicy.DenyAccess;				 +			} +			return GetSecurityPolicy(requesturi_string, policyProvider); +        } + +		[SecuritySafeCritical] +    	private static bool ShouldEnableLogging() +    	{ +    		return Environment.GetEnvironmentVariable("ENABLE_CROSSDOMAIN_LOGGING")=="1"; +    	} + +    	static public bool CheckSocketEndPoint(string connecting_to_ip, int port) +		{ +			Log.Msg("CheckSocketEndpoint called for "+connecting_to_ip+" with port: "+port); + +			if (!Application.webSecurityEnabled) return true; +			 +			bool result = CrossDomainPolicyManager.CheckSocketEndPoint(connecting_to_ip,port); +			Log.Msg("CheckSocketENdpoint returns :"+result); +			return result; +		} +		static public bool PrefetchSocketPolicy(string ip, int policyport, int timeout) +		{ +			if (!Application.webSecurityEnabled) return false; +            var policy = CrossDomainPolicyManager.FlashCrossDomainPolicyFor(ip, policyport, timeout); +            return policy != FlashCrossDomainPolicy.DenyPolicy; +		} + +    } +} diff --git a/Runtime/Managed/CrossDomainPolicyParser/UriTools.cs b/Runtime/Managed/CrossDomainPolicyParser/UriTools.cs new file mode 100644 index 0000000..6fafc86 --- /dev/null +++ b/Runtime/Managed/CrossDomainPolicyParser/UriTools.cs @@ -0,0 +1,20 @@ +using System; +using Uri = MonoForks.System.Uri; + +namespace CrossDomainPolicyParser +{ +	class UriTools +	{ +		public static Uri MakeUri(string gameurl, string url) +		{ +			if ((!url.ToLower().StartsWith("http://")) && (!url.ToLower().StartsWith("https://")) && (!url.ToLower().StartsWith("file://"))) +				url = GetBaseUrl(gameurl) + "/" +url; +			Log.Msg("About to parse url: " + url); +			return new Uri(url); +		} +		static string GetBaseUrl(string url) +		{ +			return url.Substring(0, url.LastIndexOf('/')); +		} +	} +}  | 
