summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Security/Util/URLString.cs
blob: 4ec353876a84ef6bce74fe2b8c415ba425430842 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

//  URLString
//
//
//  Implementation of membership condition for zones
//

namespace System.Security.Util {
    
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Runtime.Versioning;
    using System.Runtime.Serialization;
    using System.Globalization;
    using System.Text;
    using System.IO;
    using System.Diagnostics.Contracts;

    internal static class URLString
    {
        internal static string PreProcessForExtendedPathRemoval(bool checkPathLength, string url, bool isFileUrl)
        {
            bool isUncShare = false;
            return PreProcessForExtendedPathRemoval(checkPathLength: checkPathLength, url: url, isFileUrl: isFileUrl, isUncShare: ref isUncShare);
        }

        // Keeping this signature to avoid reflection breaks
        private static string PreProcessForExtendedPathRemoval(string url, bool isFileUrl, ref bool isUncShare)
        {
            return PreProcessForExtendedPathRemoval(checkPathLength: true, url: url, isFileUrl: isFileUrl, isUncShare: ref isUncShare);
        }

        private static string PreProcessForExtendedPathRemoval(bool checkPathLength, string url, bool isFileUrl, ref bool isUncShare)
        {
            // This is the modified URL that we will return
            StringBuilder modifiedUrl = new StringBuilder(url);

            // ITEM 1 - remove extended path characters.
            {
                // Keep track of where we are in both the comparison and altered strings.
                int curCmpIdx = 0;
                int curModIdx = 0;

                // If all the '\' have already been converted to '/', just check for //?/ or //./
                if ((url.Length - curCmpIdx) >= 4 &&
                    (String.Compare(url, curCmpIdx, "//?/", 0, 4, StringComparison.OrdinalIgnoreCase) == 0 ||
                     String.Compare(url, curCmpIdx, "//./", 0, 4, StringComparison.OrdinalIgnoreCase) == 0))
                {
                    modifiedUrl.Remove(curModIdx, 4);
                    curCmpIdx += 4;
                }
                else
                {
                    if (isFileUrl)
                    {
                        // We need to handle an indefinite number of leading front slashes for file URLs since we could
                        // get something like:
                        //      file://\\?\
                        //      file:/\\?\
                        //      file:\\?\
                        //      etc...
                        while (url[curCmpIdx] == '/')
                        {
                            curCmpIdx++;
                            curModIdx++;
                        }
                    }

                    // Remove the extended path characters
                    if ((url.Length - curCmpIdx) >= 4 &&
                        (String.Compare(url, curCmpIdx, "\\\\?\\", 0, 4, StringComparison.OrdinalIgnoreCase) == 0 ||
                         String.Compare(url, curCmpIdx, "\\\\?/", 0, 4, StringComparison.OrdinalIgnoreCase) == 0 ||
                         String.Compare(url, curCmpIdx, "\\\\.\\", 0, 4, StringComparison.OrdinalIgnoreCase) == 0 ||
                         String.Compare(url, curCmpIdx, "\\\\./", 0, 4, StringComparison.OrdinalIgnoreCase) == 0))
                    {
                        modifiedUrl.Remove(curModIdx, 4);
                        curCmpIdx += 4;
                    }
                }
            }

            // ITEM 2 - convert all slashes to forward slashes, and strip leading slashes.
            if (isFileUrl)
            {
                int slashCount = 0;
                bool seenFirstBackslash = false;

                while (slashCount < modifiedUrl.Length && (modifiedUrl[slashCount] == '/' || modifiedUrl[slashCount] == '\\'))
                {
                    // Look for sets of consecutive backslashes. We can't just look for these at the start
                    // of the string, since file:// might come first.  Instead, once we see the first \, look
                    // for a second one following it.
                    if (!seenFirstBackslash && modifiedUrl[slashCount] == '\\')
                    {
                        seenFirstBackslash = true;
                        if (slashCount + 1 < modifiedUrl.Length && modifiedUrl[slashCount + 1] == '\\')
                            isUncShare = true;
                    }

                    slashCount++;
                }

                modifiedUrl.Remove(0, slashCount);
                modifiedUrl.Replace('\\', '/');
            }

            // ITEM 3 - If the path is greater than or equal (due to terminating NULL in windows) MAX_PATH, we throw.
            if (checkPathLength)
            {
                // This needs to be a separate method to avoid hitting the static constructor on AppContextSwitches
                CheckPathTooLong(modifiedUrl);
            }

            // Create the result string from the StringBuilder
            return modifiedUrl.ToString();
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static void CheckPathTooLong(StringBuilder path)
        {
            if (path.Length >= (
#if PLATFORM_UNIX
                Interop.Sys.MaxPath))
#else
                PathInternal.MaxLongPath))
#endif
            {
                throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
            }
        }
    }
}