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 <assert.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdbool.h>
34 #include <errno.h>
35 #include <time.h>
36 #include <sys/types.h>
37 #ifndef _WIN32
38 #include <sys/wait.h>
39 #include <unistd.h>
40 #endif
41 #include <cx/string.h>
42 #include <cx/utils.h>
43 #include <cx/printf.h>
44 #include <cx/hash_map.h>
45 #include <cx/linked_list.h>
46
47
48 #include <libidav/utils.h>
49 #include <libidav/crypto.h>
50 #include <libidav/session.h>
51 #include <libidav/xml.h>
52 #include "config.h"
53 #include "error.h"
54 #include "assistant.h"
55 #include "system.h"
56 #include "pwd.h"
57 #include "finfo.h"
58 #include "main.h"
59
60 static DavContext *ctx;
61
62 static int printxmlerror =
1;
63 static void xmlerrorfnc(
void * c,
const char * msg, ... ) {
64 if(printxmlerror) {
65 va_list ap;
66 va_start(ap, msg);
67 vfprintf(stderr, msg, ap);
68 va_end(ap);
69 }
70 }
71
72
73
74
75
76
77
78 void test(CmdArgs *a) {
79
80 }
81
82 int dav_main(
int argc,
char **argv);
83
84 #ifdef _WIN32
85
86 #define strcasecmp _stricmp
87
88 static char* wchar2utf8(
const wchar_t *wstr,
size_t wlen) {
89 size_t maxlen = wlen *
4;
90 char *ret = malloc(maxlen +
1);
91 int ret_len = WideCharToMultiByte(
92 CP_UTF8,
93 0,
94 wstr,
95 wlen,
96 ret,
97 maxlen,
98 NULL,
99 NULL);
100 ret[ret_len] =
0;
101 return ret;
102 }
103
104 int wmain(
int argc,
wchar_t **argv) {
105 char **argv_utf8 = calloc(argc,
sizeof(
char*));
106 for(
int i=
0;i<argc;i++) {
107 argv_utf8[i] = wchar2utf8(argv[i], wcslen(argv[i]));
108 }
109
110 int ret = dav_main(argc, argv_utf8);
111
112
113 for(
int i=
0;i<argc;i++) {
114 free(argv_utf8[i]);
115 }
116 free(argv_utf8);
117
118 return ret;
119 }
120 #else
121 int main(
int argc,
char **argv) {
122 return dav_main(argc, argv);
123 }
124 #endif
125
126
127 int dav_main(
int argc,
char **argv) {
128 if(argc <
2) {
129 fprintf(stderr,
"Missing command\n");
130 print_usage(argv[
0]);
131 return -
1;
132 }
133
134 putenv(
"LC_TIME=C");
135
136 char *cmd = argv[
1];
137 CmdArgs *args = cmd_parse_args(argc -
2, argv +
2);
138 if(!args) {
139 print_usage(argv[
0]);
140 return -
1;
141 }
142
143 sys_init();
144 xmlGenericErrorFunc fnc = xmlerrorfnc;
145 initGenericErrorDefaultFunc(&fnc);
146 ctx = dav_context_new();
147 dav_add_namespace(ctx,
"apache",
"http://apache.org/dav/props/");
148 int cfgret = load_config(ctx);
149 int ret =
EXIT_FAILURE;
150 printxmlerror =
0;
151 #ifdef DO_THE_TEST
152 test(args);
153 return 0;
154 #endif
155 if(!strcmp(cmd,
"check") || !strcmp(cmd,
"check-config")) {
156 if(!cfgret) {
157 fprintf(stdout,
"Configuration OK.\n");
158 ret =
EXIT_SUCCESS;
159 }
else {
160
161 ret =
EXIT_FAILURE;
162 }
163 }
else if(!cfgret) {
164 if(!strcasecmp(cmd,
"list") || !strcasecmp(cmd,
"ls")) {
165 ret = cmd_list(args);
166 }
else if(!strcasecmp(cmd,
"get")) {
167 ret = cmd_get(args,
FALSE);
168 }
else if(!strcasecmp(cmd,
"cat")) {
169 cxMapPut(args->options, cx_hash_key_str(
"output"),
"-");
170 ret = cmd_get(args,
FALSE);
171 }
else if(!strcasecmp(cmd,
"edit")) {
172 ret = cmd_edit(args);
173 }
else if(!strcasecmp(cmd,
"put")) {
174 ret = cmd_put(args,
FALSE);
175 }
else if(
176 !strcasecmp(cmd,
"remove") ||
177 !strcasecmp(cmd,
"rm") ||
178 !strcasecmp(cmd,
"delete"))
179 {
180 ret = cmd_remove(args);
181 }
else if(!strcasecmp(cmd,
"mkdir") || !strcasecmp(cmd,
"mkcol")) {
182 ret = cmd_mkdir(args);
183 }
else if(!strcasecmp(cmd,
"copy") || !strcasecmp(cmd,
"cp")) {
184 ret = cmd_move(args, true);
185 }
else if(!strcasecmp(cmd,
"move") || !strcasecmp(cmd,
"mv")) {
186 ret = cmd_move(args, false);
187 }
else if(!strcasecmp(cmd,
"rename")) {
188 ret = cmd_rename(args);
189 }
else if(!strcasecmp(cmd,
"export")) {
190 ret = cmd_get(args,
TRUE);
191 }
else if(!strcasecmp(cmd,
"import")) {
192 ret = cmd_put(args,
TRUE);
193 }
else if(!strcasecmp(cmd,
"date")) {
194 ret = cmd_date(args);
195 }
else if(!strcasecmp(cmd,
"set-property")) {
196 ret = cmd_set_property(args);
197 }
else if(!strcasecmp(cmd,
"get-property")) {
198 ret = cmd_get_property(args);
199 }
else if(!strcasecmp(cmd,
"remove-property")) {
200 ret = cmd_remove_property(args);
201 }
else if(!strcasecmp(cmd,
"lock")) {
202 ret = cmd_lock(args);
203 }
else if(!strcasecmp(cmd,
"unlock")) {
204 ret = cmd_unlock(args);
205 }
else if(!strcasecmp(cmd,
"info")) {
206 ret = cmd_info(args);
207 }
else if(!strcasecmp(cmd,
"checkout")) {
208 ret = cmd_checkout(args);
209 }
else if(!strcasecmp(cmd,
"checkin")) {
210 ret = cmd_checkin(args);
211 }
else if(!strcasecmp(cmd,
"uncheckout")) {
212 ret = cmd_uncheckout(args);
213 }
else if(!strcasecmp(cmd,
"versioncontrol")) {
214 ret = cmd_versioncontrol(args);
215 }
else if(!strcasecmp(cmd,
"list-versions") || !strcasecmp(cmd,
"lsv")) {
216 ret = cmd_list_versions(args);
217 }
else if(!strcasecmp(cmd,
"add-repository")
218 || !strcasecmp(cmd,
"add-repo")) {
219 ret = cmd_add_repository(args);
220 }
else if(!strcasecmp(cmd,
"remove-repository")
221 || !strcasecmp(cmd,
"remove-repo")
222 || !strcasecmp(cmd,
"rm-repo")) {
223 ret = cmd_remove_repository(args);
224 }
else if(!strcasecmp(cmd,
"list-repositories")
225 || !strcasecmp(cmd,
"list-repos")) {
226 ret = cmd_list_repositories();
227 }
else if(!strcasecmp(cmd,
"repository-url")
228 || !strcasecmp(cmd,
"repo-url")) {
229 ret = cmd_repository_url(args);
230 }
else if(!strcasecmp(cmd,
"add-user")) {
231 ret = cmd_add_user(args);
232 }
else if(!strcasecmp(cmd,
"list-users")) {
233 ret = cmd_list_users(args);
234 }
else if(!strcasecmp(cmd,
"remove-user")) {
235 ret = cmd_remove_user(args);
236 }
else if(!strcasecmp(cmd,
"edit-user")) {
237 ret = cmd_edit_user(args);
238 }
else if(!strcasecmp(cmd,
"set-master-password") || !strcasecmp(cmd,
"set-master-pw")) {
239 ret = cmd_set_master_password(args);
240 }
else if(!strcasecmp(cmd,
"version") || !strcasecmp(cmd,
"-version")
241 || !strcasecmp(cmd,
"--version")) {
242 fprintf(stderr,
"dav %s\n",
DAV_VERSION);
243 }
else if(!strcasecmp(cmd,
"complete")) {
244 ret = cmd_complete(args);
245 }
else {
246 print_usage(argv[
0]);
247 }
248 }
249
250 dav_context_destroy(ctx);
251 cmd_args_free(args);
252 free_config();
253 xmlCleanupParser();
254 curl_global_cleanup();
255 sys_uninit();
256
257 return ret;
258 }
259
260 static char *cmdusageinfo[] = {
261 "list [-altdepcR] [-u <date>] <url>",
262 "get [-pcRK] [-o <file>] [-u <date>] [-V <version>] <url>",
263 "put [-pcR] [-k <key>] [-L <lock>] <url> <file...>",
264 "edit [-pc] [-k <key>] [-V <version>] [-L <lock>] <url>",
265 "mkdir [-pc] [-k <key>] [-L <lock>] <url> [file...]",
266 "remove [-pc] [-L <lock>] <url> [file...]",
267 "copy [-pcO] [-L <lock>] <url> <url>",
268 "move [-pcO] [-L <lock>] <url> <url>",
269 "rename [-pcO] [-L <lock>] <url> <name>",
270 "export [-pc] [-o <file>] [-u <date>] <url>",
271 "import [-pc] [-k <key>] [-L <lock>] <url> <file>",
272 "get-property [-pcx] [-V <version>] [-n <uri>] <url> <property>",
273 "set-property [-pcx] [-L <lock>] [-n <uri>] <url> <property> [value]",
274 "remove-property [-pc] [-n <uri>] <url> <property>",
275 "lock [-pc] [-T timeout] <url>",
276 "unlock [-pc] [-L <lock>] <url>",
277 "info [-pc] [-V <version>] <url>",
278 "date [url]",
279 NULL
280 };
281
282 char* find_usage_str(
const char *cmd) {
283 cxstring c = cx_str(cmd);
284 for(
int i=
0;;i++) {
285 char *str = cmdusageinfo[i];
286 if(!str) {
287 break;
288 }
289 cxstring u = cx_str(str);
290 if(cx_strprefix(u, c)) {
291 return str;
292 }
293 }
294 return NULL;
295 }
296
297 void print_usage(
char *cmd) {
298 fprintf(stderr,
"Usage: %s command [options] arguments...\n\n", cmd);
299
300 fprintf(stderr,
"Commands:\n");
301 for(
int i=
0;;i++) {
302 char *str = cmdusageinfo[i];
303 if(!str) {
304 break;
305 }
306 fprintf(stderr,
" %s\n", str);
307 }
308 fprintf(stderr,
"Options:\n");
309 fprintf(stderr,
310 " -k <key> Key to use for encryption\n");
311 fprintf(stderr,
" -p Don''t encrypt or decrypt files\n");
312 fprintf(stderr,
" -c Enable full encryption\n");
313 fprintf(stderr,
314 " -R "
315 "Recursively do the operation for all children\n");
316 fprintf(stderr,
" -K Keep already present files\n");
317 fprintf(stderr,
" -o <file> Write output to file (use ''-'' for stdout)\n");
318 fprintf(
319 stderr,
320 " -u <date> "
321 "Get resources which are modified since the specified date\n");
322 fprintf(stderr,
" -V <version> Download a specific version of a resource\n");
323 fprintf(stderr,
" -a show all files\n");
324 fprintf(stderr,
" -l print resources in long list format\n");
325 fprintf(stderr,
" -t print content type\n");
326 fprintf(stderr,
" -d order by last modified date\n");
327 fprintf(stderr,
" -e show extended flags\n");
328 fprintf(stderr,
" -O override resources\n");
329 fprintf(stderr,
" -L <lock> specificy lock token\n");
330 fprintf(stderr,
" -T <sec> timeout in seconds\n");
331 fprintf(stderr,
" -n <uri> specify namespace uri\n");
332 fprintf(stderr,
" -x xml property content\n");
333 fprintf(stderr,
" -N disable authentication prompt (all commands)\n");
334 fprintf(stderr,
" -i disable cert verification (all commands)\n");
335 fprintf(stderr,
" -v verbose output (all commands)\n");
336 fprintf(stderr,
"\n");
337 fprintf(stderr,
"Advanced commands:\n");
338 fprintf(stderr,
" versioncontrol list-versions checkout checkin uncheckout\n\n");
339 fprintf(stderr,
"Config commands:\n");
340 fprintf(stderr,
" add-repository remove-repository list-repositories repository-url\n");
341 fprintf(stderr,
" add-user remove-user edit-user list-users set-master-password\n");
342 fprintf(stderr,
" check-config\n");
343 fprintf(stderr,
"\n");
344 fprintf(stderr,
345 "Instead of an url you can pass a repository name "
346 "with an optional path:\n");
347 fprintf(stderr,
" <repository>/path/\n");
348 fprintf(stderr,
"\n");
349 }
350
351
352
353 static int set_session_config(DavSession *sn, CmdArgs *a) {
354 char *plain = cmd_getoption(a,
"plain");
355 char *crypt = cmd_getoption(a,
"crypt");
356
357 if(plain && crypt) {
358 fprintf(stderr,
"Error: -p and -c option set\n");
359 return 1;
360 }
361
362 if (plain) {
363 sn->flags &= ~
DAV_SESSION_FULL_ENCRYPTION;
364 }
else if(crypt) {
365 sn->flags |=
DAV_SESSION_FULL_ENCRYPTION;
366 }
367
368 if (cmd_getoption(a,
"verbose")) {
369 curl_easy_setopt(sn->handle,
CURLOPT_VERBOSE,
1L);
370 curl_easy_setopt(sn->handle,
CURLOPT_STDERR, stderr);
371 sn->logfunc = dav_verbose_log;
372 }
373
374 return 0;
375 }
376
377 static void set_session_lock(DavSession *sn, CmdArgs *a) {
378 char *locktoken = cmd_getoption(a,
"lock");
379 if(locktoken) {
380 DavLock *lock = dav_create_lock(sn, locktoken,
NULL);
381 dav_add_collection_lock(sn,
"/", lock);
382 }
383 }
384
385
386 int update_progress(DavResource *res,
int64_t total,
int64_t now, Progress *p) {
387 int ret =
0;
388 if(res != p->last_resource) {
389 p->cur += p->last_res_total - p->last_res_cur;
390 ret =
1;
391 }
else {
392 p->cur += now - p->last_res_cur;
393 }
394
395 p->last_resource = res;
396 p->last_res_cur = now;
397 p->last_res_total = total;
398
399 return ret;
400 }
401
402 void download_progress(DavResource *res,
int64_t total,
int64_t now,
void *data) {
403 Progress *p = data;
404 int newres = update_progress(res, total, now, p);
405
406 time_t newts = time(
NULL);
407 if((p->ts != newts)) {
408 fprintf(p->out,
"[%s]: %" PRId64
"k/%" PRId64
"k total: %" PRId64
"M/%" PRId64
"M\n", res->name, now/
1024, total/
1024, p->cur/(
1024*
1024), p->total/(
1024*
1024));
409 fflush(p->out);
410 }
411 p->ts = newts;
412 }
413
414
415 #define LIST_QUERY_ORDER_BY_NAME "select `idav:crypto-name`,`idav:crypto-key`,D:lockdiscovery,apache:executable from %s with depth = %d where lastmodified > %t order by iscollection desc, name"
416 #define LIST_QUERY_ORDER_BY_DATE "select `idav:crypto-name`,`idav:crypto-key`,D:lockdiscovery,apache:executable from %s with depth = %d where lastmodified > %t order by iscollection desc, lastmodified desc"
417
418 int cmd_list(CmdArgs *a) {
419 if(a->argc !=
1) {
420 fprintf(stderr,
"Too %s arguments\n", a->argc <
1 ?
"few":
"many");
421 fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"list"));
422 return -
1;
423 }
424
425 char *url = a->argv[
0];
426 char *path =
NULL;
427 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
428 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
429
430 if(set_session_config(sn, a)) {
431 return -
1;
432 }
433
434 char *update = cmd_getoption(a,
"update");
435 char *date = cmd_getoption(a,
"date");
436 time_t t = -
1;
437 if(update) {
438 t = util_parse_lastmodified(update);
439 }
440
441 int depth = cmd_getoption(a,
"recursive") ? -
1 :
1;
442 int ret =
0;
443
444 DavResource *ls = dav_query(
445 sn,
446 date ?
LIST_QUERY_ORDER_BY_DATE :
LIST_QUERY_ORDER_BY_NAME,
447 path,
448 depth,
449 t);
450 if(ls) {
451
452 void (*print_func)(DavResource*,
char *, CmdArgs *);
453 if(cmd_getoption(a,
"list") || cmd_getoption(a,
"extended")) {
454 print_func = ls_print_list_elm;
455 }
else {
456 print_func = ls_print_elm;
457 }
458
459 DavResource *child = ls->children;
460 while(child) {
461 print_func(child, path, a);
462 child = child->next;
463 }
464 }
else {
465 print_resource_error(sn, path);
466 ret = -
1;
467 }
468
469 free(path);
470
471
472 dav_session_destroy(sn);
473
474 return ret;
475 }
476
477 static char* ls_name(
char *parent,
char *path,
int *len) {
478 if(parent) {
479 path += strlen(parent);
480 }
481 if(path[
0] ==
'/') {
482 path++;
483 }
484 int pathlen = strlen(path);
485 if(path[pathlen-
1] ==
'/') {
486 pathlen--;
487 }
488 *len = pathlen;
489 return path;
490 }
491
492 void ls_print_list_elm(DavResource *res,
char *parent, CmdArgs *a) {
493 int recursive = cmd_getoption(a,
"recursive") ?
1 :
0;
494 int show_all = cmd_getoption(a,
"all") ?
1 :
0;
495 if(res->name[
0] ==
'.' && !show_all) {
496 return;
497 }
498
499 char flags[
16];
500 memset(flags,
'-',
15);
501
502 int type_width =
0;
503 char *type = res->contenttype;
504
505 if(res->iscollection) {
506 flags[
0] =
'd';
507 type =
"";
508 }
509 char *keyprop = dav_get_string_property_ns(
510 res,
511 DAV_NS,
512 "crypto-key");
513 if(keyprop) {
514 flags[
1] =
'c';
515 }
516
517 if(cmd_getoption(a,
"extended")) {
518 flags[
6] =
'\0';
519 if(dav_get_string_property(res,
"D:lockdiscovery")) {
520 flags[
2] =
'l';
521 }
522 char *executable = dav_get_string_property_ns(
523 res,
524 "http://apache.org/dav/props/",
525 "executable");
526 if(executable && util_getboolean(executable)) {
527 flags[
3] =
'x';
528 }
529 }
else {
530 flags[
2] =
'\0';
531 }
532
533 if(cmd_getoption(a,
"type")) {
534 type_width =
20;
535 }
536 if(type ==
NULL || type_width ==
0) {
537 type =
"";
538 }
539
540 char *date = util_date_str(res->lastmodified);
541 char *size = util_size_str(res->iscollection, res->contentlength);
542 int namelen = strlen(res->name);
543 char *name = recursive ? ls_name(parent, res->path, &namelen) : res->name;
544
545
546 printf(
547 "%s %*s %10s %12s %.*s\n",
548 flags,
549 type_width, type,
550 size,
551 date,
552 namelen,
553 name);
554 free(date);
555 free(size);
556
557 if(recursive) {
558 DavResource *child = res->children;
559 while(child) {
560
561 if(child->name[
0] !=
'.' || show_all) {
562 ls_print_list_elm(child, parent, a);
563 }
564 child = child->next;
565 }
566 }
567 }
568
569 void ls_print_elm(DavResource *res,
char *parent, CmdArgs *a) {
570 int recursive = cmd_getoption(a,
"recursive") ?
1 :
0;
571 int show_all = cmd_getoption(a,
"all") ?
1 :
0;
572 if(res->name[
0] ==
'.' && !show_all) {
573 return;
574 }
575
576 int namelen = strlen(res->name);
577 char *name = recursive ? ls_name(parent, res->path, &namelen) : res->name;
578 printf(
"%.*s\n", namelen, name);
579 if(recursive) {
580 DavResource *child = res->children;
581 while(child) {
582 ls_print_elm(child, parent, a);
583 child = child->next;
584 }
585 }
586 }
587
588 static void free_getres(
void *r) {
589 GetResource *getres = r;
590 free(getres->path);
591 free(getres);
592 }
593
594 int cmd_get(CmdArgs *a, DavBool export) {
595 if(a->argc !=
1) {
596
597 fprintf(stderr,
"Too %s arguments\n", a->argc <
1 ?
"few":
"many");
598 fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"get"));
599 return -
1;
600 }
601 if(export) {
602 cxMapPut(a->options, cx_hash_key_str(
"recursive"),
"");
603 }
604
605 char *url = a->argv[
0];
606 char *path =
NULL;
607 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
608 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
609
610 if(set_session_config(sn, a)) {
611 return -
1;
612 }
613
614 char *progressfile = cmd_getoption(a,
"progressfile");
615 Progress pdata;
616 memset(&pdata,
0,
sizeof(Progress));
617 if(progressfile) {
618 if(!strcmp(progressfile,
"-")) {
619 pdata.out = stdout;
620 pdata.isstdout =
1;
621 }
else {
622 pdata.out = fopen(progressfile,
"w");
623 }
624 if(pdata.out) {
625 dav_session_set_progresscallback(sn, download_progress,
NULL, &pdata);
626 }
627 }
628
629 char *update = cmd_getoption(a,
"update");
630 time_t t = -
1;
631 if(update) {
632 t = util_parse_lastmodified(update);
633 if (t ==
0) {
634 fprintf(stderr,
635 "Invalid date format. Possible formats are:\n"
636 " RFC-1123 - example: Thu, 29 Nov 2012 21:35:35 GMT\n"
637 " RFC-3339 - example: 2012-11-29T21:35:35Z\n");
638 return -
1;
639 }
640 }
641
642 int recursive = cmd_getoption(a,
"recursive") ?
1 :
0;
643 char *version = cmd_getoption(a,
"version");
644
645 if(recursive && version) {
646 fprintf(stderr,
"-V option can only be used without -R option\n");
647 return -
1;
648 }
649
650 DavResource *res;
651
652 int depth = recursive ? -
1 :
1;
653 res = dav_query(
654 sn,
655 "select - from %s with depth = %d where iscollection or lastmodified > %t",
656 path,
657 depth,
658 t);
659 if(!res) {
660 print_resource_error(sn, path);
661 return -
1;
662 }
663 if(!recursive && res->iscollection) {
664 fprintf(stderr,
"Resource %s is a collection.\n", res->path);
665 fprintf(stderr,
"Use the -R option to download collections.\n");
666 return -
1;
667 }
668
669 if(version) {
670 DavResource *vres = find_version(res, version);
671 if(!vres) {
672 fprintf(stderr,
"Cannot find version ''%s'' for resource.\n", version);
673 return -
1;
674 }
675 dav_resource_free_all(res);
676 res = vres;
677 }
678
679
680
681
682
683 char *outfile = cmd_getoption(a,
"output");
684 char *basepath = outfile;
685 if(!outfile) {
686 if(res->iscollection) {
687 basepath =
"";
688 }
else {
689 basepath = res->name;
690 }
691 if(export) {
692 outfile =
"-";
693 }
694 }
else if(export) {
695 basepath =
"";
696 }
else if(res->iscollection && !strcmp(outfile,
"-")) {
697 fprintf(
698 stderr,
699 "Cannot write output to stdout "
700 "if the requested resource is a collection.\n");
701 return -
1;
702 }
703
704
705 CxList *reslist = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
706 reslist->simple_destructor = (cx_destructor_func)free_getres;
707 uint64_t totalsize =
0;
708 uint64_t rescount =
0;
709
710 GetResource *getres = malloc(
sizeof(GetResource));
711 getres->res = res;
712 getres->path = strdup(basepath);
713
714 char *structure = cmd_getoption(a,
"structure");
715
716
717 CxList *stack = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
718 cxListInsert(stack,
0, getres);
719 while(stack->size >
0) {
720 GetResource *g = cxListAt(stack,
0);
721 cxListRemove(stack,
0);
722
723 if(g->res->iscollection) {
724 DavResource *child = g->res->children;
725 while(child) {
726
727 size_t pathlen = strlen(g->path);
728 GetResource *newres = malloc(
sizeof(GetResource));
729 newres->res = child;
730 newres->path = pathlen >
0 ?
731 util_concat_path(g->path, child->name) : strdup(child->name);
732
733 cxListInsert(stack,
0, newres);
734
735 child = child->next;
736 }
737 }
else {
738 if(structure) {
739
740
741 continue;
742 }
743 totalsize += g->res->contentlength;
744 rescount++;
745 }
746
747 if(strlen(g->path) ==
0) {
748 free_getres(g);
749 }
else {
750 cxListAdd(reslist, g);
751 }
752 }
753 cxListDestroy(stack);
754
755
756 pdata.total = totalsize;
757
758 int ret;
759 getfunc get;
760 TarOutputStream *tout =
NULL;
761 if(export) {
762 get = (getfunc)resource2tar;
763 FILE *tarfile = strcmp(outfile,
"-") ? fopen(outfile,
"wb") : stdout;
764 if(!tarfile) {
765 perror(
"Cannot open tar output file");
766 return -
1;
767 }
768 tout = tar_open(tarfile);
769 }
else {
770 get = get_resource;
771 }
772 CxIterator i = cxListIterator(reslist);
773 cx_foreach(GetResource *, getres, i) {
774 ret = get(repo, getres, a, tout);
775 if(ret) {
776 break;
777 }
778 }
779 if(export) {
780
781 if(tar_close(tout)) {
782 fprintf(stderr,
"tar stream broken\n");
783 ret = -
1;
784 }
785 }
786
787 cxListDestroy(reslist);
788 free(path);
789
790 if(pdata.out && !pdata.isstdout) {
791 fclose(pdata.out);
792 }
793 return ret;
794 }
795
796 static int file_seek(
FILE *f,
curl_off_t offset,
int origin) {
797 int ret = fseek(f, offset, origin);
798 return ret ==
0 ?
CURL_SEEKFUNC_OK :
CURL_SEEKFUNC_CANTSEEK;
799 }
800
801 static int check_encryption_key(CmdArgs *a, DavSession *sn) {
802
803 char *keyname = cmd_getoption(a,
"key");
804 if(keyname) {
805 DavKey *key = dav_context_get_key(ctx, keyname);
806 if(key) {
807 sn->key = key;
808 }
else {
809 fprintf(stderr,
"Key %s not found!\nAbort.\n", keyname);
810 return 1;
811 }
812
813
814
815
816
817
818 if(!
DAV_IS_ENCRYPTED(sn)) {
819 fprintf(stderr,
"A key has been explicitly specified, but no "
820 "encryption is requested.\n"
821 "You have the following options:\n"
822 " - pass ''-c'' as command line argument to request encryption\n"
823 " - activate encryption in the config.xml\n"
824 " - don''t use ''-k <key>'' "
825 "(warning: encryption will NOT happen)\n");
826 return 1;
827 }
828 }
829
830
831 if(
DAV_IS_ENCRYPTED(sn) && !(sn->key)) {
832 fprintf(stderr,
"Encryption has been requested, "
833 "but no default key is configured.\n"
834 "You may specify a custom key with the ''-k'' option.\n");
835 return 1;
836 }
837
838 return 0;
839 }
840
841 int cmd_edit(CmdArgs *a) {
842 #ifdef _WIN32
843 fprintf(stderr,
"This feature is not supported on your platform.\n");
844 return -
1;
845 #else
846 if(a->argc !=
1) {
847 fprintf(stderr,
"Too %s arguments\n", a->argc <
1 ?
"few":
"many");
848 fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"edit"));
849 return -
1;
850 }
851
852 char *url = a->argv[
0];
853 char *path =
NULL;
854 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
855 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
856
857 if(set_session_config(sn, a)) {
858 return -
1;
859 }
860 set_session_lock(sn, a);
861
862 if(check_encryption_key(a, sn)) {
863 return -
1;
864 }
865
866 char *version = cmd_getoption(a,
"version");
867 DavResource *res;
868
869 res = dav_resource_new(sn, path);
870 int fresh_resource = !dav_exists(res);
871 if(fresh_resource) {
872
873
874 if(dav_create(res)) {
875 fprintf(stderr,
"Resource does not exist and cannot be created.\n");
876 return -
1;
877 }
878 }
else {
879 if(!res) {
880 print_resource_error(sn, path);
881 return -
1;
882 }
883 if(res->iscollection) {
884 fprintf(stderr,
"Resource %s is a collection "
885 "and cannot be opened in an editor.\n", res->path);
886 return -
1;
887 }
888
889 if(version) {
890 DavResource *vres = find_version(res, version);
891 if(!vres) {
892 fprintf(stderr,
"Cannot find version ''%s'' for resource.\n", version);
893 return -
1;
894 }
895 dav_resource_free_all(res);
896 res = vres;
897 }
898 }
899
900
901 char* envtmp = getenv(
"TMPDIR");
902 char* outfile;
903 if(envtmp) {
904 size_t len = strlen(envtmp);
905 outfile = malloc(len+
24);
906 memcpy(outfile, envtmp, len+
1);
907 if(outfile[len-
1] !=
'/') {
908 outfile[len] =
'/';
909 outfile[len+
1] =
0;
910 }
911 }
else {
912 outfile = malloc(
24);
913 strncpy(outfile,
"/tmp/",
24);
914 }
915
916
917 if(access(outfile,
W_OK)) {
918 char* home = getenv(
"HOME");
919 if(home) {
920 size_t len = strlen(home);
921 outfile = malloc(len+
24);
922 memcpy(outfile, home, len+
1);
923 if(outfile[len-
1] !=
'/') {
924 outfile[len] =
'/';
925 outfile[len+
1] =
0;
926 }
927 }
else {
928
929 perror(
"Cannot write to temporary location");
930 free(outfile);
931 return -
1;
932 }
933 }
934
935
936 outfile = strncat(outfile,
".dav-edit-XXXXXX",
23);
937 int tmp_fd = mkstemp(outfile);
938 if(tmp_fd <
0) {
939 perror(
"Cannot open temporary file");
940 return -
1;
941 }
942
943
944 if(!fresh_resource) {
945 FILE* tmp_stream = sys_fopen(outfile,
"wb");
946 if(!tmp_stream) {
947 perror(
"Cannot open temporary file");
948 free(outfile);
949 close(tmp_fd);
950 return -
1;
951 }
952 if(dav_get_content(res, tmp_stream, (dav_write_func)fwrite)) {
953 print_resource_error(sn, path);
954 free(outfile);
955 close(tmp_fd);
956 sys_unlink(outfile);
957 return -
1;
958 }
959 fclose(tmp_stream);
960 }
961
962
963 SYS_STAT tmp_stat;
964 if(sys_stat(outfile, &tmp_stat)) {
965 perror(
"Cannot stat temporary file");
966 free(outfile);
967 close(tmp_fd);
968 sys_unlink(outfile);
969 return -
1;
970 }
971 time_t dl_mtime = tmp_stat.st_mtime;
972
973
974 char* default_editor =
"vi";
975 char* editor = getenv(
"EDITOR");
976 if(!editor) editor = default_editor;
977 char* viargs[
3] = {editor, outfile,
NULL};
978
979 int ret =
0;
980 pid_t pid = fork();
981 if(pid <
0) {
982 perror(
"Cannot create process for editor");
983 ret = -
1;
984 }
else if(pid ==
0) {
985 if(execvp(viargs[
0], viargs)) {
986 perror(
"Opening the editor failed");
987 return -
1;
988 }
989 }
else {
990 int status = -
1;
991 ret = waitpid(pid, &status,
0);
992 if(ret <
0) {
993 perror(
"Error waiting for editor");
994 }
else if(
WEXITSTATUS(status)) {
995 fprintf(stderr,
996 "Editor closed abnormally - file will not be uploaded.\n");
997 ret = -
1;
998 }
else {
999
1000 if (sys_stat(outfile, &tmp_stat)) {
1001
1002 perror(
"Cannot stat temporary file");
1003 ret = -
1;
1004 }
else if (dl_mtime < tmp_stat.st_mtime) {
1005
1006 FILE* tmp_stream = sys_fopen(outfile,
"rb");
1007 if(!tmp_stream) {
1008 perror(
"Cannot open temporary file");
1009 ret = -
1;
1010 }
else {
1011 dav_set_content(res, tmp_stream,
1012 (dav_read_func)fread,
1013 (dav_seek_func)file_seek);
1014 dav_set_content_length(res, tmp_stat.st_size);
1015 ret = dav_store(res);
1016 fclose(tmp_stream);
1017 if(ret) {
1018 print_resource_error(sn, path);
1019 }
1020 }
1021 }
else {
1022 printf(
"No changes by user - file will not be uploaded.\n");
1023 ret =
0;
1024 }
1025 }
1026 }
1027
1028 close(tmp_fd);
1029 if(ret) {
1030
1031 fprintf(stderr,
"File location: %s\n", outfile);
1032 }
else {
1033 sys_unlink(outfile);
1034 }
1035 free(outfile);
1036 free(path);
1037
1038 return ret;
1039 #endif
1040 }
1041
1042 int get_resource(DavCfgRepository *repo, GetResource *getres, CmdArgs *a,
void *unused) {
1043 DavResource *res = getres->res;
1044 char *out = getres->path;
1045
1046 if(res->iscollection) {
1047 printf(
"get: %s\n", res->path);
1048
1049 int ret = sys_mkdir(out);
1050 if(ret !=
0 && errno !=
EEXIST) {
1051 fprintf(stderr,
"Cannot create directory ''%s'': ", out);
1052 perror(
"");
1053 return 1;
1054 }
1055
1056 return 0;
1057 }
1058
1059 int isstdout = !strcmp(out,
"-");
1060 if(cmd_getoption(a,
"keep") && !isstdout) {
1061 SYS_STAT s;
1062 if(sys_stat(out, &s)) {
1063 if(errno !=
ENOENT) {
1064 perror(
"stat");
1065 }
1066 }
else {
1067 if(cmd_getoption(a,
"recursive")) {
1068 printf(
"skip: %s\n", res->path);
1069 }
1070 return 0;
1071 }
1072 }
1073
1074
1075 if(cmd_getoption(a,
"recursive")) {
1076 printf(
"get: %s\n", res->path);
1077 }
1078
1079 FILE *fout = isstdout ? stdout : sys_fopen(out,
"wb");
1080 if(!fout) {
1081 fprintf(stderr,
"cannot open output file\n");
1082 return -
1;
1083 }
1084
1085 int ret = dav_get_content(res, fout, (dav_write_func)fwrite);
1086 fclose(fout);
1087 if(ret && strcmp(out,
"-")) {
1088 print_resource_error(res->session, res->path);
1089
1090
1091
1092 }
1093
1094 return 0;
1095 }
1096
1097 #define DEFAULT_DIR_MODE T_IRUSR |
T_IWUSR |
T_IXUSR |
T_IRGRP |
T_IXGRP |
T_IROTH |
T_IXOTH
1098 #define DEFAULT_FILE_MODE T_IRUSR |
T_IWUSR |
T_IRGRP |
T_IROTH
1099
1100 int resource2tar(DavCfgRepository *repo, GetResource *res, CmdArgs *a, TarOutputStream *tar) {
1101 DavResource *d = res->res;
1102
1103 if(d->iscollection) {
1104 fprintf(stderr,
"add d: %s\n", res->path);
1105 return tar_add_dir(tar, res->path,
DEFAULT_DIR_MODE, d->lastmodified);
1106 }
1107
1108 fprintf(stderr,
"add f: %s\n", res->path);
1109
1110
1111 if(tar_begin_file(tar, res->path,
DEFAULT_FILE_MODE, d->contentlength, d->lastmodified)) {
1112 fprintf(stderr,
"TAR Error: %s\n", tar_error2str(tar->error));
1113 return -
1;
1114 }
1115
1116 if(dav_get_content(d, tar, (dav_write_func)tar_fwrite)) {
1117 print_resource_error(d->session, d->path);
1118 return -
1;
1119 }
1120
1121
1122
1123 return tar_end_file(tar);
1124 }
1125
1126 int cmd_put(CmdArgs *a, DavBool import) {
1127 if(a->argc <
2) {
1128 fprintf(stderr,
"Too few arguments\n");
1129 fprintf(stderr,
1130 "Usage: dav %s\n",
1131 find_usage_str(import ?
"import" :
"put"));
1132 return -
1;
1133 }
1134 DavBool use_stdin =
FALSE;
1135 for(
int i=
1;i<a->argc;i++) {
1136 if(!strcmp(a->argv[i],
"-")) {
1137 if(use_stdin) {
1138 fprintf(stderr,
"Error: stdin can only occur once in input list\n");
1139 return -
1;
1140 }
else {
1141 use_stdin =
TRUE;
1142 }
1143 }
1144 }
1145
1146 if(import) {
1147 cxMapPut(a->options, cx_hash_key_str(
"resursive"),
"");
1148 }
1149
1150 char *url = a->argv[
0];
1151 char *path =
NULL;
1152 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
1153 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
1154
1155 if(set_session_config(sn, a)) {
1156 return -
1;
1157 }
1158 set_session_lock(sn, a);
1159
1160 if(check_encryption_key(a, sn)) {
1161
1162 return -
1;
1163 }
1164
1165 DavBool printfile =
FALSE;
1166 DavBool ignoredirerr =
FALSE;
1167 if(a->argc >
2) {
1168 printfile =
TRUE;
1169 ignoredirerr =
TRUE;
1170 }
else if(cmd_getoption(a,
"recursive")) {
1171 printfile =
TRUE;
1172 }
1173
1174 char *finfo_str = cmd_getoption(a,
"finfo");
1175 uint32_t finfo =
0;
1176 if(finfo_str) {
1177 finfo = parse_finfo_settings(finfo_str,
NULL);
1178 }
1179
1180 int ret;
1181 for(
int i=
1;i<a->argc;i++) {
1182 char *file = a->argv[i];
1183 if(!import) {
1184 if(!strcmp(file,
"-")) {
1185 FILE *in = stdin;
1186 ret = put_file(repo, a, sn, path,
"stdin",
0,
NULL, in,
0);
1187 }
else {
1188 ret = put_entry(
1189 repo,
1190 a,
1191 sn,
1192 path,
1193 file,
1194 finfo,
1195 TRUE,
1196 printfile,
1197 ignoredirerr);
1198 }
1199 }
else {
1200 ret = put_tar(repo, a, sn, file, path);
1201 }
1202 if(ret) {
1203 break;
1204 }
1205 }
1206
1207 free(path);
1208 dav_session_destroy(sn);
1209 return ret;
1210 }
1211
1212 #if !defined(
S_ISREG) && defined(
S_IFMT) && defined(
S_IFREG)
1213 #define S_ISREG(m) (((m) &
S_IFMT) ==
S_IFREG)
1214 #endif
1215
1216 #ifdef _WIN32
1217 #ifndef S_ISDIR
1218 #define S_ISDIR(mode) ((mode) &
_S_IFMT) ==
_S_IFDIR
1219 #define S_ISREG(mode) ((mode) &
_S_IFMT) ==
_S_IFREG
1220 #endif
1221 #endif
1222
1223
1224 int put_entry(
1225 DavCfgRepository *repo,
1226 CmdArgs *a,
1227 DavSession *sn,
1228 char *path,
1229 char *file,
1230 uint32_t finfo,
1231 DavBool root,
1232 DavBool printfile,
1233 DavBool ignoredirerr)
1234 {
1235 int recursive = cmd_getoption(a,
"recursive") ?
1 :
0;
1236 SYS_STAT s;
1237 if(sys_stat(file, &s)) {
1238 perror(
"stat");
1239 fprintf(stderr,
"cannot stat file %s\n", file);
1240 return -
1;
1241 }
1242
1243 int ret =
0;
1244 if(
S_ISDIR(s.st_mode)) {
1245 if(!recursive) {
1246 if(ignoredirerr) {
1247 printf(
"skip: %s\n", file);
1248 }
else {
1249 fprintf(
1250 stderr,
1251 "%s is a directory.\nUse the -R option to upload directories.\n",
1252 file);
1253 return 1;
1254 }
1255 }
1256
1257 if(!root) {
1258 printf(
"mkcol: %s\n", file);
1259 DavResource *res = dav_resource_new(sn, path);
1260 res->iscollection =
TRUE;
1261 if(!dav_exists(res)) {
1262 if(dav_create(res)) {
1263 fprintf(stderr,
"Cannot create collection %s\n", path);
1264 print_resource_error(sn, res->path);
1265 dav_resource_free(res);
1266 return 1;
1267 }
1268 }
1269 dav_resource_free(res);
1270 }
1271
1272 SYS_DIR dir = sys_opendir(file);
1273 if(!dir) {
1274
1275 }
1276 SysDirEnt *entry;
1277 int nument =
0;
1278 while((entry = sys_readdir(dir)) !=
NULL) {
1279 if(!strcmp(entry->name,
".") || !strcmp(entry->name,
"..")) {
1280 continue;
1281 }
1282 nument++;
1283 char *entry_file = util_concat_path(file, entry->name);
1284 char *entry_path = util_concat_path(path, entry->name);
1285 int r = put_entry(
1286 repo,
1287 a,
1288 sn,
1289 entry_path,
1290 entry_file,
1291 finfo,
1292 FALSE,
1293 printfile,
1294 ignoredirerr);
1295 free(entry_path);
1296 free(entry_file);
1297 if(r) {
1298 ret =
1;
1299 break;
1300 }
1301 }
1302 sys_closedir(dir);
1303 }
else if(
S_ISREG(s.st_mode)) {
1304 if(printfile) {
1305 printf(
"put: %s\n", file);
1306 }
1307
1308 FILE *in = sys_fopen(file,
"rb");
1309 if(!in) {
1310 fprintf(stderr,
"cannot open input file\n");
1311 return -
1;
1312 }
1313 const char *filename = util_resource_name(file);
1314
1315 ret = put_file(repo, a, sn, path, filename, finfo, file, in, s.st_size);
1316
1317 fclose(in);
1318 }
1319
1320 return ret;
1321 }
1322
1323 int put_tar(DavCfgRepository *repo, CmdArgs *a, DavSession *sn,
char *tarfile,
char *path) {
1324 int isstdin = !strcmp(tarfile,
"-");
1325 FILE *in = isstdin ? stdin : fopen(tarfile,
"rb");
1326 if(!in) {
1327 perror(
"Cannot open tar file");
1328 return -
1;
1329 }
1330
1331 DavResource *col = dav_query(sn,
"select - from %s", path);
1332 if(!col) {
1333 if(sn->error ==
DAV_NOT_FOUND) {
1334 col = dav_resource_new(sn, path);
1335 col->iscollection =
TRUE;
1336 if(dav_create(col)) {
1337 print_resource_error(sn, path);
1338 return -
1;
1339 }
1340 }
else {
1341 print_resource_error(sn, path);
1342 return -
1;
1343 }
1344 }
else if(!col->iscollection) {
1345 fprintf(stderr,
"%s is not a collection\n", col->href);
1346 return -
1;
1347 }
1348
1349
1350 int ret =
0;
1351 TarInputStream *tar = tar_inputstream_open(in);
1352 TarEntry *e =
NULL;
1353 while((e = tar_read_entry(tar)) !=
NULL) {
1354 char *newpath = util_concat_path(path, e->path);
1355 if(e->type ==
TAR_TYPE_FILE) {
1356 fprintf(stderr,
"put: %s\n", e->path);
1357 DavResource *res = dav_resource_new(sn, newpath);
1358 dav_set_content(res, tar, (dav_read_func)tar_fread, (dav_seek_func)tar_seek);
1359 dav_set_content_length(res, (
size_t)e->size);
1360
1361 if(dav_store(res)) {
1362 print_resource_error(sn, res->path);
1363 fprintf(stderr,
"Cannot upload file.\n");
1364 if(sn->errorstr) {
1365 fprintf(stderr,
"%s\n", sn->errorstr);
1366 }
1367 return -
1;
1368 }
1369
1370 }
else if(e->type ==
TAR_TYPE_DIRECTORY) {
1371 printf(
"mkcol: %s\n", e->path);
1372 DavResource *res = dav_resource_new(sn, newpath);
1373 res->iscollection =
TRUE;
1374 if(!dav_exists(res)) {
1375 if(dav_create(res)) {
1376 fprintf(stderr,
"Cannot create collection %s\n", newpath);
1377 print_resource_error(sn, res->path);
1378 ret =
1;
1379 free(newpath);
1380 break;
1381 }
1382 }
1383 }
else {
1384 fprintf(stderr,
"skip: %s\n", e->path);
1385 }
1386 free(newpath);
1387 }
1388 if(tar->error !=
TAR_OK) {
1389 ret = -
1;
1390 }
1391
1392 if(!isstdin) {
1393 fclose(in);
1394 }
1395
1396 return ret;
1397 }
1398
1399 int put_file(
1400 DavCfgRepository *repo,
1401 CmdArgs *a,
1402 DavSession *sn,
1403 const char *path,
1404 const char *name,
1405 uint32_t finfo,
1406 const char *fpath,
1407 FILE *in,
1408 off_t len)
1409 {
1410 DavResource *res = dav_query(sn,
"select - from %s", path);
1411
1412 if(!res) {
1413 if(sn->error ==
DAV_NOT_FOUND) {
1414 res = dav_resource_new(sn, path);
1415 if(dav_create(res)) {
1416 fprintf(stderr,
"Cannot create resource.\n");
1417 return -
1;
1418 }
1419 }
else {
1420 print_resource_error(sn, path);
1421 return -
1;
1422 }
1423 }
else if(res->iscollection) {
1424
1425 char *newpath = util_concat_path(path, name);
1426
1427 if (!strcmp(path, newpath)) {
1428
1429 fprintf(stderr,
"Cannot put file, because a collection with "
1430 "that name already exists.\n");
1431 free(newpath);
1432 return -
1;
1433 }
1434
1435 path = newpath;
1436 res = dav_resource_new(sn, path);
1437 int ret = put_file(repo, a, sn, res->path,
NULL, finfo, fpath, in, len);
1438
1439 free(newpath);
1440 return ret;
1441 }
1442
1443 if(resource_set_finfo(fpath, res, finfo)) {
1444 fprintf(stderr,
"Cannot set finfo: %s.\n", strerror(errno));
1445 }
1446 if((finfo &
FINFO_XATTR) ==
FINFO_XATTR) {
1447 XAttributes *xattr = file_get_attributes(fpath,
NULL,
NULL);
1448 if(xattr) {
1449 resource_set_xattr(res, xattr);
1450 }
1451 }
1452
1453 dav_set_content(res, in, (dav_read_func)fread, (dav_seek_func)file_seek);
1454 if(len >
0 && len < 0x7d000000) {
1455 dav_set_content_length(res, (
size_t)len);
1456 }
1457
1458 if(dav_store(res)) {
1459 print_resource_error(sn, res->path);
1460 fprintf(stderr,
"Cannot upload file.\n");
1461 if(sn->errorstr) {
1462 fprintf(stderr,
"%s\n", sn->errorstr);
1463 }
1464 return -
1;
1465 }
1466 return 0;
1467 }
1468
1469 static int dav_create_col(DavResource *res) {
1470 res->iscollection =
1;
1471 return dav_create(res);
1472 }
1473
1474 static void print_cannoterr(DavSession *sn,
const char *message) {
1475 switch(sn->error) {
1476 case DAV_UNSUPPORTED_PROTOCOL:
1477 case DAV_COULDNT_RESOLVE_PROXY:
1478 case DAV_COULDNT_RESOLVE_HOST:
1479 case DAV_COULDNT_CONNECT:
1480 case DAV_TIMEOUT:
1481 case DAV_SSL_ERROR:
break;
1482 default: fprintf(stderr,
"Cannot %s.\n", message);
1483 }
1484 }
1485
1486 static int cmd_operation_on_resources(CmdArgs* a,
1487 int(*operation)(DavResource*),
1488 const char* command,
1489 const char* message,
1490 DavBool check_key)
1491 {
1492 if(a->argc <
1) {
1493 fprintf(stderr,
"Too few arguments\n");
1494 fprintf(stderr,
"Usage: dav %s\n", find_usage_str(command));
1495 return -
1;
1496 }
1497
1498 char *url = a->argv[
0];
1499 char *path =
NULL;
1500 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
1501 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
1502
1503 int exit_code = -
1;
1504 assert(!!path && !!sn);
1505
1506 if(set_session_config(sn, a)) {
1507 goto cmd_oponres_exit;
1508 }
1509
1510 set_session_lock(sn, a);
1511
1512 if(check_key && check_encryption_key(a, sn)) {
1513 goto cmd_oponres_exit;
1514 }
1515
1516 DavResource *res = dav_resource_new(sn, path);
1517 assert(!!res);
1518 res->iscollection =
1;
1519
1520 if(a->argc ==
1) {
1521 if(operation(res)) {
1522 print_cannoterr(sn, message);
1523 print_resource_error(sn, res->path);
1524 goto cmd_oponres_exit;
1525 }
1526 }
else {
1527 for(
int i =
1 ; i < a->argc ;++i) {
1528 DavResource *child = dav_resource_new_child(sn, res, a->argv[i]);
1529 assert(!!child);
1530 child->iscollection =
1;
1531 if(operation(child)) {
1532 print_cannoterr(sn, message);
1533 print_resource_error(sn, child->path);
1534 goto cmd_oponres_exit;
1535 }
1536 }
1537 }
1538
1539 exit_code =
0;
1540 cmd_oponres_exit:
1541 free(path);
1542 dav_session_destroy(sn);
1543 return exit_code;
1544 }
1545
1546 int cmd_remove(CmdArgs *a) {
1547 return cmd_operation_on_resources(a, dav_delete,
1548 "remove",
"delete resource",
FALSE);
1549 }
1550
1551 int cmd_mkdir(CmdArgs *a) {
1552 return cmd_operation_on_resources(a, dav_create_col,
1553 "mkdir",
"create collection",
TRUE);
1554 }
1555
1556 int cmd_move(CmdArgs *a,
int cp) {
1557 const char* actionstr = cp ?
"copy" :
"move";
1558
1559 if(a->argc !=
2) {
1560
1561 fprintf(stderr,
"Too %s arguments\n", a->argc <
2 ?
"few":
"many");
1562 fprintf(stderr,
"Usage: dav %s\n", find_usage_str(actionstr));
1563 return -
1;
1564 }
1565
1566 char *srcurl = a->argv[
0];
1567 char *srcpath =
NULL;
1568 DavCfgRepository *srcrepo = dav_config_url2repo(get_config(), srcurl, &srcpath);
1569
1570 DavSession *srcsn = connect_to_repo(ctx, srcrepo, srcpath, request_auth, a);
1571 if(set_session_config(srcsn, a)) {
1572 return -
1;
1573 }
1574 set_session_lock(srcsn, a);
1575
1576 DavBool override = cmd_getoption(a,
"override") ? true : false;
1577
1578 char *desturl = a->argv[
1];
1579 char *destpath =
NULL;
1580 DavCfgRepository *destrepo = dav_config_url2repo(get_config(), desturl, &destpath);
1581
1582 if(srcrepo == destrepo) {
1583 DavResource *res = dav_resource_new(srcsn, srcpath);
1584 int err = cp ? dav_copy_o(res, destpath, override)
1585 : dav_move_o(res, destpath, override);
1586 if(err) {
1587 print_resource_error(srcsn, res->path);
1588 fprintf(stderr,
"Cannot %s resource.\n", actionstr);
1589 return -
1;
1590 }
1591 }
else {
1592 char *srchost = util_url_base(srcrepo->url.value.ptr);
1593 char *desthost = util_url_base(destrepo->url.value.ptr);
1594 if(!strcmp(srchost, desthost)) {
1595 DavSession *destsn = connect_to_repo(ctx, destrepo, destpath, request_auth, a);
1596 if(set_session_config(destsn, a)) {
1597 return -
1;
1598 }
1599 DavResource *dest = dav_resource_new(destsn, destpath);
1600 char *desthref = dav_resource_get_href(dest);
1601 char *desturl = util_get_url(destsn, desthref);
1602
1603 DavResource *res = dav_resource_new(srcsn, srcpath);
1604 int err = cp ? dav_copyto(res, desturl, override)
1605 : dav_moveto(res, desturl, override);
1606
1607 free(desturl);
1608 dav_session_destroy(destsn);
1609
1610 if(err) {
1611 print_resource_error(srcsn, res->path);
1612 fprintf(stderr,
"Cannot %s resource.\n", actionstr);
1613 return -
1;
1614 }
1615 }
else {
1616 fprintf(stderr,
"Cannot %s between different hosts.\n", actionstr);
1617 return -
1;
1618 }
1619 }
1620
1621 dav_session_destroy(srcsn);
1622
1623 return 0;
1624 }
1625
1626 int cmd_rename(CmdArgs *a) {
1627 if(a->argc !=
2) {
1628
1629 fprintf(stderr,
"Too %s arguments\n", a->argc <
2 ?
"few":
"many");
1630 fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"rename"));
1631 return -
1;
1632 }
1633
1634 char *name = a->argv[
1];
1635 size_t namelen = strlen(name);
1636 for(
size_t i=
0;i<namelen;i++) {
1637 char c = name[i];
1638 if(c ==
'/') {
1639 fprintf(stderr,
"Illegal character in name: ''/''\n");
1640 return 1;
1641 }
1642 }
1643
1644 char *url = a->argv[
0];
1645 char *path =
NULL;
1646 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
1647 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
1648
1649 if(set_session_config(sn, a)) {
1650 return -
1;
1651 }
1652 set_session_lock(sn, a);
1653
1654 int ret =
0;
1655 DavResource *res = dav_get(sn, path,
NULL);
1656 if(res) {
1657 char *cryptoname = dav_get_string_property_ns(res,
DAV_NS,
"crypto-name");
1658 char *cryptokey = dav_get_string_property_ns(res,
DAV_NS,
"crypto-key");
1659 if(cryptoname && cryptokey) {
1660
1661
1662
1663 DavKey *key = dav_context_get_key(ctx, cryptokey);
1664 if(key) {
1665
1666 char *parent = util_parent_path(res->path);
1667 char *newpath = util_concat_path(parent, name);
1668 DavResource *testres = dav_resource_new(sn, newpath);
1669 if(dav_exists(testres)) {
1670 fprintf(stderr,
"A resource with this name already exists.\nAbort.\n");
1671 ret =
1;
1672 }
else {
1673 char *crname = aes_encrypt(name, namelen, key);
1674 dav_set_string_property_ns(res,
DAV_NS,
"crypto-name", crname);
1675 free(crname);
1676 if(dav_store(res)) {
1677 print_resource_error(sn, res->path);
1678 fprintf(stderr,
"Cannot store crypto-name property.\n");
1679 ret =
1;
1680 }
1681 }
1682 free(parent);
1683 free(newpath);
1684 }
else {
1685 fprintf(stderr,
"Key %s not found.\n", cryptokey);
1686 }
1687 }
else {
1688
1689
1690 char *parent = util_parent_path(res->href);
1691 char *new_href = util_concat_path(parent, name);
1692 char *dest = util_get_url(sn, new_href);
1693 free(parent);
1694 free(new_href);
1695 if(dav_moveto(res, dest, false)) {
1696 print_resource_error(sn, path);
1697 fprintf(stderr,
"Cannot rename resource.\n");
1698 ret =
1;
1699 }
1700 free(dest);
1701 }
1702 }
else {
1703 print_resource_error(sn, path);
1704 fprintf(stderr,
"Cannot rename resource.\n");
1705 ret =
1;
1706 }
1707
1708
1709 dav_session_destroy(sn);
1710 free(path);
1711 return ret;
1712 }
1713
1714
1715 static size_t get_date_header_cb(
void *header,
int s,
int n,
void *data) {
1716 char **date_str = (
char**)data;
1717
1718
1719 cxstring h = cx_strn(header, s*n);
1720 if(cx_strprefix(h,
CX_STR(
"Date:"))) {
1721 cxstring v = cx_strsubs(h,
5);
1722 *date_str = cx_strdup(cx_strtrim(v)).ptr;
1723 }
1724 return s*n;
1725 }
1726
1727 int cmd_date(CmdArgs *a) {
1728 if(a->argc <
1) {
1729 time_t now = time(
NULL);
1730 struct tm *date = gmtime(&now);
1731 char str[
32];
1732 putenv(
"LC_TIME=C");
1733 size_t len = strftime(str,
32,
"%a, %d %b %Y %H:%M:%S GMT\n", date);
1734 fwrite(str,
1, len, stdout);
1735 }
else if (a->argc ==
1) {
1736 char *url = a->argv[
0];
1737 char *path =
NULL;
1738 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
1739 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
1740
1741 DavResource *res = dav_resource_new(sn, path);
1742 char *date =
NULL;
1743 curl_easy_setopt(sn->handle,
CURLOPT_HEADERFUNCTION, get_date_header_cb);
1744 curl_easy_setopt(sn->handle,
CURLOPT_WRITEHEADER, &date);
1745 if(dav_exists(res) && date) {
1746 printf(
"%s\n", date);
1747 }
else {
1748 return -
1;
1749 }
1750 free(path);
1751 return 0;
1752 }
else {
1753 fprintf(stderr,
"Too many arguments\n");
1754 fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"date"));
1755 return -
1;
1756 }
1757 return 0;
1758 }
1759
1760 int cmd_get_property(CmdArgs *a) {
1761 if(a->argc <
2) {
1762 fprintf(stderr,
"Too few arguments\n");
1763 fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"get-property"));
1764 return -
1;
1765 }
1766
1767 char *url = a->argv[
0];
1768 char *path =
NULL;
1769 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
1770 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
1771
1772 if(set_session_config(sn, a)) {
1773 return -
1;
1774 }
1775
1776 char *namespace = cmd_getoption(a,
"namespace");
1777 char *property = a->argv[
1];
1778
1779 char *version = cmd_getoption(a,
"version");
1780
1781 DavPropName propname;
1782 if(namespace) {
1783 propname.ns = namespace;
1784 propname.name = property;
1785 }
else {
1786 dav_get_property_namespace_str(ctx, property, &propname.ns, &propname.name);
1787 if(!propname.ns || !propname.name) {
1788 fprintf(stderr,
"Error: unknown namespace prefix\n");
1789 return -
1;
1790 }
1791 }
1792
1793 DavResource *res = dav_resource_new(sn, path);
1794 if(version) {
1795 DavResource *vres = find_version(res, version);
1796 if(!vres) {
1797 fprintf(stderr,
"Cannot find version ''%s'' for resource.\n", version);
1798 return -
1;
1799 }
1800 dav_resource_free_all(res);
1801 res = vres;
1802 }
1803
1804 if(dav_load_prop(res, &propname,
1)) {
1805 print_resource_error(sn, res->path);
1806 return -
1;
1807 }
1808 free(path);
1809
1810 DavXmlNode *x = dav_get_property_ns(res, propname.ns, propname.name);
1811 if(!x) {
1812 fprintf(stderr,
"Error: no property value.\n");
1813 return -
1;
1814 }
1815
1816 if(cmd_getoption(a,
"xml")) {
1817
1818 printxmldoc(stdout, propname.name, propname.ns, x);
1819 }
else {
1820
1821
1822 if(dav_xml_isstring(x)) {
1823 printf(
"%s\n", dav_xml_getstring(x));
1824 }
else {
1825 char *str = xml2str(x);
1826 fprintf(stderr,
"%s", str);
1827 free(str);
1828 }
1829 }
1830
1831 return 0;
1832 }
1833
1834 int cmd_set_property(CmdArgs *a) {
1835 if(a->argc <
2) {
1836 fprintf(stderr,
"Too few arguments\n");
1837 fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"set-property"));
1838 return -
1;
1839 }
1840
1841 char *url = a->argv[
0];
1842 char *path =
NULL;
1843 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
1844 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
1845
1846 if(set_session_config(sn, a)) {
1847 return -
1;
1848 }
1849 set_session_lock(sn, a);
1850
1851 DavResource *res = dav_resource_new(sn, path);
1852 if(!dav_exists(res)) {
1853 print_resource_error(sn, res->path);
1854 return -
1;
1855 }
1856
1857 char *namespace = cmd_getoption(a,
"namespace");
1858 char *xml = cmd_getoption(a,
"xml");
1859
1860 char *property = a->argv[
1];
1861 char *value = a->argc >
2 ? a->argv[
2] : stdin2str();
1862
1863 int ret =
0;
1864 if(xml) {
1865 DavXmlNode *xmlvalue = dav_parse_xml(sn, value, strlen(value));
1866 if(xmlvalue) {
1867 if(namespace) {
1868 dav_set_property_ns(res, namespace, property, xmlvalue->children);
1869 }
else {
1870 dav_set_property(res, property, xmlvalue->children);
1871 }
1872 }
else {
1873 fprintf(stderr,
"Error: property content is not valid xml\n");
1874 ret =
1;
1875 }
1876 }
else {
1877 if(namespace) {
1878 dav_set_string_property_ns(res, namespace, property, value);
1879 }
else {
1880 dav_set_string_property(res, property, value);
1881 }
1882 }
1883
1884 if(ret ==
0) {
1885 if(dav_store(res)) {
1886 print_resource_error(sn, res->path);
1887 fprintf(stderr,
"Cannot set property.\n");
1888 ret = -
1;
1889 }
1890 }
else
1891
1892 free(path);
1893 dav_session_destroy(sn);
1894 return ret;
1895 }
1896
1897 int cmd_remove_property(CmdArgs *a) {
1898 if(a->argc <
2) {
1899 fprintf(stderr,
"Too few arguments\n");
1900 fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"remove-property"));
1901 return -
1;
1902 }
1903
1904 char *url = a->argv[
0];
1905 char *path =
NULL;
1906 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
1907 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
1908
1909 if(set_session_config(sn, a)) {
1910 return -
1;
1911 }
1912
1913 char *namespace = cmd_getoption(a,
"namespace");
1914 char *property = a->argv[
1];
1915
1916 DavPropName propname;
1917 if(namespace) {
1918 propname.ns = namespace;
1919 propname.name = property;
1920 }
else {
1921 dav_get_property_namespace_str(ctx, property, &propname.ns, &propname.name);
1922 }
1923
1924 int ret =
0;
1925 DavResource *res = dav_resource_new(sn, path);
1926 dav_remove_property_ns(res, propname.ns, propname.name);
1927
1928 if(dav_store(res)) {
1929 print_resource_error(sn, res->path);
1930 fprintf(stderr,
"Cannot set property.\n");
1931 ret = -
1;
1932 }
1933
1934 free(path);
1935 return ret;
1936 }
1937
1938 int cmd_lock(CmdArgs *a) {
1939 if(a->argc !=
1) {
1940 fprintf(stderr,
"Too %s arguments\n", a->argc >
1 ?
"many" :
"few");
1941 fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"lock"));
1942 return -
1;
1943 }
1944
1945 char *url = a->argv[
0];
1946 char *path =
NULL;
1947 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
1948 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
1949 cxMempoolRegister(sn->mp, path, free);
1950
1951 if(set_session_config(sn, a)) {
1952 return -
1;
1953 }
1954
1955 time_t timeout =
0;
1956 char *timeoutstr = cmd_getoption(a,
"timeout");
1957 if(timeoutstr) {
1958 if(!cx_strcasecmp(cx_str(timeoutstr),
CX_STR(
"infinite"))) {
1959 timeout = -
1;
1960 }
else {
1961 uint64_t i;
1962 if(util_strtouint(timeoutstr, &i)) {
1963 timeout = (
time_t)i;
1964 }
else {
1965 fprintf(stderr,
"Error: -T option has invalid value\n");
1966 return -
1;
1967 }
1968 }
1969 }
1970
1971 DavResource *res = dav_resource_new(sn, path);
1972 if(
dav_lock_t(res, timeout)) {
1973 print_resource_error(sn, res->path);
1974 return -
1;
1975 }
1976
1977 DavLock *lock = dav_get_lock(sn, res->path);
1978 if(!lock) {
1979
1980
1981 dav_unlock(res);
1982 fprintf(stderr,
"Error: Cannot find lock token for %s\n", res->path);
1983 return -
1;
1984 }
1985
1986 printf(
"%s\n", lock->token);
1987
1988 dav_session_destroy(sn);
1989 return 0;
1990 }
1991
1992 static char* read_line() {
1993 CxBuffer buf;
1994 cxBufferInit(&buf,
NULL,
128, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
1995 int c;
1996 while((c = getchar()) !=
EOF) {
1997 if(c ==
'\n') {
1998 break;
1999 }
2000 cxBufferPut(&buf, c);
2001 }
2002 char *str =
NULL;
2003 cxstring line = cx_strtrim(cx_strn(buf.space, buf.size));
2004 if(line.length !=
0) {
2005 str = cx_strdup(line).ptr;
2006 }
2007 cxBufferDestroy(&buf);
2008 return str;
2009 }
2010
2011 int cmd_unlock(CmdArgs *a) {
2012 if(a->argc !=
1) {
2013 fprintf(stderr,
"Too %s arguments\n", a->argc >
1 ?
"many" :
"few");
2014 fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"unlock"));
2015 return -
1;
2016 }
2017
2018 char *url = a->argv[
0];
2019 char *path =
NULL;
2020 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
2021 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
2022 cxMempoolRegister(sn->mp, path, free);
2023 if(set_session_config(sn, a)) {
2024 return -
1;
2025 }
2026
2027 char *locktoken = cmd_getoption(a,
"lock");
2028 if(locktoken) {
2029 DavLock *lock = dav_create_lock(sn, locktoken,
NULL);
2030 dav_add_collection_lock(sn,
"/", lock);
2031 }
else {
2032 locktoken = read_line();
2033 if(!locktoken) {
2034 fprintf(stderr,
"No lock token specified.\nAbort.\n");
2035 return -
1;
2036 }
2037 DavLock *lock = dav_create_lock(sn, locktoken,
NULL);
2038 dav_add_collection_lock(sn,
"/", lock);
2039 free(locktoken);
2040 }
2041
2042 int ret =
0;
2043 DavResource *res = dav_resource_new(sn, path);
2044 if(dav_unlock(res)) {
2045 print_resource_error(sn, res->path);
2046 ret = -
1;
2047 }
2048
2049 dav_session_destroy(sn);
2050 return ret;
2051 }
2052
2053 static int count_children(DavResource *res) {
2054 DavResource *child = res->children;
2055 int count =
0;
2056 while(child) {
2057 count++;
2058 child = child->next;
2059 }
2060 return count;
2061 }
2062
2063 void print_xml_infostr(DavXmlNode *xml) {
2064 if(xml->children) {
2065 printf(
"<%s>...</%s>", xml->name, xml->name);
2066 }
else {
2067 printf(
"<%s/>", xml->name);
2068 }
2069 }
2070
2071 int cmd_info(CmdArgs *a) {
2072 if(a->argc <
1) {
2073 fprintf(stderr,
"Too few arguments\n");
2074 fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"info"));
2075 return -
1;
2076 }
2077
2078 char *url = a->argv[
0];
2079 char *path =
NULL;
2080 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
2081 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
2082
2083 if(set_session_config(sn, a)) {
2084 return -
1;
2085 }
2086
2087 char *version = cmd_getoption(a,
"version");
2088
2089 DavResource *res = dav_resource_new(sn, path);
2090 if(version) {
2091 DavResource *vres = find_version(res, version);
2092 if(!vres) {
2093 fprintf(stderr,
"Cannot find version ''%s'' for resource.\n", version);
2094 return -
1;
2095 }
2096 dav_resource_free_all(res);
2097 res = vres;
2098 }
2099
2100 if(!dav_load(res)) {
2101 printf(
"name: %s\n", res->name);
2102 printf(
"path: %s\n", res->path);
2103
2104 char *server = util_url_base(sn->base_url);
2105 char *url = util_concat_path(server, res->href);
2106 printf(
"url: %s\n", url);
2107 free(url);
2108 free(server);
2109
2110 if(res->iscollection) {
2111 printf(
"type: collection\n");
2112 printf(
"size: %d\n", count_children(res));
2113 }
else {
2114 printf(
"type: resource\n");
2115 char *len = util_size_str(res->iscollection, res->contentlength);
2116 printf(
"size: %s\n", len);
2117 free(len);
2118 }
2119
2120 size_t count =
0;
2121 DavPropName *properties = dav_get_property_names(res, &count);
2122
2123 char *last_ns =
NULL;
2124 for(
int i=
0;i<count;i++) {
2125 DavPropName p = properties[i];
2126 if(!last_ns || strcmp(last_ns, p.ns)) {
2127 printf(
"\nnamespace: %s\n", p.ns);
2128 last_ns = p.ns;
2129 }
2130
2131 DavXmlNode *xval = dav_get_property_ns(res, p.ns, p.name);
2132 if(dav_xml_isstring(xval)) {
2133 cxstring value = cx_str(dav_xml_getstring(xval));
2134 printf(
" %s: %.*s\n", p.name, (
int)value.length, value.ptr);
2135 }
else {
2136
2137 printf(
" %s: ", p.name);
2138 DavXmlNode *x = xval->type ==
DAV_XML_ELEMENT ? xval : dav_xml_nextelm(xval);
2139 for(
int i=
0;i<
3;i++) {
2140 if(x) {
2141 if(i ==
2) {
2142 printf(
" ...");
2143 break;
2144 }
else {
2145 print_xml_infostr(x);
2146 }
2147 }
else {
2148 break;
2149 }
2150 x = dav_xml_nextelm(x);
2151 }
2152 printf(
"\n");
2153
2154
2155 }
2156 }
2157
2158 dav_session_free(sn, properties);
2159 return 0;
2160 }
else {
2161 print_resource_error(sn, res->path);
2162 }
2163
2164 return -
1;
2165 }
2166
2167 int cmd_checkout(CmdArgs *a) {
2168 if(a->argc <
1) {
2169 fprintf(stderr,
"Too few arguments\n");
2170 fprintf(stderr,
"Usage: dav %s\n",
"checkout [-pc] <url>");
2171 return -
1;
2172 }
2173
2174 char *url = a->argv[
0];
2175 char *path =
NULL;
2176 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
2177 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
2178
2179 if(set_session_config(sn, a)) {
2180 return -
1;
2181 }
2182 set_session_lock(sn, a);
2183
2184 int ret =
0;
2185 DavResource *res = dav_resource_new(sn, path);
2186 if(dav_checkout(res)) {
2187 print_resource_error(sn, res->path);
2188 ret =
1;
2189 }
2190
2191 return ret;
2192 }
2193
2194 int cmd_checkin(CmdArgs *a) {
2195 if(a->argc <
1) {
2196 fprintf(stderr,
"Too few arguments\n");
2197 fprintf(stderr,
"Usage: dav %s\n",
"checkin [-pc] <url>");
2198 return -
1;
2199 }
2200
2201 char *url = a->argv[
0];
2202 char *path =
NULL;
2203 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
2204 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
2205
2206 if(set_session_config(sn, a)) {
2207 return -
1;
2208 }
2209 set_session_lock(sn, a);
2210
2211 int ret =
0;
2212 DavResource *res = dav_resource_new(sn, path);
2213 if(dav_checkin(res)) {
2214 print_resource_error(sn, res->path);
2215 ret =
1;
2216 }
2217
2218 return ret;
2219 }
2220
2221 int cmd_uncheckout(CmdArgs *a) {
2222 if(a->argc <
1) {
2223 fprintf(stderr,
"Too few arguments\n");
2224 fprintf(stderr,
"Usage: dav %s\n",
"uncheckout [-pc] <url>");
2225 return -
1;
2226 }
2227
2228 char *url = a->argv[
0];
2229 char *path =
NULL;
2230 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
2231 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
2232
2233 if(set_session_config(sn, a)) {
2234 return -
1;
2235 }
2236 set_session_lock(sn, a);
2237
2238 int ret =
0;
2239 DavResource *res = dav_resource_new(sn, path);
2240 if(dav_uncheckout(res)) {
2241 print_resource_error(sn, res->path);
2242 ret =
1;
2243 }
2244
2245 return ret;
2246 }
2247 int cmd_versioncontrol(CmdArgs *a) {
2248 if(a->argc <
1) {
2249 fprintf(stderr,
"Too few arguments\n");
2250 fprintf(stderr,
"Usage: dav %s\n",
"versioncontrol [-pc] <url>");
2251 return -
1;
2252 }
2253
2254 char *url = a->argv[
0];
2255 char *path =
NULL;
2256 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
2257 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
2258
2259 if(set_session_config(sn, a)) {
2260 return -
1;
2261 }
2262 set_session_lock(sn, a);
2263
2264 int ret =
0;
2265 DavResource *res = dav_resource_new(sn, path);
2266 if(dav_versioncontrol(res)) {
2267 print_resource_error(sn, res->path);
2268 ret =
1;
2269 }
2270
2271 return ret;
2272 }
2273
2274 int cmd_list_versions(CmdArgs *a) {
2275 if(a->argc <
1) {
2276 fprintf(stderr,
"Too few arguments\n");
2277 fprintf(stderr,
"Usage: dav %s\n",
"list-versions [-pc] <url>");
2278 return -
1;
2279 }
2280
2281 char *url = a->argv[
0];
2282 char *path =
NULL;
2283 DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
2284 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
2285
2286 if(set_session_config(sn, a)) {
2287 return -
1;
2288 }
2289
2290 DavResource *res = dav_resource_new(sn, path);
2291
2292 int ret =
0;
2293 DavResource *list = dav_versiontree(res,
NULL);
2294 if(list) {
2295 char* longlist = cmd_getoption(a,
"list");
2296
2297 DavResource *v = list;
2298 int addnl =
0;
2299 while(v) {
2300 char *vname = dav_get_string_property(v,
"D:version-name");
2301
2302 if(longlist) {
2303 if(addnl) {
2304 putchar(
'\n');
2305 }
2306 printf(
"name: %s\n", vname);
2307 printf(
"href: %s\n", v->href);
2308 addnl =
1;
2309 }
else {
2310 printf(
"%s\n", vname);
2311 }
2312 v = v->next;
2313 }
2314 }
else if(sn->error !=
DAV_OK) {
2315 print_resource_error(sn, path);
2316 ret =
1;
2317 }
2318
2319 return ret;
2320 }
2321
2322 DavResource* find_version(DavResource *res,
char *version) {
2323 DavResource *list = dav_versiontree(res,
NULL);
2324 DavResource *ret =
NULL;
2325 while(list) {
2326 DavResource *next = list->next;
2327 if(!ret) {
2328 char *vname = dav_get_string_property(list,
"D:version-name");
2329 if(vname && !strcmp(vname, version)) {
2330 ret = list;
2331 }
2332 }
2333 if(list != ret) {
2334 dav_resource_free(list);
2335 }
2336 list = next;
2337 }
2338 return ret;
2339 }
2340
2341
2342 char* stdin2str() {
2343 CxBuffer buf;
2344 cxBufferInit(&buf,
NULL,
1024, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
2345 size_t size = cx_stream_copy(stdin, &buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite);
2346 if(size ==
0) {
2347 cxBufferDestroy(&buf);
2348 return NULL;
2349 }
else {
2350 cxBufferPut(&buf,
'\0');
2351 return buf.space;
2352 }
2353 }
2354
2355 static void xml2str_i(DavXmlNode *node, CxBuffer *buf,
int indent) {
2356 while(node) {
2357 if(node->type ==
DAV_XML_ELEMENT) {
2358 if(node->children) {
2359 if(dav_xml_isstring(node->children)) {
2360 cxstring s = cx_strtrim(cx_str(dav_xml_getstring(node->children)));
2361 cx_bprintf(
2362 buf,
2363 "%*s<%s>%.*s</%s>\n",
2364 indent,
2365 "",
2366 node->name,
2367 (
int)s.length,
2368 s.ptr,
2369 node->name);
2370 }
else {
2371 cx_bprintf(buf,
"%*s<%s>\n", indent,
"", node->name);
2372 xml2str_i(node->children, buf, indent+
2);
2373 cx_bprintf(buf,
"%*s</%s>\n", indent,
"", node->name);
2374 }
2375 }
else {
2376 cx_bprintf(buf,
"%*s<%s />", indent,
"", node->name);
2377 cxBufferPut(buf,
'\n');
2378 }
2379 }
else if(node->type ==
DAV_XML_TEXT) {
2380 cxstring val = cx_strtrim(cx_strn(node->content, node->contentlength));
2381 if(val.length >
0) {
2382 cx_bprintf(buf,
"%*.*s", indent, (
int)val.length, val.ptr);
2383 }
2384 }
2385
2386 node = node->next;
2387 }
2388 }
2389
2390 char* xml2str(DavXmlNode *node) {
2391 CxBuffer buf;
2392 cxBufferInit(&buf,
NULL,
256, cxDefaultAllocator,
CX_BUFFER_AUTO_EXTEND);
2393 xml2str_i(node, &buf,
0);
2394 cxBufferPut(&buf,
0);
2395 return buf.space;
2396 }
2397
2398 void printxmldoc(
FILE *out,
char *root,
char *rootns, DavXmlNode *content) {
2399 CxMap *nsmap = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
16);
2400 nsmap->simple_destructor = free;
2401
2402 cxMapPut(nsmap, cx_hash_key_str(rootns),
"x0");
2403 fprintf(out,
"%s",
"<?xml version=\"1.0\"?>\n");
2404 fprintf(out,
"<x0:%s xmlns:x0=\"%s\">", root, rootns);
2405
2406 dav_print_node(out, (cx_write_func)fwrite, nsmap, content);
2407
2408 fprintf(out,
"</x0:%s>\n", root);
2409
2410
2411 cxMapRemove(nsmap, cx_hash_key_str(rootns));
2412 cxMapDestroy(nsmap);
2413 }
2414
2415
2416
2417
2418 int cmd_add_repository(CmdArgs *args) {
2419 printf(
"Each repository must have an unique name.\n");
2420 char *name = assistant_getcfg(
"name");
2421 if(!name) {
2422 fprintf(stderr,
"Abort\n");
2423 return -
1;
2424 }
2425 if(dav_config_get_repository(get_config(), cx_str(name))) {
2426 fprintf(stderr,
"Repository %s already exists.\nAbort\n", name);
2427 return -
1;
2428 }
2429
2430 printf(
"\nSpecify the repository base url.\n");
2431 char *url = assistant_getcfg(
"url");
2432 if(!url) {
2433 fprintf(stderr,
"Abort\n");
2434 return -
1;
2435 }
2436
2437 printf(
"\nUser for HTTP authentication.\n");
2438 char *user = assistant_getoptcfg(
"user");
2439
2440 char *password =
NULL;
2441 if(user) {
2442 password = assistant_gethiddenoptcfg(
"password");
2443 }
2444 printf(
"\n");
2445
2446 DavConfig *config = get_config();
2447 const CxAllocator *a = config->mp->allocator;
2448 DavCfgRepository *repo = dav_repository_new(config);
2449
2450 repo->name.value = cx_strdup_a(a, cx_str(name));
2451 repo->url.value = cx_strdup_a(a, cx_str(url));
2452 dav_repository_set_auth(config, repo, cx_str(user), cx_str(password));
2453
2454 dav_config_add_repository(config, repo);
2455
2456 int ret =
0;
2457 if(store_config()) {
2458 fprintf(stderr,
"Cannot write config.xml\n");
2459 ret = -
1;
2460 }
else {
2461 printf(
"\nAdded repository: %s (%s)\n", name, url);
2462 }
2463
2464 free(name);
2465 free(url);
2466 if(user) {
2467 free(user);
2468 }
2469 if(password) {
2470 free(password);
2471 }
2472
2473 return ret;
2474 }
2475
2476 int cmd_remove_repository(CmdArgs *args) {
2477 if(args->argc <
1) {
2478 fprintf(stderr,
"Too few arguments\n");
2479 fprintf(stderr,
"Usage: dav remove-repository <name...>\n");
2480 return -
1;
2481 }
2482
2483 DavConfig *config = get_config();
2484
2485 DavBool store =
FALSE;
2486 for(
int i =
0 ; i < args->argc ; i++) {
2487 cxstring reponame = cx_str(args->argv[i]);
2488 DavCfgRepository* repo = dav_config_get_repository(config, reponame);
2489 if(repo) {
2490 dav_repository_remove_and_free(config, repo);
2491 store =
TRUE;
2492 }
else {
2493 fprintf(stderr,
"Repository %s does not exist - skipped.\n",
2494 reponame.ptr);
2495 return -
1;
2496 }
2497 }
2498
2499 if(store) {
2500 return store_config();
2501 }
else {
2502 return -
1;
2503 }
2504 }
2505
2506 int cmd_list_repositories(
void) {
2507 DavConfig *config = get_config();
2508 if(!config) {
2509 return 1;
2510 }
2511 for(DavCfgRepository *repo=config->repositories;repo;repo=repo->next) {
2512 printf(
"%.*s\n", (
int)repo->name.value.length, repo->name.value.ptr);
2513 }
2514 return 0;
2515 }
2516
2517 int cmd_repository_url(CmdArgs *args) {
2518 if(args->argc !=
1) {
2519 fprintf(stderr,
"Too few arguments\n");
2520 fprintf(stderr,
"Usage: dav repository-url [-p] <name>\n");
2521 return -
1;
2522 }
2523
2524 cxstring reponame = cx_str(args->argv[
0]);
2525 DavCfgRepository* repo = dav_config_get_repository(get_config(), reponame);
2526 if(repo) {
2527 cxstring url = cx_strcast(repo->url.value);
2528 if(repo->user.value.ptr && !cmd_getoption(args,
"plain")) {
2529 int hostindex =
0;
2530 if(cx_strprefix(url,
CX_STR(
"https://"))) {
2531 printf(
"https://");
2532 hostindex =
8;
2533 }
else if(cx_strprefix(url,
CX_STR(
"http://"))) {
2534 printf(
"http://");
2535 hostindex =
7;
2536 }
2537 printf(
"%.*s", (
int)repo->user.value.length, repo->user.value.ptr);
2538 if(repo->password.value.ptr) {
2539 cxmutstr pw_decoded = dav_repository_get_decodedpassword(repo);
2540 CURL *curl = curl_easy_init();
2541 char *pw = curl_easy_escape(
2542 curl,
2543 pw_decoded.ptr,
2544 pw_decoded.length);
2545 printf(
":%s", pw);
2546 curl_free(pw);
2547 curl_easy_cleanup(curl);
2548 free(pw_decoded.ptr);
2549 }
2550 putchar(
'@');
2551 printf(
"%.*s", (
int)url.length-hostindex, url.ptr+hostindex);
2552 }
else {
2553 printf(
"%s", url.ptr);
2554 }
2555 if(url.ptr[url.length-
1] !=
'/') {
2556 putchar(
'/');
2557 }
2558 putchar(
'\n');
2559 }
else {
2560 fprintf(stderr,
"Repository %s does not exist.\n", reponame.ptr);
2561 return -
1;
2562 }
2563 return 0;
2564 }
2565
2566
2567 typedef int(*sscmd_func)(CmdArgs *, PwdStore *,
void *userdata);
2568
2569 static int secretstore_after_decrypt(
2570 CmdArgs *args,
2571 PwdStore *secrets,
2572 sscmd_func cb,
2573 void *userdata);
2574
2575
2576
2577
2578
2579
2580
2581 static int secretstore_cmd(
2582 CmdArgs *args,
2583 DavBool create,
2584 sscmd_func beforedecrypt,
2585 sscmd_func afterdecrypt,
2586 void *userdata)
2587 {
2588 PwdStore *secrets = get_pwdstore();
2589 if(!secrets) {
2590 if(create) {
2591 secrets = pwdstore_new();
2592 }
else {
2593 return 1;
2594 }
2595 }
2596
2597 int ret =
0;
2598 if(beforedecrypt) {
2599 ret = beforedecrypt(args, secrets, userdata);
2600 if(ret) {
2601 afterdecrypt =
NULL;
2602 }
2603 }
2604
2605 if(afterdecrypt) {
2606 ret = secretstore_after_decrypt(args, secrets, afterdecrypt, userdata);
2607 }
2608
2609 pwdstore_free(secrets);
2610
2611 return ret;
2612 }
2613
2614 static int secretstore_after_decrypt(
2615 CmdArgs *args,
2616 PwdStore *secrets,
2617 sscmd_func cb,
2618 void *userdata)
2619 {
2620 char *master_pw = util_password_input(
"Master password: ");
2621 if(!master_pw) {
2622 fprintf(stderr,
"Error: master password required.\nAbort.\n");
2623 return 1;
2624 }
2625
2626 int err = pwdstore_setpassword(secrets, master_pw);
2627 free(master_pw);
2628 if(err) {
2629 fprintf(stderr,
"Error: Cannot generate key from password.\nAbort.\n");
2630 return 1;
2631 }
2632
2633 if(pwdstore_decrypt(secrets)) {
2634 fprintf(stderr,
"Error: Cannot decrypt secrets store.\nAbort.\n");
2635 return 1;
2636 }
2637
2638 return cb(args, secrets, userdata);
2639 }
2640
2641 static int cmd_ss_add_user(CmdArgs *Args, PwdStore *secrets,
void *userdata) {
2642 char *id = assistant_getcfg(
"Credentials identifier");
2643 if(!id) {
2644 fprintf(stderr,
"Identifier required.\n");
2645 return 1;
2646 }
2647 if(pwdstore_get(secrets, id)) {
2648 fprintf(stderr,
"Credentials with this id already exist.\n");
2649 return 1;
2650 }
2651
2652
2653 char *user = assistant_getcfg(
"User");
2654 char *password = util_password_input(
"Password: ");
2655
2656
2657 char *location =
NULL;
2658 CxList *locations = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
2659 locations->simple_destructor = free;
2660 while((location = assistant_getoptcfg(
"Location"))) {
2661 cxListAdd(locations, location);
2662 }
2663
2664 int ret =
1;
2665 if(user && password) {
2666 pwdstore_put_index(secrets, id, locations);
2667 pwdstore_put(secrets, id, user, password);
2668 ret = pwdstore_save(secrets);
2669 if(ret) {
2670 fprintf(stderr,
"Error: saving srcrets store failed.\n");
2671 }
2672 }
2673
2674 if(id) free(id);
2675 if(user) free(user);
2676 if(password) free(password);
2677
2678 cxListDestroy(locations);
2679
2680 return ret;
2681 }
2682
2683 int cmd_add_user(CmdArgs *args) {
2684 return secretstore_cmd(args,
TRUE,
NULL, cmd_ss_add_user,
NULL);
2685 }
2686
2687
2688
2689
2690 static int cmd_ss_list_users_bc(CmdArgs *Args, PwdStore *secrets,
int *ret) {
2691 if(secrets->index->size ==
0) {
2692 return 1;
2693 }
2694
2695 *ret =
1;
2696 return 0;
2697 }
2698
2699
2700
2701
2702 static int cmd_ss_list_users(CmdArgs *args, PwdStore *secrets,
int *ret) {
2703 *ret =
0;
2704
2705 CxList *list = secrets->locations;
2706 for(
int i=
0;i<
2;i++) {
2707 if(list) {
2708 CxIterator i = cxListIterator(list);
2709 cx_foreach(PwdIndexEntry*, index, i) {
2710 PwdEntry *e = cxMapGet(secrets->ids, cx_hash_key_str(index->id));
2711 if(e) {
2712 printf(
"Id: %s\n", e->id);
2713 printf(
"User: %s\n", e->user);
2714 if(index->locations) {
2715 CxIterator loc_iter = cxListIterator(index->locations);
2716 cx_foreach(
char *, location, loc_iter) {
2717 printf(
"Location: %s\n", location);
2718 }
2719 printf(
"\n");
2720 }
2721 }
else {
2722
2723 fprintf(stderr,
2724 "Warning: id ''%s'' not in secret store.\n",
2725 index->id);
2726 }
2727 }
2728 }
2729 list = secrets->noloc;
2730 }
2731
2732
2733 return 0;
2734 }
2735
2736 int cmd_list_users(CmdArgs *args) {
2737 int ret =
0;
2738 secretstore_cmd(args,
FALSE, (sscmd_func)cmd_ss_list_users_bc, (sscmd_func)cmd_ss_list_users, &ret);
2739 return ret;
2740 }
2741
2742
2743 static int cmd_ss_remove_user(CmdArgs *args, PwdStore *secrets,
void *ud) {
2744 char *id = assistant_getcfg(
"Credentials identifier");
2745 if(!id) {
2746 fprintf(stderr,
"Identifier required.\n");
2747 return 1;
2748 }
2749 if(!pwdstore_get(secrets, id)) {
2750 fprintf(stderr,
"Credentials with this id doesn''t exist.\n");
2751 free(id);
2752 return 1;
2753 }
2754
2755 pwdstore_remove_entry(secrets, id);
2756
2757 int ret = pwdstore_save(secrets);
2758 if(ret) {
2759 fprintf(stderr,
"Error: saving srcrets store failed.\n");
2760 }
2761 free(id);
2762 return ret;
2763 }
2764
2765 int cmd_remove_user(CmdArgs *args) {
2766 return secretstore_cmd(args,
FALSE,
NULL, cmd_ss_remove_user,
NULL);
2767 }
2768
2769 static void secrets_print_user_info(PwdStore *secrets,
const char *id) {
2770 PwdEntry *entry = pwdstore_get(secrets, id);
2771 if(!entry) {
2772 return;
2773 }
2774
2775 PwdIndexEntry *index = cxMapGet(secrets->index, cx_hash_key_str(id));
2776 if(!index) {
2777 return;
2778 }
2779
2780 printf(
"Id: %s\n", entry->id);
2781 printf(
"User: %s\n", entry->user);
2782 if(index->locations) {
2783 CxIterator loc_iter = cxListIterator(index->locations);
2784 cx_foreach(
char *, location, loc_iter) {
2785 printf(
"Location: %s\n", location);
2786 }
2787 }
2788 }
2789
2790 static void secrets_remove_location(PwdIndexEntry *index) {
2791 if(!index->locations || index->locations->size ==
0) {
2792 printf(
"no locations\n");
2793 return;
2794 }
2795
2796 printf(
"0: abort\n");
2797 int i =
1;
2798 CxIterator loc_iter = cxListIterator(index->locations);
2799 cx_foreach(
char *, location, loc_iter) {
2800 printf(
"%d: %s\n", i, location);
2801 i++;
2802 }
2803
2804 char *input = assistant_getcfg(
"Choose location");
2805 if(!input) {
2806 return;
2807 }
2808
2809 int64_t ln =
0;
2810 if(util_strtoint(input, &ln) && (ln >=
0 && ln < i)) {
2811 if(ln ==
0) {
2812 return;
2813 }
else {
2814 char *location = cxListAt(index->locations, ln -
1);
2815 if(location) {
2816 free(location);
2817 cxListRemove(index->locations, ln -
1);
2818 }
2819 }
2820 }
else {
2821 printf(
"illegal input, choose 0 - %d\n", i-
1);
2822 secrets_remove_location(index);
2823 }
2824 }
2825
2826 static int cmd_ss_edit_user(CmdArgs *args, PwdStore *secrets,
void *ud) {
2827 char *id = assistant_getcfg(
"Credentials identifier");
2828 if(!id) {
2829 fprintf(stderr,
"Identifier required.\n");
2830 return 1;
2831 }
2832 PwdEntry *entry = pwdstore_get(secrets, id);
2833 PwdIndexEntry *index = cxMapGet(secrets->index, cx_hash_key_str(id));
2834 if(!entry || !index) {
2835 fprintf(stderr,
"Credentials with this id doesn''t exist.\n");
2836 return 1;
2837 }
2838
2839 secrets_print_user_info(secrets, id);
2840
2841 int save =
0;
2842 int loop =
1;
2843 while(loop) {
2844 printf(
"\n");
2845 printf(
"0: change user name\n");
2846 printf(
"1: change password\n");
2847 printf(
"2: add location\n");
2848 printf(
"3: remove location\n");
2849 printf(
"4: list locations\n");
2850 printf(
"5: save and exit\n");
2851 printf(
"6: exit without saving\n");
2852
2853 char *opt = assistant_getcfg(
"Choose action");
2854 if(!opt) {
2855 break;
2856 }
2857 int64_t mnu =
0;
2858 if(util_strtoint(opt, &mnu) && (mnu >=
0 && mnu <=
6)) {
2859 printf(
"\n");
2860 switch(mnu) {
2861 case 0: {
2862
2863 char *user = assistant_getcfg(
"User");
2864 if(user) {
2865 if(entry->user) {
2866 free(entry->user);
2867 }
2868 entry->user = user;
2869 }
2870 break;
2871 }
2872 case 1: {
2873
2874 char *password = util_password_input(
"Password: ");
2875 if(password) {
2876 if(entry->password) {
2877 free(entry->password);
2878 }
2879 entry->password = password;
2880 }
2881 break;
2882 }
2883 case 2: {
2884
2885 char *location = assistant_getoptcfg(
"Location");
2886 if(location) {
2887 cxListAdd(index->locations, location);
2888 }
2889 break;
2890 }
2891 case 3: {
2892
2893 secrets_remove_location(index);
2894 break;
2895 }
2896 case 4: {
2897
2898 if(!index->locations || index->locations->size ==
0) {
2899 printf(
"no locations\n");
2900 }
else {
2901 CxIterator i = cxListIterator(index->locations);
2902 cx_foreach(
char *, location, i) {
2903 printf(
"Location: %s\n", location);
2904 }
2905 }
2906 break;
2907 }
2908 case 5: {
2909
2910 loop =
0;
2911 save =
1;
2912 break;
2913 }
2914 case 6: {
2915
2916 loop =
0;
2917 break;
2918 }
2919 }
2920 }
else {
2921 printf(
"illegal input, choose 0 - 5\n");
2922 }
2923 free(opt);
2924 }
2925
2926 int ret =
0;
2927 if(save) {
2928 ret = pwdstore_save(secrets);
2929 if(ret) {
2930 fprintf(stderr,
"Error: saving srcrets store failed.\n");
2931 }
2932 }
2933 return ret;
2934 }
2935
2936 int cmd_edit_user(CmdArgs *args) {
2937 return secretstore_cmd(args,
FALSE,
NULL, cmd_ss_edit_user,
NULL);
2938 }
2939
2940
2941 static int cmd_ss_set_master_pw(CmdArgs *args, PwdStore *secrets,
void *ud) {
2942 char *new_master_pw = util_password_input(
"New master password: ");
2943 int ret = pwdstore_setpassword(secrets, new_master_pw);
2944 if(ret) {
2945 fprintf(stderr,
"Error: failed to set new master password\n");
2946 }
2947
2948 ret = pwdstore_save(secrets);
2949 if(ret) {
2950 fprintf(stderr,
"Error: saving srcrets store failed.\n");
2951 }
2952 return ret;
2953 }
2954
2955 int cmd_set_master_password(CmdArgs *args) {
2956 return secretstore_cmd(args,
FALSE,
NULL, cmd_ss_set_master_pw,
NULL);
2957 }
2958
2959 static char** read_args_from_stdin(
int *argc) {
2960
2961 CxBuffer *in = cxBufferCreate(
NULL,
1024, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
2962 cx_stream_copy(stdin, in, (cx_read_func)fread, (cx_write_func)cxBufferWrite);
2963
2964
2965 ssize_t count =
0;
2966 cxmutstr *lines;
2967 count = cx_strsplit_ma(cxDefaultAllocator, cx_mutstrn(in->space, in->pos),
CX_STR(
"\n"),
INT_MAX, &lines);
2968
2969 char **args =
NULL;
2970 if(count >
0) {
2971 args = calloc(count,
sizeof(
char*));
2972 for(
int i=
0;i<count;i++) {
2973 args[i] = lines[i].ptr;
2974 }
2975 free(lines);
2976
2977 *argc = count;
2978 }
else {
2979 *argc =
0;
2980 }
2981
2982
2983 cxBufferFree(in);
2984
2985 return args;
2986 }
2987
2988 int cmd_complete(CmdArgs *args) {
2989 if(args->argc !=
1) {
2990 return 1;
2991 }
2992 char *index_str = args->argv[
0];
2993 int64_t index =
0;
2994 if(!util_strtoint(index_str, &index)) {
2995 return 1;
2996 }
2997
2998
2999 int comp_argc;
3000 char **comp_argv = read_args_from_stdin(&comp_argc);
3001
3002
3003 char *cmd =
NULL;
3004 if(comp_argc >
1) {
3005 cmd = comp_argv[
1];
3006 }
3007 CmdArgs *comp_args = cmd_parse_args(comp_argc -
2, comp_argv +
2);
3008 if(comp_args) {
3009
3010 if(comp_args->argc +
2 != comp_argc) {
3011
3012
3013
3014
3015
3016
3017 int j =
0;
3018 for(
int i=
0;i<comp_argc-
2;i++) {
3019 if(index == i-
2) {
3020 break;
3021 }
3022
3023 if(strcmp(comp_argv[i+
2], comp_args->argv[j])) {
3024 index--;
3025 }
else {
3026 j++;
3027 }
3028 }
3029 }
3030 }
else {
3031 comp_args =
NULL;
3032 }
3033
3034
3035 int ret =
1;
3036 if(comp_args) {
3037 ret = shell_completion(cmd, comp_args, index);
3038 }
3039
3040
3041 cmd_args_free(comp_args);
3042 free(comp_argv);
3043 return ret;
3044
3045 }
3046
3047 int shell_completion(
char *cmd, CmdArgs *args,
int index) {
3048 if(index ==
1) {
3049 cxstring prefix = {
NULL,
0 };
3050 if(cmd) {
3051 prefix = cx_str(cmd);
3052 }
3053 for(
int i=
0;;i++) {
3054 char *str = cmdusageinfo[i];
3055 if(!str) {
3056 break;
3057 }
3058 int len = (
int)strlen(str);
3059 int maxlen = len;
3060 for(
int w=
0;w<len;w++) {
3061 if(str[w] ==
' ') {
3062 maxlen = w;
3063 break;
3064 }
3065 }
3066 if(prefix.ptr) {
3067 if(!cx_strprefix(cx_strn(str, maxlen), prefix)) {
3068 continue;
3069 }
3070 }
3071 printf(
"%.*s\n", (
int)maxlen, str);
3072 }
3073 return 0;
3074 }
3075
3076 if(!strcmp(cmd,
"date")) {
3077 return 0;
3078 }
3079
3080
3081 char *url = args->argc >
0 ? args->argv[
0] :
NULL;
3082
3083
3084
3085
3086
3087
3088 if(index ==
2) {
3089
3090 return url_completion(args, url);
3091 }
else if (index ==
3) {
3092 if(!strcmp(cmd,
"put") || !strcmp(cmd,
"import")) {
3093
3094 return 12;
3095 }
else if(!strcmp(cmd,
"copy") || !strcmp(cmd,
"cp") || !strcmp(cmd,
"move") || !strcmp(cmd,
"mv")) {
3096
3097 return url_completion(args, url);
3098 }
3099 }
3100
3101 return 0;
3102 }
3103
3104 int url_completion(CmdArgs *args,
char *u) {
3105 cxstring url;
3106 url.ptr = u;
3107 url.length = u ? strlen(u) :
0;
3108
3109
3110
3111 char quote =
'\0';
3112 if(url.length >
0 && (url.ptr[
0] ==
'\'' || url.ptr[
0] ==
'\"' )) {
3113 quote = url.ptr[
0];
3114
3115
3116 url.ptr++;
3117 url.length--;
3118
3119
3120 if (url.ptr[url.length-
1] == quote) {
3121 url.length--;
3122 }
3123 }
3124
3125
3126 int repocomp =
1;
3127 for(
int i=
0;i<url.length;i++) {
3128 if(url.ptr[i] ==
'/') {
3129 repocomp =
0;
3130 break;
3131 }
3132 }
3133 if(repocomp) {
3134 DavConfig *config = get_config();
3135 for(DavCfgRepository *repo=config->repositories; repo; repo=repo->next) {
3136 if(cx_strprefix(cx_strcast(repo->name.value), url)) {
3137 if(quote ==
'\0') {
3138 printf(
"%s/\n", repo->name.value.ptr);
3139 }
else {
3140 printf(
"%c%s/%c\n", quote, repo->name.value.ptr, quote);
3141 }
3142 }
3143
3144 }
3145 }
else {
3146
3147 cxMapPut(args->options, cx_hash_key_str(
"noinput"),
"");
3148
3149 char *path =
NULL;
3150 DavCfgRepository *repo = dav_config_url2repo(get_config(), url.ptr, &path);
3151 DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, args);
3152 if(!sn) {
3153 return 0;
3154 }
3155 if(set_session_config(sn, args)) {
3156 return 0;
3157 }
3158
3159 size_t plen = strlen(path);
3160
3161 cxstring filter;
3162 char *lspath =
NULL;
3163 if(path[plen-
1] ==
'/') {
3164 lspath = strdup(path);
3165 filter =
CX_STR(
"");
3166 }
else {
3167 lspath = util_parent_path(path);
3168 filter = cx_str(util_resource_name(path));
3169 }
3170
3171 DavResource *ls = dav_query(sn,
"select - from %s order by name", lspath);
3172 DavResource *elm = ls ? ls->children :
NULL;
3173 while(elm) {
3174 cxstring name = cx_str(elm->name);
3175 if(cx_strprefix(name, filter)) {
3176 int space =
0;
3177 for(
int i=
0;i<name.length;i++) {
3178 if(name.ptr[i] ==
' ') {
3179 space =
1;
3180 break;
3181 }
3182 }
3183
3184 CxBuffer *out = cxBufferCreate(
NULL,
512, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
3185 cxBufferWrite(repo->name.value.ptr, repo->name.value.length,
1, out);
3186 if(space) {
3187 size_t l = strlen(elm->path);
3188 for(
int i=
0;i<l;i++) {
3189
3190 char nextc = elm->path[i];
3191 if(quote ==
'\0' &&
NULL != strchr(
3192 "!\"#$&''()*,;<>?[\\]^`{|}~ ", nextc)) {
3193 cxBufferPut(out,
'\\');
3194 }
3195 cxBufferPut(out, nextc);
3196 }
3197 }
else {
3198 cxBufferPutString(out, elm->path);
3199 }
3200 if(elm->iscollection) {
3201 if(out->space[out->pos-
1] !=
'/') {
3202 cxBufferPut(out,
'/');
3203 }
3204 }
3205 if (quote ==
'\0') {
3206 printf(
"%.*s\n", (
int)out->pos, out->space);
3207 }
else {
3208 printf(
"%c%.*s%c\n",
3209 quote, (
int)out->pos, out->space, quote);
3210 }
3211
3212 cxBufferFree(out);
3213 }
3214 elm = elm->next;
3215 }
3216
3217 free(lspath);
3218
3219 dav_session_destroy(sn);
3220 }
3221
3222 return 10;
3223 }
3224