mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-03-29 09:35:50 +00:00
Rust: new item module
This commit is contained in:
parent
cbb15e1895
commit
619373a8e8
2 changed files with 149 additions and 138 deletions
147
rust/src/item.rs
Normal file
147
rust/src/item.rs
Normal file
|
|
@ -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<String> {
|
||||
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<VdirsyncerComponent> = 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
|
||||
}
|
||||
140
rust/src/lib.rs
140
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<String> {
|
||||
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<VdirsyncerComponent> = 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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue