///|
/// Helper functions
///
///|
fn cd_parse_disposition_type(
s : String,
) -> Result[DispositionType, ContentDispositionError] {
match s {
"inline" => Ok(Inline)
"attachment" => Ok(Attachment)
"form-data" => Ok(FormData)
_ => Err(InvalidDispositionType)
}
}
///|
fn cd_split_params(input : String) -> Array[String] {
let mut result : Array[String] = []
let mut current = ""
let mut in_quotes = false
let mut escape_next = false
let mut idx = 0
let len = input.length()
while idx < len {
let ch = cd_char_at(input, idx)
if escape_next {
current = current + Int::unsafe_to_char(ch).to_string()
escape_next = false
idx = idx + 1
} else if ch == 92 && in_quotes { // \
current = current + "\\"
escape_next = true
idx = idx + 1
} else if ch == 34 { // "
current = current + "\""
in_quotes = !in_quotes
idx = idx + 1
} else if ch == 59 && !in_quotes { // ;
let new_result = []
let mut j = 0
while j < result.length() {
new_result.push(result[j])
j = j + 1
}
new_result.push(current)
result = new_result
current = ""
idx = idx + 1
} else {
current = current + Int::unsafe_to_char(ch).to_string()
idx = idx + 1
}
}
if current.length() > 0 {
let new_result = []
let mut j = 0
while j < result.length() {
new_result.push(result[j])
j = j + 1
}
new_result.push(current)
result = new_result
}
result
}
///|
fn cd_parse_param_value(
value : String,
) -> Result[String, ContentDispositionError] {
let s = cd_trim_string(value)
if s.length() > 0 && cd_char_at(s, 0) == 34 { // "
if s.length() >= 2 && cd_char_at(s, s.length() - 1) == 34 {
cd_parse_quoted_string(cd_string_slice(s, 1, s.length() - 1))
} else {
Err(InvalidParameter)
}
} else {
Ok(s)
}
}
///|
fn cd_parse_quoted_string(
s : String,
) -> Result[String, ContentDispositionError] {
let mut result = ""
let mut idx = 0
let len = s.length()
while idx < len {
let ch = cd_char_at(s, idx)
if ch == 92 { // \
if idx + 1 < len {
result = result +
Int::unsafe_to_char(cd_char_at(s, idx + 1)).to_string()
idx = idx + 2
} else {
return Err(InvalidParameter)
}
} else {
result = result + Int::unsafe_to_char(ch).to_string()
idx = idx + 1
}
}
Ok(result)
}
///|
fn cd_parse_ext_value(
value : String,
) -> Result[String, ContentDispositionError] {
let s = cd_trim_string(value)
// Find first '
let first_quote = cd_find_char(s, 39) // '
match first_quote {
None => return Err(InvalidExtValue)
Some(pos) => {
let charset = cd_string_slice(s, 0, pos)
let rest = cd_string_slice_from(s, pos + 1)
// Find second '
let second_quote = cd_find_char(rest, 39)
match second_quote {
None => return Err(InvalidExtValue)
Some(pos2) => {
// language is ignored
let encoded_value = cd_string_slice_from(rest, pos2 + 1)
// Only UTF-8 is supported
if cd_to_upper_case(charset) != "UTF-8" {
return Err(InvalidExtValue)
}
cd_percent_decode(encoded_value)
}
}
}
}
}
///|
fn cd_percent_decode(s : String) -> Result[String, ContentDispositionError] {
let mut bytes : Array[Int] = []
let mut idx = 0
let len = s.length()
while idx < len {
let ch = cd_char_at(s, idx)
if ch == 37 { // %
if idx + 2 < len {
let hex = cd_string_slice(s, idx + 1, idx + 3)
let byte_result = cd_parse_hex(hex)
match byte_result {
Ok(byte) => {
let new_bytes = []
let mut j = 0
while j < bytes.length() {
new_bytes.push(bytes[j])
j = j + 1
}
new_bytes.push(byte)
bytes = new_bytes
idx = idx + 3
}
Err(_) => return Err(InvalidExtValue)
}
} else {
return Err(InvalidExtValue)
}
} else {
let new_bytes = []
let mut j = 0
while j < bytes.length() {
new_bytes.push(bytes[j])
j = j + 1
}
new_bytes.push(ch)
bytes = new_bytes
idx = idx + 1
}
}
cd_utf8_from_bytes(bytes)
}
///|
fn cd_utf8_from_bytes(
bytes : Array[Int],
) -> Result[String, ContentDispositionError] {
let mut result = ""
let mut idx = 0
while idx < bytes.length() {
let b = bytes[idx]
if b < 128 {
// ASCII
result = result + Int::unsafe_to_char(b).to_string()
idx = idx + 1
} else if b >= 192 && b < 224 {
// 2-byte sequence
if idx + 1 < bytes.length() {
let b2 = bytes[idx + 1]
if b2 >= 128 && b2 < 192 {
let code = (b - 192) * 64 + (b2 - 128)
result = result + Int::unsafe_to_char(code).to_string()
idx = idx + 2
} else {
return Err(InvalidExtValue)
}
} else {
return Err(InvalidExtValue)
}
} else if b >= 224 && b < 240 {
// 3-byte sequence
if idx + 2 < bytes.length() {
let b2 = bytes[idx + 1]
let b3 = bytes[idx + 2]
if b2 >= 128 && b2 < 192 && b3 >= 128 && b3 < 192 {
let code = (b - 224) * 4096 + (b2 - 128) * 64 + (b3 - 128)
result = result + Int::unsafe_to_char(code).to_string()
idx = idx + 3
} else {
return Err(InvalidExtValue)
}
} else {
return Err(InvalidExtValue)
}
} else if b >= 240 && b < 248 {
// 4-byte sequence
if idx + 3 < bytes.length() {
let b2 = bytes[idx + 1]
let b3 = bytes[idx + 2]
let b4 = bytes[idx + 3]
if b2 >= 128 &&
b2 < 192 &&
b3 >= 128 &&
b3 < 192 &&
b4 >= 128 &&
b4 < 192 {
let code = (b - 240) * 262144 +
(b2 - 128) * 4096 +
(b3 - 128) * 64 +
(b4 - 128)
result = result + Int::unsafe_to_char(code).to_string()
idx = idx + 4
} else {
return Err(InvalidExtValue)
}
} else {
return Err(InvalidExtValue)
}
} else {
return Err(InvalidExtValue)
}
}
Ok(result)
}
///|
fn cd_parse_hex(s : String) -> Result[Int, ContentDispositionError] {
let mut result = 0
let mut idx = 0
while idx < s.length() {
let ch = cd_char_at(s, idx)
let digit = if ch >= 48 && ch <= 57 { // 0-9
ch - 48
} else if ch >= 65 && ch <= 70 { // A-F
ch - 55
} else if ch >= 97 && ch <= 102 { // a-f
ch - 87
} else {
return Err(InvalidExtValue)
}
result = result * 16 + digit
idx = idx + 1
}
Ok(result)
}
///|
fn cd_encode_ext_value(s : String) -> String {
let mut result = ""
let mut idx = 0
while idx < s.length() {
let ch = cd_char_at(s, idx)
if cd_is_attr_char(ch) {
result = result + Int::unsafe_to_char(ch).to_string()
} else {
result = result + "%" + cd_int_to_hex(ch, 2)
}
idx = idx + 1
}
result
}
///|
fn cd_is_attr_char(b : Int) -> Bool {
(b >= 65 && b <= 90) || // A-Z
(b >= 97 && b <= 122) || // a-z
(b >= 48 && b <= 57) || // 0-9
b == 33 ||
b == 35 ||
b == 36 ||
b == 38 ||
b == 43 ||
b == 45 ||
b == 46 ||
b == 94 ||
b == 95 ||
b == 96 ||
b == 124 ||
b == 126
}
///|
fn cd_escape_quoted_string(s : String) -> String {
let mut result = ""
let mut idx = 0
while idx < s.length() {
let ch = cd_char_at(s, idx)
if ch == 34 || ch == 92 { // " or \
result = result + "\\"
}
result = result + Int::unsafe_to_char(ch).to_string()
idx = idx + 1
}
result
}
///|
fn cd_int_to_hex(n : Int, padding : Int) -> String {
let chars = "0123456789ABCDEF"
let mut result = ""
let mut val = n
let mut len = 0
while val > 0 {
let digit = val % 16
result = Int::unsafe_to_char(cd_char_at(chars, digit)).to_string() + result
val = val / 16
len = len + 1
}
while len < padding {
result = "0" + result
len = len + 1
}
if result == "" {
"00"
} else {
result
}
}
///|
fn cd_to_upper_case(s : String) -> String {
let mut result = ""
let mut idx = 0
while idx < s.length() {
let ch = cd_char_at(s, idx)
if ch >= 97 && ch <= 122 { // a-z
result = result + Int::unsafe_to_char(ch - 32).to_string()
} else {
result = result + Int::unsafe_to_char(ch).to_string()
}
idx = idx + 1
}
result
}
///|
fn cd_to_lower_case(s : String) -> String {
str_to_lower_case(s)
}
///|
fn cd_trim_string(s : String) -> String {
str_trim_string(s)
}
///|
fn cd_char_at(s : String, idx : Int) -> Int {
str_char_at(s, idx)
}
///|
fn cd_find_char(s : String, target : Int) -> Int? {
let mut idx = 0
while idx < s.length() {
if cd_char_at(s, idx) == target {
return Some(idx)
}
idx = idx + 1
}
None
}
///|
fn cd_string_slice(s : String, start : Int, end : Int) -> String {
str_string_slice(s, start, end)
}
///|
fn cd_string_slice_from(s : String, start : Int) -> String {
str_string_slice_from(s, start)
}