libidav/webdav.c

changeset 33
0bbbb0341606
child 36
c8755c87ce7f
equal deleted inserted replaced
32:c9d37bb97ea8 33:0bbbb0341606
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2013 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 "methods.h"
37 #include "davql.h"
38 #include "ucx/buffer.h"
39 #include "ucx/utils.h"
40
41
42
43 DavContext* dav_context_new() {
44 DavContext *context = malloc(sizeof(DavContext));
45 if(!context) {
46 return NULL;
47 }
48 context->sessions = NULL;
49 context->namespaces = ucx_map_new(16);
50 context->http_proxy = NULL;
51 context->https_proxy = NULL;
52 context->no_proxy = NULL;
53 if(!context->namespaces) {
54 free(context);
55 return NULL;
56 }
57 DavNamespace *davns = malloc(sizeof(DavNamespace));
58 if(!davns) {
59 ucx_map_free(context->namespaces);
60 free(context);
61 return NULL;
62 }
63 davns->prefix = "D";
64 davns->name = "DAV:";
65 if(ucx_map_cstr_put(context->namespaces, "D", davns)) {
66 free(davns);
67 ucx_map_free(context->namespaces);
68 free(context);
69 return NULL;
70 }
71
72 return context;
73 }
74
75 void dav_context_destroy(DavContext *ctx) {
76 // destroy all sessions assoziated with this context
77 UCX_FOREACH(elm, ctx->sessions) {
78 dav_session_destroy(elm->data);
79 }
80
81 UcxMapIterator i = ucx_map_iterator(ctx->namespaces);
82 UcxKey k;
83 DavNamespace *ns;
84 // TODO: free map elements
85 ucx_map_free(ctx->namespaces);
86 free(ctx);
87 }
88
89 int dav_add_namespace(DavContext *context, char *prefix, char *name) {
90 DavNamespace *namespace = malloc(sizeof(DavNamespace));
91 if(!namespace) {
92 return 1;
93 }
94 namespace->prefix = strdup(prefix);
95 namespace->name = strdup(name);
96 return ucx_map_cstr_put(context->namespaces, prefix, namespace);
97 }
98
99 DavNamespace* dav_get_namespace(DavContext *context, char *prefix) {
100 return ucx_map_cstr_get(context->namespaces, prefix);
101 }
102
103 DavNamespace* dav_get_namespace_s(DavContext *context, sstr_t prefix) {
104 return ucx_map_sstr_get(context->namespaces, prefix);
105 }
106
107 void dav_get_property_namespace(
108 DavContext *ctx,
109 char *prefixed_name,
110 char **ns,
111 char **name)
112 {
113 char *pname = strchr(prefixed_name, ':');
114 char *pns = "DAV:";
115 if(pname) {
116 DavNamespace *ns = dav_get_namespace_s(
117 ctx,
118 sstrn(prefixed_name, pname-prefixed_name));
119 pns = ns->name;
120 pname++;
121 } else {
122 pname = prefixed_name;
123 }
124 *ns = pns;
125 *name = pname;
126 }
127
128 DavSession* dav_session_new(DavContext *context, char *base_url) {
129 if(!base_url) {
130 return NULL;
131 }
132 sstr_t url = sstr(base_url);
133 if(url.length == 0) {
134 return NULL;
135 }
136 DavSession *sn = malloc(sizeof(DavSession));
137 sn->errorstr = NULL;
138 sn->error = DAV_OK;
139 if(url.ptr[url.length - 1] == '/') {
140 sn->base_url = strdup(base_url);
141 } else {
142 char *url_str = malloc(url.length + 2);
143 memcpy(url_str, base_url, url.length);
144 url_str[url.length] = '/';
145 url_str[url.length + 1] = '\0';
146 sn->base_url = url_str;
147 }
148 sn->context = context;
149 sn->handle = curl_easy_init();
150 //curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L);
151 //curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr);
152
153 // set proxy
154 if(sstrprefix(url, S("https"))) {
155 if(context->https_proxy) {
156 //printf("use https_proxy: %s\n", context->https_proxy);
157 curl_easy_setopt(sn->handle, CURLOPT_PROXY, context->https_proxy);
158 }
159 } else {
160 if(context->http_proxy) {
161 //printf("use http_proxy: %s\n", context->http_proxy);
162 curl_easy_setopt(sn->handle, CURLOPT_PROXY, context->http_proxy);
163 }
164 }
165 if(context->no_proxy) {
166 //printf("use no_proxy: %s\n", context->no_proxy);
167 curl_easy_setopt(sn->handle, CURLOPT_NOPROXY, context->no_proxy);
168 }
169 // set url
170 curl_easy_setopt(sn->handle, CURLOPT_URL, base_url);
171
172 sn->mp = ucx_mempool_new(1024);
173 sn->allocator = ucx_mempool_allocator(sn->mp);
174
175 context->sessions = ucx_list_append(context->sessions, sn);
176
177 return sn;
178 }
179
180 DavSession* dav_session_new_auth(
181 DavContext *context,
182 char *base_url,
183 char *user,
184 char *password)
185 {
186 DavSession *sn = dav_session_new(context, base_url);
187 if(!sn) {
188 return NULL;
189 }
190 dav_session_set_auth(sn, user, password);
191 return sn;
192 }
193
194 void dav_session_set_auth(DavSession *sn, char *user, char *password) {
195 if(user && password) {
196 size_t ulen = strlen(user);
197 size_t plen = strlen(password);
198 size_t upwdlen = ulen + plen + 2;
199 char *upwdbuf = malloc(upwdlen);
200 snprintf(upwdbuf, upwdlen, "%s:%s\0", user, password);
201 curl_easy_setopt(sn->handle, CURLOPT_USERPWD, upwdbuf);
202 free(upwdbuf);
203 }
204 }
205
206 void session_set_error(DavSession *sn, CURLcode c, int status) {
207 if(status > 0) {
208 switch(status) {
209 default: sn->error = DAV_ERROR; break;
210 case 401: sn->error = DAV_UNAUTHORIZED; break;
211 case 403: sn->error = DAV_FORBIDDEN; break;
212 case 404: sn->error = DAV_NOT_FOUND; break;
213 case 405: sn->error = DAV_METHOD_NOT_ALLOWED; break;
214 case 409: sn->error = DAV_CONFLICT; break;
215 }
216 } else {
217 sn->error = DAV_ERROR;
218 }
219 if(c != CURLE_OK) {
220 sn->errorstr = curl_easy_strerror(c);
221 } else {
222 sn->errorstr = NULL;
223 }
224 }
225
226 void dav_session_destroy(DavSession *sn) {
227 // remove session from context
228 UcxList *sessions = sn->context->sessions;
229 ssize_t i = ucx_list_find(sessions, sn, ucx_ptrcmp, NULL);
230 if(i > 0) {
231 UcxList *elm = ucx_list_get(sessions, i);
232 if(elm) {
233 sn->context->sessions = ucx_list_remove(sessions, elm);
234 }
235 }
236
237 ucx_mempool_destroy(sn->mp);
238 curl_easy_cleanup(sn->handle);
239 free(sn->base_url);
240 free(sn);
241 }
242
243 DavResource* dav_get(DavSession *sn, char *path, char *properties) {
244 char *url = util_concat_path(sn->base_url, path);
245
246 CURL *handle = sn->handle;
247 curl_easy_setopt(handle, CURLOPT_URL, url);
248 free(url);
249
250 UcxList *proplist = NULL;
251 if(properties) {
252 proplist = parse_properties_string(sn->context, sstr(properties));
253 }
254 UcxBuffer *rqbuf = create_propfind_request(proplist);
255 UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
256
257 //fwrite(rqbuf->space, 1, rqbuf->size, stdout);
258 //printf("\n");
259
260 DavResource *resource = NULL;
261 CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf);
262 int status = 0;
263 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
264 if(ret == CURLE_OK && status == 207) {
265 //printf("response\n%s\n", rpbuf->space);
266 resource = parse_propfind_response(sn, NULL, rpbuf, NULL, 0);
267 sn->error = DAV_OK;
268 } else {
269 session_set_error(sn, ret, status);
270 }
271 return resource;
272 }
273
274 DavResource* dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf, char *path, DavQOp *cond, size_t len) {
275 char *url = util_concat_path(sn->base_url, path);
276 CURL *handle = sn->handle;
277 curl_easy_setopt(handle, CURLOPT_URL, url);
278 free(url);
279
280 UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
281 DavResource *resource = root;
282 CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf);
283 int status = 0;
284 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
285 if(ret == CURLE_OK && status == 207) {
286 //printf("response\n%s\n", rpbuf->space);
287 resource = parse_propfind_response(sn, resource, rpbuf, cond, len);
288 sn->error = DAV_OK;
289 } else {
290 session_set_error(sn, ret, status);
291 resource = NULL;
292 }
293 ucx_buffer_free(rpbuf);
294 return resource;
295 }
296
297 UcxList* propfind_stack_push(UcxList *stack, DavResource *children) {
298 while(children) {
299 if(children->iscollection) {
300 stack = ucx_list_prepend(stack, children);
301 }
302 children = children->next;
303 }
304 return stack;
305 }
306
307 DavResource* dav_get2(DavSession *sn, DavGetQuery *query) {
308 char *path;
309 int depth = 0;
310 if(parse_path_query(query->from, &path, &depth)) {
311 sn->error = DAV_ERROR;
312 return NULL;
313 }
314
315 sstr_t ps = query->properties;
316 UcxBuffer *rqbuf;
317 if(!sstrcmp(ps, S("*"))) {
318 rqbuf = create_allprop_propfind_request();
319 } else if(!sstrcmp(ps, S("-"))) {
320 rqbuf = create_propfind_request(NULL);
321 } else {
322 UcxList *proplist = parse_properties_string(sn->context, ps);
323 rqbuf = create_propfind_request(proplist);
324 }
325
326 //fwrite(rqbuf->space, 1, rqbuf->size, stdout);
327 //printf("\n");
328
329 DavResource *resource = dav_propfind(sn, NULL, rqbuf, path, query->condition, query->condlen);
330 free(path);
331 int error = 0;
332 if(resource && depth == -1) {
333 UcxList *stack = NULL; // stack with davResource* elements
334 stack = propfind_stack_push(stack, resource->children);
335 while(stack) {
336 DavResource *sr = stack->data; // get first element from the stack
337 stack = ucx_list_remove(stack, stack); // remove first element
338 // do propfind request for sr
339 sr = dav_propfind(sn, sr, rqbuf, sr->path, query->condition, query->condlen);
340 if(!sr) {
341 error = 1;
342 printf("subrequest failed\n");
343 break;
344 }
345 stack = propfind_stack_push(stack, sr->children); // add children
346 }
347 }
348 return resource;
349 }
350
351 UcxList* parse_properties_string(DavContext *context, sstr_t str) {
352 UcxList *proplist = NULL;
353 size_t nprops;
354 sstr_t *props = sstrsplit(str, S(","), &nprops);
355 for(int i=0;i<nprops;i++) {
356 sstr_t s = props[i];
357 sstr_t nsname = sstrchr(s, ':');
358 if(nsname.length > 0) {
359 sstr_t nspre = sstrsubsl(s, 0, nsname.ptr - s.ptr);
360 nsname.ptr++;
361 nsname.length--;
362
363 DavProperty *dp = malloc(sizeof(DavProperty));
364 sstr_t pre = sstrdup(sstrtrim(nspre));
365 dp->ns = dav_get_namespace(context, pre.ptr);
366 free(pre.ptr);
367 dp->name = sstrdup(nsname).ptr;
368 if(dp->ns && dp->name) {
369 proplist = ucx_list_append(proplist, dp);
370 } else {
371 free(dp->name);
372 free(dp);
373 }
374 }
375 free(s.ptr);
376 }
377 free(props);
378 return proplist;
379 }
380
381 DavResource* dav_query(DavSession *sn, char *query, ...) {
382 va_list ap;
383 va_start(ap, query);
384 DavQuery q = dav_ql_parse(query, ap);
385 va_end(ap);
386 DavResource *res = NULL;
387 switch(q.command) {
388 case DAV_QUERY_GET: {
389 res = dav_get2(sn, q.command_data);
390 free_get_query(q.command_data);
391 break;
392 }
393 }
394 return res;
395 }
396
397
398
399

mercurial