1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include "davcontroller.h"
30 #include "window.h"
31
32 #include <cx/printf.h>
33
34 #include "config.h"
35 #include "upload.h"
36 #include "download.h"
37
38 #include "system.h"
39 #include "common/context.h"
40
41 #include <libidav/config.h>
42 #include <libidav/utils.h>
43
44 DavBrowser* davbrowser_create(UiObject *toplevel) {
45 DavBrowser *doc = ui_document_new(
sizeof(DavBrowser));
46 UiContext *ctx = ui_document_context(doc);
47 CxMempool *mp = ui_cx_mempool(ctx);
48 doc->window = toplevel;
49 doc->ctx = ctx;
50
51 doc->navigation_stack = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
52 doc->navstack_enabled = true;
53 doc->navstack_pos =
0;
54
55 doc->dav_queue = ui_threadpool_create(
1);
56 cxMempoolRegister(mp, doc->dav_queue, (cx_destructor_func)ui_threadpool_destroy);
57
58 doc->res_open_inprogress = cxHashMapCreate(ctx->allocator,
CX_STORE_POINTERS,
4);
59
60 doc->path = ui_string_new(ctx,
"path");
61 doc->resources = ui_list_new(ctx,
"reslist");
62
63 return doc;
64 }
65
66
67 void davbrowser_set_collection(UiObject *ui, DavBrowser *browser, DavResource *collection) {
68 if (browser->current) {
69 dav_resource_free_all(browser->current);
70 }
71 ui_list_clear(browser->resources);
72
73 browser->current = collection;
74 browser->res_counter++;
75 for (DavResource *res = collection->children; res; res = res->next) {
76 ui_list_append(browser->resources, res);
77 }
78
79 browser->resources->update(browser->resources,
0);
80
81 ui_set_group(ui->ctx,
APP_STATE_BROWSER_SESSION);
82 }
83
84
85
86 typedef struct DavConnect2Repo {
87 UiObject *ui;
88 DavBrowser *browser;
89
90 DavCfgRepository *repo;
91 char *path;
92
93 UiString *password;
94 } DavConnect2Repo;
95
96 static void dialog_secretstore_decrypt(UiEvent *event,
void *data) {
97 DavConnect2Repo *c2r = event->window;
98
99 if(event->intval ==
4) {
100 char *pw = ui_get(c2r->password);
101
102 PwdStore *secrets = get_pwdstore();
103 pwdstore_setpassword(secrets, pw);
104 if(pwdstore_decrypt(secrets)) {
105 ui_dialog(c2r->ui, .title =
"Error", .content =
"Cannot decrypt Secret Store", .closebutton_label =
"OK");
106 }
else {
107 davbrowser_connect2repo(c2r->ui, c2r->browser, c2r->repo, c2r->path);
108 }
109 }
110 free(c2r->path);
111 if(!c2r->repo->node) {
112 dav_repository_free(get_config(), c2r->repo);
113 }
114
115 ui_close(event->obj);
116 }
117
118 int davbrowser_connect2repo(UiObject *ui, DavBrowser *browser, DavCfgRepository *repo,
const char *path) {
119 char *user =
NULL;
120 char *password =
NULL;
121 char *password_free =
NULL;
122 if (repo->user.value.ptr && repo->password.value.ptr) {
123 user = repo->user.value.ptr;
124 cxmutstr decodedpw = dav_repository_get_decodedpassword(repo);
125 password = decodedpw.ptr;
126 password_free = decodedpw.ptr;
127 }
else {
128 PwdStore *secrets = get_pwdstore();
129 const char *credentials =
NULL;
130 if(repo->stored_user.value.ptr) {
131 if(pwdstore_has_id(secrets, repo->stored_user.value.ptr)) {
132 credentials = repo->stored_user.value.ptr;
133 }
134 }
else {
135 credentials = get_location_credentials(repo, path);
136 }
137
138 if(credentials) {
139 if(!secrets->isdecrypted) {
140 UiObject *obj = ui_dialog_window(ui,
141 .title =
"Authentication",
142 .lbutton1 =
"Cancel",
143 .rbutton4 =
"Connect",
144 .default_button =
4,
145 .show_closebutton =
UI_OFF,
146 .onclick = dialog_secretstore_decrypt);
147
148 DavConnect2Repo *c2r = ui_malloc(obj->ctx,
sizeof(DavConnect2Repo));
149 c2r->ui = ui;
150 c2r->browser = browser;
151 c2r->repo = repo;
152 c2r->path = path ? strdup(path) :
NULL;
153 c2r->password = ui_string_new(obj->ctx,
NULL);
154 obj->window = c2r;
155
156 ui_grid(obj, .margin =
20, .columnspacing =
12, .rowspacing =
16) {
157 ui_llabel(obj, .label =
"Decrypt Secret Store", .colspan =
2);
158 ui_newline(obj);
159
160 ui_llabel(obj, .label =
"Password");
161 ui_passwordfield(obj, .value = c2r->password, .hexpand =
TRUE);
162 }
163
164 ui_show(obj);
165
166 return 1;
167 }
168
169 if(!get_stored_credentials(credentials, &user, &password)) {
170 fprintf(stderr,
"Error: failed to get user/password for credentials %s\n", credentials);
171 }
172 }
173 }
174
175 DavSession *sn = dav_session_new(application_dav_context(), repo->url.value.ptr);
176 if (user && password) {
177 dav_session_set_auth(sn, user, password);
178 }
179 free(password_free);
180
181 sn->flags = dav_repository_get_flags(repo);
182 sn->key = dav_context_get_key(application_dav_context(), repo->default_key.value.ptr);
183 curl_easy_setopt(sn->handle,
CURLOPT_SSLVERSION, repo->ssl_version);
184 if(repo->cert.value.ptr) {
185 curl_easy_setopt(sn->handle,
CURLOPT_CAINFO, repo->cert.value.ptr);
186 }
187 if(!repo->verification.value) {
188 curl_easy_setopt(sn->handle,
CURLOPT_SSL_VERIFYPEER,
0);
189 curl_easy_setopt(sn->handle,
CURLOPT_SSL_VERIFYHOST,
0);
190 }
191
192
193 browser->sn = sn;
194 if (repo->name.value.length >
0) {
195 browser->repo_base = cx_strdup(cx_strn(repo->name.value.ptr, repo->name.value.length)).ptr;
196 }
else {
197 browser->repo_base = cx_strdup(cx_strn(repo->url.value.ptr, repo->url.value.length)).ptr;
198 }
199
200 char *dav_path = util_concat_path(browser->repo_base, path);
201 ui_set(browser->path, dav_path);
202 free(dav_path);
203
204 SessionAuthData *auth = cxMalloc(sn->mp->allocator,
sizeof(SessionAuthData));
205 auth->obj = ui;
206 auth->cond = ui_condvar_create();
207 auth->sn = sn;
208 auth->user = repo->user.value.ptr ? cx_strdup_a(sn->mp->allocator, cx_strcast(repo->user.value)).ptr :
NULL;
209 auth->password =
NULL;
210 dav_session_set_authcallback(sn, jobthr_davbrowser_auth, auth);
211
212 davbrowser_query_path(ui, browser, path);
213
214 return 0;
215 }
216
217
218
219 static int davbrowser_auth_dialog(
void *data) {
220 SessionAuthData *auth = data;
221 auth_dialog(auth);
222 return 0;
223 }
224
225 void davbrowser_auth_set_user_pwd(SessionAuthData *auth,
const char *user,
const char *password) {
226 dav_session_free(auth->sn, auth->user);
227 dav_session_free(auth->sn, auth->password);
228 auth->user = user ? dav_session_strdup(auth->sn, user) :
NULL;
229 auth->password = password ? dav_session_strdup(auth->sn, password) :
NULL;
230 }
231
232 int jobthr_davbrowser_auth(DavSession *sn,
void *data) {
233 SessionAuthData *auth = data;
234
235 ui_call_mainthread(davbrowser_auth_dialog, auth);
236 ui_condvar_wait(auth->cond);
237
238 if(auth->cond->intdata) {
239 dav_session_set_auth(sn, auth->user, auth->password);
240 }
241 dav_session_free(auth->sn, auth->password);
242 auth->password =
NULL;
243
244 return 0;
245 }
246
247
248
249
250 typedef struct DavBrowserQueryPath {
251 UiThreadpool *pool;
252 DavBrowser *browser;
253 char *path;
254 DavResource *result;
255 } DavBrowserQueryPath;
256
257 static int browser_query_path(
void *data) {
258 DavBrowserQueryPath *query = data;
259 DavSession *sn = query->browser->sn;
260
261 DavResource *res = dav_query(sn,
"select `idav:crypto-name`,`idav:crypto-key`,D:lockdiscovery from %s with depth = 1 order by iscollection desc, name", query->path);
262 query->result = res;
263
264 return 0;
265 }
266
267 static void browser_query_finished(UiEvent *event,
void *data) {
268 DavBrowserQueryPath *query = data;
269 DavBrowser *browser = event->document;
270
271 if (query->pool == browser->dav_queue) {
272 if (query->result) {
273 davbrowser_set_collection(event->obj, browser, query->result);
274 }
else {
275 cxmutstr error = cx_asprintf(
"Error %d", query->browser->sn->error);
276 ui_dialog(event->obj, .title =
"Error", .content = error.ptr, .closebutton_label =
"OK");
277 }
278
279 window_progress(event->window,
0);
280 }
else {
281
282 if (query->result) {
283 dav_resource_free_all(query->result);
284 }
285 }
286
287 free(query->path);
288 free(query);
289 }
290
291 void davbrowser_query_path(UiObject *ui, DavBrowser *browser,
const char *path) {
292 if (!browser->sn) {
293
294 return;
295 }
296
297
298 size_t len = path ? strlen(path) :
0;
299 if (len ==
1 && *path ==
'/') {
300 path =
"";
301 }
302
303
304
305 char *full_path = util_concat_path(browser->repo_base, path);
306 char *full_path_col = util_concat_path(full_path,
"/");
307 char *current_path = ui_get(browser->path);
308 cxstring cpath = cx_str(current_path);
309 cxstring newc = cx_str(full_path_col);
310 if (!cx_strprefix(cpath, newc)) {
311 ui_set(browser->path, full_path);
312 }
313 free(full_path);
314 free(full_path_col);
315
316 DavBrowserQueryPath *query = malloc(
sizeof(DavBrowserQueryPath));
317 query->pool = browser->dav_queue;
318 query->browser = browser;
319 query->path = strdup(path);
320 query->result =
NULL;
321 ui_threadpool_job(browser->dav_queue, ui, browser_query_path, query, browser_query_finished, query);
322
323 window_progress(ui->window,
1);
324
325 davbrowser_add2navstack(browser, browser->repo_base, path);
326 }
327
328 void davbrowser_query_url(UiObject *ui, DavBrowser *browser,
const char *url) {
329 if (browser->repo_base) {
330 cxstring base = cx_str(browser->repo_base);
331 cxstring newurl = cx_str(url);
332
333 if (cx_strprefix(newurl, base)) {
334 const char *path = url + base.length;
335 davbrowser_query_path(ui, browser, path);
336 return;
337 }
338 }
339
340 char *path =
NULL;
341 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
342
343 int ret = davbrowser_connect2repo(ui, browser, repo, path);
344 free(path);
345 if(ret) {
346 return;
347 }
348
349 if (!repo->node) {
350 dav_repository_free(get_config(), repo);
351 }
352 }
353
354 void davbrowser_open_resource(UiObject *ui, DavBrowser *browser, DavResource *res,
const char *contenttype) {
355 DavResourceViewType type =
DAV_RESOURCE_VIEW_PROPERTIES;
356 if(!contenttype) {
357 contenttype = res->contenttype;
358 }
359
360 if(res->iscollection) {
361
362 }
else if(contenttype) {
363 cxstring ctype = cx_str(contenttype);
364 if(cx_strprefix(ctype,
CX_STR(
"text/"))) {
365 type =
DAV_RESOURCE_VIEW_TEXT;
366 }
else if(cx_strprefix(ctype,
CX_STR(
"image/"))) {
367 type =
DAV_RESOURCE_VIEW_IMAGE;
368 }
else if(cx_strprefix(ctype,
CX_STR(
"application/"))) {
369 if(cx_strsuffix(ctype,
CX_STR(
"json"))) {
370 type =
DAV_RESOURCE_VIEW_TEXT;
371 }
else if(cx_strsuffix(ctype,
CX_STR(
"/xml"))) {
372 type =
DAV_RESOURCE_VIEW_TEXT;
373 }
else if(cx_strsuffix(ctype,
CX_STR(
"+xml"))) {
374 type =
DAV_RESOURCE_VIEW_TEXT;
375 }
else if(cx_strsuffix(ctype,
CX_STR(
"/xml"))) {
376 type =
DAV_RESOURCE_VIEW_TEXT;
377 }
378 }
379 }
else {
380 cxstring path = cx_str(res->path);
381 if(cx_strsuffix(path,
CX_STR(
".png"))) {
382 type =
DAV_RESOURCE_VIEW_IMAGE;
383 }
else if(cx_strsuffix(path,
CX_STR(
".jpg"))) {
384 type =
DAV_RESOURCE_VIEW_IMAGE;
385 }
else if(cx_strsuffix(path,
CX_STR(
".jpeg"))) {
386 type =
DAV_RESOURCE_VIEW_IMAGE;
387 }
else if(cx_strsuffix(path,
CX_STR(
".tif"))) {
388 type =
DAV_RESOURCE_VIEW_IMAGE;
389 }
else if(cx_strsuffix(path,
CX_STR(
".tiff"))) {
390 type =
DAV_RESOURCE_VIEW_IMAGE;
391 }
else if(cx_strsuffix(path,
CX_STR(
".webp"))) {
392 type =
DAV_RESOURCE_VIEW_IMAGE;
393 }
else if(cx_strsuffix(path,
CX_STR(
".bmp"))) {
394 type =
DAV_RESOURCE_VIEW_IMAGE;
395 }
else if(cx_strsuffix(path,
CX_STR(
".gif"))) {
396 type =
DAV_RESOURCE_VIEW_IMAGE;
397 }
else if(cx_strsuffix(path,
CX_STR(
".txt"))) {
398 type =
DAV_RESOURCE_VIEW_TEXT;
399 }
else if(cx_strsuffix(path,
CX_STR(
".md"))) {
400 type =
DAV_RESOURCE_VIEW_TEXT;
401 }
else if(cx_strsuffix(path,
CX_STR(
".xml"))) {
402 type =
DAV_RESOURCE_VIEW_TEXT;
403 }
404 }
405
406 resourceviewer_new(browser, res->path, type);
407 }
408
409 void davbrowser_add2navstack(DavBrowser *browser,
const char *base,
const char *path) {
410 if (browser->navstack_enabled) {
411 for (
int i =
0; i < browser->navstack_pos; i++) {
412 char *nav_url = cxListAt(browser->navigation_stack,
0);
413 cxListRemove(browser->navigation_stack,
0);
414 free(nav_url);
415 }
416 browser->navstack_pos =
0;
417
418 char *nav_url = util_concat_path(base, path);
419 cxListInsert(browser->navigation_stack,
0, nav_url);
420
421 if (cxListSize(browser->navigation_stack) >
DAVBROWSER_MAX_NAVLIST) {
422 char *nav = cxListAt(browser->navigation_stack, cxListSize(browser->navigation_stack) -
1);
423 free(nav);
424 cxListRemove(browser->navigation_stack, cxListSize(browser->navigation_stack) -
1);
425 }
426 }
427 }
428
429 void davbrowser_navigation_parent(UiObject *ui, DavBrowser *browser) {
430 if(browser->current) {
431 char *parent = util_parent_path(browser->current->path);
432 if(strlen(parent) >
0) {
433 davbrowser_query_path(ui, browser, parent);
434 }
435 free(parent);
436 }
437 }
438
439 void davbrowser_navigation_back(UiObject *ui, DavBrowser *browser) {
440 if (browser->navstack_pos+
1 < cxListSize(browser->navigation_stack)) {
441 browser->navstack_pos++;
442 char *nav_url = cxListAt(browser->navigation_stack, browser->navstack_pos);
443 browser->navstack_enabled = false;
444 davbrowser_query_url(ui, browser, nav_url);
445 browser->navstack_enabled = true;
446 ui_set(browser->path, nav_url);
447 }
448 }
449
450 void davbrowser_navigation_forward(UiObject *ui, DavBrowser *browser) {
451 if (browser->navstack_pos >
0) {
452 browser->navstack_pos--;
453 char *nav_url = cxListAt(browser->navigation_stack, browser->navstack_pos);
454 browser->navstack_enabled = false;
455 davbrowser_query_url(ui, browser, nav_url);
456 browser->navstack_enabled = true;
457 ui_set(browser->path, nav_url);
458 }
459 }
460
461
462 void davbrowser_upload_files(UiObject *ui, DavBrowser *browser, UiFileList files) {
463 if (!browser->sn) {
464 return;
465 }
466
467 cxmutstr wtitle = cx_asprintf(
"Upload to: %s", ui_get(browser->path));
468 UiObject *dialog = ui_simple_window(wtitle.ptr,
NULL);
469 free(wtitle.ptr);
470
471 DavFileUpload *upload = dav_upload_create(browser, dialog, files);
472 transfer_window_init(dialog, action_upload_cancel);
473 dav_upload_start(upload);
474 application_register_transfer(&upload->trans);
475 }
476
477 void davbrowser_download(UiObject *ui, DavBrowser *browser, DavResource *reslist,
const char *local_path) {
478 cxmutstr wtitle = cx_asprintf(
"Download to: %s", local_path);
479 UiObject *dialog = ui_simple_window(wtitle.ptr,
NULL);
480 free(wtitle.ptr);
481
482 DavFileDownload *download = dav_download_create(browser, dialog, reslist, local_path);
483 transfer_window_init(dialog, action_download_cancel);
484 dav_download_start(download);
485 application_register_transfer(&download->trans);
486 }
487
488
489
490
491 enum DavPathOpType {
492 DAV_PATH_OP_DELETE =
0,
493 DAV_PATH_OP_CREATE
494 };
495
496 typedef struct DavPathOp {
497 UiObject *ui;
498 DavBrowser *browser;
499
500
501 enum DavPathOpType op;
502
503 DavSession *sn;
504
505 char **path;
506
507 size_t *list_indices;
508
509 size_t nelm;
510
511 DavBool iscollection;
512
513
514
515 DavResource *collection;
516 int64_t collection_ctn;
517 } DavPathOp;
518
519 typedef struct DavPathOpResult {
520 UiObject *ui;
521 DavBrowser *browser;
522 DavResource *collection;
523 int64_t collection_ctn;
524 enum DavPathOpType op;
525 DavBool iscollection;
526
527 char *path;
528 int res_index;
529 int result;
530 char *errormsg;
531
532 time_t result_lastmodified;
533 uint64_t result_contentlength;
534 char *result_contenttype;
535 } DavPathOpResult;
536
537 typedef struct DavRenameOp {
538 UiObject *ui;
539 DavBrowser *browser;
540
541
542 DavSession *sn;
543 char *path;
544 char *newname;
545 int result;
546 char *errormsg;
547
548
549 size_t index;
550
551
552
553 DavResource *collection;
554 int64_t collection_ctn;
555 } DavRenameOp;
556
557 static int uithr_pathop_delete_error(
void *data) {
558 DavPathOpResult *result = data;
559
560 cxmutstr msg = cx_asprintf(
"Cannot delete resource %s", result->path);
561 ui_dialog(result->ui, .title =
"Error", .content = msg.ptr, .button1_label =
"OK");
562 free(msg.ptr);
563
564 if (result->errormsg) {
565 free(result->errormsg);
566 }
567 free(result->path);
568 free(result);
569 return 0;
570 }
571
572 static int uithr_pathop_delete_sucess(
void *data) {
573 DavPathOpResult *result = data;
574
575 if (result->browser->current == result->collection && result->browser->res_counter == result->collection_ctn) {
576 ui_list_remove(result->browser->resources, result->res_index);
577 result->browser->resources->update(result->browser->resources,
0);
578 }
579
580 free(result->path);
581 free(result);
582 return 0;
583 }
584
585 static int uithr_pathop_create_resource_error(
void *data) {
586 DavPathOpResult *result = data;
587
588 cxmutstr msg = cx_asprintf(
"Cannot create %s %s", result->iscollection ?
"collection" :
"resource", result->path);
589 ui_dialog(result->ui, .title =
"Error", .content = msg.ptr, .button1_label =
"OK");
590 free(msg.ptr);
591
592 if (result->errormsg) {
593 free(result->errormsg);
594 }
595 free(result->path);
596 free(result);
597 return 0;
598 }
599
600 static int uithr_pathop_create_resource_sucess(
void *data) {
601 DavPathOpResult *result = data;
602
603 if (result->browser->current == result->collection && result->browser->res_counter == result->collection_ctn) {
604 DavResource *res = dav_resource_new(result->browser->sn, result->path);
605 res->iscollection = result->iscollection;
606 res->lastmodified = result->result_lastmodified;
607 res->contentlength = result->result_contentlength;
608 res->contenttype = result->result_contenttype ? dav_session_strdup(res->session, result->result_contenttype) :
NULL;
609
610 ui_list_append(result->browser->resources, res);
611 result->browser->resources->update(result->browser->resources,
0);
612 }
613
614 free(result->path);
615 free(result->result_contenttype);
616 free(result);
617 return 0;
618 }
619
620 static int jobthr_path_op(
void *data) {
621 DavPathOp *op = data;
622
623 for (
int i = op->nelm-
1; i >=
0; i--) {
624 if (op->path[i]) {
625 DavResource *res = dav_resource_new(op->sn, op->path[i]);
626
627 DavPathOpResult *result = malloc(
sizeof(DavPathOpResult));
628 result->ui = op->ui;
629 result->browser = op->browser;
630 result->collection = op->collection;
631 result->collection_ctn = op->collection_ctn;
632 result->op = op->op;
633 result->path = strdup(res->path);
634 result->result =
0;
635 result->res_index = op->list_indices[i];
636 result->errormsg =
NULL;
637 result->iscollection = op->iscollection;
638 result->result_lastmodified =
0;
639 result->result_contentlength =
0;
640 result->result_contenttype =
NULL;
641
642 if (op->op ==
DAV_PATH_OP_DELETE) {
643 ui_threadfunc result_callback = uithr_pathop_delete_sucess;
644 if (dav_delete(res)) {
645 result->errormsg = op->sn->errorstr ? strdup(op->sn->errorstr) :
NULL;
646 result_callback = uithr_pathop_delete_error;
647 }
648 ui_call_mainthread(result_callback, result);
649 }
else if (op->op ==
DAV_PATH_OP_CREATE) {
650 res->iscollection = op->iscollection;
651 ui_threadfunc result_callback = uithr_pathop_create_resource_sucess;
652 if (dav_create(res)) {
653 result->errormsg = op->sn->errorstr ? strdup(op->sn->errorstr) :
NULL;
654 result_callback = uithr_pathop_create_resource_error;
655 }
else {
656
657
658
659 dav_load_prop(res,
NULL,
0);
660 result->result_lastmodified = res->lastmodified;
661 result->result_contentlength = res->contentlength;
662 result->result_contenttype = res->contenttype ? strdup(res->contenttype) :
NULL;
663 }
664 ui_call_mainthread(result_callback, result);
665 }
666
667 dav_resource_free(res);
668 free(op->path[i]);
669 }
670 }
671
672 dav_session_destroy(op->sn);
673 free(op->path);
674 free(op->list_indices);
675 free(op);
676
677 return 0;
678 }
679
680 void davbrowser_delete(UiObject *ui, DavBrowser *browser, UiListSelection selection) {
681 DavPathOp *op = malloc(
sizeof(DavPathOp));
682 op->ui = ui;
683 op->browser = browser;
684 op->op =
DAV_PATH_OP_DELETE;
685 op->sn = dav_session_clone(browser->sn);
686 op->path = calloc(selection.count,
sizeof(
char*));
687 op->list_indices = calloc(selection.count,
sizeof(
size_t));
688 op->nelm = selection.count;
689
690 op->collection = browser->current;
691 op->collection_ctn = browser->res_counter;
692
693 for (
int i =
0; i < selection.count; i++) {
694 DavResource *res = ui_list_get(browser->resources, selection.rows[i]);
695 if (res) {
696 op->path[i] = strdup(res->path);
697 op->list_indices[i] = selection.rows[i];
698 }
699 }
700
701 ui_job(ui, jobthr_path_op, op,
NULL,
NULL);
702 }
703
704 void davbrowser_create_resource(UiObject *ui, DavBrowser *browser,
const char *name, DavBool iscollection) {
705 DavPathOp *op = malloc(
sizeof(DavPathOp));
706 op->ui = ui;
707 op->browser = browser;
708 op->op =
DAV_PATH_OP_CREATE;
709 op->sn = dav_session_clone(browser->sn);
710 op->path = calloc(
1,
sizeof(
char*));
711 op->list_indices = calloc(
1,
sizeof(
size_t));
712 op->nelm =
1;
713 op->iscollection = iscollection;
714
715 op->path[
0] = util_concat_path(browser->current->path, name);
716
717 op->collection = browser->current;
718 op->collection_ctn = browser->res_counter;
719
720 ui_job(ui, jobthr_path_op, op,
NULL,
NULL);
721 }
722
723 void davbrowser_mkcol(UiObject *ui, DavBrowser *browser,
const char *name) {
724 davbrowser_create_resource(ui, browser, name,
TRUE);
725 }
726
727 void davbrowser_newfile(UiObject *ui, DavBrowser *browser,
const char *name) {
728 davbrowser_create_resource(ui, browser, name,
FALSE);
729 }
730
731
732
733
734 static int jobthr_rename(
void *data) {
735 DavRenameOp *op = data;
736
737 DavResource *res = dav_get(op->sn, op->path,
NULL);
738 if(!res) {
739 fprintf(stderr,
"Error: Cannot get resource %s\n", op->path);
740 op->result =
1;
741 return 0;
742 }
743
744 char *cryptoname = dav_get_string_property_ns(res,
DAV_NS,
"crypto-name");
745 char *cryptokey = dav_get_string_property_ns(res,
DAV_NS,
"crypto-key");
746 if(cryptoname && cryptokey) {
747
748
749
750 DavKey *key = dav_context_get_key(op->sn->context, cryptokey);
751 if(!key) {
752 cxmutstr error = cx_asprintf_a(op->sn->mp->allocator,
"Cannot rename resource: crypto key %s not found.", cryptokey);
753 op->errormsg = error.ptr;
754 op->result =
1;
755 return 0;
756 }
757
758
759 char *parent = util_parent_path(res->path);
760 char *newpath = util_concat_path(parent, op->newname);
761 DavResource *testres = dav_resource_new(op->sn, newpath);
762 if(dav_exists(testres)) {
763 cxmutstr error = cx_asprintf_a(op->sn->mp->allocator,
"A resource with the name %s already exists.", op->newname);
764 op->errormsg = error.ptr;
765 op->result =
1;
766 }
else {
767 char *crname = aes_encrypt(op->newname, strlen(op->newname), key);
768 dav_set_string_property_ns(res,
DAV_NS,
"crypto-name", crname);
769 free(crname);
770 if(dav_store(res)) {
771 op->result =
1;
772 }
773 }
774 free(parent);
775 free(newpath);
776 }
else {
777
778 char *parent = util_parent_path(res->href);
779 char *new_href = util_concat_path(parent, op->newname);
780 char *dest = util_get_url(op->sn, new_href);
781 free(parent);
782 free(new_href);
783 if(dav_moveto(res, dest, false)) {
784 op->result =
1;
785 }
786 free(dest);
787 }
788
789 if(op->result && !op->errormsg) {
790 cxmutstr error = cx_asprintf_a(op->sn->mp->allocator,
"Error: %d", op->sn->error);
791 op->errormsg = error.ptr;
792 }
793
794 return 0;
795 }
796
797 static void uithr_rename_finished(UiEvent *event,
void *data) {
798 DavRenameOp *op = data;
799
800 if(!op->result) {
801
802 if (op->browser->current == op->collection && op->browser->res_counter == op->collection_ctn) {
803 DavResource *res = ui_list_get(op->browser->resources, op->index);
804
805 char *parent = util_parent_path(res->path);
806 char *newpath = util_concat_path(parent, op->newname);
807 dav_session_free(res->session, res->path);
808 dav_session_free(res->session, res->name);
809 res->path = dav_session_strdup(res->session, newpath);
810 res->name = dav_session_strdup(res->session, op->newname);
811 op->browser->resources->update(op->browser->resources,
0);
812 free(parent);
813 free(newpath);
814 }
815 }
else {
816
817 ui_dialog(op->ui, .title =
"Error", .content = op->errormsg, .closebutton_label =
"OK");
818 }
819 dav_session_destroy(op->sn);
820 }
821
822 static void action_resource_rename(UiEvent *event,
void *data) {
823 DavRenameOp *op = data;
824 if(event->intval ==
1) {
825 char *newname = event->eventdata;
826 if(!newname || strlen(newname) ==
0) {
827 ui_dialog(op->ui, .title =
"Error", .content =
"No name specified", .closebutton_label =
"OK");
828 dav_session_destroy(op->sn);
829 return;
830 }
831
832 char *s = strchr(newname,
'/');
833 if(s) {
834 ui_dialog(op->ui, .title =
"Error", .content =
"Character ''/'' is not allowed", .closebutton_label =
"OK");
835 dav_session_destroy(op->sn);
836 return;
837 }
838
839 op->newname = dav_session_strdup(op->sn, newname);
840
841 ui_job(op->ui, jobthr_rename, op, uithr_rename_finished, op);
842 return;
843 }
844 dav_session_destroy(op->sn);
845 }
846
847 void davbrowser_rename(UiObject *ui, DavBrowser *browser, UiListSelection selection) {
848 DavSession *sn = dav_session_clone(browser->sn);
849
850 DavResource *res = ui_list_get(browser->resources, selection.rows[
0]);
851
852 DavRenameOp *rename = dav_session_malloc(sn,
sizeof(DavRenameOp));
853 memset(rename,
0,
sizeof(DavRenameOp));
854 rename->browser = browser;
855 rename->ui = ui;
856 rename->sn = sn;
857 rename->path = dav_session_strdup(sn, res->path);
858 rename->index = selection.rows[
0];
859
860 rename->collection = browser->current;
861 rename->collection_ctn = browser->res_counter;
862
863 ui_dialog(ui,
864 .title =
"Rename",
865 .content = res->name,
866 .input =
TRUE,
867 .input_value = res->name,
868 .result = action_resource_rename,
869 .resultdata = rename,
870 .button1_label =
"Rename",
871 .closebutton_label =
"Cancel");
872 }
873
874
875
876 DavResourceViewer* dav_resourceviewer_create(UiObject *toplevel, DavSession *sn,
const char *path, DavResourceViewType type) {
877 DavResourceViewer *doc = ui_document_new(
sizeof(DavResourceViewer));
878 UiContext *ctx = ui_document_context(doc);
879 CxMempool *mp = ui_cx_mempool(ctx);
880 doc->obj = toplevel;
881 doc->ctx = ctx;
882
883 doc->sn = dav_session_clone(sn);
884 doc->dav_queue = ui_threadpool_create(
1);
885 cxMempoolRegister(mp, doc->dav_queue, (cx_destructor_func)ui_threadpool_destroy);
886 doc->path = strdup(path);
887 doc->type = type;
888
889 doc->tabview = ui_int_new(ctx,
"tabview");
890 doc->loading = ui_int_new(ctx,
"loading");
891 doc->message = ui_string_new(ctx,
"message");
892
893 doc->text = ui_text_new(ctx,
"text");
894 doc->image = ui_generic_new(ctx,
"image");
895
896 doc->properties = ui_list_new(ctx,
"properties");
897
898 doc->info_url = ui_string_new(ctx,
"info_url");
899 doc->info_name = ui_string_new(ctx,
"info_name");
900 doc->info_type = ui_string_new(ctx,
"info_type");
901 doc->info_encrypted = ui_string_new(ctx,
"info_encrypted");
902 doc->info_etag = ui_string_new(ctx,
"info_etag");
903 doc->info_size = ui_string_new(ctx,
"info_size");
904
905 doc->property_type = ui_int_new(ctx,
NULL);
906 doc->property_ns = ui_string_new(ctx,
NULL);
907 doc->property_name = ui_string_new(ctx,
NULL);
908 doc->property_nsdef = ui_string_new(ctx,
NULL);
909 doc->property_value = ui_text_new(ctx,
NULL);
910 doc->property_errormsg = ui_string_new(ctx,
NULL);
911
912 return doc;
913 }
914
915 static char* gen_tmp_download_filename(
const char *name) {
916 char *dir = ui_getappdir();
917
918 unsigned char rd[
8];
919 memset(rd,
0,
8);
920 dav_rand_bytes(rd,
8);
921 char *hex = util_hexstr(rd,
8);
922 cxmutstr tmp = cx_asprintf(
"%sdownload-%s-%s", dir, hex, name);
923 return tmp.ptr;
924 }
925
926 static int jobthr_resourceviewer_load(
void *data) {
927 DavResourceViewer *doc = data;
928
929 DavResource *res = dav_resource_new(doc->sn, doc->path);
930 doc->error = dav_load(res);
931 if(!doc->error) {
932 doc->current = res;
933
934 if(res->contentlength <
DAV_RESOURCEVIEWER_PREVIEW_MAX_SIZE) {
935 if(doc->type ==
DAV_RESOURCE_VIEW_TEXT) {
936 doc->text_content = cxBufferCreate(
NULL, res->contentlength, cxDefaultAllocator,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
937 int err = dav_get_content(res, doc->text_content, (dav_write_func)cxBufferWrite);
938 cxBufferPut(doc->text_content,
0);
939 if(err) {
940 doc->error = err;
941 doc->message_str =
"Cannot load content";
942 }
943 }
else if(doc->type ==
DAV_RESOURCE_VIEW_IMAGE) {
944 char *tmp = gen_tmp_download_filename(res->name);
945 FILE *f = sys_fopen(tmp,
"wb");
946 if(f) {
947 int err = dav_get_content(res, f, (dav_write_func)fwrite);
948 if(!err) {
949 doc->tmp_file = tmp;
950 }
else {
951 free(tmp);
952 }
953 fclose(f);
954 }
else {
955 free(tmp);
956 }
957 }
958 }
else {
959
960 }
961 }
else {
962 doc->message_str =
"Cannot load properties";
963 dav_resource_free(res);
964 }
965
966 return 0;
967 }
968
969 static void resourceviewer_set_info(DavResourceViewer *doc) {
970 DavResource *res = doc->current;
971 if(!res) {
972 return;
973 }
974
975 char *url = util_concat_path(res->session->base_url, res->href);
976 ui_set(doc->info_url, url);
977 free(url);
978
979 ui_set(doc->info_name, res->name);
980
981 if(res->iscollection) {
982 ui_set(doc->info_type,
"Collection");
983 }
else {
984 if(res->contenttype) {
985 cxmutstr type = cx_asprintf(
"Resource (%s)", res->contenttype);
986 ui_set(doc->info_type, type.ptr);
987 free(type.ptr);
988 }
else {
989 ui_set(doc->info_type,
"Resource");
990 }
991 }
992
993 char *keyprop = dav_get_string_property_ns(
994 res,
995 DAV_NS,
996 "crypto-key");
997 if(keyprop) {
998 cxmutstr info_encrypted = cx_asprintf(
"Yes Key: %s", keyprop);
999 ui_set(doc->info_encrypted, info_encrypted.ptr);
1000 free(info_encrypted.ptr);
1001 }
else {
1002 ui_set(doc->info_encrypted,
"No");
1003 }
1004
1005 char *etag = dav_get_string_property_ns(
1006 res,
1007 "DAV:",
1008 "getetag");
1009 ui_set(doc->info_etag, etag);
1010
1011 if(res->contentlength >
0) {
1012 char *sz = util_size_str(
FALSE, res->contentlength);
1013 cxmutstr size_str = cx_asprintf(
"%s (%" PRIu64
" bytes)", sz, res->contentlength);
1014 ui_set(doc->info_size, size_str.ptr);
1015 free(size_str.ptr);
1016 }
else {
1017 ui_set(doc->info_size,
"0");
1018 }
1019 }
1020
1021 static void resourceviewer_update_proplist(DavResourceViewer *doc) {
1022 DavResource *res = doc->current;
1023 if(!res) {
1024 return;
1025 }
1026
1027 size_t count =
0;
1028 DavPropName *properties = dav_get_property_names(res, &count);
1029 for(
int i=
0;i<count;i++) {
1030 DavPropertyList *prop = ui_malloc(doc->ctx,
sizeof(DavPropertyList));
1031 prop->ns = properties[i].ns ? ui_strdup(doc->ctx, properties[i].ns) :
NULL;
1032 prop->name = ui_strdup(doc->ctx, properties[i].name);
1033 prop->value_simplified =
NULL;
1034 prop->value_full =
NULL;
1035 prop->update =
FALSE;
1036 prop->isnew =
FALSE;
1037
1038 DavXmlNode *xval = dav_get_property_ns(res, prop->ns, prop->name);
1039 prop->xml = xval;
1040 if(xval) {
1041 if(dav_xml_isstring(xval)) {
1042 char *value = dav_xml_getstring(xval);
1043 if(value) {
1044 prop->value_simplified =
NULL;
1045 prop->value_full = ui_strdup(doc->ctx, value);
1046 }
1047 }
else {
1048 DavXmlNode *x = xval->type ==
DAV_XML_ELEMENT ? xval : dav_xml_nextelm(xval);
1049 cxmutstr value = cx_asprintf_a(ui_allocator(doc->ctx),
"<%s>...</%s>", x->name, x->name);
1050 prop->value_simplified = value.ptr;
1051 }
1052 }
1053
1054 ui_list_append(doc->properties, prop);
1055 }
1056 doc->properties->update(doc->properties,
0);
1057 }
1058
1059 static void resourceviewer_load_finished(UiEvent *event,
void *data) {
1060 DavResourceViewer *doc = data;
1061
1062 if(doc->window_closed) {
1063 dav_resourceviewer_destroy(doc);
1064 return;
1065 }
1066
1067 resourceviewer_set_info(doc);
1068 resourceviewer_update_proplist(doc);
1069
1070 if(doc->type ==
DAV_RESOURCE_VIEW_TEXT) {
1071 ui_set(doc->text, doc->text_content->space);
1072 }
else if(doc->type ==
DAV_RESOURCE_VIEW_IMAGE) {
1073 ui_image_load_file(doc->image, doc->tmp_file);
1074 }
1075
1076 ui_set(doc->tabview,
1);
1077
1078 doc->loaded =
TRUE;
1079 }
1080
1081 void dav_resourceviewer_load(UiObject *ui, DavResourceViewer *res) {
1082 ui_set(res->loading,
1);
1083 ui_set(res->message,
"Loading...");
1084 ui_set(res->tabview,
0);
1085
1086 ui_job(ui, jobthr_resourceviewer_load, res, resourceviewer_load_finished, res);
1087 }
1088
1089
1090 typedef struct ResourceViewerUploadFile {
1091 UiObject *ui;
1092 DavSession *sn;
1093 char *path;
1094 cxmutstr text;
1095 int error;
1096 } ResourceViewerUploadFile;
1097
1098 static int jobthr_upload_text(
void *data) {
1099 ResourceViewerUploadFile *upload = data;
1100
1101 DavResource *res = dav_resource_new(upload->sn, upload->path);
1102 dav_set_content_data(res, upload->text.ptr, upload->text.length);
1103 upload->error = dav_store(res);
1104 dav_resource_free(res);
1105
1106 return 0;
1107 }
1108
1109 static void uithr_upload_text_finished(UiEvent *event,
void *data) {
1110 ResourceViewerUploadFile *upload = data;
1111 ui_object_unref(event->obj);
1112
1113 if(upload->error) {
1114 cxmutstr errormsg = cx_asprintf(
"Upload failed: %d", upload->sn->error);
1115 ui_dialog(event->obj, .title =
"Error", .content = errormsg.ptr, .closebutton_label =
"OK");
1116 free(errormsg.ptr);
1117 ui_set_group(event->obj->ctx,
RESOURCEVIEWER_STATE_MODIFIED);
1118 }
1119
1120 free(upload->text.ptr);
1121 free(upload);
1122 }
1123
1124 static int jobthr_store_properties(
void *data) {
1125 DavResourceViewer *res = data;
1126 res->error = dav_store(res->current);
1127 return 0;
1128 }
1129
1130 static void uithr_store_properties_finished(UiEvent *event,
void *data) {
1131 DavResourceViewer *res = data;
1132 ui_object_unref(event->obj);
1133 if(res->error) {
1134 cxmutstr errormsg = cx_asprintf(
"Proppatch failed: %d", res->sn->error);
1135 ui_dialog(event->obj, .title =
"Error", .content = errormsg.ptr, .closebutton_label =
"OK");
1136 free(errormsg.ptr);
1137 ui_set_group(event->obj->ctx,
RESOURCEVIEWER_STATE_MODIFIED);
1138 res->properties_modified =
TRUE;
1139 }
else {
1140 CxList *properties = res->properties->data;
1141 CxIterator i = cxListIterator(properties);
1142 cx_foreach(DavPropertyList *, prop, i) {
1143 prop->update =
FALSE;
1144 prop->isnew =
FALSE;
1145 }
1146 }
1147 }
1148
1149 void dav_resourceviewer_save(UiObject *ui, DavResourceViewer *res) {
1150 if(res->type ==
DAV_RESOURCE_VIEW_TEXT) {
1151 ResourceViewerUploadFile *upload = malloc(
sizeof(ResourceViewerUploadFile));
1152 upload->ui = ui;
1153 upload->sn = res->sn;
1154 upload->path = res->current->path;
1155 char *text = ui_get(res->text);
1156 upload->text = cx_strdup(cx_str(text));
1157 ui_object_ref(res->obj);
1158 ui_threadpool_job(res->dav_queue, ui, jobthr_upload_text, upload, uithr_upload_text_finished, upload);
1159 }
1160
1161 if(res->properties_modified) {
1162 CxList *properties = res->properties->data;
1163 CxIterator i = cxListIterator(properties);
1164 cx_foreach(DavPropertyList *, prop, i) {
1165 if(prop->update) {
1166 if(prop->value_full) {
1167
1168 dav_set_string_property_ns(res->current, prop->ns, prop->name, prop->value_full);
1169 }
else {
1170
1171 dav_set_property_ns(res->current, prop->ns, prop->name, prop->xml);
1172 }
1173 }
1174 }
1175
1176 ui_object_ref(res->obj);
1177 ui_threadpool_job(res->dav_queue, ui, jobthr_store_properties, res, uithr_store_properties_finished, res);
1178 }
1179
1180 ui_unset_group(ui->ctx,
RESOURCEVIEWER_STATE_MODIFIED);
1181 }
1182
1183 void dav_resourceviewer_destroy(DavResourceViewer *res) {
1184
1185 }
1186
1187 void dav_resourceviewer_property_remove(DavResourceViewer *res, DavPropertyList *prop) {
1188 if(!prop->isnew) {
1189 dav_remove_property_ns(res->current, prop->ns, prop->name);
1190 ui_set_group(res->obj->ctx,
RESOURCEVIEWER_STATE_MODIFIED);
1191 res->properties_modified =
TRUE;
1192 }
1193
1194 CxList *properties = res->properties->data;
1195 cxListFindRemove(properties, prop);
1196 ui_free(res->ctx, prop->ns);
1197 ui_free(res->ctx, prop->name);
1198 ui_free(res->ctx, prop->value_simplified);
1199 ui_free(res->ctx, prop->value_full);
1200
1201 ui_free(res->ctx, prop);
1202 ui_list_update(res->properties);
1203 }
1204
1205 void dav_resourceviewer_property_update_text(DavResourceViewer *res, DavPropertyList *prop,
const char *text) {
1206 ui_free(res->ctx, prop->value_simplified);
1207 ui_free(res->ctx, prop->value_full);
1208 prop->xml =
NULL;
1209 prop->value_full = ui_strdup(res->ctx, text);
1210 prop->value_simplified =
NULL;
1211 prop->update =
TRUE;
1212
1213 ui_set_group(res->obj->ctx,
RESOURCEVIEWER_STATE_MODIFIED);
1214 res->properties_modified =
TRUE;
1215 ui_list_update(res->properties);
1216 }
1217
1218 void dav_resourceviewer_property_update_xml(DavResourceViewer *res, DavPropertyList *prop, DavXmlNode *xml) {
1219
1220 }
1221
1222 void dav_resourceviewer_property_add_text(DavResourceViewer *res,
const char *ns,
const char *name,
const char *text) {
1223 DavPropertyList *prop = ui_malloc(res->ctx,
sizeof(DavPropertyList));
1224 prop->ns = ui_strdup(res->ctx, ns);
1225 prop->name = ui_strdup(res->ctx, name);
1226 prop->value_simplified =
NULL;
1227 prop->value_full = ui_strdup(res->ctx, text);
1228 prop->xml =
NULL;
1229 prop->isnew =
TRUE;
1230 prop->update =
TRUE;
1231
1232 ui_list_append(res->properties, prop);
1233 ui_set_group(res->obj->ctx,
RESOURCEVIEWER_STATE_MODIFIED);
1234 res->properties_modified =
TRUE;
1235 ui_list_update(res->properties);
1236 }
1237
1238 void dav_resourceviewer_property_add_xml(DavResourceViewer *res,
const char *ns,
const char *name,
const char *nsdef, DavXmlNode *xml) {
1239
1240 }
1241
1242
1243
1244 uint64_t dav_transfer_speed(TransferProgress *progress,
time_t current) {
1245 size_t bytes = progress->transferred_bytes - progress->speedtest_bytes;
1246 time_t t = current - progress->speedtest_start;
1247
1248 progress->speedtest_start = current;
1249 progress->speedtest_bytes = progress->transferred_bytes;
1250
1251 return bytes/t;
1252 }
1253