WIP: Add ICU bindings for stringprep, idna2008 and spoof checker.

This commit is contained in:
Emmanuel Gil Peyrot 2019-12-02 02:57:20 +01:00 committed by Maxime “pep” Buquet
commit 6eb25755a3
12 changed files with 671 additions and 2 deletions

88
icu/src/stringprep.rs Normal file
View file

@ -0,0 +1,88 @@
//! Crate wrapping what we need from ICUs C API for JIDs.
//!
//! See http://site.icu-project.org/
use crate::bindings::{
icu_stringprep_open, icu_stringprep_prepare, UChar, UErrorCode, UStringPrepProfile,
UStringPrepProfileType, USPREP_ALLOW_UNASSIGNED, USPREP_DEFAULT, U_ZERO_ERROR,
};
use crate::error::Error;
use crate::Strict;
use std::ptr::null_mut;
/// Struct representing a given stringprep profile.
pub struct Stringprep {
inner: *mut UStringPrepProfile,
}
impl Stringprep {
/// Create a new Stringprep struct for the given profile.
pub(crate) fn new(profile: UStringPrepProfileType) -> Result<Stringprep, UErrorCode> {
let mut err: UErrorCode = U_ZERO_ERROR;
let inner = unsafe { icu_stringprep_open(profile, &mut err) };
match err {
U_ZERO_ERROR => Ok(Stringprep { inner }),
err => Err(err),
}
}
/// Perform a stringprep operation using this profile.
///
/// # Panics
/// Panics if ICU doesnt return a valid UTF-16 string, which should never happen.
pub fn stringprep(&self, input: &str, strict: Strict) -> Result<String, Error> {
if input.len() > 1023 {
return Err(Error::TooLong);
}
// ICU works on UTF-16 data, so convert it first.
let unprepped: Vec<UChar> = input.encode_utf16().collect();
// Now do the actual stringprep operation.
let mut prepped: Vec<UChar> = vec![0u16; 1024];
let flags = match strict {
Strict::True => USPREP_DEFAULT,
Strict::AllowUnassigned => USPREP_ALLOW_UNASSIGNED,
};
self.prepare(&unprepped, &mut prepped, flags)?;
// And then convert it back to UTF-8.
let output = std::char::decode_utf16(prepped.into_iter())
//.map(Result::unwrap)
.try_fold(Vec::new(), |mut acc, c| match c {
Ok(c) => {
acc.push(c);
Ok(acc)
}
Err(err) => Err(err),
})?;
let output: String = output.into_iter().collect();
if output.len() > 1023 {
return Err(Error::TooLong);
}
Ok(output)
}
fn prepare(&self, input: &[UChar], buf: &mut Vec<UChar>, flags: i32) -> Result<(), UErrorCode> {
let mut err: UErrorCode = U_ZERO_ERROR;
let prepped_len = unsafe {
icu_stringprep_prepare(
self.inner,
input.as_ptr(),
input.len() as i32,
buf.as_mut_ptr(),
buf.len() as i32,
flags,
null_mut(),
&mut err,
)
};
if err != U_ZERO_ERROR {
return Err(err);
}
buf.truncate(prepped_len as usize);
Ok(())
}
}