|
1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
3 * |
|
4 * Copyright 2014 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 |
|
33 #include <ucx/buffer.h> |
|
34 #include <ucx/utils.h> |
|
35 |
|
36 #include "utils.h" |
|
37 #include "session.h" |
|
38 #include "resource.h" |
|
39 #include "methods.h" |
|
40 |
|
41 DavSession* dav_session_new(DavContext *context, char *base_url) { |
|
42 if(!base_url) { |
|
43 return NULL; |
|
44 } |
|
45 sstr_t url = sstr(base_url); |
|
46 if(url.length == 0) { |
|
47 return NULL; |
|
48 } |
|
49 DavSession *sn = malloc(sizeof(DavSession)); |
|
50 sn->mp = ucx_mempool_new(DAV_SESSION_MEMPOOL_SIZE); |
|
51 sn->pathcache = ucx_map_new_a(sn->mp->allocator, DAV_PATH_CACHE_SIZE); |
|
52 sn->key = NULL; |
|
53 sn->errorstr = NULL; |
|
54 sn->error = DAV_OK; |
|
55 sn->flags = 0; |
|
56 if(url.ptr[url.length - 1] == '/') { |
|
57 sstr_t url = sstrdup_a(sn->mp->allocator, sstr(base_url)); |
|
58 sn->base_url = url.ptr; |
|
59 } else { |
|
60 char *url_str = malloc(url.length + 2); |
|
61 memcpy(url_str, base_url, url.length); |
|
62 url_str[url.length] = '/'; |
|
63 url_str[url.length + 1] = '\0'; |
|
64 sn->base_url = url_str; |
|
65 } |
|
66 sn->context = context; |
|
67 sn->handle = curl_easy_init(); |
|
68 //curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L); |
|
69 //curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr); |
|
70 curl_easy_setopt(sn->handle, CURLOPT_FOLLOWLOCATION, 1L); |
|
71 |
|
72 // set proxy |
|
73 DavProxy *proxy = sstrprefix(url, S("https")) ? context->https_proxy |
|
74 : context->http_proxy; |
|
75 |
|
76 if (proxy->url) { |
|
77 curl_easy_setopt(sn->handle, CURLOPT_PROXY, proxy->url); |
|
78 if (proxy->username) { |
|
79 curl_easy_setopt(sn->handle, CURLOPT_PROXYUSERNAME, |
|
80 proxy->username); |
|
81 if (proxy->password) { |
|
82 curl_easy_setopt(sn->handle, CURLOPT_PROXYPASSWORD, |
|
83 proxy->password); |
|
84 } else { |
|
85 // TODO: prompt |
|
86 } |
|
87 } |
|
88 if(proxy->no_proxy) { |
|
89 curl_easy_setopt(sn->handle, CURLOPT_NOPROXY, |
|
90 proxy->no_proxy); |
|
91 } |
|
92 } |
|
93 |
|
94 // set url |
|
95 curl_easy_setopt(sn->handle, CURLOPT_URL, base_url); |
|
96 |
|
97 context->sessions = ucx_list_append(context->sessions, sn); |
|
98 |
|
99 return sn; |
|
100 } |
|
101 |
|
102 DavSession* dav_session_new_auth( |
|
103 DavContext *context, |
|
104 char *base_url, |
|
105 char *user, |
|
106 char *password) |
|
107 { |
|
108 DavSession *sn = dav_session_new(context, base_url); |
|
109 if(!sn) { |
|
110 return NULL; |
|
111 } |
|
112 dav_session_set_auth(sn, user, password); |
|
113 return sn; |
|
114 } |
|
115 |
|
116 void dav_session_set_auth(DavSession *sn, char *user, char *password) { |
|
117 if(user && password) { |
|
118 size_t ulen = strlen(user); |
|
119 size_t plen = strlen(password); |
|
120 size_t upwdlen = ulen + plen + 2; |
|
121 char *upwdbuf = malloc(upwdlen); |
|
122 snprintf(upwdbuf, upwdlen, "%s:%s\0", user, password); |
|
123 curl_easy_setopt(sn->handle, CURLOPT_USERPWD, upwdbuf); |
|
124 free(upwdbuf); |
|
125 } |
|
126 } |
|
127 |
|
128 void dav_session_set_flags(DavSession *sn, uint32_t flags) { |
|
129 sn->flags = flags; |
|
130 } |
|
131 |
|
132 uint32_t dav_session_get_flags(DavSession *sn) { |
|
133 return sn->flags; |
|
134 } |
|
135 |
|
136 void dav_session_enable_encryption(DavSession *sn, DavKey *key, int flags) { |
|
137 sn->key = key; |
|
138 if(flags != 0) { |
|
139 sn->flags |= flags; |
|
140 } else { |
|
141 sn->flags |= DAV_SESSION_ENCRYPT_CONTENT; |
|
142 } |
|
143 } |
|
144 |
|
145 void dav_session_set_error(DavSession *sn, CURLcode c, int status) { |
|
146 if(status > 0) { |
|
147 switch(status) { |
|
148 default: sn->error = DAV_ERROR; break; |
|
149 case 401: sn->error = DAV_UNAUTHORIZED; break; |
|
150 case 403: sn->error = DAV_FORBIDDEN; break; |
|
151 case 404: sn->error = DAV_NOT_FOUND; break; |
|
152 case 405: sn->error = DAV_METHOD_NOT_ALLOWED; break; |
|
153 case 409: sn->error = DAV_CONFLICT; break; |
|
154 } |
|
155 } else { |
|
156 sn->error = DAV_ERROR; |
|
157 } |
|
158 if(c != CURLE_OK) { |
|
159 sn->errorstr = curl_easy_strerror(c); |
|
160 } else { |
|
161 sn->errorstr = NULL; |
|
162 } |
|
163 } |
|
164 |
|
165 void dav_session_destroy(DavSession *sn) { |
|
166 // remove session from context |
|
167 UcxList *sessions = sn->context->sessions; |
|
168 ssize_t i = ucx_list_find(sessions, sn, ucx_ptrcmp, NULL); |
|
169 if(i > 0) { |
|
170 UcxList *elm = ucx_list_get(sessions, i); |
|
171 if(elm) { |
|
172 sn->context->sessions = ucx_list_remove(sessions, elm); |
|
173 } |
|
174 } |
|
175 |
|
176 ucx_mempool_destroy(sn->mp); |
|
177 curl_easy_cleanup(sn->handle); |
|
178 free(sn); |
|
179 } |
|
180 |
|
181 |
|
182 void* dav_session_malloc(DavSession *sn, size_t size) { |
|
183 return ucx_mempool_malloc(sn->mp, size); |
|
184 } |
|
185 |
|
186 void* dav_session_calloc(DavSession *sn, size_t nelm, size_t size) { |
|
187 return ucx_mempool_calloc(sn->mp, nelm, size); |
|
188 } |
|
189 |
|
190 void* dav_session_realloc(DavSession *sn, void *ptr, size_t size) { |
|
191 return ucx_mempool_realloc(sn->mp, ptr, size); |
|
192 } |
|
193 |
|
194 void dav_session_free(DavSession *sn, void *ptr) { |
|
195 ucx_mempool_free(sn->mp, ptr); |
|
196 } |
|
197 |
|
198 char* dav_session_strdup(DavSession *sn, char *str) { |
|
199 return sstrdup_a(sn->mp->allocator, sstr(str)).ptr; |
|
200 } |
|
201 |
|
202 |
|
203 char* dav_session_create_plain_href(DavSession *sn, char *path) { |
|
204 if(!DAV_ENCRYPT_NAME(sn)) { |
|
205 // non encrypted file names |
|
206 char *url = util_path_to_url(sn, path); |
|
207 char *href = dav_session_strdup(sn, util_url_path(url)); |
|
208 free(url); |
|
209 return href; |
|
210 } else { |
|
211 return NULL; |
|
212 } |
|
213 } |
|
214 |
|
215 char* dav_session_get_href(DavSession *sn, char *path) { |
|
216 if(DAV_ENCRYPT_NAME(sn)) { |
|
217 sstr_t p = sstr(path); |
|
218 UcxBuffer *href = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND); |
|
219 UcxBuffer *pbuf = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND); |
|
220 int start = 0; |
|
221 int begin = 0; |
|
222 |
|
223 // check path cache |
|
224 char *cp = strdup(path); |
|
225 //printf("cp: %s\n", cp); |
|
226 while(strlen(cp) > 1) { |
|
227 char *cached = ucx_map_cstr_get(sn->pathcache, cp); |
|
228 if(cached) { |
|
229 start = strlen(cp); |
|
230 begin = start; |
|
231 ucx_buffer_puts(href, cached); |
|
232 break; |
|
233 } else { |
|
234 // check, if the parent path is cached |
|
235 char *f = cp; |
|
236 cp = util_parent_path(cp); |
|
237 free(f); |
|
238 } |
|
239 } |
|
240 free(cp); |
|
241 if(href->pos == 0) { |
|
242 // if there are no cached elements we have to add the base url path |
|
243 // to the href buffer |
|
244 ucx_buffer_puts(href, util_url_path(sn->base_url)); |
|
245 } |
|
246 |
|
247 // create resource for name lookup |
|
248 sstr_t rp = sstrdup(sstrn(path, start)); |
|
249 DavResource *root = dav_resource_new(sn, rp.ptr); |
|
250 resource_set_href(root, sstrn(href->space, href->pos)); |
|
251 |
|
252 // create request buffer for propfind requests |
|
253 UcxBuffer *rqbuf = create_basic_propfind_request(); |
|
254 |
|
255 sstr_t remaining = sstrsubs(p, start); |
|
256 size_t nelm = 0; |
|
257 sstr_t *elms = sstrsplit(remaining, S("/"), &nelm); |
|
258 DavResource *res = root; |
|
259 ucx_buffer_puts(pbuf, res->path); |
|
260 // iterate over all remaining path elements |
|
261 for(int i=0;i<nelm;i++) { |
|
262 sstr_t elm = elms[i]; |
|
263 if(elm.length > 0) { |
|
264 //printf("elm: %.*s\n", elm.length, elm.ptr); |
|
265 DavResource *child = dav_find_child(sn, res, rqbuf, elm.ptr); |
|
266 |
|
267 // if necessary add a path separator |
|
268 if(pbuf->space[pbuf->pos-1] != '/') { |
|
269 if(href->space[href->pos-1] != '/') { |
|
270 ucx_buffer_putc(href, '/'); |
|
271 } |
|
272 ucx_buffer_putc(pbuf, '/'); |
|
273 } |
|
274 // add last path/href to the cache |
|
275 sstr_t pp = sstrn(pbuf->space, pbuf->size); |
|
276 sstr_t hh = sstrn(href->space, href->size); |
|
277 dav_session_cache_path(sn, pp, hh); |
|
278 |
|
279 ucx_buffer_write(elm.ptr, 1, elm.length, pbuf); |
|
280 if(child) { |
|
281 ucx_buffer_puts(href, util_resource_name(child->href)); |
|
282 res = child; |
|
283 } else { |
|
284 //printf("random\n"); |
|
285 char *random_name = util_random_str(); |
|
286 ucx_buffer_puts(href, random_name); |
|
287 free(random_name); |
|
288 } |
|
289 |
|
290 } |
|
291 } |
|
292 |
|
293 // if necessary add a path separator |
|
294 if(p.ptr[p.length-1] == '/') { |
|
295 if(href->space[href->pos-1] != '/') { |
|
296 ucx_buffer_putc(href, '/'); |
|
297 } |
|
298 ucx_buffer_putc(pbuf, '/'); |
|
299 } |
|
300 // add the final path to the cache |
|
301 sstr_t pp = sstrn(pbuf->space, pbuf->size); |
|
302 sstr_t hh = sstrn(href->space, href->size); |
|
303 dav_session_cache_path(sn, pp, hh); |
|
304 |
|
305 sstr_t href_str = sstrdup_a( |
|
306 sn->mp->allocator, |
|
307 sstrn(href->space, |
|
308 href->size)); |
|
309 |
|
310 // cleanup |
|
311 dav_resource_free_all(root); |
|
312 ucx_buffer_free(pbuf); |
|
313 ucx_buffer_free(href); |
|
314 |
|
315 return href_str.ptr; |
|
316 } else { |
|
317 return dav_session_create_plain_href(sn, path); |
|
318 } |
|
319 } |
|
320 |
|
321 DavResource* dav_find_child(DavSession *sn, DavResource *res, UcxBuffer *rqbuf, char *name) { |
|
322 if(res && !dav_propfind(sn, res, rqbuf, NULL, 0)) { |
|
323 DavResource *child = res->children; |
|
324 while(child) { |
|
325 if(!strcmp(child->name, name)) { |
|
326 return child; |
|
327 } |
|
328 child = child->next; |
|
329 } |
|
330 } |
|
331 return NULL; |
|
332 } |
|
333 |
|
334 void dav_session_cache_path(DavSession *sn, sstr_t path, sstr_t href) { |
|
335 char *elm = ucx_map_sstr_get(sn->pathcache, path); |
|
336 if(!elm) { |
|
337 href = sstrdup_a(sn->mp->allocator, href); |
|
338 ucx_map_sstr_put(sn->pathcache, path, href.ptr); |
|
339 } |
|
340 } |