Skip to content

Commit fa412d9

Browse files
committed
Updated/fixed validators and their unit tests
1 parent 1668826 commit fa412d9

File tree

9 files changed

+353
-113
lines changed

9 files changed

+353
-113
lines changed

MimeKit/Encodings/Base64Validator.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class Base64Validator : IEncodingValidator
4646
int lineNumber;
4747
int padding;
4848
int octets;
49+
bool invalid;
4950

5051
/// <summary>
5152
/// Initialize a new instance of the <see cref="Base64Validator"/> class.
@@ -112,6 +113,7 @@ unsafe void Validate (ref byte table, byte* input, int length)
112113
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidBase64Character, streamOffset, lineNumber);
113114
}
114115
} else if (c == (byte) '=') {
116+
streamOffset++;
115117
padding = 1;
116118
octets++;
117119
break;
@@ -136,10 +138,15 @@ unsafe void Validate (ref byte table, byte* input, int length)
136138
padding++;
137139
octets++;
138140

139-
if (padding > 2)
141+
if (padding > 2) {
140142
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidBase64Padding, streamOffset, lineNumber);
143+
invalid = true;
144+
break;
145+
}
141146
} else if (!c.IsWhitespace ()) {
142147
reader.OnMimeComplianceViolation (MimeComplianceViolation.Base64CharactersAfterPadding, streamOffset, lineNumber);
148+
invalid = true;
149+
break;
143150
}
144151

145152
streamOffset++;
@@ -166,6 +173,9 @@ public unsafe void Write (byte[] buffer, int startIndex, int length)
166173
{
167174
ValidateArguments (buffer, startIndex, length);
168175

176+
if (invalid)
177+
return;
178+
169179
fixed (byte* inbuf = buffer) {
170180
ref byte table = ref MemoryMarshal.GetReference (Base64Decoder.base64_rank);
171181

@@ -181,7 +191,7 @@ public unsafe void Write (byte[] buffer, int startIndex, int length)
181191
/// </remarks>
182192
public void Flush ()
183193
{
184-
if (octets % 4 != 0)
194+
if (!invalid && octets % 4 != 0)
185195
reader.OnMimeComplianceViolation (MimeComplianceViolation.IncompleteBase64Quantum, streamOffset, lineNumber);
186196
}
187197
}

MimeKit/Encodings/QuotedPrintableValidator.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ unsafe void Validate (byte* input, int length)
118118
}
119119
break;
120120
case QpValidatorState.EqualSign:
121-
c = *inptr++;
121+
c = *inptr;
122122

123123
if (c.IsXDigit ()) {
124124
state = QpValidatorState.DecodeByte;
@@ -131,9 +131,11 @@ unsafe void Validate (byte* input, int length)
131131
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidQuotedPrintableEncoding, streamOffset + (inptr - input), lineNumber);
132132
state = QpValidatorState.PassThrough;
133133
}
134+
135+
inptr++;
134136
break;
135137
case QpValidatorState.SoftBreak:
136-
c = *inptr++;
138+
c = *inptr;
137139

138140
if (c == '\n') {
139141
lineNumber++;
@@ -142,9 +144,10 @@ unsafe void Validate (byte* input, int length)
142144
}
143145

144146
state = QpValidatorState.PassThrough;
147+
inptr++;
145148
break;
146149
case QpValidatorState.DecodeByte:
147-
c = *inptr++;
150+
c = *inptr;
148151

149152
if (!c.IsXDigit ()) {
150153
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidQuotedPrintableEncoding, streamOffset + (inptr - input), lineNumber);
@@ -154,6 +157,7 @@ unsafe void Validate (byte* input, int length)
154157
}
155158

156159
state = QpValidatorState.PassThrough;
160+
inptr++;
157161
break;
158162
}
159163
}

MimeKit/Encodings/UUValidator.cs

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ enum UUValidatorState : byte
6565
long streamOffset;
6666
int lineNumber;
6767
UUValidatorState state;
68+
bool invalidPretext;
6869
byte nsaved;
6970
byte uulen;
7071

@@ -142,8 +143,11 @@ unsafe bool ScanBeginMarker (ref byte* inptr, byte* inend)
142143

143144
// only lines containing whitespace are allowed before the begin marker
144145
while (inptr < inend && *inptr != (byte) '\n') {
145-
if (!(*inptr).IsWhitespace ())
146-
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodePretext, streamOffset, lineNumber);
146+
if (!invalidPretext && !(*inptr).IsWhitespace ()) {
147+
// FIXME: should this really be emitted for *each* character before the 'begin'?
148+
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodePretext, streamOffset + (int) (inptr - start), lineNumber);
149+
invalidPretext = true;
150+
}
147151

148152
inptr++;
149153
}
@@ -154,10 +158,10 @@ unsafe bool ScanBeginMarker (ref byte* inptr, byte* inend)
154158
return false;
155159
}
156160

157-
SkipByte (ref inptr);
158-
159161
streamOffset += (int) (inptr - start);
160162

163+
SkipByte (ref inptr);
164+
161165
if (inptr == inend)
162166
return false;
163167
}
@@ -166,7 +170,10 @@ unsafe bool ScanBeginMarker (ref byte* inptr, byte* inend)
166170
if (nsaved != (byte) 'b') {
167171
// only lines containing whitespace are allowed before the begin marker
168172
if (!nsaved.IsWhitespace ()) {
169-
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodePretext, streamOffset, lineNumber);
173+
if (!invalidPretext) {
174+
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodePretext, streamOffset - 1, lineNumber);
175+
invalidPretext = true;
176+
}
170177
state = UUValidatorState.ExpectBegin;
171178
continue;
172179
}
@@ -182,7 +189,10 @@ unsafe bool ScanBeginMarker (ref byte* inptr, byte* inend)
182189
if (state == UUValidatorState.B) {
183190
nsaved = ReadByte (ref inptr);
184191
if (nsaved != (byte) 'e') {
185-
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodePretext, streamOffset, lineNumber);
192+
if (!invalidPretext) {
193+
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodePretext, streamOffset - 1, lineNumber);
194+
invalidPretext = true;
195+
}
186196
state = UUValidatorState.ExpectBegin;
187197
continue;
188198
}
@@ -195,7 +205,10 @@ unsafe bool ScanBeginMarker (ref byte* inptr, byte* inend)
195205
if (state == UUValidatorState.Be) {
196206
nsaved = ReadByte (ref inptr);
197207
if (nsaved != (byte) 'g') {
198-
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodePretext, streamOffset, lineNumber);
208+
if (!invalidPretext) {
209+
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodePretext, streamOffset - 1, lineNumber);
210+
invalidPretext = true;
211+
}
199212
state = UUValidatorState.ExpectBegin;
200213
continue;
201214
}
@@ -208,7 +221,10 @@ unsafe bool ScanBeginMarker (ref byte* inptr, byte* inend)
208221
if (state == UUValidatorState.Beg) {
209222
nsaved = ReadByte (ref inptr);
210223
if (nsaved != (byte) 'i') {
211-
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodePretext, streamOffset, lineNumber);
224+
if (!invalidPretext) {
225+
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodePretext, streamOffset - 1, lineNumber);
226+
invalidPretext = true;
227+
}
212228
state = UUValidatorState.ExpectBegin;
213229
continue;
214230
}
@@ -221,7 +237,10 @@ unsafe bool ScanBeginMarker (ref byte* inptr, byte* inend)
221237
if (state == UUValidatorState.Begi) {
222238
nsaved = ReadByte (ref inptr);
223239
if (nsaved != (byte) 'n') {
224-
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodePretext, streamOffset, lineNumber);
240+
if (!invalidPretext) {
241+
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodePretext, streamOffset - 1, lineNumber);
242+
invalidPretext = true;
243+
}
225244
state = UUValidatorState.ExpectBegin;
226245
continue;
227246
}
@@ -234,7 +253,10 @@ unsafe bool ScanBeginMarker (ref byte* inptr, byte* inend)
234253
if (state == UUValidatorState.Begin) {
235254
nsaved = ReadByte (ref inptr);
236255
if (nsaved != (byte) ' ') {
237-
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodePretext, streamOffset, lineNumber);
256+
if (!invalidPretext) {
257+
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodePretext, streamOffset - 1, lineNumber);
258+
invalidPretext = true;
259+
}
238260
state = UUValidatorState.ExpectBegin;
239261
continue;
240262
}
@@ -296,9 +318,10 @@ unsafe bool ScanBeginMarker (ref byte* inptr, byte* inend)
296318
return false;
297319
}
298320

321+
streamOffset += (int) (inptr - start);
322+
299323
SkipByte (ref inptr);
300324

301-
streamOffset += (int) (inptr - start);
302325
state = UUValidatorState.Payload;
303326
nsaved = 0;
304327

@@ -376,7 +399,7 @@ unsafe void Validate (byte* input, int length)
376399
}
377400
} else {
378401
// extra data beyond the end of the uuencoded line
379-
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodedLineLength, streamOffset, lineNumber);
402+
reader.OnMimeComplianceViolation (MimeComplianceViolation.InvalidUUEncodedLineLength, streamOffset - 1, lineNumber);
380403
}
381404
}
382405
}

UnitTests/ComplianceMimeReader.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// ComplianceMimeReader.cs
3+
//
4+
// Author: Jeffrey Stedfast <jestedfa@microsoft.com>
5+
//
6+
// Copyright (c) 2013-2026 .NET Foundation and Contributors
7+
//
8+
// Permission is hereby granted, free of charge, to any person obtaining a copy
9+
// of this software and associated documentation files (the "Software"), to deal
10+
// in the Software without restriction, including without limitation the rights
11+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
// copies of the Software, and to permit persons to whom the Software is
13+
// furnished to do so, subject to the following conditions:
14+
//
15+
// The above copyright notice and this permission notice shall be included in
16+
// all copies or substantial portions of the Software.
17+
//
18+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
// THE SOFTWARE.
25+
//
26+
27+
using MimeKit;
28+
29+
namespace UnitTests {
30+
class ComplianceMimeReader : MimeReader
31+
{
32+
public readonly List<MimeComplianceViolationEventArgs> ComplianceViolations = new List<MimeComplianceViolationEventArgs> ();
33+
34+
public ComplianceMimeReader (ParserOptions options, Stream stream, MimeFormat format = MimeFormat.Default) : base (options, stream, format)
35+
{
36+
DetectMimeComplianceViolations = true;
37+
}
38+
39+
public ComplianceMimeReader (Stream stream, MimeFormat format = MimeFormat.Default) : base (stream, format)
40+
{
41+
DetectMimeComplianceViolations = true;
42+
}
43+
44+
public ComplianceMimeReader () : this (Stream.Null, MimeFormat.Default)
45+
{
46+
}
47+
48+
protected internal override void OnMimeComplianceViolation (MimeComplianceViolation violation, long offset, int lineNumber)
49+
{
50+
ComplianceViolations.Add (new MimeComplianceViolationEventArgs (violation, offset, lineNumber));
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)