|
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 |