From 619373a8e89b02cdf9978c6ef323e854085a653e Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Wed, 11 Oct 2017 13:53:10 +0200 Subject: [PATCH] Rust: new item module --- rust/src/item.rs | 147 +++++++++++++++++++++++++++++++++++++++++++++++ rust/src/lib.rs | 140 +------------------------------------------- 2 files changed, 149 insertions(+), 138 deletions(-) create mode 100644 rust/src/item.rs diff --git a/rust/src/item.rs b/rust/src/item.rs new file mode 100644 index 0000000..eb4238d --- /dev/null +++ b/rust/src/item.rs @@ -0,0 +1,147 @@ +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; +use std::mem; +use std::ptr; + +use vobject; +use ring; + +use std::fmt::Write; +use VdirsyncerError; + + +const EMPTY_STRING: *const c_char = b"\0" as *const u8 as *const c_char; + +// Workaround to be able to use opaque pointer +#[repr(C)] +pub struct VdirsyncerComponent(vobject::Component); + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_get_uid(c: *mut VdirsyncerComponent) -> *const c_char { + match safe_get_uid(&(*c).0) { + Some(x) => CString::new(x).unwrap().into_raw(), + None => EMPTY_STRING + } +} + +#[inline] +fn safe_get_uid(c: &vobject::Component) -> Option { + let mut stack = vec![c]; + + while let Some(vobj) = stack.pop() { + if let Some(prop) = vobj.get_only("UID") { + return Some(prop.value_as_string()); + } + stack.extend(vobj.subcomponents.iter()); + }; + None +} + + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_parse_component(s: *const c_char, err: *mut VdirsyncerError) -> *mut VdirsyncerComponent { + let cstring = CStr::from_ptr(s); + match vobject::parse_component(cstring.to_str().unwrap()) { + Ok(x) => mem::transmute(Box::new(VdirsyncerComponent(x))), + Err(e) => { + (*err).failed = true; + (*err).msg = CString::new(e.into_string()).unwrap().into_raw(); + mem::zeroed() + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_free_component(c: *mut VdirsyncerComponent) { + let _: Box = mem::transmute(c); +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_clear_err(e: *mut VdirsyncerError) { + CString::from_raw((*e).msg); + (*e).msg = ptr::null_mut(); +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_change_uid(c: *mut VdirsyncerComponent, uid: *const c_char) { + let uid_cstring = CStr::from_ptr(uid); + change_uid(&mut (*c).0, uid_cstring.to_str().unwrap()); +} + +fn change_uid(c: &mut vobject::Component, uid: &str) { + let mut stack = vec![c]; + while let Some(component) = stack.pop() { + match component.name.as_ref() { + "VEVENT" | "VTODO" | "VJOURNAL" | "VCARD" => { + if !uid.is_empty() { + component.set(vobject::Property::new("UID", uid)); + } else { + component.remove("UID"); + } + }, + _ => () + } + + stack.extend(component.subcomponents.iter_mut()); + } +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_clone_component(c: *mut VdirsyncerComponent) -> *mut VdirsyncerComponent { + mem::transmute(Box::new((*c).0.clone())) +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_write_component(c: *mut VdirsyncerComponent) -> *const c_char { + CString::new(vobject::write_component(&(*c).0)).unwrap().into_raw() +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_hash_component(c: *mut VdirsyncerComponent) -> *const c_char { + CString::new(safe_hash_component(&(*c).0)).unwrap().into_raw() +} + +fn safe_hash_component(c: &vobject::Component) -> String { + let mut new_c = c.clone(); + { + let mut stack = vec![&mut new_c]; + while let Some(component) = stack.pop() { + // PRODID is changed by radicale for some reason after upload + component.remove("PRODID"); + // Sometimes METHOD:PUBLISH is added by WebCAL providers, for us it doesn't make a difference + component.remove("METHOD"); + // X-RADICALE-NAME is used by radicale, because hrefs don't really exist in their filesystem backend + component.remove("X-RADICALE-NAME"); + // Apparently this is set by Horde? + // https://github.com/pimutils/vdirsyncer/issues/318 + component.remove("X-WR-CALNAME"); + // Those are from the VCARD specification and is supposed to change when the + // item does -- however, we can determine that ourselves + component.remove("REV"); + component.remove("LAST-MODIFIED"); + component.remove("CREATED"); + // Some iCalendar HTTP calendars generate the DTSTAMP at request time, so + // this property always changes when the rest of the item didn't. Some do + // the same with the UID. + // + // - Google's read-only calendar links + // - http://www.feiertage-oesterreich.at/ + component.remove("DTSTAMP"); + component.remove("UID"); + + if component.name == "VCALENDAR" { + component.subcomponents.retain(|ref c| c.name != "VTIMEZONE"); + } + + stack.extend(component.subcomponents.iter_mut()); + } + } + + // FIXME: Possible optimization: Stream component to hasher instead of allocating new string + let digest = ring::digest::digest(&ring::digest::SHA256, vobject::write_component(&new_c).as_bytes()); + let mut rv = String::new(); + for &byte in digest.as_ref() { + write!(&mut rv, "{:x}", byte).unwrap(); + } + rv +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a1a5209..417b5ad 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,14 +1,10 @@ extern crate vobject; extern crate ring; -use std::ffi::{CStr, CString}; +use std::ffi::CStr; use std::os::raw::c_char; -use std::mem; -use std::ptr; -use std::fmt::Write; - -const EMPTY_STRING: *const c_char = b"\0" as *const u8 as *const c_char; +pub mod item; #[repr(C)] pub struct VdirsyncerError { @@ -16,140 +12,8 @@ pub struct VdirsyncerError { pub msg: *mut c_char, } -// Workaround to be able to use opaque pointer -#[repr(C)] -pub struct VdirsyncerComponent(vobject::Component); - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_get_uid(c: *mut VdirsyncerComponent) -> *const c_char { - match safe_get_uid(&(*c).0) { - Some(x) => CString::new(x).unwrap().into_raw(), - None => EMPTY_STRING - } -} - -#[inline] -fn safe_get_uid(c: &vobject::Component) -> Option { - let mut stack = vec![c]; - - while let Some(vobj) = stack.pop() { - if let Some(prop) = vobj.get_only("UID") { - return Some(prop.value_as_string()); - } - stack.extend(vobj.subcomponents.iter()); - }; - None -} #[no_mangle] pub unsafe extern "C" fn vdirsyncer_free_str(s: *const c_char) { CStr::from_ptr(s); } - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_parse_component(s: *const c_char, err: *mut VdirsyncerError) -> *mut VdirsyncerComponent { - let cstring = CStr::from_ptr(s); - match vobject::parse_component(cstring.to_str().unwrap()) { - Ok(x) => mem::transmute(Box::new(VdirsyncerComponent(x))), - Err(e) => { - (*err).failed = true; - (*err).msg = CString::new(e.into_string()).unwrap().into_raw(); - mem::zeroed() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_free_component(c: *mut VdirsyncerComponent) { - let _: Box = mem::transmute(c); -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_clear_err(e: *mut VdirsyncerError) { - CString::from_raw((*e).msg); - (*e).msg = ptr::null_mut(); -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_change_uid(c: *mut VdirsyncerComponent, uid: *const c_char) { - let uid_cstring = CStr::from_ptr(uid); - change_uid(&mut (*c).0, uid_cstring.to_str().unwrap()); -} - -fn change_uid(c: &mut vobject::Component, uid: &str) { - let mut stack = vec![c]; - while let Some(component) = stack.pop() { - match component.name.as_ref() { - "VEVENT" | "VTODO" | "VJOURNAL" | "VCARD" => { - if !uid.is_empty() { - component.set(vobject::Property::new("UID", uid)); - } else { - component.remove("UID"); - } - }, - _ => () - } - - stack.extend(component.subcomponents.iter_mut()); - } -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_clone_component(c: *mut VdirsyncerComponent) -> *mut VdirsyncerComponent { - mem::transmute(Box::new((*c).0.clone())) -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_write_component(c: *mut VdirsyncerComponent) -> *const c_char { - CString::new(vobject::write_component(&(*c).0)).unwrap().into_raw() -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_hash_component(c: *mut VdirsyncerComponent) -> *const c_char { - CString::new(safe_hash_component(&(*c).0)).unwrap().into_raw() -} - -fn safe_hash_component(c: &vobject::Component) -> String { - let mut new_c = c.clone(); - { - let mut stack = vec![&mut new_c]; - while let Some(component) = stack.pop() { - // PRODID is changed by radicale for some reason after upload - component.remove("PRODID"); - // Sometimes METHOD:PUBLISH is added by WebCAL providers, for us it doesn't make a difference - component.remove("METHOD"); - // X-RADICALE-NAME is used by radicale, because hrefs don't really exist in their filesystem backend - component.remove("X-RADICALE-NAME"); - // Apparently this is set by Horde? - // https://github.com/pimutils/vdirsyncer/issues/318 - component.remove("X-WR-CALNAME"); - // Those are from the VCARD specification and is supposed to change when the - // item does -- however, we can determine that ourselves - component.remove("REV"); - component.remove("LAST-MODIFIED"); - component.remove("CREATED"); - // Some iCalendar HTTP calendars generate the DTSTAMP at request time, so - // this property always changes when the rest of the item didn't. Some do - // the same with the UID. - // - // - Google's read-only calendar links - // - http://www.feiertage-oesterreich.at/ - component.remove("DTSTAMP"); - component.remove("UID"); - - if component.name == "VCALENDAR" { - component.subcomponents.retain(|ref c| c.name != "VTIMEZONE"); - } - - stack.extend(component.subcomponents.iter_mut()); - } - } - - // FIXME: Possible optimization: Stream component to hasher instead of allocating new string - let digest = ring::digest::digest(&ring::digest::SHA256, vobject::write_component(&new_c).as_bytes()); - let mut rv = String::new(); - for &byte in digest.as_ref() { - write!(&mut rv, "{:x}", byte).unwrap(); - } - rv -}