#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#ifndef _WIN32
#include <sys/wait.h>
#include <unistd.h>
#endif
#include <cx/string.h>
#include <cx/utils.h>
#include <cx/printf.h>
#include <cx/hash_map.h>
#include <cx/linked_list.h>
#include <libidav/utils.h>
#include <libidav/crypto.h>
#include <libidav/session.h>
#include <libidav/xml.h>
#include "config.h"
#include "error.h"
#include "assistant.h"
#include "system.h"
#include "finfo.h"
#include "main.h"
#include "connect.h"
static DavContext *ctx;
static int printxmlerror =
1;
static void xmlerrorfnc(
void * c,
const char * msg, ... ) {
if(printxmlerror) {
va_list ap;
va_start(ap, msg);
vfprintf(stderr, msg, ap);
va_end(ap);
}
}
void test(CmdArgs *a) {
}
int dav_main(
int argc,
char **argv);
#ifdef _WIN32
#define strcasecmp _stricmp
static char* wchar2utf8(
const wchar_t *wstr,
size_t wlen) {
size_t maxlen = wlen *
4;
char *ret = malloc(maxlen +
1);
int ret_len = WideCharToMultiByte(
CP_UTF8,
0,
wstr,
wlen,
ret,
maxlen,
NULL,
NULL);
ret[ret_len] =
0;
return ret;
}
int wmain(
int argc,
wchar_t **argv) {
char **argv_utf8 = calloc(argc,
sizeof(
char*));
for(
int i=
0;i<argc;i++) {
argv_utf8[i] = wchar2utf8(argv[i], wcslen(argv[i]));
}
int ret = dav_main(argc, argv_utf8);
for(
int i=
0;i<argc;i++) {
free(argv_utf8[i]);
}
free(argv_utf8);
return ret;
}
#else
int main(
int argc,
char **argv) {
return dav_main(argc, argv);
}
#endif
int dav_main(
int argc,
char **argv) {
if(argc <
2) {
fprintf(stderr,
"Missing command\n");
print_usage(argv[
0]);
return -
1;
}
putenv(
"LC_TIME=C");
char *cmd = argv[
1];
CmdArgs *args = cmd_parse_args(argc -
2, argv +
2);
if(!args) {
print_usage(argv[
0]);
return -
1;
}
if(cmd_getoption(args,
"noinput")) {
pwdstore_set_pwinput_func(
NULL,
NULL);
}
sys_init();
xmlGenericErrorFunc fnc = xmlerrorfnc;
initGenericErrorDefaultFunc(&fnc);
ctx = dav_context_new();
dav_add_namespace(ctx,
"apache",
"http://apache.org/dav/props/");
int cfgret = load_config(ctx);
int ret =
EXIT_FAILURE;
printxmlerror =
0;
#ifdef DO_THE_TEST
test(args);
return 0;
#endif
if(!strcmp(cmd,
"check") || !strcmp(cmd,
"check-config")) {
if(!cfgret) {
fprintf(stdout,
"Configuration OK.\n");
ret =
EXIT_SUCCESS;
}
else {
ret =
EXIT_FAILURE;
}
}
else if(!cfgret) {
if(!strcasecmp(cmd,
"list") || !strcasecmp(cmd,
"ls")) {
ret = cmd_list(args);
}
else if(!strcasecmp(cmd,
"get")) {
ret = cmd_get(args,
FALSE);
}
else if(!strcasecmp(cmd,
"cat")) {
cxMapPut(args->options, cx_hash_key_str(
"output"),
"-");
ret = cmd_get(args,
FALSE);
}
else if(!strcasecmp(cmd,
"edit")) {
ret = cmd_edit(args);
}
else if(!strcasecmp(cmd,
"put")) {
ret = cmd_put(args,
FALSE);
}
else if(
!strcasecmp(cmd,
"remove") ||
!strcasecmp(cmd,
"rm") ||
!strcasecmp(cmd,
"delete"))
{
ret = cmd_remove(args);
}
else if(!strcasecmp(cmd,
"mkdir") || !strcasecmp(cmd,
"mkcol")) {
ret = cmd_mkdir(args);
}
else if(!strcasecmp(cmd,
"copy") || !strcasecmp(cmd,
"cp")) {
ret = cmd_move(args, true);
}
else if(!strcasecmp(cmd,
"move") || !strcasecmp(cmd,
"mv")) {
ret = cmd_move(args, false);
}
else if(!strcasecmp(cmd,
"rename")) {
ret = cmd_rename(args);
}
else if(!strcasecmp(cmd,
"export")) {
ret = cmd_get(args,
TRUE);
}
else if(!strcasecmp(cmd,
"import")) {
ret = cmd_put(args,
TRUE);
}
else if(!strcasecmp(cmd,
"date")) {
ret = cmd_date(args);
}
else if(!strcasecmp(cmd,
"set-property")) {
ret = cmd_set_property(args);
}
else if(!strcasecmp(cmd,
"get-property")) {
ret = cmd_get_property(args);
}
else if(!strcasecmp(cmd,
"remove-property")) {
ret = cmd_remove_property(args);
}
else if(!strcasecmp(cmd,
"lock")) {
ret = cmd_lock(args);
}
else if(!strcasecmp(cmd,
"unlock")) {
ret = cmd_unlock(args);
}
else if(!strcasecmp(cmd,
"info")) {
ret = cmd_info(args);
}
else if(!strcasecmp(cmd,
"checkout")) {
ret = cmd_checkout(args);
}
else if(!strcasecmp(cmd,
"checkin")) {
ret = cmd_checkin(args);
}
else if(!strcasecmp(cmd,
"uncheckout")) {
ret = cmd_uncheckout(args);
}
else if(!strcasecmp(cmd,
"versioncontrol")) {
ret = cmd_versioncontrol(args);
}
else if(!strcasecmp(cmd,
"list-versions") || !strcasecmp(cmd,
"lsv")) {
ret = cmd_list_versions(args);
}
else if(!strcasecmp(cmd,
"add-repository")
|| !strcasecmp(cmd,
"add-repo")) {
ret = cmd_add_repository(args);
}
else if(!strcasecmp(cmd,
"remove-repository")
|| !strcasecmp(cmd,
"remove-repo")
|| !strcasecmp(cmd,
"rm-repo")) {
ret = cmd_remove_repository(args);
}
else if(!strcasecmp(cmd,
"list-repositories")
|| !strcasecmp(cmd,
"list-repos")) {
ret = cmd_list_repositories();
}
else if(!strcasecmp(cmd,
"repository-url")
|| !strcasecmp(cmd,
"repo-url")) {
ret = cmd_repository_url(args);
}
else if(!strcasecmp(cmd,
"add-user")) {
ret = cmd_add_user(args);
}
else if(!strcasecmp(cmd,
"list-users")) {
ret = cmd_list_users(args);
}
else if(!strcasecmp(cmd,
"remove-user")) {
ret = cmd_remove_user(args);
}
else if(!strcasecmp(cmd,
"edit-user")) {
ret = cmd_edit_user(args);
}
else if(!strcasecmp(cmd,
"set-master-password") || !strcasecmp(cmd,
"set-master-pw")) {
ret = cmd_set_master_password(args);
}
else if(!strcasecmp(cmd,
"version") || !strcasecmp(cmd,
"-version")
|| !strcasecmp(cmd,
"--version")) {
fprintf(stderr,
"dav %s\n",
DAV_VERSION);
}
else if(!strcasecmp(cmd,
"complete")) {
ret = cmd_complete(args);
}
else {
print_usage(argv[
0]);
}
}
dav_context_destroy(ctx);
cmd_args_free(args);
free_config();
xmlCleanupParser();
curl_global_cleanup();
sys_uninit();
return ret;
}
static char *cmdusageinfo[] = {
"list [-altdepcR] [-u <date>] <url>",
"get [-pcRK] [-o <file>] [-u <date>] [-V <version>] <url>",
"put [-pcR] [-k <key>] [-L <lock>] <url> <file...>",
"edit [-pc] [-k <key>] [-V <version>] [-L <lock>] <url>",
"mkdir [-pc] [-k <key>] [-L <lock>] <url> [file...]",
"remove [-pc] [-L <lock>] <url> [file...]",
"copy [-pcO] [-L <lock>] <url> <url>",
"move [-pcO] [-L <lock>] <url> <url>",
"rename [-pcO] [-L <lock>] <url> <name>",
"export [-pc] [-o <file>] [-u <date>] <url>",
"import [-pc] [-k <key>] [-L <lock>] <url> <file>",
"get-property [-pcx] [-V <version>] [-n <uri>] <url> <property>",
"set-property [-pcx] [-L <lock>] [-n <uri>] <url> <property> [value]",
"remove-property [-pc] [-n <uri>] <url> <property>",
"lock [-pc] [-T timeout] <url>",
"unlock [-pc] [-L <lock>] <url>",
"info [-pc] [-V <version>] <url>",
"date [url]",
NULL
};
char* find_usage_str(
const char *cmd) {
cxstring c = cx_str(cmd);
for(
int i=
0;;i++) {
char *str = cmdusageinfo[i];
if(!str) {
break;
}
cxstring u = cx_str(str);
if(cx_strprefix(u, c)) {
return str;
}
}
return NULL;
}
void print_usage(
char *cmd) {
fprintf(stderr,
"Usage: %s command [options] arguments...\n\n", cmd);
fprintf(stderr,
"Commands:\n");
for(
int i=
0;;i++) {
char *str = cmdusageinfo[i];
if(!str) {
break;
}
fprintf(stderr,
" %s\n", str);
}
fprintf(stderr,
"Options:\n");
fprintf(stderr,
" -k <key> Key to use for encryption\n");
fprintf(stderr,
" -p Don''t encrypt or decrypt files\n");
fprintf(stderr,
" -c Enable full encryption\n");
fprintf(stderr,
" -R "
"Recursively do the operation for all children\n");
fprintf(stderr,
" -K Keep already present files\n");
fprintf(stderr,
" -o <file> Write output to file (use ''-'' for stdout)\n");
fprintf(
stderr,
" -u <date> "
"Get resources which are modified since the specified date\n");
fprintf(stderr,
" -V <version> Download a specific version of a resource\n");
fprintf(stderr,
" -a show all files\n");
fprintf(stderr,
" -l print resources in long list format\n");
fprintf(stderr,
" -t print content type\n");
fprintf(stderr,
" -d order by last modified date\n");
fprintf(stderr,
" -e show extended flags\n");
fprintf(stderr,
" -O override resources\n");
fprintf(stderr,
" -L <lock> specificy lock token\n");
fprintf(stderr,
" -T <sec> timeout in seconds\n");
fprintf(stderr,
" -n <uri> specify namespace uri\n");
fprintf(stderr,
" -x xml property content\n");
fprintf(stderr,
" -N disable authentication prompt (all commands)\n");
fprintf(stderr,
" -i disable cert verification (all commands)\n");
fprintf(stderr,
" -v verbose output (all commands)\n");
fprintf(stderr,
"\n");
fprintf(stderr,
"Advanced commands:\n");
fprintf(stderr,
" versioncontrol list-versions checkout checkin uncheckout\n\n");
fprintf(stderr,
"Config commands:\n");
fprintf(stderr,
" add-repository remove-repository list-repositories repository-url\n");
fprintf(stderr,
" add-user remove-user edit-user list-users set-master-password\n");
fprintf(stderr,
" check-config\n");
fprintf(stderr,
"\n");
fprintf(stderr,
"Instead of an url you can pass a repository name "
"with an optional path:\n");
fprintf(stderr,
" <repository>/path/\n");
fprintf(stderr,
"\n");
}
static int set_session_config(DavSession *sn, CmdArgs *a) {
char *plain = cmd_getoption(a,
"plain");
char *crypt = cmd_getoption(a,
"crypt");
if(plain && crypt) {
fprintf(stderr,
"Error: -p and -c option set\n");
return 1;
}
if (plain) {
sn->flags &= ~
DAV_SESSION_FULL_ENCRYPTION;
}
else if(crypt) {
sn->flags |=
DAV_SESSION_FULL_ENCRYPTION;
}
if (cmd_getoption(a,
"verbose")) {
curl_easy_setopt(sn->handle,
CURLOPT_VERBOSE,
1L);
curl_easy_setopt(sn->handle,
CURLOPT_STDERR, stderr);
sn->logfunc = dav_verbose_log;
}
return 0;
}
static void set_session_lock(DavSession *sn, CmdArgs *a) {
char *locktoken = cmd_getoption(a,
"lock");
if(locktoken) {
DavLock *lock = dav_create_lock(sn, locktoken,
NULL);
dav_add_collection_lock(sn,
"/", lock);
}
}
int update_progress(DavResource *res,
int64_t total,
int64_t now, Progress *p) {
int ret =
0;
if(res != p->last_resource) {
p->cur += p->last_res_total - p->last_res_cur;
ret =
1;
}
else {
p->cur += now - p->last_res_cur;
}
p->last_resource = res;
p->last_res_cur = now;
p->last_res_total = total;
return ret;
}
void download_progress(DavResource *res,
int64_t total,
int64_t now,
void *data) {
Progress *p = data;
int newres = update_progress(res, total, now, p);
time_t newts = time(
NULL);
if((p->ts != newts)) {
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));
fflush(p->out);
}
p->ts = newts;
}
#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"
#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"
int cmd_list(CmdArgs *a) {
if(a->argc !=
1) {
fprintf(stderr,
"Too %s arguments\n", a->argc <
1 ?
"few":
"many");
fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"list"));
return -
1;
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
if(set_session_config(sn, a)) {
return -
1;
}
char *update = cmd_getoption(a,
"update");
char *date = cmd_getoption(a,
"date");
time_t t = -
1;
if(update) {
t = util_parse_lastmodified(update);
}
int depth = cmd_getoption(a,
"recursive") ? -
1 :
1;
int ret =
0;
DavResource *ls = dav_query(
sn,
date ?
LIST_QUERY_ORDER_BY_DATE :
LIST_QUERY_ORDER_BY_NAME,
path,
depth,
t);
if(ls) {
void (*print_func)(DavResource*,
char *, CmdArgs *);
if(cmd_getoption(a,
"list") || cmd_getoption(a,
"extended")) {
print_func = ls_print_list_elm;
}
else {
print_func = ls_print_elm;
}
DavResource *child = ls->children;
while(child) {
print_func(child, path, a);
child = child->next;
}
}
else {
print_resource_error(sn, path);
ret = -
1;
}
free(path);
dav_session_destroy(sn);
return ret;
}
static char* ls_name(
char *parent,
char *path,
int *len) {
if(parent) {
path += strlen(parent);
}
if(path[
0] ==
'/') {
path++;
}
int pathlen = strlen(path);
if(path[pathlen-
1] ==
'/') {
pathlen--;
}
*len = pathlen;
return path;
}
void ls_print_list_elm(DavResource *res,
char *parent, CmdArgs *a) {
int recursive = cmd_getoption(a,
"recursive") ?
1 :
0;
int show_all = cmd_getoption(a,
"all") ?
1 :
0;
if(res->name[
0] ==
'.' && !show_all) {
return;
}
char flags[
16];
memset(flags,
'-',
15);
int type_width =
0;
char *type = res->contenttype;
if(res->iscollection) {
flags[
0] =
'd';
type =
"";
}
char *keyprop = dav_get_string_property_ns(
res,
DAV_NS,
"crypto-key");
if(keyprop) {
flags[
1] =
'c';
}
if(cmd_getoption(a,
"extended")) {
flags[
6] =
'\0';
if(dav_get_string_property(res,
"D:lockdiscovery")) {
flags[
2] =
'l';
}
char *executable = dav_get_string_property_ns(
res,
"http://apache.org/dav/props/",
"executable");
if(executable && util_getboolean(executable)) {
flags[
3] =
'x';
}
}
else {
flags[
2] =
'\0';
}
if(cmd_getoption(a,
"type")) {
type_width =
20;
}
if(type ==
NULL || type_width ==
0) {
type =
"";
}
char *date = util_date_str(res->lastmodified);
char *size = util_size_str(res->iscollection, res->contentlength);
int namelen = strlen(res->name);
char *name = recursive ? ls_name(parent, res->path, &namelen) : res->name;
printf(
"%s %*s %10s %12s %.*s\n",
flags,
type_width, type,
size,
date,
namelen,
name);
free(date);
free(size);
if(recursive) {
DavResource *child = res->children;
while(child) {
if(child->name[
0] !=
'.' || show_all) {
ls_print_list_elm(child, parent, a);
}
child = child->next;
}
}
}
void ls_print_elm(DavResource *res,
char *parent, CmdArgs *a) {
int recursive = cmd_getoption(a,
"recursive") ?
1 :
0;
int show_all = cmd_getoption(a,
"all") ?
1 :
0;
if(res->name[
0] ==
'.' && !show_all) {
return;
}
int namelen = strlen(res->name);
char *name = recursive ? ls_name(parent, res->path, &namelen) : res->name;
printf(
"%.*s\n", namelen, name);
if(recursive) {
DavResource *child = res->children;
while(child) {
ls_print_elm(child, parent, a);
child = child->next;
}
}
}
static void free_getres(
void *r) {
GetResource *getres = r;
free(getres->path);
free(getres);
}
int cmd_get(CmdArgs *a, DavBool export) {
if(a->argc !=
1) {
fprintf(stderr,
"Too %s arguments\n", a->argc <
1 ?
"few":
"many");
fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"get"));
return -
1;
}
if(export) {
cxMapPut(a->options, cx_hash_key_str(
"recursive"),
"");
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
if(set_session_config(sn, a)) {
return -
1;
}
char *progressfile = cmd_getoption(a,
"progressfile");
Progress pdata;
memset(&pdata,
0,
sizeof(Progress));
if(progressfile) {
if(!strcmp(progressfile,
"-")) {
pdata.out = stdout;
pdata.isstdout =
1;
}
else {
pdata.out = fopen(progressfile,
"w");
}
if(pdata.out) {
dav_session_set_progresscallback(sn, download_progress,
NULL, &pdata);
}
}
char *update = cmd_getoption(a,
"update");
time_t t = -
1;
if(update) {
t = util_parse_lastmodified(update);
if (t ==
0) {
fprintf(stderr,
"Invalid date format. Possible formats are:\n"
" RFC-1123 - example: Thu, 29 Nov 2012 21:35:35 GMT\n"
" RFC-3339 - example: 2012-11-29T21:35:35Z\n");
return -
1;
}
}
int recursive = cmd_getoption(a,
"recursive") ?
1 :
0;
char *version = cmd_getoption(a,
"version");
if(recursive && version) {
fprintf(stderr,
"-V option can only be used without -R option\n");
return -
1;
}
DavResource *res;
int depth = recursive ? -
1 :
1;
res = dav_query(
sn,
"select - from %s with depth = %d where iscollection or lastmodified > %t",
path,
depth,
t);
if(!res) {
print_resource_error(sn, path);
return -
1;
}
if(!recursive && res->iscollection) {
fprintf(stderr,
"Resource %s is a collection.\n", res->path);
fprintf(stderr,
"Use the -R option to download collections.\n");
return -
1;
}
if(version) {
DavResource *vres = find_version(res, version);
if(!vres) {
fprintf(stderr,
"Cannot find version ''%s'' for resource.\n", version);
return -
1;
}
dav_resource_free_all(res);
res = vres;
}
char *outfile = cmd_getoption(a,
"output");
char *basepath = outfile;
if(!outfile) {
if(res->iscollection) {
basepath =
"";
}
else {
basepath = res->name;
}
if(export) {
outfile =
"-";
}
}
else if(export) {
basepath =
"";
}
else if(res->iscollection && !strcmp(outfile,
"-")) {
fprintf(
stderr,
"Cannot write output to stdout "
"if the requested resource is a collection.\n");
return -
1;
}
CxList *reslist = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
cxDefineDestructor(reslist, free_getres);
uint64_t totalsize =
0;
uint64_t rescount =
0;
GetResource *getres = malloc(
sizeof(GetResource));
getres->res = res;
getres->path = strdup(basepath);
char *structure = cmd_getoption(a,
"structure");
CxList *stack = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
cxListInsert(stack,
0, getres);
while(cxListSize(stack) >
0) {
GetResource *g = cxListAt(stack,
0);
cxListRemove(stack,
0);
if(g->res->iscollection) {
DavResource *child = g->res->children;
while(child) {
size_t pathlen = strlen(g->path);
GetResource *newres = malloc(
sizeof(GetResource));
newres->res = child;
newres->path = pathlen >
0 ?
util_concat_path(g->path, child->name) : strdup(child->name);
cxListInsert(stack,
0, newres);
child = child->next;
}
}
else {
if(structure) {
continue;
}
totalsize += g->res->contentlength;
rescount++;
}
if(strlen(g->path) ==
0) {
free_getres(g);
}
else {
cxListAdd(reslist, g);
}
}
cxListDestroy(stack);
pdata.total = totalsize;
int ret;
getfunc get;
TarOutputStream *tout =
NULL;
if(export) {
get = (getfunc)resource2tar;
FILE *tarfile = strcmp(outfile,
"-") ? fopen(outfile,
"wb") : stdout;
if(!tarfile) {
perror(
"Cannot open tar output file");
return -
1;
}
tout = tar_open(tarfile);
}
else {
get = get_resource;
}
CxIterator iter = cxListIterator(reslist);
cx_foreach(GetResource *, res_item, iter) {
ret = get(repo, res_item, a, tout);
if(ret) {
break;
}
}
if(export) {
if(tar_close(tout)) {
fprintf(stderr,
"tar stream broken\n");
ret = -
1;
}
}
cxListDestroy(reslist);
free(path);
if(pdata.out && !pdata.isstdout) {
fclose(pdata.out);
}
return ret;
}
static int file_seek(
FILE *f,
curl_off_t offset,
int origin) {
int ret = fseek(f, offset, origin);
return ret ==
0 ?
CURL_SEEKFUNC_OK :
CURL_SEEKFUNC_CANTSEEK;
}
static int check_encryption_key(CmdArgs *a, DavSession *sn) {
char *keyname = cmd_getoption(a,
"key");
if(keyname) {
DavKey *key = dav_context_get_key(ctx, keyname);
if(key) {
sn->key = key;
}
else {
fprintf(stderr,
"Key %s not found!\nAbort.\n", keyname);
return 1;
}
if(!
DAV_IS_ENCRYPTED(sn)) {
fprintf(stderr,
"A key has been explicitly specified, but no "
"encryption is requested.\n"
"You have the following options:\n"
" - pass ''-c'' as command line argument to request encryption\n"
" - activate encryption in the config.xml\n"
" - don''t use ''-k <key>'' "
"(warning: encryption will NOT happen)\n");
return 1;
}
}
if(
DAV_IS_ENCRYPTED(sn) && !(sn->key)) {
fprintf(stderr,
"Encryption has been requested, "
"but no default key is configured.\n"
"You may specify a custom key with the ''-k'' option.\n");
return 1;
}
return 0;
}
int cmd_edit(CmdArgs *a) {
#ifdef _WIN32
fprintf(stderr,
"This feature is not supported on your platform.\n");
return -
1;
#else
if(a->argc !=
1) {
fprintf(stderr,
"Too %s arguments\n", a->argc <
1 ?
"few":
"many");
fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"edit"));
return -
1;
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
if(set_session_config(sn, a)) {
return -
1;
}
set_session_lock(sn, a);
if(check_encryption_key(a, sn)) {
return -
1;
}
char *version = cmd_getoption(a,
"version");
DavResource *res;
res = dav_resource_new(sn, path);
int fresh_resource = !dav_exists(res);
if(fresh_resource) {
if(dav_create(res)) {
fprintf(stderr,
"Resource does not exist and cannot be created.\n");
return -
1;
}
}
else {
if(!res) {
print_resource_error(sn, path);
return -
1;
}
if(res->iscollection) {
fprintf(stderr,
"Resource %s is a collection "
"and cannot be opened in an editor.\n", res->path);
return -
1;
}
if(version) {
DavResource *vres = find_version(res, version);
if(!vres) {
fprintf(stderr,
"Cannot find version ''%s'' for resource.\n", version);
return -
1;
}
dav_resource_free_all(res);
res = vres;
}
}
char* envtmp = getenv(
"TMPDIR");
char* outfile;
if(envtmp) {
size_t len = strlen(envtmp);
outfile = malloc(len+
24);
memcpy(outfile, envtmp, len+
1);
if(outfile[len-
1] !=
'/') {
outfile[len] =
'/';
outfile[len+
1] =
0;
}
}
else {
outfile = malloc(
24);
strncpy(outfile,
"/tmp/",
24);
}
if(access(outfile,
W_OK)) {
char* home = getenv(
"HOME");
if(home) {
size_t len = strlen(home);
outfile = malloc(len+
24);
memcpy(outfile, home, len+
1);
if(outfile[len-
1] !=
'/') {
outfile[len] =
'/';
outfile[len+
1] =
0;
}
}
else {
perror(
"Cannot write to temporary location");
free(outfile);
return -
1;
}
}
outfile = strncat(outfile,
".dav-edit-XXXXXX",
23);
int tmp_fd = mkstemp(outfile);
if(tmp_fd <
0) {
perror(
"Cannot open temporary file");
return -
1;
}
if(!fresh_resource) {
FILE* tmp_stream = sys_fopen(outfile,
"wb");
if(!tmp_stream) {
perror(
"Cannot open temporary file");
free(outfile);
close(tmp_fd);
return -
1;
}
if(dav_get_content(res, tmp_stream, (dav_write_func)fwrite)) {
print_resource_error(sn, path);
free(outfile);
close(tmp_fd);
sys_unlink(outfile);
return -
1;
}
fclose(tmp_stream);
}
SYS_STAT tmp_stat;
if(sys_stat(outfile, &tmp_stat)) {
perror(
"Cannot stat temporary file");
free(outfile);
close(tmp_fd);
sys_unlink(outfile);
return -
1;
}
time_t dl_mtime = tmp_stat.st_mtime;
char* default_editor =
"vi";
char* editor = getenv(
"EDITOR");
if(!editor) editor = default_editor;
char* viargs[
3] = {editor, outfile,
NULL};
int ret =
0;
pid_t pid = fork();
if(pid <
0) {
perror(
"Cannot create process for editor");
ret = -
1;
}
else if(pid ==
0) {
if(execvp(viargs[
0], viargs)) {
perror(
"Opening the editor failed");
return -
1;
}
}
else {
int status = -
1;
ret = waitpid(pid, &status,
0);
if(ret <
0) {
perror(
"Error waiting for editor");
}
else if(
WEXITSTATUS(status)) {
fprintf(stderr,
"Editor closed abnormally - file will not be uploaded.\n");
ret = -
1;
}
else {
if (sys_stat(outfile, &tmp_stat)) {
perror(
"Cannot stat temporary file");
ret = -
1;
}
else if (dl_mtime < tmp_stat.st_mtime) {
FILE* tmp_stream = sys_fopen(outfile,
"rb");
if(!tmp_stream) {
perror(
"Cannot open temporary file");
ret = -
1;
}
else {
dav_set_content(res, tmp_stream,
(dav_read_func)fread,
(dav_seek_func)file_seek);
dav_set_content_length(res, tmp_stat.st_size);
ret = dav_store(res);
fclose(tmp_stream);
if(ret) {
print_resource_error(sn, path);
}
}
}
else {
printf(
"No changes by user - file will not be uploaded.\n");
ret =
0;
}
}
}
close(tmp_fd);
if(ret) {
fprintf(stderr,
"File location: %s\n", outfile);
}
else {
sys_unlink(outfile);
}
free(outfile);
free(path);
return ret;
#endif
}
int get_resource(DavCfgRepository *repo, GetResource *getres, CmdArgs *a,
void *unused) {
DavResource *res = getres->res;
char *out = getres->path;
if(res->iscollection) {
printf(
"get: %s\n", res->path);
int ret = sys_mkdir(out);
if(ret !=
0 && errno !=
EEXIST) {
fprintf(stderr,
"Cannot create directory ''%s'': ", out);
perror(
"");
return 1;
}
return 0;
}
int isstdout = !strcmp(out,
"-");
if(cmd_getoption(a,
"keep") && !isstdout) {
SYS_STAT s;
if(sys_stat(out, &s)) {
if(errno !=
ENOENT) {
perror(
"stat");
}
}
else {
if(cmd_getoption(a,
"recursive")) {
printf(
"skip: %s\n", res->path);
}
return 0;
}
}
if(cmd_getoption(a,
"recursive")) {
printf(
"get: %s\n", res->path);
}
FILE *fout = isstdout ? stdout : sys_fopen(out,
"wb");
if(!fout) {
fprintf(stderr,
"cannot open output file\n");
return -
1;
}
int ret = dav_get_content(res, fout, (dav_write_func)fwrite);
fclose(fout);
if(ret && strcmp(out,
"-")) {
print_resource_error(res->session, res->path);
}
return 0;
}
#define DEFAULT_DIR_MODE T_IRUSR |
T_IWUSR |
T_IXUSR |
T_IRGRP |
T_IXGRP |
T_IROTH |
T_IXOTH
#define DEFAULT_FILE_MODE T_IRUSR |
T_IWUSR |
T_IRGRP |
T_IROTH
int resource2tar(DavCfgRepository *repo, GetResource *res, CmdArgs *a, TarOutputStream *tar) {
DavResource *d = res->res;
if(d->iscollection) {
fprintf(stderr,
"add d: %s\n", res->path);
return tar_add_dir(tar, res->path,
DEFAULT_DIR_MODE, d->lastmodified);
}
fprintf(stderr,
"add f: %s\n", res->path);
if(tar_begin_file(tar, res->path,
DEFAULT_FILE_MODE, d->contentlength, d->lastmodified)) {
fprintf(stderr,
"TAR Error: %s\n", tar_error2str(tar->error));
return -
1;
}
if(dav_get_content(d, tar, (dav_write_func)tar_fwrite)) {
print_resource_error(d->session, d->path);
return -
1;
}
return tar_end_file(tar);
}
int cmd_put(CmdArgs *a, DavBool import) {
if(a->argc <
2) {
fprintf(stderr,
"Too few arguments\n");
fprintf(stderr,
"Usage: dav %s\n",
find_usage_str(import ?
"import" :
"put"));
return -
1;
}
DavBool use_stdin =
FALSE;
for(
int i=
1;i<a->argc;i++) {
if(!strcmp(a->argv[i],
"-")) {
if(use_stdin) {
fprintf(stderr,
"Error: stdin can only occur once in input list\n");
return -
1;
}
else {
use_stdin =
TRUE;
}
}
}
if(import) {
cxMapPut(a->options, cx_hash_key_str(
"resursive"),
"");
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
if(set_session_config(sn, a)) {
return -
1;
}
set_session_lock(sn, a);
if(check_encryption_key(a, sn)) {
return -
1;
}
DavBool printfile =
FALSE;
DavBool ignoredirerr =
FALSE;
if(a->argc >
2) {
printfile =
TRUE;
ignoredirerr =
TRUE;
}
else if(cmd_getoption(a,
"recursive")) {
printfile =
TRUE;
}
char *finfo_str = cmd_getoption(a,
"finfo");
uint32_t finfo =
0;
if(finfo_str) {
finfo = parse_finfo_settings(finfo_str,
NULL);
}
int ret;
for(
int i=
1;i<a->argc;i++) {
char *file = a->argv[i];
if(!import) {
if(!strcmp(file,
"-")) {
FILE *in = stdin;
ret = put_file(repo, a, sn, path,
"stdin",
0,
NULL, in,
0);
}
else {
ret = put_entry(
repo,
a,
sn,
path,
file,
finfo,
TRUE,
printfile,
ignoredirerr);
}
}
else {
ret = put_tar(repo, a, sn, file, path);
}
if(ret) {
break;
}
}
free(path);
dav_session_destroy(sn);
return ret;
}
#if !defined(
S_ISREG) && defined(
S_IFMT) && defined(
S_IFREG)
#define S_ISREG(m) (((m) &
S_IFMT) ==
S_IFREG)
#endif
#ifdef _WIN32
#ifndef S_ISDIR
#define S_ISDIR(mode) ((mode) &
_S_IFMT) ==
_S_IFDIR
#define S_ISREG(mode) ((mode) &
_S_IFMT) ==
_S_IFREG
#endif
#endif
int put_entry(
DavCfgRepository *repo,
CmdArgs *a,
DavSession *sn,
char *path,
char *file,
uint32_t finfo,
DavBool root,
DavBool printfile,
DavBool ignoredirerr)
{
int recursive = cmd_getoption(a,
"recursive") ?
1 :
0;
SYS_STAT s;
if(sys_stat(file, &s)) {
perror(
"stat");
fprintf(stderr,
"cannot stat file %s\n", file);
return -
1;
}
int ret =
0;
if(
S_ISDIR(s.st_mode)) {
if(!recursive) {
if(ignoredirerr) {
printf(
"skip: %s\n", file);
}
else {
fprintf(
stderr,
"%s is a directory.\nUse the -R option to upload directories.\n",
file);
return 1;
}
}
if(!root) {
printf(
"mkcol: %s\n", file);
DavResource *res = dav_resource_new(sn, path);
res->iscollection =
TRUE;
if(!dav_exists(res)) {
if(dav_create(res)) {
fprintf(stderr,
"Cannot create collection %s\n", path);
print_resource_error(sn, res->path);
dav_resource_free(res);
return 1;
}
}
dav_resource_free(res);
}
SYS_DIR dir = sys_opendir(file);
if(!dir) {
}
SysDirEnt *entry;
int nument =
0;
while((entry = sys_readdir(dir)) !=
NULL) {
if(!strcmp(entry->name,
".") || !strcmp(entry->name,
"..")) {
continue;
}
nument++;
char *entry_file = util_concat_path(file, entry->name);
char *entry_path = util_concat_path(path, entry->name);
int r = put_entry(
repo,
a,
sn,
entry_path,
entry_file,
finfo,
FALSE,
printfile,
ignoredirerr);
free(entry_path);
free(entry_file);
if(r) {
ret =
1;
break;
}
}
sys_closedir(dir);
}
else if(
S_ISREG(s.st_mode)) {
if(printfile) {
printf(
"put: %s\n", file);
}
FILE *in = sys_fopen(file,
"rb");
if(!in) {
fprintf(stderr,
"cannot open input file\n");
return -
1;
}
const char *filename = util_resource_name(file);
ret = put_file(repo, a, sn, path, filename, finfo, file, in, s.st_size);
fclose(in);
}
return ret;
}
int put_tar(DavCfgRepository *repo, CmdArgs *a, DavSession *sn,
char *tarfile,
char *path) {
int isstdin = !strcmp(tarfile,
"-");
FILE *in = isstdin ? stdin : fopen(tarfile,
"rb");
if(!in) {
perror(
"Cannot open tar file");
return -
1;
}
DavResource *col = dav_query(sn,
"select - from %s", path);
if(!col) {
if(sn->error ==
DAV_NOT_FOUND) {
col = dav_resource_new(sn, path);
col->iscollection =
TRUE;
if(dav_create(col)) {
print_resource_error(sn, path);
return -
1;
}
}
else {
print_resource_error(sn, path);
return -
1;
}
}
else if(!col->iscollection) {
fprintf(stderr,
"%s is not a collection\n", col->href);
return -
1;
}
int ret =
0;
TarInputStream *tar = tar_inputstream_open(in);
TarEntry *e =
NULL;
while((e = tar_read_entry(tar)) !=
NULL) {
char *newpath = util_concat_path(path, e->path);
if(e->type ==
TAR_TYPE_FILE) {
fprintf(stderr,
"put: %s\n", e->path);
DavResource *res = dav_resource_new(sn, newpath);
dav_set_content(res, tar, (dav_read_func)tar_fread, (dav_seek_func)tar_seek);
dav_set_content_length(res, (
size_t)e->size);
if(dav_store(res)) {
print_resource_error(sn, res->path);
fprintf(stderr,
"Cannot upload file.\n");
if(sn->errorstr) {
fprintf(stderr,
"%s\n", sn->errorstr);
}
return -
1;
}
}
else if(e->type ==
TAR_TYPE_DIRECTORY) {
printf(
"mkcol: %s\n", e->path);
DavResource *res = dav_resource_new(sn, newpath);
res->iscollection =
TRUE;
if(!dav_exists(res)) {
if(dav_create(res)) {
fprintf(stderr,
"Cannot create collection %s\n", newpath);
print_resource_error(sn, res->path);
ret =
1;
free(newpath);
break;
}
}
}
else {
fprintf(stderr,
"skip: %s\n", e->path);
}
free(newpath);
}
if(tar->error !=
TAR_OK) {
ret = -
1;
}
if(!isstdin) {
fclose(in);
}
return ret;
}
int put_file(
DavCfgRepository *repo,
CmdArgs *a,
DavSession *sn,
const char *path,
const char *name,
uint32_t finfo,
const char *fpath,
FILE *in,
off_t len)
{
DavResource *res = dav_query(sn,
"select - from %s", path);
if(!res) {
if(sn->error ==
DAV_NOT_FOUND) {
res = dav_resource_new(sn, path);
if(dav_create(res)) {
fprintf(stderr,
"Cannot create resource.\n");
return -
1;
}
}
else {
print_resource_error(sn, path);
return -
1;
}
}
else if(res->iscollection) {
char *newpath = util_concat_path(path, name);
if (!strcmp(path, newpath)) {
fprintf(stderr,
"Cannot put file, because a collection with "
"that name already exists.\n");
free(newpath);
return -
1;
}
path = newpath;
res = dav_resource_new(sn, path);
int ret = put_file(repo, a, sn, res->path,
NULL, finfo, fpath, in, len);
free(newpath);
return ret;
}
if(resource_set_finfo(fpath, res, finfo)) {
fprintf(stderr,
"Cannot set finfo: %s.\n", strerror(errno));
}
if((finfo &
FINFO_XATTR) ==
FINFO_XATTR) {
XAttributes *xattr = file_get_attributes(fpath,
NULL,
NULL);
if(xattr) {
resource_set_xattr(res, xattr);
}
}
dav_set_content(res, in, (dav_read_func)fread, (dav_seek_func)file_seek);
if(len >
0 && len < 0x7d000000) {
dav_set_content_length(res, (
size_t)len);
}
if(dav_store(res)) {
print_resource_error(sn, res->path);
fprintf(stderr,
"Cannot upload file.\n");
if(sn->errorstr) {
fprintf(stderr,
"%s\n", sn->errorstr);
}
return -
1;
}
return 0;
}
static int dav_create_col(DavResource *res) {
res->iscollection =
1;
return dav_create(res);
}
static void print_cannoterr(DavSession *sn,
const char *message) {
switch(sn->error) {
case DAV_UNSUPPORTED_PROTOCOL:
case DAV_COULDNT_RESOLVE_PROXY:
case DAV_COULDNT_RESOLVE_HOST:
case DAV_COULDNT_CONNECT:
case DAV_TIMEOUT:
case DAV_SSL_ERROR:
break;
default: fprintf(stderr,
"Cannot %s.\n", message);
}
}
static int cmd_operation_on_resources(CmdArgs* a,
int(*operation)(DavResource*),
const char* command,
const char* message,
DavBool check_key)
{
if(a->argc <
1) {
fprintf(stderr,
"Too few arguments\n");
fprintf(stderr,
"Usage: dav %s\n", find_usage_str(command));
return -
1;
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
int exit_code = -
1;
assert(!!path && !!sn);
if(set_session_config(sn, a)) {
goto cmd_oponres_exit;
}
set_session_lock(sn, a);
if(check_key && check_encryption_key(a, sn)) {
goto cmd_oponres_exit;
}
DavResource *res = dav_resource_new(sn, path);
assert(!!res);
res->iscollection =
1;
if(a->argc ==
1) {
if(operation(res)) {
print_cannoterr(sn, message);
print_resource_error(sn, res->path);
goto cmd_oponres_exit;
}
}
else {
for(
int i =
1 ; i < a->argc ;++i) {
DavResource *child = dav_resource_new_child(sn, res, a->argv[i]);
assert(!!child);
child->iscollection =
1;
if(operation(child)) {
print_cannoterr(sn, message);
print_resource_error(sn, child->path);
goto cmd_oponres_exit;
}
}
}
exit_code =
0;
cmd_oponres_exit:
free(path);
dav_session_destroy(sn);
return exit_code;
}
int cmd_remove(CmdArgs *a) {
return cmd_operation_on_resources(a, dav_delete,
"remove",
"delete resource",
FALSE);
}
int cmd_mkdir(CmdArgs *a) {
return cmd_operation_on_resources(a, dav_create_col,
"mkdir",
"create collection",
TRUE);
}
int cmd_move(CmdArgs *a,
int cp) {
const char* actionstr = cp ?
"copy" :
"move";
if(a->argc !=
2) {
fprintf(stderr,
"Too %s arguments\n", a->argc <
2 ?
"few":
"many");
fprintf(stderr,
"Usage: dav %s\n", find_usage_str(actionstr));
return -
1;
}
char *srcurl = a->argv[
0];
char *srcpath =
NULL;
DavCfgRepository *srcrepo = dav_config_url2repo(get_config(), srcurl, &srcpath);
DavSession *srcsn = connect_to_repo(ctx, srcrepo, srcpath, request_auth, a);
if(set_session_config(srcsn, a)) {
return -
1;
}
set_session_lock(srcsn, a);
DavBool override = cmd_getoption(a,
"override") ? true : false;
char *desturl = a->argv[
1];
char *destpath =
NULL;
DavCfgRepository *destrepo = dav_config_url2repo(get_config(), desturl, &destpath);
if(srcrepo == destrepo) {
DavResource *res = dav_resource_new(srcsn, srcpath);
int err = cp ? dav_copy_o(res, destpath, override)
: dav_move_o(res, destpath, override);
if(err) {
print_resource_error(srcsn, res->path);
fprintf(stderr,
"Cannot %s resource.\n", actionstr);
return -
1;
}
}
else {
char *srchost = util_url_base(srcrepo->url.value.ptr);
char *desthost = util_url_base(destrepo->url.value.ptr);
if(!strcmp(srchost, desthost)) {
DavSession *destsn = connect_to_repo(ctx, destrepo, destpath, request_auth, a);
if(set_session_config(destsn, a)) {
return -
1;
}
DavResource *dest = dav_resource_new(destsn, destpath);
char *desthref = dav_resource_get_href(dest);
desturl = util_get_url(destsn, desthref);
DavResource *res = dav_resource_new(srcsn, srcpath);
int err = cp ? dav_copyto(res, desturl, override)
: dav_moveto(res, desturl, override);
free(desturl);
dav_session_destroy(destsn);
if(err) {
print_resource_error(srcsn, res->path);
fprintf(stderr,
"Cannot %s resource.\n", actionstr);
return -
1;
}
}
else {
fprintf(stderr,
"Cannot %s between different hosts.\n", actionstr);
return -
1;
}
}
dav_session_destroy(srcsn);
return 0;
}
int cmd_rename(CmdArgs *a) {
if(a->argc !=
2) {
fprintf(stderr,
"Too %s arguments\n", a->argc <
2 ?
"few":
"many");
fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"rename"));
return -
1;
}
char *name = a->argv[
1];
size_t namelen = strlen(name);
for(
size_t i=
0;i<namelen;i++) {
char c = name[i];
if(c ==
'/') {
fprintf(stderr,
"Illegal character in name: ''/''\n");
return 1;
}
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
if(set_session_config(sn, a)) {
return -
1;
}
set_session_lock(sn, a);
int ret =
0;
DavResource *res = dav_get(sn, path,
NULL);
if(res) {
char *cryptoname = dav_get_string_property_ns(res,
DAV_NS,
"crypto-name");
char *cryptokey = dav_get_string_property_ns(res,
DAV_NS,
"crypto-key");
if(cryptoname && cryptokey) {
DavKey *key = dav_context_get_key(ctx, cryptokey);
if(key) {
char *parent = util_parent_path(res->path);
char *newpath = util_concat_path(parent, name);
DavResource *testres = dav_resource_new(sn, newpath);
if(dav_exists(testres)) {
fprintf(stderr,
"A resource with this name already exists.\nAbort.\n");
ret =
1;
}
else {
char *crname = aes_encrypt(name, namelen, key);
dav_set_string_property_ns(res,
DAV_NS,
"crypto-name", crname);
free(crname);
if(dav_store(res)) {
print_resource_error(sn, res->path);
fprintf(stderr,
"Cannot store crypto-name property.\n");
ret =
1;
}
}
free(parent);
free(newpath);
}
else {
fprintf(stderr,
"Key %s not found.\n", cryptokey);
}
}
else {
char *parent = util_parent_path(res->href);
char *new_href = util_concat_path(parent, name);
char *dest = util_get_url(sn, new_href);
free(parent);
free(new_href);
if(dav_moveto(res, dest, false)) {
print_resource_error(sn, path);
fprintf(stderr,
"Cannot rename resource.\n");
ret =
1;
}
free(dest);
}
}
else {
print_resource_error(sn, path);
fprintf(stderr,
"Cannot rename resource.\n");
ret =
1;
}
dav_session_destroy(sn);
free(path);
return ret;
}
static size_t get_date_header_cb(
void *header,
int s,
int n,
void *data) {
char **date_str = (
char**)data;
cxstring h = cx_strn(header, s*n);
if(cx_strprefix(h,
CX_STR(
"Date:"))) {
cxstring v = cx_strsubs(h,
5);
*date_str = cx_strdup(cx_strtrim(v)).ptr;
}
return s*n;
}
int cmd_date(CmdArgs *a) {
if(a->argc <
1) {
time_t now = time(
NULL);
struct tm *date = gmtime(&now);
char str[
32];
putenv(
"LC_TIME=C");
size_t len = strftime(str,
32,
"%a, %d %b %Y %H:%M:%S GMT\n", date);
fwrite(str,
1, len, stdout);
}
else if (a->argc ==
1) {
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
DavResource *res = dav_resource_new(sn, path);
char *date =
NULL;
curl_easy_setopt(sn->handle,
CURLOPT_HEADERFUNCTION, get_date_header_cb);
curl_easy_setopt(sn->handle,
CURLOPT_WRITEHEADER, &date);
if(dav_exists(res) && date) {
printf(
"%s\n", date);
}
else {
return -
1;
}
free(path);
return 0;
}
else {
fprintf(stderr,
"Too many arguments\n");
fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"date"));
return -
1;
}
return 0;
}
int cmd_get_property(CmdArgs *a) {
if(a->argc <
2) {
fprintf(stderr,
"Too few arguments\n");
fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"get-property"));
return -
1;
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
if(set_session_config(sn, a)) {
return -
1;
}
char *namespace = cmd_getoption(a,
"namespace");
char *property = a->argv[
1];
char *version = cmd_getoption(a,
"version");
DavPropName propname;
if(namespace) {
propname.ns = namespace;
propname.name = property;
}
else {
dav_get_property_namespace_str(ctx, property, &propname.ns, &propname.name);
if(!propname.ns || !propname.name) {
fprintf(stderr,
"Error: unknown namespace prefix\n");
return -
1;
}
}
DavResource *res = dav_resource_new(sn, path);
if(version) {
DavResource *vres = find_version(res, version);
if(!vres) {
fprintf(stderr,
"Cannot find version ''%s'' for resource.\n", version);
return -
1;
}
dav_resource_free_all(res);
res = vres;
}
if(dav_load_prop(res, &propname,
1)) {
print_resource_error(sn, res->path);
return -
1;
}
free(path);
DavXmlNode *x = dav_get_property_ns(res, propname.ns, propname.name);
if(!x) {
fprintf(stderr,
"Error: no property value.\n");
return -
1;
}
if(cmd_getoption(a,
"xml")) {
printxmldoc(stdout, propname.name, propname.ns, x);
}
else {
if(dav_xml_isstring(x)) {
printf(
"%s\n", dav_xml_getstring(x));
}
else {
char *str = xml2str(x);
fprintf(stderr,
"%s", str);
free(str);
}
}
return 0;
}
int cmd_set_property(CmdArgs *a) {
if(a->argc <
2) {
fprintf(stderr,
"Too few arguments\n");
fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"set-property"));
return -
1;
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
if(set_session_config(sn, a)) {
return -
1;
}
set_session_lock(sn, a);
DavResource *res = dav_resource_new(sn, path);
if(!dav_exists(res)) {
print_resource_error(sn, res->path);
return -
1;
}
char *namespace = cmd_getoption(a,
"namespace");
char *xml = cmd_getoption(a,
"xml");
char *property = a->argv[
1];
char *value = a->argc >
2 ? a->argv[
2] : stdin2str();
int ret =
0;
if(xml) {
DavXmlNode *xmlvalue = dav_parse_xml(sn, value, strlen(value));
if(xmlvalue) {
if(namespace) {
dav_set_property_ns(res, namespace, property, xmlvalue->children);
}
else {
dav_set_property(res, property, xmlvalue->children);
}
}
else {
fprintf(stderr,
"Error: property content is not valid xml\n");
ret =
1;
}
}
else {
if(namespace) {
dav_set_string_property_ns(res, namespace, property, value);
}
else {
if(dav_set_string_property(res, property, value)) {
fprintf(stderr,
"%s\n", res->session->errorstr);
}
}
}
if(ret ==
0) {
if(dav_store(res)) {
print_resource_error(sn, res->path);
fprintf(stderr,
"Cannot set property.\n");
ret = -
1;
}
}
else
free(path);
dav_session_destroy(sn);
return ret;
}
int cmd_remove_property(CmdArgs *a) {
if(a->argc <
2) {
fprintf(stderr,
"Too few arguments\n");
fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"remove-property"));
return -
1;
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
if(set_session_config(sn, a)) {
return -
1;
}
char *namespace = cmd_getoption(a,
"namespace");
char *property = a->argv[
1];
DavPropName propname;
if(namespace) {
propname.ns = namespace;
propname.name = property;
}
else {
dav_get_property_namespace_str(ctx, property, &propname.ns, &propname.name);
}
int ret =
0;
DavResource *res = dav_resource_new(sn, path);
dav_remove_property_ns(res, propname.ns, propname.name);
if(dav_store(res)) {
print_resource_error(sn, res->path);
fprintf(stderr,
"Cannot set property.\n");
ret = -
1;
}
free(path);
return ret;
}
int cmd_lock(CmdArgs *a) {
if(a->argc !=
1) {
fprintf(stderr,
"Too %s arguments\n", a->argc >
1 ?
"many" :
"few");
fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"lock"));
return -
1;
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
cxMempoolRegister(sn->mp, path, free);
if(set_session_config(sn, a)) {
return -
1;
}
time_t timeout =
0;
char *timeoutstr = cmd_getoption(a,
"timeout");
if(timeoutstr) {
if(!cx_strcasecmp(cx_str(timeoutstr),
CX_STR(
"infinite"))) {
timeout = -
1;
}
else {
uint64_t i;
if(util_strtouint(timeoutstr, &i)) {
timeout = (
time_t)i;
}
else {
fprintf(stderr,
"Error: -T option has invalid value\n");
return -
1;
}
}
}
DavResource *res = dav_resource_new(sn, path);
if(
dav_lock_t(res, timeout)) {
print_resource_error(sn, res->path);
return -
1;
}
DavLock *lock = dav_get_lock(sn, res->path);
if(!lock) {
dav_unlock(res);
fprintf(stderr,
"Error: Cannot find lock token for %s\n", res->path);
return -
1;
}
printf(
"%s\n", lock->token);
dav_session_destroy(sn);
return 0;
}
static char* read_line() {
CxBuffer buf;
cxBufferInit(&buf,
NULL,
128, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
int c;
while((c = getchar()) !=
EOF) {
if(c ==
'\n') {
break;
}
cxBufferPut(&buf, c);
}
char *str =
NULL;
cxstring line = cx_strtrim(cx_strn(buf.space, buf.size));
if(line.length !=
0) {
str = cx_strdup(line).ptr;
}
cxBufferDestroy(&buf);
return str;
}
int cmd_unlock(CmdArgs *a) {
if(a->argc !=
1) {
fprintf(stderr,
"Too %s arguments\n", a->argc >
1 ?
"many" :
"few");
fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"unlock"));
return -
1;
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
cxMempoolRegister(sn->mp, path, free);
if(set_session_config(sn, a)) {
return -
1;
}
char *locktoken = cmd_getoption(a,
"lock");
if(locktoken) {
DavLock *lock = dav_create_lock(sn, locktoken,
NULL);
dav_add_collection_lock(sn,
"/", lock);
}
else {
locktoken = read_line();
if(!locktoken) {
fprintf(stderr,
"No lock token specified.\nAbort.\n");
return -
1;
}
DavLock *lock = dav_create_lock(sn, locktoken,
NULL);
dav_add_collection_lock(sn,
"/", lock);
free(locktoken);
}
int ret =
0;
DavResource *res = dav_resource_new(sn, path);
if(dav_unlock(res)) {
print_resource_error(sn, res->path);
ret = -
1;
}
dav_session_destroy(sn);
return ret;
}
static int count_children(DavResource *res) {
DavResource *child = res->children;
int count =
0;
while(child) {
count++;
child = child->next;
}
return count;
}
void print_xml_infostr(DavXmlNode *xml) {
if(xml->children) {
printf(
"<%s>...</%s>", xml->name, xml->name);
}
else {
printf(
"<%s/>", xml->name);
}
}
int cmd_info(CmdArgs *a) {
if(a->argc <
1) {
fprintf(stderr,
"Too few arguments\n");
fprintf(stderr,
"Usage: dav %s\n", find_usage_str(
"info"));
return -
1;
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
if(set_session_config(sn, a)) {
return -
1;
}
char *version = cmd_getoption(a,
"version");
DavResource *res = dav_resource_new(sn, path);
if(version) {
DavResource *vres = find_version(res, version);
if(!vres) {
fprintf(stderr,
"Cannot find version ''%s'' for resource.\n", version);
return -
1;
}
dav_resource_free_all(res);
res = vres;
}
if(!dav_load(res)) {
printf(
"name: %s\n", res->name);
printf(
"path: %s\n", res->path);
char *server = util_url_base(sn->base_url);
url = util_concat_path(server, res->href);
printf(
"url: %s\n", url);
free(url);
free(server);
if(res->iscollection) {
printf(
"type: collection\n");
printf(
"size: %d\n", count_children(res));
}
else {
printf(
"type: resource\n");
char *len = util_size_str(res->iscollection, res->contentlength);
printf(
"size: %s\n", len);
free(len);
}
size_t count =
0;
DavPropName *properties = dav_get_property_names(res, &count);
char *last_ns =
NULL;
for(
int i=
0;i<count;i++) {
DavPropName p = properties[i];
if(!last_ns || strcmp(last_ns, p.ns)) {
printf(
"\nnamespace: %s\n", p.ns);
last_ns = p.ns;
}
DavXmlNode *xval = dav_get_property_ns(res, p.ns, p.name);
if(dav_xml_isstring(xval)) {
cxstring value = cx_str(dav_xml_getstring(xval));
printf(
" %s: %.*s\n", p.name, (
int)value.length, value.ptr);
}
else {
printf(
" %s: ", p.name);
DavXmlNode *x = xval->type ==
DAV_XML_ELEMENT ? xval : dav_xml_nextelm(xval);
for(
int j=
0;j<
3;j++) {
if(x) {
if(j ==
2) {
printf(
" ...");
break;
}
else {
print_xml_infostr(x);
}
}
else {
break;
}
x = dav_xml_nextelm(x);
}
printf(
"\n");
}
}
dav_session_free(sn, properties);
return 0;
}
else {
print_resource_error(sn, res->path);
}
return -
1;
}
int cmd_checkout(CmdArgs *a) {
if(a->argc <
1) {
fprintf(stderr,
"Too few arguments\n");
fprintf(stderr,
"Usage: dav %s\n",
"checkout [-pc] <url>");
return -
1;
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
if(set_session_config(sn, a)) {
return -
1;
}
set_session_lock(sn, a);
int ret =
0;
DavResource *res = dav_resource_new(sn, path);
if(dav_checkout(res)) {
print_resource_error(sn, res->path);
ret =
1;
}
return ret;
}
int cmd_checkin(CmdArgs *a) {
if(a->argc <
1) {
fprintf(stderr,
"Too few arguments\n");
fprintf(stderr,
"Usage: dav %s\n",
"checkin [-pc] <url>");
return -
1;
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
if(set_session_config(sn, a)) {
return -
1;
}
set_session_lock(sn, a);
int ret =
0;
DavResource *res = dav_resource_new(sn, path);
if(dav_checkin(res)) {
print_resource_error(sn, res->path);
ret =
1;
}
return ret;
}
int cmd_uncheckout(CmdArgs *a) {
if(a->argc <
1) {
fprintf(stderr,
"Too few arguments\n");
fprintf(stderr,
"Usage: dav %s\n",
"uncheckout [-pc] <url>");
return -
1;
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
if(set_session_config(sn, a)) {
return -
1;
}
set_session_lock(sn, a);
int ret =
0;
DavResource *res = dav_resource_new(sn, path);
if(dav_uncheckout(res)) {
print_resource_error(sn, res->path);
ret =
1;
}
return ret;
}
int cmd_versioncontrol(CmdArgs *a) {
if(a->argc <
1) {
fprintf(stderr,
"Too few arguments\n");
fprintf(stderr,
"Usage: dav %s\n",
"versioncontrol [-pc] <url>");
return -
1;
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
if(set_session_config(sn, a)) {
return -
1;
}
set_session_lock(sn, a);
int ret =
0;
DavResource *res = dav_resource_new(sn, path);
if(dav_versioncontrol(res)) {
print_resource_error(sn, res->path);
ret =
1;
}
return ret;
}
int cmd_list_versions(CmdArgs *a) {
if(a->argc <
1) {
fprintf(stderr,
"Too few arguments\n");
fprintf(stderr,
"Usage: dav %s\n",
"list-versions [-pc] <url>");
return -
1;
}
char *url = a->argv[
0];
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
if(set_session_config(sn, a)) {
return -
1;
}
DavResource *res = dav_resource_new(sn, path);
int ret =
0;
DavResource *list = dav_versiontree(res,
NULL);
if(list) {
char* longlist = cmd_getoption(a,
"list");
DavResource *v = list;
int addnl =
0;
while(v) {
char *vname = dav_get_string_property(v,
"D:version-name");
if(longlist) {
if(addnl) {
putchar(
'\n');
}
printf(
"name: %s\n", vname);
printf(
"href: %s\n", v->href);
addnl =
1;
}
else {
printf(
"%s\n", vname);
}
v = v->next;
}
}
else if(sn->error !=
DAV_OK) {
print_resource_error(sn, path);
ret =
1;
}
return ret;
}
DavResource* find_version(DavResource *res,
char *version) {
DavResource *list = dav_versiontree(res,
NULL);
DavResource *ret =
NULL;
while(list) {
DavResource *next = list->next;
if(!ret) {
char *vname = dav_get_string_property(list,
"D:version-name");
if(vname && !strcmp(vname, version)) {
ret = list;
}
}
if(list != ret) {
dav_resource_free(list);
}
list = next;
}
return ret;
}
char* stdin2str() {
CxBuffer buf;
cxBufferInit(&buf,
NULL,
1024, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
size_t size = cx_stream_copy(stdin, &buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite);
if(size ==
0) {
cxBufferDestroy(&buf);
return NULL;
}
else {
cxBufferPut(&buf,
'\0');
return buf.space;
}
}
static void xml2str_i(DavXmlNode *node, CxBuffer *buf,
int indent) {
while(node) {
if(node->type ==
DAV_XML_ELEMENT) {
if(node->children) {
if(dav_xml_isstring(node->children)) {
cxstring s = cx_strtrim(cx_str(dav_xml_getstring(node->children)));
cx_bprintf(
buf,
"%*s<%s>%.*s</%s>\n",
indent,
"",
node->name,
(
int)s.length,
s.ptr,
node->name);
}
else {
cx_bprintf(buf,
"%*s<%s>\n", indent,
"", node->name);
xml2str_i(node->children, buf, indent+
2);
cx_bprintf(buf,
"%*s</%s>\n", indent,
"", node->name);
}
}
else {
cx_bprintf(buf,
"%*s<%s />", indent,
"", node->name);
cxBufferPut(buf,
'\n');
}
}
else if(node->type ==
DAV_XML_TEXT) {
cxstring val = cx_strtrim(cx_strn(node->content, node->contentlength));
if(val.length >
0) {
cx_bprintf(buf,
"%*.*s", indent, (
int)val.length, val.ptr);
}
}
node = node->next;
}
}
char* xml2str(DavXmlNode *node) {
CxBuffer buf;
cxBufferInit(&buf,
NULL,
256, cxDefaultAllocator,
CX_BUFFER_AUTO_EXTEND);
xml2str_i(node, &buf,
0);
cxBufferPut(&buf,
0);
return buf.space;
}
void printxmldoc(
FILE *out,
char *root,
char *rootns, DavXmlNode *content) {
CxMap *nsmap = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
16);
cxDefineDestructor(nsmap, free);
cxMapPut(nsmap, cx_hash_key_str(rootns),
"x0");
fprintf(out,
"%s",
"<?xml version=\"1.0\"?>\n");
fprintf(out,
"<x0:%s xmlns:x0=\"%s\">", root, rootns);
dav_print_node(out, (cx_write_func)fwrite, nsmap, content);
fprintf(out,
"</x0:%s>\n", root);
cxMapRemove(nsmap, cx_hash_key_str(rootns));
cxMapDestroy(nsmap);
}
int cmd_add_repository(CmdArgs *args) {
printf(
"Each repository must have an unique name.\n");
char *name = assistant_getcfg(
"name");
if(!name) {
fprintf(stderr,
"Abort\n");
return -
1;
}
if(dav_config_get_repository(get_config(), cx_str(name))) {
fprintf(stderr,
"Repository %s already exists.\nAbort\n", name);
return -
1;
}
printf(
"\nSpecify the repository base url.\n");
char *url = assistant_getcfg(
"url");
if(!url) {
fprintf(stderr,
"Abort\n");
return -
1;
}
printf(
"\nUser for HTTP authentication.\n");
char *user = assistant_getoptcfg(
"user");
char *password =
NULL;
if(user) {
password = assistant_gethiddenoptcfg(
"password");
}
printf(
"\n");
DavConfig *config = get_config();
const CxAllocator *a = config->mp->allocator;
DavCfgRepository *repo = dav_repository_new(config);
repo->name.value = cx_strdup_a(a, cx_str(name));
repo->url.value = cx_strdup_a(a, cx_str(url));
cxstring user_s = user ? cx_str(user) : cx_strn(
NULL,
0);
cxstring password_s = password ? cx_str(password) : cx_strn(
NULL,
0);
dav_repository_set_auth(config, repo, user_s, password_s);
dav_config_add_repository(config, repo);
int ret =
0;
if(store_config()) {
fprintf(stderr,
"Cannot write config.xml\n");
ret = -
1;
}
else {
printf(
"\nAdded repository: %s (%s)\n", name, url);
}
free(name);
free(url);
if(user) {
free(user);
}
if(password) {
free(password);
}
return ret;
}
int cmd_remove_repository(CmdArgs *args) {
if(args->argc <
1) {
fprintf(stderr,
"Too few arguments\n");
fprintf(stderr,
"Usage: dav remove-repository <name...>\n");
return -
1;
}
DavConfig *config = get_config();
DavBool store =
FALSE;
for(
int i =
0 ; i < args->argc ; i++) {
cxstring reponame = cx_str(args->argv[i]);
DavCfgRepository* repo = dav_config_get_repository(config, reponame);
if(repo) {
dav_repository_remove_and_free(config, repo);
store =
TRUE;
}
else {
fprintf(stderr,
"Repository %s does not exist - skipped.\n",
reponame.ptr);
return -
1;
}
}
if(store) {
return store_config();
}
else {
return -
1;
}
}
int cmd_list_repositories(
void) {
DavConfig *config = get_config();
if(!config) {
return 1;
}
for(DavCfgRepository *repo=config->repositories;repo;repo=repo->next) {
printf(
"%.*s\n", (
int)repo->name.value.length, repo->name.value.ptr);
}
return 0;
}
int cmd_repository_url(CmdArgs *args) {
if(args->argc !=
1) {
fprintf(stderr,
"Too few arguments\n");
fprintf(stderr,
"Usage: dav repository-url [-p] <name>\n");
return -
1;
}
cxstring reponame = cx_str(args->argv[
0]);
DavCfgRepository* repo = dav_config_get_repository(get_config(), reponame);
if(repo) {
cxstring url = cx_strcast(repo->url.value);
if(repo->user.value.ptr && !cmd_getoption(args,
"plain")) {
int hostindex =
0;
if(cx_strprefix(url,
CX_STR(
"https://"))) {
printf(
"https://");
hostindex =
8;
}
else if(cx_strprefix(url,
CX_STR(
"http://"))) {
printf(
"http://");
hostindex =
7;
}
printf(
"%.*s", (
int)repo->user.value.length, repo->user.value.ptr);
if(repo->password.value.ptr) {
cxmutstr pw_decoded = dav_repository_get_decodedpassword(repo);
CURL *curl = curl_easy_init();
char *pw = curl_easy_escape(
curl,
pw_decoded.ptr,
pw_decoded.length);
printf(
":%s", pw);
curl_free(pw);
curl_easy_cleanup(curl);
free(pw_decoded.ptr);
}
putchar(
'@');
printf(
"%.*s", (
int)url.length-hostindex, url.ptr+hostindex);
}
else {
printf(
"%s", url.ptr);
}
if(url.ptr[url.length-
1] !=
'/') {
putchar(
'/');
}
putchar(
'\n');
}
else {
fprintf(stderr,
"Repository %s does not exist.\n", reponame.ptr);
return -
1;
}
return 0;
}
typedef int(*sscmd_func)(CmdArgs *, PwdStore *,
void *userdata);
static int secretstore_after_decrypt(
CmdArgs *args,
PwdStore *secrets,
sscmd_func cb,
void *userdata);
static int secretstore_cmd(
CmdArgs *args,
DavBool create,
sscmd_func beforedecrypt,
sscmd_func afterdecrypt,
void *userdata)
{
PwdStore *secrets = get_pwdstore();
if(!secrets) {
if(create) {
secrets = pwdstore_new();
}
else {
return 1;
}
}
int ret =
0;
if(beforedecrypt) {
ret = beforedecrypt(args, secrets, userdata);
if(ret) {
afterdecrypt =
NULL;
}
}
if(afterdecrypt) {
ret = secretstore_after_decrypt(args, secrets, afterdecrypt, userdata);
}
pwdstore_free(secrets);
return ret;
}
static int secretstore_after_decrypt(
CmdArgs *args,
PwdStore *secrets,
sscmd_func cb,
void *userdata)
{
char *master_pw = util_password_input(
"Master password: ");
if(!master_pw) {
fprintf(stderr,
"Error: master password required.\nAbort.\n");
return 1;
}
int err = pwdstore_setpassword(secrets, master_pw);
free(master_pw);
if(err) {
fprintf(stderr,
"Error: Cannot generate key from password.\nAbort.\n");
return 1;
}
if(pwdstore_decrypt(secrets)) {
fprintf(stderr,
"Error: Cannot decrypt secrets store.\nAbort.\n");
return 1;
}
return cb(args, secrets, userdata);
}
static int cmd_ss_add_user(CmdArgs *Args, PwdStore *secrets,
void *userdata) {
char *id = assistant_getcfg(
"Credentials identifier");
if(!id) {
fprintf(stderr,
"Identifier required.\n");
return 1;
}
if(pwdstore_get(secrets, id)) {
fprintf(stderr,
"Credentials with this id already exist.\n");
return 1;
}
char *user = assistant_getcfg(
"User");
char *password = util_password_input(
"Password: ");
char *location =
NULL;
CxList *locations = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
cxDefineDestructor(locations, free);
while((location = assistant_getoptcfg(
"Location"))) {
cxListAdd(locations, location);
}
int ret =
1;
if(user && password) {
pwdstore_put_index(secrets, id, locations);
pwdstore_put(secrets, id, user, password);
ret = pwdstore_save(secrets);
if(ret) {
fprintf(stderr,
"Error: saving srcrets store failed.\n");
}
if(cxListSize(locations) ==
0) {
cxListDestroy(locations);
}
}
else {
cxListDestroy(locations);
}
if(id) free(id);
if(user) free(user);
if(password) free(password);
return ret;
}
int cmd_add_user(CmdArgs *args) {
return secretstore_cmd(args,
TRUE,
NULL, cmd_ss_add_user,
NULL);
}
static int cmd_ss_list_users_bc(CmdArgs *Args, PwdStore *secrets,
int *ret) {
if(cxMapSize(secrets->index) ==
0) {
return 1;
}
*ret =
1;
return 0;
}
static int cmd_ss_list_users(CmdArgs *args, PwdStore *secrets,
int *ret) {
*ret =
0;
CxList *list = secrets->locations;
for(
int i=
0;i<
2;i++) {
if(list) {
CxIterator iter = cxListIterator(list);
cx_foreach(PwdIndexEntry*, index, iter) {
PwdEntry *e = cxMapGet(secrets->ids, cx_hash_key_str(index->id));
if(e) {
printf(
"Id: %s\n", e->id);
printf(
"User: %s\n", e->user);
if(index->locations) {
CxIterator loc_iter = cxListIterator(index->locations);
cx_foreach(
char *, location, loc_iter) {
printf(
"Location: %s\n", location);
}
printf(
"\n");
}
}
else {
fprintf(stderr,
"Warning: id ''%s'' not in secret store.\n",
index->id);
}
}
}
list = secrets->noloc;
}
return 0;
}
int cmd_list_users(CmdArgs *args) {
int ret =
0;
secretstore_cmd(args,
FALSE, (sscmd_func)cmd_ss_list_users_bc, (sscmd_func)cmd_ss_list_users, &ret);
return ret;
}
static int cmd_ss_remove_user(CmdArgs *args, PwdStore *secrets,
void *ud) {
char *id = assistant_getcfg(
"Credentials identifier");
if(!id) {
fprintf(stderr,
"Identifier required.\n");
return 1;
}
if(!pwdstore_get(secrets, id)) {
fprintf(stderr,
"Credentials with this id doesn''t exist.\n");
free(id);
return 1;
}
pwdstore_remove_entry(secrets, id);
int ret = pwdstore_save(secrets);
if(ret) {
fprintf(stderr,
"Error: saving srcrets store failed.\n");
}
free(id);
return ret;
}
int cmd_remove_user(CmdArgs *args) {
return secretstore_cmd(args,
FALSE,
NULL, cmd_ss_remove_user,
NULL);
}
static void secrets_print_user_info(PwdStore *secrets,
const char *id) {
PwdEntry *entry = pwdstore_get(secrets, id);
if(!entry) {
return;
}
PwdIndexEntry *index = cxMapGet(secrets->index, cx_hash_key_str(id));
if(!index) {
return;
}
printf(
"Id: %s\n", entry->id);
printf(
"User: %s\n", entry->user);
if(index->locations) {
CxIterator loc_iter = cxListIterator(index->locations);
cx_foreach(
char *, location, loc_iter) {
printf(
"Location: %s\n", location);
}
}
}
static void secrets_remove_location(PwdIndexEntry *index) {
if(!index->locations || cxListSize(index->locations) ==
0) {
printf(
"no locations\n");
return;
}
printf(
"0: abort\n");
int i =
1;
CxIterator loc_iter = cxListIterator(index->locations);
cx_foreach(
char *, location, loc_iter) {
printf(
"%d: %s\n", i, location);
i++;
}
char *input = assistant_getcfg(
"Choose location");
if(!input) {
return;
}
int64_t ln =
0;
if(util_strtoint(input, &ln) && (ln >=
0 && ln < i)) {
if(ln ==
0) {
return;
}
else {
char *location = cxListAt(index->locations, ln -
1);
if(location) {
free(location);
cxListRemove(index->locations, ln -
1);
}
}
}
else {
printf(
"illegal input, choose 0 - %d\n", i-
1);
secrets_remove_location(index);
}
}
static int cmd_ss_edit_user(CmdArgs *args, PwdStore *secrets,
void *ud) {
char *id = assistant_getcfg(
"Credentials identifier");
if(!id) {
fprintf(stderr,
"Identifier required.\n");
return 1;
}
PwdEntry *entry = pwdstore_get(secrets, id);
PwdIndexEntry *index = cxMapGet(secrets->index, cx_hash_key_str(id));
if(!entry || !index) {
fprintf(stderr,
"Credentials with this id doesn''t exist.\n");
return 1;
}
secrets_print_user_info(secrets, id);
int save =
0;
int loop =
1;
while(loop) {
printf(
"\n");
printf(
"0: change user name\n");
printf(
"1: change password\n");
printf(
"2: add location\n");
printf(
"3: remove location\n");
printf(
"4: list locations\n");
printf(
"5: save and exit\n");
printf(
"6: exit without saving\n");
char *opt = assistant_getcfg(
"Choose action");
if(!opt) {
break;
}
int64_t mnu =
0;
if(util_strtoint(opt, &mnu) && (mnu >=
0 && mnu <=
6)) {
printf(
"\n");
switch(mnu) {
case 0: {
char *user = assistant_getcfg(
"User");
if(user) {
if(entry->user) {
free(entry->user);
}
entry->user = user;
}
break;
}
case 1: {
char *password = util_password_input(
"Password: ");
if(password) {
if(entry->password) {
free(entry->password);
}
entry->password = password;
}
break;
}
case 2: {
char *location = assistant_getoptcfg(
"Location");
if(location) {
cxListAdd(index->locations, location);
}
break;
}
case 3: {
secrets_remove_location(index);
break;
}
case 4: {
if(!index->locations || cxListSize(index->locations)) {
printf(
"no locations\n");
}
else {
CxIterator i = cxListIterator(index->locations);
cx_foreach(
char *, location, i) {
printf(
"Location: %s\n", location);
}
}
break;
}
case 5: {
loop =
0;
save =
1;
break;
}
case 6: {
loop =
0;
break;
}
}
}
else {
printf(
"illegal input, choose 0 - 5\n");
}
free(opt);
}
int ret =
0;
if(save) {
ret = pwdstore_save(secrets);
if(ret) {
fprintf(stderr,
"Error: saving srcrets store failed.\n");
}
}
return ret;
}
int cmd_edit_user(CmdArgs *args) {
return secretstore_cmd(args,
FALSE,
NULL, cmd_ss_edit_user,
NULL);
}
static int cmd_ss_set_master_pw(CmdArgs *args, PwdStore *secrets,
void *ud) {
char *new_master_pw = util_password_input(
"New master password: ");
int ret = pwdstore_setpassword(secrets, new_master_pw);
if(ret) {
fprintf(stderr,
"Error: failed to set new master password\n");
}
ret = pwdstore_save(secrets);
if(ret) {
fprintf(stderr,
"Error: saving srcrets store failed.\n");
}
return ret;
}
int cmd_set_master_password(CmdArgs *args) {
return secretstore_cmd(args,
FALSE,
NULL, cmd_ss_set_master_pw,
NULL);
}
static char** read_args_from_stdin(
int *argc) {
CxBuffer *in = cxBufferCreate(
NULL,
1024, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
cx_stream_copy(stdin, in, (cx_read_func)fread, (cx_write_func)cxBufferWrite);
ssize_t count =
0;
cxmutstr *lines;
count = cx_strsplit_ma(cxDefaultAllocator, cx_mutstrn(in->space, in->pos),
CX_STR(
"\n"),
INT_MAX, &lines);
char **args =
NULL;
if(count >
0) {
args = calloc(count,
sizeof(
char*));
for(
int i=
0;i<count;i++) {
args[i] = lines[i].ptr;
}
free(lines);
*argc = count;
}
else {
*argc =
0;
}
cxBufferFree(in);
return args;
}
int cmd_complete(CmdArgs *args) {
if(args->argc !=
1) {
return 1;
}
char *index_str = args->argv[
0];
int64_t index =
0;
if(!util_strtoint(index_str, &index)) {
return 1;
}
int comp_argc;
char **comp_argv = read_args_from_stdin(&comp_argc);
char *cmd =
NULL;
if(comp_argc >
1) {
cmd = comp_argv[
1];
}
CmdArgs *comp_args = cmd_parse_args(comp_argc -
2, comp_argv +
2);
if(comp_args) {
if(comp_args->argc +
2 != comp_argc) {
int j =
0;
for(
int i=
0;i<comp_argc-
2;i++) {
if(index == i-
2) {
break;
}
if(strcmp(comp_argv[i+
2], comp_args->argv[j])) {
index--;
}
else {
j++;
}
}
}
}
else {
comp_args =
NULL;
}
int ret =
1;
if(comp_args) {
ret = shell_completion(cmd, comp_args, index);
}
cmd_args_free(comp_args);
free(comp_argv);
return ret;
}
int shell_completion(
char *cmd, CmdArgs *args,
int index) {
if(index ==
1) {
cxstring prefix = {
NULL,
0 };
if(cmd) {
prefix = cx_str(cmd);
}
for(
int i=
0;;i++) {
char *str = cmdusageinfo[i];
if(!str) {
break;
}
int len = (
int)strlen(str);
int maxlen = len;
for(
int w=
0;w<len;w++) {
if(str[w] ==
' ') {
maxlen = w;
break;
}
}
if(prefix.ptr) {
if(!cx_strprefix(cx_strn(str, maxlen), prefix)) {
continue;
}
}
printf(
"%.*s\n", (
int)maxlen, str);
}
return 0;
}
if(!strcmp(cmd,
"date")) {
return 0;
}
char *url = args->argc >
0 ? args->argv[
0] :
NULL;
if(index ==
2) {
return url_completion(args, url);
}
else if (index ==
3) {
if(!strcmp(cmd,
"put") || !strcmp(cmd,
"import")) {
return 12;
}
else if(!strcmp(cmd,
"copy") || !strcmp(cmd,
"cp") || !strcmp(cmd,
"move") || !strcmp(cmd,
"mv")) {
return url_completion(args, url);
}
}
return 0;
}
int url_completion(CmdArgs *args,
char *u) {
cxstring url;
url.ptr = u;
url.length = u ? strlen(u) :
0;
char quote =
'\0';
if(url.length >
0 && (url.ptr[
0] ==
'\'' || url.ptr[
0] ==
'\"' )) {
quote = url.ptr[
0];
url.ptr++;
url.length--;
if (url.ptr[url.length-
1] == quote) {
url.length--;
}
}
int repocomp =
1;
for(
int i=
0;i<url.length;i++) {
if(url.ptr[i] ==
'/') {
repocomp =
0;
break;
}
}
if(repocomp) {
DavConfig *config = get_config();
for(DavCfgRepository *repo=config->repositories; repo; repo=repo->next) {
if(cx_strprefix(cx_strcast(repo->name.value), url)) {
if(quote ==
'\0') {
printf(
"%s/\n", repo->name.value.ptr);
}
else {
printf(
"%c%s/%c\n", quote, repo->name.value.ptr, quote);
}
}
}
}
else {
cxMapPut(args->options, cx_hash_key_str(
"noinput"),
"");
char *path =
NULL;
DavCfgRepository *repo = dav_config_url2repo(get_config(), url.ptr, &path);
DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, args);
if(!sn) {
return 0;
}
if(set_session_config(sn, args)) {
return 0;
}
size_t plen = strlen(path);
cxstring filter;
char *lspath =
NULL;
if(path[plen-
1] ==
'/') {
lspath = strdup(path);
filter =
CX_STR(
"");
}
else {
lspath = util_parent_path(path);
filter = cx_str(util_resource_name(path));
}
DavResource *ls = dav_query(sn,
"select - from %s order by name", lspath);
DavResource *elm = ls ? ls->children :
NULL;
while(elm) {
cxstring name = cx_str(elm->name);
if(cx_strprefix(name, filter)) {
int space =
0;
for(
int i=
0;i<name.length;i++) {
if(name.ptr[i] ==
' ') {
space =
1;
break;
}
}
CxBuffer *out = cxBufferCreate(
NULL,
512, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
cxBufferWrite(repo->name.value.ptr, repo->name.value.length,
1, out);
if(space) {
size_t l = strlen(elm->path);
for(
int i=
0;i<l;i++) {
char nextc = elm->path[i];
if(quote ==
'\0' &&
NULL != strchr(
"!\"#$&''()*,;<>?[\\]^`{|}~ ", nextc)) {
cxBufferPut(out,
'\\');
}
cxBufferPut(out, nextc);
}
}
else {
cxBufferPutString(out, elm->path);
}
if(elm->iscollection) {
if(out->space[out->pos-
1] !=
'/') {
cxBufferPut(out,
'/');
}
}
if (quote ==
'\0') {
printf(
"%.*s\n", (
int)out->pos, out->space);
}
else {
printf(
"%c%.*s%c\n",
quote, (
int)out->pos, out->space, quote);
}
cxBufferFree(out);
}
elm = elm->next;
}
free(lspath);
dav_session_destroy(sn);
}
return 10;
}