From 07a23b9611cf5e39910e3a75fb272419c2849b16 Mon Sep 17 00:00:00 2001 From: Eike Kettner Date: Wed, 11 Dec 2019 21:56:31 +0100 Subject: [PATCH] Fix percent encoding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Must use utf8 bytes, of courseā€¦ --- .../scala/docspell/common/LenientUri.scala | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/modules/common/src/main/scala/docspell/common/LenientUri.scala b/modules/common/src/main/scala/docspell/common/LenientUri.scala index 4ea61a32..840ca9f2 100644 --- a/modules/common/src/main/scala/docspell/common/LenientUri.scala +++ b/modules/common/src/main/scala/docspell/common/LenientUri.scala @@ -8,6 +8,7 @@ import cats.data.NonEmptyList import cats.effect.{Blocker, ContextShift, Sync} import docspell.common.LenientUri.Path import io.circe.{Decoder, Encoder} +import scodec.bits.ByteVector /** A URI. * @@ -166,19 +167,28 @@ object LenientUri { } } - private[this] val delims: Set[Char] = ":/?#[]@".toSet + private[this] val delims: Set[Char] = ",/?:@&=+$# %".toSet - private def percentEncode(s: String): String = - s.flatMap(c => if (delims.contains(c)) s"%${c.toInt.toHexString}" else c.toString) + private def percent(s: String): String = + "%" + ByteVector.encodeUtf8(s). + fold(throw _, identity). + toHex - private def percentDecode(s: String): String = + def percentEncode(s: String): String = + s.flatMap(c => + if (delims.contains(c)) percent(c.toString) else c.toString) + + def percentDecode(s: String): String = if (!s.contains("%")) s - else s.foldLeft(("", "")) { case ((acc, res), c) => - if (acc.length == 2) ("", res :+ Integer.parseInt(acc.drop(1) :+ c, 16).toChar) - else if (acc.startsWith("%")) (acc :+ c, res) - else if (c == '%') ("%", res) - else (acc, res :+ c) - }._2 + else + s.foldLeft(("", ByteVector.empty)) { + case ((acc, res), c) => + if (acc.length == 2) ("", res ++ ByteVector.fromValidHex(acc.drop(1) + c)) + else if (acc.startsWith("%")) (acc :+ c, res) + else if (c == '%') ("%", res) + else (acc, res :+ c.toByte) + } + ._2.decodeUtf8.fold(throw _, identity) private def stripLeading(s: String, c: Char): String = if (s.length > 0 && s.charAt(0) == c) s.substring(1)