mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46:35 +03:00
refactor(strencode): rustify some strencode methods
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -489,6 +489,7 @@ dependencies = [
|
||||
"image-meta 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"imap 1.0.2 (git+https://github.com/jonhoo/rust-imap?rev=281d2eb8ab50dc656ceff2ae749ca5045f334e15)",
|
||||
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jetscii 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lettre 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1010,6 +1011,11 @@ name = "itoa"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "jetscii"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.0"
|
||||
@@ -2960,6 +2966,7 @@ dependencies = [
|
||||
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
|
||||
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
|
||||
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
|
||||
"checksum jetscii 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5f25cca2463cb19dbb1061eb3bd38a8b5e4ce1cc5a5a9fc0e02de486d92b9b05"
|
||||
"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
@@ -50,6 +50,7 @@ image-meta = "0.1.0"
|
||||
quick-xml = "0.15.0"
|
||||
escaper = "0.1.0"
|
||||
bitflags = "1.1.0"
|
||||
jetscii = "0.4.4"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0"
|
||||
|
||||
7
proptest-regressions/dc_strencode.txt
Normal file
7
proptest-regressions/dc_strencode.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
# Seeds for failure cases proptest has generated in the past. It is
|
||||
# automatically read and these particular cases re-run before any
|
||||
# novel cases are generated.
|
||||
#
|
||||
# It is recommended to check this file in to source control so that
|
||||
# everyone who runs the test benefits from these saved cases.
|
||||
cc 679506fe9ac59df773f8cfa800fdab5f0a32fe49d2ab370394000a1aa5bc2a72 # shrinks to buf = "%0A"
|
||||
@@ -1218,7 +1218,7 @@ unsafe fn build_body_file(
|
||||
/* create mime part, for Content-Disposition, see RFC 2183.
|
||||
`Content-Disposition: attachment` seems not to make a difference to `Content-Disposition: inline` at least on tested Thunderbird and Gma'l in 2017.
|
||||
But I've heard about problems with inline and outl'k, so we just use the attachment-type until we run into other problems ... */
|
||||
needs_ext = dc_needs_ext_header(filename_to_send);
|
||||
needs_ext = dc_needs_ext_header(as_str(filename_to_send));
|
||||
mime_fields = mailmime_fields_new_filename(
|
||||
MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int,
|
||||
if needs_ext {
|
||||
@@ -1255,7 +1255,7 @@ unsafe fn build_body_file(
|
||||
strdup(
|
||||
b"filename*\x00" as *const u8 as *const libc::c_char,
|
||||
),
|
||||
dc_encode_ext_header(filename_to_send),
|
||||
dc_encode_ext_header(as_str(filename_to_send)).strdup(),
|
||||
),
|
||||
);
|
||||
if !parm.is_null() {
|
||||
|
||||
@@ -1204,8 +1204,8 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
}
|
||||
if !filename_parts.is_empty() {
|
||||
free(desired_filename as *mut libc::c_void);
|
||||
let parts_c = CString::yolo(filename_parts);
|
||||
desired_filename = dc_decode_ext_header(parts_c.as_ptr());
|
||||
desired_filename =
|
||||
dc_decode_ext_header(filename_parts.as_bytes()).strdup();
|
||||
}
|
||||
if desired_filename.is_null() {
|
||||
let param = mailmime_find_ct_parameter(
|
||||
|
||||
@@ -1,83 +1,17 @@
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
|
||||
use charset::Charset;
|
||||
use mmime::mailmime_decode::*;
|
||||
use mmime::mmapstring::*;
|
||||
use mmime::other::*;
|
||||
use percent_encoding::{percent_decode, utf8_percent_encode, AsciiSet, CONTROLS};
|
||||
|
||||
use crate::dc_tools::*;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
#[inline]
|
||||
fn isalnum(c: libc::c_int) -> libc::c_int {
|
||||
if c < std::u8::MAX as libc::c_int {
|
||||
(c as u8 as char).is_ascii_alphanumeric() as libc::c_int
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/* ******************************************************************************
|
||||
* URL encoding and decoding, RFC 3986
|
||||
******************************************************************************/
|
||||
unsafe fn int_2_uppercase_hex(code: libc::c_char) -> libc::c_char {
|
||||
static mut HEX: [libc::c_char; 17] = [
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 0,
|
||||
];
|
||||
|
||||
HEX[(code as libc::c_int & 15i32) as usize]
|
||||
}
|
||||
|
||||
pub unsafe fn dc_urldecode(to_decode: *const libc::c_char) -> *mut libc::c_char {
|
||||
let mut pstr: *const libc::c_char = to_decode;
|
||||
if to_decode.is_null() {
|
||||
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
|
||||
}
|
||||
let buf: *mut libc::c_char = malloc(strlen(to_decode).wrapping_add(1)) as *mut libc::c_char;
|
||||
let mut pbuf: *mut libc::c_char = buf;
|
||||
assert!(!buf.is_null());
|
||||
|
||||
while 0 != *pstr {
|
||||
if *pstr as libc::c_int == '%' as i32 {
|
||||
if 0 != *pstr.offset(1isize) as libc::c_int && 0 != *pstr.offset(2isize) as libc::c_int
|
||||
{
|
||||
let fresh5 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh5 = ((hex_2_int(*pstr.offset(1isize)) as libc::c_int) << 4i32
|
||||
| hex_2_int(*pstr.offset(2isize)) as libc::c_int)
|
||||
as libc::c_char;
|
||||
pstr = pstr.offset(2isize)
|
||||
}
|
||||
} else if *pstr as libc::c_int == '+' as i32 {
|
||||
let fresh6 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh6 = ' ' as i32 as libc::c_char
|
||||
} else {
|
||||
let fresh7 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh7 = *pstr
|
||||
}
|
||||
pstr = pstr.offset(1isize)
|
||||
}
|
||||
*pbuf = '\u{0}' as i32 as libc::c_char;
|
||||
|
||||
buf
|
||||
}
|
||||
|
||||
fn hex_2_int(ch: libc::c_char) -> libc::c_char {
|
||||
let ch = ch as u8 as char;
|
||||
if !ch.is_ascii_hexdigit() {
|
||||
return (ch.to_ascii_lowercase() as i32 - 'a' as i32 + 10) as libc::c_char;
|
||||
}
|
||||
|
||||
match ch.to_digit(16) {
|
||||
Some(res) => res as libc::c_char,
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_encode_header_words(to_encode: *const libc::c_char) -> *mut libc::c_char {
|
||||
let mut ok_to_continue = true;
|
||||
let mut ret_str: *mut libc::c_char = ptr::null_mut();
|
||||
@@ -290,374 +224,63 @@ pub unsafe fn dc_decode_header_words(in_0: *const libc::c_char) -> *mut libc::c_
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
unsafe fn dc_encode_modified_utf7(
|
||||
mut to_encode: *const libc::c_char,
|
||||
change_spaces: libc::c_int,
|
||||
) -> *mut libc::c_char {
|
||||
let mut utf8pos: libc::c_uint;
|
||||
let mut utf8total: libc::c_uint;
|
||||
let mut c: libc::c_uint;
|
||||
let mut utf7mode: libc::c_uint;
|
||||
let mut bitstogo: libc::c_uint;
|
||||
let mut utf16flag: libc::c_uint;
|
||||
let mut ucs4: libc::c_ulong = 0;
|
||||
let mut bitbuf: libc::c_ulong = 0;
|
||||
let mut dst: *mut libc::c_char;
|
||||
let res: *mut libc::c_char;
|
||||
if to_encode.is_null() {
|
||||
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
|
||||
}
|
||||
res = malloc(2usize.wrapping_mul(strlen(to_encode)).wrapping_add(1)) as *mut libc::c_char;
|
||||
dst = res;
|
||||
assert!(!dst.is_null());
|
||||
pub fn dc_needs_ext_header(to_check: impl AsRef<str>) -> bool {
|
||||
let to_check = to_check.as_ref();
|
||||
|
||||
utf7mode = 0i32 as libc::c_uint;
|
||||
utf8total = 0i32 as libc::c_uint;
|
||||
bitstogo = 0i32 as libc::c_uint;
|
||||
utf8pos = 0i32 as libc::c_uint;
|
||||
loop {
|
||||
c = *to_encode as libc::c_uchar as libc::c_uint;
|
||||
if !(c != '\u{0}' as i32 as libc::c_uint) {
|
||||
break;
|
||||
}
|
||||
to_encode = to_encode.offset(1isize);
|
||||
// normal character?
|
||||
if c >= ' ' as i32 as libc::c_uint
|
||||
&& c <= '~' as i32 as libc::c_uint
|
||||
&& (c != '_' as i32 as libc::c_uint || 0 == change_spaces)
|
||||
{
|
||||
if 0 != utf7mode {
|
||||
if 0 != bitstogo {
|
||||
let fresh8 = dst;
|
||||
dst = dst.offset(1);
|
||||
*fresh8 = BASE64CHARS
|
||||
[(bitbuf << (6i32 as libc::c_uint).wrapping_sub(bitstogo) & 0x3f) as usize]
|
||||
}
|
||||
let fresh9 = dst;
|
||||
dst = dst.offset(1);
|
||||
*fresh9 = '-' as i32 as libc::c_char;
|
||||
utf7mode = 0i32 as libc::c_uint;
|
||||
utf8pos = 0i32 as libc::c_uint;
|
||||
bitstogo = 0i32 as libc::c_uint;
|
||||
utf8total = 0i32 as libc::c_uint
|
||||
}
|
||||
if 0 != change_spaces && c == ' ' as i32 as libc::c_uint {
|
||||
let fresh10 = dst;
|
||||
dst = dst.offset(1);
|
||||
*fresh10 = '_' as i32 as libc::c_char
|
||||
} else {
|
||||
let fresh11 = dst;
|
||||
dst = dst.offset(1);
|
||||
*fresh11 = c as libc::c_char
|
||||
}
|
||||
if c == '&' as i32 as libc::c_uint {
|
||||
let fresh12 = dst;
|
||||
dst = dst.offset(1);
|
||||
*fresh12 = '-' as i32 as libc::c_char
|
||||
}
|
||||
} else {
|
||||
if 0 == utf7mode {
|
||||
let fresh13 = dst;
|
||||
dst = dst.offset(1);
|
||||
*fresh13 = '&' as i32 as libc::c_char;
|
||||
utf7mode = 1i32 as libc::c_uint
|
||||
}
|
||||
// encode ascii characters as themselves
|
||||
if c < 0x80i32 as libc::c_uint {
|
||||
ucs4 = c as libc::c_ulong
|
||||
} else if 0 != utf8total {
|
||||
ucs4 = ucs4 << 6i32 | c as libc::c_ulong & 0x3f;
|
||||
utf8pos = utf8pos.wrapping_add(1);
|
||||
if utf8pos < utf8total {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
utf8pos = 1i32 as libc::c_uint;
|
||||
if c < 0xe0i32 as libc::c_uint {
|
||||
utf8total = 2i32 as libc::c_uint;
|
||||
ucs4 = (c & 0x1fi32 as libc::c_uint) as libc::c_ulong
|
||||
} else if c < 0xf0i32 as libc::c_uint {
|
||||
utf8total = 3i32 as libc::c_uint;
|
||||
ucs4 = (c & 0xfi32 as libc::c_uint) as libc::c_ulong
|
||||
} else {
|
||||
utf8total = 4i32 as libc::c_uint;
|
||||
ucs4 = (c & 0x3i32 as libc::c_uint) as libc::c_ulong
|
||||
}
|
||||
continue;
|
||||
}
|
||||
utf8total = 0i32 as libc::c_uint;
|
||||
loop {
|
||||
if ucs4 >= 0x10000 {
|
||||
ucs4 = ucs4.wrapping_sub(0x10000);
|
||||
bitbuf = bitbuf << 16 | (ucs4 >> 10).wrapping_add(0xd800);
|
||||
ucs4 = (ucs4 & 0x3ff).wrapping_add(0xdc00);
|
||||
utf16flag = 1i32 as libc::c_uint
|
||||
} else {
|
||||
bitbuf = bitbuf << 16 | ucs4;
|
||||
utf16flag = 0i32 as libc::c_uint
|
||||
}
|
||||
bitstogo = bitstogo.wrapping_add(16i32 as libc::c_uint);
|
||||
while bitstogo >= 6i32 as libc::c_uint {
|
||||
bitstogo = bitstogo.wrapping_sub(6i32 as libc::c_uint);
|
||||
let fresh14 = dst;
|
||||
dst = dst.offset(1);
|
||||
*fresh14 = BASE64CHARS[(if 0 != bitstogo {
|
||||
bitbuf >> bitstogo
|
||||
} else {
|
||||
bitbuf
|
||||
} & 0x3f) as usize]
|
||||
}
|
||||
if !(0 != utf16flag) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if to_check.is_empty() {
|
||||
return false;
|
||||
}
|
||||
if 0 != utf7mode {
|
||||
if 0 != bitstogo {
|
||||
let fresh15 = dst;
|
||||
dst = dst.offset(1);
|
||||
*fresh15 = BASE64CHARS
|
||||
[(bitbuf << (6i32 as libc::c_uint).wrapping_sub(bitstogo) & 0x3f) as usize]
|
||||
}
|
||||
let fresh16 = dst;
|
||||
dst = dst.offset(1);
|
||||
*fresh16 = '-' as i32 as libc::c_char
|
||||
}
|
||||
*dst = '\u{0}' as i32 as libc::c_char;
|
||||
|
||||
res
|
||||
to_check.chars().any(|c| {
|
||||
!(c.is_ascii_alphanumeric()
|
||||
|| c == '-'
|
||||
|| c == '_'
|
||||
|| c == '_'
|
||||
|| c == '.'
|
||||
|| c == '~'
|
||||
|| c == '%')
|
||||
})
|
||||
}
|
||||
|
||||
/* ******************************************************************************
|
||||
* Encode/decode modified UTF-7 as needed for IMAP, see RFC 2192
|
||||
******************************************************************************/
|
||||
const EXT_ASCII_ST: &AsciiSet = &CONTROLS
|
||||
.add(b' ')
|
||||
.add(b'-')
|
||||
.add(b'_')
|
||||
.add(b'.')
|
||||
.add(b'~')
|
||||
.add(b'%');
|
||||
|
||||
// UTF7 modified base64 alphabet
|
||||
#[cfg(test)]
|
||||
static mut BASE64CHARS: [libc::c_char; 65] = [
|
||||
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
|
||||
89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
|
||||
115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 44, 0,
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
unsafe fn dc_decode_modified_utf7(
|
||||
to_decode: *const libc::c_char,
|
||||
change_spaces: libc::c_int,
|
||||
) -> *mut libc::c_char {
|
||||
let mut c: libc::c_uint;
|
||||
let mut i: libc::c_uint;
|
||||
let mut bitcount: libc::c_uint;
|
||||
let mut ucs4: libc::c_ulong;
|
||||
let mut utf16: libc::c_ulong;
|
||||
let mut bitbuf: libc::c_ulong;
|
||||
let mut base64: [libc::c_uchar; 256] = [0; 256];
|
||||
let mut src: *const libc::c_char;
|
||||
let mut dst: *mut libc::c_char;
|
||||
let res: *mut libc::c_char;
|
||||
if to_decode.is_null() {
|
||||
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
|
||||
}
|
||||
res = malloc(4usize.wrapping_mul(strlen(to_decode)).wrapping_add(1)) as *mut libc::c_char;
|
||||
dst = res;
|
||||
src = to_decode;
|
||||
assert!(!dst.is_null());
|
||||
|
||||
libc::memset(
|
||||
base64.as_mut_ptr() as *mut libc::c_void,
|
||||
64,
|
||||
::std::mem::size_of::<[libc::c_uchar; 256]>(),
|
||||
);
|
||||
i = 0i32 as libc::c_uint;
|
||||
while (i as libc::c_ulong) < ::std::mem::size_of::<[libc::c_char; 65]>() as libc::c_ulong {
|
||||
base64[BASE64CHARS[i as usize] as libc::c_uint as usize] = i as libc::c_uchar;
|
||||
i = i.wrapping_add(1)
|
||||
}
|
||||
while *src as libc::c_int != '\u{0}' as i32 {
|
||||
let fresh17 = src;
|
||||
src = src.offset(1);
|
||||
c = *fresh17 as libc::c_uint;
|
||||
if c != '&' as i32 as libc::c_uint || *src as libc::c_int == '-' as i32 {
|
||||
if 0 != change_spaces && c == '_' as i32 as libc::c_uint {
|
||||
let fresh18 = dst;
|
||||
dst = dst.offset(1);
|
||||
*fresh18 = ' ' as i32 as libc::c_char
|
||||
} else {
|
||||
let fresh19 = dst;
|
||||
dst = dst.offset(1);
|
||||
*fresh19 = c as libc::c_char
|
||||
}
|
||||
if c == '&' as i32 as libc::c_uint {
|
||||
src = src.offset(1isize)
|
||||
}
|
||||
} else {
|
||||
bitbuf = 0;
|
||||
bitcount = 0i32 as libc::c_uint;
|
||||
ucs4 = 0;
|
||||
loop {
|
||||
c = base64[*src as libc::c_uchar as usize] as libc::c_uint;
|
||||
if !(c != 64i32 as libc::c_uint) {
|
||||
break;
|
||||
}
|
||||
src = src.offset(1isize);
|
||||
bitbuf = bitbuf << 6i32 | c as libc::c_ulong;
|
||||
bitcount = bitcount.wrapping_add(6i32 as libc::c_uint);
|
||||
// enough bits for a UTF-16 character?
|
||||
if !(bitcount >= 16i32 as libc::c_uint) {
|
||||
continue;
|
||||
}
|
||||
bitcount = bitcount.wrapping_sub(16i32 as libc::c_uint);
|
||||
utf16 = if 0 != bitcount {
|
||||
bitbuf >> bitcount
|
||||
} else {
|
||||
bitbuf
|
||||
} & 0xffff;
|
||||
|
||||
// convert UTF16 to UCS4
|
||||
if utf16 >= 0xd800 && utf16 <= 0xdbff {
|
||||
ucs4 = utf16.wrapping_sub(0xd800) << 10i32
|
||||
} else {
|
||||
if utf16 >= 0xdc00 && utf16 <= 0xdfff {
|
||||
ucs4 = ucs4.wrapping_add(utf16.wrapping_sub(0xdc00).wrapping_add(0x10000))
|
||||
} else {
|
||||
ucs4 = utf16
|
||||
}
|
||||
if ucs4 <= 0x7f {
|
||||
*dst.offset(0isize) = ucs4 as libc::c_char;
|
||||
dst = dst.offset(1isize)
|
||||
} else if ucs4 <= 0x7ff {
|
||||
*dst.offset(0isize) = (0xc0 | ucs4 >> 6i32) as libc::c_char;
|
||||
*dst.offset(1isize) = (0x80 | ucs4 & 0x3f) as libc::c_char;
|
||||
dst = dst.offset(2isize)
|
||||
} else if ucs4 <= 0xffff {
|
||||
*dst.offset(0isize) = (0xe0 | ucs4 >> 12i32) as libc::c_char;
|
||||
*dst.offset(1isize) = (0x80 | ucs4 >> 6i32 & 0x3f) as libc::c_char;
|
||||
*dst.offset(2isize) = (0x80 | ucs4 & 0x3f) as libc::c_char;
|
||||
dst = dst.offset(3isize)
|
||||
} else {
|
||||
*dst.offset(0isize) = (0xf0 | ucs4 >> 18i32) as libc::c_char;
|
||||
*dst.offset(1isize) = (0x80 | ucs4 >> 12i32 & 0x3f) as libc::c_char;
|
||||
*dst.offset(2isize) = (0x80 | ucs4 >> 6i32 & 0x3f) as libc::c_char;
|
||||
*dst.offset(3isize) = (0x80 | ucs4 & 0x3f) as libc::c_char;
|
||||
dst = dst.offset(4isize)
|
||||
}
|
||||
}
|
||||
}
|
||||
if *src as libc::c_int == '-' as i32 {
|
||||
src = src.offset(1isize)
|
||||
}
|
||||
}
|
||||
}
|
||||
*dst = '\u{0}' as i32 as libc::c_char;
|
||||
|
||||
res
|
||||
/// Encode an UTF-8 string to the extended header format.
|
||||
pub fn dc_encode_ext_header(to_encode: impl AsRef<str>) -> String {
|
||||
let encoded = utf8_percent_encode(to_encode.as_ref(), &EXT_ASCII_ST);
|
||||
format!("utf-8''{}", encoded)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_needs_ext_header(mut to_check: *const libc::c_char) -> bool {
|
||||
if !to_check.is_null() {
|
||||
while 0 != *to_check {
|
||||
if 0 == isalnum(*to_check as libc::c_int)
|
||||
&& *to_check as libc::c_int != '-' as i32
|
||||
&& *to_check as libc::c_int != '_' as i32
|
||||
&& *to_check as libc::c_int != '.' as i32
|
||||
&& *to_check as libc::c_int != '~' as i32
|
||||
{
|
||||
return true;
|
||||
}
|
||||
to_check = to_check.offset(1isize)
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub unsafe fn dc_encode_ext_header(to_encode: *const libc::c_char) -> *mut libc::c_char {
|
||||
let mut pstr: *const libc::c_char = to_encode;
|
||||
if to_encode.is_null() {
|
||||
return dc_strdup(b"utf-8\'\'\x00" as *const u8 as *const libc::c_char);
|
||||
}
|
||||
let buf: *mut libc::c_char = malloc(
|
||||
strlen(b"utf-8\'\'\x00" as *const u8 as *const libc::c_char)
|
||||
.wrapping_add(strlen(to_encode).wrapping_mul(3))
|
||||
.wrapping_add(1),
|
||||
) as *mut libc::c_char;
|
||||
assert!(!buf.is_null());
|
||||
|
||||
let mut pbuf: *mut libc::c_char = buf;
|
||||
strcpy(pbuf, b"utf-8\'\'\x00" as *const u8 as *const libc::c_char);
|
||||
pbuf = pbuf.offset(strlen(pbuf) as isize);
|
||||
while 0 != *pstr {
|
||||
if 0 != isalnum(*pstr as libc::c_int)
|
||||
|| *pstr as libc::c_int == '-' as i32
|
||||
|| *pstr as libc::c_int == '_' as i32
|
||||
|| *pstr as libc::c_int == '.' as i32
|
||||
|| *pstr as libc::c_int == '~' as i32
|
||||
{
|
||||
let fresh20 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh20 = *pstr
|
||||
} else {
|
||||
let fresh21 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh21 = '%' as i32 as libc::c_char;
|
||||
let fresh22 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh22 = int_2_uppercase_hex((*pstr as libc::c_int >> 4i32) as libc::c_char);
|
||||
let fresh23 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh23 = int_2_uppercase_hex((*pstr as libc::c_int & 15i32) as libc::c_char)
|
||||
}
|
||||
pstr = pstr.offset(1isize)
|
||||
}
|
||||
*pbuf = '\u{0}' as i32 as libc::c_char;
|
||||
|
||||
buf
|
||||
}
|
||||
|
||||
pub unsafe fn dc_decode_ext_header(to_decode: *const libc::c_char) -> *mut libc::c_char {
|
||||
let mut decoded: *mut libc::c_char = ptr::null_mut();
|
||||
let mut charset: *mut libc::c_char = ptr::null_mut();
|
||||
let mut p2: *const libc::c_char;
|
||||
if !to_decode.is_null() {
|
||||
// get char set
|
||||
p2 = strchr(to_decode, '\'' as i32);
|
||||
if !(p2.is_null() || p2 == to_decode) {
|
||||
/*no empty charset allowed*/
|
||||
charset =
|
||||
dc_null_terminate(to_decode, p2.wrapping_offset_from(to_decode) as libc::c_int);
|
||||
p2 = p2.offset(1isize);
|
||||
/// Decode an extended-header-format strings to UTF-8.
|
||||
pub fn dc_decode_ext_header(to_decode: &[u8]) -> Cow<str> {
|
||||
if let Some(index) = bytes!(b'\'').find(to_decode) {
|
||||
let (charset, rest) = to_decode.split_at(index);
|
||||
if !charset.is_empty() {
|
||||
// skip language
|
||||
p2 = strchr(p2, '\'' as i32);
|
||||
if !p2.is_null() {
|
||||
p2 = p2.offset(1isize);
|
||||
decoded = dc_urldecode(p2);
|
||||
if !charset.is_null()
|
||||
&& strcmp(charset, b"utf-8\x00" as *const u8 as *const libc::c_char) != 0i32
|
||||
&& strcmp(charset, b"UTF-8\x00" as *const u8 as *const libc::c_char) != 0i32
|
||||
{
|
||||
if let Some(encoding) =
|
||||
Charset::for_label(CStr::from_ptr(charset).to_str().unwrap().as_bytes())
|
||||
{
|
||||
let data =
|
||||
std::slice::from_raw_parts(decoded as *const u8, strlen(decoded));
|
||||
if let Some(index2) = bytes!(b'\'').find(&rest[1..]) {
|
||||
let decoded = percent_decode(&rest[index2 + 2..]);
|
||||
|
||||
let (res, _, _) = encoding.decode(data);
|
||||
free(decoded as *mut _);
|
||||
let r = std::ffi::CString::new(res.as_bytes()).unwrap();
|
||||
decoded = dc_strdup(r.as_ptr());
|
||||
if charset != b"utf-8" && charset != b"UTF-8" {
|
||||
if let Some(encoding) = Charset::for_label(charset) {
|
||||
let bytes = decoded.collect::<Vec<u8>>();
|
||||
let (res, _, _) = encoding.decode(&bytes);
|
||||
return Cow::Owned(res.into_owned());
|
||||
} else {
|
||||
return decoded.decode_utf8_lossy();
|
||||
}
|
||||
} else {
|
||||
return decoded.decode_utf8_lossy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(charset as *mut libc::c_void);
|
||||
if !decoded.is_null() {
|
||||
decoded
|
||||
} else {
|
||||
dc_strdup(to_decode)
|
||||
}
|
||||
|
||||
String::from_utf8_lossy(to_decode)
|
||||
}
|
||||
|
||||
unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
|
||||
@@ -672,16 +295,8 @@ unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[test]
|
||||
fn test_isalnum() {
|
||||
assert_eq!(isalnum(0), 0);
|
||||
assert_eq!(isalnum('5' as libc::c_int), 1);
|
||||
assert_eq!(isalnum('Q' as libc::c_int), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_decode_header_words() {
|
||||
unsafe {
|
||||
@@ -742,99 +357,43 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_dc_encode_ext_header() {
|
||||
unsafe {
|
||||
let mut buf1 = dc_encode_ext_header(
|
||||
b"Bj\xc3\xb6rn Petersen\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
assert_eq!(
|
||||
CStr::from_ptr(buf1).to_str().unwrap(),
|
||||
"utf-8\'\'Bj%C3%B6rn%20Petersen"
|
||||
);
|
||||
let buf2 = dc_decode_ext_header(buf1);
|
||||
assert_eq!(
|
||||
strcmp(
|
||||
buf2,
|
||||
b"Bj\xc3\xb6rn Petersen\x00" as *const u8 as *const libc::c_char,
|
||||
),
|
||||
0
|
||||
);
|
||||
free(buf1 as *mut libc::c_void);
|
||||
free(buf2 as *mut libc::c_void);
|
||||
let buf1 = dc_encode_ext_header("Björn Petersen");
|
||||
assert_eq!(&buf1, "utf-8\'\'Bj%C3%B6rn%20Petersen");
|
||||
let buf2 = dc_decode_ext_header(buf1.as_bytes());
|
||||
assert_eq!(&buf2, "Björn Petersen",);
|
||||
|
||||
buf1 = dc_decode_ext_header(
|
||||
b"iso-8859-1\'en\'%A3%20rates\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
assert_eq!(
|
||||
strcmp(
|
||||
buf1,
|
||||
b"\xc2\xa3 rates\x00" as *const u8 as *const libc::c_char,
|
||||
),
|
||||
0
|
||||
);
|
||||
free(buf1 as *mut libc::c_void);
|
||||
let buf1 = dc_decode_ext_header(b"iso-8859-1\'en\'%A3%20rates");
|
||||
assert_eq!(buf1, "£ rates",);
|
||||
|
||||
buf1 = dc_decode_ext_header(b"wrong\'format\x00" as *const u8 as *const libc::c_char);
|
||||
assert_eq!(
|
||||
strcmp(
|
||||
buf1,
|
||||
b"wrong\'format\x00" as *const u8 as *const libc::c_char,
|
||||
),
|
||||
0
|
||||
);
|
||||
free(buf1 as *mut libc::c_void);
|
||||
let buf1 = dc_decode_ext_header(b"wrong\'format");
|
||||
assert_eq!(buf1, "wrong\'format",);
|
||||
|
||||
buf1 = dc_decode_ext_header(b"\'\'\x00" as *const u8 as *const libc::c_char);
|
||||
assert_eq!(
|
||||
strcmp(buf1, b"\'\'\x00" as *const u8 as *const libc::c_char),
|
||||
0
|
||||
);
|
||||
free(buf1 as *mut libc::c_void);
|
||||
let buf1 = dc_decode_ext_header(b"\'\'");
|
||||
assert_eq!(buf1, "\'\'");
|
||||
|
||||
buf1 = dc_decode_ext_header(b"x\'\'\x00" as *const u8 as *const libc::c_char);
|
||||
assert_eq!(strcmp(buf1, b"\x00" as *const u8 as *const libc::c_char), 0);
|
||||
free(buf1 as *mut libc::c_void);
|
||||
let buf1 = dc_decode_ext_header(b"x\'\'");
|
||||
assert_eq!(buf1, "");
|
||||
|
||||
buf1 = dc_decode_ext_header(b"\'\x00" as *const u8 as *const libc::c_char);
|
||||
assert_eq!(
|
||||
strcmp(buf1, b"\'\x00" as *const u8 as *const libc::c_char),
|
||||
0
|
||||
);
|
||||
free(buf1 as *mut libc::c_void);
|
||||
let buf1 = dc_decode_ext_header(b"\'");
|
||||
assert_eq!(buf1, "\'");
|
||||
|
||||
buf1 = dc_decode_ext_header(b"\x00" as *const u8 as *const libc::c_char);
|
||||
assert_eq!(strcmp(buf1, b"\x00" as *const u8 as *const libc::c_char), 0);
|
||||
free(buf1 as *mut libc::c_void);
|
||||
}
|
||||
let buf1 = dc_decode_ext_header(b"");
|
||||
assert_eq!(buf1, "");
|
||||
|
||||
// regressions
|
||||
assert_eq!(
|
||||
dc_decode_ext_header(dc_encode_ext_header("%0A").as_bytes()),
|
||||
"%0A"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_needs_ext_header() {
|
||||
unsafe {
|
||||
assert_eq!(
|
||||
dc_needs_ext_header(b"Bj\xc3\xb6rn\x00" as *const u8 as *const libc::c_char),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
dc_needs_ext_header(b"Bjoern\x00" as *const u8 as *const libc::c_char),
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
dc_needs_ext_header(b"\x00" as *const u8 as *const libc::c_char),
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
dc_needs_ext_header(b" \x00" as *const u8 as *const libc::c_char),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
dc_needs_ext_header(b"a b\x00" as *const u8 as *const libc::c_char),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
dc_needs_ext_header(0 as *const u8 as *const libc::c_char),
|
||||
false
|
||||
);
|
||||
}
|
||||
assert_eq!(dc_needs_ext_header("Björn"), true);
|
||||
assert_eq!(dc_needs_ext_header("Bjoern"), false);
|
||||
assert_eq!(dc_needs_ext_header(""), false);
|
||||
assert_eq!(dc_needs_ext_header(" "), true);
|
||||
assert_eq!(dc_needs_ext_header("a b"), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -849,69 +408,20 @@ mod tests {
|
||||
assert_eq!(to_string(hex.as_ptr() as *const _), "=3A");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_urlencode_urldecode() {
|
||||
unsafe {
|
||||
let buf1 = percent_encode(b"Bj\xc3\xb6rn Petersen", NON_ALPHANUMERIC)
|
||||
.to_string()
|
||||
.strdup();
|
||||
use proptest::prelude::*;
|
||||
|
||||
assert_eq!(
|
||||
CStr::from_ptr(buf1 as *const libc::c_char)
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"Bj%C3%B6rn%20Petersen"
|
||||
);
|
||||
|
||||
let buf2 = dc_urldecode(buf1);
|
||||
|
||||
assert_eq!(
|
||||
strcmp(
|
||||
buf2,
|
||||
b"Bj\xc3\xb6rn Petersen\x00" as *const u8 as *const libc::c_char
|
||||
),
|
||||
0
|
||||
);
|
||||
|
||||
free(buf1 as *mut libc::c_void);
|
||||
free(buf2 as *mut libc::c_void);
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_ext_header_roundtrip(buf: String) {
|
||||
let encoded = dc_encode_ext_header(&buf);
|
||||
let decoded = dc_decode_ext_header(encoded.as_bytes());
|
||||
assert_eq!(buf, decoded);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_encode_decode_modified_utf7() {
|
||||
unsafe {
|
||||
let buf1 = dc_encode_modified_utf7(
|
||||
b"Bj\xc3\xb6rn Petersen\x00" as *const u8 as *const libc::c_char,
|
||||
1,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
CStr::from_ptr(buf1 as *const libc::c_char)
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"Bj&APY-rn_Petersen"
|
||||
);
|
||||
|
||||
let buf2 = dc_decode_modified_utf7(buf1, 1);
|
||||
|
||||
assert_eq!(
|
||||
strcmp(
|
||||
buf2,
|
||||
b"Bj\xc3\xb6rn Petersen\x00" as *const u8 as *const libc::c_char
|
||||
),
|
||||
0
|
||||
);
|
||||
|
||||
free(buf1 as *mut libc::c_void);
|
||||
free(buf2 as *mut libc::c_void);
|
||||
#[test]
|
||||
fn test_ext_header_decode_anything(buf: Vec<u8>) {
|
||||
// make sure this never panics
|
||||
let _decoded = dc_decode_ext_header(&buf);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_hex_to_int() {
|
||||
assert_eq!(hex_2_int(b'A' as libc::c_char), 10);
|
||||
assert_eq!(hex_2_int(b'a' as libc::c_char), 10);
|
||||
assert_eq!(hex_2_int(b'4' as libc::c_char), 4);
|
||||
assert_eq!(hex_2_int(b'K' as libc::c_char), 20);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ extern crate rusqlite;
|
||||
extern crate strum;
|
||||
#[macro_use]
|
||||
extern crate strum_macros;
|
||||
#[macro_use]
|
||||
extern crate jetscii;
|
||||
|
||||
#[macro_use]
|
||||
mod log;
|
||||
|
||||
Reference in New Issue
Block a user