PackUriHelper.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Base / System / IO / Packaging / PackUriHelper.cs / 2 / PackUriHelper.cs

                            //------------------------------------------------------------------------------ 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//  This is a helper class for pack:// Uris. This is a part of the 
//  Metro Packaging Layer
// 
// History:
//  07/07/2004: [....]: Initial creation.
//  11/15/2004: [....]: Adding the Compare and Normalize methods
//----------------------------------------------------------------------------- 

// Allow use of presharp warning numbers [6506] unknown to the compiler 
#pragma warning disable 1634, 1691 

using System; 
using System.IO;                        // for Path class
using System.Security;
using System.Security.Permissions;      // for Infrastructure permission
using System.Diagnostics; 
using System.Windows;                   // For Exception strings - SRID
using System.Collections.Generic;       // For IEqualityComparer<> 
 
namespace System.IO.Packaging
{ 
    /// 
    /// This class has the utility methods for composing and parsing an Uri of pack:// scheme
    /// 
    public static class PackUriHelper 
    {
        //----------------------------------------------------- 
        // 
        //  Public Constructors
        // 
        //-----------------------------------------------------
        // None
        //------------------------------------------------------
        // 
        //  Public Properties
        // 
        //----------------------------------------------------- 
        // None
        //------------------------------------------------------ 
        //
        //  Public Methods
        //
        //------------------------------------------------------ 

        #region Public Methods 
 
        /// 
        /// This method is used to create a valid pack Uri 
        /// 
        /// This is the uri that points to the entire package.
        /// This parameter should be an absolute Uri. This parameter cannot be null or empty
        /// This method will create a valid pack uri that references the entire package 
        /// A Uri with the "pack://" scheme
        /// If packageUri parameter is null 
        /// If packageUri parameter is not an absolute Uri 
        public static Uri Create(Uri packageUri)
        { 
            return Create(packageUri, null, null);
        }

        ///  
        /// This method is used to create a valid pack Uri
        ///  
        /// This is the uri that points to the entire package. 
        /// This parameter should be an absolute Uri. This parameter cannot be null or empty 
        /// This is the uri that points to the part within the package 
        /// This parameter should be a relative Uri.
        /// This parameter can be null in which case we will create a valid pack uri
        /// that references the entire package
        /// A Uri with the "pack://" scheme 
         /// If packageUri parameter is null
        /// If packageUri parameter is not an absolute Uri 
        /// If partUri parameter does not conform to the valid partUri syntax 
        public static Uri Create(Uri packageUri, Uri partUri)
        { 
            return Create(packageUri, partUri, null);
        }

        ///  
        /// This method is used to create a valid pack Uri
        ///  
        /// This is the uri that points to the entire package. 
        /// This parameter should be an absolute Uri. This parameter cannot be null or empty 
        /// This is the uri that points to the part within the package 
        /// This parameter should be a relative Uri.
        /// This parameter can be null in which case we will create a valid pack uri
        /// that references the entire package
        /// Fragment for the resulting Pack URI. This parameter can be null 
        /// The fragment string must start with a "#"
        /// A Uri with the "pack://" scheme 
        /// If packageUri parameter is null 
        /// If packageUri parameter is not an absolute Uri
        /// If partUri parameter does not conform to the valid partUri syntax 
        /// If fragment parameter is empty or does not start with a "#"
        public static Uri Create(Uri packageUri, Uri partUri, string fragment)
        {
            // Step 1 - Validate input parameters 
            packageUri = ValidatePackageUri(packageUri);
 
            if (partUri != null) 
                partUri = ValidatePartUri(partUri);
 
            if (fragment != null)
            {
                if (fragment == String.Empty || fragment[0] != '#')
                    throw new ArgumentException(SR.Get(SRID.FragmentMustStartWithHash)); 
            }
 
            // Step 2 - Remove fragment identifier from the package URI, if it is present 
            // Since '#" is an excluded character in Uri syntax, it can only occur as the
            // fragment identifier, in all other places it should be escaped. 
            // Hence we can safely use IndexOf to find the begining of the fragment.
            string absolutePackageUri = packageUri.GetComponents(UriComponents.AbsoluteUri, UriFormat.UriEscaped);

            //PRESHARP:Warning 6506 Parameter to this public method must be validated:  A null-dereference can occur here. 
            //We know that the Fragment property will always be a string and never will return null, String.Empty if the fragment is empty.
#pragma warning disable 6506 
            if (packageUri.Fragment.Length != 0) 
#pragma warning restore 6506
            { 
                absolutePackageUri = absolutePackageUri.Substring(0, absolutePackageUri.IndexOf('#'));
            }

            // Step 3 - Escape: "%", "?", "@", "#" and "," in the package URI 
            absolutePackageUri = EscapeSpecialCharacters(absolutePackageUri);
 
            // Step 4 - Replace all '/' with ',' in the resulting string 
            absolutePackageUri = absolutePackageUri.Replace('/', ',');
 
            // Step 5 - Append pack:// at the begining and a '/' at the end of the pack uri obtained so far
            absolutePackageUri = String.Concat(PackUriHelper.UriSchemePack, Uri.SchemeDelimiter, absolutePackageUri);

            Uri packUri = new Uri(absolutePackageUri); 

            // Step 6 - Append the part Uri if present. 
            if (partUri != null) 
                packUri = new Uri(packUri, partUri);
 
            // Step 7 - Append fragment if present
            if (fragment != null)
                packUri = new Uri(String.Concat(packUri.GetComponents(UriComponents.AbsoluteUri, UriFormat.UriEscaped), fragment));
 
            // We want to ensure that internal content of resulting Uri has canonical form
            // i.e.  result.OrignalString would appear as perfectly formatted Uri string 
            // so we roundtrip the result. 

            return new Uri(packUri.GetComponents(UriComponents.AbsoluteUri, UriFormat.UriEscaped)); 
        }

        /// 
        /// This method parses the pack uri and returns the inner 
        /// Uri that points to the package as a whole.
        ///  
        /// Uri which has pack:// scheme 
        /// Returns the inner uri that points to the entire package
        /// If packUri parameter is null 
        /// If packUri parameter is not an absolute Uri
        /// If packUri parameter does not have "pack://" scheme
        /// If inner packageUri extracted from the packUri has a fragment component
        public static Uri GetPackageUri ( Uri packUri ) 
        {
            Uri packageUri; 
            Uri partUri; 

            //Parameter Validation is done in the follwoing method 
            ValidateAndGetPackUriComponents(packUri, out packageUri, out partUri);

            return packageUri;
        } 

        ///  
        /// This method parses the pack uri and returns the absolute 
        /// path of the URI. This corresponds to the part within the
        /// package. This corresponds to the absolute path component in 
        /// the Uri. If there is no part component present, this method
        /// returns a null
        /// 
        /// Returns a relative Uri that represents the 
        /// part within the package. If the pack Uri points to the entire
        /// package then we return a null 
        /// Returns a relative URI with an absolute path that points to a part within a package 
        /// If packUri parameter is null
        /// If packUri parameter is not an absolute Uri 
        /// If packUri parameter does not have "pack://" scheme
        /// If partUri extracted from packUri does not conform to the valid partUri syntax
        public static Uri GetPartUri(Uri packUri)
        { 
            Uri packageUri;
            Uri partUri; 
 
            //Parameter Validation is done in the follwoing method
            ValidateAndGetPackUriComponents(packUri, out packageUri, out partUri); 

            return partUri;
        }
 
        /// 
        /// This method is used to create a valid part Uri given a relative URI 
        /// Makes sure that the URI - 
        /// 1. Relative
        /// 2. Begins with '/' 
        /// 3. Does not begin with two "//"
        /// 4. Does not end with "/"
        /// 5. Does not have a fragment component
        /// 6. Does the correct escaping 
        /// 7. And is refined correctly
        ///  
        /// The relative uri that represents the part within a package 
        /// Returns a relative URI with an absolute path that points to a part within a package
        /// If partUri parameter is null 
        /// If partUri parameter is an absolute Uri
        /// If partUri parameter is empty
        /// If partUri parameter ends with "/"
        /// If partUri parameter starts with two "/" 
        /// If partUri parameter has a fragment
        public static Uri CreatePartUri(Uri partUri) 
        { 
            if (partUri == null)
                throw new ArgumentNullException("partUri"); 

            ThrowIfAbsoluteUri(partUri);

            string serializedPartUri = partUri.GetComponents(UriComponents.SerializationInfoString, UriFormat.SafeUnescaped); 
            ThrowIfPartNameStartsWithTwoSlashes(serializedPartUri);
            ThrowIfFragmentPresent(serializedPartUri); 
 
            //Create an absolute URI to get the refinement on the relative URI
            Uri resolvedUri = new Uri(_defaultUri, partUri); 

            string partName = GetStringForPartUriFromAnyUri(resolvedUri);

            if (partName == String.Empty) 
                throw new ArgumentException(SR.Get(SRID.PartUriIsEmpty));
 
            ThrowIfPartNameEndsWithSlash(partName); 

            return new ValidatedPartUri(partName); 
        }

        /// 
        /// This method is used to resolve part Uris 
        /// Constructs resolved relative URI from two relative URIs
        /// This method should be used in places where we have a 
        /// a target URI in the PackageRelationship and we want to get the 
        /// name of the part it targets with respect to the source part
        ///  
        /// This should be a valid partName.
        /// The only exception to this rule is an Uri of the form "/". This uri
        /// will only be used to resolve package level relationships. This Uri
        /// indicates that the relative Uri is being resolved against the root of the 
        /// package.
        /// This URI can be any relative URI 
        ///  
        /// If either sourcePartUri or targetUri parameter is null
        /// If either sourcePartUri or targetUri parameter is an absolute Uri 
        /// If sourcePartUri parameter does not conform to the valid partUri syntax
        public static Uri ResolvePartUri(Uri sourcePartUri, Uri targetUri)
        {
            if (sourcePartUri == null) 
                throw new ArgumentNullException("sourcePartUri");
 
            if (targetUri == null) 
                throw new ArgumentNullException("targetUri");
 
            ThrowIfAbsoluteUri(sourcePartUri);

            ThrowIfAbsoluteUri(targetUri);
 
            Uri resolvedUri;
 
            if (sourcePartUri == PackageRootUri) 
                resolvedUri = new Uri(_defaultUri, targetUri);
            else 
                resolvedUri = new Uri(new Uri(_defaultUri, ValidatePartUri(sourcePartUri)), targetUri);

            return new Uri(resolvedUri.AbsolutePath, UriKind.Relative);
        } 

        ///  
        /// This method returns the relative uri between two given parts 
        /// 
        ///  
        /// 
        /// The relative path between two parts
        /// If either the sourcePartUri or targetPartUri parameter is null
        /// If either sourcePartUri or targetPartUri parameter does not conform to the valid partUri syntax 
        public static Uri GetRelativeUri(Uri sourcePartUri, Uri targetPartUri)
        { 
            //although we do expect the subsequent ValidatePartUri call to throw in case of null 
            // as well, it dosn't have the right parameter namer for ValidatePartUri function
            if (sourcePartUri == null) 
                throw new ArgumentNullException("sourcePartUri");

            if (targetPartUri == null)
                throw new ArgumentNullException("targetPartUri"); 

            sourcePartUri = new Uri (_defaultUri, ValidatePartUri(sourcePartUri)); 
            targetPartUri = new Uri (_defaultUri, ValidatePartUri(targetPartUri)); 

            return sourcePartUri.MakeRelativeUri(targetPartUri); 
        }

        /// 
        /// Returns the normalized form of the part URI 
        /// 
        /// Part Uri 
        /// Normalized Part Uri 
        /// If partUri is null
        /// If partUri parameter does not conform to the valid partUri syntax 
        public static Uri GetNormalizedPartUri(Uri partUri)
        {
            if (partUri == null)
                throw new ArgumentNullException("partUri"); 

            if (!(partUri is ValidatedPartUri)) 
                partUri = ValidatePartUri(partUri); 
            return ((ValidatedPartUri)partUri).NormalizedPartUri;
        } 

        /// 
        /// This method compares two pack uris and returns an int to indicate the equivalence.
        ///  
        /// First Uri of pack:// scheme to be compared
        /// Second Uri of pack:// scheme to be compared 
        /// A 32-bit signed integer indicating the lexical relationship between the compared Uri components. 
        /// Value - Less than zero means firstUri is less than secondUri
        /// Value - Equal to zero means both the Uris are equal 
        /// Value - Greater than zero means firstUri is greater than secondUri 
        /// If either of the Uris are not absolute or if either of the Uris are not with pack:// scheme
        /// If firstPackUri or secondPackUri parameter is not an absolute Uri
        /// If firstPackUri or secondPackUri parameter does not have "pack://" scheme 
        public static int ComparePackUri(Uri firstPackUri, Uri secondPackUri)
        { 
            //If any of the operands are null then we simply call System.Uri compare to return the correct value 
            if (firstPackUri == null || secondPackUri == null)
                return CompareUsingSystemUri(firstPackUri, secondPackUri); 
            else
            {
                int compareResult;
 
                Uri firstPackageUri;
                Uri secondPackageUri; 
                Uri firstPartUri; 
                Uri secondPartUri;
 
                ValidateAndGetPackUriComponents(firstPackUri, out firstPackageUri, out firstPartUri);
                ValidateAndGetPackUriComponents(secondPackUri, out secondPackageUri, out secondPartUri);

 
                if (firstPackageUri.Scheme == PackUriHelper.UriSchemePack && secondPackageUri.Scheme == PackUriHelper.UriSchemePack)
                { 
                    compareResult = ComparePackUri(firstPackageUri, secondPackageUri); 
                }
                else 
                {
                    compareResult = CompareUsingSystemUri(firstPackageUri, secondPackageUri);
                }
 
                //Iff the PackageUri match do we compare the part uris.
                if (compareResult == 0) 
                { 
                    compareResult = ComparePartUri(firstPartUri, secondPartUri);
                } 

                return compareResult;
            }
        } 

        ///  
        /// This method compares two part uris and returns an int to indicate the equivalence 
        /// Null values are allowed
        ///  
        /// First part Uri to be compared
        /// Second part Uri to be compared
        /// A 32-bit signed integer indicating the lexical relationship between the compared Uri components.
        /// Value - Less than zero means firstUri is less than secondUri 
        /// Value - Equal to zero means both the Uris are equal
        /// Value - Greater than zero means firstUri is greater than secondUri 
        /// If firstPartUri or secondPartUri parameter does not conform to the valid partUri syntax 
        public static int ComparePartUri(Uri firstPartUri, Uri secondPartUri)
        { 
            if(firstPartUri!=null)
                firstPartUri = ValidatePartUri(firstPartUri);

            if(secondPartUri!=null) 
                secondPartUri = ValidatePartUri(secondPartUri);
 
            //If any of the operands are null then we simply call System.Uri compare to return the correct value 
            if (firstPartUri == null || secondPartUri == null)
                return CompareUsingSystemUri(firstPartUri, secondPartUri); 

            return ((IComparable)firstPartUri).CompareTo((ValidatedPartUri)secondPartUri);
        }
 
        /// 
        /// IsRelationshipPartUri method returns a boolean indicating whether the 
        /// Uri given is a relationship part Uri or not. 
        /// 
        /// uri of part to evaluate 
        /// true if the given part is a PackageRelationship part
        /// Does not inspect the part contents - this is based solely on the name
        /// If partUri parameter is null
        /// If partUri parameter is an absolute Uri 
        /// If partUri parameter does not conform to the valid partUri Syntax
        public static bool IsRelationshipPartUri(Uri partUri) 
        { 
            if (partUri == null)
                throw new ArgumentNullException("partUri"); 

            if(!(partUri is ValidatedPartUri))
                partUri = ValidatePartUri(partUri);
 
            return ((ValidatedPartUri)partUri).IsRelationshipPartUri;
        } 
 
        /// 
        /// This method returns a relationship part Uri given a part Uri 
        /// Example Input - partUri - /files/document.xaml
        /// Return  - Relationship Uri  - /files/_rels/document.xaml.rels
        /// If the input to the method is Uri - "/", then we will return /_rels/.rels as the
        /// relationship part Uri for the Package level relationships 
        /// 
        /// Part Uri for which the relationship part Uri is wanted 
        /// returns a Uri that conforms to the relationship part Uri syntax 
        /// If the input Uri is a relationship part Uri itself
        /// If partUri parameter is null 
        /// If partUri parameter is an absolute Uri
        /// If partUri parameter does not conform to the valid partUri Syntax
        public static Uri GetRelationshipPartUri(Uri partUri)
        { 
            if (partUri == null)
                throw new ArgumentNullException("partUri"); 
 
            if (Uri.Compare(partUri, PackageRootUri, UriComponents.SerializationInfoString, UriFormat.UriEscaped, StringComparison.Ordinal) == 0)
                return PackageRelationship.ContainerRelationshipPartName; 

            // Verify  -
            // 1. Validates that this part Uri is a valid part Uri
 
            partUri = ValidatePartUri(partUri);
 
            // 2. Checks that this part Uri is not a relationshipPart Uri 
            if(IsRelationshipPartUri(partUri))
                throw new ArgumentException(SR.Get(SRID.RelationshipPartUriNotExpected)); 

            //We should have a ValidatedPartUri by this time
            string partName = ((ValidatedPartUri)partUri).PartUriString;
 
            string file = Path.GetFileName(partName);
 
            Debug.Assert((partName.Length - file.Length) > 0, 
                "The partname may not be wellformed");
 
            // Get the parname without the last segment
            partName = partName.Substring(0, partName.Length - file.Length);

            partName = Path.Combine(partName, _relationshipPartSegmentName);         // Adding the "_rels" segment 
            partName = Path.Combine(partName, file);                                 // Adding the last segment back
            partName = String.Concat(partName, _relationshipPartExtensionName);      // Adding the ".rels" extension 
            partName = partName.Replace(BackwardSlashChar, ForwardSlashChar); 

            // convert to Uri - We could use PackUriHelper.Create, but since we know that this is a 
            //valid Part Uri we can just call the Uri constructor.
            return new ValidatedPartUri(partName, true /*IsRelationship*/);
        }
 

        ///  
        /// Given a valid relationship Part Uri, this method returns the source Part Uri for 
        /// this relationship Part Uri.
        /// If the relationship part name is for the Package Level relationships [/_rels/.rels], 
        /// we return a relative Uri of the form "/" indicating that it has no part as the parent,
        /// but is at the package level
        /// Example Input - Relationship Uri  - /files/_rels/document.xaml.rels
        /// Returns -Source Part Uri - /files/document.xaml 
        /// 
        /// relationship part Uri 
        /// A uri that is a valid source part Uri for the relationship Uri provided 
        /// If relationshipPartUri parameter is null
        /// If relationshipPartUri parameter is an absolute Uri 
        /// If relationshipPartUri parameter does not conform to the valid partUri Syntax
        /// If the relationshipPartUri is not a relationship part Uri itself
        /// If the resultant Uri obtained is a relationship part Uri
        public static Uri GetSourcePartUriFromRelationshipPartUri(Uri relationshipPartUri) 
        {
            if (relationshipPartUri == null) 
                throw new ArgumentNullException("relationshipPartUri"); 

            // Verify - 
            // 1. Validates that this part Uri is a valid part Uri
            relationshipPartUri = ValidatePartUri(relationshipPartUri);

            // 2. Checks that this part Uri is not a relationshipPart Uri 
            if (!IsRelationshipPartUri(relationshipPartUri))
                throw new ArgumentException(SR.Get(SRID.RelationshipPartUriExpected)); 
 
            // _rels/.rels has no parent part
            if (PackUriHelper.ComparePartUri(PackageRelationship.ContainerRelationshipPartName, relationshipPartUri) == 0) 
            {
                return PackageRootUri;
            }
            else 
            {
                //We should have a ValidatedPartUri by this time 
                string path = ((ValidatedPartUri)relationshipPartUri).PartUriString; 

                string partNameWithoutExtension = Path.GetFileNameWithoutExtension(path); 

                Debug.Assert((path.Length - partNameWithoutExtension.Length - _relationshipPartExtensionName.Length - 1) > 0,
                    "The partname may not be wellformed");
 
                //Get the part name without the last segment
                path = path.Substring(0, path.Length - partNameWithoutExtension.Length - _relationshipPartExtensionName.Length - 1); 
 
                Debug.Assert((path.Length - _relationshipPartSegmentName.Length) > 0,
                    "The partname may not be wellformed"); 

                path = path.Substring(0, path.Length - _relationshipPartSegmentName.Length); // Removing rels segment
                path = Path.Combine(path, partNameWithoutExtension);        // Adding the last segment without ".rels" extension
                path = path.Replace(BackwardSlashChar, ForwardSlashChar); 

                // convert to Uri - We could use PackUriHelper.Create, but since we know that this is a 
                //valid Part Uri we can just call the Uri constructor. 
                return new ValidatedPartUri(path, false /*IsRelationship*/);
            } 
        }

        #endregion Public Methods
 
        //-----------------------------------------------------
        // 
        //  Public Events 
        //
        //------------------------------------------------------ 
        // None
        //-----------------------------------------------------
        //
        //  Internal Constructors 
        //
        //----------------------------------------------------- 
        // None 
        //-----------------------------------------------------
        // 
        //  Internal Properties
        //
        //------------------------------------------------------
 
        #region Internal Properties
 
        internal static Uri PackageRootUri 
        {
            get 
            {
                return _packageRootUri;
            }
        } 

        #endregion Internal Properties 
 
        //-----------------------------------------------------
        // 
        //  Internal Methods
        //
        //------------------------------------------------------
 
        #region Internal Methods
 
        internal static bool IsPackUri(Uri uri) 
        {
            return uri != null && 
                string.Compare(uri.Scheme, UriSchemePack, StringComparison.OrdinalIgnoreCase) == 0;
        }

        internal static bool TryValidatePartUri(Uri partUri, out ValidatedPartUri validatedPartUri) 
        {
            if (partUri is ValidatedPartUri) 
            { 
                validatedPartUri = (ValidatedPartUri)partUri;
                return true; 
            }
            else
            {
                string partUriString; 
                Exception exception = GetExceptionIfPartUriInvalid(partUri, out partUriString);
                if (exception != null) 
                { 
                    validatedPartUri = null;
                    return false; 
                }
                else
                {
                    validatedPartUri = new ValidatedPartUri(partUriString); 
                    return true;
                } 
            } 
        }
 
        /// 
        /// This method is used to validate a part Uri
        /// This method does not perform a case sensitive check of the Uri
        ///  
        /// The string that represents the part within a package
        /// Returns the part uri if it is valid 
        /// If partUri parameter is null 
        /// If partUri parameter is an absolute Uri
        /// If partUri parameter is empty 
        /// If partUri parameter does not start with a "/"
        /// If partUri parameter starts with two "/"
        /// If partUri parameter ends with a "/"
        /// If partUri parameter has a fragment 
        /// If partUri parameter has some escaped characters that should not be escaped
        /// or some characters that should be escaped are not escaped. 
        internal static ValidatedPartUri ValidatePartUri(Uri partUri) 
        {
            if (partUri is ValidatedPartUri) 
                return (ValidatedPartUri)partUri;

            string partUriString;
            Exception exception = GetExceptionIfPartUriInvalid(partUri, out partUriString); 
            if (exception != null)
            { 
                Debug.Assert(partUriString != null && partUriString.Length == 0); 
                throw exception;
            } 
            else
            {
                Debug.Assert(partUriString != null && partUriString.Length > 0);
                return new ValidatedPartUri(partUriString); 
            }
        } 
 
        //Returns the part name in its escaped string form.
        internal static string GetStringForPartUri(Uri partUri) 
        {
            Debug.Assert(partUri != null, "Null reference check for this uri parameter should have been made earlier");
            if (!(partUri is ValidatedPartUri))
                partUri = ValidatePartUri(partUri); 

            return ((ValidatedPartUri)partUri).PartUriString; 
        } 

        //This method validates the packUri and returns its two components if they are valid- 
        //1. Package Uri
        //2. Part Uri
        internal static void ValidateAndGetPackUriComponents(Uri packUri, out Uri packageUri, out Uri partUri)
        { 
            //Validate if its not null and is an absolute Uri, has pack:// Scheme.
            packUri = ValidatePackUri(packUri); 
            packageUri = GetPackageUriComponent(packUri); 
            partUri = GetPartUriComponent(packUri);
        } 

        #endregion Internal Methods

        //------------------------------------------------------ 
        //
        //  Internal Events 
        // 
        //-----------------------------------------------------
        // None 
        //------------------------------------------------------
        //
        //  Private Constructors
        // 
        //-----------------------------------------------------
 
        #region Private Constructor 

        //  
        //   Critical    - as this code does an elevation
        //   TreatAsSafe - the net effect of this is to enable registration of Pack: Uri scheme.
        //   This enables fetching of resources via this scheme. Considered safe - as currently
        //   the pack: scheme has no elevations in it ( and any necessary elevations for container 
        //   access will be reviewed as needed).
        //  
        [SecurityTreatAsSafe, SecurityCritical] 
        static PackUriHelper()
        { 
            // indicate that we want "basic" parsing
            if (!UriParser.IsKnownScheme(UriSchemePack))
            {
                try 
                {
                    SecurityPermission permobj = new SecurityPermission(SecurityPermissionFlag.Infrastructure); 
                    permobj.Assert(); //BlessedAssert: 

                    // Indicate that we want a default hierarchical parser with a registry based authority 
                    UriParser.Register(new GenericUriParser(GenericUriParserOptions.GenericAuthority), UriSchemePack, -1);
                }
                finally
                { 
                    SecurityPermission.RevertAssert();
                } 
            } 
        }
 
        #endregion Private Constructor

        //-----------------------------------------------------
        // 
        //  Private Methods
        // 
        //----------------------------------------------------- 

        #region Private Methods 

        /// 
        /// This method is used to validate the package uri
        ///  
        /// 
        ///  
        private static Uri ValidatePackageUri(Uri packageUri) 
        {
            if (packageUri == null) 
                throw new ArgumentNullException("packageUri");

            if (!packageUri.IsAbsoluteUri)
                throw new ArgumentException(SR.Get(SRID.UriShouldBeAbsolute)); 

            return packageUri; 
        } 

        //validates is a given uri has pack:// scheme 
        private static Uri ValidatePackUri(Uri packUri)
        {
            if (packUri == null)
                throw new ArgumentNullException("packUri"); 

            if (!packUri.IsAbsoluteUri) 
                throw new ArgumentException(SR.Get(SRID.UriShouldBeAbsolute)); 

            if (packUri.Scheme != PackUriHelper.UriSchemePack) 
                throw new ArgumentException(SR.Get(SRID.UriShouldBePackScheme));

            return packUri;
 
        }
 
        ///  
        /// Escapes -  %', '@', ',', '?' in the package URI
        /// This method modifies the string in a culture safe and case safe manner. 
        /// 
        /// 
        /// 
        private static string EscapeSpecialCharacters(string path) 
        {
            string characterString; 
 
            // Escaping for the following - '%'; '@'; ',' and '?'
            // !!Important!! - The order is important - The '%' sign should be escaped first. 
            // This is currently enforced by the order of characters in the _specialCharacters array
            foreach (char c in _specialCharacters)
            {
                characterString = c.ToString(); 
                if (path.Contains(characterString))
                    path = path.Replace(characterString, Uri.HexEscape(c)); 
            } 

            return path; 
        }

        private static Exception GetExceptionIfPartUriInvalid(Uri partUri, out string partUriString)
        { 
            partUriString = String.Empty;
 
            if (partUri == null) 
                return new ArgumentNullException("partUri");
 
            Exception argumentException = null;

            argumentException = GetExceptionIfAbsoluteUri(partUri);
            if (argumentException != null) 
                return argumentException;
 
            string partName = GetStringForPartUriFromAnyUri(partUri); 

            //We need to make sure that the URI passed to us is not just "/" 
            //"/" is a valid relative uri, but is not a valid partname
            if (partName == String.Empty)
                return new ArgumentException(SR.Get(SRID.PartUriIsEmpty));
 
            if (partName[0] != '/')
                return new ArgumentException(SR.Get(SRID.PartUriShouldStartWithForwardSlash)); 
 
            argumentException = GetExceptionIfPartNameStartsWithTwoSlashes(partName);
            if (argumentException != null) 
                return argumentException;

            argumentException = GetExceptionIfPartNameEndsWithSlash(partName);
            if (argumentException != null) 
                return argumentException;
 
            argumentException = GetExceptionIfFragmentPresent(partName); 
            if (argumentException != null)
                return argumentException; 

            //We test if the URI is wellformed and refined.
            //The relative URI that was passed to us may not be correctly escaped and so we test that.
            //Also there might be navigation "/../" present in the URI which we need to detect. 
            string wellFormedPartName =
                new Uri(_defaultUri, partName).GetComponents(UriComponents.Path | 
                                                             UriComponents.KeepDelimiter, UriFormat.UriEscaped); 

            //Note - For Relative Uris the output of ToString() and OriginalString property 
            //are the same as per the current implementation of System.Uri
            //Need to use OriginalString property or ToString() here as we are want to
            //validate that the input uri given to us was valid in the first place.
            //We do not want to use GetComponents in this case as it might lead to 
            //performing escaping or unescaping as per the UriFormat enum value and that
            //may alter the string that the user created the Uri with and we may not be able 
            //to verify the uri correctly. 
            //We perform the comparison in a case-insensitive manner, as at this point,
            //only escaped hex digits (A-F) might vary in casing. 
            if (String.CompareOrdinal(partUri.ToString().ToUpperInvariant(), wellFormedPartName.ToUpperInvariant()) != 0)
                return new ArgumentException(SR.Get(SRID.InvalidPartUri));

            //if we get here, the partUri is valid and so we return null, as there is no exception. 
            partUriString = partName;
            return null; 
        } 

        private static void ThrowIfAbsoluteUri(Uri uri) 
        {
            Exception exception = GetExceptionIfAbsoluteUri(uri);
            if (exception != null)
                throw exception; 
        }
 
        private static ArgumentException GetExceptionIfAbsoluteUri(Uri uri) 
        {
            if (uri.IsAbsoluteUri) 
                return new ArgumentException(SR.Get(SRID.URIShouldNotBeAbsolute));
            else
                return null;
        } 

        private static void ThrowIfFragmentPresent(string partName) 
        { 
            Exception exception = GetExceptionIfFragmentPresent(partName);
            if (exception != null) 
                throw exception;
        }

        private static ArgumentException GetExceptionIfFragmentPresent(string partName) 
        {
            if (partName.Contains("#")) 
                return new ArgumentException(SR.Get(SRID.PartUriCannotHaveAFragment)); 
            else
                return null; 
        }

        private static void ThrowIfPartNameEndsWithSlash(string partName)
        { 
            Exception exception = GetExceptionIfPartNameEndsWithSlash(partName);
            if (exception != null) 
                throw exception; 
        }
 
        private static ArgumentException GetExceptionIfPartNameEndsWithSlash(string partName)
        {
            if (partName.Length > 0)
            { 
                if (partName[partName.Length - 1] == '/')
                    return new ArgumentException(SR.Get(SRID.PartUriShouldNotEndWithForwardSlash)); 
            } 
            return null;
        } 

        private static void ThrowIfPartNameStartsWithTwoSlashes(string partName)
        {
            Exception exception = GetExceptionIfPartNameStartsWithTwoSlashes(partName); 
            if (exception != null)
                throw exception; 
        } 

        // A relative reference that begins with two slash characters is termed 
        // a network-path reference; such references are rarely used.
        // However, when they are resolved they represent the authority part of the URI
        // Absolute URI - `http://a/b/c/d;p?q
        // Relative URI - //m 
        // Resolved URI - `http://m
        private static ArgumentException GetExceptionIfPartNameStartsWithTwoSlashes(string partName) 
        { 
            if (partName.Length > 1)
            { 
                if (partName[0] == '/' && partName[1] == '/')
                    return new ArgumentException(SR.Get(SRID.PartUriShouldNotStartWithTwoForwardSlashes));
            }
            return null; 
        }
 
        //Calling System.Uri.Compare method 
        //This method minimizes the false positives that we might get as a result
        //of comparing two URIs. 
        //Also, we exclude the Fragment component while comparing.
        private static int CompareUsingSystemUri(Uri firstUri, Uri secondUri)
        {
            return  Uri.Compare( 
                    firstUri,
                    secondUri, 
                    UriComponents.AbsoluteUri & ~UriComponents.Fragment, 
                    UriFormat.UriEscaped,
                    StringComparison.Ordinal); 
        }

        //Returns the part name in its escaped string form from an Absolute [must be pack://] or a Relative URI
        private static string GetStringForPartUriFromAnyUri(Uri partUri) 
        {
            Debug.Assert(partUri != null, "Null reference check for this uri parameter should have been made earlier"); 
            Debug.Assert(!(partUri is ValidatedPartUri), "This method should only be called when we have not already validated the part uri"); 

            Uri safeUnescapedUri; 

            // Step 1: Get the safe-unescaped form of the URI first. This will unescape all the characters
            // that can be safely un-escaped, unreserved characters, unicode characters, etc.
            if (!partUri.IsAbsoluteUri) 
            {
                //We assume a well formed part uri has been passed to this method 
                safeUnescapedUri = new Uri(partUri.GetComponents(UriComponents.SerializationInfoString, UriFormat.SafeUnescaped), UriKind.Relative); 
            }
            else 
            {
                safeUnescapedUri =
                    new Uri(partUri.GetComponents(UriComponents.Path |
                                                  UriComponents.KeepDelimiter, UriFormat.SafeUnescaped), UriKind.Relative); 
            }
 
            // Step 2: Get the canonically escaped Path with only ascii characters 
            //Get the escaped string for the part name as part names should have only ascii characters
            String partName = safeUnescapedUri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped); 

            //The part name can be empty in cases where we were passed a pack URI that has no part component
            if (IsPartNameEmpty(partName))
                return String.Empty; 
            else
                return partName; 
        } 

        //Verifies whether the part name is empty. PartName can be empty in two cases : 
        //1. Empty String
        //2. String with just the begining "/"
        private static bool IsPartNameEmpty(string partName)
        { 
            Debug.Assert(partName != null, "Null reference check for this partName parameter should have been made earlier");
 
            // Uri.GetComponents may return a single forward slash when there is no absolute path. 
            // This is Whidbey PS399695.  Until that is changed, we check for both cases - either an entirely empty string,
            // or a single forward slash character.  Either case means there is no part name. 
            if (partName.Length == 0 || ((partName.Length == 1) && (partName[0] == '/')))
                return true;
            else
                return false; 

        } 
 
        //This method validates and returns the PackageUri component
        private static Uri GetPackageUriComponent(Uri packUri) 
        {
            Debug.Assert(packUri != null, "packUri parameter cannot be null");

            //Step 1 - Get the authority part of the URI. This section represents that package URI 
            String hostAndPort = packUri.GetComponents(UriComponents.HostAndPort, UriFormat.UriEscaped);
 
            //Step 2 - Replace the ',' with '/' to reconstruct the package URI 
            hostAndPort = hostAndPort.Replace(',', '/');
 
            //Step 3 - Unescape the special characters that we had escaped to construct the packUri
            Uri packageUri = new Uri(Uri.UnescapeDataString(hostAndPort));

            if (packageUri.Fragment != String.Empty) 
                throw new ArgumentException(SR.Get(SRID.InnerPackageUriHasFragment));
 
            return packageUri; 
        }
 
        //This method validates and returns the PartUri component.
        private static PackUriHelper.ValidatedPartUri GetPartUriComponent(Uri packUri)
        {
            Debug.Assert(packUri != null, "packUri parameter cannot be null"); 

            string partName = GetStringForPartUriFromAnyUri(packUri); 
 
            if (partName == String.Empty)
                return null; 
            else
                return ValidatePartUri(new Uri(partName, UriKind.Relative));
        }
 
        #endregion Private Methods
 
        //------------------------------------------------------ 
        //
        //  Private Fields 
        //
        //-----------------------------------------------------

        #region Private Members 

        //we use this dummy URI to resolve relative URIs treating the container as the authority. 
        private static readonly Uri _defaultUri = new Uri("http://defaultcontainer/"); 

        //we use this dummy Uri to represent the root of the container. 
        private static readonly Uri _packageRootUri = new Uri("/", UriKind.Relative);

        // We need to perform Escaping for the following - '%'; '@'; ',' and '?'
        // !!Important!! - The order is important - The '%' sign should be escaped first. 
        // If any more characters need to be added to the array below they should be added at the end.
        private static readonly char[] _specialCharacters = { '%', '@', ',', '?' }; 
 
        //Rels segment and extension
        private static readonly string _relationshipPartSegmentName = "_rels"; 
        private static readonly string _relationshipPartExtensionName = ".rels";

        // Forward Slash
        internal static readonly char ForwardSlashChar = '/'; 

        // Backward Slash 
        internal static readonly char BackwardSlashChar = '\\'; 

        ///  
        /// pack scheme name
        /// 
        public static readonly string UriSchemePack = "pack";
 
        #endregion Private Members
 
        #region Private Class 

        ///  
        /// ValidatedPartUri class
        /// Once the partUri has been validated as per the syntax in the OPC spec
        /// we create a ValidatedPartUri, this way we do not have to re-validate
        /// this. 
        /// This class is heavily used throughout the Packaging APIs and in order
        /// to reduce the parsing and number of allocations for Strings and Uris 
        /// we cache the results after parsing. 
        /// 
        internal sealed class ValidatedPartUri : Uri, IComparable, IEquatable 
        {
            //------------------------------------------------------
            //
            //  Internal Constructors 
            //
            //------------------------------------------------------ 
 
            #region Internal Constructors
 
            internal ValidatedPartUri(string partUriString)
                : this(partUriString, false /*isNormalized*/, true /*computeIsRelationship*/, false /*dummy value as we will compute it later*/)
            {
            } 

            //Use this constructor when you already know if a given string is a relationship 
            //or no. One place this is used is while creating a normalized uri for a part Uri 
            //This will optimize the code and we will not have to parse the Uri to find out
            //if it is a relationship part uri 
            internal ValidatedPartUri(string partUriString, bool isRelationshipUri)
                : this(partUriString, false /*isNormalized*/, false /*computeIsRelationship*/, isRelationshipUri)
            {
            } 

            #endregion Internal Constructors 
 
            //-----------------------------------------------------
            // 
            //  Internal Methods
            //
            //------------------------------------------------------
 
            #region IComparable Methods
 
            int IComparable.CompareTo(ValidatedPartUri otherPartUri) 
            {
                return Compare(otherPartUri); 
            }

            #endregion IComparable Methods
 
            #region IEqualityComparer Methods
 
            bool IEquatable.Equals(ValidatedPartUri otherPartUri) 
            {
                return Compare(otherPartUri) == 0; 
            }

            #endregion IEqualityComparer Methods
 
            #region Internal Properties
 
            //----------------------------------------------------- 
            //
            //  Internal Properties 
            //
            //-----------------------------------------------------

            //Returns the PartUri string 
            internal string PartUriString
            { 
                get 
                {
                    return _partUriString; 
                }
            }

            internal string PartUriExtension 
            {
                get 
                { 
                    if (_partUriExtension == null)
                    { 
                        _partUriExtension = Path.GetExtension(_partUriString);

                        //If extension is absent just return the empty string
                        //else remove the leading "." from the returned extension 
                        //string
                        if (_partUriExtension.Length > 0) 
                            _partUriExtension = _partUriExtension.Substring(1); 
                    }
                    return _partUriExtension; 
                }
            }

            //Returns the normalized string for the part uri. 
            internal string NormalizedPartUriString
            { 
                get 
                {
                    if (_normalizedPartUriString == null) 
                        _normalizedPartUriString = GetNormalizedPartUriString();

                    return _normalizedPartUriString;
                } 
            }
 
            //Returns the normalized part uri 
            internal ValidatedPartUri NormalizedPartUri
            { 
                get
                {
                    if (_normalizedPartUri == null)
                        _normalizedPartUri = GetNormalizedPartUri(); 

                    return _normalizedPartUri; 
                } 
            }
 
            //Returns true, if the original string passed to create
            //this object was normalized
            internal bool IsNormalized
            { 
                get
                { 
                    return _isNormalized; 
                }
            } 

            //Returns, true is the partUri is a relationship part uri
            internal bool IsRelationshipPartUri
            { 
                get
                { 
                    return _isRelationshipPartUri; 
                }
            } 

            #endregion Internal Properties

            //----------------------------------------------------- 
            //
            //  Private Methods 
            // 
            //------------------------------------------------------
 
            #region Private Constructor

            //Note - isRelationshipPartUri parameter is only meaningful if computeIsRelationship
            //bool is false, in which case, it means that we already know whether the partUriString 
            //represents a relationship or no, and as such there is no need to compute/parse to
            //find out if its a relationship. 
            private ValidatedPartUri(string partUriString, bool isNormalized, bool computeIsRelationship, bool isRelationshipPartUri) 
                : base(partUriString, UriKind.Relative)
            { 
                Debug.Assert(partUriString != null && partUriString.Length > 0);

                _partUriString = partUriString;
                _isNormalized = isNormalized; 
                if (computeIsRelationship)
                    _isRelationshipPartUri = IsRelationshipUri(); 
                else 
                    _isRelationshipPartUri = isRelationshipPartUri;
            } 

            #endregion PrivateConstuctor

            //----------------------------------------------------- 
            //
            //  Private Methods 
            // 
            //------------------------------------------------------
 
            #region Private Methods

            // IsRelationshipPartUri method returns a boolean indicating whether the
            // Uri given is a relationship part Uri or no. 
            private bool IsRelationshipUri()
            { 
                bool result = false; 

                //exit early if the partUri does not end with the relationship extention 
                if (!NormalizedPartUriString.EndsWith(_relationshipPartUpperCaseExtension, StringComparison.Ordinal))
                    return false;

                // if uri is /_rels/.rels then we return true 
                if (PackUriHelper.ComparePartUri(_containerRelationshipNormalizedPartUri, this) == 0)
                    return true; 
 
                // Look for pattern that matches: "XXX/_rels/YYY.rels" where XXX is zero or more part name characters and
                // YYY is any legal part name characters. 
                // We can assume that the string is a valid URI because it would have been rejected by the Uri parsing
                // code in the Uri constructor if it wasn't.
                // Uri's are case insensitive so we can compare them by upper-casing them
                // Essentially, we will just look for the existence of a "folder" called _rels and the trailing extension 
                // of .rels.  The folder must also be the last "folder".
                // Comparing using the normalized string to reduce the number of ToUpperInvariant operations 
                // required for case-insensitive comparison 
                string[] segments = NormalizedPartUriString.Split(_forwardSlashSeparator); //new Uri(_defaultUri, this).Segments; //partUri.Segments cannot be called on a relative Uri;
 
                // String.Split, will always return an empty string as the
                // first member in the array as the string starts with a "/"

                Debug.Assert(segments.Length > 0 && segments[0] == String.Empty); 

                //If the extension was not equal to .rels, we would have exited early. 
                Debug.Assert(String.CompareOrdinal((Path.GetExtension(segments[segments.Length - 1])), _relationshipPartUpperCaseExtension) == 0); 

                // must be at least two segments and the last one must end with .RELs 
                // and the length of the segment should be greater than just the extension.
                if ((segments.Length >= 3) &&
                    (segments[segments.Length - 1].Length > _relationshipPartExtensionName.Length))
                { 
                    // look for "_RELS" segment which must be second last segment
                    result = (String.CompareOrdinal(segments[segments.Length - 2], _relationshipPartUpperCaseSegmentName) == 0); 
                } 

                // In addition we need to make sure that the relationship is not created by taking another relationship 
                // as the source of this uri. So XXX/_rels/_rels/YYY.rels.rels would be invalid.
                if (segments.Length > 3 && result == true)
                {
                    if ((segments[segments.Length - 1]).EndsWith(_relsrelsUpperCaseExtension, StringComparison.Ordinal)) 
                    {
                        // look for "_rels" segment in the third last segment 
                        if(String.CompareOrdinal(segments[segments.Length - 3], _relationshipPartUpperCaseSegmentName) == 0) 
                            throw new ArgumentException(SR.Get(SRID.NotAValidRelationshipPartUri));
                    } 
                }

                return result;
            } 

            //Returns the normalized string for the part uri. 
            //Currently normalizing the PartUriString consists of only one step - 
            //1. Take the wellformed and escaped partUri string and case fold to UpperInvariant
            private string GetNormalizedPartUriString() 
            {
                //Case Fold the partUri string to Invariant Upper case (this helps us perform case insensitive comparison)
                //We follow the Simple case folding specified in the Unicode standard
 
                if (_isNormalized)
                    return _partUriString; 
                else 
                    return _partUriString.ToUpperInvariant();
            } 

            private ValidatedPartUri GetNormalizedPartUri()
            {
                if (IsNormalized) 
                    return this;
                else 
                    return new ValidatedPartUri(_normalizedPartUriString, 
                                                true /*isNormalized*/,
                                                false /*computeIsRelationship*/, 
                                                IsRelationshipPartUri);
            }

            private int Compare(ValidatedPartUri otherPartUri) 
            {
                //If otherPartUri is null then we return 1 
                if (otherPartUri == null) 
                    return 1;
 
                //Compare the normalized uri strings for the two part uris.
                return String.CompareOrdinal(this.NormalizedPartUriString, otherPartUri.NormalizedPartUriString);
            }
 
            //------------------------------------------------------
            // 
            //  Private Members 
            //
            //----------------------------------------------------- 

            private ValidatedPartUri _normalizedPartUri;
            private string _partUriString;
            private string _normalizedPartUriString; 
            private string _partUriExtension;
            private bool   _isNormalized; 
            private bool   _isRelationshipPartUri; 

            //String Uppercase variants 

            private static readonly string _relationshipPartUpperCaseExtension   = ".RELS";
            private static readonly string _relationshipPartUpperCaseSegmentName = "_RELS";
            private static readonly string _relsrelsUpperCaseExtension = String.Concat(_relationshipPartUpperCaseExtension, _relationshipPartUpperCaseExtension); 

            //need to use the private constructor to initialize this particular partUri as we need this in the 
            //IsRelationshipPartUri, that is called from the constructor. 
            private static readonly Uri   _containerRelationshipNormalizedPartUri = new ValidatedPartUri("/_RELS/.RELS",
                                                                                                         true /*isnormalized*/, 
                                                                                                         false /*computeIsRelationship*/,
                                                                                                         true /*IsRelationship*/);

            private static readonly char[] _forwardSlashSeparator = { '/' }; 

            #endregion Private Methods 
 
            //------------------------------------------------------
        } 

        #endregion Private Class
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK