157 lines
6.2 KiB
Kotlin
157 lines
6.2 KiB
Kotlin
/*
|
|
* Copyright 2023 dorkbox, llc
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2005-2012, Paul Tuckey
|
|
* All rights reserved.
|
|
* ====================================================================
|
|
* Licensed under the BSD License. Text as follows.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* - Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
* - Neither the name tuckey.org nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
* ====================================================================
|
|
*
|
|
* https://www.talisman.org/%7Eerlkonig/misc/lunatech%5Ewhat-every-webdev-must-know-about-url-encoding/
|
|
*/
|
|
package dorkbox.netUtil.web
|
|
|
|
|
|
import java.io.UnsupportedEncodingException
|
|
import java.net.URISyntaxException
|
|
import java.nio.charset.Charset
|
|
|
|
object URLDecoder {
|
|
private const val byte_0 = '0'.code.toByte()
|
|
private const val byte_1 = '1'.code.toByte()
|
|
private const val byte_2 = '2'.code.toByte()
|
|
private const val byte_3 = '3'.code.toByte()
|
|
private const val byte_4 = '4'.code.toByte()
|
|
private const val byte_5 = '5'.code.toByte()
|
|
private const val byte_6 = '6'.code.toByte()
|
|
private const val byte_7= '7'.code.toByte()
|
|
private const val byte_8 = '8'.code.toByte()
|
|
private const val byte_9 = '9'.code.toByte()
|
|
private const val byte_a = 'a'.code.toByte()
|
|
private const val byte_b = 'b'.code.toByte()
|
|
private const val byte_c = 'c'.code.toByte()
|
|
private const val byte_d = 'd'.code.toByte()
|
|
private const val byte_e = 'e'.code.toByte()
|
|
private const val byte_f = 'f'.code.toByte()
|
|
private const val byte_A = 'A'.code.toByte()
|
|
private const val byteB = 'B'.code.toByte()
|
|
private const val byteC = 'C'.code.toByte()
|
|
private const val byte_D= 'D'.code.toByte()
|
|
private const val byte_E= 'E'.code.toByte()
|
|
private const val byte_F = 'F'.code.toByte()
|
|
|
|
@Throws(URISyntaxException::class)
|
|
fun decodeURL(url: String, charset: Charset): String {
|
|
val queryPart = url.indexOf('?')
|
|
var query: String? = null
|
|
var path = url
|
|
if (queryPart != -1) {
|
|
query = url.substring(queryPart + 1)
|
|
path = url.substring(0, queryPart)
|
|
}
|
|
val decodedPath = decodePath(path, charset)
|
|
return if (query != null) decodedPath + '?' + decodeQuery(query, charset) else decodedPath
|
|
}
|
|
|
|
@Throws(URISyntaxException::class)
|
|
fun decodePath(path: String, charset: Charset): String {
|
|
return decodeURLEncoded(path, false, charset)
|
|
}
|
|
|
|
@Throws(URISyntaxException::class)
|
|
fun decodeQuery(query: String, charset: Charset): String {
|
|
return decodeURLEncoded(query, true, charset)
|
|
}
|
|
|
|
@Throws(URISyntaxException::class)
|
|
fun decodeURLEncoded(part: String, query: Boolean, charset: Charset): String {
|
|
return try {
|
|
val ascii = part.toByteArray(Charsets.US_ASCII)
|
|
val decoded = ByteArray(ascii.size)
|
|
var j = 0
|
|
var i = 0
|
|
while (i < ascii.size) {
|
|
if (ascii[i] == '%'.code.toByte()) {
|
|
if (i + 2 >= ascii.size) throw URISyntaxException(part, "Invalid URL-encoded string at char $i")
|
|
// get the next two bytes
|
|
val first = ascii[++i]
|
|
val second = ascii[++i]
|
|
decoded[j] = (hexToByte(first) * 16 + hexToByte(second)).toByte()
|
|
} else if (query && ascii[i] == '+'.code.toByte()) decoded[j] = ' '.code.toByte() else decoded[j] = ascii[i]
|
|
i++
|
|
j++
|
|
}
|
|
// now decode
|
|
String(decoded, 0, j, charset)
|
|
} catch (x: UnsupportedEncodingException) {
|
|
throw URISyntaxException(part, "Invalid encoding: $charset")
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Throws(URISyntaxException::class)
|
|
private fun hexToByte(b: Byte): Byte {
|
|
when (b) {
|
|
byte_0 -> return 0
|
|
byte_1 -> return 1
|
|
byte_2 -> return 2
|
|
byte_3 -> return 3
|
|
byte_4 -> return 4
|
|
byte_5 -> return 5
|
|
byte_6 -> return 6
|
|
byte_7 -> return 7
|
|
byte_8 -> return 8
|
|
byte_9 -> return 9
|
|
byte_a, byte_A -> return 10
|
|
byte_b, byteB -> return 11
|
|
byte_c, byteC -> return 12
|
|
byte_d, byte_D -> return 13
|
|
byte_e, byte_E -> return 14
|
|
byte_f, byte_F -> return 15
|
|
}
|
|
throw URISyntaxException(b.toString(), "Invalid URL-encoded string")
|
|
}
|
|
}
|