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