update uwproj
[mizunara.git] / libidav / webdav.c
1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3  *
4  * Copyright 2018 Olaf Wintermann. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *   1. Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above copyright
13  *      notice, this list of conditions and the following disclaimer in the
14  *      documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <libxml/tree.h>
33
34 #include "utils.h"
35 #include "webdav.h"
36 #include "session.h"
37 #include "methods.h"
38 #include "ucx/buffer.h"
39 #include "ucx/utils.h"
40 #include "davqlparser.h"
41 #include "davqlexec.h"
42
43
44 DavContext* dav_context_new(void) {
45     // initialize
46     DavContext *context = calloc(1, sizeof(DavContext));
47     if(!context) {
48         return NULL;
49     }
50     context->sessions = NULL;
51     context->http_proxy = calloc(1, sizeof(DavProxy));
52     if(!context->http_proxy) {
53         dav_context_destroy(context);
54         return NULL;
55     }
56     context->https_proxy = calloc(1, sizeof(DavProxy));
57     if(!context->https_proxy) {
58         dav_context_destroy(context);
59         return NULL;
60     }
61     context->namespaces = ucx_map_new(16);
62     if(!context->namespaces) {
63         dav_context_destroy(context);
64         return NULL;
65     }
66     context->namespaceinfo = ucx_map_new(16);
67     if(!context->namespaceinfo) {
68         dav_context_destroy(context);
69     }
70     context->keys = ucx_map_new(16);
71     if(!context->keys) {
72         dav_context_destroy(context);
73         return NULL;
74     }
75     
76     // add DAV: namespace
77     if(dav_add_namespace(context, "D", "DAV:")) {
78         dav_context_destroy(context);
79         return NULL;
80     }
81     
82     
83     // add idav namespace
84     if(dav_add_namespace(context, "idav", DAV_NS)) {
85         dav_context_destroy(context);
86         return NULL;
87     }
88     
89     // add idavprops namespace
90     if(dav_add_namespace(context, "idavprops", DAV_PROPS_NS)) {
91         dav_context_destroy(context);
92         return NULL;
93     }   
94
95     return context;
96 }
97
98 void dav_context_destroy(DavContext *ctx) {
99     // destroy all sessions assoziated with this context
100     UcxList *elm = ctx->sessions;
101     while(elm) {
102         DavSession *sn = elm->data;
103         elm = elm->next;
104         dav_session_destroy(sn);
105     }
106     if(ctx->http_proxy) {
107         free(ctx->http_proxy);
108     }
109     if(ctx->https_proxy) {
110         free(ctx->https_proxy);
111     }
112     
113     if(ctx->namespaces) {
114         UcxMapIterator i = ucx_map_iterator(ctx->namespaces);
115         UcxKey k;
116         DavNamespace *ns;
117         UCX_MAP_FOREACH(k, ns, i) {
118             if(!ns) continue;
119             if(ns->prefix) {
120                 free(ns->prefix);
121             }
122             if(ns->name) {
123                 free(ns->name);
124             }
125             free(ns);
126         }
127         ucx_map_free(ctx->namespaces);
128     }
129     if(ctx->namespaceinfo) {
130         // TODO: implement
131     }
132     if(ctx->keys) {
133         UcxMapIterator i = ucx_map_iterator(ctx->keys);
134         UcxKey k;
135         DavKey *key;
136         UCX_MAP_FOREACH(k, key, i) {
137             if(!key) continue;
138             if(key->name) {
139                 free(key->name);
140             }
141             if(key->data) {
142                 free(key->data);
143             }
144             free(key);
145         }
146         ucx_map_free(ctx->keys);
147     }    
148     
149     free(ctx);
150 }
151
152 void dav_context_add_key(DavContext *context, DavKey *key) {
153     ucx_map_cstr_put(context->keys, key->name, key);
154 }
155
156 DavKey* dav_context_get_key(DavContext *context, char *name) {
157     if(name) {
158         return ucx_map_cstr_get(context->keys, name);
159     }
160     return NULL;
161 }
162
163 int dav_add_namespace(DavContext *context, const char *prefix, const char *name) {
164     DavNamespace *namespace = malloc(sizeof(DavNamespace));
165     if(!namespace) {
166         return 1;
167     }
168     
169     char *p = strdup(prefix);
170     char *n = strdup(name);
171     
172     int err = 0;
173     if(p && n) {
174         namespace->prefix = p;
175         namespace->name = n;
176         err = ucx_map_cstr_put(context->namespaces, prefix, namespace);
177     }
178     
179     if(err) {
180         free(namespace);
181         if(p) free(p);
182         if(n) free(n);
183     }
184     
185     return err;
186 }
187
188 DavNamespace* dav_get_namespace(DavContext *context, const char *prefix) {
189     return ucx_map_cstr_get(context->namespaces, prefix);
190 }
191
192 DavNamespace* dav_get_namespace_s(DavContext *context, sstr_t prefix) {
193     return ucx_map_sstr_get(context->namespaces, prefix);
194 }
195
196 int dav_enable_namespace_encryption(DavContext *context, const char *ns, DavBool encrypt) {
197     DavNSInfo *info = ucx_map_cstr_get(context->namespaceinfo, ns);
198     if(!info) {
199         info = calloc(1, sizeof(DavNSInfo));
200         info->encrypt = encrypt;
201         ucx_map_cstr_put(context->namespaceinfo, ns, info);
202     } else {
203         info->encrypt = encrypt;
204     }
205     return 0;
206 }
207
208 int dav_namespace_is_encrypted(DavContext *context, const char *ns) {
209     DavNSInfo *info = ucx_map_cstr_get(context->namespaceinfo, ns);
210     if(info) {
211         return info->encrypt;
212     }
213     return 0;
214 }
215
216 void dav_get_property_namespace_str(
217         DavContext *ctx,
218         char *prefixed_name,
219         char **ns,
220         char **name)
221 {
222     // TODO: rewrite using dav_get_property_ns
223     
224     char *pname = strchr(prefixed_name, ':');
225     char *pns = "DAV:";
226     if(pname) {
227         DavNamespace *ns = dav_get_namespace_s(
228                 ctx,
229                 sstrn(prefixed_name, pname-prefixed_name));
230         if(ns) {
231             pns = ns->name;
232             pname++;
233         } else {
234             pns = NULL;
235             pname = NULL;
236         }
237     } else {
238         pname = prefixed_name;
239     }
240     *ns = pns;
241     *name = pname;
242 }
243
244 DavNamespace* dav_get_property_namespace(
245         DavContext *ctx,
246         char *prefixed_name,
247         char **name)
248 {
249     char *pname = strchr(prefixed_name, ':');
250     if(pname) {
251         DavNamespace *ns = dav_get_namespace_s(
252                 ctx,
253                 sstrn(prefixed_name, pname-prefixed_name));
254         if(ns) {
255             *name = pname +1;
256             return ns;
257         } else {
258             *name = NULL;
259             return NULL;
260         }
261     } else {
262         *name = prefixed_name;
263         return dav_get_namespace_s(ctx, S("D"));
264     }
265 }
266
267 // TODO: add sstr_t version of dav_get_property_ns
268
269 void dav_set_effective_href(DavSession *sn, DavResource *resource) {
270     char *eff_url;
271     curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &eff_url);
272     if(eff_url) {
273         char *href = util_url_path(eff_url);
274         if(strcmp(href, resource->href)) {
275             dav_session_free(sn, resource->href);
276             resource->href = dav_session_strdup(sn, href);
277         }
278     }
279 }
280
281 DavResource* dav_get(DavSession *sn, char *path, char *properties) {  
282     CURL *handle = sn->handle;
283     DavResource *resource = dav_resource_new(sn, path);
284     util_set_url(sn, dav_resource_get_href(resource));
285     
286     UcxList *proplist = NULL;
287     if(properties) {
288         proplist = parse_properties_string(sn->context, sstr(properties));
289     }
290     UcxBuffer *rqbuf = create_propfind_request(sn, proplist, "propfind", 0);
291     UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
292     
293     //fwrite(rqbuf->space, 1, rqbuf->size, stdout);
294     //printf("\n");
295     
296     CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf);
297     long status = 0;
298     curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
299     if(ret == CURLE_OK && status == 207) {
300         dav_set_effective_href(sn, resource);
301         
302         //printf("response\n%s\n", rpbuf->space);
303         // TODO: use PropfindParser
304         resource = parse_propfind_response(sn, resource, rpbuf);
305         resource->exists = 1;
306         sn->error = DAV_OK;
307     } else  {
308         dav_session_set_error(sn, ret, status);
309         dav_resource_free(resource);
310         resource = NULL;
311     }
312     
313     ucx_buffer_free(rqbuf);
314     ucx_buffer_free(rpbuf);
315     while(proplist) {
316         DavProperty *p = proplist->data;
317         free(p->name);
318         free(p);
319         UcxList *next = proplist->next;
320         free(proplist);
321         proplist = next;
322     }
323     
324     return resource;
325 }
326
327
328 int dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf) {
329     // clean resource properties
330     DavResourceData *data = root->data;
331     ucx_map_clear(data->properties); // TODO: free existing content
332     
333     CURL *handle = sn->handle;
334     util_set_url(sn, dav_resource_get_href(root));
335      
336     UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
337     DavResource *resource = root;
338     CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf);
339     long status = 0;
340     long error = 0;
341     curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
342     if(ret == CURLE_OK && status == 207) {
343         //printf("response\n%s\n", rpbuf->space); 
344         dav_set_effective_href(sn, resource);
345         // TODO: use PropfindParser
346         resource = parse_propfind_response(sn, resource, rpbuf);
347         sn->error = DAV_OK;
348         root->exists = 1;
349     } else  {
350         dav_session_set_error(sn, ret, status);
351         error = 1;
352     }
353     ucx_buffer_free(rpbuf);
354     return error;
355 }
356
357 UcxList* parse_properties_string(DavContext *context, sstr_t str) {
358     UcxList *proplist = NULL;
359     ssize_t nprops = 0;
360     sstr_t *props = sstrsplit(str, S(","), &nprops);
361     for(int i=0;i<nprops;i++) {
362         sstr_t s = props[i];
363         sstr_t nsname = sstrchr(s, ':');
364         if(nsname.length > 0) {
365             sstr_t nspre = sstrsubsl(s, 0, nsname.ptr - s.ptr);
366             nsname.ptr++;
367             nsname.length--;
368             
369             DavProperty *dp = malloc(sizeof(DavProperty));
370             sstr_t pre = sstrtrim(nspre);
371             dp->ns = dav_get_namespace_s(context, pre);
372             dp->name = sstrdup(nsname).ptr;
373             if(dp->ns && dp->name) {
374                 proplist = ucx_list_append(proplist, dp);
375             } else {
376                 free(dp->name);
377                 free(dp);
378             }
379         }
380         free(s.ptr);
381     }
382     free(props);
383     return proplist;
384 }
385
386 DavResource* dav_query(DavSession *sn, char *query, ...) {
387     DavQLStatement *stmt = dav_parse_statement(sstr(query));
388     if(!stmt) {
389         sn->error = DAV_ERROR;
390         return NULL;
391     }
392     if(stmt->errorcode != 0) {
393         sn->error = DAV_QL_ERROR;
394         dav_free_statement(stmt);
395         return NULL;
396     }
397     
398     va_list ap;
399     va_start(ap, query);
400     DavResult result = dav_statement_execv(sn, stmt, ap);
401     va_end(ap);
402     
403     dav_free_statement(stmt);
404     return result.result;
405 }
406
407
408
409