Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1765)

Unified Diff: sdk/lib/convert/base64.dart

Issue 2694373003: Normalize UriData.parse result. (Closed)
Patch Set: Address comments. Fix bug. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « CHANGELOG.md ('k') | sdk/lib/convert/convert.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/convert/base64.dart
diff --git a/sdk/lib/convert/base64.dart b/sdk/lib/convert/base64.dart
index 0254f853dbacd699704530b9be2596bbff3411b8..ee65ae7487ffd82d7ae994c3cffd834440642c00 100644
--- a/sdk/lib/convert/base64.dart
+++ b/sdk/lib/convert/base64.dart
@@ -57,6 +57,140 @@ class Base64Codec extends Codec<List<int>, String> {
Base64Encoder get encoder => _encoder;
Base64Decoder get decoder => const Base64Decoder();
+
+ /**
+ * Validates and normalizes the base64 encoded data in [source].
+ *
+ * Only acts on the substring from [start] to [end], with [end]
+ * defaulting to the end of the string.
+ *
+ * Normalization will:
+ * * Unescape any `%`-escapes.
+ * * Only allow valid characters (`A`-`Z`, `a`-`z`, `0`-`9`, `/` and `+`).
+ * * Normalize a `_` or `-` character to `/` or `+`.
+ * * Validate that existing padding (trailing `=` characters) is correct.
+ * * If no padding exists, add correct padding if necessary and possible.
+ * * Validate that the length is correct (a multiple of four).
+ */
+ String normalize(String source, [int start = 0, int end]) {
+ end = RangeError.checkValidRange(start, end, source.length);
+ const int percent = 0x25;
+ const int equals = 0x3d;
+ StringBuffer buffer = null;
+ int sliceStart = start;
+ var alphabet = _Base64Encoder._base64Alphabet;
+ var inverseAlphabet = _Base64Decoder._inverseAlphabet;
+ int firstPadding = -1;
+ int firstPaddingSourceIndex = -1;
+ int paddingCount = 0;
+ for (int i = start; i < end;) {
+ int sliceEnd = i;
+ int char = source.codeUnitAt(i++);
+ int originalChar = char;
+ // Normalize char, keep originalChar to see if it matches the source.
+ if (char == percent) {
+ if (i + 2 <= end) {
+ char = parseHexByte(source, i); // May be negative.
+ i += 2;
+ // We know that %25 isn't valid, but our table considers it
+ // a potential padding start, so skip the checks.
+ if (char == percent) char = -1;
+ } else {
+ // An invalid HEX escape (too short).
+ // Just skip past the handling and reach the throw below.
+ char = -1;
+ }
+ }
+ // If char is negative here, hex-decoding failed in some way.
+ if (0 <= char && char <= 127) {
+ int value = inverseAlphabet[char];
+ if (value >= 0) {
+ char = alphabet.codeUnitAt(value);
+ if (char == originalChar) continue;
+ } else if (value == _Base64Decoder._padding) {
+ // We have ruled out percent, so char is '='.
+ if (firstPadding < 0) {
+ // Mark position in normalized output where padding occurs.
+ firstPadding = (buffer?.length ?? 0) + (sliceEnd - sliceStart);
+ firstPaddingSourceIndex = sliceEnd;
+ }
+ paddingCount++;
+ // It could have been an escaped equals (%3D).
+ if (originalChar == equals) continue;
+ }
+ if (value != _Base64Decoder._invalid) {
+ buffer ??= new StringBuffer();
+ buffer.write(source.substring(sliceStart, sliceEnd));
+ buffer.writeCharCode(char);
+ sliceStart = i;
+ continue;
+ }
+ }
+ throw new FormatException("Invalid base64 data", source, sliceEnd);
+ }
+ if (buffer != null) {
+ buffer.write(source.substring(sliceStart, end));
+ if (firstPadding >= 0) {
+ // There was padding in the source. Check that it is valid:
+ // * result length a multiple of four
+ // * one or two padding characters at the end.
+ _checkPadding(source, firstPaddingSourceIndex, end,
+ firstPadding, paddingCount, buffer.length);
+ } else {
+ // Length of last chunk (1-4 chars) in the encoding.
+ int endLength = ((buffer.length - 1) % 4) + 1;
+ if (endLength == 1) {
+ // The data must have length 0, 2 or 3 modulo 4.
+ throw new FormatException("Invalid base64 encoding length ",
+ source, end);
+ }
+ while (endLength < 4) {
+ buffer.write("=");
+ endLength++;
+ }
+ }
+ return source.replaceRange(start, end, buffer.toString());
+ }
+ // Original was already normalized, only check padding.
+ int length = end - start;
+ if (firstPadding >= 0) {
+ _checkPadding(source, firstPaddingSourceIndex, end,
+ firstPadding, paddingCount, length);
+ } else {
+ // No padding given, so add some if needed it.
+ int endLength = length % 4;
+ if (endLength == 1) {
+ // The data must have length 0, 2 or 3 modulo 4.
+ throw new FormatException("Invalid base64 encoding length ",
+ source, end);
+ }
+ if (endLength > 1) {
+ // There is no "insertAt" on String, but this works as well.
+ source = source.replaceRange(end, end, (endLength == 2) ? "==" : "=");
+ }
+ }
+ return source;
+ }
+
+ static int _checkPadding(String source, int sourceIndex, int sourceEnd,
+ int firstPadding, int paddingCount, int length) {
+ if (length % 4 != 0) {
+ throw new FormatException(
+ "Invalid base64 padding, padded length must be multiple of four, "
+ "is $length",
+ source, sourceEnd);
+ }
+ if (firstPadding + paddingCount != length) {
+ throw new FormatException(
+ "Invalid base64 padding, '=' not at the end",
+ source, sourceIndex);
+ }
+ if (paddingCount > 2) {
+ throw new FormatException(
+ "Invalid base64 padding, more than two '=' characters",
+ source, sourceIndex);
+ }
+ }
}
// ------------------------------------------------------------------------
@@ -389,7 +523,7 @@ class _Base64Decoder {
*
* Accepts the "URL-safe" alphabet as well (`-` and `_` are the
* 62nd and 63rd alphabet characters), and considers `%` a padding
- * character, which mush then be followed by `3D`, the percent-escape
+ * character, which must then be followed by `3D`, the percent-escape
* for `=`.
*/
static final List<int> _inverseAlphabet = new Int8List.fromList([
« no previous file with comments | « CHANGELOG.md ('k') | sdk/lib/convert/convert.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698