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('/')); + } + } +} |