src/server/plugins/postgresql/webdav.c

branch
webdav
changeset 306
e03737cea6e2
parent 303
ad9ba51c8634
child 307
8787cb5ebab3
--- a/src/server/plugins/postgresql/webdav.c	Thu Apr 21 17:16:49 2022 +0200
+++ b/src/server/plugins/postgresql/webdav.c	Sun Apr 24 18:35:44 2022 +0200
@@ -27,6 +27,11 @@
  */
 
 #include "webdav.h"
+#include "vfs.h"
+
+#include "../../util/util.h"
+
+#include <libxml/tree.h>
 
 static WebdavBackend pg_webdav_backend = {
     pg_dav_propfind_init,
@@ -43,6 +48,162 @@
     NULL
 };
 
+
+/*
+ * SQL Queries
+ */
+
+// propfind with depth = 0
+// params: $1: resource_id
+static const char *sql_propfind_allprop_depth0 = "\
+select\n\
+    NULL as ppath,\n\
+    r.resource_id,\n\
+    r.parent_id,\n\
+    r.nodename,\n\
+    r.iscollection,\n\
+    r.lastmodified,\n\
+    r.creationdate,\n\
+    r.contentlength,\n\
+    p.xmlns,\n\
+    p.pname,\n\
+    p.pvalue\n\
+from Resource r\n\
+left join Property p on r.resource_id = p.resource_id\n\
+where r.resource_id = $1;";
+
+// propfind with depth = 0 for specific properties
+// params: $1: resource_id
+static const char *sql_propfind_depth0 = "\
+select\n\
+    NULL as ppath,\n\
+    r.resource_id,\n\
+    r.parent_id,\n\
+    r.nodename,\n\
+    r.iscollection,\n\
+    r.lastmodified,\n\
+    r.creationdate,\n\
+    r.contentlength,\n\
+    p.xmlns,\n\
+    p.pname,\n\
+    p.pvalue\n\
+from Resource r\n\
+left join Property p on r.resource_id = p.resource_id\n\
+inner join (select unnest($2::text[]) as xmlns, unnest($3::text[]) as pname) n\n\
+   on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\
+where r.resource_id = $1;";
+
+// propfind with depth = 1
+// params: $1: resource_id
+static const char *sql_propfind_allprop_depth1 = "\
+select\n\
+    NULL ass ppath,\n\
+    r.resource_id,\n\
+    r.parent_id,\n\
+    r.nodename,\n\
+    r.iscollection,\n\
+    r.lastmodified,\n\
+    r.creationdate,\n\
+    r.contentlength,\n\
+    p.xmlns,\n\
+    p.pname,\n\
+    p.pvalue\n\
+from Resource r\n\
+left join Property p on r.resource_id = p.resource_id\n\
+where r.resource_id = $1 or r.parent_id = $1\n\
+order by order by case when resource_id = $1 then 0 else 1 end, nodename, resource_id;";
+
+
+// propfind with depth = 1 for specific properties
+// params: $1: resource_id
+static const char *sql_propfind_depth1 = "\
+select\n\
+    NULL as ppath,\n\
+    r.resource_id,\n\
+    r.parent_id,\n\
+    r.nodename,\n\
+    r.iscollection,\n\
+    r.lastmodified,\n\
+    r.creationdate,\n\
+    r.contentlength,\n\
+    p.xmlns,\n\
+    p.pname,\n\
+    p.pvalue\n\
+from Resource r\n\
+left join Property p on r.resource_id = p.resource_id\n\
+inner join (select unnest($2::text[]) as xmlns, unnest($3::text[]) as pname) n\n\
+   on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\
+where r.resource_id = $1 or r.parent_id = $1\n\
+order by order by case when resource_id = $1 then 0 else 1 end, nodename, resource_id;";
+
+// recursive propfind
+// params: $1: resource_id
+static const char *sql_propfind_allprop_recursive = "\
+with recursive resolvepath as (\n\
+    select\n\
+        '' as ppath,\n\
+        *\n\
+    from Resource\n\
+    where resource_id = $1 \n\
+    union\n\
+    select\n\
+        p.ppath || '/' || r.nodename,\n\
+        r.*\n\
+    from Resource r\n\
+    inner join resolvepath p on r.parent_id = p.resource_id\n\
+    )\n\
+select\n\
+    r.ppath,\n\
+    r.resource_id,\n\
+    r.parent_id,\n\
+    r.nodename,\n\
+    r.iscollection,\n\
+    r.lastmodified,\n\
+    r.creationdate,\n\
+    r.contentlength,\n\
+    p.xmlns,\n\
+    p.pname,\n\
+    p.pvalue\n\
+    from resolvepath r\n\
+left join Property p on r.resource_id = p.resource_id\n\
+order by replace(ppath, '/', chr(1)), resource_id;";
+
+// recursive propfind for specific properties
+// params: $1: resource_id
+//         $2: array of property xmlns
+//         $3: array of property names
+static const char *sql_propfind_recursive = "\
+with recursive resolvepath as (\n\
+    select\n\
+        '' as ppath,\n\
+        *\n\
+    from Resource\n\
+    where resource_id = $1 \n\
+    union\n\
+    select\n\
+        p.ppath || '/' || r.nodename,\n\
+        r.*\n\
+    from Resource r\n\
+    inner join resolvepath p on r.parent_id = p.resource_id\n\
+    )\n\
+select\n\
+    r.ppath,\n\
+    r.resource_id,\n\
+    r.parent_id,\n\
+    r.nodename,\n\
+    r.iscollection,\n\
+    r.lastmodified,\n\
+    r.creationdate,\n\
+    r.contentlength,\n\
+    p.xmlns,\n\
+    p.pname,\n\
+    p.pvalue\n\
+from resolvepath r\n\
+left join Property p on r.resource_id = p.resource_id\n\
+inner join (select unnest($2::text[]) as xmlns, unnest($3::text[]) as pname) n\n\
+   on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\
+order by replace(ppath, '/', chr(1)), resource_id;";
+
 WebdavBackend* pg_webdav_create(Session *sn, Request *rq, pblock *pb) {
     // resourcepool is required
     char *resource_pool = pblock_findval("resourcepool", pb);
@@ -69,7 +230,7 @@
     *webdav = pg_webdav_backend;
     
     PgWebdavBackend *instance = pool_malloc(sn->pool, sizeof(PgWebdavBackend));
-    if(instance) {
+    if(!instance) {
         pool_free(sn->pool, webdav);
         return NULL;
     }
@@ -91,10 +252,75 @@
         const char *path,
         WebdavPList **outplist)
 {
+    PgWebdavBackend *pgdav = rq->dav->instance;
+    
+    // check if the resource exists
+    int64_t parent_id;
+    int64_t resource_id;
+    const char *resourcename;
+    WSBool iscollection;
+    int res_errno = 0;
+    int err = pg_resolve_path(
+            pgdav->connection,
+            path,
+            &parent_id,
+            &resource_id,
+            NULL, // OID
+            &resourcename,
+            &iscollection,
+            NULL, // stat
+            &res_errno);
+    
+    if(err) {
+        if(res_errno == ENOENT) {
+            protocol_status(rq->sn, rq->rq, PROTOCOL_NOT_FOUND, NULL);
+        }
+        return 1;
+    }
+    
+    // choose sql query
+    const char *query = NULL;
+    if(!iscollection || rq->depth == 0) {
+        query = rq->allprop ? sql_propfind_allprop_depth0 : sql_propfind_depth0;
+    } else if(rq->depth == 1) {
+        query = rq->allprop ? sql_propfind_allprop_depth1 : sql_propfind_depth1;
+    } else if(rq->depth == -1) {
+        query = rq->allprop ? sql_propfind_allprop_recursive : sql_propfind_recursive;
+    } else {
+        log_ereport(LOG_FAILURE, "%s", "pg_dav_propfind_init: invalid depth");
+        return 1;
+    }
+    
+    // get all resources and properties
+    char resource_id_str[32];
+    snprintf(resource_id_str, 32, "%" PRId64, resource_id);
+    
+    const char* params[1] = { resource_id_str };
+    PGresult *result = PQexecParams(
+            pgdav->connection,
+            sql_propfind_allprop_recursive,
+            1,     // number of parameters
+            NULL,
+            params, // parameter value
+            NULL,
+            NULL,
+            0);    // 0: result in text format
+    int nrows = PQntuples(result);
+    if(nrows < 1) {
+        PQclear(result);
+        return 1;
+    }
+    
     PgPropfind *pg = pool_malloc(rq->sn->pool, sizeof(PgPropfind));
     rq->userdata = pg;
     
-    return 1;
+    pg->path = path;
+    pg->resource_id = resource_id;
+    pg->vfsproperties = webdav_vfs_properties(outplist, TRUE, 0);
+    pg->result = result;
+    pg->nrows = nrows;
+    
+    return 0;
 }
 
 int pg_dav_propfind_do(
@@ -104,11 +330,103 @@
 	WebdavResource *resource,
 	struct stat *s)
 {
-    return 1;
+    PgPropfind *pg = rq->userdata;
+    pool_handle_t *pool = rq->sn->pool;
+    PGresult *result = pg->result;
+    WebdavVFSProperties vfsprops = pg->vfsproperties;
+    
+    WSBool vfsprops_set = 0;
+    int64_t current_resource_id = pg->resource_id;
+    for(int r=0;r<pg->nrows;r++) {
+        // columns:
+        //  0: path
+        //  1: resource_id
+        //  2: parent_id
+        //  3: nodename
+        //  4: iscollection
+        //  5: lastmodified
+        //  6: creationdate
+        //  7: contentlength
+        //  8: property xmlns
+        //  9: property name
+        // 10: property value
+        
+        char *path = PQgetvalue(result, r, 0);
+        char *res_id = PQgetvalue(result, r, 1);
+        int64_t resource_id;
+        if(!util_strtoint(res_id, &resource_id)) {
+            log_ereport(LOG_FAILURE, "pg_dav_propfind_do: cannot convert resource_id '%s' to int", res_id);
+            return 1;
+        }
+        
+        char *nodename = PQgetvalue(result, r, 3);
+        if(resource_id != current_resource_id) {
+            // new resource
+            resource = response->addresource(response, nodename);
+            vfsprops_set = FALSE;
+            current_resource_id = resource_id;
+        }
+        
+        // standard webdav properties
+        if(!vfsprops_set) {
+            if(vfsprops.getresourcetype) {
+                char *iscollection = PQgetvalue(result, r, 4);
+                if(iscollection && iscollection[0] == 't') {
+                    resource->addproperty(resource, webdav_resourcetype_collection(), 200);
+                } else {
+                    resource->addproperty(resource, webdav_resourcetype_empty(), 200);
+                }
+            }
+            if(vfsprops.getlastmodified) {
+                char *lastmodified = PQgetvalue(result, r, 5);
+            }
+            if(vfsprops.creationdate) {
+                char *creationdate = PQgetvalue(result, r, 6);
+            }
+            if(vfsprops.getcontentlength) {
+                char *contentlength = PQgetvalue(result, r, 7);
+            }
+            if(vfsprops.getetag) {
+                
+            }
+            
+            
+            vfsprops_set = TRUE;
+        }
+        
+        // dead properties
+        if(!PQgetisnull(result, r, 9)) {
+            char *xmlns = PQgetvalue(result, r, 8);
+            char *pname = PQgetvalue(result, r, 9);
+            char *pvalue = PQgetvalue(result, r, 10);
+            
+            WebdavProperty *property = pool_malloc(pool, sizeof(WebdavProperty));
+            property->lang = NULL;
+            property->name = pool_strdup(pool, pname);
+            
+            xmlNs *namespace = pool_malloc(pool, sizeof(xmlNs));
+            memset(namespace, 0, sizeof(struct _xmlNs));
+            namespace->href = pool_strdup(pool, xmlns);
+            namespace->prefix = "zx1";
+            property->namespace = namespace;
+            
+            property->vtype = WS_VALUE_TEXT;
+            property->value.text.str = pool_strdup(pool, pvalue);
+            property->value.text.length = strlen(pvalue);
+            
+            resource->addproperty(resource, property, 200);
+        }
+        
+        
+    }
+    
+
+    
+    return 0;
 }
 
 int pg_dav_propfind_finish(WebdavPropfindRequest *rq) {
-    return 1;
+    return 0;
 }
 
 int pg_dav_proppatch_do(

mercurial