]> uap-core.de Git - note.git/commitdiff
improve dav error handling
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Wed, 24 Jun 2026 18:53:15 +0000 (20:53 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Wed, 24 Jun 2026 18:53:15 +0000 (20:53 +0200)
dav-rs/src/dav/ffi.rs
dav-rs/src/dav/resource.rs
dav-rs/src/dav/session.rs
libidav/session.c
libidav/webdav.h

index 2ac03914ece5a2271ef133b144c941aac5b97f2f..ed2d100ce8692af5a9f2f7550dde4efd2055e0f8 100644 (file)
@@ -68,3 +68,28 @@ pub type DavReadFunc = Option<extern "C" fn(data: *const c_void, size: libc::siz
 pub type DavWriteFunc = Option<extern "C" fn(data: *const c_void, size: libc::size_t, count: libc::size_t, stream: *mut c_void) -> libc::size_t>;
 pub type DavSeekFunc = Option<extern "C" fn(stream: *mut c_void, offset: c_long, whence: c_int) -> c_int>;
 
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum DavError {
+    DavOk = 0,
+    DavError,
+    DavNotFound,
+    DavUnauthorized,
+    DavForbidden,
+    DavMethodNotAllowed,
+    DavConflict,
+    DavLocked,
+    DavUnsupportedProtocol,
+    DavCouldntResolveProxy,
+    DavCouldntResolveHost,
+    DavCouldntConnect,
+    DavTimeout,
+    DavSslError,
+    DavQlError,
+    DavContentVerificationError,
+    DavPreconditionFailed,
+    DavRequestEntityTooLarge,
+    DavRequestUrlTooLong,
+    DavProxyAuthRequired,
+    DavNetAuthRequired,
+}
index c2e556f77e4959946a55e3227717e7dc36f547ef..536b434f8e169d56560abb38af54a012cc040ead 100644 (file)
@@ -36,7 +36,7 @@ use libc::size_t;
 use crate::dav::context::DavContext;
 use crate::dav::ffi;
 use crate::dav::ffi::{DavReadFunc, DavSeekFunc, DavWriteFunc};
-use crate::dav::session::Session;
+use crate::dav::session::{get_session_error, DavError, Session};
 
 pub struct Resource<'a> {
     pub base: ResourceRef<'a>
@@ -155,27 +155,60 @@ impl<'a> ResourceRef<'a> {
         }
     }
 
-    pub fn exists(&self) -> bool {
-        unsafe { dav_resource_exists(self.ptr) != 0 }
+    pub fn exists(&self) -> Result<bool, DavError> {
+        let res = unsafe { dav_resource_exists(self.ptr) != 0 };
+        if res {
+            Ok(res)
+        } else {
+            let err = unsafe { get_session_error((*self.ptr).session) };
+            match err {
+                DavError::NotFound => Ok(false),
+                _ => Err(err),
+            }
+        }
     }
 
-    pub fn create(&self) -> bool {
-        unsafe { dav_resource_create(self.ptr) == 0 }
+    pub fn create(&self) -> Result<(), DavError> {
+        unsafe {
+            if dav_resource_create(self.ptr) == 0 {
+                Ok(())
+            } else {
+                Err(get_session_error((*self.ptr).session))
+            }
+        }
     }
 
-    pub fn delete(&self) -> bool {
-        unsafe { dav_resource_delete(self.ptr) == 0 }
+    pub fn delete(&self) -> Result<(), DavError> {
+        unsafe {
+            if dav_resource_delete(self.ptr) == 0 {
+                Ok(())
+            } else {
+                Err(get_session_error((*self.ptr).session))
+            }
+        }
     }
 
-    pub fn load(&self) -> bool {
-        unsafe { dav_load(self.ptr) == 0 }
+    pub fn load(&self) -> Result<(), DavError> {
+        unsafe {
+            if dav_load(self.ptr) == 0 {
+                Ok(())
+            } else {
+                Err(get_session_error((*self.ptr).session))
+            }
+        }
     }
 
-    pub fn store(&self) -> bool {
-        unsafe { dav_store(self.ptr) == 0 }
+    pub fn store(&self) -> Result<(), DavError> {
+        unsafe {
+            if dav_store(self.ptr) == 0 {
+                Ok(())
+            } else {
+                Err(get_session_error((*self.ptr).session))
+            }
+        }
     }
 
-    pub fn store_with_data(&self, data: &[u8]) -> bool {
+    pub fn store_with_data(&self, data: &[u8]) -> Result<(), DavError> {
         unsafe {
             dav_set_content_data(
                 self.ptr,
@@ -183,13 +216,15 @@ impl<'a> ResourceRef<'a> {
                 data.len() as size_t,
             );
 
-            let res = dav_store(self.ptr);
-
-            res == 0
+            if dav_store(self.ptr) == 0 {
+                Ok(())
+            } else {
+                Err(get_session_error((*self.ptr).session))
+            }
         }
     }
 
-    pub fn store_with_seekable_stream<T: Read + Seek>(&self, stream: &mut T) -> bool {
+    pub fn store_with_seekable_stream<T: Read + Seek>(&self, stream: &mut T) -> Result<(), DavError> {
         let mut wrapper = DavSeekableInputStream { inner: stream };
 
         unsafe {
@@ -200,13 +235,15 @@ impl<'a> ResourceRef<'a> {
                 Some(seek_callback::<T>),
             );
 
-            let res = dav_store(self.ptr);
-
-            res == 0
+            if dav_store(self.ptr) == 0 {
+                Ok(())
+            } else {
+                Err(get_session_error((*self.ptr).session))
+            }
         }
     }
 
-    pub fn store_with_stream<T: Read>(&self, stream: &mut T) -> bool {
+    pub fn store_with_stream<T: Read>(&self, stream: &mut T) -> Result<(), DavError> {
         let mut wrapper = DavInputStream { inner: stream };
 
         unsafe {
@@ -217,36 +254,57 @@ impl<'a> ResourceRef<'a> {
                 None,
             );
 
-            let res = dav_store(self.ptr);
-
-            res == 0
+            if dav_store(self.ptr) == 0 {
+                Ok(())
+            } else {
+                Err(get_session_error((*self.ptr).session))
+            }
         }
     }
 
-    pub fn get_content<T: Write>(&self, stream: &mut T) -> bool {
+    pub fn get_content<T: Write>(&self, stream: &mut T) -> Result<(), DavError> {
         let mut wrapper = DavOutputStream { inner: stream };
-
-        let result = unsafe {
-            dav_get_content(
-                self.ptr,
-                &mut wrapper as *mut _ as *mut c_void,
-                Some(write_callback::<T>),
-            )
-        };
-
-        result == 0
+        unsafe {
+            let result = dav_get_content(
+                    self.ptr,
+                    &mut wrapper as *mut _ as *mut c_void,
+                    Some(write_callback::<T>));
+            if result == 0 {
+                Ok(())
+            } else {
+                Err(get_session_error((*self.ptr).session))
+            }
+        }
     }
 
-    pub fn lock(&self) -> bool {
-        unsafe { dav_lock(self.ptr) == 0 }
+    pub fn lock(&self) -> Result<(), DavError> {
+        unsafe {
+            if dav_lock(self.ptr) == 0 {
+                Ok(())
+            } else {
+                Err(get_session_error((*self.ptr).session))
+            }
+        }
     }
 
-    pub fn lock_t(&self, timeout: u64) -> bool {
-        unsafe { dav_lock_t(self.ptr, timeout as libc::time_t)  == 0 }
+    pub fn lock_t(&self, timeout: u64) -> Result<(), DavError> {
+        unsafe {
+            if dav_lock_t(self.ptr, timeout as libc::time_t) == 0 {
+                Ok(())
+            } else {
+                Err(get_session_error((*self.ptr).session))
+            }
+        }
     }
 
-    pub fn unlock(&self) -> bool {
-        unsafe { dav_unlock(self.ptr) == 0 }
+    pub fn unlock(&self) -> Result<(), DavError> {
+        unsafe {
+            if dav_unlock(self.ptr) == 0 {
+                Ok(())
+            } else {
+                Err(get_session_error((*self.ptr).session))
+            }
+        }
     }
 }
 
index f63c41e50f15e8c95a0e13ef3326228387d8d06e..d962d439fcac3b5e3fdee853d3db37b8746ea274 100644 (file)
@@ -27,7 +27,7 @@
  */
 #![allow(dead_code)]
 
-use std::ffi::{c_char, CString};
+use std::ffi::{c_char, CStr, CString};
 use crate::dav::context::DavContext;
 use crate::dav::ffi;
 
@@ -61,6 +61,10 @@ impl Session {
             dav_session_set_auth(self.ptr, user_cstr.as_ptr(), password_cstr.as_ptr());
         }
     }
+
+    pub fn get_error(&self) -> DavError {
+        get_session_error(self.ptr)
+    }
 }
 
 
@@ -81,6 +85,70 @@ impl DavContext {
     }
 }
 
+pub fn get_session_error(sn: *const ffi::DavSession) -> DavError {
+    let err = unsafe { dav_session_get_error(sn) };
+    let errstr = unsafe { dav_session_get_errorstr(sn) };
+
+    match err {
+        ffi::DavError::DavOk => DavError::Ok,
+        ffi::DavError::DavError => {
+            let str = if errstr.is_null() {
+                String::new()
+            } else {
+                unsafe {
+                    CStr::from_ptr(errstr)
+                        .to_string_lossy()
+                        .into_owned()
+                }
+            };
+            DavError::Error(str)
+        },
+        ffi::DavError::DavNotFound => DavError::NotFound,
+        ffi::DavError::DavUnauthorized => DavError::Unauthorized,
+        ffi::DavError::DavForbidden => DavError::Forbidden,
+        ffi::DavError::DavMethodNotAllowed => DavError::MethodNotAllowed,
+        ffi::DavError::DavConflict => DavError::Conflict,
+        ffi::DavError::DavLocked => DavError::Locked,
+        ffi::DavError::DavUnsupportedProtocol => DavError::UnsupportedProtocol,
+        ffi::DavError::DavCouldntResolveProxy => DavError::CouldntResolveProxy,
+        ffi::DavError::DavCouldntResolveHost => DavError::CouldntResolveHost,
+        ffi::DavError::DavCouldntConnect => DavError::CouldntConnect,
+        ffi::DavError::DavTimeout => DavError::Timeout,
+        ffi::DavError::DavSslError => DavError::SslError,
+        ffi::DavError::DavQlError => DavError::QlError,
+        ffi::DavError::DavContentVerificationError => DavError::ContentVerificationError,
+        ffi::DavError::DavPreconditionFailed => DavError::PreconditionFailed,
+        ffi::DavError::DavRequestEntityTooLarge => DavError::RequestEntityTooLarge,
+        ffi::DavError::DavRequestUrlTooLong => DavError::RequestUrlTooLong,
+        ffi::DavError::DavProxyAuthRequired => DavError::ProxyAuthRequired,
+        ffi::DavError::DavNetAuthRequired => DavError::NetAuthRequired,
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum DavError {
+    Ok,
+    Error(String),
+    NotFound,
+    Unauthorized,
+    Forbidden,
+    MethodNotAllowed,
+    Conflict,
+    Locked,
+    UnsupportedProtocol,
+    CouldntResolveProxy,
+    CouldntResolveHost,
+    CouldntConnect,
+    Timeout,
+    SslError,
+    QlError,
+    ContentVerificationError,
+    PreconditionFailed,
+    RequestEntityTooLarge,
+    RequestUrlTooLong,
+    ProxyAuthRequired,
+    NetAuthRequired,
+}
 
 unsafe extern "C" {
     fn dav_session_new(ctx: *mut ffi::DavContext, base_url: *const libc::c_char) -> *mut ffi::DavSession;
@@ -88,4 +156,7 @@ unsafe extern "C" {
     fn dav_session_unref(sn: *mut ffi::DavSession);
     fn dav_session_clone(sn: *mut ffi::DavSession) -> *mut ffi::DavSession;
     fn dav_session_set_auth(sn: *mut ffi::DavSession, user: *const c_char, password: *const c_char);
+
+    fn dav_session_get_error(sn: *const ffi::DavSession) -> ffi::DavError;
+    fn dav_session_get_errorstr(sn: *const ffi::DavSession) -> *const c_char;
 }
\ No newline at end of file
index e922593c52afd8bf04ed0355fac9c9bf95772096..4d1b6c565d879bfabf6cbda464489a0f7d2e7e77 100644 (file)
@@ -359,6 +359,14 @@ void dav_session_set_errstr(DavSession *sn, const char *str) {
     sn->errorstr = errstr;
 }
 
+DavError dav_session_get_error(DavSession *sn) {
+    return sn->error;
+}
+
+const char* dav_session_get_errorstr(DavSession *sn) {
+    return sn->errorstr;
+}
+
 void dav_session_destroy(DavSession *sn) { 
     // remove session from context
     DavContext *ctx = sn->context;
index e1efa31637fd4a79ed2a543959a065a2552f42a7..14f4160a6dedbe9678fdc56a19bcefc53e12c729 100644 (file)
@@ -305,6 +305,9 @@ void dav_session_enable_encryption(DavSession *sn, DavKey *key, int flags);
 void dav_session_set_authcallback(DavSession *sn, dav_auth_func func, void *userdata);
 void dav_session_set_progresscallback(DavSession *sn, dav_progress_func get, dav_progress_func put, void *userdata);
 
+DavError dav_session_get_error(DavSession *sn);
+const char* dav_session_get_errorstr(DavSession *sn);
+
 void dav_session_destroy(DavSession *sn);
 
 void dav_session_destructor(DavSession *sn);