1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections.Generic;
6 using System.Text.Internal;
7 using System.Text.Unicode;
8 
9 namespace System.Text.Encodings.Web
10 {
11     /// <summary>
12     /// Represents a filter which allows only certain Unicode code points through.
13     /// </summary>
14     public class TextEncoderSettings
15     {
16         private AllowedCharactersBitmap _allowedCharactersBitmap;
17 
18         /// <summary>
19         /// Instantiates an empty filter (allows no code points through by default).
20         /// </summary>
TextEncoderSettings()21         public TextEncoderSettings()
22         {
23             _allowedCharactersBitmap = AllowedCharactersBitmap.CreateNew();
24         }
25 
26         /// <summary>
27         /// Instantiates the filter by cloning the allow list of another <see cref="TextEncoderSettings"/>.
28         /// </summary>
TextEncoderSettings(TextEncoderSettings other)29         public TextEncoderSettings(TextEncoderSettings other)
30         {
31             if (other == null)
32             {
33                 throw new ArgumentNullException(nameof(other));
34             }
35 
36             _allowedCharactersBitmap = AllowedCharactersBitmap.CreateNew();
37             AllowCodePoints(other.GetAllowedCodePoints());
38         }
39 
40         /// <summary>
41         /// Instantiates the filter where only the character ranges specified by <paramref name="allowedRanges"/>
42         /// are allowed by the filter.
43         /// </summary>
TextEncoderSettings(params UnicodeRange[] allowedRanges)44         public TextEncoderSettings(params UnicodeRange[] allowedRanges)
45         {
46             if(allowedRanges == null)
47             {
48                 throw new ArgumentNullException(nameof(allowedRanges));
49             }
50             _allowedCharactersBitmap = AllowedCharactersBitmap.CreateNew();
51             AllowRanges(allowedRanges);
52         }
53 
54         /// <summary>
55         /// Allows the character specified by <paramref name="character"/> through the filter.
56         /// </summary>
AllowCharacter(char character)57         public virtual void AllowCharacter(char character)
58         {
59             _allowedCharactersBitmap.AllowCharacter(character);
60         }
61 
62         /// <summary>
63         /// Allows all characters specified by <paramref name="characters"/> through the filter.
64         /// </summary>
AllowCharacters(params char[] characters)65         public virtual void AllowCharacters(params char[] characters)
66         {
67             if (characters == null)
68             {
69                 throw new ArgumentNullException(nameof(characters));
70             }
71 
72             for (int i = 0; i < characters.Length; i++)
73             {
74                 _allowedCharactersBitmap.AllowCharacter(characters[i]);
75             }
76         }
77 
78         /// <summary>
79         /// Allows all code points specified by <paramref name="codePoints"/>.
80         /// </summary>
AllowCodePoints(IEnumerable<int> codePoints)81         public virtual void AllowCodePoints(IEnumerable<int> codePoints)
82         {
83             if (codePoints == null)
84             {
85                 throw new ArgumentNullException(nameof(codePoints));
86             }
87 
88             foreach (var allowedCodePoint in codePoints)
89             {
90                 // If the code point can't be represented as a BMP character, skip it.
91                 char codePointAsChar = (char)allowedCodePoint;
92                 if (allowedCodePoint == codePointAsChar)
93                 {
94                     _allowedCharactersBitmap.AllowCharacter(codePointAsChar);
95                 }
96             }
97         }
98 
99         /// <summary>
100         /// Allows all characters specified by <paramref name="range"/> through the filter.
101         /// </summary>
AllowRange(UnicodeRange range)102         public virtual void AllowRange(UnicodeRange range)
103         {
104             if (range == null)
105             {
106                 throw new ArgumentNullException(nameof(range));
107             }
108 
109             int firstCodePoint = range.FirstCodePoint;
110             int rangeSize = range.Length;
111             for (int i = 0; i < rangeSize; i++)
112             {
113                 _allowedCharactersBitmap.AllowCharacter((char)(firstCodePoint + i));
114             }
115         }
116 
117         /// <summary>
118         /// Allows all characters specified by <paramref name="ranges"/> through the filter.
119         /// </summary>
AllowRanges(params UnicodeRange[] ranges)120         public virtual void AllowRanges(params UnicodeRange[] ranges)
121         {
122             if (ranges == null)
123             {
124                 throw new ArgumentNullException(nameof(ranges));
125             }
126 
127             for (int i = 0; i < ranges.Length; i++)
128             {
129                 AllowRange(ranges[i]);
130             }
131         }
132 
133         /// <summary>
134         /// Resets this settings object by disallowing all characters.
135         /// </summary>
Clear()136         public virtual void Clear()
137         {
138             _allowedCharactersBitmap.Clear();
139         }
140 
141         /// <summary>
142         /// Disallows the character <paramref name="character"/> through the filter.
143         /// </summary>
ForbidCharacter(char character)144         public virtual void ForbidCharacter(char character)
145         {
146             _allowedCharactersBitmap.ForbidCharacter(character);
147         }
148 
149         /// <summary>
150         /// Disallows all characters specified by <paramref name="characters"/> through the filter.
151         /// </summary>
ForbidCharacters(params char[] characters)152         public virtual void ForbidCharacters(params char[] characters)
153         {
154             if (characters == null)
155             {
156                 throw new ArgumentNullException(nameof(characters));
157             }
158 
159             for (int i = 0; i < characters.Length; i++)
160             {
161                 _allowedCharactersBitmap.ForbidCharacter(characters[i]);
162             }
163         }
164 
165         /// <summary>
166         /// Disallows all characters specified by <paramref name="range"/> through the filter.
167         /// </summary>
ForbidRange(UnicodeRange range)168         public virtual void ForbidRange(UnicodeRange range)
169         {
170             if (range == null)
171             {
172                 throw new ArgumentNullException(nameof(range));
173             }
174 
175             int firstCodePoint = range.FirstCodePoint;
176             int rangeSize = range.Length;
177             for (int i = 0; i < rangeSize; i++)
178             {
179                 _allowedCharactersBitmap.ForbidCharacter((char)(firstCodePoint + i));
180             }
181         }
182 
183         /// <summary>
184         /// Disallows all characters specified by <paramref name="ranges"/> through the filter.
185         /// </summary>
ForbidRanges(params UnicodeRange[] ranges)186         public virtual void ForbidRanges(params UnicodeRange[] ranges)
187         {
188             if (ranges == null)
189             {
190                 throw new ArgumentNullException(nameof(ranges));
191             }
192 
193             for (int i = 0; i < ranges.Length; i++)
194             {
195                 ForbidRange(ranges[i]);
196             }
197         }
198 
199         /// <summary>
200         /// Retrieves the bitmap of allowed characters from this settings object.
201         /// The returned bitmap is a clone of the original bitmap to avoid unintentional modification.
202         /// </summary>
GetAllowedCharacters()203         internal AllowedCharactersBitmap GetAllowedCharacters()
204         {
205             return _allowedCharactersBitmap.Clone();
206         }
207 
208         /// <summary>
209         /// Gets an enumeration of all allowed code points.
210         /// </summary>
GetAllowedCodePoints()211         public virtual IEnumerable<int> GetAllowedCodePoints()
212         {
213             for (int i = 0; i < 0x10000; i++)
214             {
215                 if (_allowedCharactersBitmap.IsCharacterAllowed((char)i))
216                 {
217                     yield return i;
218                 }
219             }
220         }
221     }
222 }
223